summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rwxr-xr-xsql/CMakeLists.txt161
-rw-r--r--sql/Makefile.am22
-rw-r--r--sql/authors.h36
-rw-r--r--sql/client_settings.h5
-rw-r--r--sql/create_options.cc616
-rw-r--r--sql/create_options.h92
-rw-r--r--sql/derror.cc85
-rw-r--r--sql/event_data_objects.cc2
-rw-r--r--sql/event_data_objects.h6
-rw-r--r--sql/event_db_repository.cc31
-rw-r--r--sql/event_db_repository.h1
-rw-r--r--sql/event_parse_data.h6
-rw-r--r--sql/event_queue.cc14
-rwxr-xr-xsql/event_scheduler.cc11
-rw-r--r--sql/events.cc13
-rw-r--r--sql/events.h2
-rw-r--r--sql/field.cc198
-rw-r--r--sql/field.h153
-rw-r--r--sql/filesort.cc28
-rw-r--r--sql/ha_ndbcluster.cc19
-rw-r--r--sql/ha_ndbcluster_binlog.cc2
-rw-r--r--sql/ha_partition.cc121
-rw-r--r--sql/ha_partition.h30
-rw-r--r--sql/handler.cc249
-rw-r--r--sql/handler.h256
-rw-r--r--sql/hash_filo.h2
-rw-r--r--sql/item.cc61
-rw-r--r--sql/item.h136
-rw-r--r--sql/item_cmpfunc.cc49
-rw-r--r--sql/item_cmpfunc.h23
-rw-r--r--sql/item_func.cc74
-rw-r--r--sql/item_func.h77
-rw-r--r--sql/item_row.h3
-rw-r--r--sql/item_strfunc.cc4
-rw-r--r--sql/item_strfunc.h17
-rw-r--r--sql/item_subselect.cc60
-rw-r--r--sql/item_subselect.h14
-rw-r--r--sql/item_sum.cc22
-rw-r--r--sql/item_sum.h21
-rw-r--r--sql/item_timefunc.h37
-rw-r--r--sql/item_xmlfunc.cc9
-rw-r--r--sql/item_xmlfunc.h4
-rw-r--r--sql/key.cc3
-rw-r--r--sql/lex.h15
-rw-r--r--sql/lock.cc4
-rw-r--r--sql/log.cc132
-rw-r--r--sql/log.h6
-rw-r--r--sql/log_event.cc160
-rw-r--r--sql/log_event.h20
-rw-r--r--sql/log_event_old.cc74
-rw-r--r--sql/mysql_install_db.cc637
-rw-r--r--sql/mysql_priv.h66
-rw-r--r--sql/mysql_upgrade_service.cc522
-rw-r--r--sql/mysqld.cc486
-rw-r--r--sql/net_serv.cc4
-rw-r--r--sql/opt_range.cc128
-rw-r--r--sql/opt_range.h2
-rw-r--r--sql/opt_sum.cc51
-rw-r--r--sql/opt_table_elimination.cc11
-rw-r--r--sql/parse_file.h4
-rw-r--r--sql/partition_info.cc4
-rw-r--r--sql/password.c10
-rw-r--r--sql/procedure.h6
-rw-r--r--sql/protocol.cc21
-rw-r--r--sql/protocol.h1
-rw-r--r--sql/records.cc30
-rw-r--r--sql/rpl_filter.cc9
-rw-r--r--sql/rpl_filter.h5
-rw-r--r--sql/rpl_mi.h4
-rw-r--r--sql/rpl_rli.cc3
-rw-r--r--sql/scheduler.cc11
-rw-r--r--sql/set_var.cc81
-rw-r--r--sql/set_var.h18
-rw-r--r--sql/share/CMakeLists.txt54
-rw-r--r--sql/share/Makefile.am2
-rw-r--r--sql/share/errmsg.txt38
-rw-r--r--sql/slave.cc5
-rw-r--r--sql/sp.cc71
-rw-r--r--sql/sp.h16
-rw-r--r--sql/sp_head.cc16
-rw-r--r--sql/sp_head.h22
-rw-r--r--sql/sp_rcontext.cc2
-rw-r--r--sql/sp_rcontext.h2
-rw-r--r--sql/sql_acl.cc2236
-rw-r--r--sql/sql_acl.h54
-rw-r--r--sql/sql_analyse.h2
-rw-r--r--sql/sql_base.cc233
-rw-r--r--sql/sql_bitmap.h30
-rw-r--r--sql/sql_builtin.cc.in11
-rw-r--r--sql/sql_cache.cc14
-rw-r--r--sql/sql_cache.h6
-rw-r--r--sql/sql_class.cc260
-rw-r--r--sql/sql_class.h243
-rw-r--r--sql/sql_connect.cc966
-rw-r--r--sql/sql_cursor.cc4
-rw-r--r--sql/sql_db.cc1
-rw-r--r--sql/sql_delete.cc18
-rw-r--r--sql/sql_handler.cc20
-rw-r--r--sql/sql_help.cc41
-rw-r--r--sql/sql_insert.cc79
-rw-r--r--sql/sql_lex.cc10
-rw-r--r--sql/sql_lex.h25
-rw-r--r--sql/sql_load.cc15
-rw-r--r--sql/sql_parse.cc319
-rw-r--r--sql/sql_partition.cc5
-rw-r--r--sql/sql_plugin.cc492
-rw-r--r--sql/sql_plugin.h20
-rw-r--r--sql/sql_plugin_services.h44
-rw-r--r--sql/sql_prepare.cc17
-rw-r--r--sql/sql_select.cc223
-rw-r--r--sql/sql_servers.cc29
-rw-r--r--sql/sql_show.cc697
-rw-r--r--sql/sql_string.cc26
-rw-r--r--sql/sql_string.h81
-rw-r--r--sql/sql_table.cc184
-rw-r--r--sql/sql_test.cc29
-rw-r--r--sql/sql_trigger.cc7
-rw-r--r--sql/sql_udf.cc16
-rw-r--r--sql/sql_update.cc60
-rw-r--r--sql/sql_view.cc8
-rw-r--r--sql/sql_yacc.yy293
-rw-r--r--sql/structs.h115
-rw-r--r--sql/table.cc804
-rw-r--r--sql/table.h92
-rw-r--r--sql/thr_malloc.cc2
-rw-r--r--sql/tztime.cc46
-rw-r--r--sql/unireg.cc118
-rw-r--r--sql/unireg.h7
-rw-r--r--sql/winservice.c247
-rw-r--r--sql/winservice.h24
130 files changed, 10995 insertions, 2806 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 535f53335be..e281b187149 100755
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -21,17 +21,19 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/sql
${CMAKE_SOURCE_DIR}/regex
${CMAKE_SOURCE_DIR}/zlib
- ${CMAKE_SOURCE_DIR}/extra/libevent
+ ${CMAKE_SOURCE_DIR}/extra/libevent
+ ${CMAKE_BINARY_DIR}/extra/libevent
+ ${CMAKE_CURRENT_BINARY_DIR}
)
-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
+SET_SOURCE_FILES_PROPERTIES(${CMAKE_BINARY_DIR}/sql/sql_yacc.h
+ ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc
+ ${CMAKE_BINARY_DIR}/include/mysql_version.h
+ ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc
+ ${CMAKE_BINARY_DIR}/sql/lex_hash.h
+ ${CMAKE_BINARY_DIR}/include/mysqld_error.h
+ ${CMAKE_BINARY_DIR}/include/mysqld_ername.h
+ ${CMAKE_BINARY_DIR}/include/sql_state.h
PROPERTIES GENERATED 1)
ADD_DEFINITIONS(-DMYSQL_SERVER -D_CONSOLE -DHAVE_DLOPEN -DHAVE_EVENT_SCHEDULER)
@@ -44,7 +46,7 @@ SET (SQL_SOURCE
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
+ handler.cc hash_filo.cc hash_filo.h sql_plugin_services.h
hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc
item_create.cc item_func.cc item_geofunc.cc item_row.cc
item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc
@@ -52,7 +54,7 @@ SET (SQL_SOURCE
log_event.cc rpl_record.cc rpl_reporting.cc
log_event_old.cc rpl_record_old.cc
message.h mf_iocache.cc my_decimal.cc ../sql-common/my_time.c
- mysqld.cc net_serv.cc
+ mysqld.cc net_serv.cc ../sql-common/client_plugin.c
nt_servc.cc nt_servc.h opt_range.cc opt_range.h opt_sum.cc
../sql-common/pack.c parse_file.cc password.c procedure.cc
protocol.cc records.cc repl_failsafe.cc rpl_filter.cc set_var.cc
@@ -75,20 +77,23 @@ SET (SQL_SOURCE
rpl_rli.cc rpl_mi.cc sql_servers.cc
sql_connect.cc scheduler.cc
sql_profile.cc event_parse_data.cc opt_table_elimination.cc
- ${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/sql_builtin.cc
- ${PROJECT_SOURCE_DIR}/sql/lex_hash.h)
+ create_options.cc
+ ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc
+ ${CMAKE_BINARY_DIR}/sql/sql_yacc.h
+ ${CMAKE_BINARY_DIR}/include/mysqld_error.h
+ ${CMAKE_BINARY_DIR}/include/mysqld_ername.h
+ ${CMAKE_BINARY_DIR}/include/sql_state.h
+ ${CMAKE_BINARY_DIR}/include/mysql_version.h
+ ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc
+ ${CMAKE_BINARY_DIR}/sql/lex_hash.h)
ADD_LIBRARY(sql ${SQL_SOURCE})
-IF (NOT EXISTS cmake_dummy.cc)
- FILE (WRITE cmake_dummy.cc "")
-ENDIF (NOT EXISTS cmake_dummy.cc)
-ADD_EXECUTABLE(mysqld cmake_dummy.cc message.rc)
+CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/win/cmake/dummy.in cmake_dummy.cc COPYONLY)
+MYSQL_ADD_EXECUTABLE(mysqld cmake_dummy.cc message.rc DESTINATION ${INSTALL_SBINDIR} COMPONENT Server)
+INSTALL_DEBUG_TARGET(mysqld
+ DESTINATION ${INSTALL_SBINDIR}
+ PDB_DESTINATION ${INSTALL_SBINDIR}/debug
+ RENAME mysqld-debug)
SET_TARGET_PROPERTIES(mysqld PROPERTIES OUTPUT_NAME mysqld${MYSQLD_EXE_SUFFIX})
SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE)
@@ -112,17 +117,17 @@ IF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS)
ADD_CUSTOM_COMMAND(TARGET mysqld PRE_LINK
COMMAND cscript ARGS //nologo ${PROJECT_SOURCE_DIR}/win/create_def_file.js
${PLATFORM} ${LIB_LOCATIONS} > mysqld.def
- WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/sql)
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
ENDIF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS)
ADD_DEPENDENCIES(sql GenError)
# Sql Parser custom command
ADD_CUSTOM_COMMAND(
- OUTPUT ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
- ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
+ OUTPUT ${CMAKE_BINARY_DIR}/sql/sql_yacc.h
+ ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc
COMMAND bison ARGS -y -p MYSQL --defines=sql_yacc.h
- --output=sql_yacc.cc sql_yacc.yy
+ --output=sql_yacc.cc "${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy"
DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.yy)
@@ -131,17 +136,15 @@ ADD_EXECUTABLE(gen_lex_hash gen_lex_hash.cc)
TARGET_LINK_LIBRARIES(gen_lex_hash debug dbug mysqlclient strings wsock32)
GET_TARGET_PROPERTY(GEN_LEX_HASH_EXE gen_lex_hash LOCATION)
ADD_CUSTOM_COMMAND(
- OUTPUT ${PROJECT_SOURCE_DIR}/sql/lex_hash.h
+ OUTPUT ${CMAKE_BINARY_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)
+ DEPENDS ${CMAKE_BINARY_DIR}/sql/sql_yacc.h
+ ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc
+ ${CMAKE_BINARY_DIR}/sql/lex_hash.h)
ADD_DEPENDENCIES(sql GenServerSource)
@@ -153,7 +156,91 @@ ADD_LIBRARY(udf_example MODULE udf_example.c udf_example.def)
ADD_DEPENDENCIES(udf_example strings GenError)
TARGET_LINK_LIBRARIES(udf_example strings wsock32)
-INSTALL(TARGETS mysqld
- RUNTIME DESTINATION bin COMPONENT runtime
- LIBRARY DESTINATION lib COMPONENT runtime
- ARCHIVE DESTINATION lib COMPONENT runtime)
+ADD_SUBDIRECTORY(share)
+
+IF(WIN32)
+ SET(my_bootstrap_sql ${CMAKE_CURRENT_BINARY_DIR}/my_bootstrap.sql)
+ FILE(TO_NATIVE_PATH ${my_bootstrap_sql} native_outfile)
+
+ # Create bootstrapper SQL script
+ ADD_CUSTOM_COMMAND(OUTPUT
+ ${my_bootstrap_sql}
+ COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_SOURCE_DIR}/scripts
+ cmd /c copy mysql_system_tables.sql+mysql_system_tables_data.sql+fill_help_tables.sql ${native_outfile}
+ DEPENDS
+ ${CMAKE_SOURCE_DIR}/scripts/mysql_system_tables.sql
+ ${CMAKE_SOURCE_DIR}/scripts/mysql_system_tables_data.sql
+ ${CMAKE_SOURCE_DIR}/scripts/fill_help_tables.sql
+ )
+
+ADD_CUSTOM_COMMAND(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mysql_bootstrap_sql.c
+ COMMAND comp_sql
+ mysql_bootstrap_sql
+ ${CMAKE_CURRENT_BINARY_DIR}/my_bootstrap.sql
+ mysql_bootstrap_sql.c
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS comp_sql ${my_bootstrap_sql})
+
+ MYSQL_ADD_EXECUTABLE(mysql_install_db
+ mysql_install_db.cc
+ ${CMAKE_CURRENT_BINARY_DIR}/mysql_bootstrap_sql.c
+ COMPONENT Server)
+ TARGET_LINK_LIBRARIES(mysql_install_db mysys strings dbug)
+
+ ADD_LIBRARY(winservice STATIC winservice.c)
+
+ MYSQL_ADD_EXECUTABLE(mysql_upgrade_service
+ mysql_upgrade_service.cc
+ COMPONENT Server)
+
+ TARGET_LINK_LIBRARIES(mysql_upgrade_service mysys strings dbug winservice)
+
+ # mysql_install_db should be in the same directory as mysqld
+ # to work correctly
+ GET_TARGET_PROPERTY(MYSQLD_EXECUTABLE mysqld LOCATION)
+ IF(NOT MYSQLD_EXECUTABLE)
+ MESSAGE(FATAL_ERROR "Unexpected")
+ ENDIF()
+ENDIF()
+
+# We need to create empty directories (data/test) the installation.
+# This does not work with current CPack due to http://www.cmake.org/Bug/view.php?id=8767
+# Avoid completely empty directories and install dummy file instead.
+SET(DUMMY_FILE ${CMAKE_CURRENT_BINARY_DIR}/.empty )
+FILE(WRITE ${DUMMY_FILE} "")
+INSTALL(FILES ${DUMMY_FILE} DESTINATION data/test COMPONENT DataFiles)
+
+# Install initial database on windows
+IF(NOT CMAKE_CROSSCOMPILING)
+ GET_TARGET_PROPERTY(MYSQLD_EXECUTABLE mysqld LOCATION)
+ENDIF()
+IF(WIN32 AND MYSQLD_EXECUTABLE)
+ CONFIGURE_FILE(
+ ${CMAKE_SOURCE_DIR}/win/cmake/create_initial_db.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake
+ @ONLY
+ )
+
+ IF(MSVC_IDE OR CMAKE_GENERATOR MATCHES "Xcode")
+ SET (CONFIG_PARAM -DCONFIG=${CMAKE_CFG_INTDIR})
+ ENDIF()
+ MAKE_DIRECTORY(${CMAKE_CURRENT_BINARY_DIR}/data)
+ ADD_CUSTOM_COMMAND(
+ OUTPUT initdb.dep
+ COMMAND ${CMAKE_COMMAND}
+ ${CONFIG_PARAM} -P ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data
+ COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep
+ DEPENDS mysqld
+ )
+ ADD_CUSTOM_TARGET(initial_database
+ ALL
+ DEPENDS initdb.dep
+ )
+ INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data DESTINATION .
+ COMPONENT DataFiles PATTERN "initdb.dep" EXCLUDE PATTERN "bootstrap.sql" EXCLUDE)
+ELSE()
+ # Not windows or cross compiling, just install an empty directory
+ INSTALL(FILES ${DUMMY_FILE} DESTINATION data/mysql COMPONENT DataFiles)
+ENDIF()
diff --git a/sql/Makefile.am b/sql/Makefile.am
index c413f8ce771..181924e9cec 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -49,7 +49,7 @@ mysqld_LDADD = libndb.la \
noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
item_strfunc.h item_timefunc.h \
- item_xmlfunc.h \
+ item_xmlfunc.h sql_plugin_services.h \
item_create.h item_subselect.h item_row.h \
mysql_priv.h item_geofunc.h sql_bitmap.h \
procedure.h sql_class.h sql_lex.h sql_list.h \
@@ -78,7 +78,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.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
+ contributors.h sql_servers.h \
+ create_options.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \
@@ -124,9 +125,9 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
sql_plugin.cc sql_binlog.cc \
sql_builtin.cc sql_tablespace.cc partition_info.cc \
sql_servers.cc event_parse_data.cc \
- opt_table_elimination.cc
+ opt_table_elimination.cc create_options.cc
-nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c
+nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c client_plugin.c
libndb_la_CPPFLAGS= @ndbcluster_includes@
libndb_la_SOURCES= ha_ndbcluster.cc \
@@ -140,18 +141,19 @@ mysql_tzinfo_to_sql_SOURCES = tztime.cc
mysql_tzinfo_to_sql_CXXFLAGS= -DTZINFO2SQL
DEFS = -DMYSQL_SERVER \
- -DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \
- -DMYSQL_DATADIR="\"$(MYSQLDATAdir)\"" \
- -DSHAREDIR="\"$(MYSQLSHAREdir)\"" \
- -DPLUGINDIR="\"$(pkgplugindir)\"" \
+ -DDEFAULT_MYSQL_HOME='"$(MYSQLBASEdir)"' \
+ -DMYSQL_DATADIR='"$(MYSQLDATAdir)"' \
+ -DSHAREDIR='"$(MYSQLSHAREdir)"' \
+ -DPLUGINDIR='"$(pkgplugindir)"' \
-DHAVE_EVENT_SCHEDULER \
@DEFS@
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 \
+ nt_servc.cc nt_servc.h mysql_install_db.cc mysql_upgrade_service.cc \
message.mc message.h message.rc MSG00001.bin \
+ winservice.c winservice.h \
CMakeLists.txt
CLEANFILES = lex_hash.h sql_yacc.output link_sources
@@ -167,6 +169,8 @@ link_sources:
@LN_CP_F@ $(top_srcdir)/sql-common/pack.c pack.c
rm -f client.c
@LN_CP_F@ $(top_srcdir)/sql-common/client.c client.c
+ rm -f client_plugin.c
+ @LN_CP_F@ $(top_srcdir)/sql-common/client_plugin.c client_plugin.c
rm -f my_time.c
@LN_CP_F@ $(top_srcdir)/sql-common/my_time.c my_time.c
rm -f my_user.c
diff --git a/sql/authors.h b/sql/authors.h
index 9f88b97c3ad..3925696d640 100644
--- a/sql/authors.h
+++ b/sql/authors.h
@@ -36,23 +36,35 @@ struct show_table_authors_st {
*/
struct show_table_authors_st show_table_authors[]= {
+ { "Michael (Monty) Widenius", "Tusby, Finland",
+ "Lead developer and main author" },
+ { "David Axmark", "London, England",
+ "MySQL founder; Small stuff long time ago, Monty ripped it out!" },
+ { "Sergei Golubchik", "Kerpen, Germany",
+ "Full-text search, precision math" },
+ { "Igor Babaev", "Bellevue, USA", "Optimizer, keycache, core work"},
+ { "Sergey Petrunia", "St. Petersburg, Russia", "Optimizer"},
+ { "Oleksandr Byelkin", "Lugansk, Ukraine",
+ "Query Cache (4.0), Subqueries (4.1), Views (5.0)" },
{ "Brian (Krow) Aker", "Seattle, WA, USA",
"Architecture, archive, federated, bunch of little stuff :)" },
- { "Venu Anuganti", "", "Client/server protocol (4.1)" },
- { "David Axmark", "Uppsala, Sweden",
- "Small stuff long time ago, Monty ripped it out!" },
+ { "Kristian Nielsen", "Copenhagen, Denmark",
+ "General build stuff," },
{ "Alexander (Bar) Barkov", "Izhevsk, Russia",
"Unicode and character sets (4.1)" },
+ { "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" },
+ { "Venu Anuganti", "", "Client/server protocol (4.1)" },
+ { "Konstantin Osipov", "Moscow, Russia",
+ "Prepared statements (4.1), Cursors (5.0)" },
+ { "Dmitri Lenev", "Moscow, Russia",
+ "Time zones support (4.1), Triggers (5.0)" },
{ "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" },
@@ -69,8 +81,6 @@ struct show_table_authors_st show_table_authors[]= {
{ "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" },
@@ -86,8 +96,6 @@ struct show_table_authors_st show_table_authors[]= {
{ "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" },
@@ -99,8 +107,6 @@ struct show_table_authors_st show_table_authors[]= {
{ "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",
@@ -108,8 +114,6 @@ struct show_table_authors_st show_table_authors[]= {
{ "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" },
@@ -147,9 +151,9 @@ struct show_table_authors_st show_table_authors[]= {
{ "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" },
+ {"Mark Mark Callaghan", "Texas, USA", "Statistics patches"},
+ {"Percona", "CA, USA", "Microslow patches"},
{NULL, NULL, NULL}
};
diff --git a/sql/client_settings.h b/sql/client_settings.h
index a0bece9949f..a76955cd0f7 100644
--- a/sql/client_settings.h
+++ b/sql/client_settings.h
@@ -18,6 +18,7 @@
#include <thr_alarm.h>
+#include <sql_common.h>
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | \
CLIENT_SECURE_CONNECTION | CLIENT_TRANSACTIONS | \
@@ -31,10 +32,10 @@
#define mysql_master_send_query(A, B, C) 1
#define mysql_slave_send_query(A, B, C) 1
#define mysql_rpl_probe(mysql) 0
-#undef HAVE_SMEM
#undef _CUSTOMCONFIG_
-#define mysql_server_init(a,b,c) 0
+#define mysql_server_init(a,b,c) mysql_client_plugin_init()
+#define mysql_server_end() mysql_client_plugin_deinit()
#ifdef HAVE_REPLICATION
C_MODE_START
diff --git a/sql/create_options.cc b/sql/create_options.cc
new file mode 100644
index 00000000000..42c69436897
--- /dev/null
+++ b/sql/create_options.cc
@@ -0,0 +1,616 @@
+/* Copyright (C) 2010 Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You 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
+
+ Engine defined options of tables/fields/keys in CREATE/ALTER TABLE.
+*/
+
+#include "mysql_priv.h"
+#include "create_options.h"
+#include <my_getopt.h>
+
+#define FRM_QUOTED_VALUE 0x8000
+
+/**
+ Links this item to the given list end
+
+ @param start The list beginning or NULL
+ @param end The list last element or does not matter
+*/
+
+void engine_option_value::link(engine_option_value **start,
+ engine_option_value **end)
+{
+ DBUG_ENTER("engine_option_value::link");
+ DBUG_PRINT("enter", ("name: '%s' (%u) value: '%s' (%u)",
+ name.str, (uint) name.length,
+ value.str, (uint) value.length));
+ engine_option_value *opt;
+ /* check duplicates to avoid writing them to frm*/
+ for(opt= *start;
+ opt && ((opt->parsed && !opt->value.str) ||
+ my_strnncoll(system_charset_info,
+ (uchar *)name.str, name.length,
+ (uchar*)opt->name.str, opt->name.length));
+ opt= opt->next) /* no-op */;
+ if (opt)
+ {
+ opt->value.str= NULL; /* remove previous value */
+ opt->parsed= TRUE; /* and don't issue warnings for it anymore */
+ }
+ /*
+ Add this option to the end of the list
+
+ @note: We add even if it is opt->value.str == NULL because it can be
+ ALTER TABLE to remove the option.
+ */
+ if (*start)
+ {
+ (*end)->next= this;
+ *end= this;
+ }
+ else
+ {
+ /*
+ note that is *start == 0, the value of *end does not matter,
+ it can be uninitialized.
+ */
+ *start= *end= this;
+ }
+ DBUG_VOID_RETURN;
+}
+
+static bool report_wrong_value(THD *thd, const char *name, const char *val,
+ my_bool suppress_warning)
+{
+ if (suppress_warning)
+ return 0;
+
+ if (!(thd->variables.sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) &&
+ !thd->slave_thread)
+ {
+ my_error(ER_BAD_OPTION_VALUE, MYF(0), val, name);
+ return 1;
+ }
+
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_OPTION_VALUE,
+ ER(ER_BAD_OPTION_VALUE), val, name);
+ return 0;
+}
+
+static bool report_unknown_option(THD *thd, engine_option_value *val,
+ my_bool suppress_warning)
+{
+ DBUG_ENTER("report_unknown_option");
+
+ if (val->parsed || suppress_warning)
+ {
+ DBUG_PRINT("info", ("parsed => exiting"));
+ DBUG_RETURN(FALSE);
+ }
+
+ if (!(thd->variables.sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) &&
+ !thd->slave_thread)
+ {
+ my_error(ER_UNKNOWN_OPTION, MYF(0), val->name.str);
+ DBUG_RETURN(TRUE);
+ }
+
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_UNKNOWN_OPTION, ER(ER_UNKNOWN_OPTION), val->name.str);
+ DBUG_RETURN(FALSE);
+}
+
+static bool set_one_value(ha_create_table_option *opt,
+ THD *thd, LEX_STRING *value, void *base,
+ my_bool suppress_warning,
+ MEM_ROOT *root)
+{
+ DBUG_ENTER("set_one_value");
+ DBUG_PRINT("enter", ("opt: 0x%lx type: %u name '%s' value: '%s'",
+ (ulong) opt,
+ opt->type, opt->name,
+ (value->str ? value->str : "<DEFAULT>")));
+ switch (opt->type)
+ {
+ case HA_OPTION_TYPE_ULL:
+ {
+ ulonglong *val= (ulonglong*)((char*)base + opt->offset);
+ if (!value->str)
+ {
+ *val= opt->def_value;
+ DBUG_RETURN(0);
+ }
+
+ my_option optp=
+ { opt->name, 1, 0, (uchar **)val, 0, 0, GET_ULL,
+ REQUIRED_ARG, opt->def_value, opt->min_value, opt->max_value,
+ 0, (long) opt->block_size, 0};
+
+ ulonglong orig_val= strtoull(value->str, NULL, 10);
+ my_bool unused;
+ *val= orig_val;
+ *val= getopt_ull_limit_value(*val, &optp, &unused);
+ if (*val == orig_val)
+ DBUG_RETURN(0);
+
+ DBUG_RETURN(report_wrong_value(thd, opt->name, value->str,
+ suppress_warning));
+ }
+ case HA_OPTION_TYPE_STRING:
+ {
+ char **val= (char **)((char *)base + opt->offset);
+ if (!value->str)
+ {
+ *val= 0;
+ DBUG_RETURN(0);
+ }
+
+ if (!(*val= strmake_root(root, value->str, value->length)))
+ DBUG_RETURN(1);
+ DBUG_RETURN(0);
+ }
+ case HA_OPTION_TYPE_ENUM:
+ {
+ uint *val= (uint *)((char *)base + opt->offset), num;
+
+ *val= (uint) opt->def_value;
+ if (!value->str)
+ DBUG_RETURN(0);
+
+ const char *start= opt->values, *end;
+
+ num= 0;
+ while (*start)
+ {
+ for (end=start;
+ *end && *end != ',';
+ end+= my_mbcharlen(system_charset_info, *end)) /* no-op */;
+ if (!my_strnncoll(system_charset_info,
+ (uchar*)start, end-start,
+ (uchar*)value->str, value->length))
+ {
+ *val= num;
+ DBUG_RETURN(0);
+ }
+ if (*end)
+ end++;
+ start= end;
+ num++;
+ }
+
+ DBUG_RETURN(report_wrong_value(thd, opt->name, value->str,
+ suppress_warning));
+ }
+ case HA_OPTION_TYPE_BOOL:
+ {
+ bool *val= (bool *)((char *)base + opt->offset);
+ *val= opt->def_value;
+
+ if (!value->str)
+ DBUG_RETURN(0);
+
+ if (!my_strnncoll(system_charset_info,
+ (const uchar*)"NO", 2,
+ (uchar *)value->str, value->length) ||
+ !my_strnncoll(system_charset_info,
+ (const uchar*)"OFF", 3,
+ (uchar *)value->str, value->length) ||
+ !my_strnncoll(system_charset_info,
+ (const uchar*)"0", 1,
+ (uchar *)value->str, value->length))
+ {
+ *val= FALSE;
+ DBUG_RETURN(FALSE);
+ }
+
+ if (!my_strnncoll(system_charset_info,
+ (const uchar*)"YES", 3,
+ (uchar *)value->str, value->length) ||
+ !my_strnncoll(system_charset_info,
+ (const uchar*)"ON", 2,
+ (uchar *)value->str, value->length) ||
+ !my_strnncoll(system_charset_info,
+ (const uchar*)"1", 1,
+ (uchar *)value->str, value->length))
+ {
+ *val= TRUE;
+ DBUG_RETURN(FALSE);
+ }
+
+ DBUG_RETURN(report_wrong_value(thd, opt->name, value->str,
+ suppress_warning));
+ }
+ }
+ DBUG_ASSERT(0);
+ my_error(ER_UNKNOWN_ERROR, MYF(0));
+ DBUG_RETURN(1);
+}
+
+static const size_t ha_option_type_sizeof[]=
+{ sizeof(ulonglong), sizeof(char *), sizeof(uint), sizeof(bool)};
+
+/**
+ Creates option structure and parses list of options in it
+
+ @param thd thread handler
+ @param option_struct where to store pointer on the option struct
+ @param option_list list of options given by user
+ @param rules list of option description by engine
+ @param suppress_warning second parse so we do not need warnings
+ @param root MEM_ROOT where allocate memory
+
+ @retval TRUE Error
+ @retval FALSE OK
+*/
+
+my_bool parse_option_list(THD* thd, void *option_struct_arg,
+ engine_option_value *option_list,
+ ha_create_table_option *rules,
+ my_bool suppress_warning,
+ MEM_ROOT *root)
+{
+ ha_create_table_option *opt;
+ size_t option_struct_size= 0;
+ engine_option_value *val= option_list;
+ void **option_struct= (void**)option_struct_arg;
+ DBUG_ENTER("parse_option_list");
+ DBUG_PRINT("enter",
+ ("struct: 0x%lx list: 0x%lx rules: 0x%lx suppres %u root 0x%lx",
+ (ulong) *option_struct, (ulong)option_list, (ulong)rules,
+ (uint) suppress_warning, (ulong) root));
+
+ if (rules)
+ {
+ LEX_STRING default_val= {NULL, 0};
+ for (opt= rules; opt->name; opt++)
+ set_if_bigger(option_struct_size, opt->offset +
+ ha_option_type_sizeof[opt->type]);
+
+ *option_struct= alloc_root(root, option_struct_size);
+
+ /* set all values to default */
+ for (opt= rules; opt->name; opt++)
+ set_one_value(opt, thd, &default_val, *option_struct,
+ suppress_warning, root);
+ }
+
+ for (; val; val= val->next)
+ {
+ for (opt= rules; opt && opt->name; opt++)
+ {
+ if (my_strnncoll(system_charset_info,
+ (uchar*)opt->name, opt->name_length,
+ (uchar*)val->name.str, val->name.length))
+ continue;
+
+ if (set_one_value(opt, thd, &val->value,
+ *option_struct, suppress_warning || val->parsed, root))
+ DBUG_RETURN(TRUE);
+ val->parsed= true;
+ break;
+ }
+ if (report_unknown_option(thd, val, suppress_warning))
+ DBUG_RETURN(TRUE);
+ val->parsed= true;
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Parses all table/fields/keys options
+
+ @param thd thread handler
+ @param file handler of the table
+ @parem share descriptor of the table
+
+ @retval TRUE Error
+ @retval FALSE OK
+*/
+
+my_bool parse_engine_table_options(THD *thd, handlerton *ht,
+ TABLE_SHARE *share)
+{
+ MEM_ROOT *root= &share->mem_root;
+ DBUG_ENTER("parse_engine_table_options");
+
+ if (parse_option_list(thd, &share->option_struct, share->option_list,
+ ht->table_options, TRUE, root))
+ DBUG_RETURN(TRUE);
+
+ for (Field **field= share->field; *field; field++)
+ {
+ if (parse_option_list(thd, &(*field)->option_struct, (*field)->option_list,
+ ht->field_options, TRUE, root))
+ DBUG_RETURN(TRUE);
+ }
+
+ for (uint index= 0; index < share->keys; index ++)
+ {
+ if (parse_option_list(thd, &share->key_info[index].option_struct,
+ share->key_info[index].option_list,
+ ht->index_options, TRUE, root))
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Returns representation length of key and value in the frm file
+*/
+
+uint engine_option_value::frm_length()
+{
+ /*
+ 1 byte - name length
+ 2 bytes - value length
+
+ if value.str is NULL, this option is not written to frm (=DEFAULT)
+ */
+ return value.str ? 1 + name.length + 2 + value.length : 0;
+}
+
+
+/**
+ Returns length of representation of option list in the frm file
+*/
+
+static uint option_list_frm_length(engine_option_value *opt)
+{
+ uint res= 0;
+
+ for (; opt; opt= opt->next)
+ res+= opt->frm_length();
+
+ return res;
+}
+
+
+/**
+ Calculates length of options image in the .frm
+
+ @param table_option_list list of table options
+ @param create_fields field descriptors list
+ @param keys number of keys
+ @param key_info array of key descriptors
+
+ @returns length of image in frm
+*/
+
+uint engine_table_options_frm_length(engine_option_value *table_option_list,
+ List<Create_field> &create_fields,
+ uint keys, KEY *key_info)
+{
+ List_iterator<Create_field> it(create_fields);
+ Create_field *field;
+ uint res, index;
+ DBUG_ENTER("engine_table_options_frm_length");
+
+ res= option_list_frm_length(table_option_list);
+
+ while ((field= it++))
+ res+= option_list_frm_length(field->option_list);
+
+ for (index= 0; index < keys; index++, key_info++)
+ res+= option_list_frm_length(key_info->option_list);
+
+ /*
+ if there's at least one option somewhere (res > 0)
+ we write option lists for all fields and keys, zero-terminated.
+ If there're no options we write nothing at all (backward compatibility)
+ */
+ DBUG_RETURN(res ? res + 1 + create_fields.elements + keys : 0);
+}
+
+
+/**
+ Writes image of the key and value to the frm image buffer
+
+ @param buff pointer to the buffer free space beginning
+
+ @returns pointer to byte after last recorded in the buffer
+*/
+
+uchar *engine_option_value::frm_image(uchar *buff)
+{
+ if (value.str)
+ {
+ *buff++= name.length;
+ memcpy(buff, name.str, name.length);
+ buff+= name.length;
+ int2store(buff, value.length | (quoted_value ? FRM_QUOTED_VALUE : 0));
+ buff+= 2;
+ memcpy(buff, (const uchar *) value.str, value.length);
+ buff+= value.length;
+ }
+ return buff;
+}
+
+/**
+ Writes image of the key and value to the frm image buffer
+
+ @param buff pointer to the buffer to store the options in
+ @param opt list of options;
+
+ @returns pointer to the end of the stored data in the buffer
+*/
+static uchar *option_list_frm_image(uchar *buff, engine_option_value *opt)
+{
+ for (; opt; opt= opt->next)
+ buff= opt->frm_image(buff);
+
+ *buff++= 0;
+ return buff;
+}
+
+
+/**
+ Writes options image in the .frm buffer
+
+ @param buff pointer to the buffer
+ @param table_option_list list of table options
+ @param create_fields field descriptors list
+ @param keys number of keys
+ @param key_info array of key descriptors
+
+ @returns pointer to byte after last recorded in the buffer
+*/
+
+uchar *engine_table_options_frm_image(uchar *buff,
+ engine_option_value *table_option_list,
+ List<Create_field> &create_fields,
+ uint keys, KEY *key_info)
+{
+ List_iterator<Create_field> it(create_fields);
+ Create_field *field;
+ KEY *key_info_end= key_info + keys;
+ DBUG_ENTER("engine_table_options_frm_image");
+
+ buff= option_list_frm_image(buff, table_option_list);
+
+ while ((field= it++))
+ buff= option_list_frm_image(buff, field->option_list);
+
+ while (key_info < key_info_end)
+ buff= option_list_frm_image(buff, (key_info++)->option_list);
+
+ DBUG_RETURN(buff);
+}
+
+/**
+ Reads name and value from buffer, then link it in the list
+
+ @param buff the buffer to read from
+ @param start The list beginning or NULL
+ @param end The list last element or does not matter
+ @param root MEM_ROOT for allocating
+
+ @returns pointer to byte after last recorded in the buffer
+*/
+uchar *engine_option_value::frm_read(const uchar *buff, engine_option_value **start,
+ engine_option_value **end, MEM_ROOT *root)
+{
+ LEX_STRING name, value;
+ uint len;
+
+ name.length= buff[0];
+ buff++;
+ if (!(name.str= strmake_root(root, (const char*)buff, name.length)))
+ return NULL;
+ buff+= name.length;
+ len= uint2korr(buff);
+ value.length= len & ~FRM_QUOTED_VALUE;
+ buff+= 2;
+ if (!(value.str= strmake_root(root, (const char*)buff, value.length)))
+ return NULL;
+ buff+= value.length;
+
+ engine_option_value *ptr=new (root)
+ engine_option_value(name, value, len & FRM_QUOTED_VALUE, start, end);
+ if (!ptr)
+ return NULL;
+
+ return (uchar *)buff;
+}
+
+
+/**
+ Reads options from this buffer
+
+ @param buff the buffer to read from
+ @param length buffer length
+ @param share table descriptor
+ @param root MEM_ROOT for allocating
+
+ @retval TRUE Error
+ @retval FALSE OK
+*/
+
+my_bool engine_table_options_frm_read(const uchar *buff, uint length,
+ TABLE_SHARE *share)
+{
+ const uchar *buff_end= buff + length;
+ engine_option_value *end;
+ MEM_ROOT *root= &share->mem_root;
+ uint count;
+ DBUG_ENTER("engine_table_options_frm_read");
+
+ while (buff < buff_end && *buff)
+ {
+ if (!(buff= engine_option_value::frm_read(buff, &share->option_list, &end,
+ root)))
+ DBUG_RETURN(TRUE);
+ }
+ buff++;
+
+ for (count=0; count < share->fields; count++)
+ {
+ while (buff < buff_end && *buff)
+ {
+ if (!(buff= engine_option_value::frm_read(buff,
+ &share->field[count]->option_list,
+ &end, root)))
+ DBUG_RETURN(TRUE);
+ }
+ buff++;
+ }
+
+ for (count=0; count < share->keys; count++)
+ {
+ while (buff < buff_end && *buff)
+ {
+ if (!(buff= engine_option_value::frm_read(buff,
+ &share->key_info[count].option_list,
+ &end, root)))
+ DBUG_RETURN(TRUE);
+ }
+ buff++;
+ }
+
+ if (buff < buff_end)
+ sql_print_warning("Table '%s' was created in a later MariaDB version - "
+ "unknown table attributes were ignored",
+ share->table_name.str);
+
+ DBUG_RETURN(buff > buff_end);
+}
+
+/**
+ Merges two lists of engine_option_value's with duplicate removal.
+*/
+
+engine_option_value *merge_engine_table_options(engine_option_value *first,
+ engine_option_value *second,
+ MEM_ROOT *root)
+{
+ engine_option_value *end, *opt;
+ DBUG_ENTER("merge_engine_table_options");
+ LINT_INIT(end);
+
+ /* find last element */
+ if (first && second)
+ for (end= first; end->next; end= end->next) /* no-op */;
+
+ for (opt= second; opt; opt= opt->next)
+ new (root) engine_option_value(opt->name, opt->value, opt->quoted_value,
+ &first, &end);
+ DBUG_RETURN(first);
+}
diff --git a/sql/create_options.h b/sql/create_options.h
new file mode 100644
index 00000000000..174abb1a59a
--- /dev/null
+++ b/sql/create_options.h
@@ -0,0 +1,92 @@
+/* Copyright (C) 2010 Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You 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
+
+ Engine defined options of tables/fields/keys in CREATE/ALTER TABLE.
+*/
+
+#ifndef SQL_CREATE_OPTIONS_INCLUDED
+#define SQL_CREATE_OPTIONS_INCLUDED
+
+#include "handler.h"
+
+class engine_option_value: public Sql_alloc
+{
+ public:
+ LEX_STRING name;
+ LEX_STRING value;
+ engine_option_value *next; ///< parser puts them in a FIFO linked list
+ bool parsed; ///< to detect unrecognized options
+ bool quoted_value; ///< option=VAL vs. option='VAL'
+
+ engine_option_value(LEX_STRING &name_arg, LEX_STRING &value_arg, bool quoted,
+ engine_option_value **start, engine_option_value **end) :
+ name(name_arg), value(value_arg),
+ next(NULL), parsed(false), quoted_value(quoted)
+ {
+ link(start, end);
+ }
+ engine_option_value(LEX_STRING &name_arg,
+ engine_option_value **start, engine_option_value **end) :
+ name(name_arg), value(null_lex_str),
+ next(NULL), parsed(false), quoted_value(false)
+ {
+ link(start, end);
+ }
+ engine_option_value(LEX_STRING &name_arg, ulonglong value_arg,
+ engine_option_value **start, engine_option_value **end,
+ MEM_ROOT *root) :
+ name(name_arg), next(NULL), parsed(false), quoted_value(false)
+ {
+ if ((value.str= (char *)alloc_root(root, 22)))
+ {
+ value.length= longlong10_to_str(value_arg, value.str, 10) - value.str;
+ link(start, end);
+ }
+ }
+ static uchar *frm_read(const uchar *buff, engine_option_value **start,
+ engine_option_value **end, MEM_ROOT *root);
+ void link(engine_option_value **start, engine_option_value **end);
+ uint frm_length();
+ uchar *frm_image(uchar *buff);
+};
+
+typedef struct st_key KEY;
+class Create_field;
+
+my_bool parse_engine_table_options(THD *thd, handlerton *ht,
+ TABLE_SHARE *share);
+my_bool parse_option_list(THD* thd, void *option_struct,
+ engine_option_value *option_list,
+ ha_create_table_option *rules,
+ my_bool suppress_warning,
+ MEM_ROOT *root);
+my_bool engine_table_options_frm_read(const uchar *buff,
+ uint length,
+ TABLE_SHARE *share);
+engine_option_value *merge_engine_table_options(engine_option_value *source,
+ engine_option_value *changes,
+ MEM_ROOT *root);
+
+uint engine_table_options_frm_length(engine_option_value *table_option_list,
+ List<Create_field> &create_fields,
+ uint keys, KEY *key_info);
+uchar *engine_table_options_frm_image(uchar *buff,
+ engine_option_value *table_option_list,
+ List<Create_field> &create_fields,
+ uint keys, KEY *key_info);
+#endif
diff --git a/sql/derror.cc b/sql/derror.cc
index 55e9b49dddc..9f05c95157b 100644
--- a/sql/derror.cc
+++ b/sql/derror.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+ Copyright (C) 2011 Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,6 +24,7 @@
#include "mysql_priv.h"
#include "mysys_err.h"
+static bool check_error_mesg(const char *file_name, const char **errmsg);
static bool read_texts(const char *file_name,const char ***point,
uint error_messages);
static void init_myfunc_errs(void);
@@ -31,9 +33,12 @@ static void init_myfunc_errs(void);
Read messages from errorfile.
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.
+
+ If it fails to load the messages:
+ - If we already have error messages loaded, keep the old ones and
+ return FALSE(ok)
+ - Initializing the errmesg pointer to an array of empty strings
+ and return TRUE (error)
@retval
FALSE OK
@@ -43,25 +48,44 @@ static void init_myfunc_errs(void);
bool init_errmessage(void)
{
- const char **errmsgs, **ptr;
+ const char **errmsgs, **ptr, **org_errmsgs;
+ bool error= FALSE;
DBUG_ENTER("init_errmessage");
/*
Get a pointer to the old error messages pointer array.
read_texts() tries to free it.
*/
- errmsgs= my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST);
+ org_errmsgs= my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST);
/* Read messages from file. */
- if (read_texts(ERRMSG_FILE, &errmsgs, ER_ERROR_LAST - ER_ERROR_FIRST + 1) &&
- !errmsgs)
+ if (read_texts(ERRMSG_FILE, &errmsgs, ER_ERROR_LAST - ER_ERROR_FIRST + 1) ||
+ check_error_mesg(ERRMSG_FILE, 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= "";
+ x_free(errmsgs);
+
+ if (org_errmsgs)
+ {
+ /* Use old error messages */
+ errmsgs= org_errmsgs;
+ }
+ else
+ {
+ /*
+ No error messages. Create a temporary empty error message so
+ that we don't get a crash if some code wrongly tries to access
+ a non existing error message.
+ */
+ 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= "";
+ error= TRUE;
+ }
}
+ else
+ x_free(org_errmsgs); // Free old language
/* Register messages for use with my_error(). */
if (my_error_register(errmsgs, ER_ERROR_FIRST, ER_ERROR_LAST))
@@ -72,7 +96,29 @@ bool init_errmessage(void)
errmesg= errmsgs; /* Init global variabel */
init_myfunc_errs(); /* Init myfunc messages */
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Check the error messages array contains all relevant error messages
+*/
+
+static bool check_error_mesg(const char *file_name, const char **errmsg)
+{
+ /*
+ The last MySQL error message can't be an empty string; If it is,
+ it means that the error file doesn't contain all MySQL messages
+ and is probably from an older version of MySQL / MariaDB.
+ */
+ if (errmsg[ER_ERROR_LAST - ER_ERROR_FIRST][0] == 0)
+ {
+ sql_print_error("Error message file '%s' is probably from and older "
+ "version of MariaDB / MYSQL as it doesn't contain all "
+ "error messages", file_name);
+ return 1;
+ }
+ return 0;
}
@@ -97,6 +143,8 @@ static bool read_texts(const char *file_name,const char ***point,
uchar head[32],*pos;
DBUG_ENTER("read_texts");
+ *point= 0;
+
LINT_INIT(buff);
funktpos=0;
if ((file=my_open(fn_format(name,file_name,language,"",4),
@@ -106,6 +154,7 @@ static bool read_texts(const char *file_name,const char ***point,
funktpos=1;
if (my_read(file,(uchar*) head,32,MYF(MY_NABP))) goto err;
+ funktpos=2;
if (head[0] != (uchar) 254 || head[1] != (uchar) 254 ||
head[2] != 2 || head[3] != 1)
goto err; /* purecov: inspected */
@@ -131,19 +180,16 @@ Please install the latest version of this file.",name);
if (count < error_messages)
{
sql_print_error("\
-Error message file '%s' had only %d error messages,\n\
-but it should contain at least %d error messages.\n\
-Check that the above file is the right version for this program!",
+Error message file '%s' had only %d error messages, but it should contain at least %d error messages.\nCheck that the above file is the right version for this program!",
name,count,error_messages);
VOID(my_close(file,MYF(MY_WME)));
DBUG_RETURN(1);
}
- x_free((uchar*) *point); /* Free old language */
if (!(*point= (const char**)
my_malloc((size_t) (length+count*sizeof(char*)),MYF(0))))
{
- funktpos=2; /* purecov: inspected */
+ funktpos=3; /* purecov: inspected */
goto err; /* purecov: inspected */
}
buff= (uchar*) (*point + count);
@@ -166,7 +212,8 @@ Check that the above file is the right version for this program!",
DBUG_RETURN(0);
err:
- sql_print_error((funktpos == 2) ? "Not enough memory for messagefile '%s'" :
+ sql_print_error((funktpos == 3) ? "Not enough memory for messagefile '%s'" :
+ (funktpos == 2) ? "Incompatible header in messagefile '%s'. Probably from another version of MariaDB" :
((funktpos == 1) ? "Can't read from messagefile '%s'" :
"Can't find messagefile '%s'"), name);
err1:
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
index 5c4d9e94a6e..e4436afd057 100644
--- a/sql/event_data_objects.cc
+++ b/sql/event_data_objects.cc
@@ -1369,7 +1369,7 @@ Event_job_data::execute(THD *thd, bool drop)
DBUG_ENTER("Event_job_data::execute");
- mysql_reset_thd_for_next_command(thd);
+ mysql_reset_thd_for_next_command(thd, 0);
/*
MySQL parser currently assumes that current database is either
diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h
index e32077b9c97..1ca619b8ed6 100644
--- a/sql/event_data_objects.h
+++ b/sql/event_data_objects.h
@@ -89,9 +89,9 @@ public:
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;
+ bool starts_null;
+ bool ends_null;
+ bool execute_at_null;
longlong expression;
interval_type interval;
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index 7560d490641..960b5933a99 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -107,7 +107,8 @@ const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
{
{ 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',"
+ "'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY',"
+ "'NO_UNSIGNED_SUBTRACTION',"
"'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
"'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40',"
"'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
@@ -213,7 +214,7 @@ mysql_event_fill_row(THD *thd,
Safety: this can only happen if someone started the server
and then altered mysql.event.
*/
- my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED, MYF(0), table->alias,
+ my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED, MYF(0), table->alias.c_ptr(),
(int) ET_FIELD_COUNT, table->s->fields);
DBUG_RETURN(TRUE);
}
@@ -431,17 +432,18 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
}
key_copy(key_buf, event_table->record[0], key_info, key_len);
- if (!(ret= event_table->file->index_read_map(event_table->record[0], key_buf,
- (key_part_map)1,
- HA_READ_KEY_EXACT)))
+ if (!(ret= event_table->file->ha_index_read_map(event_table->record[0],
+ key_buf,
+ (key_part_map) 1,
+ HA_READ_KEY_EXACT)))
{
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);
+ ret= event_table->file->ha_index_next_same(event_table->record[0],
+ key_buf, key_len);
} while (ret == 0);
}
DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
@@ -481,7 +483,8 @@ Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
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);
+ if (init_read_record(&read_record_info, thd, event_table, NULL, 1, 0, FALSE))
+ DBUG_RETURN(TRUE);
/*
rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE,
@@ -922,8 +925,9 @@ Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name,
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))
+ if (table->file->ha_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);
@@ -978,7 +982,9 @@ Event_db_repository::drop_events_by_field(THD *thd,
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);
+ if (init_read_record(&read_record_info, thd, table, NULL, 1, 0, FALSE))
+ goto end;
+
while (!ret && !(read_record_info.read_record(&read_record_info)) )
{
char *et_field= get_field(thd->mem_root, table->field[field]);
@@ -1000,8 +1006,9 @@ Event_db_repository::drop_events_by_field(THD *thd,
}
}
end_read_record(&read_record_info);
- close_thread_tables(thd);
+end:
+ close_thread_tables(thd);
DBUG_VOID_RETURN;
}
diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h
index e25cb6b85a4..8c028f0717f 100644
--- a/sql/event_db_repository.h
+++ b/sql/event_db_repository.h
@@ -75,7 +75,6 @@ public:
bool
create_event(THD *thd, Event_parse_data *parse_data, bool create_if_not,
bool *event_already_exists);
-
bool
update_event(THD *thd, Event_parse_data *parse_data, LEX_STRING *new_dbname,
LEX_STRING *new_name);
diff --git a/sql/event_parse_data.h b/sql/event_parse_data.h
index 228ca1bb0c9..ae834cf9a9f 100644
--- a/sql/event_parse_data.h
+++ b/sql/event_parse_data.h
@@ -73,9 +73,9 @@ public:
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;
+ bool starts_null;
+ bool ends_null;
+ bool execute_at_null;
sp_name *identifier;
Item* item_expression;
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index d68dc8ef479..b9585dc9c51 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -734,12 +734,14 @@ Event_queue::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
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);
-
+ if (!thd->killed)
+ {
+ 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;
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index fb66a50ee7f..7704ed3528d 100755
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -103,7 +103,7 @@ Event_worker_thread::print_warnings(THD *thd, Event_job_data *et)
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());
+ err_msg.c_ptr_safe());
}
DBUG_VOID_RETURN;
}
@@ -651,7 +651,14 @@ Event_scheduler::stop()
/* 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");
+
+ /*
+ Wait only 2 seconds, as there is a small chance the thread missed the
+ above awake() call and we may have to do it again
+ */
+ struct timespec top_time;
+ set_timespec(top_time, 2);
+ COND_STATE_WAIT(thd, &top_time, "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");
diff --git a/sql/events.cc b/sql/events.cc
index 6eaa27b3212..f0188bbb0ae 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -490,7 +490,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
of CURRENT_USER will be written into the binary log as the
definer for the SQL thread.
*/
- ret= write_bin_log(thd, TRUE, log_query.c_ptr(), log_query.length());
+ ret= write_bin_log(thd, TRUE, log_query.ptr(), log_query.length());
}
}
}
@@ -790,7 +790,7 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
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(),
+ protocol->store(show_str.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),
@@ -928,7 +928,7 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
*/
bool
-Events::init(my_bool opt_noacl_or_bootstrap)
+Events::init(bool opt_noacl_or_bootstrap)
{
THD *thd;
@@ -1225,7 +1225,12 @@ Events::load_events_from_db(THD *thd)
DBUG_RETURN(TRUE);
}
- init_read_record(&read_record_info, thd, table, NULL, 0, 1, FALSE);
+ if (init_read_record(&read_record_info, thd, table, NULL, 0, 1, FALSE))
+ {
+ close_thread_tables(thd);
+ DBUG_RETURN(TRUE);
+ }
+
while (!(read_record_info.read_record(&read_record_info)))
{
Event_queue_element *et;
diff --git a/sql/events.h b/sql/events.h
index 2bc87517748..a3c98d84f00 100644
--- a/sql/events.h
+++ b/sql/events.h
@@ -92,7 +92,7 @@ public:
get_db_repository() { return db_repository; }
static bool
- init(my_bool opt_noacl);
+ init(bool opt_noacl);
static void
deinit();
diff --git a/sql/field.cc b/sql/field.cc
index a8bb59ca417..2a52963afa9 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -59,7 +59,7 @@ const char field_separator=',';
((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)))
+#define ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED DBUG_ASSERT(!table || (!table->write_set || bitmap_is_set(table->write_set, field_index) || bitmap_is_set(table->vcol_set, field_index)))
/*
Rules for merging different types of fields in UNION
@@ -1286,7 +1286,7 @@ static bool test_if_real(const char *str,int length, CHARSET_INFO *cs)
This is used for printing bit_fields as numbers while debugging.
*/
-String *Field::val_int_as_str(String *val_buffer, my_bool unsigned_val)
+String *Field::val_int_as_str(String *val_buffer, bool unsigned_val)
{
ASSERT_COLUMN_MARKED_FOR_READ;
CHARSET_INFO *cs= &my_charset_bin;
@@ -1308,13 +1308,13 @@ String *Field::val_int_as_str(String *val_buffer, my_bool unsigned_val)
Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
uchar null_bit_arg,
utype unireg_check_arg, const char *field_name_arg)
- :ptr(ptr_arg), null_ptr(null_ptr_arg),
- table(0), orig_table(0), table_name(0),
- field_name(field_name_arg),
- 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)
+ :ptr(ptr_arg), null_ptr(null_ptr_arg), table(0), orig_table(0),
+ table_name(0), field_name(field_name_arg), option_list(0),
+ option_struct(0), key_start(0), part_of_key(0),
+ part_of_key_not_clustered(0), part_of_sortkey(0),
+ unireg_check(unireg_check_arg), field_length(length_arg),
+ null_bit(null_bit_arg), is_created_from_null_item(FALSE), vcol_info(0),
+ stored_in_db(TRUE)
{
flags=null_ptr ? 0: NOT_NULL_FLAG;
comment.str= (char*) "";
@@ -1538,9 +1538,9 @@ void Field::make_field(Send_field *field)
}
else
field->org_table_name= field->db_name= "";
- if (orig_table && orig_table->alias)
+ if (orig_table && orig_table->alias.ptr())
{
- field->table_name= orig_table->alias;
+ field->table_name= orig_table->alias.ptr();
field->org_col_name= field_name;
}
else
@@ -1615,7 +1615,7 @@ longlong Field::convert_decimal2longlong(const my_decimal *val,
int Field_num::store_decimal(const my_decimal *val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int err= 0;
longlong i= convert_decimal2longlong(val, unsigned_flag, &err);
return test(err | store(i, unsigned_flag));
@@ -1687,7 +1687,7 @@ void Field_num::make_field(Send_field *field)
int Field_str::store_decimal(const my_decimal *d)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
double val;
/* TODO: use decimal2string? */
int err= warn_if_overflow(my_decimal2double(E_DEC_FATAL_ERROR &
@@ -1763,7 +1763,7 @@ bool Field::get_time(MYSQL_TIME *ltime)
int Field::store_time(MYSQL_TIME *ltime, timestamp_type type_arg)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
char buff[MAX_DATE_STRING_REP_LENGTH];
uint length= (uint) my_TIME_to_str(ltime, buff);
return store(buff, length, &my_charset_bin);
@@ -1890,7 +1890,7 @@ void Field_decimal::overflow(bool negative)
int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
char buff[STRING_BUFFER_USUAL_SIZE];
String tmp(buff,sizeof(buff), &my_charset_bin);
const uchar *from= (uchar*) from_arg;
@@ -2256,7 +2256,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
int Field_decimal::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
if (unsigned_flag && nr < 0)
{
overflow(1);
@@ -2301,7 +2301,7 @@ int Field_decimal::store(double nr)
int Field_decimal::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
char buff[22];
uint length, int_part;
char fyllchar;
@@ -2581,7 +2581,7 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value,
bool Field_new_decimal::store_value(const my_decimal *decimal_value)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
DBUG_ENTER("Field_new_decimal::store_value");
#ifndef DBUG_OFF
@@ -2626,7 +2626,7 @@ 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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int err;
my_decimal decimal_value;
DBUG_ENTER("Field_new_decimal::store(char*)");
@@ -2693,7 +2693,7 @@ int Field_new_decimal::store(const char *from, uint length,
int Field_new_decimal::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
my_decimal decimal_value;
int err;
DBUG_ENTER("Field_new_decimal::store(double)");
@@ -2728,7 +2728,7 @@ int Field_new_decimal::store(double nr)
int Field_new_decimal::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
my_decimal decimal_value;
int err;
@@ -2750,7 +2750,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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
return store_value(decimal_value);
}
@@ -2972,7 +2972,7 @@ Field_new_decimal::unpack(uchar* to,
int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error;
longlong rnd;
@@ -2984,7 +2984,7 @@ int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_tiny::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
nr=rint(nr);
if (unsigned_flag)
@@ -3027,7 +3027,7 @@ int Field_tiny::store(double nr)
int Field_tiny::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
if (unsigned_flag)
@@ -3147,7 +3147,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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int store_tmp;
int error;
longlong rnd;
@@ -3168,7 +3168,7 @@ int Field_short::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_short::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
int16 res;
nr=rint(nr);
@@ -3220,7 +3220,7 @@ int Field_short::store(double nr)
int Field_short::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
int16 res;
@@ -3394,7 +3394,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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int store_tmp;
int error;
longlong rnd;
@@ -3408,7 +3408,7 @@ int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_medium::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
nr=rint(nr);
if (unsigned_flag)
@@ -3454,7 +3454,7 @@ int Field_medium::store(double nr)
int Field_medium::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
if (unsigned_flag)
@@ -3584,7 +3584,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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
long store_tmp;
int error;
longlong rnd;
@@ -3605,7 +3605,7 @@ int Field_long::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_long::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
int32 res;
nr=rint(nr);
@@ -3657,7 +3657,7 @@ int Field_long::store(double nr)
int Field_long::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
int32 res;
@@ -3831,7 +3831,7 @@ void Field_long::sql_type(String &res) const
int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
char *end;
ulonglong tmp;
@@ -3861,7 +3861,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_longlong::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
longlong res;
@@ -3913,7 +3913,7 @@ int Field_longlong::store(double nr)
int Field_longlong::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
if (nr < 0) // Only possible error
@@ -4139,7 +4139,7 @@ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_float::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= truncate(&nr, FLT_MAX);
float j= (float)nr;
@@ -4401,7 +4401,7 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_double::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= truncate(&nr, DBL_MAX);
#ifdef WORDS_BIGENDIAN
@@ -4828,7 +4828,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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
MYSQL_TIME l_time;
my_time_t tmp= 0;
int error;
@@ -4891,7 +4891,7 @@ int Field_timestamp::store(double nr)
int Field_timestamp::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
MYSQL_TIME l_time;
my_time_t timestamp= 0;
int error;
@@ -5190,7 +5190,7 @@ int Field_time::store_time(MYSQL_TIME *ltime, timestamp_type time_type)
int Field_time::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
long tmp;
int error= 0;
if (nr > (double)TIME_MAX_VALUE)
@@ -5228,7 +5228,7 @@ int Field_time::store(double nr)
int Field_time::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
long tmp;
int error= 0;
if (nr < (longlong) -TIME_MAX_VALUE && !unsigned_val)
@@ -5386,7 +5386,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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
char *end;
int error;
longlong nr= cs->cset->strntoull10rnd(cs, from, len, 0, &end, &error);
@@ -5434,7 +5434,7 @@ int Field_year::store(double nr)
int Field_year::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155)
{
*ptr= 0;
@@ -5509,7 +5509,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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
MYSQL_TIME l_time;
uint32 tmp;
int error;
@@ -5564,7 +5564,7 @@ int Field_date::store(double nr)
int Field_date::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
MYSQL_TIME not_used;
int error;
longlong initial_nr= nr;
@@ -5743,7 +5743,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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
long tmp;
MYSQL_TIME l_time;
int error;
@@ -5793,7 +5793,7 @@ int Field_newdate::store(double nr)
int Field_newdate::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
MYSQL_TIME l_time;
longlong tmp;
int error;
@@ -5829,7 +5829,7 @@ int Field_newdate::store(longlong nr, bool unsigned_val)
int Field_newdate::store_time(MYSQL_TIME *ltime,timestamp_type time_type)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
long tmp;
int error= 0;
if (time_type == MYSQL_TIMESTAMP_DATE ||
@@ -5976,7 +5976,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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
MYSQL_TIME time_tmp;
int error;
ulonglong tmp= 0;
@@ -6029,7 +6029,7 @@ int Field_datetime::store(double nr)
int Field_datetime::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
MYSQL_TIME not_used;
int error;
longlong initial_nr= nr;
@@ -6067,7 +6067,7 @@ 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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
longlong tmp;
int error= 0;
/*
@@ -6370,7 +6370,7 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end,
int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
uint copy_length;
const char *well_formed_error_pos;
const char *cannot_convert_error_pos;
@@ -6411,7 +6411,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
int Field_str::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
uint length;
uint local_char_length= field_length / charset()->mbmaxlen;
@@ -6707,8 +6707,7 @@ void Field_string::sql_type(String &res) const
length= cs->cset->snprintf(cs,(char*) res.ptr(),
res.alloced_length(), "%s(%d)",
- ((type() == MYSQL_TYPE_VAR_STRING &&
- !thd->variables.new_mode) ?
+ (type() == MYSQL_TYPE_VAR_STRING ?
(has_charset() ? "varchar" : "varbinary") :
(has_charset() ? "char" : "binary")),
(int) field_length / charset()->mbmaxlen);
@@ -6860,7 +6859,7 @@ int Field_string::do_save_field_metadata(uchar *metadata_ptr)
*/
int Field_string::pack_cmp(const uchar *a, const uchar *b, uint length,
- my_bool insert_or_update)
+ bool insert_or_update)
{
uint a_length, b_length;
if (length > 255)
@@ -6898,7 +6897,7 @@ int Field_string::pack_cmp(const uchar *a, const uchar *b, uint length,
*/
int Field_string::pack_cmp(const uchar *key, uint length,
- my_bool insert_or_update)
+ bool insert_or_update)
{
uint row_length, local_key_length;
uchar *end;
@@ -7015,7 +7014,7 @@ int Field_varstring::do_save_field_metadata(uchar *metadata_ptr)
int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
uint copy_length;
const char *well_formed_error_pos;
const char *cannot_convert_error_pos;
@@ -7377,7 +7376,7 @@ Field_varstring::unpack(uchar *to, const uchar *from,
int Field_varstring::pack_cmp(const uchar *a, const uchar *b,
uint key_length_arg,
- my_bool insert_or_update)
+ bool insert_or_update)
{
uint a_length, b_length;
if (key_length_arg > 255)
@@ -7398,7 +7397,7 @@ int Field_varstring::pack_cmp(const uchar *a, const uchar *b,
int Field_varstring::pack_cmp(const uchar *b, uint key_length_arg,
- my_bool insert_or_update)
+ bool insert_or_update)
{
uchar *a= ptr+ length_bytes;
uint a_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
@@ -7679,7 +7678,7 @@ void Field_blob::put_length(uchar *pos, uint32 length)
int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
uint copy_length, new_length;
const char *well_formed_error_pos;
const char *cannot_convert_error_pos;
@@ -8129,7 +8128,7 @@ const uchar *Field_blob::unpack(uchar *to,
/* Keys for blobs are like keys on varchars */
int Field_blob::pack_cmp(const uchar *a, const uchar *b, uint key_length_arg,
- my_bool insert_or_update)
+ bool insert_or_update)
{
uint a_length, b_length;
if (key_length_arg > 255)
@@ -8150,7 +8149,7 @@ int Field_blob::pack_cmp(const uchar *a, const uchar *b, uint key_length_arg,
int Field_blob::pack_cmp(const uchar *b, uint key_length_arg,
- my_bool insert_or_update)
+ bool insert_or_update)
{
uchar *a;
uint a_length, b_length;
@@ -8440,7 +8439,7 @@ void Field_enum::store_type(ulonglong value)
int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int err= 0;
uint32 not_used;
char buff[STRING_BUFFER_USUAL_SIZE];
@@ -8489,7 +8488,7 @@ int Field_enum::store(double nr)
int Field_enum::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
if ((ulonglong) nr > typelib->count || nr == 0)
{
@@ -8658,7 +8657,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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
bool got_warning= 0;
int err= 0;
char *not_used;
@@ -8698,7 +8697,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;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int error= 0;
ulonglong max_nr;
@@ -9017,7 +9016,7 @@ uint Field_bit::is_equal(Create_field *new_field)
int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int delta;
for (; length && !*from; from++, length--) // skip left 0's
@@ -9439,7 +9438,7 @@ Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg,
int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int delta;
uchar bits= (uchar) (field_length & 7);
@@ -9548,6 +9547,8 @@ void Create_field::init_for_tmp_table(enum_field_types sql_type_arg,
((decimals_arg & FIELDFLAG_MAX_DEC) << FIELDFLAG_DEC_SHIFT) |
(maybe_null ? FIELDFLAG_MAYBE_NULL : 0) |
(is_unsigned ? 0 : FIELDFLAG_DECIMAL));
+ vcol_info= 0;
+ stored_in_db= TRUE;
}
@@ -9567,6 +9568,7 @@ void Create_field::init_for_tmp_table(enum_field_types sql_type_arg,
@param fld_interval_list Interval list (if any)
@param fld_charset Field charset
@param fld_geom_type Field geometry type (if any)
+ @param fld_vcol_info Virtual column data
@retval
FALSE on success
@@ -9579,17 +9581,20 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
uint fld_type_modifier, Item *fld_default_value,
Item *fld_on_update_value, LEX_STRING *fld_comment,
char *fld_change, List<String> *fld_interval_list,
- CHARSET_INFO *fld_charset, uint fld_geom_type)
+ CHARSET_INFO *fld_charset, uint fld_geom_type,
+ Virtual_column_info *fld_vcol_info,
+ engine_option_value *create_opt)
{
uint sign_len, allowed_type_modifier= 0;
ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
DBUG_ENTER("Create_field::init()");
-
+
field= 0;
field_name= fld_name;
def= fld_default_value;
flags= fld_type_modifier;
+ option_list= create_opt;
unireg_check= (fld_type_modifier & AUTO_INCREMENT_FLAG ?
Field::NEXT_NUMBER : Field::NONE);
decimals= fld_decimals ? (uint)atoi(fld_decimals) : 0;
@@ -9610,6 +9615,33 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
interval_list.empty();
comment= *fld_comment;
+ vcol_info= fld_vcol_info;
+ stored_in_db= TRUE;
+
+ /* Initialize data for a computed field */
+ if ((uchar)fld_type == (uchar)MYSQL_TYPE_VIRTUAL)
+ {
+ DBUG_ASSERT(vcol_info && vcol_info->expr_item);
+ stored_in_db= vcol_info->is_stored();
+ /*
+ Walk through the Item tree checking if all items are valid
+ to be part of the virtual column
+ */
+ if (vcol_info->expr_item->walk(&Item::check_vcol_func_processor, 0, NULL))
+ {
+ my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), field_name);
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ Make a field created for the real type.
+ Note that regular and computed fields differ from each other only by
+ Field::vcol_info. It is is always NULL for a column that is not
+ computed.
+ */
+ sql_type= fld_type= vcol_info->get_real_type();
+ }
+
/*
Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and
it is NOT NULL, not an AUTO_INCREMENT field and not a TIMESTAMP.
@@ -9899,7 +9931,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
}
case MYSQL_TYPE_DECIMAL:
DBUG_ASSERT(0); /* Was obsolete */
- }
+ }
/* Remember the value of length */
char_length= length;
@@ -10009,7 +10041,6 @@ uint pack_length_to_packflag(uint type)
return 0; // This shouldn't happen
}
-
Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
uchar *null_pos, uchar null_bit,
uint pack_flag,
@@ -10201,6 +10232,10 @@ Create_field::Create_field(Field *old_field,Field *orig_field)
charset= old_field->charset(); // May be NULL ptr
comment= old_field->comment;
decimals= old_field->decimals();
+ vcol_info= old_field->vcol_info;
+ stored_in_db= old_field->stored_in_db;
+ option_list= old_field->option_list;
+ option_struct= old_field->option_struct;
/* Fix if the original table had 4 byte pointer blobs */
if (flags & BLOB_FLAG)
@@ -10275,6 +10310,19 @@ Create_field::Create_field(Field *old_field,Field *orig_field)
/**
+ Makes a clone of this object for ALTER/CREATE TABLE
+
+ @param mem_root MEM_ROOT where to clone the field
+*/
+
+Create_field *Create_field::clone(MEM_ROOT *mem_root) const
+{
+ Create_field *res= new (mem_root) Create_field(*this);
+ return res;
+}
+
+
+/**
maximum possible display length for blob.
@return
diff --git a/sql/field.h b/sql/field.h
index 9db4f47a0f3..3cfcf308282 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -34,6 +34,7 @@ class Send_field;
class Protocol;
class Create_field;
class Relay_log_info;
+struct ha_field_option_struct;
struct st_cache_field;
int field_conv(Field *to,Field *from);
@@ -49,6 +50,71 @@ inline uint get_set_pack_length(int elements)
return len > 4 ? 8 : len;
}
+/*
+ Virtual_column_info is the class to contain additional
+ characteristics that is specific for a virtual/computed
+ field such as:
+ - the defining expression that is evaluated to compute the value
+ of the field
+ - whether the field is to be stored in the database
+ - whether the field is used in a partitioning expression
+*/
+
+class Virtual_column_info: public Sql_alloc
+{
+private:
+ /*
+ The following data is only updated by the parser and read
+ when a Create_field object is created/initialized.
+ */
+ enum_field_types field_type; /* Real field type*/
+ /* Flag indicating that the field is physically stored in the database */
+ bool stored_in_db;
+ /* Flag indicating that the field used in a partitioning expression */
+ bool in_partitioning_expr;
+
+public:
+ /* The expression to compute the value of the virtual column */
+ Item *expr_item;
+ /* Text representation of the defining expression */
+ LEX_STRING expr_str;
+
+ Virtual_column_info()
+ : field_type((enum enum_field_types)MYSQL_TYPE_VIRTUAL),
+ stored_in_db(FALSE), in_partitioning_expr(FALSE),
+ expr_item(NULL)
+ {
+ expr_str.str= NULL;
+ expr_str.length= 0;
+ };
+ ~Virtual_column_info() {}
+ enum_field_types get_real_type()
+ {
+ return field_type;
+ }
+ void set_field_type(enum_field_types fld_type)
+ {
+ /* Calling this function can only be done once. */
+ field_type= fld_type;
+ }
+ bool is_stored()
+ {
+ return stored_in_db;
+ }
+ void set_stored_in_db_flag(bool stored)
+ {
+ stored_in_db= stored;
+ }
+ bool is_in_partitioning_expr()
+ {
+ return in_partitioning_expr;
+ }
+ void mark_as_in_partitioning_expr()
+ {
+ in_partitioning_expr= TRUE;
+ }
+};
+
class Field
{
Field(const Item &); /* Prevent use of these */
@@ -65,20 +131,24 @@ public:
*/
uchar *null_ptr;
/*
- Note that you can use table->in_use as replacement for current_thd member
+ Note that you can use table->in_use as replacement for current_thd member
only inside of val_*() and store() members (e.g. you can't use it in cons)
*/
struct st_table *table; // Pointer for table
struct st_table *orig_table; // Pointer to original table
- const char **table_name, *field_name;
+ const char * const *table_name;
+ const char *field_name;
+ /** reference to the list of options or NULL */
+ engine_option_value *option_list;
+ ha_field_option_struct *option_struct; /* structure with parsed options */
LEX_STRING comment;
/* Field is part of the following keys */
key_map key_start, part_of_key, part_of_key_not_clustered;
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
- NOW() as default and on update value for such fields but unable to hold
+ /*
+ 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
+ NOW() as default and on update value for such fields but unable to hold
this info anywhere except unireg_check field. This issue will be resolved
in more clean way with transition to new text based .frm format.
See also comment for Field_timestamp::Field_timestamp().
@@ -111,6 +181,19 @@ public:
*/
bool is_created_from_null_item;
+ /*
+ This is additional data provided for any computed(virtual) field.
+ In particular it includes a pointer to the item by which this field
+ can be computed from other fields.
+ */
+ Virtual_column_info *vcol_info;
+ /*
+ Flag indicating that the field is physically stored in tables
+ rather than just computed from other fields.
+ As of now, FALSE can be set only for computed virtual columns.
+ */
+ bool stored_in_db;
+
Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
const char *field_name_arg);
@@ -140,7 +223,7 @@ public:
This trickery is used to decrease a number of malloc calls.
*/
virtual String *val_str(String*,String *)=0;
- String *val_int_as_str(String *val_buffer, my_bool unsigned_flag);
+ String *val_int_as_str(String *val_buffer, 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.
@@ -439,10 +522,10 @@ public:
{ return max_length;}
virtual int pack_cmp(const uchar *a,const uchar *b, uint key_length_arg,
- my_bool insert_or_update)
+ bool insert_or_update)
{ return cmp(a,b); }
virtual int pack_cmp(const uchar *b, uint key_length_arg,
- my_bool insert_or_update)
+ bool insert_or_update)
{ return cmp(ptr,b); }
uint offset(uchar *record)
{
@@ -474,10 +557,14 @@ public:
return (op_result == E_DEC_OVERFLOW);
}
int warn_if_overflow(int op_result);
+ void set_table_name(String *alias)
+ {
+ table_name= &alias->Ptr;
+ }
void init(TABLE *table_arg)
{
orig_table= table= table_arg;
- table_name= &table_arg->alias;
+ set_table_name(&table_arg->alias);
}
/* maximum possible display length */
@@ -708,7 +795,7 @@ public:
/* base class for float and double and decimal (old one) */
class Field_real :public Field_num {
public:
- my_bool not_fixed;
+ bool not_fixed;
Field_real(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
@@ -1135,7 +1222,7 @@ public:
NONE, field_name_arg, dec_arg, 0, 0)
{}
Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg,
- uint8 dec_arg, my_bool not_fixed_arg)
+ uint8 dec_arg, bool not_fixed_arg)
:Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
NONE, field_name_arg, dec_arg, 0, 0)
{not_fixed= not_fixed_arg; }
@@ -1226,7 +1313,7 @@ public:
Field::set_default();
}
/* Get TIMESTAMP field value as seconds since begging of Unix Epoch */
- inline long get_timestamp(my_bool *null_value)
+ inline long get_timestamp(bool *null_value)
{
if ((*null_value= is_null()))
return 0;
@@ -1513,8 +1600,8 @@ public:
const Relay_log_info *rli, uint16 mflags);
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);
+ bool insert_or_update);
+ int pack_cmp(const uchar *b,uint key_length,bool insert_or_update);
uint packed_col_length(const uchar *to, uint length);
uint max_packed_col_length(uint max_length);
uint size_of() const { return sizeof(*this); }
@@ -1596,8 +1683,8 @@ public:
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 uchar *b, uint key_length,my_bool insert_or_update);
+ bool insert_or_update);
+ int pack_cmp(const uchar *b, uint key_length,bool insert_or_update);
int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0L);
int key_cmp(const uchar *,const uchar*);
int key_cmp(const uchar *str, uint length);
@@ -1782,8 +1869,8 @@ public:
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 uchar *b, uint key_length,my_bool insert_or_update);
+ bool insert_or_update);
+ int pack_cmp(const uchar *b, uint key_length,bool insert_or_update);
uint packed_col_length(const uchar *col_ptr, uint length);
uint max_packed_col_length(uint max_length);
void free() { value.free(); }
@@ -2069,14 +2156,31 @@ public:
CHARSET_INFO *charset;
Field::geometry_type geom_type;
Field *field; // For alter table
+ engine_option_value *option_list;
+ /** structure with parsed options (for comparing fields in ALTER TABLE) */
+ ha_field_option_struct *option_struct;
uint8 row,col,sc_length,interval_id; // For rea_create_table
uint offset,pack_flag;
- Create_field() :after(0) {}
+
+ /*
+ This is additinal data provided for any computed(virtual) field.
+ In particular it includes a pointer to the item by which this field
+ can be computed from other fields.
+ */
+ Virtual_column_info *vcol_info;
+ /*
+ Flag indicating that the field is physically stored in tables
+ rather than just computed from other fields.
+ As of now, FALSE can be set only for computed virtual columns.
+ */
+ bool stored_in_db;
+
+ Create_field() :after(0), option_list(NULL), option_struct(NULL)
+ {}
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); }
+ Create_field *clone(MEM_ROOT *mem_root) const;
void create_length_to_internal_length(void);
/* Init for a tmp table field. To be extended if need be. */
@@ -2088,7 +2192,8 @@ public:
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);
+ uint uint_geom_type, Virtual_column_info *vcol_info,
+ engine_option_value *option_list);
bool field_flags_are_binary()
{
@@ -2127,7 +2232,7 @@ class Copy_field :public Sql_alloc {
public:
uchar *from_ptr,*to_ptr;
uchar *from_null_ptr,*to_null_ptr;
- my_bool *null_row;
+ bool *null_row;
uint from_bit,to_bit;
/**
Number of bytes in the fields pointed to by 'from_ptr' and
diff --git a/sql/filesort.cc b/sql/filesort.cc
index e95ff08fcdd..ad891db3991 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -517,7 +517,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
THD *thd= current_thd;
volatile THD::killed_state *killed= &thd->killed;
handler *file;
- MY_BITMAP *save_read_set, *save_write_set;
+ MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set;
DBUG_ENTER("find_all_keys");
DBUG_PRINT("info",("using: %s",
(select ? select->quick ? "ranges" : "where":
@@ -539,7 +539,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
if (! indexfile && ! quick_select)
{
next_pos=(uchar*) 0; /* Find records in sequence */
- file->ha_rnd_init(1);
+ if (file->ha_rnd_init_with_error(1))
+ DBUG_RETURN(HA_POS_ERROR);
file->extra_opt(HA_EXTRA_CACHE,
current_thd->variables.read_buff_size);
}
@@ -553,6 +554,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
/* Remember original bitmaps */
save_read_set= sort_form->read_set;
save_write_set= sort_form->write_set;
+ save_vcol_set= sort_form->vcol_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 */
@@ -561,7 +563,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
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);
+ sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set,
+ &sort_form->tmp_set);
for (;;)
{
@@ -569,6 +572,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
{
if ((error= select->quick->get_next()))
break;
+ if (!error)
+ update_virtual_fields(thd, sort_form);
file->position(sort_form->record[0]);
DBUG_EXECUTE_IF("debug_filesort", dbug_print_record(sort_form, TRUE););
}
@@ -581,11 +586,13 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
error= my_errno ? my_errno : -1; /* Abort */
break;
}
- error=file->rnd_pos(sort_form->record[0],next_pos);
+ error=file->ha_rnd_pos(sort_form->record[0],next_pos);
}
else
{
- error=file->rnd_next(sort_form->record[0]);
+ error=file->ha_rnd_next(sort_form->record[0]);
+ if (!error)
+ update_virtual_fields(thd, sort_form);
if (!flag)
{
my_store_ptr(ref_pos,ref_length,record); // Position to row
@@ -640,7 +647,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
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);
+ sort_form->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set);
DBUG_PRINT("test",("error: %d indexpos: %d",error,indexpos));
if (error != HA_ERR_END_OF_FILE)
@@ -1007,7 +1014,14 @@ static void register_used_fields(SORTPARAM *param)
if ((field= sort_field->field))
{
if (field->table == table)
- bitmap_set_bit(bitmap, field->field_index);
+ {
+ if (field->vcol_info)
+ {
+ Item *vcol_item= field->vcol_info->expr_item;
+ vcol_item->walk(&Item::register_field_in_read_map, 1, (uchar *) 0);
+ }
+ bitmap_set_bit(bitmap, field->field_index);
+ }
}
else
{ // Item
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index 8e2cd207962..140931f94f1 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -9172,7 +9172,7 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
thd->client_capabilities = 0;
my_net_init(&thd->net, 0);
thd->main_security_ctx.master_access= ~0;
- thd->main_security_ctx.priv_user = 0;
+ thd->main_security_ctx.priv_user[0] = 0;
CHARSET_INFO *charset_connection;
charset_connection= get_charset_by_csname("utf8",
@@ -10549,6 +10549,23 @@ mysql_declare_plugin(ndbcluster)
NULL /* config options */
}
mysql_declare_plugin_end;
+maria_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 */
+ "1.0", /* string version */
+ MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */
+}
+maria_declare_plugin_end;
#else
int Sun_ar_require_a_symbol_here= 0;
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index 31bede644d9..31afaebc8cc 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -3682,7 +3682,7 @@ pthread_handler_t ndb_binlog_thread_func(void *arg)
thd->client_capabilities= 0;
my_net_init(&thd->net, 0);
thd->main_security_ctx.master_access= ~0;
- thd->main_security_ctx.priv_user= 0;
+ thd->main_security_ctx.priv_user[0]= 0;
/*
Set up ndb binlog
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index e70eacfab5d..412734faf23 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -54,6 +54,7 @@
#endif
#include "mysql_priv.h"
+#include "create_options.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -1153,7 +1154,8 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
error != HA_ADMIN_ALREADY_DONE &&
error != HA_ADMIN_TRY_ALTER)
{
- print_admin_msg(thd, "error", table_share->db.str, table->alias,
+ print_admin_msg(thd, "error", table_share->db.str,
+ table->alias.c_ptr(),
opt_op_name[flag],
"Subpartition %s returned error",
sub_elem->partition_name);
@@ -1180,7 +1182,8 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
error != HA_ADMIN_ALREADY_DONE &&
error != HA_ADMIN_TRY_ALTER)
{
- print_admin_msg(thd, "error", table_share->db.str, table->alias,
+ print_admin_msg(thd, "error", table_share->db.str,
+ table->alias.c_ptr(),
opt_op_name[flag], "Partition %s returned error",
part_elem->partition_name);
}
@@ -1723,11 +1726,11 @@ int ha_partition::copy_partitions(ulonglong * const copied,
uint32 new_part;
late_extra_cache(reorg_part);
- if ((result= file->ha_rnd_init(1)))
+ if ((result= file->ha_rnd_init_with_error(1)))
goto error;
while (TRUE)
{
- if ((result= file->rnd_next(m_rec0)))
+ if ((result= file->ha_rnd_next(m_rec0)))
{
if (result == HA_ERR_RECORD_DELETED)
continue; //Probably MyISAM
@@ -1951,6 +1954,8 @@ uint ha_partition::del_ren_cre_table(const char *from,
{
if ((error= set_up_table_before_create(table_arg, from_buff,
create_info, i, NULL)) ||
+ parse_engine_table_options(ha_thd(), (*file)->ht,
+ (*file)->table_share) ||
((error= (*file)->ha_create(from_buff, table_arg, create_info))))
goto create_error;
}
@@ -2457,7 +2462,7 @@ bool ha_partition::read_par_file(const char *name)
fn_format(buff, name, "", ha_par_ext, MY_APPEND_EXT);
/* Following could be done with my_stat to read in whole file */
- if ((file= my_open(buff, O_RDONLY | O_SHARE, MYF(0))) < 0)
+ if ((file= my_open(buff, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
DBUG_RETURN(true);
if (my_read(file, (uchar *) & buff[0], PAR_WORD_SIZE, MYF(MY_NABP)))
goto err1;
@@ -2564,7 +2569,7 @@ bool ha_partition::setup_engine_array(MEM_ROOT *mem_root)
for (i= 0; i < m_tot_parts; i++)
m_engine_array[i]= ha_lock_engine(NULL, engine_array[i]);
- my_afree((gptr) engine_array);
+ my_afree(engine_array);
if (create_handlers(mem_root))
{
@@ -2575,7 +2580,7 @@ bool ha_partition::setup_engine_array(MEM_ROOT *mem_root)
DBUG_RETURN(false);
err:
- my_afree((gptr) engine_array);
+ my_afree(engine_array);
DBUG_RETURN(true);
}
@@ -2676,7 +2681,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
name_buffer_ptr= m_name_buffer_ptr;
m_start_key.length= 0;
m_rec0= table->record[0];
- m_rec_length= table_share->reclength;
+ m_rec_length= table_share->stored_rec_length;
alloc_len= m_tot_parts * (m_rec_length + PARTITION_BYTES_IN_POS);
alloc_len+= table_share->max_key_length;
if (!m_ordered_rec_buffer)
@@ -3941,6 +3946,7 @@ int ha_partition::rnd_next(uchar *buf)
int result= HA_ERR_END_OF_FILE;
uint part_id= m_part_spec.start_part;
DBUG_ENTER("ha_partition::rnd_next");
+ decrement_statistics(&SSV::ha_read_rnd_next_count);
if (NO_CURRENT_PART_ID == part_id)
{
@@ -3956,7 +3962,7 @@ int ha_partition::rnd_next(uchar *buf)
while (TRUE)
{
- result= file->rnd_next(buf);
+ result= file->ha_rnd_next(buf);
if (!result)
{
m_last_part= part_id;
@@ -4088,6 +4094,7 @@ int ha_partition::rnd_pos(uchar * buf, uchar *pos)
uint part_id;
handler *file;
DBUG_ENTER("ha_partition::rnd_pos");
+ decrement_statistics(&SSV::ha_read_rnd_count);
part_id= uint2korr((const uchar *) pos);
DBUG_ASSERT(part_id < m_tot_parts);
@@ -4300,6 +4307,7 @@ int ha_partition::index_read_map(uchar *buf, const uchar *key,
enum ha_rkey_function find_flag)
{
DBUG_ENTER("ha_partition::index_read_map");
+ decrement_statistics(&SSV::ha_read_key_count);
end_range= 0;
m_index_scan_type= partition_index_read;
m_start_key.key= key;
@@ -4428,6 +4436,7 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key)
int ha_partition::index_first(uchar * buf)
{
DBUG_ENTER("ha_partition::index_first");
+ decrement_statistics(&SSV::ha_read_first_count);
end_range= 0;
m_index_scan_type= partition_index_first;
@@ -4459,6 +4468,7 @@ int ha_partition::index_first(uchar * buf)
int ha_partition::index_last(uchar * buf)
{
DBUG_ENTER("ha_partition::index_last");
+ decrement_statistics(&SSV::ha_read_last_count);
m_index_scan_type= partition_index_last;
DBUG_RETURN(common_first_last(buf));
@@ -4487,39 +4497,6 @@ int ha_partition::common_first_last(uchar *buf)
/*
- 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));
-}
-
-
-/*
Optimization of the default implementation to take advantage of dynamic
partition pruning.
*/
@@ -4595,6 +4572,7 @@ int ha_partition::index_read_idx_map(uchar *buf, uint index,
int ha_partition::index_next(uchar * buf)
{
DBUG_ENTER("ha_partition::index_next");
+ decrement_statistics(&SSV::ha_read_next_count);
/*
TODO(low priority):
@@ -4631,6 +4609,7 @@ int ha_partition::index_next(uchar * buf)
int ha_partition::index_next_same(uchar *buf, const uchar *key, uint keylen)
{
DBUG_ENTER("ha_partition::index_next_same");
+ decrement_statistics(&SSV::ha_read_next_count);
DBUG_ASSERT(keylen == m_start_key.length);
DBUG_ASSERT(m_index_scan_type != partition_index_last);
@@ -4658,6 +4637,7 @@ int ha_partition::index_next_same(uchar *buf, const uchar *key, uint keylen)
int ha_partition::index_prev(uchar * buf)
{
DBUG_ENTER("ha_partition::index_prev");
+ decrement_statistics(&SSV::ha_read_prev_count);
/* TODO: read comment in index_next */
DBUG_ASSERT(m_index_scan_type != partition_index_first);
@@ -4870,8 +4850,8 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same)
}
else if (is_next_same)
{
- if (!(error= file->index_next_same(buf, m_start_key.key,
- m_start_key.length)))
+ if (!(error= file->ha_index_next_same(buf, m_start_key.key,
+ m_start_key.length)))
{
m_last_part= m_part_spec.start_part;
DBUG_RETURN(0);
@@ -4879,7 +4859,7 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same)
}
else
{
- if (!(error= file->index_next(buf)))
+ if (!(error= file->ha_index_next(buf)))
{
m_last_part= m_part_spec.start_part;
DBUG_RETURN(0); // Row was in range
@@ -4934,13 +4914,13 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf)
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);
+ error= file->ha_index_read_map(buf, m_start_key.key,
+ m_start_key.keypart_map,
+ m_start_key.flag);
break;
case partition_index_first:
DBUG_PRINT("info", ("index_first on partition %d", i));
- error= file->index_first(buf);
+ error= file->ha_index_first(buf);
break;
case partition_index_first_unordered:
/*
@@ -5021,23 +5001,17 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
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);
+ error= file->ha_index_read_map(rec_buf_ptr,
+ m_start_key.key,
+ m_start_key.keypart_map,
+ m_start_key.flag);
break;
case partition_index_first:
- error= file->index_first(rec_buf_ptr);
+ error= file->ha_index_first(rec_buf_ptr);
reverse_order= FALSE;
break;
case partition_index_last:
- 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);
+ error= file->ha_index_last(rec_buf_ptr);
reverse_order= TRUE;
break;
case partition_read_range:
@@ -5139,10 +5113,10 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
memcpy(rec_buf(part_id), table->record[0], m_rec_length);
}
else if (!is_next_same)
- error= file->index_next(rec_buf(part_id));
+ error= file->ha_index_next(rec_buf(part_id));
else
- error= file->index_next_same(rec_buf(part_id), m_start_key.key,
- m_start_key.length);
+ error= file->ha_index_next_same(rec_buf(part_id), m_start_key.key,
+ m_start_key.length);
if (error)
{
if (error == HA_ERR_END_OF_FILE)
@@ -5187,7 +5161,7 @@ int ha_partition::handle_ordered_prev(uchar *buf)
handler *file= m_file[part_id];
DBUG_ENTER("ha_partition::handle_ordered_prev");
- if ((error= file->index_prev(rec_buf(part_id))))
+ if ((error= file->ha_index_prev(rec_buf(part_id))))
{
if (error == HA_ERR_END_OF_FILE)
{
@@ -7148,5 +7122,22 @@ mysql_declare_plugin(partition)
NULL /* config options */
}
mysql_declare_plugin_end;
+maria_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 */
+ "1.0", /* string version */
+ MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
+}
+maria_declare_plugin_end;
#endif
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index adb8214aae4..73d1c25c0c4 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -76,9 +76,8 @@ private:
partition_index_first= 1,
partition_index_first_unordered= 2,
partition_index_last= 3,
- partition_index_read_last= 4,
- partition_read_range = 5,
- partition_no_index_scan= 6
+ partition_read_range = 4,
+ partition_no_index_scan= 5
};
/* Data for the partition handler */
int m_mode; // Open mode
@@ -493,8 +492,6 @@ public:
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
@@ -1147,4 +1144,27 @@ public:
-------------------------------------------------------------------------
virtual void append_create_info(String *packet)
*/
+
+ /*
+ the following heavily relies on the fact that all partitions
+ are in the same storage engine.
+
+ When this limitation is lifted, the following hack should go away,
+ and a proper interface for engines needs to be introduced:
+
+ an PARTITION_SHARE structure that has a pointer to the TABLE_SHARE.
+ is given to engines everywhere where TABLE_SHARE is used now
+ has members like option_struct, ha_data
+ perhaps TABLE needs to be split the same way too...
+
+ this can also be done before partition will support a mix of engines,
+ but preferably together with other incompatible API changes.
+ */
+ virtual handlerton *partition_ht() const
+ {
+ handlerton *h= m_file[0]->ht;
+ for (uint i=1; i < m_tot_parts; i++)
+ DBUG_ASSERT(h == m_file[i]->ht);
+ return h;
+ }
};
diff --git a/sql/handler.cc b/sql/handler.cc
index 3993fad1f9a..1f6a3b9f665 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -25,6 +25,7 @@
#endif
#include "mysql_priv.h"
+#include "create_options.h"
#include "rpl_filter.h"
#include <myisampack.h>
#include "myisam.h"
@@ -59,7 +60,7 @@ static const LEX_STRING sys_table_aliases[]=
{ 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") },
- { C_STRING_WITH_LEN("Aria") }, { C_STRING_WITH_LEN("Maria") },
+ { C_STRING_WITH_LEN("Maria") }, { C_STRING_WITH_LEN("Aria") },
{NullS, 0}
};
@@ -1247,6 +1248,7 @@ int ha_commit_one_phase(THD *thd, bool all)
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
error=1;
}
+ /* Should this be done only if is_real_trans is set ? */
status_var_increment(thd->status_var.ha_commit_count);
ha_info_next= ha_info->next();
ha_info->reset(); /* keep it conveniently zero-filled */
@@ -1348,9 +1350,14 @@ int ha_rollback_trans(THD *thd, bool all)
slave SQL thread, it would not stop the thread but just be printed in
the error log; but we don't want users to wonder why they have this
message in the error log, so we don't send it.
+
+ We don't have to test for thd->killed == THD::KILL_SYSTEM_THREAD as
+ it doesn't matter if a warning is pushed to a system thread or not:
+ No one will see it...
*/
if (is_real_trans && thd->transaction.all.modified_non_trans_table &&
- !thd->slave_thread && thd->killed != THD::KILL_CONNECTION)
+ !thd->slave_thread && thd->killed != THD::KILL_CONNECTION &&
+ thd->killed != THD::KILL_SERVER)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARNING_NOT_COMPLETE_ROLLBACK,
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
@@ -2013,7 +2020,8 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
dummy_share.db.length= strlen(db);
dummy_share.table_name.str= (char*) alias;
dummy_share.table_name.length= strlen(alias);
- dummy_table.alias= alias;
+ dummy_table.alias.set(alias, dummy_share.table_name.length,
+ table_alias_charset);
file->change_table_ptr(&dummy_table, &dummy_share);
@@ -2084,11 +2092,6 @@ double handler::keyread_time(uint index, uint ranges, ha_rows rows)
return (rows + keys_per_block - 1)/ keys_per_block;
}
-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);
@@ -2153,10 +2156,24 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
dup_ref=ref+ALIGN_SIZE(ref_length);
cached_table_flags= table_flags();
}
+ rows_read= rows_changed= 0;
+ memset(index_rows_read, 0, sizeof(index_rows_read));
DBUG_RETURN(error);
}
+/* Initialize handler for random reading, with error handling */
+
+int handler::ha_rnd_init_with_error(bool scan)
+{
+ int error;
+ if (!(error= ha_rnd_init(scan)))
+ return 0;
+ table->file->print_error(error, MYF(0));
+ return error;
+}
+
+
/**
Read first row (only) from a table.
@@ -2168,8 +2185,6 @@ int handler::read_first_row(uchar * buf, uint primary_key)
register int error;
DBUG_ENTER("handler::read_first_row");
- 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.
@@ -2178,15 +2193,17 @@ int handler::read_first_row(uchar * buf, uint primary_key)
if (stats.deleted < 10 || primary_key >= MAX_KEY ||
!(index_flags(primary_key, 0, 0) & HA_READ_ORDER))
{
- (void) ha_rnd_init(1);
- while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ;
- (void) ha_rnd_end();
+ if ((!(error= ha_rnd_init(1))))
+ {
+ while ((error= ha_rnd_next(buf)) == HA_ERR_RECORD_DELETED) ;
+ (void) ha_rnd_end();
+ }
}
else
{
/* Find the first row through the primary key */
if (!(error = ha_index_init(primary_key, 0)))
- error= index_first(buf);
+ error= ha_index_first(buf);
(void) ha_index_end();
}
DBUG_RETURN(error);
@@ -2574,7 +2591,7 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
ha_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]);
+ error=ha_index_last(table->record[1]);
/*
MySQL implicitely assumes such method does locking (as MySQL decides to
use nr+increment without checking again with the handler, in
@@ -2588,9 +2605,10 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
key_copy(key, table->record[0],
table->key_info + table->s->next_number_index,
table->s->next_number_key_offset);
- error= index_read_map(table->record[1], key,
- make_prev_keypart_map(table->s->next_number_keypart),
- HA_READ_PREFIX_LAST);
+ error= ha_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
@@ -2882,11 +2900,12 @@ void handler::print_error(int error, myf errflag)
{
const char* engine= table_type();
if (temporary)
- my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(), engine);
+ my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.c_ptr(),
+ engine);
else
{
SET_FATAL_ERROR;
- my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine);
+ my_error(ER_GET_ERRMSG, MYF(0), error, str.c_ptr(), engine);
}
}
else
@@ -3213,11 +3232,14 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
if it is started.
*/
-inline
void
-handler::mark_trx_read_write()
+handler::mark_trx_read_write_part2()
{
Ha_trx_info *ha_info= &ha_thd()->ha_data[ht->slot].ha_info[0];
+
+ /* Don't call this function again for this statement */
+ mark_trx_done= TRUE;
+
/*
When a storage engine method is called, the transaction must
have been started, unless it's a DDL call, for which the
@@ -3598,7 +3620,7 @@ int ha_enable_transaction(THD *thd, bool on)
int handler::index_next_same(uchar *buf, const uchar *key, uint keylen)
{
int error;
- DBUG_ENTER("index_next_same");
+ DBUG_ENTER("handler::index_next_same");
if (!(error=index_next(buf)))
{
my_ptrdiff_t ptrdiff= buf - table->record[0];
@@ -3643,6 +3665,7 @@ int handler::index_next_same(uchar *buf, const uchar *key, uint keylen)
key_part->field->move_field_offset(-ptrdiff);
}
}
+ DBUG_PRINT("return",("%i", error));
DBUG_RETURN(error);
}
@@ -3668,6 +3691,122 @@ void handler::get_dynamic_partition_info(PARTITION_INFO *stat_info,
}
+/*
+ Updates the global table stats with the TABLE this handler represents
+*/
+
+void handler::update_global_table_stats()
+{
+ TABLE_STATS * table_stats;
+
+ status_var_add(table->in_use->status_var.rows_read, rows_read);
+
+ if (!table->in_use->userstat_running)
+ {
+ rows_read= rows_changed= 0;
+ return;
+ }
+
+ if (rows_read + rows_changed == 0)
+ return; // Nothing to update.
+
+ DBUG_ASSERT(table->s && table->s->table_cache_key.str);
+
+ pthread_mutex_lock(&LOCK_global_table_stats);
+ /* Gets the global table stats, creating one if necessary. */
+ if (!(table_stats= (TABLE_STATS*)
+ hash_search(&global_table_stats,
+ (uchar*) table->s->table_cache_key.str,
+ table->s->table_cache_key.length)))
+ {
+ if (!(table_stats = ((TABLE_STATS*)
+ my_malloc(sizeof(TABLE_STATS),
+ MYF(MY_WME | MY_ZEROFILL)))))
+ {
+ /* Out of memory error already given */
+ goto end;
+ }
+ memcpy(table_stats->table, table->s->table_cache_key.str,
+ table->s->table_cache_key.length);
+ table_stats->table_name_length= table->s->table_cache_key.length;
+ table_stats->engine_type= ht->db_type;
+ /* No need to set variables to 0, as we use MY_ZEROFILL above */
+
+ if (my_hash_insert(&global_table_stats, (uchar*) table_stats))
+ {
+ /* Out of memory error is already given */
+ my_free(table_stats, 0);
+ goto end;
+ }
+ }
+ // Updates the global table stats.
+ table_stats->rows_read+= rows_read;
+ table_stats->rows_changed+= rows_changed;
+ table_stats->rows_changed_x_indexes+= (rows_changed *
+ (table->s->keys ? table->s->keys :
+ 1));
+ rows_read= rows_changed= 0;
+end:
+ pthread_mutex_unlock(&LOCK_global_table_stats);
+}
+
+
+/*
+ Updates the global index stats with this handler's accumulated index reads.
+*/
+
+void handler::update_global_index_stats()
+{
+ DBUG_ASSERT(table->s);
+
+ if (!table->in_use->userstat_running)
+ {
+ /* Reset all index read values */
+ bzero(index_rows_read, sizeof(index_rows_read[0]) * table->s->keys);
+ return;
+ }
+
+ for (uint index = 0; index < table->s->keys; index++)
+ {
+ if (index_rows_read[index])
+ {
+ INDEX_STATS* index_stats;
+ uint key_length;
+ KEY *key_info = &table->key_info[index]; // Rows were read using this
+
+ DBUG_ASSERT(key_info->cache_name);
+ if (!key_info->cache_name)
+ continue;
+ key_length= table->s->table_cache_key.length + key_info->name_length + 1;
+ pthread_mutex_lock(&LOCK_global_index_stats);
+ // Gets the global index stats, creating one if necessary.
+ if (!(index_stats= (INDEX_STATS*) hash_search(&global_index_stats,
+ key_info->cache_name,
+ key_length)))
+ {
+ if (!(index_stats = ((INDEX_STATS*)
+ my_malloc(sizeof(INDEX_STATS),
+ MYF(MY_WME | MY_ZEROFILL)))))
+ goto end; // Error is already given
+
+ memcpy(index_stats->index, key_info->cache_name, key_length);
+ index_stats->index_name_length= key_length;
+ if (my_hash_insert(&global_index_stats, (uchar*) index_stats))
+ {
+ my_free(index_stats, 0);
+ goto end;
+ }
+ }
+ /* Updates the global index stats. */
+ index_stats->rows_read+= index_rows_read[index];
+ index_rows_read[index]= 0;
+end:
+ pthread_mutex_unlock(&LOCK_global_index_stats);
+ }
+ }
+}
+
+
/****************************************************************************
** Some general functions that isn't in the handler class
****************************************************************************/
@@ -3704,6 +3843,7 @@ int ha_create_table(THD *thd, const char *path,
name= get_canonical_filename(table.file, share.path.str, name_buff);
error= table.file->ha_create(name, &table, create_info);
+
VOID(closefrm(&table, 0));
if (error)
{
@@ -3811,11 +3951,13 @@ int ha_init_key_cache(const char *name, KEY_CACHE *key_cache)
uint tmp_block_size= (uint) key_cache->param_block_size;
uint division_limit= key_cache->param_division_limit;
uint age_threshold= key_cache->param_age_threshold;
+ uint partitions= key_cache->param_partitions;
pthread_mutex_unlock(&LOCK_global_system_variables);
DBUG_RETURN(!init_key_cache(key_cache,
tmp_block_size,
tmp_buff_size,
- division_limit, age_threshold));
+ division_limit, age_threshold,
+ partitions));
}
DBUG_RETURN(0);
}
@@ -3845,10 +3987,12 @@ int ha_resize_key_cache(KEY_CACHE *key_cache)
/**
- Change parameters for key cache (like size)
+ Change parameters for key cache (like division_limit)
*/
int ha_change_key_cache_param(KEY_CACHE *key_cache)
{
+ DBUG_ENTER("ha_change_key_cache_param");
+
if (key_cache->key_cache_inited)
{
pthread_mutex_lock(&LOCK_global_system_variables);
@@ -3857,9 +4001,35 @@ int ha_change_key_cache_param(KEY_CACHE *key_cache)
pthread_mutex_unlock(&LOCK_global_system_variables);
change_key_cache_param(key_cache, division_limit, age_threshold);
}
- return 0;
+ DBUG_RETURN(0);
}
+
+/**
+ Repartition key cache
+*/
+int ha_repartition_key_cache(KEY_CACHE *key_cache)
+{
+ DBUG_ENTER("ha_repartition_key_cache");
+
+ if (key_cache->key_cache_inited)
+ {
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ size_t tmp_buff_size= (size_t) key_cache->param_buff_size;
+ long tmp_block_size= (long) key_cache->param_block_size;
+ uint division_limit= key_cache->param_division_limit;
+ uint age_threshold= key_cache->param_age_threshold;
+ uint partitions= key_cache->param_partitions;
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ DBUG_RETURN(!repartition_key_cache(key_cache, tmp_block_size,
+ tmp_buff_size,
+ division_limit, age_threshold,
+ partitions));
+ }
+ DBUG_RETURN(0);
+}
+
+
/**
Free memory allocated by a key cache.
*/
@@ -4328,17 +4498,16 @@ int handler::read_range_first(const key_range *start_key,
range_key_part= table->key_info[active_index].key_part;
if (!start_key) // Read first record
- result= index_first(table->record[0]);
+ result= ha_index_first(table->record[0]);
else
- result= index_read_map(table->record[0],
- start_key->key,
- start_key->keypart_map,
- start_key->flag);
+ result= ha_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)
? HA_ERR_END_OF_FILE
: result);
-
DBUG_RETURN (compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE);
}
@@ -4364,11 +4533,11 @@ int handler::read_range_next()
if (eq_range)
{
/* We trust that index_next_same always gives a row in range */
- DBUG_RETURN(index_next_same(table->record[0],
- end_range->key,
- end_range->length));
+ DBUG_RETURN(ha_index_next_same(table->record[0],
+ end_range->key,
+ end_range->length));
}
- result= index_next(table->record[0]);
+ result= ha_index_next(table->record[0]);
if (result)
DBUG_RETURN(result);
DBUG_RETURN(compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE);
@@ -4549,6 +4718,8 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
if (!result)
my_eof(thd);
+ else if (!thd->is_error())
+ my_error(ER_GET_ERRNO, MYF(0), 0);
return result;
}
@@ -4750,9 +4921,11 @@ int handler::ha_write_row(uchar *buf)
DBUG_ENTER("handler::ha_write_row");
mark_trx_read_write();
+ increment_statistics(&SSV::ha_write_count);
if (unlikely(error= write_row(buf)))
DBUG_RETURN(error);
+ rows_changed++;
if (unlikely(error= binlog_log_row(table, 0, buf, log_func)))
DBUG_RETURN(error); /* purecov: inspected */
DBUG_RETURN(0);
@@ -4771,9 +4944,11 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
DBUG_ASSERT(new_data == table->record[0]);
mark_trx_read_write();
+ increment_statistics(&SSV::ha_update_count);
if (unlikely(error= update_row(old_data, new_data)))
return error;
+ rows_changed++;
if (unlikely(error= binlog_log_row(table, old_data, new_data, log_func)))
return error;
return 0;
@@ -4785,9 +4960,11 @@ int handler::ha_delete_row(const uchar *buf)
Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function;
mark_trx_read_write();
+ increment_statistics(&SSV::ha_delete_count);
if (unlikely(error= delete_row(buf)))
return error;
+ rows_changed++;
if (unlikely(error= binlog_log_row(table, buf, 0, log_func)))
return error;
return 0;
diff --git a/sql/handler.h b/sql/handler.h
index e8f6abdca65..79e838d2ca6 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -18,6 +18,9 @@
/* Definitions for parameters to do with handler-routines */
+#ifndef SQL_HANDLER_INCLUDED
+#define SQL_HANDLER_INCLUDED
+
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
@@ -31,6 +34,10 @@
#define USING_TRANSACTIONS
+#if MAX_KEY > 128
+#error MAX_KEY is too large. Values up to 128 are supported.
+#endif
+
// the following is for checking tables
#define HA_ADMIN_ALREADY_DONE 1
@@ -131,6 +138,7 @@
#define HA_BINLOG_STMT_CAPABLE (LL(1) << 35)
/* Has automatic checksums and uses the new checksum format */
#define HA_HAS_NEW_CHECKSUM (LL(1) << 36)
+#define HA_CAN_VIRTUAL_COLUMNS (LL(1) << 37)
/*
Set of all binlog flags. Currently only contain the capabilities
@@ -549,6 +557,108 @@ struct handler_log_file_data {
enum log_status status;
};
+/*
+ Definitions for engine-specific table/field/index options in the CREATE TABLE.
+
+ Options are declared with HA_*OPTION_* macros (HA_TOPTION_NUMBER,
+ HA_FOPTION_ENUM, HA_IOPTION_STRING, etc).
+
+ Every macros takes the option name, and the name of the underlying field of
+ the appropriate C structure. The "appropriate C structure" is
+ ha_table_option_struct for table level options,
+ ha_field_option_struct for field level options,
+ ha_index_option_struct for key level options. The engine either
+ defines a structure of this name, or uses #define's to map
+ these "appropriate" names to the actual structure type name.
+
+ ULL options use a ulonglong as the backing store.
+ HA_*OPTION_NUMBER() takes the option name, the structure field name,
+ the default value for the option, min, max, and blk_siz values.
+
+ STRING options use a char* as a backing store.
+ HA_*OPTION_STRING takes the option name and the structure field name.
+ The default value will be 0.
+
+ ENUM options use a uint as a backing store (not enum!!!).
+ HA_*OPTION_ENUM takes the option name, the structure field name,
+ the default value for the option as a number, and a string with the
+ permitted values for this enum - one string with comma separated values,
+ for example: "gzip,bzip2,lzma"
+
+ BOOL options use a bool as a backing store.
+ HA_*OPTION_BOOL takes the option name, the structure field name,
+ and the default value for the option.
+ From the SQL, BOOL options accept YES/NO, ON/OFF, and 1/0.
+
+ The name of the option is limited to 255 bytes,
+ the value (for string options) - to the 32767 bytes.
+
+ See ha_example.cc for an example.
+*/
+
+struct ha_table_option_struct;
+struct ha_field_option_struct;
+struct ha_index_option_struct;
+
+enum ha_option_type { HA_OPTION_TYPE_ULL, /* unsigned long long */
+ HA_OPTION_TYPE_STRING, /* char * */
+ HA_OPTION_TYPE_ENUM, /* uint */
+ HA_OPTION_TYPE_BOOL}; /* bool */
+
+#define HA_xOPTION_NUMBER(name, struc, field, def, min, max, blk_siz) \
+ { HA_OPTION_TYPE_ULL, name, sizeof(name)-1, \
+ offsetof(struc, field), def, min, max, blk_siz, 0 }
+#define HA_xOPTION_STRING(name, struc, field) \
+ { HA_OPTION_TYPE_STRING, name, sizeof(name)-1, \
+ offsetof(struc, field), 0, 0, 0, 0, 0 }
+#define HA_xOPTION_ENUM(name, struc, field, values, def) \
+ { HA_OPTION_TYPE_ENUM, name, sizeof(name)-1, \
+ offsetof(struc, field), def, 0, \
+ sizeof(values)-1, 0, values }
+#define HA_xOPTION_BOOL(name, struc, field, def) \
+ { HA_OPTION_TYPE_BOOL, name, sizeof(name)-1, \
+ offsetof(struc, field), def, 0, 1, 0, 0 }
+#define HA_xOPTION_END { HA_OPTION_TYPE_ULL, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+#define HA_TOPTION_NUMBER(name, field, def, min, max, blk_siz) \
+ HA_xOPTION_NUMBER(name, ha_table_option_struct, field, def, min, max, blk_siz)
+#define HA_TOPTION_STRING(name, field) \
+ HA_xOPTION_STRING(name, ha_table_option_struct, field)
+#define HA_TOPTION_ENUM(name, field, values, def) \
+ HA_xOPTION_ENUM(name, ha_table_option_struct, field, values, def)
+#define HA_TOPTION_BOOL(name, field, def) \
+ HA_xOPTION_BOOL(name, ha_table_option_struct, field, def)
+#define HA_TOPTION_END HA_xOPTION_END
+
+#define HA_FOPTION_NUMBER(name, field, def, min, max, blk_siz) \
+ HA_xOPTION_NUMBER(name, ha_field_option_struct, field, def, min, max, blk_siz)
+#define HA_FOPTION_STRING(name, field) \
+ HA_xOPTION_STRING(name, ha_field_option_struct, field)
+#define HA_FOPTION_ENUM(name, field, values, def) \
+ HA_xOPTION_ENUM(name, ha_field_option_struct, field, values, def)
+#define HA_FOPTION_BOOL(name, field, def) \
+ HA_xOPTION_BOOL(name, ha_field_option_struct, field, def)
+#define HA_FOPTION_END HA_xOPTION_END
+
+#define HA_IOPTION_NUMBER(name, field, def, min, max, blk_siz) \
+ HA_xOPTION_NUMBER(name, ha_index_option_struct, field, def, min, max, blk_siz)
+#define HA_IOPTION_STRING(name, field) \
+ HA_xOPTION_STRING(name, ha_index_option_struct, field)
+#define HA_IOPTION_ENUM(name, field, values, def) \
+ HA_xOPTION_ENUM(name, ha_index_option_struct, field, values, def)
+#define HA_IOPTION_BOOL(name, field, values, def) \
+ HA_xOPTION_BOOL(name, ha_index_option_struct, field, values, def)
+#define HA_IOPTION_END HA_xOPTION_END
+
+typedef struct st_ha_create_table_option {
+ enum ha_option_type type;
+ const char *name;
+ size_t name_length;
+ ptrdiff_t offset;
+ ulonglong def_value;
+ ulonglong min_value, max_value, block_size;
+ const char *values;
+} ha_create_table_option;
enum handler_iterator_type
{
@@ -609,8 +719,9 @@ struct handlerton
SHOW_COMP_OPTION state;
/*
- Historical number used for frm file to determine the correct storage engine.
- This is going away and new engines will just use "name" for this.
+ 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 legacy_db_type db_type;
/*
@@ -720,7 +831,13 @@ struct handlerton
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 */
+ /*
+ Optional clauses in the CREATE/ALTER TABLE
+ */
+ ha_create_table_option *table_options; // table level options
+ ha_create_table_option *field_options; // these are specified per field
+ ha_create_table_option *index_options; // these are specified per index
+
};
@@ -736,7 +853,6 @@ inline LEX_STRING *hton_name(const handlerton *hton)
#define HTON_ALTER_NOT_SUPPORTED (1 << 1) //Engine does not support alter
#define HTON_CAN_RECREATE (1 << 2) //Delete all is used 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
@@ -944,11 +1060,16 @@ typedef struct st_ha_create_information
uint extra_size; /* length of extra data segment */
/** Transactional or not. Unused; reserved for future versions. */
enum ha_choice transactional;
- bool table_existed; /* 1 in create if table existed */
- bool frm_only; /* 1 if no ha_create_table() */
- bool varchar; /* 1 if table has a VARCHAR */
- enum ha_storage_media storage_media; /* DEFAULT, DISK or MEMORY */
- enum ha_choice page_checksum; /* If we have page_checksums */
+ 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
+ engine_option_value *option_list; ///< list of table create options
+ /* the following three are only for ALTER TABLE, check_if_incompatible_data() */
+ ha_table_option_struct *option_struct; ///< structure with parsed table options
+ ha_field_option_struct **fields_option_struct; ///< array of field option structures
+ ha_index_option_struct **indexes_option_struct; ///< array of index option structures
} HA_CREATE_INFO;
@@ -1134,6 +1255,7 @@ public:
enum {NONE=0, INDEX, RND} inited;
bool locked;
bool implicit_emptied; /* Can be !=0 only if HEAP */
+ bool mark_trx_done;
bool cloned; /* 1 if this was created with clone */
const COND *pushed_cond;
/**
@@ -1157,6 +1279,12 @@ public:
Interval returned by get_auto_increment() and being consumed by the
inserter.
*/
+ /* Statistics variables */
+ ulonglong rows_read;
+ ulonglong rows_changed;
+ /* One bigger than needed to avoid to test if key == MAX_KEY */
+ ulonglong index_rows_read[MAX_KEY+1];
+
Discrete_interval auto_inc_interval_for_cur_row;
/**
Number of reserved auto-increment intervals. Serves as a heuristic
@@ -1172,10 +1300,13 @@ public:
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), cloned(0),
+ locked(FALSE), implicit_emptied(FALSE), mark_trx_done(FALSE), cloned(0),
pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
auto_inc_intervals_count(0)
- {}
+ {
+ reset_statistics();
+ }
+
virtual ~handler(void)
{
DBUG_ASSERT(locked == FALSE);
@@ -1212,7 +1343,7 @@ public:
}
/* This is called after index_init() if we need to do a index scan */
virtual int prepare_index_scan() { return 0; }
- int ha_rnd_init(bool scan)
+ int ha_rnd_init(bool scan) __attribute__ ((warn_unused_result))
{
int result;
DBUG_ENTER("ha_rnd_init");
@@ -1227,7 +1358,15 @@ public:
inited=NONE;
DBUG_RETURN(rnd_end());
}
+ int ha_rnd_init_with_error(bool scan) __attribute__ ((warn_unused_result));
int ha_reset();
+ /* Tell handler (not storage engine) this is start of a new statement */
+ void ha_start_of_new_statement()
+ {
+ ft_handler= 0;
+ mark_trx_done= FALSE;
+ }
+
/* this is necessary in many places, e.g. in HANDLER command */
int ha_index_or_rnd_end()
{
@@ -1301,10 +1440,16 @@ public:
virtual void print_error(int error, myf errflag);
virtual bool get_error_message(int error, String *buf);
uint get_dup_key(int error);
+ void reset_statistics()
+ {
+ rows_read= rows_changed= 0;
+ bzero(index_rows_read, sizeof(index_rows_read));
+ }
virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
{
table= table_arg;
table_share= share;
+ reset_statistics();
}
virtual double scan_time()
{ return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
@@ -1428,22 +1573,23 @@ public:
}
/**
@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.
+ 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.
*/
+protected:
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);
+ 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.
+ 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,
@@ -1457,17 +1603,26 @@ public:
virtual int index_last(uchar * buf)
{ return HA_ERR_WRONG_COMMAND; }
virtual int index_next_same(uchar *buf, const uchar *key, uint keylen);
- /**
- @brief
- The following functions works like index_read, but it find the last
- row with the current key value or prefix.
- */
- virtual int index_read_last_map(uchar * buf, const uchar * key,
- key_part_map keypart_map)
+ inline void update_index_statistics()
{
- uint key_len= calculate_key_len(table, active_index, key, keypart_map);
- return index_read_last(buf, key, key_len);
+ index_rows_read[active_index]++;
+ rows_read++;
}
+public:
+
+ /* Similar functions like the above, but does statistics counting */
+ inline int ha_index_read_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ inline int ha_index_read_idx_map(uchar * buf, uint index, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ inline int ha_index_next(uchar * buf);
+ inline int ha_index_prev(uchar * buf);
+ inline int ha_index_first(uchar * buf);
+ inline int ha_index_last(uchar * buf);
+ inline int ha_index_next_same(uchar *buf, const uchar *key, uint keylen);
+
virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
KEY_MULTI_RANGE *ranges, uint range_count,
bool sorted, HANDLER_BUFFER *buffer);
@@ -1481,6 +1636,7 @@ public:
void ft_end() { ft_handler=NULL; }
virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key)
{ return NULL; }
+private:
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;
@@ -1490,11 +1646,20 @@ public:
It will return the row with the PK given in the record argument.
*/
virtual int rnd_pos_by_record(uchar *record)
- {
- position(record);
- return rnd_pos(record, ref);
- }
+ {
+ position(record);
+ return rnd_pos(record, ref);
+ }
virtual int read_first_row(uchar *buf, uint primary_key);
+public:
+
+ /* Same as above, but with statistics */
+ inline int ha_ft_read(uchar *buf);
+ inline int ha_rnd_next(uchar *buf);
+ inline int ha_rnd_pos(uchar *buf, uchar *pos);
+ inline int ha_rnd_pos_by_record(uchar *buf);
+ inline int ha_read_first_row(uchar *buf, uint primary_key);
+
/**
The following 3 function is only needed for tables that may be
internal temporary tables during joins.
@@ -1670,6 +1835,9 @@ public:
virtual bool is_crashed() const { return 0; }
virtual bool auto_repair() const { return 0; }
+ void update_global_table_stats();
+ void update_global_index_stats();
+
#define CHF_CREATE_FLAG 0
#define CHF_DELETE_FLAG 1
#define CHF_RENAME_FLAG 2
@@ -1805,8 +1973,10 @@ public:
LEX_STRING *engine_name() { return hton_name(ht); }
protected:
+ /* deprecated, don't use in new engines */
+ inline void ha_statistic_increment(ulong SSV::*offset) const { }
+
/* 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;
@@ -1826,7 +1996,15 @@ protected:
private:
/* Private helpers */
- inline void mark_trx_read_write();
+ void mark_trx_read_write_part2();
+ inline void mark_trx_read_write()
+ {
+ if (!mark_trx_done)
+ mark_trx_read_write_part2();
+ }
+ inline void increment_statistics(ulong SSV::*offset) const;
+ inline void decrement_statistics(ulong SSV::*offset) const;
+
/*
Low-level primitives for storage engines. These should be
overridden by the storage engine class. To call these methods, use
@@ -1913,8 +2091,6 @@ private:
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
@@ -1987,6 +2163,11 @@ private:
{ return HA_ERR_WRONG_COMMAND; }
virtual int rename_partitions(const char *path)
{ return HA_ERR_WRONG_COMMAND; }
+ friend class ha_partition;
+public:
+ /* XXX to be removed, see ha_partition::partition_ht() */
+ virtual handlerton *partition_ht() const
+ { return ht; }
};
@@ -2069,6 +2250,7 @@ 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_repartition_key_cache(KEY_CACHE *key_cache);
int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache);
int ha_end_key_cache(KEY_CACHE *key_cache);
@@ -2122,3 +2304,5 @@ int ha_binlog_end(THD *thd);
#define ha_binlog_wait(a) do {} while (0)
#define ha_binlog_end(a) do {} while (0)
#endif
+
+#endif
diff --git a/sql/hash_filo.h b/sql/hash_filo.h
index ab13d338695..8ddeeeb02fc 100644
--- a/sql/hash_filo.h
+++ b/sql/hash_filo.h
@@ -107,7 +107,7 @@ public:
return entry;
}
- my_bool add(hash_filo_element *entry)
+ bool add(hash_filo_element *entry)
{
if (cache.records == size)
{
diff --git a/sql/item.cc b/sql/item.cc
index 23294a6dbeb..c08333e4b80 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -429,8 +429,8 @@ Item::Item(THD *thd, Item *item):
with_sum_func(item->with_sum_func),
fixed(item->fixed),
is_autogenerated_name(item->is_autogenerated_name),
- collation(item->collation),
with_subselect(item->with_subselect),
+ collation(item->collation),
cmp_context(item->cmp_context)
{
next= thd->free_list; // Put in free list
@@ -698,9 +698,24 @@ 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);
+ if (field->vcol_info && field->vcol_info->expr_item)
+ return field->vcol_info->expr_item->walk(&Item::register_field_in_read_map,
+ 1, arg);
return 0;
}
+/*
+ @brief
+ Mark field in bitmap supplied as *arg
+*/
+
+bool Item_field::register_field_in_bitmap(uchar *arg)
+{
+ MY_BITMAP *bitmap= (MY_BITMAP *) arg;
+ DBUG_ASSERT(bitmap);
+ bitmap_set_bit(bitmap, field->field_index);
+ return 0;
+}
bool Item::check_cols(uint c)
{
@@ -724,7 +739,8 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
}
if (cs->ctype)
{
- uint orig_len= length;
+ const char *str_start= str;
+
/*
This will probably need a better implementation in the future:
a function in CHARSET_INFO structure.
@@ -734,16 +750,20 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
length--;
str++;
}
- if (orig_len != length && !is_autogenerated_name)
+ if (str != str_start && !is_autogenerated_name)
{
+ char buff[SAFE_NAME_LEN];
+ strmake(buff, str_start,
+ min(sizeof(buff)-1, length + (int) (str-str_start)));
+
if (length == 0)
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY),
- str + length - orig_len);
+ buff);
else
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES),
- str + length - orig_len);
+ buff);
}
}
if (!my_charset_same(cs, system_charset_info))
@@ -2477,14 +2497,12 @@ double_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *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)))
{
- /*
- We can use str_value.ptr() here as Item_string is gurantee to put an
- end \0 here.
- */
+ char buff[80];
+ strmake(buff, cptr, min(sizeof(buff)-1, (size_t) (org_end-cptr)));
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE",
- cptr);
+ buff);
}
return tmp;
}
@@ -2493,8 +2511,10 @@ double_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end)
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());
+ return double_from_string_with_check(str_value.charset(),
+ str_value.ptr(),
+ (char *) str_value.ptr() +
+ str_value.length());
}
@@ -2801,7 +2821,7 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
unsigned_flag= entry->unsigned_flag;
if (limit_clause_param)
{
- my_bool unused;
+ bool unused;
set_int(entry->val_int(&unused), MY_INT64_NUM_DECIMAL_DIGITS);
item_type= Item::INT_ITEM;
DBUG_RETURN(!unsigned_flag && value.integer < 0 ? 1 : 0);
@@ -4483,6 +4503,21 @@ error:
return TRUE;
}
+/*
+ @brief
+ Mark virtual columns as used in a partitioning expression
+*/
+
+bool Item_field::vcol_in_partition_func_processor(uchar *int_arg)
+{
+ DBUG_ASSERT(fixed);
+ if (field->vcol_info)
+ {
+ field->vcol_info->mark_as_in_partitioning_expr();
+ }
+ return FALSE;
+}
+
Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs)
{
diff --git a/sql/item.h b/sql/item.h
index be2d340a4d3..0445cd4a9d5 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -18,6 +18,24 @@
#pragma interface /* gcc class implementation */
#endif
+inline
+bool trace_unsupported_func(const char *where, const char *processor_name)
+{
+ char buff[64];
+ sprintf(buff, "%s::%s", where, processor_name);
+ DBUG_ENTER(buff);
+ sprintf(buff, "%s returns TRUE: unsupported function", processor_name);
+ DBUG_PRINT("info", ("%s", buff));
+ DBUG_RETURN(TRUE);
+}
+
+inline
+bool trace_unsupported_by_check_vcol_func_processor(const char *where)
+{
+ return trace_unsupported_func(where, "check_vcol_func_processor");
+}
+
+
class Protocol;
struct TABLE_LIST;
void item_init(void); /* Init item functions */
@@ -522,19 +540,19 @@ public:
uint name_length; /* Length of name */
int8 marker;
uint8 decimals;
- my_bool maybe_null; /* If item may be null */
- my_bool in_rollup; /* If used in GROUP BY list
+ bool maybe_null; /* If item may be null */
+ bool in_rollup; /* If used in GROUP BY list
of a query with ROLLUP */
- my_bool null_value; /* if item is null */
- my_bool unsigned_flag;
- my_bool with_sum_func;
- my_bool fixed; /* If item fixed with fix_fields */
- my_bool is_autogenerated_name; /* indicate was name of this Item
+ bool null_value; /* if item is null */
+ bool unsigned_flag;
+ bool with_sum_func;
+ bool fixed; /* If item fixed with fix_fields */
+ bool is_autogenerated_name; /* indicate was name of this Item
autogenerated or set by user */
- DTCollation collation;
- my_bool with_subselect; /* If this item is a subselect or some
+ bool with_subselect; /* If this item is a subselect or some
of its arguments is or contains a
subselect */
+ DTCollation collation;
Item_result cmp_context; /* Comparison context */
// alloc & destruct is done as start of select using sql_alloc
Item();
@@ -938,6 +956,11 @@ public:
return FALSE;
}
/*
+ The next function differs from the previous one that a bitmap to be updated
+ is passed as uchar *arg.
+ */
+ virtual bool register_field_in_bitmap(uchar *arg) { return 0; }
+ /*
Check if a partition function is allowed
SYNOPSIS
check_partition_func_processor()
@@ -989,11 +1012,43 @@ public:
fields.
*/
virtual bool check_partition_func_processor(uchar *bool_arg) { return TRUE;}
+ /*
+ @brief
+ Processor used to mark virtual columns used in partitioning expression
+
+ @param
+ arg always ignored
+
+ @retval
+ FALSE always
+ */
+ virtual bool vcol_in_partition_func_processor(uchar *arg)
+ {
+ return FALSE;
+ }
+
virtual bool subst_argument_checker(uchar **arg)
- {
+ {
if (*arg)
- *arg= NULL;
- return TRUE;
+ *arg= NULL;
+ return TRUE;
+ }
+ /*
+ @brief
+ Processor used to check acceptability of an item in the defining
+ expression for a virtual column
+
+ @param
+ arg always ignored
+
+ @retval
+ FALSE the item is accepted in the definition of a virtual column
+ @retval
+ TRUE otherwise
+ */
+ virtual bool check_vcol_func_processor(uchar *arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(full_name());
}
virtual Item *equal_fields_propagator(uchar * arg) { return this; }
@@ -1402,6 +1457,10 @@ public:
{
return value_item->send(protocol, str);
}
+ bool check_vcol_func_processor(uchar *arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor("name_const");
+ }
};
bool agg_item_collations(DTCollation &c, const char *name,
@@ -1421,6 +1480,7 @@ public:
virtual Item_num *neg()= 0;
Item *safe_charset_converter(CHARSET_INFO *tocs);
bool check_partition_func_processor(uchar *int_arg) { return FALSE;}
+ bool check_vcol_func_processor(uchar *arg) { return FALSE;}
};
#define NO_CACHED_FIELD_INDEX ((uint)(-1))
@@ -1581,7 +1641,10 @@ public:
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 register_field_in_bitmap(uchar *arg);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool vcol_in_partition_func_processor(uchar *bool_arg);
+ bool check_vcol_func_processor(uchar *arg) { return FALSE;}
bool enumerate_field_refs_processor(uchar *arg);
void cleanup();
bool result_as_longlong()
@@ -1642,6 +1705,7 @@ public:
Item *safe_charset_converter(CHARSET_INFO *tocs);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *arg) { return FALSE;}
};
class Item_null_result :public Item_null
@@ -1655,7 +1719,11 @@ public:
save_in_field(result_field, no_conversions);
}
bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
-};
+ bool check_vcol_func_processor(uchar *arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(full_name());
+ }
+};
/* Item represents one placeholder ('?') of prepared statement */
@@ -1796,6 +1864,7 @@ public:
/** Item is a argument to a limit clause. */
bool limit_clause_param;
void set_param_type_and_swap_value(Item_param *from);
+
};
@@ -1831,6 +1900,7 @@ public:
{ 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;}
+ bool check_vcol_func_processor(uchar *arg) { return FALSE;}
};
@@ -1849,6 +1919,7 @@ public:
Item_num *neg ();
uint decimal_precision() const { return max_length; }
bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
+ bool check_vcol_func_processor(uchar *arg) { return FALSE;}
};
@@ -1890,6 +1961,7 @@ public:
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;}
+ bool check_vcol_func_processor(uchar *arg) { return FALSE;}
};
@@ -2047,6 +2119,7 @@ public:
}
virtual void print(String *str, enum_query_type query_type);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *arg) { return FALSE;}
/**
Return TRUE if character-set-introducer was explicitly specified in the
@@ -2100,7 +2173,7 @@ 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;
-public:
+ public:
Item_static_string_func(const char *name_par, const char *str, uint length,
CHARSET_INFO *cs,
Derivation dv= DERIVATION_COERCIBLE)
@@ -2114,6 +2187,10 @@ public:
}
bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
+ bool check_vcol_func_processor(uchar *arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name);
+ }
};
@@ -2125,6 +2202,10 @@ public:
CHARSET_INFO *cs= NULL):
Item_string(name_arg, length, cs)
{}
+ bool check_vcol_func_processor(uchar *arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor("safe_string");
+ }
};
@@ -2204,6 +2285,7 @@ public:
bool eq(const Item *item, bool binary_cmp) const;
virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *arg) { return FALSE;}
private:
void hex_string_init(const char *str, uint str_length);
};
@@ -2236,6 +2318,7 @@ public:
save_in_field(result_field, no_conversions);
}
void cleanup();
+ bool check_vcol_func_processor(uchar *arg) { return FALSE;}
/*
This method is used for debug purposes to print the name of an
item to the debug log. The second use of this method is as
@@ -2395,12 +2478,15 @@ public:
if (ref && result_type() == ROW_RESULT)
(*ref)->bring_value();
}
+ bool check_vcol_func_processor(uchar *arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor("ref");
+ }
bool get_time(MYSQL_TIME *ltime)
{
DBUG_ASSERT(fixed);
return (*ref)->get_time(ltime);
}
-
};
@@ -2577,7 +2663,7 @@ 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_with_ref(longlong i, Item *ref_arg, bool unsigned_arg) :
Item_int(i), ref(ref_arg)
{
unsigned_flag= unsigned_arg;
@@ -2689,6 +2775,10 @@ public:
table_map used_tables() const { return (table_map) 1L; }
bool const_item() const { return 0; }
bool is_null() { return null_value; }
+ bool check_vcol_func_processor(uchar *arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor("copy");
+ }
/*
Override the methods below as pure virtual to make sure all the
@@ -2807,7 +2897,7 @@ public:
class Cached_item :public Sql_alloc
{
public:
- my_bool null_value;
+ bool null_value;
Cached_item() :null_value(0) {}
virtual bool cmp(void)=0;
virtual ~Cached_item(); /*line -e1509 */
@@ -2933,6 +3023,10 @@ public:
return arg->walk(processor, walk_subquery, args) ||
(this->*processor)(args);
}
+ bool check_vcol_func_processor(uchar *arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor("values");
+ }
};
@@ -3029,6 +3123,10 @@ private:
BEFORE INSERT of BEFORE UPDATE trigger.
*/
bool read_only;
+ virtual bool check_vcol_func_processor(uchar *arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor("trigger");
+ }
};
@@ -3097,6 +3195,10 @@ public:
{
return this == item;
}
+ bool check_vcol_func_processor(uchar *arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor("cache");
+ }
/**
If this item caches a field value, return pointer to underlying field.
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index c6c09bf0cb5..c4d56a93af6 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -245,36 +245,61 @@ Item_bool_func2* Eq_creator::create(Item *a, Item *b) const
return new Item_func_eq(a, b);
}
+Item_bool_func2* Eq_creator::create_swap(Item *a, Item *b) const
+{
+ return new Item_func_eq(b, a);
+}
Item_bool_func2* Ne_creator::create(Item *a, Item *b) const
{
return new Item_func_ne(a, b);
}
+Item_bool_func2* Ne_creator::create_swap(Item *a, Item *b) const
+{
+ return new Item_func_ne(b, a);
+}
Item_bool_func2* Gt_creator::create(Item *a, Item *b) const
{
return new Item_func_gt(a, b);
}
+Item_bool_func2* Gt_creator::create_swap(Item *a, Item *b) const
+{
+ return new Item_func_lt(b, a);
+}
Item_bool_func2* Lt_creator::create(Item *a, Item *b) const
{
return new Item_func_lt(a, b);
}
+Item_bool_func2* Lt_creator::create_swap(Item *a, Item *b) const
+{
+ return new Item_func_gt(b, a);
+}
Item_bool_func2* Ge_creator::create(Item *a, Item *b) const
{
return new Item_func_ge(a, b);
}
+Item_bool_func2* Ge_creator::create_swap(Item *a, Item *b) const
+{
+ return new Item_func_le(b, a);
+}
Item_bool_func2* Le_creator::create(Item *a, Item *b) const
{
return new Item_func_le(a, b);
}
+Item_bool_func2* Le_creator::create_swap(Item *a, Item *b) const
+{
+ return new Item_func_ge(b, a);
+}
+
/*
Test functions
Most of these returns 0LL if false and 1LL if true and
@@ -412,7 +437,8 @@ static bool convert_constant_item(THD *thd, Item_field *field_item,
LINT_INIT(old_maps[0]);
LINT_INIT(old_maps[1]);
- if (table)
+ /* table->read_set may not be set if we come here from a CREATE TABLE */
+ if (table && table->read_set)
dbug_tmp_use_all_columns(table, old_maps,
table->read_set, table->write_set);
/* For comparison purposes allow invalid dates like 2000-01-32 */
@@ -448,7 +474,7 @@ 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)
+ if (table && table->read_set)
dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_maps);
}
return result;
@@ -825,7 +851,6 @@ Arg_comparator::can_compare_as_dates(Item *a, Item *b, ulonglong *const_value)
return cmp_type;
}
-
/*
Retrieves correct TIME value from the given item.
@@ -876,7 +901,12 @@ get_time_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))
{
+ Query_arena backup;
+ Query_arena *save_arena= thd->switch_to_arena_for_cached_items(&backup);
Item_cache_int *cache= new Item_cache_int();
+ if (save_arena)
+ thd->set_query_arena(save_arena);
+
/* Mark the cache as non-const to prevent re-caching. */
cache->set_used_tables(1);
cache->store_longlong(item, value);
@@ -912,7 +942,12 @@ int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,
cache_converted_constant can't be used here because it can't
correctly convert a DATETIME value from string to int representation.
*/
+ Query_arena backup;
+ Query_arena *save_arena= thd->switch_to_arena_for_cached_items(&backup);
Item_cache_int *cache= new Item_cache_int(MYSQL_TYPE_DATETIME);
+ if (save_arena)
+ thd->set_query_arena(save_arena);
+
/* Mark the cache as non-const to prevent re-caching. */
cache->set_used_tables(1);
if (!(*a)->is_datetime())
@@ -1142,7 +1177,12 @@ 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))
{
+ Query_arena backup;
+ Query_arena *save_arena= thd->switch_to_arena_for_cached_items(&backup);
Item_cache_int *cache= new Item_cache_int(MYSQL_TYPE_DATETIME);
+ if (save_arena)
+ thd->set_query_arena(save_arena);
+
/* Mark the cache as non-const to prevent re-caching. */
cache->set_used_tables(1);
cache->store_longlong(item, value);
@@ -4751,8 +4791,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
We could also do boyer-more for non-const items, but as we would have to
recompute the tables for each row it's not worth it.
*/
- if (args[1]->const_item() && !use_strnxfrm(collation.collation) &&
- !(specialflag & SPECIAL_NO_NEW_FUNC))
+ if (args[1]->const_item() && !use_strnxfrm(collation.collation))
{
String* res2 = args[1]->val_str(&cmp.value2);
if (!res2)
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index f612f944877..0278ad201af 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -220,7 +220,7 @@ public:
class Item_cache;
-#define UNKNOWN ((my_bool)-1)
+#define UNKNOWN (-1)
/*
@@ -249,7 +249,7 @@ protected:
FALSE - result is FALSE
TRUE - result is NULL
*/
- my_bool result_for_null_param;
+ int result_for_null_param;
public:
Item_in_optimizer(Item *a, Item_in_subselect *b):
Item_bool_func(a, my_reinterpret_cast(Item *)(b)), cache(0),
@@ -270,7 +270,14 @@ class Comp_creator
public:
Comp_creator() {} /* Remove gcc warning */
virtual ~Comp_creator() {} /* Remove gcc warning */
+ /**
+ Create operation with given arguments.
+ */
virtual Item_bool_func2* create(Item *a, Item *b) const = 0;
+ /**
+ Create operation with given arguments in swap order.
+ */
+ virtual Item_bool_func2* create_swap(Item *a, Item *b) const = 0;
virtual const char* symbol(bool invert) const = 0;
virtual bool eqne_op() const = 0;
virtual bool l_op() const = 0;
@@ -282,6 +289,7 @@ public:
Eq_creator() {} /* Remove gcc warning */
virtual ~Eq_creator() {} /* Remove gcc warning */
virtual Item_bool_func2* create(Item *a, Item *b) const;
+ virtual Item_bool_func2* create_swap(Item *a, Item *b) const;
virtual const char* symbol(bool invert) const { return invert? "<>" : "="; }
virtual bool eqne_op() const { return 1; }
virtual bool l_op() const { return 0; }
@@ -293,6 +301,7 @@ public:
Ne_creator() {} /* Remove gcc warning */
virtual ~Ne_creator() {} /* Remove gcc warning */
virtual Item_bool_func2* create(Item *a, Item *b) const;
+ virtual Item_bool_func2* create_swap(Item *a, Item *b) const;
virtual const char* symbol(bool invert) const { return invert? "=" : "<>"; }
virtual bool eqne_op() const { return 1; }
virtual bool l_op() const { return 0; }
@@ -304,6 +313,7 @@ public:
Gt_creator() {} /* Remove gcc warning */
virtual ~Gt_creator() {} /* Remove gcc warning */
virtual Item_bool_func2* create(Item *a, Item *b) const;
+ virtual Item_bool_func2* create_swap(Item *a, Item *b) const;
virtual const char* symbol(bool invert) const { return invert? "<=" : ">"; }
virtual bool eqne_op() const { return 0; }
virtual bool l_op() const { return 0; }
@@ -315,6 +325,7 @@ public:
Lt_creator() {} /* Remove gcc warning */
virtual ~Lt_creator() {} /* Remove gcc warning */
virtual Item_bool_func2* create(Item *a, Item *b) const;
+ virtual Item_bool_func2* create_swap(Item *a, Item *b) const;
virtual const char* symbol(bool invert) const { return invert? ">=" : "<"; }
virtual bool eqne_op() const { return 0; }
virtual bool l_op() const { return 1; }
@@ -326,6 +337,7 @@ public:
Ge_creator() {} /* Remove gcc warning */
virtual ~Ge_creator() {} /* Remove gcc warning */
virtual Item_bool_func2* create(Item *a, Item *b) const;
+ virtual Item_bool_func2* create_swap(Item *a, Item *b) const;
virtual const char* symbol(bool invert) const { return invert? "<" : ">="; }
virtual bool eqne_op() const { return 0; }
virtual bool l_op() const { return 0; }
@@ -337,6 +349,7 @@ public:
Le_creator() {} /* Remove gcc warning */
virtual ~Le_creator() {} /* Remove gcc warning */
virtual Item_bool_func2* create(Item *a, Item *b) const;
+ virtual Item_bool_func2* create_swap(Item *a, Item *b) const;
virtual const char* symbol(bool invert) const { return invert? ">" : "<="; }
virtual bool eqne_op() const { return 0; }
virtual bool l_op() const { return 1; }
@@ -657,7 +670,7 @@ struct interval_range
class Item_func_interval :public Item_int_func
{
Item_row *row;
- my_bool use_decimal_comparison;
+ bool use_decimal_comparison;
interval_range *intervals;
public:
Item_func_interval(Item_row *a)
@@ -782,7 +795,7 @@ public:
virtual uchar *get_value(Item *item)=0;
void sort()
{
- my_qsort2(base,used_count,size,compare,collation);
+ my_qsort2(base,used_count,size,compare,(void*)collation);
}
int find(Item *item);
@@ -865,7 +878,7 @@ public:
void value_to_item(uint pos, Item *item)
{
((Item_int*) item)->value= ((packed_longlong*) base)[pos].val;
- ((Item_int*) item)->unsigned_flag= (my_bool)
+ ((Item_int*) item)->unsigned_flag= (bool)
((packed_longlong*) base)[pos].unsigned_flag;
}
Item_result result_type() { return INT_RESULT; }
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 2d1278e7ac3..ed771b60769 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -2078,10 +2078,12 @@ double Item_func_round::real_op()
{
double value= args[0]->val_real();
- if (!(null_value= args[0]->null_value || args[1]->null_value))
- return my_double_round(value, args[1]->val_int(), args[1]->unsigned_flag,
- truncate);
-
+ if (!(null_value= args[0]->null_value))
+ {
+ longlong dec= args[1]->val_int();
+ if (!(null_value= args[1]->null_value))
+ return my_double_round(value, dec, args[1]->unsigned_flag, truncate);
+ }
return 0.0;
}
@@ -3169,11 +3171,15 @@ void Item_udf_func::print(String *str, enum_query_type query_type)
double Item_func_udf_float::val_real()
{
+ double res;
+ my_bool tmp_null_value;
DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_func_udf_float::val");
DBUG_PRINT("info",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
- DBUG_RETURN(udf.val(&null_value));
+ res= udf.val(&tmp_null_value);
+ null_value= tmp_null_value;
+ DBUG_RETURN(res);
}
@@ -3190,9 +3196,13 @@ String *Item_func_udf_float::val_str(String *str)
longlong Item_func_udf_int::val_int()
{
+ longlong res;
+ my_bool tmp_null_value;
DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_func_udf_int::val_int");
- DBUG_RETURN(udf.val_int(&null_value));
+ res= udf.val_int(&tmp_null_value);
+ null_value= tmp_null_value;
+ DBUG_RETURN(res);
}
@@ -3209,8 +3219,10 @@ String *Item_func_udf_int::val_str(String *str)
longlong Item_func_udf_decimal::val_int()
{
- my_decimal dec_buf, *dec= udf.val_decimal(&null_value, &dec_buf);
+ my_bool tmp_null_value;
longlong result;
+ my_decimal dec_buf, *dec= udf.val_decimal(&tmp_null_value, &dec_buf);
+ null_value= tmp_null_value;
if (null_value)
return 0;
my_decimal2int(E_DEC_FATAL_ERROR, dec, unsigned_flag, &result);
@@ -3220,8 +3232,10 @@ longlong Item_func_udf_decimal::val_int()
double Item_func_udf_decimal::val_real()
{
- my_decimal dec_buf, *dec= udf.val_decimal(&null_value, &dec_buf);
+ my_bool tmp_null_value;
double result;
+ my_decimal dec_buf, *dec= udf.val_decimal(&tmp_null_value, &dec_buf);
+ null_value= tmp_null_value;
if (null_value)
return 0.0;
my_decimal2double(E_DEC_FATAL_ERROR, dec, &result);
@@ -3231,18 +3245,24 @@ double Item_func_udf_decimal::val_real()
my_decimal *Item_func_udf_decimal::val_decimal(my_decimal *dec_buf)
{
+ my_decimal *res;
+ my_bool tmp_null_value;
DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_func_udf_decimal::val_decimal");
DBUG_PRINT("info",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
- DBUG_RETURN(udf.val_decimal(&null_value, dec_buf));
+ res= udf.val_decimal(&tmp_null_value, dec_buf);
+ null_value= tmp_null_value;
+ DBUG_RETURN(res);
}
String *Item_func_udf_decimal::val_str(String *str)
{
- my_decimal dec_buf, *dec= udf.val_decimal(&null_value, &dec_buf);
+ my_bool tmp_null_value;
+ my_decimal dec_buf, *dec= udf.val_decimal(&tmp_null_value, &dec_buf);
+ null_value= tmp_null_value;
if (null_value)
return 0;
if (str->length() < DECIMAL_MAX_STR_LENGTH)
@@ -3877,10 +3897,30 @@ bool Item_func_set_user_var::register_field_in_read_map(uchar *arg)
TABLE *table= (TABLE *) arg;
if (result_field->table == table || !table)
bitmap_set_bit(result_field->table->read_set, result_field->field_index);
+ if (result_field->vcol_info)
+ return result_field->vcol_info->
+ expr_item->walk(&Item::register_field_in_read_map, 1, arg);
}
return 0;
}
+/*
+ Mark field in bitmap supplied as *arg
+
+*/
+
+bool Item_func_set_user_var::register_field_in_bitmap(uchar *arg)
+{
+ MY_BITMAP *bitmap = (MY_BITMAP *) arg;
+ DBUG_ASSERT(bitmap);
+ if (result_field)
+ {
+ if (!bitmap)
+ return 1;
+ bitmap_set_bit(bitmap, result_field->field_index);
+ }
+ return 0;
+}
/**
Set value to user variable.
@@ -3984,7 +4024,7 @@ Item_func_set_user_var::update_hash(void *ptr, uint length,
/** Get the value of a variable as a double. */
-double user_var_entry::val_real(my_bool *null_value)
+double user_var_entry::val_real(bool *null_value)
{
if ((*null_value= (value == 0)))
return 0.0;
@@ -4013,7 +4053,7 @@ double user_var_entry::val_real(my_bool *null_value)
/** Get the value of a variable as an integer. */
-longlong user_var_entry::val_int(my_bool *null_value) const
+longlong user_var_entry::val_int(bool *null_value) const
{
if ((*null_value= (value == 0)))
return LL(0);
@@ -4045,7 +4085,7 @@ longlong user_var_entry::val_int(my_bool *null_value) const
/** Get the value of a variable as a string. */
-String *user_var_entry::val_str(my_bool *null_value, String *str,
+String *user_var_entry::val_str(bool *null_value, String *str,
uint decimals)
{
if ((*null_value= (value == 0)))
@@ -4078,7 +4118,7 @@ String *user_var_entry::val_str(my_bool *null_value, String *str,
/** Get the value of a variable as a decimal. */
-my_decimal *user_var_entry::val_decimal(my_bool *null_value, my_decimal *val)
+my_decimal *user_var_entry::val_decimal(bool *null_value, my_decimal *val)
{
if ((*null_value= (value == 0)))
return 0;
@@ -5767,7 +5807,7 @@ Item_func_sp::cleanup()
sp_result_field= NULL;
}
m_sp= NULL;
- dummy_table->alias= NULL;
+ dummy_table->alias.free();
Item_func::cleanup();
}
@@ -5793,7 +5833,7 @@ Item_func_sp::func_name() const
qname.append('.');
}
append_identifier(thd, &qname, m_name->m_name.str, m_name->m_name.length);
- return qname.ptr();
+ return qname.c_ptr_safe();
}
@@ -5849,7 +5889,7 @@ Item_func_sp::init_result_field(THD *thd)
*/
share= dummy_table->s;
- dummy_table->alias = "";
+ dummy_table->alias.set("", 0, table_alias_charset);
dummy_table->maybe_null = maybe_null;
dummy_table->in_use= thd;
dummy_table->copy_blobs= TRUE;
diff --git a/sql/item_func.h b/sql/item_func.h
index ea9ed39159a..e750e372691 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -414,6 +414,7 @@ public:
void fix_length_and_dec();
bool fix_fields(THD *thd, Item **ref);
longlong val_int() { DBUG_ASSERT(fixed == 1); return value; }
+ bool check_vcol_func_processor(uchar *int_arg) { return TRUE;}
};
@@ -474,6 +475,7 @@ 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;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
};
@@ -509,6 +511,7 @@ public:
my_decimal *decimal_op(my_decimal *);
void result_precision();
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
};
@@ -541,6 +544,7 @@ public:
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
};
@@ -555,6 +559,7 @@ public:
void result_precision();
void fix_length_and_dec();
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
};
@@ -571,6 +576,7 @@ public:
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;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
};
@@ -584,6 +590,7 @@ public:
const char *func_name() const { return "abs"; }
void fix_length_and_dec();
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
};
// A class to handle logarithmic and trigonometric functions
@@ -739,6 +746,7 @@ public:
double real_op();
my_decimal *decimal_op(my_decimal *);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
};
@@ -751,6 +759,7 @@ public:
double real_op();
my_decimal *decimal_op(my_decimal *);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
};
/* This handles round and truncate */
@@ -782,6 +791,10 @@ public:
void update_used_tables();
bool fix_fields(THD *thd, Item **ref);
void cleanup() { first_eval= TRUE; Item_real_func::cleanup(); }
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
private:
void seed_random (Item * val);
};
@@ -1064,6 +1077,10 @@ public:
max_length= args[0]->max_length;
}
bool fix_fields(THD *thd, Item **ref);
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -1077,6 +1094,10 @@ public:
const char *func_name() const { return "benchmark"; }
void fix_length_and_dec() { max_length=1; maybe_null=0; }
virtual void print(String *str, enum_query_type query_type);
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -1092,6 +1113,10 @@ public:
used_tables_cache|= RAND_TABLE_BIT;
}
longlong val_int();
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -1341,6 +1366,10 @@ class Item_func_get_lock :public Item_int_func
longlong val_int();
const char *func_name() const { return "get_lock"; }
void fix_length_and_dec() { max_length=1; maybe_null=1;}
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
class Item_func_release_lock :public Item_int_func
@@ -1351,6 +1380,10 @@ public:
longlong val_int();
const char *func_name() const { return "release_lock"; }
void fix_length_and_dec() { max_length=1; maybe_null=1;}
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
/* replication functions */
@@ -1364,6 +1397,10 @@ public:
longlong val_int();
const char *func_name() const { return "master_pos_wait"; }
void fix_length_and_dec() { max_length=21; maybe_null=1;}
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -1436,6 +1473,7 @@ public:
}
void save_org_in_field(Field *field) { (void)save_in_field(field, 1, 0); }
bool register_field_in_read_map(uchar *arg);
+ bool register_field_in_bitmap(uchar *arg);
bool set_entry(THD *thd, bool create_if_not_exists);
void cleanup();
};
@@ -1523,7 +1561,7 @@ class Item_func_get_system_var :public Item_func
longlong cached_llval;
double cached_dval;
String cached_strval;
- my_bool cached_null_value;
+ bool cached_null_value;
query_id_t used_query_id;
uchar cache_present;
@@ -1610,6 +1648,11 @@ public:
bool fix_index();
void init_search(bool no_order);
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ /* TODO: consider adding in support for the MATCH-based virtual columns */
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -1629,6 +1672,17 @@ public:
longlong val_int();
const char *func_name() const { return "is_free_lock"; }
void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;}
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+#if 0
+ DBUG_ENTER("Item_func_is_free_lock::check_vcol_func_processor");
+ DBUG_PRINT("info",
+ ("check_vcol_func_processor returns TRUE: unsupported function"));
+ DBUG_RETURN(TRUE);
+#else
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+#endif
+ }
};
class Item_func_is_used_lock :public Item_int_func
@@ -1639,6 +1693,10 @@ public:
longlong val_int();
const char *func_name() const { return "is_used_lock"; }
void fix_length_and_dec() { decimals=0; max_length=10; maybe_null=1;}
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
/* For type casts */
@@ -1658,6 +1716,11 @@ public:
longlong val_int();
const char *func_name() const { return "row_count"; }
void fix_length_and_dec() { decimals= 0; maybe_null=0; }
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -1766,6 +1829,10 @@ public:
{
return sp_result_field;
}
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -1776,6 +1843,10 @@ public:
longlong val_int();
const char *func_name() const { return "found_rows"; }
void fix_length_and_dec() { decimals= 0; maybe_null=0; }
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -1790,5 +1861,9 @@ public:
void fix_length_and_dec()
{ max_length= 21; unsigned_flag=1; }
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
diff --git a/sql/item_row.h b/sql/item_row.h
index 9838e360e5b..15dadb3d01c 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -80,4 +80,5 @@ public:
bool check_cols(uint c);
bool null_inside() { return with_null; };
void bring_value();
-};
+ bool check_vcol_func_processor(uchar *int_arg) {return FALSE; }
+ };
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 60d5d2f1341..1488fe25fd9 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -2754,7 +2754,7 @@ String *Item_func_conv::val_str(String *str)
from_base, &endptr, &err);
}
- ptr= longlong2str(dec, ans, to_base);
+ ptr= longlong2str(dec, ans, to_base, 1);
if (str->copy(ans, (uint32) (ptr-ans), default_charset()))
return make_empty_result();
return str;
@@ -2915,7 +2915,7 @@ String *Item_func_hex::val_str(String *str)
if ((null_value= args[0]->null_value))
return 0;
- ptr= longlong2str(dec,ans,16);
+ ptr= longlong2str(dec,ans,16,1);
if (str->copy(ans,(uint32) (ptr-ans),default_charset()))
return make_empty_result(); // End of memory
return str;
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index e8fa041af4f..b731c13a871 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -363,6 +363,10 @@ public:
String *val_str(String *);
void fix_length_and_dec() { maybe_null=1; max_length = 13; }
const char *func_name() const { return "encrypt"; }
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
#include "sql_crypt.h"
@@ -411,6 +415,11 @@ public:
call
*/
virtual const char *fully_qualified_func_name() const = 0;
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(
+ fully_qualified_func_name());
+ }
};
@@ -674,6 +683,10 @@ public:
maybe_null=1;
max_length=MAX_BLOB_WIDTH;
}
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -873,5 +886,9 @@ public:
}
const char *func_name() const{ return "uuid"; }
String *val_str(String *);
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index b74ea0e804d..fa9eff0d95b 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -390,7 +390,10 @@ Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param,
{
DBUG_ENTER("Item_maxmin_subselect::Item_maxmin_subselect");
max= max_arg;
- init(select_lex, new select_max_min_finder_subselect(this, max_arg));
+ init(select_lex,
+ new select_max_min_finder_subselect(this, max_arg,
+ parent->substype() ==
+ Item_subselect::ALL_SUBS));
max_columns= 1;
maybe_null= 1;
max_columns= 1;
@@ -1008,11 +1011,20 @@ Item_in_subselect::single_value_transformer(JOIN *join,
}
Item *subs;
- if (!select_lex->group_list.elements &&
- !select_lex->having &&
- !select_lex->with_sum_func &&
- !(select_lex->next_select()) &&
- select_lex->table_list.elements)
+ /*
+ Check if optimization with aggregate min/max possible
+ 1 There is no aggregate in the subquery
+ 2 It is not UNION
+ 3 There is tables
+ 4 It is not ALL subquery with possible NULLs in the SELECT list
+ */
+ if (!select_lex->group_list.elements && /*1*/
+ !select_lex->having && /*1*/
+ !select_lex->with_sum_func && /*1*/
+ !(select_lex->next_select()) && /*2*/
+ select_lex->table_list.elements && /*3*/
+ (!select_lex->ref_pointer_array[0]->maybe_null || /*4*/
+ substype() != Item_subselect::ALL_SUBS)) /*4*/
{
Item_sum_hybrid *item;
nesting_map save_allow_sum_func;
@@ -1072,8 +1084,11 @@ Item_in_subselect::single_value_transformer(JOIN *join,
if (upper_item)
upper_item->set_sub_test(item);
}
- /* fix fields is already called for left expression */
- substitution= func->create(left_expr, subs);
+ /*
+ The swap is needed for expressions of type 'f1 < ALL ( SELECT ....)'
+ where we want to evaluate the sub query even if f1 would be null.
+ */
+ substitution= func->create_swap(left_expr, subs);
DBUG_RETURN(RES_OK);
}
@@ -2075,13 +2090,14 @@ int subselect_uniquesubquery_engine::scan_table()
if (table->file->inited)
table->file->ha_index_end();
- table->file->ha_rnd_init(1);
+ if (table->file->ha_rnd_init_with_error(1))
+ DBUG_RETURN(1);
table->file->extra_opt(HA_EXTRA_CACHE,
current_thd->variables.read_buff_size);
table->null_row= 0;
for (;;)
{
- error=table->file->rnd_next(table->record[0]);
+ error=table->file->ha_rnd_next(table->record[0]);
if (error && error != HA_ERR_END_OF_FILE)
{
error= report_error(table, error);
@@ -2255,10 +2271,11 @@ int subselect_uniquesubquery_engine::exec()
if (!table->file->inited)
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);
+ error= table->file->ha_index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->
+ ref.key_parts),
+ HA_READ_KEY_EXACT);
if (error &&
error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
error= report_error(table, error);
@@ -2376,10 +2393,11 @@ int subselect_indexsubquery_engine::exec()
if (!table->file->inited)
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);
+ error= table->file->ha_index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->
+ ref.key_parts),
+ HA_READ_KEY_EXACT);
if (error &&
error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
error= report_error(table, error);
@@ -2400,9 +2418,9 @@ int subselect_indexsubquery_engine::exec()
((Item_in_subselect *) item)->value= 1;
break;
}
- error= table->file->index_next_same(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length);
+ error= table->file->ha_index_next_same(table->record[0],
+ tab->ref.key_buff,
+ tab->ref.key_length);
if (error && error != HA_ERR_END_OF_FILE)
{
error= report_error(table, error);
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 775e4b4b317..9842641788b 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -32,7 +32,7 @@ class Item_bool_func2;
class Item_subselect :public Item_result_field
{
- my_bool value_assigned; /* value already assigned to subselect */
+ bool value_assigned; /* value already assigned to subselect */
protected:
/* thread handler, will be assigned in fix_fields only */
THD *thd;
@@ -139,6 +139,10 @@ public:
bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
bool mark_as_eliminated_processor(uchar *arg);
bool enumerate_field_refs_processor(uchar *arg);
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor("subselect");
+ }
/**
Get the SELECT_LEX structure associated with this Item.
@@ -429,10 +433,10 @@ protected:
class subselect_single_select_engine: public subselect_engine
{
- my_bool prepared; /* simple subselect is prepared */
- my_bool optimized; /* simple subselect is optimized */
- my_bool executed; /* simple subselect is executed */
- my_bool optimize_error; ///< simple subselect optimization failed
+ bool prepared; /* simple subselect is prepared */
+ bool optimized; /* simple subselect is optimized */
+ bool executed; /* simple subselect is executed */
+ bool optimize_error; /* simple subselect optimization failed */
st_select_lex *select_lex; /* corresponding select_lex */
JOIN * join; /* corresponding JOIN structure */
public:
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 92c2ba83f23..ce7602b7f03 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -2617,8 +2617,10 @@ void Item_udf_sum::clear()
bool Item_udf_sum::add()
{
+ my_bool tmp_null_value;
DBUG_ENTER("Item_udf_sum::add");
- udf.add(&null_value);
+ udf.add(&tmp_null_value);
+ null_value= tmp_null_value;
DBUG_RETURN(0);
}
@@ -2654,11 +2656,15 @@ Item *Item_sum_udf_float::copy_or_same(THD* thd)
double Item_sum_udf_float::val_real()
{
+ my_bool tmp_null_value;
+ double res;
DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_sum_udf_float::val");
DBUG_PRINT("info",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
- DBUG_RETURN(udf.val(&null_value));
+ res= udf.val(&tmp_null_value);
+ null_value= tmp_null_value;
+ DBUG_RETURN(res);
}
@@ -2694,12 +2700,16 @@ longlong Item_sum_udf_decimal::val_int()
my_decimal *Item_sum_udf_decimal::val_decimal(my_decimal *dec_buf)
{
+ my_decimal *res;
+ my_bool tmp_null_value;
DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_func_udf_decimal::val_decimal");
DBUG_PRINT("info",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
- DBUG_RETURN(udf.val_decimal(&null_value, dec_buf));
+ res= udf.val_decimal(&tmp_null_value, dec_buf);
+ null_value= tmp_null_value;
+ DBUG_RETURN(res);
}
@@ -2716,11 +2726,15 @@ Item *Item_sum_udf_int::copy_or_same(THD* thd)
longlong Item_sum_udf_int::val_int()
{
+ my_bool tmp_null_value;
+ longlong res;
DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_sum_udf_int::val_int");
DBUG_PRINT("info",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
- DBUG_RETURN(udf.val_int(&null_value));
+ res= udf.val_int(&tmp_null_value);
+ null_value= tmp_null_value;
+ DBUG_RETURN(res);
}
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 27dfb90ecf4..3d4b00e3b52 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -385,6 +385,10 @@ public:
Item *get_arg(int i) { return args[i]; }
Item *set_arg(int i, THD *thd, Item *new_val);
uint get_arg_count() { return arg_count; }
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -665,6 +669,10 @@ public:
}
void fix_length_and_dec() {}
enum Item_result result_type () const { return hybrid_type; }
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor("avg_field");
+ }
const char *func_name() const { DBUG_ASSERT(0); return "avg_field"; }
};
@@ -734,6 +742,10 @@ public:
}
void fix_length_and_dec() {}
enum Item_result result_type () const { return hybrid_type; }
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor("var_field");
+ }
const char *func_name() const { DBUG_ASSERT(0); return "variance_field"; }
};
@@ -1265,8 +1277,13 @@ public:
void make_unique();
double val_real()
{
- String *res; res=val_str(&str_value);
- return res ? my_atof(res->c_ptr()) : 0.0;
+ int error;
+ const char *end;
+ String *res;
+ if (!(res= val_str(&str_value)))
+ return 0.0;
+ end= res->ptr() + res->length();
+ return (my_strtod(res->ptr(), (char**) &end, &error));
}
longlong val_int()
{
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 4daef708fa3..963e28b80e8 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -72,6 +72,7 @@ public:
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;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_date_args();
@@ -92,6 +93,7 @@ public:
maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_date_args();
@@ -124,6 +126,7 @@ public:
maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_date_args();
@@ -141,6 +144,7 @@ public:
enum Item_result result_type () const { return STRING_RESULT; }
void fix_length_and_dec();
bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
+ bool check_vcol_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -157,6 +161,7 @@ public:
maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_date_args();
@@ -177,6 +182,7 @@ public:
maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_time_args();
@@ -197,6 +203,7 @@ public:
maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_time_args();
@@ -217,6 +224,7 @@ public:
maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_date_args();
@@ -237,6 +245,7 @@ public:
maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_time_args();
@@ -271,6 +280,7 @@ public:
maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_date_args();
@@ -293,6 +303,7 @@ public:
maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_date_args();
@@ -327,6 +338,7 @@ public:
maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_date_args();
@@ -343,6 +355,7 @@ class Item_func_dayname :public Item_func_weekday
enum Item_result result_type () const { return STRING_RESULT; }
void fix_length_and_dec();
bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
};
@@ -369,6 +382,14 @@ public:
decimals=0;
max_length=10*MY_CHARSET_BIN_MB_MAXLEN;
}
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ /*
+ TODO: Allow UNIX_TIMESTAMP called with an argument to be a part
+ of the expression for a virtual column
+ */
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -385,6 +406,7 @@ public:
max_length=10*MY_CHARSET_BIN_MB_MAXLEN;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_time_args();
@@ -509,6 +531,10 @@ public:
*/
virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0;
bool result_as_longlong() { return TRUE; }
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -545,6 +571,10 @@ public:
void fix_length_and_dec();
bool get_date(MYSQL_TIME *res, uint fuzzy_date);
virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0;
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -585,6 +615,10 @@ public:
void fix_length_and_dec();
bool get_date(MYSQL_TIME *res, uint fuzzy_date);
virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0;
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
@@ -642,6 +676,7 @@ public:
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;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return has_date_args() || has_time_args();
@@ -772,6 +807,7 @@ class Item_extract :public Item_int_func
bool eq(const Item *item, bool binary_cmp) const;
virtual void print(String *str, enum_query_type query_type);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
switch (int_type) {
@@ -1058,6 +1094,7 @@ public:
maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
{
return !has_time_args();
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index 7464fbb440e..57995b2b643 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -222,6 +222,11 @@ public:
collation.collation= pxml->charset();
}
const char *func_name() const { return "nodeset"; }
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
+
};
@@ -528,6 +533,10 @@ public:
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; }
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
longlong val_int()
{
diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h
index dadbb5ccf42..ce33d161c79 100644
--- a/sql/item_xmlfunc.h
+++ b/sql/item_xmlfunc.h
@@ -40,6 +40,10 @@ public:
}
void fix_length_and_dec();
String *parse_xml(String *raw_xml, String *parsed_xml_buf);
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
};
diff --git a/sql/key.cc b/sql/key.cc
index 6336b0b8a5f..fec67b35ebc 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -281,8 +281,7 @@ bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length)
key++;
store_length--;
}
- if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART |
- HA_BIT_PART))
+ if (!(key_part->key_part_flag & HA_CAN_MEMCMP))
{
if (key_part->field->key_cmp(key, key_part->length))
return 1;
diff --git a/sql/lex.h b/sql/lex.h
index a22b491b739..f304e0412fa 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -65,6 +65,7 @@ static SYMBOL symbols[] = {
{ "ALL", SYM(ALL)},
{ "ALGORITHM", SYM(ALGORITHM_SYM)},
{ "ALTER", SYM(ALTER)},
+ { "ALWAYS", SYM(ALWAYS_SYM)},
{ "ANALYZE", SYM(ANALYZE_SYM)},
{ "AND", SYM(AND_SYM)},
{ "ANY", SYM(ANY_SYM)},
@@ -109,6 +110,7 @@ static SYMBOL symbols[] = {
{ "CHECKSUM", SYM(CHECKSUM_SYM)},
{ "CIPHER", SYM(CIPHER_SYM)},
{ "CLIENT", SYM(CLIENT_SYM)},
+ { "CLIENT_STATISTICS", SYM(CLIENT_STATS_SYM)},
{ "CLOSE", SYM(CLOSE_SYM)},
{ "COALESCE", SYM(COALESCE)},
{ "CODE", SYM(CODE_SYM)},
@@ -223,6 +225,7 @@ static SYMBOL symbols[] = {
{ "FULL", SYM(FULL)},
{ "FULLTEXT", SYM(FULLTEXT_SYM)},
{ "FUNCTION", SYM(FUNCTION_SYM)},
+ { "GENERATED", SYM(GENERATED_SYM)},
{ "GEOMETRY", SYM(GEOMETRY_SYM)},
{ "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION)},
{ "GET_FORMAT", SYM(GET_FORMAT)},
@@ -248,6 +251,7 @@ static SYMBOL symbols[] = {
{ "IN", SYM(IN_SYM)},
{ "INDEX", SYM(INDEX_SYM)},
{ "INDEXES", SYM(INDEXES)},
+ { "INDEX_STATISTICS", SYM(INDEX_STATS_SYM)},
{ "INFILE", SYM(INFILE)},
{ "INITIAL_SIZE", SYM(INITIAL_SIZE_SYM)},
{ "INNER", SYM(INNER_SYM)},
@@ -388,14 +392,16 @@ static SYMBOL symbols[] = {
{ "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)},
+ { "PARSER", SYM(PARSER_SYM)},
+ { "PARSE_VCOL_EXPR", SYM(PARSE_VCOL_EXPR_SYM)},
{ "PARTIAL", SYM(PARTIAL)},
{ "PARTITION", SYM(PARTITION_SYM)},
{ "PARTITIONING", SYM(PARTITIONING_SYM)},
{ "PARTITIONS", SYM(PARTITIONS_SYM)},
{ "PASSWORD", SYM(PASSWORD)},
+ { "PERSISTENT", SYM(PERSISTENT_SYM)},
{ "PHASE", SYM(PHASE_SYM)},
{ "PLUGIN", SYM(PLUGIN_SYM)},
{ "PLUGINS", SYM(PLUGINS_SYM)},
@@ -481,6 +487,7 @@ static SYMBOL symbols[] = {
{ "SIGNED", SYM(SIGNED_SYM)},
{ "SIMPLE", SYM(SIMPLE_SYM)},
{ "SLAVE", SYM(SLAVE)},
+ { "SLOW", SYM(SLOW_SYM)},
{ "SNAPSHOT", SYM(SNAPSHOT_SYM)},
{ "SMALLINT", SYM(SMALLINT)},
{ "SOCKET", SYM(SOCKET_SYM)},
@@ -529,6 +536,7 @@ static SYMBOL symbols[] = {
{ "TABLE", SYM(TABLE_SYM)},
{ "TABLES", SYM(TABLES)},
{ "TABLESPACE", SYM(TABLESPACE)},
+ { "TABLE_STATISTICS", SYM(TABLE_STATS_SYM)},
{ "TABLE_CHECKSUM", SYM(TABLE_CHECKSUM_SYM)},
{ "TEMPORARY", SYM(TEMPORARY)},
{ "TEMPTABLE", SYM(TEMPTABLE_SYM)},
@@ -572,6 +580,7 @@ static SYMBOL symbols[] = {
{ "USE", SYM(USE_SYM)},
{ "USER", SYM(USER)},
{ "USER_RESOURCES", SYM(RESOURCES)},
+ { "USER_STATISTICS", SYM(USER_STATS_SYM)},
{ "USE_FRM", SYM(USE_FRM)},
{ "USING", SYM(USING)},
{ "UTC_DATE", SYM(UTC_DATE_SYM)},
@@ -584,13 +593,15 @@ static SYMBOL symbols[] = {
{ "VARCHARACTER", SYM(VARCHAR)},
{ "VARIABLES", SYM(VARIABLES)},
{ "VARYING", SYM(VARYING)},
+ { "VIA", SYM(VIA_SYM)},
+ { "VIEW", SYM(VIEW_SYM)},
+ { "VIRTUAL", SYM(VIRTUAL_SYM)},
{ "WAIT", SYM(WAIT_SYM)},
{ "WARNINGS", SYM(WARNINGS)},
{ "WEEK", SYM(WEEK_SYM)},
{ "WHEN", SYM(WHEN_SYM)},
{ "WHERE", SYM(WHERE)},
{ "WHILE", SYM(WHILE_SYM)},
- { "VIEW", SYM(VIEW_SYM)},
{ "WITH", SYM(WITH)},
{ "WORK", SYM(WORK_SYM)},
{ "WRAPPER", SYM(WRAPPER_SYM)},
diff --git a/sql/lock.cc b/sql/lock.cc
index 7fb725c9861..1b66fd3d33f 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -903,7 +903,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
*write_lock_used=table;
if (table->db_stat & HA_READ_ONLY)
{
- my_error(ER_OPEN_AS_READONLY,MYF(0),table->alias);
+ my_error(ER_OPEN_AS_READONLY,MYF(0),table->alias.c_ptr());
/* Clear the lock type of the lock data that are stored already. */
sql_lock->lock_count= (uint) (locks - sql_lock->locks);
reset_lock_data(sql_lock);
@@ -927,7 +927,7 @@ 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;
- (*org_locks)->lock->name= table->alias;
+ (*org_locks)->lock->name= table->alias.c_ptr();
}
}
/*
diff --git a/sql/log.cc b/sql/log.cc
index 95f8e9e82ee..047439f6b73 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -112,6 +112,27 @@ char *make_default_log_name(char *buff,const char* log_ext)
MYF(MY_UNPACK_FILENAME|MY_REPLACE_EXT));
}
+
+/*
+ Create a filename from a base with a given suffix.
+ The name is allocated trough my_once_alloc(), so one should only
+ use this for startup options that can all be freed at once.
+*/
+
+char *make_once_alloced_filename(const char *basename, const char *ext)
+{
+ char buff[FN_REFLEN+10], *end, *res;
+ size_t length;
+ strmake(buff, basename, sizeof(buff)-10);
+ end= strmov(fn_ext(buff), ext);
+ length= (size_t) (end - buff) + 1;
+
+ if ((res= (char*) my_once_alloc(length, MYF(MY_WME))))
+ memcpy(res, buff, length);
+ return res;
+}
+
+
/*
Helper class to hold a mutex for the duration of the
block.
@@ -413,7 +434,7 @@ bool Log_to_csv_event_handler::
need_close= TRUE;
if (table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) ||
- table->file->ha_rnd_init(0))
+ table->file->ha_rnd_init_with_error(0))
goto err;
need_rnd_end= TRUE;
@@ -564,7 +585,7 @@ bool Log_to_csv_event_handler::
need_close= TRUE;
if (table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) ||
- table->file->ha_rnd_init(0))
+ table->file->ha_rnd_init_with_error(0))
goto err;
need_rnd_end= TRUE;
@@ -825,6 +846,13 @@ void Log_to_file_event_handler::flush()
mysql_slow_log.reopen_file();
}
+void Log_to_file_event_handler::flush_slow_log()
+{
+ /* reopen slow log file */
+ if (opt_slow_log)
+ mysql_slow_log.reopen_file();
+}
+
/*
Log error with all enabled log event handlers
@@ -920,8 +948,6 @@ void LOGGER::init_log_tables()
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
@@ -933,7 +959,24 @@ bool LOGGER::flush_logs(THD *thd)
/* end of log flush */
logger.unlock();
- return rc;
+ return 0;
+}
+
+
+bool LOGGER::flush_slow_log(THD *thd)
+{
+ /*
+ 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_slow_log();
+
+ /* end of log flush */
+ logger.unlock();
+ return 0;
}
@@ -1731,7 +1774,7 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
log_query.append("`"))
DBUG_RETURN(1);
int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
- Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(),
+ Query_log_event qinfo(thd, log_query.ptr(), log_query.length(),
TRUE, TRUE, errcode);
DBUG_RETURN(mysql_bin_log.write(&qinfo));
}
@@ -1755,7 +1798,7 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
log_query.append("`"))
DBUG_RETURN(1);
int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
- Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(),
+ Query_log_event qinfo(thd, log_query.ptr(), log_query.length(),
TRUE, TRUE, errcode);
DBUG_RETURN(mysql_bin_log.write(&qinfo));
}
@@ -4424,6 +4467,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info)
if (likely(is_open()))
{
IO_CACHE *file= &log_file;
+ my_off_t my_org_b_tell;
#ifdef HAVE_REPLICATION
/*
In the future we need to add to the following if tests like
@@ -4441,6 +4485,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info)
}
#endif /* HAVE_REPLICATION */
+ my_org_b_tell= my_b_tell(file);
+
#if defined(USING_TRANSACTIONS)
/*
Should we write to the binlog cache or to the binlog on disk?
@@ -4459,7 +4505,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info)
2 - --binlog-direct-non-transactional-updates is set and we are about to
use the statement format. When using the row format (cache_stmt == TRUE).
*/
- if (opt_using_transactions && thd)
+ if (opt_using_transactions)
{
if (thd->binlog_setup_trx_data())
goto err;
@@ -4502,7 +4548,6 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info)
If row-based binlogging, Insert_id, Rand and other kind of "setting
context" events are not needed.
*/
- if (thd)
{
if (!thd->current_stmt_binlog_row_based)
{
@@ -4549,16 +4594,16 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info)
}
}
- /*
- Write the SQL command
- */
-
+ /* Write the SQL command */
if (event_info->write(file) ||
DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
goto err;
if (file == &log_file) // we are writing to the real log (disk)
{
+ ulonglong data_written= (my_b_tell(file) - my_org_b_tell);
+ status_var_add(thd->status_var.binlog_bytes_written, data_written);
+
if (flush_and_sync())
goto err;
signal_update();
@@ -4717,6 +4762,7 @@ uint MYSQL_BIN_LOG::next_file_id()
SYNOPSIS
write_cache()
+ thd Current_thread
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
@@ -4726,7 +4772,8 @@ uint MYSQL_BIN_LOG::next_file_id()
be reset as a READ_CACHE to be able to read the contents from it.
*/
-int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
+int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache, bool lock_log,
+ bool sync_log)
{
Mutex_sentry sentry(lock_log ? &LOCK_log : NULL);
@@ -4774,6 +4821,7 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
/* write the first half of the split header */
if (my_b_write(&log_file, header, carry))
return ER_ERROR_ON_WRITE;
+ status_var_add(thd->status_var.binlog_bytes_written, carry);
/*
copy fixed second half of header to cache so the correct
@@ -4842,6 +4890,8 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
/* Write data to the binary log file */
if (my_b_write(&log_file, cache->read_pos, length))
return ER_ERROR_ON_WRITE;
+ status_var_add(thd->status_var.binlog_bytes_written, length);
+
cache->read_pos=cache->read_end; // Mark buffer used up
} while ((length= my_b_fill(cache)));
@@ -4869,7 +4919,8 @@ int query_error_code(THD *thd, bool not_killed)
is not set to these errors when specified not_killed by the
caller.
*/
- if (error == ER_SERVER_SHUTDOWN || error == ER_QUERY_INTERRUPTED)
+ if (error == ER_SERVER_SHUTDOWN || error == ER_QUERY_INTERRUPTED ||
+ error == ER_NEW_ABORTING_CONNECTION)
error= 0;
}
else
@@ -4897,6 +4948,7 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd, bool lock)
if (lock)
pthread_mutex_lock(&LOCK_log);
error= ev.write(&log_file);
+ status_var_add(thd->status_var.binlog_bytes_written, ev.data_written);
if (lock)
{
if (!error && !(error= flush_and_sync()))
@@ -4968,21 +5020,28 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event,
*/
if (qinfo.write(&log_file))
goto err;
+ status_var_add(thd->status_var.binlog_bytes_written, qinfo.data_written);
DBUG_EXECUTE_IF("crash_before_writing_xid",
{
- if ((write_error= write_cache(cache, false, true)))
+ if ((write_error= write_cache(thd, cache, FALSE,
+ TRUE)))
DBUG_PRINT("info", ("error writing binlog cache: %d",
write_error));
DBUG_PRINT("info", ("crashing before writing xid"));
DBUG_SUICIDE();
});
- if ((write_error= write_cache(cache, false, false)))
+ if ((write_error= write_cache(thd, cache, FALSE, FALSE)))
goto err;
- if (commit_event && commit_event->write(&log_file))
- goto err;
+ if (commit_event)
+ {
+ if (commit_event->write(&log_file))
+ goto err;
+ status_var_add(thd->status_var.binlog_bytes_written,
+ commit_event->data_written);
+ }
if (incident && write_incident(thd, FALSE))
goto err;
@@ -5203,9 +5262,23 @@ static bool test_if_number(register const char *str,
void sql_perror(const char *message)
{
-#ifdef HAVE_STRERROR
+#if defined(_WIN32)
+ char* buf;
+ DWORD dw= GetLastError();
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL ) > 0)
+ {
+ sql_print_error("%s: %s",message, buf);
+ LocalFree((HLOCAL)buf);
+ }
+ else
+ {
+ sql_print_error("%s", message);
+ }
+#elif defined(HAVE_STRERROR)
sql_print_error("%s: %s",message, strerror(errno));
-#else
+#else
perror(message);
#endif
}
@@ -6158,3 +6231,20 @@ mysql_declare_plugin(binlog)
NULL /* config options */
}
mysql_declare_plugin_end;
+maria_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 */
+ "1.0", /* string version */
+ MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
+}
+maria_declare_plugin_end;
diff --git a/sql/log.h b/sql/log.h
index 361ad0f2a91..4b32ecfdc02 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -361,9 +361,9 @@ public:
void reset_gathered_updates(THD *thd);
bool write(Log_event* event_info); // binary log write
bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident);
-
bool write_incident(THD *thd, bool lock);
- int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync);
+ int write_cache(THD *thd, IO_CACHE *cache, bool lock_log,
+ bool flush_and_sync);
void set_write_error(THD *thd);
bool check_write_error(THD *thd);
@@ -501,6 +501,7 @@ public:
const char *sql_text, uint sql_text_len,
CHARSET_INFO *client_cs);
void flush();
+ void flush_slow_log();
void init_pthread_objects();
MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; }
MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; }
@@ -545,6 +546,7 @@ public:
void init_base();
void init_log_tables();
bool flush_logs(THD *thd);
+ bool flush_slow_log(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 */
diff --git a/sql/log_event.cc b/sql/log_event.cc
index f56fa3c698a..927b61eb391 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -580,7 +580,7 @@ append_query_string(CHARSET_INFO *csinfo,
if (to->reserve(orig_len + from->length()*2+3))
return 1;
- beg= to->c_ptr_quick() + to->length();
+ beg= (char*) to->ptr() + to->length();
ptr= beg;
if (csinfo->escape_with_backslash_is_dangerous)
ptr= str_to_hex(ptr, from->ptr(), from->length());
@@ -1133,7 +1133,7 @@ failed my_b_read"));
goto err;
}
if ((res= read_log_event(buf, data_len, &error, description_event)))
- res->register_temp_buf(buf);
+ res->register_temp_buf(buf, TRUE);
err:
UNLOCK_MUTEX;
@@ -4691,7 +4691,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
*/
lex_start(thd);
thd->lex->local_file= local_fname;
- mysql_reset_thd_for_next_command(thd);
+ mysql_reset_thd_for_next_command(thd, 0);
if (!use_rli_only_for_errors)
{
@@ -6491,7 +6491,7 @@ int Append_block_log_event::do_apply_event(Relay_log_info const *rli)
as the present method does not call mysql_parse().
*/
lex_start(thd);
- mysql_reset_thd_for_next_command(thd);
+ mysql_reset_thd_for_next_command(thd, 0);
my_delete(fname, MYF(0)); // old copy may exist already
if ((fd= my_create(fname, CREATE_MODE,
O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
@@ -7431,7 +7431,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
we need to do any changes to that value after this function.
*/
lex_start(thd);
- mysql_reset_thd_for_next_command(thd);
+ mysql_reset_thd_for_next_command(thd, 0);
/*
The current statement is just about to begin and
has not yet modified anything. Note, all.modified is reset
@@ -8233,6 +8233,111 @@ Table_map_log_event::~Table_map_log_event()
my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
}
+
+#ifdef MYSQL_CLIENT
+
+/*
+ Rewrite database name for the event to name specified by new_db
+ SYNOPSIS
+ new_db Database name to change to
+ new_len Length
+ desc Event describing binlog that we're writing to.
+
+ DESCRIPTION
+ Reset db name. This function assumes that temp_buf member contains event
+ representation taken from a binary log. It resets m_dbnam and m_dblen and
+ rewrites temp_buf with new db name.
+
+ RETURN
+ 0 - Success
+ other - Error
+*/
+
+int Table_map_log_event::rewrite_db(const char* new_db, size_t new_len,
+ const Format_description_log_event* desc)
+{
+ DBUG_ENTER("Table_map_log_event::rewrite_db");
+ DBUG_ASSERT(temp_buf);
+
+ uint header_len= min(desc->common_header_len,
+ LOG_EVENT_MINIMAL_HEADER_LEN) + TABLE_MAP_HEADER_LEN;
+ int len_diff;
+
+ if (!(len_diff= new_len - m_dblen))
+ {
+ memcpy((void*) (temp_buf + header_len + 1), new_db, m_dblen + 1);
+ memcpy((void*) m_dbnam, new_db, m_dblen + 1);
+ DBUG_RETURN(0);
+ }
+
+ // Create new temp_buf
+ ulong event_cur_len= uint4korr(temp_buf + EVENT_LEN_OFFSET);
+ ulong event_new_len= event_cur_len + len_diff;
+ char* new_temp_buf= (char*) my_malloc(event_new_len, MYF(MY_WME));
+
+ if (!new_temp_buf)
+ {
+ sql_print_error("Table_map_log_event::rewrite_db: "
+ "failed to allocate new temp_buf (%d bytes required)",
+ event_new_len);
+ DBUG_RETURN(-1);
+ }
+
+ // Rewrite temp_buf
+ char* ptr= new_temp_buf;
+ ulong cnt= 0;
+
+ // Copy header and change event length
+ memcpy(ptr, temp_buf, header_len);
+ int4store(ptr + EVENT_LEN_OFFSET, event_new_len);
+ ptr += header_len;
+ cnt += header_len;
+
+ // Write new db name length and new name
+ *ptr++ = new_len;
+ memcpy(ptr, new_db, new_len + 1);
+ ptr += new_len + 1;
+ cnt += m_dblen + 2;
+
+ // Copy rest part
+ memcpy(ptr, temp_buf + cnt, event_cur_len - cnt);
+
+ // Reregister temp buf
+ free_temp_buf();
+ register_temp_buf(new_temp_buf, TRUE);
+
+ // Reset m_dbnam and m_dblen members
+ m_dblen= new_len;
+
+ // m_dbnam resides in m_memory together with m_tblnam and m_coltype
+ uchar* memory= m_memory;
+ char const* tblnam= m_tblnam;
+ uchar* coltype= m_coltype;
+
+ m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
+ &m_dbnam, (uint) m_dblen + 1,
+ &m_tblnam, (uint) m_tbllen + 1,
+ &m_coltype, (uint) m_colcnt,
+ NullS);
+
+ if (!m_memory)
+ {
+ sql_print_error("Table_map_log_event::rewrite_db: "
+ "failed to allocate new m_memory (%d + %d + %d bytes required)",
+ m_dblen + 1, m_tbllen + 1, m_colcnt);
+ DBUG_RETURN(-1);
+ }
+
+ memcpy((void*)m_dbnam, new_db, m_dblen + 1);
+ memcpy((void*)m_tblnam, tblnam, m_tbllen + 1);
+ memcpy(m_coltype, coltype, m_colcnt);
+
+ my_free(memory, MYF(MY_WME));
+ DBUG_RETURN(0);
+}
+#endif /* MYSQL_CLIENT */
+
+
/*
Return value is an error code, one of:
@@ -8830,7 +8935,7 @@ Rows_log_event::write_row(const Relay_log_info *const rli,
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);
+ error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref);
if (error)
{
DBUG_PRINT("info",("rnd_pos() returns error %d",error));
@@ -8862,10 +8967,10 @@ Rows_log_event::write_row(const Relay_log_info *const rli,
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);
+ error= table->file->ha_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)));
@@ -9108,10 +9213,10 @@ record_compare_exit:
/**
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.
+ 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
@@ -9172,13 +9277,14 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
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);
+ int error= table->file->ha_rnd_pos(table->record[0],
+ table->file->ref);
ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0],
table->s->reclength) == 0);
*/
DBUG_PRINT("info",("locating record using primary key (position)"));
- int error= table->file->rnd_pos_by_record(table->record[0]);
+ int error= table->file->ha_rnd_pos_by_record(table->record[0]);
if (error)
{
DBUG_PRINT("info",("rnd_pos returns error %d",error));
@@ -9238,9 +9344,9 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
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)))
+ if ((error= table->file->ha_index_read_map(table->record[0], m_key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT)))
{
DBUG_PRINT("info",("no record matching the key found in the table"));
if (error == HA_ERR_RECORD_DELETED)
@@ -9329,7 +9435,7 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
256U - (1U << table->s->last_null_bit_pos);
}
- while ((error= table->file->index_next(table->record[0])))
+ while ((error= table->file->ha_index_next(table->record[0])))
{
/* We just skip records that has already been deleted */
if (error == HA_ERR_RECORD_DELETED)
@@ -9353,11 +9459,10 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
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)))
+ if ((error= table->file->ha_rnd_init_with_error(1)))
{
DBUG_PRINT("info",("error initializing table scan"
" (ha_rnd_init returns %d)",error));
- table->file->print_error(error, MYF(0));
goto err;
}
@@ -9365,7 +9470,7 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
do
{
restart_rnd_next:
- error= table->file->rnd_next(table->record[0]);
+ error= table->file->ha_rnd_next(table->record[0]);
if (error)
DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
@@ -9383,7 +9488,14 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
case HA_ERR_END_OF_FILE:
if (++restart_count < 2)
- table->file->ha_rnd_init(1);
+ {
+ int error2;
+ if ((error2= table->file->ha_rnd_init_with_error(1)))
+ {
+ error= error2;
+ goto err;
+ }
+ }
break;
default:
diff --git a/sql/log_event.h b/sql/log_event.h
index 645585c8ccb..a8b6e95ea2e 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -883,6 +883,13 @@ public:
event's type, and its content is distributed in the event-specific fields.
*/
char *temp_buf;
+
+ /*
+ TRUE <=> this event 'owns' temp_buf and should call my_free() when done
+ with it
+ */
+ bool event_owns_temp_buf;
+
/*
Timestamp on the master(for debugging and replication of
NOW()/TIMESTAMP). It is important for queries and LOAD DATA
@@ -1024,12 +1031,17 @@ public:
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 register_temp_buf(char* buf, bool must_free)
+ {
+ temp_buf= buf;
+ event_owns_temp_buf= must_free;
+ }
void free_temp_buf()
{
if (temp_buf)
{
- my_free(temp_buf, MYF(0));
+ if (event_owns_temp_buf)
+ my_free(temp_buf, MYF(0));
temp_buf = 0;
}
}
@@ -1364,7 +1376,7 @@ protected:
MODE_PIPES_AS_CONCAT==0x2
MODE_ANSI_QUOTES==0x4
MODE_IGNORE_SPACE==0x8
- MODE_NOT_USED==0x10
+ MODE_IGNORE_BAD_TABLE_OPTIONS==0x10
MODE_ONLY_FULL_GROUP_BY==0x20
MODE_NO_UNSIGNED_SUBTRACTION==0x40
MODE_NO_DIR_IN_CREATE==0x80
@@ -3354,6 +3366,8 @@ public:
ulong get_table_id() const { return m_table_id; }
const char *get_table_name() const { return m_tblnam; }
const char *get_db_name() const { return m_dbnam; }
+ int rewrite_db(const char* new_name, size_t new_name_len,
+ const Format_description_log_event*);
#endif
virtual Log_event_type get_type_code() { return TABLE_MAP_EVENT; }
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index cc9d712a834..94e45bc9ca6 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -77,7 +77,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
we need to do any changes to that value after this function.
*/
lex_start(ev_thd);
- mysql_reset_thd_for_next_command(ev_thd);
+ mysql_reset_thd_for_next_command(ev_thd, 0);
/*
Check if the slave is set to use SBR. If so, it should switch
@@ -582,7 +582,7 @@ replace_record(THD *thd, TABLE *table,
*/
if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
{
- error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
+ error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref);
if (error)
{
DBUG_PRINT("info",("rnd_pos() returns error %d",error));
@@ -608,10 +608,10 @@ replace_record(THD *thd, TABLE *table,
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);
+ error= table->file->ha_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));
@@ -723,13 +723,13 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
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);
+ int error= table->file->ha_rnd_pos(table->record[0], table->file->ref);
ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0],
table->s->reclength) == 0);
*/
table->file->position(table->record[0]);
- int error= table->file->rnd_pos(table->record[0], table->file->ref);
+ int error= table->file->ha_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].
@@ -767,8 +767,9 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
my_ptrdiff_t const pos=
table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0;
table->record[1][pos]= 0xFF;
- if ((error= table->file->index_read_map(table->record[1], key, HA_WHOLE_KEY,
- HA_READ_KEY_EXACT)))
+ if ((error= table->file->ha_index_read_map(table->record[1], key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT)))
{
table->file->print_error(error, MYF(0));
table->file->ha_index_end();
@@ -822,7 +823,7 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
256U - (1U << table->s->last_null_bit_pos);
}
- while ((error= table->file->index_next(table->record[1])))
+ while ((error= table->file->ha_index_next(table->record[1])))
{
/* We just skip records that has already been deleted */
if (error == HA_ERR_RECORD_DELETED)
@@ -844,14 +845,14 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
int error;
/* We don't have a key: search the table using rnd_next() */
- if ((error= table->file->ha_rnd_init(1)))
+ if ((error= table->file->ha_rnd_init_with_error(1)))
return error;
/* Continue until we find the right record or have made a full loop */
do
{
restart_rnd_next:
- error= table->file->rnd_next(table->record[1]);
+ error= table->file->ha_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);
@@ -868,15 +869,19 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
goto restart_rnd_next;
case HA_ERR_END_OF_FILE:
- if (++restart_count < 2)
- table->file->ha_rnd_init(1);
- break;
+ if (++restart_count < 2)
+ {
+ int error2;
+ if ((error2= table->file->ha_rnd_init_with_error(1)))
+ DBUG_RETURN(error2);
+ }
+ break;
default:
- table->file->print_error(error, MYF(0));
+ table->file->print_error(error, MYF(0));
DBUG_PRINT("info", ("Record not found"));
table->file->ha_rnd_end();
- DBUG_RETURN(error);
+ DBUG_RETURN(error);
}
}
while (restart_count < 2 && record_compare(table));
@@ -2144,7 +2149,7 @@ Old_rows_log_event::write_row(const Relay_log_info *const rli,
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);
+ error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref);
if (error)
{
DBUG_PRINT("info",("rnd_pos() returns error %d",error));
@@ -2176,10 +2181,10 @@ Old_rows_log_event::write_row(const Relay_log_info *const rli,
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);
+ error= table->file->ha_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));
@@ -2330,13 +2335,13 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
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);
+ int error= table->file->ha_rnd_pos(table->record[0], table->file->ref);
ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0],
table->s->reclength) == 0);
*/
DBUG_PRINT("info",("locating record using primary key (position)"));
- int error= table->file->rnd_pos_by_record(table->record[0]);
+ int error= table->file->ha_rnd_pos_by_record(table->record[0]);
if (error)
{
DBUG_PRINT("info",("rnd_pos returns error %d",error));
@@ -2396,9 +2401,9 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
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)))
+ if ((error= table->file->ha_index_read_map(table->record[0], m_key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT)))
{
DBUG_PRINT("info",("no record matching the key found in the table"));
if (error == HA_ERR_RECORD_DELETED)
@@ -2487,7 +2492,7 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
256U - (1U << table->s->last_null_bit_pos);
}
- while ((error= table->file->index_next(table->record[0])))
+ while ((error= table->file->ha_index_next(table->record[0])))
{
/* We just skip records that has already been deleted */
if (error == HA_ERR_RECORD_DELETED)
@@ -2511,11 +2516,10 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
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)))
+ if ((error= table->file->ha_rnd_init_with_error(1)))
{
DBUG_PRINT("info",("error initializing table scan"
" (ha_rnd_init returns %d)",error));
- table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
@@ -2523,7 +2527,7 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
do
{
restart_rnd_next:
- error= table->file->rnd_next(table->record[0]);
+ error= table->file->ha_rnd_next(table->record[0]);
switch (error) {
@@ -2535,7 +2539,11 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
case HA_ERR_END_OF_FILE:
if (++restart_count < 2)
- table->file->ha_rnd_init(1);
+ {
+ int error2;
+ if ((error2= table->file->ha_rnd_init_with_error(1)))
+ DBUG_RETURN(error2);
+ }
break;
default:
diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc
new file mode 100644
index 00000000000..086dc292dec
--- /dev/null
+++ b/sql/mysql_install_db.cc
@@ -0,0 +1,637 @@
+/* Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You 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_install_db creates a new database instance (optionally as service)
+ on Windows.
+*/
+#define DONT_DEFINE_VOID
+#include <my_global.h>
+#include <my_getopt.h>
+#include <my_sys.h>
+#include <m_string.h>
+
+#include <windows.h>
+#include <assert.h>
+#include <shellapi.h>
+#include <accctrl.h>
+#include <aclapi.h>
+
+#define USAGETEXT \
+"mysql_install_db.exe Ver 1.00 for Windows\n" \
+"Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub\n" \
+"This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n" \
+"and you are welcome to modify and redistribute it under the GPL v2 license\n" \
+"Usage: mysql_install_db.exe [OPTIONS]\n" \
+"OPTIONS:"
+
+extern "C" const char mysql_bootstrap_sql[];
+
+char default_os_user[]= "NT AUTHORITY\\NetworkService";
+static int create_db_instance();
+static uint opt_silent;
+static char datadir_buffer[FN_REFLEN];
+static char mysqld_path[FN_REFLEN];
+static char *opt_datadir;
+static char *opt_service;
+static char *opt_password;
+static int opt_port;
+static char *opt_socket;
+static char *opt_os_user;
+static char *opt_os_password;
+static my_bool opt_default_user;
+static my_bool opt_allow_remote_root_access;
+static my_bool opt_skip_networking;
+static my_bool verbose_errors;
+
+
+static struct my_option my_long_options[]=
+{
+ {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"datadir", 'd', "Data directory of the new database",
+ &opt_datadir, &opt_datadir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"service", 'S', "Name of the Windows service",
+ &opt_service, &opt_service, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"password", 'p', "Root password",
+ &opt_password, &opt_password, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"port", 'P', "mysql port",
+ &opt_port, &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"socket", 'W',
+ "named pipe name (if missing, it will be set the same as service)",
+ &opt_socket, &opt_socket, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"default-user", 'D', "Create default user",
+ &opt_default_user, &opt_default_user, 0 , GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"allow-remote-root-access", 'R',
+ "Allows remote access from network for user root",
+ &opt_allow_remote_root_access, &opt_allow_remote_root_access, 0 , GET_BOOL,
+ OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"skip-networking", 'N', "Do not use TCP connections, use pipe instead",
+ &opt_skip_networking, &opt_skip_networking, 0 , GET_BOOL, OPT_ARG, 0, 0, 0, 0,
+ 0, 0},
+ {"silent", 's', "Print less information", &opt_silent,
+ &opt_silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+
+static my_bool
+get_one_option(int optid,
+ const struct my_option *opt __attribute__ ((unused)),
+ char *argument __attribute__ ((unused)))
+{
+ DBUG_ENTER("get_one_option");
+ switch (optid) {
+ case '?':
+ printf("%s\n", USAGETEXT);
+ my_print_help(my_long_options);
+ exit(0);
+ break;
+ }
+ DBUG_RETURN(0);
+}
+
+
+static void die(const char *fmt, ...)
+{
+ va_list args;
+ DBUG_ENTER("die");
+
+ /* Print the error message */
+ va_start(args, fmt);
+ fprintf(stderr, "FATAL ERROR: ");
+ vfprintf(stderr, fmt, args);
+ fputc('\n', stderr);
+ if (verbose_errors)
+ {
+ fprintf(stderr,
+ "http://kb.askmonty.org/v/installation-issues-on-windows contains some help\n"
+ "for solving the most common problems. If this doesn't help you, please\n"
+ "leave a comment in the knowledge base or file a bug report at\n"
+ "https://bugs.launchpad.net/maria");
+ }
+ fflush(stderr);
+ va_end(args);
+ my_end(0);
+ exit(1);
+}
+
+
+static void verbose(const char *fmt, ...)
+{
+ va_list args;
+
+ if (opt_silent)
+ return;
+
+ /* Print the verbose message */
+ va_start(args, fmt);
+ vfprintf(stdout, fmt, args);
+ fputc('\n', stdout);
+ fflush(stdout);
+ va_end(args);
+}
+
+
+int main(int argc, char **argv)
+{
+ int error;
+ char self_name[FN_REFLEN];
+ char *p;
+
+ MY_INIT(argv[0]);
+ GetModuleFileName(NULL, self_name, FN_REFLEN);
+ strcpy(mysqld_path,self_name);
+ p= strrchr(mysqld_path, FN_LIBCHAR);
+ if (p)
+ {
+ strcpy(p, "\\mysqld.exe");
+ }
+
+ if ((error= handle_options(&argc, &argv, my_long_options, get_one_option)))
+ exit(error);
+ if (!opt_datadir)
+ {
+ my_print_help(my_long_options);
+ die("parameter --datadir=# is mandatory");
+ }
+
+ /* Print some help on errors */
+ verbose_errors= TRUE;
+
+ if (!opt_os_user)
+ {
+ opt_os_user= default_os_user;
+ opt_os_password= NULL;
+ }
+ /* Workaround WiX bug (strip possible quote character at the end of path) */
+ size_t len= strlen(opt_datadir);
+ if (len > 0)
+ {
+ if (opt_datadir[len-1] == '"')
+ {
+ opt_datadir[len-1]= 0;
+ }
+ }
+ GetFullPathName(opt_datadir, FN_REFLEN, datadir_buffer, NULL);
+ opt_datadir= datadir_buffer;
+
+ if (create_db_instance())
+ {
+ die("database creation failed");
+ }
+
+ printf("Creation of the database was successfull");
+ return 0;
+}
+
+
+
+/**
+ Convert slashes in paths into MySQL-compatible form
+*/
+
+static void convert_slashes(char *s)
+{
+ for (; *s ; s++)
+ if (*s == '\\')
+ *s= '/';
+}
+
+
+/**
+ Calculate basedir from mysqld.exe path.
+ Basedir assumed to be is one level up from the mysqld.exe directory location.
+ E.g basedir for C:\my\bin\mysqld.exe would be C:\my
+*/
+
+static void get_basedir(char *basedir, int size, const char *mysqld_path)
+{
+ strcpy_s(basedir, size, mysqld_path);
+ convert_slashes(basedir);
+ char *p= strrchr(basedir,'/');
+ if (p)
+ {
+ *p = 0;
+ p= strrchr(basedir, '/');
+ if (p)
+ *p= 0;
+ }
+}
+
+
+/**
+ Allocate and initialize command line for mysqld --bootstrap.
+ The resulting string is passed to popen, so it has a lot of quoting
+ quoting around the full string plus quoting around parameters with spaces.
+*/
+
+static char *init_bootstrap_command_line(char *cmdline, size_t size)
+{
+ char basedir[MAX_PATH];
+ get_basedir(basedir, sizeof(basedir), mysqld_path);
+
+ my_snprintf(cmdline, size-1,
+ "\"\"%s\" --no-defaults --bootstrap"
+ " \"--language=%s\\share\\english\""
+ " --basedir=. --datadir=. --default-storage-engine=myisam"
+ " --max_allowed_packet=9M --loose-skip-innodb --loose-skip-pbxt"
+ " --net-buffer-length=16k\"", mysqld_path, basedir);
+ return cmdline;
+}
+
+
+/**
+ Create my.ini in current directory (this is assumed to be
+ data directory as well).
+*/
+
+static int create_myini()
+{
+ my_bool enable_named_pipe= FALSE;
+ printf("Creating my.ini file\n");
+
+ char path_buf[MAX_PATH];
+ GetCurrentDirectory(MAX_PATH, path_buf);
+
+ /* Create ini file. */
+ FILE *myini= fopen("my.ini","wt");
+ if (!myini)
+ {
+ die("Cannot create my.ini in data directory");
+ }
+
+ /* Write out server settings. */
+ fprintf(myini, "[mysqld]\n");
+ convert_slashes(path_buf);
+ fprintf(myini, "datadir=%s\n", path_buf);
+ if (opt_skip_networking)
+ {
+ fprintf(myini,"skip-networking\n");
+ if (!opt_socket)
+ opt_socket= opt_service;
+ }
+ enable_named_pipe= (my_bool)
+ ((opt_socket && opt_socket[0]) || opt_skip_networking);
+
+ if (enable_named_pipe)
+ {
+ fprintf(myini,"enable-named-pipe\n");
+ }
+
+ if (opt_socket && opt_socket[0])
+ {
+ fprintf(myini, "socket=%s\n", opt_socket);
+ }
+ if (opt_port)
+ {
+ fprintf(myini,"port=%d\n", opt_port);
+ }
+
+ /* Write out client settings. */
+ fprintf(myini, "[client]\n");
+
+ /* Used for named pipes */
+ if (opt_socket && opt_socket[0])
+ fprintf(myini,"socket=%s\n",opt_socket);
+ if (opt_skip_networking)
+ fprintf(myini,"protocol=pipe\n");
+ else if (opt_port)
+ fprintf(myini,"port=%d\n",opt_port);
+ fclose(myini);
+ return 0;
+}
+
+
+static const char update_root_passwd_part1[]=
+ "UPDATE mysql.user SET Password = PASSWORD('";
+static const char update_root_passwd_part2[]=
+ "') where User='root';\n";
+static const char remove_default_user_cmd[]=
+ "DELETE FROM mysql.user where User='';\n";
+static const char allow_remote_root_access_cmd[]=
+ "CREATE TEMPORARY TABLE tmp_user LIKE user;\n"
+ "INSERT INTO tmp_user SELECT * from user where user='root' "
+ " AND host='localhost';\n"
+ "UPDATE tmp_user SET host='%';\n"
+ "INSERT INTO user SELECT * FROM tmp_user;\n"
+ "DROP TABLE tmp_user;\n";
+static const char end_of_script[]="-- end.";
+
+/* Register service. Assume my.ini is in datadir */
+
+static int register_service()
+{
+ char buf[3*MAX_PATH +32]; /* path to mysqld.exe, to my.ini, service name */
+ SC_HANDLE sc_manager, sc_service;
+
+ size_t datadir_len= strlen(opt_datadir);
+ const char *backslash_after_datadir= "\\";
+
+ if (datadir_len && opt_datadir[datadir_len-1] == '\\')
+ backslash_after_datadir= "";
+
+ verbose("Registering service '%s'", opt_service);
+ my_snprintf(buf, sizeof(buf)-1,
+ "\"%s\" \"--defaults-file=%s%smy.ini\" \"%s\"" , mysqld_path, opt_datadir,
+ backslash_after_datadir, opt_service);
+
+ /* Get a handle to the SCM database. */
+ sc_manager= OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ if (!sc_manager)
+ {
+ die("OpenSCManager failed (%u)\n", GetLastError());
+ }
+
+ /* Create the service. */
+ sc_service= CreateService(sc_manager, opt_service, opt_service,
+ SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,
+ SERVICE_ERROR_NORMAL, buf, NULL, NULL, NULL, opt_os_user, opt_os_password);
+
+ if (!sc_service)
+ {
+ CloseServiceHandle(sc_manager);
+ die("CreateService failed (%u)", GetLastError());
+ }
+
+ SERVICE_DESCRIPTION sd= { "MariaDB database server" };
+ ChangeServiceConfig2(sc_service, SERVICE_CONFIG_DESCRIPTION, &sd);
+ CloseServiceHandle(sc_service);
+ CloseServiceHandle(sc_manager);
+ return 0;
+}
+
+
+static void clean_directory(const char *dir)
+{
+ char dir2[MAX_PATH+2];
+ *(strmake(dir2, dir, MAX_PATH+1)+1)= 0;
+
+ SHFILEOPSTRUCT fileop;
+ fileop.hwnd= NULL; /* no status display */
+ fileop.wFunc= FO_DELETE; /* delete operation */
+ fileop.pFrom= dir2; /* source file name as double null terminated string */
+ fileop.pTo= NULL; /* no destination needed */
+ fileop.fFlags= FOF_NOCONFIRMATION|FOF_SILENT; /* do not prompt the user */
+
+
+ fileop.fAnyOperationsAborted= FALSE;
+ fileop.lpszProgressTitle= NULL;
+ fileop.hNameMappings= NULL;
+
+ SHFileOperation(&fileop);
+}
+
+
+/*
+ Define directory permission to have inheritable all access for a user
+ (defined as username or group string or as SID)
+*/
+
+static int set_directory_permissions(const char *dir, const char *os_user)
+{
+
+ struct{
+ TOKEN_USER tokenUser;
+ BYTE buffer[SECURITY_MAX_SID_SIZE];
+ } tokenInfoBuffer;
+
+ HANDLE hDir= CreateFile(dir,READ_CONTROL|WRITE_DAC,0,NULL,OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,NULL);
+ if (hDir == INVALID_HANDLE_VALUE)
+ return -1;
+ ACL* pOldDACL;
+ SECURITY_DESCRIPTOR* pSD= NULL;
+ EXPLICIT_ACCESS ea={0};
+ BOOL isWellKnownSID= FALSE;
+ WELL_KNOWN_SID_TYPE wellKnownSidType = WinNullSid;
+ PSID pSid= NULL;
+
+ GetSecurityInfo(hDir, SE_FILE_OBJECT , DACL_SECURITY_INFORMATION,NULL, NULL,
+ &pOldDACL, NULL, (void**)&pSD);
+
+ if (os_user)
+ {
+ /* Check for 3 predefined service users
+ They might have localized names in non-English Windows, thus they need
+ to be handled using well-known SIDs.
+ */
+ if (stricmp(os_user, "NT AUTHORITY\\NetworkService") == 0)
+ {
+ wellKnownSidType= WinNetworkServiceSid;
+ }
+ else if (stricmp(os_user, "NT AUTHORITY\\LocalService") == 0)
+ {
+ wellKnownSidType= WinLocalServiceSid;
+ }
+ else if (stricmp(os_user, "NT AUTHORITY\\LocalSystem") == 0)
+ {
+ wellKnownSidType= WinLocalSystemSid;
+ }
+
+ if (wellKnownSidType != WinNullSid)
+ {
+ DWORD size= SECURITY_MAX_SID_SIZE;
+ pSid= (PSID)tokenInfoBuffer.buffer;
+ if (!CreateWellKnownSid(wellKnownSidType, NULL, pSid,
+ &size))
+ {
+ return 1;
+ }
+ ea.Trustee.TrusteeForm= TRUSTEE_IS_SID;
+ ea.Trustee.ptstrName= (LPTSTR)pSid;
+ }
+ else
+ {
+ ea.Trustee.TrusteeForm= TRUSTEE_IS_NAME;
+ ea.Trustee.ptstrName= (LPSTR)os_user;
+ }
+ }
+ else
+ {
+ HANDLE token;
+ if (OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY, &token))
+ {
+
+ DWORD length= (DWORD) sizeof(tokenInfoBuffer);
+ if (GetTokenInformation(token, TokenUser, &tokenInfoBuffer,
+ length, &length))
+ {
+ pSid= tokenInfoBuffer.tokenUser.User.Sid;
+ }
+ }
+ if (!pSid)
+ return 0;
+ ea.Trustee.TrusteeForm= TRUSTEE_IS_SID;
+ ea.Trustee.ptstrName= (LPTSTR)pSid;
+ }
+ ea.grfAccessMode= GRANT_ACCESS;
+ ea.grfAccessPermissions= GENERIC_ALL;
+ ea.grfInheritance= CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE;
+ ea.Trustee.TrusteeType= TRUSTEE_IS_UNKNOWN;
+ ACL* pNewDACL= 0;
+ DWORD err= SetEntriesInAcl(1,&ea,pOldDACL,&pNewDACL);
+ if (pNewDACL)
+ {
+ SetSecurityInfo(hDir,SE_FILE_OBJECT,DACL_SECURITY_INFORMATION,NULL, NULL,
+ pNewDACL, NULL);
+ }
+ if (pSD != NULL)
+ LocalFree((HLOCAL) pSD);
+ if (pNewDACL != NULL)
+ LocalFree((HLOCAL) pNewDACL);
+ CloseHandle(hDir);
+ return 0;
+}
+
+
+/*
+ Give directory permissions for special service user NT SERVICE\servicename
+ this user is available only on Win7 and later.
+*/
+
+void grant_directory_permissions_to_service()
+{
+ char service_user[MAX_PATH+ 12];
+ OSVERSIONINFO info;
+ info.dwOSVersionInfoSize= sizeof(info);
+ GetVersionEx(&info);
+ if (info.dwMajorVersion >6 ||
+ (info.dwMajorVersion== 6 && info.dwMinorVersion > 0)
+ && opt_service)
+ {
+ my_snprintf(service_user,sizeof(service_user), "NT SERVICE\\%s",
+ opt_service);
+ set_directory_permissions(opt_datadir, service_user);
+ }
+}
+
+
+/* Create database instance (including registering as service etc) .*/
+
+static int create_db_instance()
+{
+ int ret= 0;
+ char cwd[MAX_PATH];
+ DWORD cwd_len= MAX_PATH;
+ char cmdline[3*MAX_PATH];
+ FILE *in;
+
+ verbose("Running bootstrap");
+
+ GetCurrentDirectory(cwd_len, cwd);
+ CreateDirectory(opt_datadir, NULL); /*ignore error, it might already exist */
+
+ if (!SetCurrentDirectory(opt_datadir))
+ {
+ die("Cannot set current directory to '%s'\n",opt_datadir);
+ return -1;
+ }
+
+ CreateDirectory("mysql",NULL);
+ CreateDirectory("test", NULL);
+
+ /*
+ Set data directory permissions for both current user and
+ default_os_user (the one who runs services).
+ */
+ set_directory_permissions(opt_datadir, NULL);
+ set_directory_permissions(opt_datadir, default_os_user);
+
+ /* Do mysqld --bootstrap. */
+ init_bootstrap_command_line(cmdline, sizeof(cmdline));
+ /* verbose("Executing %s", cmdline); */
+
+ in= popen(cmdline, "wt");
+ if (!in)
+ goto end;
+
+ if (fwrite("use mysql;\n",11,1, in) != 1)
+ {
+ verbose("ERROR: Cannot write to mysqld's stdin");
+ ret= 1;
+ goto end;
+ }
+
+ /* Write the bootstrap script to stdin. */
+ if (fwrite(mysql_bootstrap_sql, strlen(mysql_bootstrap_sql), 1, in) != 1)
+ {
+ verbose("ERROR: Cannot write to mysqld's stdin");
+ ret= 1;
+ goto end;
+ }
+
+ /* Remove default user, if requested. */
+ if (!opt_default_user)
+ {
+ verbose("Removing default user",remove_default_user_cmd);
+ fputs(remove_default_user_cmd, in);
+ fflush(in);
+ }
+
+ if (opt_allow_remote_root_access)
+ {
+ verbose("Allowing remote access for user root",remove_default_user_cmd);
+ fputs(allow_remote_root_access_cmd,in);
+ fflush(in);
+ }
+
+ /* Change root password if requested. */
+ if (opt_password)
+ {
+ verbose("Changing root password",remove_default_user_cmd);
+ fputs(update_root_passwd_part1, in);
+ fputs(opt_password, in);
+ fputs(update_root_passwd_part2, in);
+ fflush(in);
+ }
+
+ /*
+ On some reason, bootstrap chokes if last command sent via stdin ends with
+ newline, so we supply a dummy comment, that does not end with newline.
+ */
+ fputs(end_of_script, in);
+ fflush(in);
+
+ /* Check if bootstrap has completed successfully. */
+ ret= pclose(in);
+ if (ret)
+ {
+ verbose("mysqld returned error %d in pclose",ret);
+ goto end;
+ }
+
+ /* Create my.ini file in data directory.*/
+ ret= create_myini();
+ if (ret)
+ goto end;
+
+ /* Register service if requested. */
+ if (opt_service && opt_service[0])
+ {
+ ret= register_service();
+ grant_directory_permissions_to_service();
+ if (ret)
+ goto end;
+ }
+
+end:
+ if (ret)
+ {
+ SetCurrentDirectory(cwd);
+ clean_directory(opt_datadir);
+ }
+ return ret;
+}
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 7b3c264c896..065425c4c96 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -30,6 +30,15 @@
#ifndef MYSQL_CLIENT
+/*
+ the following #define adds server-only members to enum_mysql_show_type,
+ that is defined in mysql/plugin.h
+ it has to be before mysql/plugin.h is included.
+*/
+#define SHOW_always_last SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, \
+ SHOW_HAVE, SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, \
+ SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS
+
#include <my_global.h>
#include <mysql_version.h>
#include <mysql_embed.h>
@@ -94,12 +103,16 @@ extern MYSQL_PLUGIN_IMPORT const char *primary_key_name;
#include "unireg.h"
void init_sql_alloc(MEM_ROOT *root, uint block_size, uint pre_alloc_size);
+#endif // MYSQL_CLIENT
+
void *sql_alloc(size_t);
void *sql_calloc(size_t);
char *sql_strdup(const char *str);
char *sql_strmake(const char *str, size_t len);
void *sql_memdup(const void * ptr, size_t size);
void sql_element_free(void *ptr);
+
+#ifndef MYSQL_CLIENT
char *sql_strmake_with_convert(const char *str, size_t arg_length,
CHARSET_INFO *from_cs,
size_t max_res_length,
@@ -510,7 +523,7 @@ protected:
#define MODE_PIPES_AS_CONCAT 2
#define MODE_ANSI_QUOTES 4
#define MODE_IGNORE_SPACE 8
-#define MODE_NOT_USED 16
+#define MODE_IGNORE_BAD_TABLE_OPTIONS 16
#define MODE_ONLY_FULL_GROUP_BY 32
#define MODE_NO_UNSIGNED_SUBTRACTION 64
#define MODE_NO_DIR_IN_CREATE 128
@@ -802,6 +815,7 @@ inline bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list)
inline bool check_some_routine_access(THD *thd, const char *db,
const char *name, bool is_proc)
{ return false; }
+#define decrease_user_connections(X) do { } while(0) /* nothing */
#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
@@ -1030,15 +1044,14 @@ int 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);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
void decrease_user_connections(USER_CONN *uc);
+#endif
bool thd_init_client_charset(THD *thd, uint cs_number);
inline bool is_supported_parser_charset(CHARSET_INFO *cs)
{
@@ -1048,6 +1061,10 @@ bool setup_connection_thread_globals(THD *thd);
bool login_connection(THD *thd);
void end_connection(THD *thd);
void prepare_new_connection_state(THD* thd);
+void update_global_user_stats(THD* thd, bool create_user, time_t now);
+int get_or_create_user_conn(THD *thd, const char *user,
+ const char *host, USER_RESOURCES *mqh);
+int check_for_max_user_connections(THD *thd, USER_CONN *uc);
int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent);
bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create);
@@ -1084,14 +1101,22 @@ 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);
+void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat);
bool mysql_new_select(LEX *lex, bool move_down);
void create_select_for_variable(const char *var_name);
void mysql_init_multi_delete(LEX *lex);
bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
void init_max_user_conn(void);
void init_update_queries(void);
+void init_global_user_stats(void);
+void init_global_table_stats(void);
+void init_global_index_stats(void);
+void init_global_client_stats(void);
void free_max_user_conn(void);
+void free_global_user_stats(void);
+void free_global_table_stats(void);
+void free_global_index_stats(void);
+void free_global_client_stats(void);
pthread_handler_t handle_bootstrap(void *arg);
int mysql_execute_command(THD *thd);
bool do_command(THD *thd);
@@ -1341,6 +1366,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
bool allow_rowid, uint *cached_field_index_ptr);
Field *
find_field_in_table_sef(TABLE *table, const char *name);
+int update_virtual_fields(THD *thd, TABLE *table, bool ignore_stored= FALSE);
#endif /* MYSQL_SERVER */
@@ -1471,14 +1497,17 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum enum_field_types t
LEX_STRING *comment,
char *change, List<String> *interval_list,
CHARSET_INFO *cs,
- uint uint_geom_type);
+ uint uint_geom_type,
+ Virtual_column_info *vcol_info,
+ engine_option_value *create_options);
Create_field * new_create_field(THD *thd, char *field_name, enum_field_types type,
char *length, char *decimals,
uint type_modifier,
Item *default_value, Item *on_update_value,
LEX_STRING *comment, char *change,
List<String> *interval_list, CHARSET_INFO *cs,
- uint uint_geom_type);
+ uint uint_geom_type,
+ Virtual_column_info *vcol_info);
void store_position_for_column(const char *name);
bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *group,bool asc);
bool push_new_name_resolution_context(THD *thd,
@@ -1608,6 +1637,7 @@ void remove_db_from_cache(const char *db);
void flush_tables();
bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
char *make_default_log_name(char *buff,const char* log_ext);
+char *make_once_alloced_filename(const char *basename, const char *ext);
#ifdef WITH_PARTITION_STORAGE_ENGINE
uint fast_alter_partition_table(THD *thd, TABLE *table,
@@ -1949,7 +1979,7 @@ extern MYSQL_PLUGIN_IMPORT uint reg_ext_length;
#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 char log_error_file[FN_REFLEN], *opt_tc_log_file, *opt_log_basename;
extern ulonglong log_10_int[20];
extern ulonglong keybuff_size;
extern ulonglong thd_startup_options;
@@ -1969,6 +1999,7 @@ extern ulong max_connect_errors, connect_timeout;
extern ulong extra_max_connections;
extern ulong slave_net_timeout, slave_trans_retries;
extern uint max_user_connections;
+extern ulonglong denied_connections;
extern ulong what_to_log,flush_time;
extern ulong query_buff_size;
extern ulong max_prepared_stmt_count, prepared_stmt_count;
@@ -2018,12 +2049,14 @@ extern bool volatile abort_loop, shutdown_in_progress;
extern bool in_bootstrap;
extern uint volatile thread_count, thread_running, global_read_lock;
extern ulong thread_created;
+extern uint thread_handling;
extern uint connection_count, extra_connection_count;
extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types;
extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
extern my_bool opt_slave_compressed_protocol, use_temp_pool;
extern ulong slave_exec_mode_options;
extern my_bool opt_readonly, lower_case_file_system;
+extern my_bool opt_userstat_running;
extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
extern my_bool opt_secure_auth, debug_assert_if_crashed_table;
extern char* opt_secure_file_priv;
@@ -2068,6 +2101,11 @@ extern pthread_mutex_t LOCK_des_key_file;
#endif
extern pthread_mutex_t LOCK_server_started;
extern pthread_cond_t COND_server_started;
+extern pthread_mutex_t LOCK_global_user_client_stats;
+extern pthread_mutex_t LOCK_global_table_stats;
+extern pthread_mutex_t LOCK_global_index_stats;
+extern pthread_mutex_t LOCK_stats;
+
extern int mysqld_server_started;
extern rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
extern rw_lock_t LOCK_system_variables_hash;
@@ -2094,6 +2132,11 @@ extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
extern String null_string;
extern HASH open_cache, lock_db_cache;
+extern HASH global_user_stats;
+extern HASH global_client_stats;
+extern HASH global_table_stats;
+extern HASH global_index_stats;
+
extern TABLE *unused_tables;
extern const char* any_db;
extern struct my_option my_long_options[];
@@ -2261,7 +2304,7 @@ longlong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
int test_if_number(char *str,int *res,bool allow_wildcards);
void change_byte(uchar *,uint,char,char);
-void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form,
+bool init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form,
SQL_SELECT *select, int use_record_cache,
bool print_errors, bool disable_rr_cache);
void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
@@ -2302,6 +2345,7 @@ 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);
@@ -2326,10 +2370,6 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db,
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
-#define SAFE_NAME_LEN (NAME_LEN + MYSQL50_TABLE_NAME_PREFIX_LENGTH)
-
uint build_table_shadow_filename(char *buff, size_t bufflen,
ALTER_PARTITION_PARAM_TYPE *lpt);
/* Flags for conversion functions. */
diff --git a/sql/mysql_upgrade_service.cc b/sql/mysql_upgrade_service.cc
new file mode 100644
index 00000000000..db916101eb1
--- /dev/null
+++ b/sql/mysql_upgrade_service.cc
@@ -0,0 +1,522 @@
+/* Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You 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_upgrade_service upgrades mysql service on Windows.
+ It changes service definition to point to the new mysqld.exe, restarts the
+ server and runs mysql_upgrade
+*/
+
+#define DONT_DEFINE_VOID
+#include <process.h>
+#include <my_global.h>
+#include <my_getopt.h>
+#include <my_sys.h>
+#include <m_string.h>
+#include <mysql_version.h>
+#include <winservice.h>
+
+#include <windows.h>
+
+/* We're using version APIs */
+#pragma comment(lib, "version")
+
+#define USAGETEXT \
+"mysql_upgrade_service.exe Ver 1.00 for Windows\n" \
+"Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub" \
+"This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n" \
+"and you are welcome to modify and redistribute it under the GPL v2 license\n" \
+"Usage: mysql_upgrade_service.exe [OPTIONS]\n" \
+"OPTIONS:"
+
+static char mysqld_path[MAX_PATH];
+static char mysqladmin_path[MAX_PATH];
+static char mysqlupgrade_path[MAX_PATH];
+
+static char defaults_file_param[MAX_PATH + 16]; /*--defaults-file=<path> */
+static char logfile_path[MAX_PATH];
+static char *opt_service;
+static SC_HANDLE service;
+static SC_HANDLE scm;
+HANDLE mysqld_process; // mysqld.exe started for upgrade
+DWORD initial_service_state= -1; // initial state of the service
+HANDLE logfile_handle;
+
+/*
+ Startup and shutdown timeouts, in seconds.
+ Maybe,they can be made parameters
+*/
+static unsigned int startup_timeout= 60;
+static unsigned int shutdown_timeout= 60;
+
+static struct my_option my_long_options[]=
+{
+ {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"service", 'S', "Name of the existing Windows service",
+ &opt_service, &opt_service, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+
+
+static my_bool
+get_one_option(int optid,
+ const struct my_option *opt __attribute__ ((unused)),
+ char *argument __attribute__ ((unused)))
+{
+ DBUG_ENTER("get_one_option");
+ switch (optid) {
+ case '?':
+ printf("%s\n", USAGETEXT);
+ my_print_help(my_long_options);
+ exit(0);
+ break;
+ }
+ DBUG_RETURN(0);
+}
+
+
+
+static void log(const char *fmt, ...)
+{
+ va_list args;
+ /* Print the error message */
+ va_start(args, fmt);
+ vfprintf(stdout,fmt, args);
+ va_end(args);
+ fputc('\n', stdout);
+ fflush(stdout);
+}
+
+
+static void die(const char *fmt, ...)
+{
+ va_list args;
+ DBUG_ENTER("die");
+
+ /* Print the error message */
+ va_start(args, fmt);
+
+ fprintf(stderr, "FATAL ERROR: ");
+ vfprintf(stderr, fmt, args);
+ if (logfile_path[0])
+ {
+ fprintf(stderr, "Additional information can be found in the log file %s",
+ logfile_path);
+ }
+ va_end(args);
+ fputc('\n', stderr);
+ fflush(stdout);
+ /* Cleanup */
+
+ /*
+ Stop service that we started, if it was not initally running at
+ program start.
+ */
+ if (initial_service_state != -1 && initial_service_state != SERVICE_RUNNING)
+ {
+ SERVICE_STATUS service_status;
+ ControlService(service, SERVICE_CONTROL_STOP, &service_status);
+ }
+
+ if (scm)
+ CloseServiceHandle(scm);
+ if (service)
+ CloseServiceHandle(service);
+ /* Stop mysqld.exe, if it was started for upgrade */
+ if (mysqld_process)
+ TerminateProcess(mysqld_process, 3);
+ if (logfile_handle)
+ CloseHandle(logfile_handle);
+ my_end(0);
+
+ exit(1);
+}
+
+
+/*
+ spawn-like function to run subprocesses.
+ We also redirect the full output to the log file.
+
+ Typical usage could be something like
+ run_tool(P_NOWAIT, "cmd.exe", "/c" , "echo", "foo", NULL)
+
+ @param wait_flag (P_WAIT or P_NOWAIT)
+ @program program to run
+
+ Rest of the parameters is NULL terminated strings building command line.
+
+ @return intptr containing either process handle, if P_NOWAIT is used
+ or return code of the process (if P_WAIT is used)
+*/
+
+static intptr_t run_tool(int wait_flag, const char *program,...)
+{
+ static char cmdline[32*1024];
+ char *end;
+ va_list args;
+ va_start(args, program);
+ if (!program)
+ die("Invalid call to run_tool");
+ end= strxmov(cmdline, "\"", program, "\"", NullS);
+
+ for(;;)
+ {
+ char *param= va_arg(args,char *);
+ if(!param)
+ break;
+ end= strxmov(end, " \"", param, "\"", NullS);
+ }
+ va_end(args);
+
+ /* Create output file if not alredy done */
+ if (!logfile_handle)
+ {
+ char tmpdir[FN_REFLEN];
+ GetTempPath(FN_REFLEN, tmpdir);
+ sprintf_s(logfile_path, "%s\\mysql_upgrade_service.%s.log", tmpdir,
+ opt_service);
+ logfile_handle= CreateFile(logfile_path, GENERIC_WRITE, FILE_SHARE_READ,
+ NULL, TRUNCATE_EXISTING, 0, NULL);
+ if (!logfile_handle)
+ {
+ die("Cannot open log file %s, windows error %u",
+ logfile_path, GetLastError());
+ }
+ }
+
+ /* Start child process */
+ STARTUPINFO si= {0};
+ si.cb= sizeof(si);
+ si.hStdInput= GetStdHandle(STD_INPUT_HANDLE);
+ si.hStdError= logfile_handle;
+ si.hStdOutput= logfile_handle;
+ si.dwFlags= STARTF_USESTDHANDLES;
+ PROCESS_INFORMATION pi;
+ if (!CreateProcess(NULL, cmdline, NULL,
+ NULL, TRUE, NULL, NULL, NULL, &si, &pi))
+ {
+ die("CreateProcess failed (commandline %s)", cmdline);
+ }
+ CloseHandle(pi.hThread);
+
+ if (wait_flag == P_NOWAIT)
+ {
+ /* Do not wait for process to complete, return handle. */
+ return (intptr_t)pi.hProcess;
+ }
+
+ /* Wait for process to complete. */
+ if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0)
+ {
+ die("WaitForSingleObject() failed");
+ }
+ DWORD exit_code;
+ if (!GetExitCodeProcess(pi.hProcess, &exit_code))
+ {
+ die("GetExitCodeProcess() failed");
+ }
+ return (intptr_t)exit_code;
+}
+
+
+void stop_mysqld_service()
+{
+ DWORD needed;
+ SERVICE_STATUS_PROCESS ssp;
+ int timeout= shutdown_timeout*1000;
+ for(;;)
+ {
+ if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
+ (LPBYTE)&ssp,
+ sizeof(SERVICE_STATUS_PROCESS),
+ &needed))
+ {
+ die("QueryServiceStatusEx failed (%u)\n", GetLastError());
+ }
+
+ /*
+ Remeber initial state of the service, we will restore it on
+ exit.
+ */
+ if(initial_service_state == -1)
+ initial_service_state= ssp.dwCurrentState;
+
+ switch(ssp.dwCurrentState)
+ {
+ case SERVICE_STOPPED:
+ return;
+ case SERVICE_RUNNING:
+ if(!ControlService(service, SERVICE_CONTROL_STOP,
+ (SERVICE_STATUS *)&ssp))
+ die("ControlService failed, error %u\n", GetLastError());
+ case SERVICE_START_PENDING:
+ case SERVICE_STOP_PENDING:
+ if(timeout < 0)
+ die("Service does not stop after %d seconds timeout",shutdown_timeout);
+ Sleep(100);
+ timeout -= 100;
+ break;
+ default:
+ die("Unexpected service state %d",ssp.dwCurrentState);
+ }
+ }
+}
+
+
+/*
+ Shutdown mysql server. Not using mysqladmin, since
+ our --skip-grant-tables do not work anymore after mysql_upgrade
+ that does "flush privileges". Instead, the shutdown event is set.
+*/
+void initiate_mysqld_shutdown()
+{
+ char event_name[32];
+ DWORD pid= GetProcessId(mysqld_process);
+ sprintf_s(event_name, "MySQLShutdown%d", pid);
+ HANDLE shutdown_handle= OpenEvent(EVENT_MODIFY_STATE, FALSE, event_name);
+ if(!shutdown_handle)
+ {
+ die("OpenEvent() failed for shutdown event");
+ }
+
+ if(!SetEvent(shutdown_handle))
+ {
+ die("SetEvent() failed");
+ }
+}
+
+
+/*
+ Change service configuration (binPath) to point to mysqld from
+ this installation.
+*/
+static void change_service_config()
+{
+
+ char defaults_file[MAX_PATH];
+ char default_character_set[64];
+ char buf[MAX_PATH];
+ char commandline[3*MAX_PATH + 19];
+ int i;
+
+ scm= OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ if(!scm)
+ die("OpenSCManager failed with %u", GetLastError());
+ service= OpenService(scm, opt_service, SERVICE_ALL_ACCESS);
+ if (!service)
+ die("OpenService failed with %u", GetLastError());
+
+ BYTE config_buffer[8*1024];
+ LPQUERY_SERVICE_CONFIGW config= (LPQUERY_SERVICE_CONFIGW)config_buffer;
+ DWORD size= sizeof(config_buffer);
+ DWORD needed;
+ if (!QueryServiceConfigW(service, config, size, &needed))
+ die("QueryServiceConfig failed with %u", GetLastError());
+
+ mysqld_service_properties props;
+ if (get_mysql_service_properties(config->lpBinaryPathName, &props))
+ {
+ die("Not a valid MySQL service");
+ }
+
+ int my_major= MYSQL_VERSION_ID/10000;
+ int my_minor= (MYSQL_VERSION_ID %10000)/100;
+ int my_patch= MYSQL_VERSION_ID%100;
+
+ if(my_major < props.version_major ||
+ (my_major == props.version_major && my_minor < props.version_minor))
+ {
+ die("Can not downgrade, the service is currently running as version %d.%d.%d"
+ ", my version is %d.%d.%d", props.version_major, props.version_minor,
+ props.version_patch, my_major, my_minor, my_patch);
+ }
+
+ if(props.inifile[0] == 0)
+ {
+ /*
+ Weird case, no --defaults-file in service definition, need to create one.
+ */
+ sprintf_s(props.inifile, MAX_PATH, "%s\\my.ini", props.datadir);
+ }
+
+ /*
+ Write datadir to my.ini, after converting backslashes to
+ unix style slashes.
+ */
+ strcpy_s(buf, MAX_PATH, props.datadir);
+ for(i= 0; buf[i]; i++)
+ {
+ if (buf[i] == '\\')
+ buf[i]= '/';
+ }
+ WritePrivateProfileString("mysqld", "datadir",buf, props.inifile);
+
+ /*
+ Remove basedir from defaults file, otherwise the service wont come up in
+ the new version, and will complain about mismatched message file.
+ */
+ WritePrivateProfileString("mysqld", "basedir",NULL, props.inifile);
+
+ /*
+ Replace default-character-set with character-set-server, to avoid
+ "default-character-set is deprecated and will be replaced ..."
+ message.
+ */
+ default_character_set[0]= 0;
+ GetPrivateProfileString("mysqld", "default-character-set", NULL,
+ default_character_set, sizeof(default_character_set), defaults_file);
+ if (default_character_set[0])
+ {
+ WritePrivateProfileString("mysqld", "default-character-set", NULL,
+ defaults_file);
+ WritePrivateProfileString("mysqld", "character-set-server",
+ default_character_set, defaults_file);
+ }
+
+ sprintf(defaults_file_param,"--defaults-file=%s", props.inifile);
+ sprintf_s(commandline, "\"%s\" \"%s\" \"%s\"", mysqld_path,
+ defaults_file_param, opt_service);
+ if (!ChangeServiceConfig(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
+ SERVICE_NO_CHANGE, commandline, NULL, NULL, NULL, NULL, NULL, NULL))
+ {
+ die("ChangeServiceConfig failed with %u", GetLastError());
+ }
+
+}
+
+
+int main(int argc, char **argv)
+{
+ int error;
+ MY_INIT(argv[0]);
+ char bindir[FN_REFLEN];
+ char *p;
+
+ /* Parse options */
+ if ((error= handle_options(&argc, &argv, my_long_options, get_one_option)))
+ die("");
+ if (!opt_service)
+ die("--service=# parameter is mandatory");
+
+ /*
+ Get full path to mysqld, we need it when changing service configuration.
+ Assume installation layout, i.e mysqld.exe, mysqladmin.exe, mysqlupgrade.exe
+ and mysql_upgrade_service.exe are in the same directory.
+ */
+ GetModuleFileName(NULL, bindir, FN_REFLEN);
+ p= strrchr(bindir, FN_LIBCHAR);
+ if(p)
+ {
+ *p= 0;
+ }
+ sprintf_s(mysqld_path, "%s\\mysqld.exe", bindir);
+ sprintf_s(mysqladmin_path, "%s\\mysqladmin.exe", bindir);
+ sprintf_s(mysqlupgrade_path, "%s\\mysql_upgrade.exe", bindir);
+
+ char *paths[]= {mysqld_path, mysqladmin_path, mysqlupgrade_path};
+ for(int i= 0; i< 3;i++)
+ {
+ if(GetFileAttributes(paths[i]) == INVALID_FILE_ATTRIBUTES)
+ die("File %s does not exist", paths[i]);
+ }
+
+ /*
+ Messages written on stdout should not be buffered, GUI upgrade program
+ reads them from pipe and uses as progress indicator.
+ */
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ log("Phase 1/8: Changing service configuration");
+ change_service_config();
+
+ log("Phase 2/8: Stopping service");
+ stop_mysqld_service();
+
+ /*
+ Start mysqld.exe as non-service skipping privileges (so we do not
+ care about the password). But disable networking and enable pipe
+ for communication, for security reasons.
+ */
+ char socket_param[FN_REFLEN];
+ sprintf_s(socket_param,"--socket=mysql_upgrade_service_%d",
+ GetCurrentProcessId());
+
+ log("Phase 3/8: Starting mysqld for upgrade");
+ mysqld_process= (HANDLE)run_tool(P_NOWAIT, mysqld_path,
+ defaults_file_param, "--skip-networking", "--skip-grant-tables",
+ "--enable-named-pipe", socket_param, NULL);
+
+ if (mysqld_process == INVALID_HANDLE_VALUE)
+ {
+ die("Cannot start mysqld.exe process, errno=%d", errno);
+ }
+
+ log("Phase 4/8: Waiting for startup to complete");
+ DWORD start_duration_ms= 0;
+ for(;;)
+ {
+ if (WaitForSingleObject(mysqld_process, 0) != WAIT_TIMEOUT)
+ die("mysqld.exe did not start");
+
+ if (run_tool(P_WAIT, mysqladmin_path, "--protocol=pipe",
+ socket_param, "ping", NULL) == 0)
+ {
+ break;
+ }
+ if (start_duration_ms > startup_timeout*1000)
+ die("Server did not come up in %d seconds",startup_timeout);
+ Sleep(500);
+ start_duration_ms+= 500;
+ }
+
+ log("Phase 5/8: Running mysql_upgrade");
+ int upgrade_err= (int) run_tool(P_WAIT, mysqlupgrade_path,
+ "--protocol=pipe", "--force", socket_param,
+ NULL);
+
+ if (upgrade_err)
+ die("mysql_upgrade failed with error code %d\n", upgrade_err);
+
+ log("Phase 6/8: Initiating server shutdown");
+ initiate_mysqld_shutdown();
+
+ log("Phase 7/8: Waiting for shutdown to complete");
+ if (WaitForSingleObject(mysqld_process, shutdown_timeout*1000)
+ != WAIT_OBJECT_0)
+ {
+ /* Shutdown takes too long */
+ die("mysqld does not shutdown.");
+ }
+ CloseHandle(mysqld_process);
+ mysqld_process= NULL;
+
+ log("Phase 8/8: Starting service%s",
+ (initial_service_state == SERVICE_RUNNING)?"":" (skipped)");
+ if (initial_service_state == SERVICE_RUNNING)
+ {
+ StartService(service, NULL, NULL);
+ }
+
+ log("Service '%s' successfully upgraded.\nLog file is written to %s",
+ opt_service, logfile_path);
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ if (logfile_handle)
+ CloseHandle(logfile_handle);
+ my_end(0);
+ exit(0);
+} \ No newline at end of file
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index c4ec0b741f0..0d0ad919515 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -24,6 +24,7 @@
#include "rpl_mi.h"
#include "sql_repl.h"
#include "rpl_filter.h"
+#include "client_settings.h"
#include "repl_failsafe.h"
#include <my_stacktrace.h>
#include "mysqld_suffix.h"
@@ -280,7 +281,7 @@ const char *show_comp_option_name[]= {"YES", "NO", "DISABLED"};
static const char *sql_mode_names[]=
{
"REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE",
- "?", "ONLY_FULL_GROUP_BY", "NO_UNSIGNED_SUBTRACTION",
+ "IGNORE_BAD_TABLE_OPTIONS", "ONLY_FULL_GROUP_BY", "NO_UNSIGNED_SUBTRACTION",
"NO_DIR_IN_CREATE",
"POSTGRESQL", "ORACLE", "MSSQL", "DB2", "MAXDB", "NO_KEY_OPTIONS",
"NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS", "MYSQL323", "MYSQL40", "ANSI",
@@ -300,7 +301,7 @@ static const unsigned int sql_mode_names_len[]=
/*PIPES_AS_CONCAT*/ 15,
/*ANSI_QUOTES*/ 11,
/*IGNORE_SPACE*/ 12,
- /*?*/ 1,
+ /*IGNORE_BAD_TABLE_OPTIONS*/ 24,
/*ONLY_FULL_GROUP_BY*/ 18,
/*NO_UNSIGNED_SUBTRACTION*/ 23,
/*NO_DIR_IN_CREATE*/ 16,
@@ -383,6 +384,16 @@ TYPELIB thread_handling_typelib=
thread_handling_names, NULL
};
+const char *plugin_maturity_names[]=
+{ "unknown", "experimental", "alpha", "beta", "gamma", "stable", 0 };
+
+TYPELIB plugin_maturity_values=
+{
+ array_elements(plugin_maturity_names) - 1, "", plugin_maturity_names, 0
+};
+
+const int server_maturity= MariaDB_PLUGIN_MATURITY_UNKNOWN;
+
const char *first_keyword= "first", *binary_keyword= "BINARY";
const char *my_localhost= "localhost", *delayed_user= "DELAYED";
#if SIZEOF_OFF_T > 4 && defined(BIG_TABLES)
@@ -424,6 +435,7 @@ static my_bool opt_ignore_wrong_options= 0, opt_expect_abort= 0;
static my_bool opt_sync= 0;
static uint kill_cached_threads, wake_thread;
ulong thread_created;
+uint thread_handling;
static ulong max_used_connections;
static ulong my_bind_addr; /**< the address we bind to */
static volatile ulong cached_thread_count= 0;
@@ -437,6 +449,9 @@ static const char *optimizer_switch_str="index_merge=on,index_merge_union=on,"
#else
;
#endif
+#ifdef SAFEMALLOC
+my_bool sf_malloc_trough_check= 0;
+#endif
static char *mysqld_user, *mysqld_chroot, *log_error_file_ptr;
static char *opt_init_slave, *language_ptr, *opt_init_connect;
static char *default_character_set_name;
@@ -456,6 +471,7 @@ static pthread_cond_t COND_thread_cache, COND_flush_thread_cache;
bool opt_update_log, opt_bin_log, opt_ignore_builtin_innodb= 0;
my_bool opt_log, opt_slow_log, debug_assert_if_crashed_table;
+my_bool opt_userstat_running;
ulong log_output_options;
my_bool opt_log_queries_not_using_indexes= 0;
bool opt_error_log= IF_WIN(1,0);
@@ -598,6 +614,7 @@ ulong extra_max_connections;
ulong max_long_data_size;
uint max_user_connections= 0;
+ulonglong denied_connections;
/**
Limit of the total number of prepared statements in the server.
Is necessary to protect the server against out-of-memory attacks.
@@ -624,7 +641,7 @@ 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;
-char log_error_file[FN_REFLEN], glob_hostname[FN_REFLEN];
+char log_error_file[FN_REFLEN], glob_hostname[FN_REFLEN], *opt_log_basename;
char mysql_real_data_home[FN_REFLEN],
language[FN_REFLEN], reg_ext[FN_EXTLEN], mysql_charsets_dir[FN_REFLEN],
*opt_init_file, *opt_tc_log_file,
@@ -700,6 +717,9 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count,
LOCK_global_system_variables,
LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
LOCK_connection_count, LOCK_short_uuid_generator;
+pthread_mutex_t LOCK_stats, LOCK_global_user_client_stats;
+pthread_mutex_t LOCK_global_table_stats, LOCK_global_index_stats;
+
/**
The below lock protects access to two global server variables:
max_prepared_stmt_count and prepared_stmt_count. These variables
@@ -1064,7 +1084,7 @@ static void close_connections(void)
tmp->thread_id,
(tmp->main_security_ctx.user ?
tmp->main_security_ctx.user : ""));
- close_connection(tmp,0,0);
+ close_connection(tmp,ER_SERVER_SHUTDOWN,0);
}
#endif
DBUG_PRINT("quit",("Unlocking LOCK_thread_count"));
@@ -1402,11 +1422,13 @@ void clean_up(bool print_message)
#ifdef HAVE_REPLICATION
my_free(slave_load_tmpdir,MYF(MY_ALLOW_ZERO_PTR));
#endif
- x_free(opt_bin_logname);
- x_free(opt_relay_logname);
x_free(opt_secure_file_priv);
bitmap_free(&temp_pool);
free_max_user_conn();
+ free_global_user_stats();
+ free_global_client_stats();
+ free_global_table_stats();
+ free_global_index_stats();
#ifdef HAVE_REPLICATION
end_slave_list();
#endif
@@ -1432,6 +1454,7 @@ void clean_up(bool print_message)
if (print_message && errmesg && server_start_time)
sql_print_information(ER(ER_SHUTDOWN_COMPLETE),my_progname);
thread_scheduler.end();
+ mysql_library_end();
finish_client_errs();
my_free((uchar*) my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST),
MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
@@ -1499,21 +1522,26 @@ static void clean_up_mutexes()
(void) pthread_mutex_destroy(&LOCK_bytes_received);
(void) pthread_mutex_destroy(&LOCK_user_conn);
(void) pthread_mutex_destroy(&LOCK_connection_count);
+ (void) pthread_mutex_destroy(&LOCK_stats);
+ (void) pthread_mutex_destroy(&LOCK_global_user_client_stats);
+ (void) pthread_mutex_destroy(&LOCK_global_table_stats);
+ (void) pthread_mutex_destroy(&LOCK_global_index_stats);
+
#ifndef EMBEDDED_LIBRARY
Events::destroy_mutexes();
-#endif
+#endif /* !EMBEDDED_LIBRARY */
#ifdef HAVE_OPENSSL
(void) pthread_mutex_destroy(&LOCK_des_key_file);
#ifndef HAVE_YASSL
for (int i= 0; i < CRYPTO_num_locks(); ++i)
(void) rwlock_destroy(&openssl_stdlocks[i].lock);
OPENSSL_free(openssl_stdlocks);
-#endif
-#endif
+#endif /* HAVE_YASSL */
+#endif /* HAVE_OPENSSL */
#ifdef HAVE_REPLICATION
(void) pthread_mutex_destroy(&LOCK_rpl_status);
(void) pthread_cond_destroy(&COND_rpl_status);
-#endif
+#endif /* HAVE_REPLICATION */
(void) pthread_mutex_destroy(&LOCK_server_started);
(void) pthread_cond_destroy(&COND_server_started);
(void) pthread_mutex_destroy(&LOCK_active_mi);
@@ -1541,20 +1569,23 @@ static void clean_up_mutexes()
mysys/thr_mutex.c, will give a warning on first wrong mutex usage!
*/
+#ifdef SAFE_MUTEX
+#define always_in_that_order(A,B) \
+ pthread_mutex_lock(A); pthread_mutex_lock(B); \
+ pthread_mutex_unlock(B); pthread_mutex_unlock(A)
+#else
+#define always_in_that_order(A,B)
+#endif
+
static void register_mutex_order()
{
-#ifdef SAFE_MUTEX
/*
We must have LOCK_open before LOCK_global_system_variables because
LOCK_open is hold while sql_plugin.c::intern_sys_var_ptr() is called.
*/
- pthread_mutex_lock(&LOCK_open);
- pthread_mutex_lock(&LOCK_global_system_variables);
-
- pthread_mutex_unlock(&LOCK_global_system_variables);
- pthread_mutex_unlock(&LOCK_open);
-#endif
+ always_in_that_order(&LOCK_open, &LOCK_global_system_variables);
}
+#undef always_in_that_order
/****************************************************************************
@@ -1942,6 +1973,17 @@ void close_connection(THD *thd, uint errcode, bool lock)
if (lock)
(void) pthread_mutex_lock(&LOCK_thread_count);
thd->killed= THD::KILL_CONNECTION;
+
+ if (global_system_variables.log_warnings > 3)
+ {
+ Security_context *sctx= &thd->main_security_ctx;
+ sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
+ thd->thread_id,(thd->db ? thd->db : "unconnected"),
+ sctx->user ? sctx->user : "unauthenticated",
+ sctx->host_or_ip,
+ (errcode ? ER(errcode) : "CLOSE_CONNECTION"));
+ }
+
if ((vio= thd->net.vio) != 0)
{
if (errcode)
@@ -2109,24 +2151,6 @@ void flush_thread_cache()
}
-#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;
- DBUG_ENTER("abort_thread");
- if (thd)
- thd->killed= THD::KILL_CONNECTION;
- DBUG_VOID_RETURN;
-}
-#endif
-
-
/******************************************************************************
Setup a signal thread with handles all signals.
Because Linux doesn't support schemas use a mutex to check that
@@ -2661,6 +2685,12 @@ the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n",
case THD::KILL_QUERY:
kreason= "KILL_QUERY";
break;
+ case THD::KILL_SYSTEM_THREAD:
+ kreason= "KILL_SYSTEM_THREAD";
+ break;
+ case THD::KILL_SERVER:
+ kreason= "KILL_SERVER";
+ break;
case THD::KILLED_NO_VALUE:
kreason= "KILLED_NO_VALUE";
break;
@@ -3012,7 +3042,7 @@ int my_message_sql(uint error, const char *str, myf MyFlags)
MYSQL_ERROR::enum_warning_level level;
sql_print_message_func func;
DBUG_ENTER("my_message_sql");
- DBUG_PRINT("error", ("error: %u message: '%s'", error, str));
+ DBUG_PRINT("error", ("error: %u message: '%s' Flag: %d", error, str, MyFlags));
DBUG_ASSERT(str != NULL);
/*
@@ -3028,7 +3058,8 @@ int my_message_sql(uint error, const char *str, myf MyFlags)
{
/* At least, prevent new abuse ... */
DBUG_ASSERT(strncmp(str, "MyISAM table", 12) == 0 ||
- strncmp(str, "MARIA table", 11) == 0);
+ strncmp(str, "Aria table", 11) == 0 ||
+ (MyFlags & ME_JUST_INFO));
error= ER_UNKNOWN_ERROR;
}
@@ -3055,7 +3086,10 @@ int my_message_sql(uint error, const char *str, myf MyFlags)
this could be improved by having a common stack of handlers.
*/
if (thd->handle_error(error, str, level))
+ {
+ DBUG_PRINT("info", ("error handled by handle_error()"));
DBUG_RETURN(0);
+ }
if (level == MYSQL_ERROR::WARN_LEVEL_WARN)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, error, str);
@@ -3134,7 +3168,7 @@ to_error_log:
/* When simulating OOM, skip writing to error log to avoid mtr errors */
DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_RETURN(0););
- if (!thd || (MyFlags & ME_NOREFRESH))
+ if (!thd || thd->log_all_errors || (MyFlags & ME_NOREFRESH))
(*func)("%s: %s", my_progname_short, str); /* purecov: inspected */
DBUG_RETURN(0);
}
@@ -3325,6 +3359,7 @@ SHOW_VAR com_status_vars[]= {
{"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_client_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CLIENT_STATS]), SHOW_LONG_STATUS},
{"show_collations", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS},
{"show_column_types", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLUMN_TYPES]), SHOW_LONG_STATUS},
{"show_contributors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CONTRIBUTORS]), SHOW_LONG_STATUS},
@@ -3347,6 +3382,7 @@ SHOW_VAR com_status_vars[]= {
{"show_function_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_FUNC]), SHOW_LONG_STATUS},
{"show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS},
{"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS},
+ {"show_index_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS},
{"show_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},
@@ -3363,9 +3399,11 @@ SHOW_VAR com_status_vars[]= {
{"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_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATS]), 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_user_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_USER_STATS]), SHOW_LONG_STATUS},
{"show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
{"show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
{"slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
@@ -3395,6 +3433,7 @@ static int init_common_variables(const char *conf_file_name, int argc,
char **argv, const char **groups)
{
char buff[FN_REFLEN], *s;
+ const char *basename;
umask(((~my_umask) & 0666));
my_decimal_set_zero(&decimal_zero); // set decimal_zero constant;
tzset(); // Set tzname
@@ -3448,13 +3487,20 @@ static int init_common_variables(const char *conf_file_name, int argc,
if (gethostname(glob_hostname,sizeof(glob_hostname)) < 0)
{
+ /*
+ Get hostname of computer (used by 'show variables') and as default
+ basename for the pid file if --log-basename is not given.
+ */
strmake(glob_hostname, STRING_WITH_LEN("localhost"));
sql_print_warning("gethostname failed, using '%s' as hostname",
- glob_hostname);
- strmake(pidfile_name, STRING_WITH_LEN("mysql"));
+ glob_hostname);
+ basename= "mysql";
}
else
- strmake(pidfile_name, glob_hostname, sizeof(pidfile_name)-5);
+ {
+ basename= glob_hostname;
+ }
+ strmake(pidfile_name, basename, sizeof(pidfile_name)-5);
strmov(fn_ext(pidfile_name),".pid"); // Add proper extension
/*
@@ -3570,6 +3616,7 @@ static int init_common_variables(const char *conf_file_name, int argc,
if (init_errmessage()) /* Read error messages from file */
return 1;
init_client_errs();
+ mysql_library_init(never,never,never); /* for replication */
lex_init();
if (item_create_init())
return 1;
@@ -3782,6 +3829,12 @@ static int init_thread_environment()
(void) pthread_mutex_init(&LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_short_uuid_generator, MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_connection_count, MY_MUTEX_INIT_FAST);
+ (void) pthread_mutex_init(&LOCK_stats, MY_MUTEX_INIT_FAST);
+ (void) pthread_mutex_init(&LOCK_global_user_client_stats,
+ MY_MUTEX_INIT_FAST);
+ (void) pthread_mutex_init(&LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
+ (void) pthread_mutex_init(&LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
+
#ifdef HAVE_OPENSSL
(void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST);
#ifndef HAVE_YASSL
@@ -3948,6 +4001,32 @@ static void end_ssl()
#endif /* EMBEDDED_LIBRARY */
+#ifdef _WIN32
+/**
+ Registers a file to be collected when Windows Error Reporting creates a crash
+ report.
+
+ @note only works on Vista and later, since WerRegisterFile() is not available
+ on earlier Windows.
+*/
+#include <werapi.h>
+static void add_file_to_crash_report(char *file)
+{
+ /* Load WerRegisterFile function dynamically.*/
+ HRESULT (WINAPI *pWerRegisterFile)(PCWSTR, WER_REGISTER_FILE_TYPE, DWORD)
+ =(HRESULT (WINAPI *) (PCWSTR, WER_REGISTER_FILE_TYPE, DWORD))
+ GetProcAddress(GetModuleHandle("kernel32"),"WerRegisterFile");
+
+ if (pWerRegisterFile)
+ {
+ wchar_t wfile[MAX_PATH+1]= {0};
+ if (mbstowcs(wfile, file, MAX_PATH) != (size_t)-1)
+ {
+ pWerRegisterFile(wfile, WerRegFileTypeOther, WER_FILE_ANONYMOUS_DATA);
+ }
+ }
+}
+#endif
static int init_server_components()
{
@@ -4000,6 +4079,11 @@ static int init_server_components()
if (!res)
setbuf(stderr, NULL);
+
+#ifdef _WIN32
+ /* Add error log to windows crash reporting. */
+ add_file_to_crash_report(log_error_file);
+#endif
}
}
@@ -4056,7 +4140,7 @@ version 5.0 and above. It is replaced by the binary log.");
if (opt_update_logname)
{
/* as opt_bin_log==0, no need to free opt_bin_logname */
- if (!(opt_bin_logname= my_strdup(opt_update_logname, MYF(MY_WME))))
+ if (!(opt_bin_logname= my_once_strdup(opt_update_logname, MYF(MY_WME))))
{
sql_print_error("Out of memory");
return EXIT_OUT_OF_MEMORY;
@@ -4156,8 +4240,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
}
if (ln == buf)
{
- my_free(opt_bin_logname, MYF(MY_ALLOW_ZERO_PTR));
- opt_bin_logname=my_strdup(buf, MYF(0));
+ opt_bin_logname= my_once_strdup(buf, MYF(MY_WME));
}
if (mysql_bin_log.open_index_file(opt_binlog_index_name, ln, TRUE))
{
@@ -4168,6 +4251,9 @@ a file name for --log-bin-index option", opt_binlog_index_name);
/* call ha_init_key_cache() on all key caches to init them */
process_key_caches(&ha_init_key_cache);
+ init_global_table_stats();
+ init_global_index_stats();
+
/* Allow storage engine to give real error messages */
if (ha_init_errors())
DBUG_RETURN(1);
@@ -4301,10 +4387,10 @@ a file name for --log-bin-index option", opt_binlog_index_name);
pthread_mutex_unlock(&LOCK_global_system_variables);
}
}
-#if defined(WITH_MARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
+#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
if (!ha_storage_engine_is_enabled(maria_hton) && !opt_bootstrap)
{
- sql_print_error("Maria engine is not enabled or did not start. The Maria engine must be enabled to continue as mysqld was configured with --with-maria-tmp-tables");
+ sql_print_error("Aria engine is not enabled or did not start. The Aria engine must be enabled to continue as mysqld was configured with --with-aria-tmp-tables");
unireg_abort(1);
}
#endif
@@ -4370,6 +4456,8 @@ a file name for --log-bin-index option", opt_binlog_index_name);
init_max_user_conn();
init_update_queries();
+ init_global_user_stats();
+ init_global_client_stats();
DBUG_RETURN(0);
}
@@ -5138,7 +5226,7 @@ void create_thread_to_handle_connection(THD *thd)
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);
+ close_connection(thd,ER_OUT_OF_RESOURCES,0);
delete thd;
(void) pthread_mutex_unlock(&LOCK_thread_count);
return;
@@ -5185,6 +5273,7 @@ static void create_new_thread(THD *thd)
DBUG_PRINT("error",("Too many connections"));
close_connection(thd, ER_CON_COUNT_ERROR, 1);
+ statistic_increment(denied_connections, &LOCK_status);
delete thd;
DBUG_VOID_RETURN;
}
@@ -5818,7 +5907,7 @@ error:
enum options_mysqld
{
- OPT_ISAM_LOG=256, OPT_SKIP_NEW,
+ OPT_ISAM_LOG=256,
OPT_SKIP_GRANT, OPT_SKIP_LOCK,
OPT_ENABLE_LOCK, OPT_USE_LOCKING,
OPT_SOCKET, OPT_UPDATE_LOG,
@@ -5872,7 +5961,7 @@ enum options_mysqld
OPT_NDB_REPORT_THRESH_BINLOG_EPOCH_SLIP,
OPT_NDB_REPORT_THRESH_BINLOG_MEM_USAGE,
OPT_NDB_USE_COPYING_ALTER_TABLE,
- OPT_SKIP_SAFEMALLOC, OPT_MUTEX_DEADLOCK_DETECTOR,
+ OPT_SAFEMALLOC, OPT_MUTEX_DEADLOCK_DETECTOR,
OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_COMPLETION_TYPE,
OPT_SKIP_SYMLINKS,
OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL,
@@ -5895,6 +5984,7 @@ enum options_mysqld
OPT_INTERACTIVE_TIMEOUT, OPT_JOIN_BUFF_SIZE,
OPT_KEY_BUFFER_SIZE, OPT_KEY_CACHE_BLOCK_SIZE,
OPT_KEY_CACHE_DIVISION_LIMIT, OPT_KEY_CACHE_AGE_THRESHOLD,
+ OPT_KEY_CACHE_PARTITIONS,
OPT_LONG_QUERY_TIME,
OPT_LOWER_CASE_TABLE_NAMES, OPT_MAX_ALLOWED_PACKET,
OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE,
@@ -5973,6 +6063,7 @@ enum options_mysqld
OPT_TABLE_LOCK_WAIT_TIMEOUT,
OPT_PLUGIN_LOAD,
OPT_PLUGIN_DIR,
+ OPT_PLUGIN_MATURITY,
OPT_SYMBOLIC_LINKS,
OPT_WARNINGS,
OPT_RECORD_BUFFER_OLD,
@@ -5981,13 +6072,15 @@ enum options_mysqld
OPT_PROFILING,
OPT_KEEP_FILES_ON_CREATE,
OPT_GENERAL_LOG,
- OPT_SLOW_LOG,
+ OPT_SLOW_LOG, OPT_LOG_BASENAME,
OPT_THREAD_HANDLING,
OPT_INNODB_ROLLBACK_ON_TIMEOUT,
OPT_SECURE_FILE_PRIV,
OPT_MIN_EXAMINED_ROW_LIMIT,
OPT_LOG_SLOW_SLAVE_STATEMENTS,
- OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_DEBUG_ASSERT_IF_CRASHED_TABLE, OPT_OLD_MODE,
+ OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_DEBUG_ASSERT_IF_CRASHED_TABLE,
+ OPT_DEBUG_ASSERT_ON_ERROR,
+ OPT_OLD_MODE,
OPT_TEST_IGNORE_WRONG_OPTIONS, OPT_TEST_RESTART,
#if defined(ENABLED_DEBUG_SYNC)
OPT_DEBUG_SYNC_TIMEOUT,
@@ -6001,6 +6094,7 @@ enum options_mysqld
OPT_LOG_SLOW_RATE_LIMIT,
OPT_LOG_SLOW_VERBOSITY,
OPT_LOG_SLOW_FILTER,
+ OPT_USERSTAT,
OPT_GENERAL_LOG_FILE,
OPT_SLOW_QUERY_LOG_FILE,
OPT_IGNORE_BUILTIN_INNODB,
@@ -6161,6 +6255,10 @@ struct my_option my_long_options[] =
"Do an assert in handler::print_error() if we get a crashed table",
&debug_assert_if_crashed_table, &debug_assert_if_crashed_table,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"debug-assert-on-error", OPT_DEBUG_ASSERT_ON_ERROR,
+ "Do an assert in various functions if we get a fatal error",
+ &my_assert_on_error, &my_assert_on_error,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
#endif
{"default-character-set", OPT_DEFAULT_CHARACTER_SET_OLD,
"Set the default character set (deprecated option, use --character-set-server instead).",
@@ -6248,8 +6346,8 @@ struct my_option my_long_options[] =
&opt_debugging, &opt_debugging,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"general_log", OPT_GENERAL_LOG,
- "Enable/disable general log.", &opt_log,
- &opt_log, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ "Enable/disable general log. Filename can be specified with --general-log-file or --log-basename. Is 'hostname.log' by default.",
+ &opt_log, &opt_log, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
#ifdef HAVE_LARGE_PAGES
{"large-pages", OPT_ENABLE_LARGE_PAGES, "Enable support for large pages. "
"Disable with --skip-large-pages.", &opt_large_pages, &opt_large_pages,
@@ -6289,16 +6387,32 @@ each time the SQL thread starts.",
"--general_log/--general_log_file instead).", &opt_logname,
&opt_logname, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"general_log_file", OPT_GENERAL_LOG_FILE,
- "Log connections and queries to given file.", &opt_logname,
- &opt_logname, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ "Log connections and queries to given file. Defaults to "
+ "'datadir'/'log-basename'.log or 'datadir'/'hostname'.log if not "
+ "specified.",
+ &opt_logname, &opt_logname, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"log-basename", OPT_LOG_BASENAME,
+ "Basename for all log files and the .pid file. This sets all log file "
+ "names at once (in 'datadir') and is normally the only option you need "
+ "for specifying log files. This is especially recommend to be set if you "
+ "are using replication as it ensures that your log file names are not "
+ "depending on your host name. Sets names for --log-bin, --log-bin-index, "
+ "--relay-log, --relay-log-index, --general-log-file, "
+ "--log-slow-query-log-file, --log-error-file and --pid-file",
+ &opt_log_basename, &opt_log_basename, 0, GET_STR, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0},
{"log-bin", OPT_BIN_LOG,
- "Log update queries in binary format. Optional (but strongly recommended "
- "to avoid replication problems if server's hostname changes) argument "
- "should be the chosen location for the binary log files.",
- &opt_bin_logname, &opt_bin_logname, 0, GET_STR_ALLOC,
+ "Log update queries in binary format. Optional argument should be name for "
+ "binary log. If not given "
+ "datadir/'log-basename'-bin or 'datadir'/mysql-bin will be used (the later if "
+ "--log-basename is not specified). We strongly recommend you to use either "
+ "--log-basename or specify a filename to ensure that replication doesn't "
+ "stop if the real hostname of the computer changes'.",
+ &opt_bin_logname, &opt_bin_logname, 0, GET_STR,
OPT_ARG, 0, 0, 0, 0, 0, 0},
{"log-bin-index", OPT_BIN_LOG_INDEX,
- "File that holds the names for last binary log files.",
+ "File that holds the names for last binary log files. If not specified, "
+ "defaults to 'datadir/log-basename'-bin.index or 'datadir/mysql-bin.index'",
&opt_binlog_index_name, &opt_binlog_index_name, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#ifndef TO_BE_REMOVED_IN_5_1_OR_6_0
@@ -6327,7 +6441,10 @@ each time the SQL thread starts.",
"break, so you can safely set this to 1."
,&trust_function_creators, &trust_function_creators, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"log-error", OPT_ERROR_LOG_FILE, "Error log file.",
+ {"log-error", OPT_ERROR_LOG_FILE,
+ "Log errors to file (instead of stdout). If file name is not specified "
+ "then 'datadir'/'log-basename'.err or the pid-file path with extension "
+ ".err is used.",
&log_error_file_ptr, &log_error_file_ptr, 0, GET_STR,
OPT_ARG, 0, 0, 0, 0, 0, 0},
{"log-isam", OPT_ISAM_LOG, "Log all MyISAM changes to file.",
@@ -6365,15 +6482,20 @@ each time the SQL thread starts.",
&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)",
+ "Enable logging of slow queries (longer than --log-slow-time) to log file "
+ "or table. Optional argument is file name for slow log. If not given, "
+ "'log-basename'-slow.log will be used. Use --log-output=TABLE if you want "
+ "to have the log in the table 'mysql.slow_log'",
&opt_slow_logname, &opt_slow_logname, 0, GET_STR, OPT_ARG,
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.",
+ "Specify name for slow query log. Defaults to 'log-basename'-slow.log if "
+ "not given",
+ &opt_slow_logname, &opt_slow_logname, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"log-slow-file", OPT_SLOW_QUERY_LOG_FILE,
+ "Specify name for slow query log. Defaults to 'log-basename'-slow.log if "
+ "not given",
&opt_slow_logname, &opt_slow_logname, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"log-tc", OPT_LOG_TC,
@@ -6392,7 +6514,7 @@ each time the SQL thread starts.",
"log and this option just turns on --log-bin instead.",
&opt_update_logname, &opt_update_logname, 0, GET_STR,
OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"log-warnings", 'W', "Log some not critical warnings to the log file.",
+ {"log-warnings", 'W', "Log some not critical warnings to the general log file. Value can be between 0-11; The higher value, the more warnings",
&global_system_variables.log_warnings,
&max_system_variables.log_warnings, 0, GET_ULONG, OPT_ARG, 1, 0, 0,
0, 0, 0},
@@ -6415,7 +6537,8 @@ each time the SQL thread starts.",
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.",
+ "the I/O replication thread is in the master's binlogs. Defaults to "
+ "master.info. Is not affected by --log-basename.",
&master_info_file, &master_info_file, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"master-password", OPT_MASTER_PASSWORD,
@@ -6617,7 +6740,9 @@ each time the SQL thread starts.",
"per each user+host vs. per account).",
&opt_old_style_user_limits, &opt_old_style_user_limits,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"pid-file", OPT_PID_FILE, "Pid file used by safe_mysqld.",
+ {"pid-file", OPT_PID_FILE,
+ "Pid file used by safe_mysqld. If not set, defaults to "
+ "'datadir'/'log-basename'.pid or 'datadir'/'hostname'.pid'.",
&pidfile_name_ptr, &pidfile_name_ptr, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"port", 'P', "Port number to use for connection or 0 for default to, in "
@@ -6639,17 +6764,19 @@ each time the SQL thread starts.",
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.",
+ "The location and name to use for relay logs. If not specified "
+ "'datadir'/'log-basename' will be used.",
&opt_relay_logname, &opt_relay_logname, 0,
- GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"relay-log-index", OPT_RELAY_LOG_INDEX,
- "The location and name to use for the file that keeps a list of the last \
-relay logs.",
+ "The location and name to use for the file that keeps a list of the last "
+ "relay logs. If not specified 'datadir'/'log-basename' will be used.",
&opt_relaylog_index_name, &opt_relaylog_index_name, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"relay-log-info-file", OPT_RELAY_LOG_INFO_FILE,
- "The location and name of the file that remembers where the SQL replication \
-thread is in the relay logs.",
+ "The location and name of the file that remembers where the SQL "
+ "replication thread is in the relay logs. Defaults to relay-log.info. "
+ "Is not affected by '--log-basename'.",
&relay_log_info_file, &relay_log_info_file, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"replicate-do-db", OPT_REPLICATE_DO_DB,
@@ -6765,8 +6892,6 @@ thread is in the relay logs.",
{"shared-memory", OPT_ENABLE_SHARED_MEMORY,
"Enable the shared memory.",&opt_enable_shared_memory, &opt_enable_shared_memory,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
-#endif
-#ifdef HAVE_SMEM
{"shared-memory-base-name",OPT_SHARED_MEMORY_BASE_NAME,
"Base name of shared memory.", &shared_memory_base_name, &shared_memory_base_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
@@ -6795,14 +6920,11 @@ thread is in the relay logs.",
{"skip-networking", OPT_SKIP_NETWORKING,
"Don't allow connection with TCP/IP.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0,
0, 0, 0},
- {"skip-new", OPT_SKIP_NEW, "Don't use new, possibly wrong routines.",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
-#ifndef DBUG_OFF
#ifdef SAFEMALLOC
- {"skip-safemalloc", OPT_SKIP_SAFEMALLOC,
- "Don't use the memory allocation checking.", 0, 0, 0, GET_NO_ARG, NO_ARG,
- 0, 0, 0, 0, 0, 0},
-#endif
+ {"safemalloc", OPT_SAFEMALLOC,
+ "Check all memory allocation for every malloc/free call.",
+ &sf_malloc_trough_check, &sf_malloc_trough_check, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
#endif
{"skip-show-database", OPT_SKIP_SHOW_DB,
"Don't allow 'SHOW DATABASE' commands.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0,
@@ -6833,7 +6955,8 @@ thread is in the relay logs.",
&slave_exec_mode_str, &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.", &opt_slow_log,
+ "Enable/disable slow query log. See also '--log-slow-queries'",
+ &opt_slow_log,
&opt_slow_log, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"socket", OPT_SOCKET, "Socket file to use for connection.",
&mysqld_unix_port, &mysqld_unix_port, 0, GET_STR,
@@ -7070,6 +7193,11 @@ thread is in the relay logs.",
"The minimum percentage of warm blocks in key cache.",
&dflt_key_cache_var.param_division_limit, 0, 0,
(GET_ULONG | GET_ASK_ADDR) , REQUIRED_ARG, 100, 1, 100, 0, 1, 0},
+ {"key_cache_segments", OPT_KEY_CACHE_PARTITIONS,
+ "The number of segments in a key cache",
+ &dflt_key_cache_var.param_partitions, 0,
+ 0, (GET_ULONG | GET_ASK_ADDR), REQUIRED_ARG, DEFAULT_KEY_CACHE_PARTITIONS,
+ 0, MAX_KEY_CACHE_PARTITIONS, 0, 1, 0},
{"log-slow-filter", OPT_LOG_SLOW_FILTER,
"Log only the queries that followed certain execution plan. Multiple flags "
"allowed in a comma-separated string. [admin, filesort, filesort_on_disk, "
@@ -7086,19 +7214,16 @@ thread is in the relay logs.",
"Choose how verbose the messages to your slow log will be. Multiple flags "
"allowed in a comma-separated string. [query_plan, innodb]",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
- {"log-slow-file", OPT_SLOW_QUERY_LOG_FILE,
- "Log slow queries to given log file. Defaults logging to hostname-slow.log",
- &opt_slow_logname, &opt_slow_logname, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"long_query_time", OPT_LONG_QUERY_TIME,
- "Log all queries that have taken more than long_query_time seconds to "
- "execute. The argument will be treated as a decimal value with "
- "microsecond precision.",
+ {"log-slow-time", OPT_LONG_QUERY_TIME,
+ "Log all queries that have taken more than log-slow-time seconds to "
+ "execute to file. The argument will be treated as a decimal value with "
+ "microsecond precission. Same as --log-query-time.",
&long_query_time, &long_query_time, 0, GET_DOUBLE,
REQUIRED_ARG, 10, 0, LONG_TIMEOUT, 0, 0, 0},
- {"log-slow-time", OPT_LONG_QUERY_TIME,
- "Log all queries that have taken more than long_query_time seconds to execute to file. "
- "The argument will be treated as a decimal value with microsecond precission.",
+ {"long_query_time", OPT_LONG_QUERY_TIME,
+ "Log all queries that have taken more than long-query-time seconds to "
+ "execute to file. The argument will be treated as a decimal value with "
+ "microsecond precission. Same as --log-slow-time.",
&long_query_time, &long_query_time, 0, GET_DOUBLE,
REQUIRED_ARG, 10, 0, LONG_TIMEOUT, 0, 0, 0},
{"lower_case_table_names", OPT_LOWER_CASE_TABLE_NAMES,
@@ -7341,6 +7466,10 @@ thread is in the relay logs.",
"is the plugin library in plugin_dir.",
&opt_plugin_load, &opt_plugin_load, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"plugin-maturity", OPT_PLUGIN_MATURITY,
+ "The lowest desirable plugin maturity. Plugins less mature than that will not be installed or loaded.",
+ (uchar**) &plugin_maturity, (uchar**) &plugin_maturity, &plugin_maturity_values,
+ GET_ENUM, REQUIRED_ARG, server_maturity, 0, 0, 0, 0, 0},
{"preload_buffer_size", OPT_PRELOAD_BUFFER_SIZE,
"The size of the buffer that is allocated when preloading indexes.",
&global_system_variables.preload_buff_size,
@@ -7514,7 +7643,7 @@ thread is in the relay logs.",
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/Maria table.",
+ "automatically convert it to an on-disk MyISAM/Aria table.",
&global_system_variables.tmp_table_size,
&max_system_variables.tmp_table_size, 0, GET_ULL,
REQUIRED_ARG, 16*1024*1024L, 1024, MAX_MEM_TABLE_SIZE, 0, 1, 0},
@@ -7554,6 +7683,10 @@ thread is in the relay logs.",
&max_system_variables.net_wait_timeout, 0, GET_ULONG,
REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT),
0, 1, 0},
+ {"userstat", OPT_USERSTAT,
+ "Control USER_STATISTICS, CLIENT_STATISTICS, INDEX_STATISTICS and TABLE_STATISTICS running",
+ (uchar**) &opt_userstat_running, (uchar**) &opt_userstat_running,
+ 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0},
{"binlog-direct-non-transactional-updates", OPT_BINLOG_DIRECT_NON_TRANS_UPDATE,
"Causes updates to non-transactional engines using statement format to be "
"written directly to binary log. Before using this option, make sure that "
@@ -7926,6 +8059,46 @@ static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff)
#endif /* HAVE_OPENSSL */
+static int show_default_keycache(THD *thd, SHOW_VAR *var, char *buff)
+{
+ struct st_data {
+ KEY_CACHE_STATISTICS stats;
+ SHOW_VAR var[8];
+ } *data;
+ SHOW_VAR *v;
+
+ data=(st_data *)buff;
+ v= data->var;
+
+ var->type= SHOW_ARRAY;
+ var->value= (char*)v;
+
+ get_key_cache_statistics(dflt_key_cache, 0, &data->stats);
+
+#define set_one_keycache_var(X,Y) \
+ v->name= X; \
+ v->type= SHOW_LONGLONG; \
+ v->value= (char*)&data->stats.Y; \
+ v++;
+
+ set_one_keycache_var("blocks_not_flushed", blocks_changed);
+ set_one_keycache_var("blocks_unused", blocks_unused);
+ set_one_keycache_var("blocks_used", blocks_used);
+ set_one_keycache_var("blocks_warm", blocks_warm);
+ set_one_keycache_var("read_requests", read_requests);
+ set_one_keycache_var("reads", reads);
+ set_one_keycache_var("write_requests", write_requests);
+ set_one_keycache_var("writes", writes);
+
+ v->name= 0;
+
+ DBUG_ASSERT((char*)(v+1) <= buff + SHOW_VAR_FUNC_BUFF_SIZE);
+
+#undef set_one_keycache_var
+
+ return 0;
+}
+
/*
Variables shown by SHOW STATUS in alphabetical order
@@ -7934,19 +8107,24 @@ static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff)
SHOW_VAR status_vars[]= {
{"Aborted_clients", (char*) &aborted_threads, SHOW_LONG},
{"Aborted_connects", (char*) &aborted_connects, SHOW_LONG},
+ {"Access_denied_errors", (char*) offsetof(STATUS_VAR, access_denied_errors), SHOW_LONG_STATUS},
{"Binlog_cache_disk_use", (char*) &binlog_cache_disk_use, SHOW_LONG},
{"Binlog_cache_use", (char*) &binlog_cache_use, SHOW_LONG},
+ {"Busy_time", (char*) offsetof(STATUS_VAR, busy_time), SHOW_DOUBLE_STATUS},
{"Bytes_received", (char*) offsetof(STATUS_VAR, bytes_received), SHOW_LONGLONG_STATUS},
{"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONGLONG_STATUS},
+ {"Binlog_bytes_written", (char*) offsetof(STATUS_VAR, binlog_bytes_written), SHOW_LONGLONG_STATUS},
{"Com", (char*) com_status_vars, SHOW_ARRAY},
{"Compression", (char*) &show_net_compression, SHOW_FUNC},
{"Connections", (char*) &thread_id, SHOW_LONG_NOFLUSH},
+ {"Cpu_time", (char*) offsetof(STATUS_VAR, cpu_time), SHOW_DOUBLE_STATUS},
{"Created_tmp_disk_tables", (char*) offsetof(STATUS_VAR, created_tmp_disk_tables), SHOW_LONG_STATUS},
{"Created_tmp_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_NOFLUSH},
{"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG},
+ {"Empty_queries", (char*) offsetof(STATUS_VAR, empty_queries), SHOW_LONG_STATUS},
{"Flush_commands", (char*) &refresh_version, SHOW_LONG_NOFLUSH},
{"Handler_commit", (char*) offsetof(STATUS_VAR, ha_commit_count), SHOW_LONG_STATUS},
{"Handler_delete", (char*) offsetof(STATUS_VAR, ha_delete_count), SHOW_LONG_STATUS},
@@ -7963,14 +8141,7 @@ SHOW_VAR 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},
- {"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_blocks_warm", (char*) offsetof(KEY_CACHE, warm_blocks), 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},
+ {"Key", (char*) &show_default_keycache, SHOW_FUNC},
{"Last_query_cost", (char*) offsetof(STATUS_VAR, last_query_cost), SHOW_DOUBLE_STATUS},
{"Max_used_connections", (char*) &max_used_connections, SHOW_LONG},
{"Not_flushed_delayed_rows", (char*) &delayed_rows_in_use, SHOW_LONG_NOFLUSH},
@@ -7982,6 +8153,8 @@ SHOW_VAR status_vars[]= {
{"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS},
{"Opened_table_definitions", (char*) offsetof(STATUS_VAR, opened_shares), SHOW_LONG_STATUS},
{"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_FUNC},
+ {"Rows_sent", (char*) offsetof(STATUS_VAR, rows_sent), SHOW_LONGLONG_STATUS},
+ {"Rows_read", (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONGLONG_STATUS},
#ifdef HAVE_QUERY_CACHE
{"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH},
{"Qcache_free_memory", (char*) &query_cache.free_memory, SHOW_LONG_NOFLUSH},
@@ -8160,6 +8333,7 @@ static int mysql_init_variables(void)
opt_skip_name_resolve= 0;
opt_ignore_builtin_innodb= 0;
opt_logname= opt_update_logname= opt_binlog_index_name= opt_slow_logname= 0;
+ opt_log_basename= 0;
opt_tc_log_file= (char *)"tc.log"; // no hostname in tc_log file name !
opt_secure_auth= 0;
opt_secure_file_priv= 0;
@@ -8568,8 +8742,44 @@ mysqld_get_one_option(int optid,
case (int) OPT_BIN_LOG:
opt_bin_log= test(argument != disabled_my_option);
break;
+ case (int) OPT_LOG_BASENAME:
+ {
+ if (opt_log_basename[0] == 0 || strchr(opt_log_basename, FN_EXTCHAR) ||
+ strchr(opt_log_basename,FN_LIBCHAR))
+ {
+ sql_print_error("Wrong argument for --log-basename. It can't be empty or contain '.' or '" FN_DIRSEP "'");
+ return 1;
+ }
+ log_error_file_ptr= argument;
+
+ /*
+ The following file named needs explicite extensions (should be fixed in
+ future by having the creating code do this).
+ */
+ opt_logname= make_once_alloced_filename(argument, ".log");
+ opt_slow_logname= make_once_alloced_filename(argument, "-slow.log");
+ opt_bin_logname= make_once_alloced_filename(argument, "-bin");
+ opt_binlog_index_name= make_once_alloced_filename(argument, "-bin.index");
+ opt_relay_logname= make_once_alloced_filename(argument, "-relay-bin");
+ opt_relaylog_index_name=make_once_alloced_filename(argument,
+ "-relay-bin.index");
+
+ pidfile_name_ptr= pidfile_name;
+ strmake(pidfile_name, argument, sizeof(pidfile_name)-5);
+ strmov(fn_ext(pidfile_name),".pid");
+
+ /* The following is depricated so don't set it by default */
+ if (opt_update_logname)
+ opt_update_logname= argument;
+
+ /* check for errors */
+ if (!opt_bin_logname || !opt_relaylog_index_name || ! opt_logname ||
+ ! opt_slow_logname)
+ return 1; // out of memory error
+ break;
+ }
case (int) OPT_ERROR_LOG_FILE:
- opt_error_log= 1;
+ opt_error_log= (argument != disabled_my_option);
break;
#ifdef HAVE_REPLICATION
case (int) OPT_INIT_RPL_ROLE:
@@ -8595,6 +8805,7 @@ mysqld_get_one_option(int optid,
}
case (int)OPT_REPLICATE_REWRITE_DB:
{
+ /* See also OPT_REWRITE_DB handling in client/mysqlbinlog.cc */
char* key = argument,*p, *val;
if (!(p= strstr(argument, "->")))
@@ -8683,7 +8894,6 @@ mysqld_get_one_option(int optid,
}
#endif /* HAVE_REPLICATION */
case (int) OPT_SLOW_QUERY_LOG:
- WARN_DEPRECATED(NULL, "7.0", "--log_slow_queries", "'--slow_query_log'/'--log-slow-file'");
opt_slow_log= 1;
break;
case OPT_LOG_OUTPUT:
@@ -8705,29 +8915,20 @@ mysqld_get_one_option(int optid,
}
case OPT_EVENT_SCHEDULER:
#ifndef HAVE_EVENT_SCHEDULER
- sql_perror("Event scheduler is not supported in embedded build.");
+ sql_print_error("Event scheduler is not supported in embedded build.");
#else
if (Events::set_opt_event_scheduler(argument))
return 1;
#endif
break;
- case (int) OPT_SKIP_NEW:
- opt_specialflag|= SPECIAL_NO_NEW_FUNC;
- delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE;
- myisam_concurrent_insert=0;
- myisam_recover_options= HA_RECOVER_NONE;
- sp_automatic_privileges=0;
- my_use_symdir=0;
- ha_open_options&= ~(HA_OPEN_ABORT_IF_CRASHED | HA_OPEN_DELAY_KEY_WRITE);
-#ifdef HAVE_QUERY_CACHE
- query_cache_size=0;
-#endif
- break;
case (int) OPT_SAFE:
- opt_specialflag|= SPECIAL_SAFE_MODE;
+ opt_specialflag|= SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC;
delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE;
myisam_recover_options= HA_RECOVER_DEFAULT;
ha_open_options&= ~(HA_OPEN_DELAY_KEY_WRITE);
+#ifdef HAVE_QUERY_CACHE
+ query_cache_size=0;
+#endif
break;
case (int) OPT_SKIP_PRIOR:
opt_specialflag|= SPECIAL_NO_PRIOR;
@@ -9038,16 +9239,16 @@ mysqld_get_one_option(int optid,
break;
}
case OPT_ONE_THREAD:
- global_system_variables.thread_handling= SCHEDULER_NO_THREADS;
- opt_thread_handling= thread_handling_typelib.type_names[global_system_variables.thread_handling];
+ thread_handling= SCHEDULER_NO_THREADS;
+ opt_thread_handling= thread_handling_typelib.type_names[thread_handling];
break;
case OPT_THREAD_HANDLING:
{
int id;
LINT_INIT(id);
if (!find_opt_type(argument, &thread_handling_typelib, opt->name, &id))
- global_system_variables.thread_handling= id - 1;
- opt_thread_handling= thread_handling_typelib.type_names[global_system_variables.thread_handling];
+ thread_handling= id - 1;
+ opt_thread_handling= thread_handling_typelib.type_names[thread_handling];
break;
}
case OPT_FT_BOOLEAN_SYNTAX:
@@ -9058,11 +9259,6 @@ mysqld_get_one_option(int optid,
}
strmake(ft_boolean_syntax, argument, sizeof(ft_boolean_syntax)-1);
break;
- case OPT_SKIP_SAFEMALLOC:
-#ifdef SAFEMALLOC
- sf_malloc_quick=1;
-#endif
- break;
case OPT_LOWER_CASE_TABLE_NAMES:
lower_case_table_names= argument ? atoi(argument) : 1;
lower_case_table_names_used= 1;
@@ -9112,6 +9308,7 @@ mysql_getopt_value(const char *keyname, uint key_length,
case OPT_KEY_CACHE_BLOCK_SIZE:
case OPT_KEY_CACHE_DIVISION_LIMIT:
case OPT_KEY_CACHE_AGE_THRESHOLD:
+ case OPT_KEY_CACHE_PARTITIONS:
{
KEY_CACHE *key_cache;
if (!(key_cache= get_or_create_key_cache(keyname, key_length)))
@@ -9129,6 +9326,8 @@ mysql_getopt_value(const char *keyname, uint key_length,
return &key_cache->param_division_limit;
case OPT_KEY_CACHE_AGE_THRESHOLD:
return &key_cache->param_age_threshold;
+ case OPT_KEY_CACHE_PARTITIONS:
+ return (uchar**) &key_cache->param_partitions;
}
}
}
@@ -9217,7 +9416,7 @@ static int get_options(int *argc,char **argv)
if (mysqld_chroot)
set_root(mysqld_chroot);
#else
- global_system_variables.thread_handling = SCHEDULER_NO_THREADS;
+ thread_handling = SCHEDULER_NO_THREADS;
max_allowed_packet= global_system_variables.max_allowed_packet;
net_buffer_length= global_system_variables.net_buffer_length;
#endif
@@ -9238,6 +9437,19 @@ static int get_options(int *argc,char **argv)
myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size);
my_crc_dbug_check= opt_my_crc_dbug_check;
+ /*
+ Log mysys errors when we don't have a thd or thd->log_all_errors is set
+ (recovery) to the log. This is mainly useful for debugging strange system
+ errors.
+ */
+ if (global_system_variables.log_warnings >= 10)
+ my_global_flags= MY_WME | ME_JUST_INFO;
+ /* Log all errors not handled by thd->handle_error() to my_message_sql() */
+ if (global_system_variables.log_warnings >= 11)
+ my_global_flags|= ME_NOREFRESH;
+ if (my_assert_on_error)
+ debug_assert_if_crashed_table= 1;
+
/* long_query_time is in microseconds */
global_system_variables.long_query_time= max_system_variables.long_query_time=
(longlong) (long_query_time * 1000000.0);
@@ -9253,15 +9465,17 @@ static int get_options(int *argc,char **argv)
&global_system_variables.datetime_format))
return 1;
+#ifdef SAFEMALLOC
+ sf_malloc_quick= !sf_malloc_trough_check;
+#endif
#ifdef EMBEDDED_LIBRARY
one_thread_scheduler(&thread_scheduler);
one_thread_scheduler(&extra_thread_scheduler);
#else
- if (global_system_variables.thread_handling <=
- SCHEDULER_ONE_THREAD_PER_CONNECTION)
+ if (thread_handling <= SCHEDULER_ONE_THREAD_PER_CONNECTION)
one_thread_per_connection_scheduler(&thread_scheduler, &max_connections,
&connection_count);
- else if (global_system_variables.thread_handling == SCHEDULER_NO_THREADS)
+ else if (thread_handling == SCHEDULER_NO_THREADS)
one_thread_scheduler(&thread_scheduler);
else
pool_of_threads_scheduler(&thread_scheduler); /* purecov: tested */
@@ -9649,6 +9863,8 @@ void refresh_status(THD *thd)
/* Reset thread's status variables */
bzero((uchar*) &thd->status_var, sizeof(thd->status_var));
+ bzero((uchar*) &thd->org_status_var, sizeof(thd->org_status_var));
+ thd->start_bytes_received= 0;
/* Reset some global variables */
reset_status_vars();
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index eb5f45bbced..272b1759151 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -116,7 +116,7 @@ my_bool my_net_init(NET *net, Vio* vio)
net->vio = vio;
my_net_local_init(net); /* Set some limits */
if (!(net->buff=(uchar*) my_malloc((size_t) net->max_packet+
- NET_HEADER_SIZE + COMP_HEADER_SIZE,
+ NET_HEADER_SIZE + COMP_HEADER_SIZE +1,
MYF(MY_WME))))
DBUG_RETURN(1);
net->buff_end=net->buff+net->max_packet;
@@ -580,7 +580,7 @@ net_real_write(NET *net,const uchar *packet, size_t len)
uchar *b;
uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE;
if (!(b= (uchar*) my_malloc(len + NET_HEADER_SIZE +
- COMP_HEADER_SIZE, MYF(MY_WME))))
+ COMP_HEADER_SIZE + 1, MYF(MY_WME))))
{
net->error= 2;
net->last_errno= ER_OUT_OF_RESOURCES;
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index a42a03f4c86..3821665fb22 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -708,8 +708,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
static
TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
double read_time);
-static
-TRP_GROUP_MIN_MAX *get_best_group_min_max(PARAM *param, SEL_TREE *tree);
+static TRP_GROUP_MIN_MAX *get_best_group_min_max(PARAM *param, SEL_TREE *tree);
#ifndef DBUG_OFF
static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map,
@@ -1437,7 +1436,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler)
quick->record= head->record[0];
}
- if (need_to_fetch_row && head->file->ha_rnd_init(1))
+ if (need_to_fetch_row && head->file->ha_rnd_init_with_error(1))
{
DBUG_PRINT("error", ("ROR index_merge rnd_init call failed"));
DBUG_RETURN(1);
@@ -1615,7 +1614,7 @@ int QUICK_ROR_UNION_SELECT::reset()
queue_insert(&queue, (uchar*)quick);
}
- if (head->file->ha_rnd_init(1))
+ if (head->file->ha_rnd_init_with_error(1))
{
DBUG_PRINT("error", ("ROR index_merge rnd_init call failed"));
DBUG_RETURN(1);
@@ -7607,8 +7606,8 @@ check_quick_keys(PARAM *param, uint idx, SEL_ARG *key_tree,
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 &&
+ (param->table->key_info[keynr].flags & HA_NOSAME) &&
+ min_key_length == max_key_length &&
!memcmp(param->min_key, param->max_key, min_key_length) &&
!param->first_null_comp)
{
@@ -7897,7 +7896,7 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
{
KEY *table_key=quick->head->key_info+quick->index;
flag=EQ_RANGE;
- if ((table_key->flags & (HA_NOSAME | HA_END_SPACE_KEY)) == HA_NOSAME &&
+ if ((table_key->flags & HA_NOSAME) &&
key->part == table_key->key_parts-1)
{
if (!(table_key->flags & HA_NULL_PART_KEY) ||
@@ -7947,7 +7946,7 @@ bool QUICK_RANGE_SELECT::unique_key_range()
if ((tmp->flag & (EQ_RANGE | NULL_RANGE)) == EQ_RANGE)
{
KEY *key=head->key_info+index;
- return ((key->flags & (HA_NOSAME | HA_END_SPACE_KEY)) == HA_NOSAME &&
+ return ((key->flags & HA_NOSAME) &&
key->key_length == tmp->min_length);
}
}
@@ -8066,8 +8065,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
range->min_length= range->max_length= ref->key_length;
range->min_keypart_map= range->max_keypart_map=
make_prev_keypart_map(ref->key_parts);
- range->flag= ((ref->key_length == key_info->key_length &&
- (key_info->flags & HA_END_SPACE_KEY) == 0) ? EQ_RANGE : 0);
+ range->flag= (ref->key_length == key_info->key_length ? EQ_RANGE : 0);
if (!(quick->key_parts=key_part=(KEY_PART *)
alloc_root(&quick->alloc,sizeof(KEY_PART)*ref->key_parts)))
@@ -8224,7 +8222,8 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge()
index_merge currently doesn't support "using index" at all
*/
head->disable_keyread();
- init_read_record(&read_record, thd, head, (SQL_SELECT*) 0, 1 , 1, TRUE);
+ if (init_read_record(&read_record, thd, head, (SQL_SELECT*) 0, 1 , 1, TRUE))
+ result= 1;
DBUG_RETURN(result);
err:
@@ -8370,7 +8369,7 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
/* 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);
+ error= head->file->ha_rnd_pos(head->record[0], last_rowid);
} while (error == HA_ERR_RECORD_DELETED);
if (!need_to_fetch_row)
@@ -8451,7 +8450,7 @@ int QUICK_ROR_UNION_SELECT::get_next()
cur_rowid= prev_rowid;
prev_rowid= tmp;
- error= head->file->rnd_pos(quick->record, prev_rowid);
+ error= head->file->ha_rnd_pos(quick->record, prev_rowid);
} while (error == HA_ERR_RECORD_DELETED);
DBUG_RETURN(error);
}
@@ -8660,10 +8659,12 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length,
int result;
if (last_range)
{
- /* Read the next record in the same range with prefix after cur_prefix. */
+ /*
+ Read the next record in the same range with prefix after cur_prefix.
+ */
DBUG_ASSERT(cur_prefix != NULL);
- result= file->index_read_map(record, cur_prefix, keypart_map,
- HA_READ_AFTER_KEY);
+ result= file->ha_index_read_map(record, cur_prefix, keypart_map,
+ HA_READ_AFTER_KEY);
if (result || last_range->max_keypart_map == 0)
DBUG_RETURN(result);
@@ -8712,8 +8713,8 @@ int QUICK_RANGE_SELECT_GEOM::get_next()
if (last_range)
{
// Already read through key
- result= file->index_next_same(record, last_range->min_key,
- last_range->min_length);
+ result= file->ha_index_next_same(record, last_range->min_key,
+ last_range->min_length);
if (result != HA_ERR_END_OF_FILE)
DBUG_RETURN(result);
}
@@ -8727,10 +8728,10 @@ int QUICK_RANGE_SELECT_GEOM::get_next()
}
last_range= *(cur_range++);
- result= file->index_read_map(record, last_range->min_key,
- last_range->min_keypart_map,
- (ha_rkey_function)(last_range->flag ^
- GEOM_FLAG));
+ result= file->ha_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
@@ -8842,9 +8843,9 @@ 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, last_range->min_key,
+ file->ha_index_next_same(record, last_range->min_key,
last_range->min_length) :
- file->index_prev(record));
+ file->ha_index_prev(record));
if (!result)
{
if (cmp_prev(*rev_it.ref()) == 0)
@@ -8860,7 +8861,7 @@ int QUICK_SELECT_DESC::get_next()
if (last_range->flag & NO_MAX_RANGE) // Read last record
{
int local_error;
- if ((local_error=file->index_last(record)))
+ if ((local_error= file->ha_index_last(record)))
DBUG_RETURN(local_error); // Empty table
if (cmp_prev(last_range) == 0)
DBUG_RETURN(0);
@@ -8872,9 +8873,9 @@ int QUICK_SELECT_DESC::get_next()
used_key_parts <= head->key_info[index].key_parts)
{
- result = file->index_read_map(record, last_range->max_key,
- last_range->max_keypart_map,
- HA_READ_KEY_EXACT);
+ result= file->ha_index_read_map(record, last_range->max_key,
+ last_range->max_keypart_map,
+ HA_READ_KEY_EXACT);
}
else
{
@@ -8882,11 +8883,11 @@ 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_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));
+ result= file->ha_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)
{
@@ -9055,7 +9056,7 @@ void QUICK_RANGE_SELECT::add_keys_and_lengths(String *key_names,
uint length;
KEY *key_info= head->key_info + index;
key_names->append(key_info->name);
- length= longlong2str(max_used_key_length, buf, 10) - buf;
+ length= longlong10_to_str(max_used_key_length, buf, 10) - buf;
used_lengths->append(buf, length);
}
@@ -9080,7 +9081,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;
+ length= longlong10_to_str(quick->max_used_key_length, buf, 10) - buf;
used_lengths->append(buf, length);
}
if (pk_quick_select)
@@ -9088,7 +9089,8 @@ void QUICK_INDEX_MERGE_SELECT::add_keys_and_lengths(String *key_names,
KEY *key_info= head->key_info + pk_quick_select->index;
key_names->append(',');
key_names->append(key_info->name);
- length= longlong2str(pk_quick_select->max_used_key_length, buf, 10) - buf;
+ length= (longlong10_to_str(pk_quick_select->max_used_key_length, buf, 10)
+ - buf);
used_lengths->append(',');
used_lengths->append(buf, length);
}
@@ -9113,7 +9115,7 @@ void QUICK_ROR_INTERSECT_SELECT::add_keys_and_lengths(String *key_names,
used_lengths->append(',');
}
key_names->append(key_info->name);
- length= longlong2str(qr->quick->max_used_key_length, buf, 10) - buf;
+ length= longlong10_to_str(qr->quick->max_used_key_length, buf, 10) - buf;
used_lengths->append(buf, length);
}
@@ -9122,7 +9124,7 @@ void QUICK_ROR_INTERSECT_SELECT::add_keys_and_lengths(String *key_names,
KEY *key_info= head->key_info + cpk_quick->index;
key_names->append(',');
key_names->append(key_info->name);
- length= longlong2str(cpk_quick->max_used_key_length, buf, 10) - buf;
+ length= longlong10_to_str(cpk_quick->max_used_key_length, buf, 10) - buf;
used_lengths->append(',');
used_lengths->append(buf, length);
}
@@ -10634,7 +10636,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::reset(void)
DBUG_RETURN(result);
if (quick_prefix_select && quick_prefix_select->reset())
DBUG_RETURN(1);
- result= file->index_last(record);
+ result= file->ha_index_last(record);
if (result == HA_ERR_END_OF_FILE)
DBUG_RETURN(0);
/* Save the prefix of the last group. */
@@ -10736,9 +10738,9 @@ 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_map(record, group_prefix,
- make_prev_keypart_map(real_key_parts),
- HA_READ_KEY_EXACT);
+ result= file->ha_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 || result == HA_ERR_END_OF_FILE) &&
@@ -10800,9 +10802,10 @@ 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_map(record, group_prefix,
- make_prev_keypart_map(real_key_parts),
- HA_READ_KEY_EXACT)))
+ if ((result=
+ file->ha_index_read_map(record, group_prefix,
+ make_prev_keypart_map(real_key_parts),
+ HA_READ_KEY_EXACT)))
DBUG_RETURN(result);
}
@@ -10817,9 +10820,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_map(record, tmp_record,
- make_keypart_map(real_key_parts),
- HA_READ_AFTER_KEY);
+ result= file->ha_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
@@ -10874,9 +10877,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_map(record, group_prefix,
- make_prev_keypart_map(real_key_parts),
- HA_READ_PREFIX_LAST);
+ result= file->ha_index_read_map(record, group_prefix,
+ make_prev_keypart_map(real_key_parts),
+ HA_READ_PREFIX_LAST);
DBUG_RETURN(result);
}
@@ -10920,7 +10923,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix()
{
if (!seen_first_key)
{
- result= file->index_first(record);
+ result= file->ha_index_first(record);
if (result)
DBUG_RETURN(result);
seen_first_key= TRUE;
@@ -10928,9 +10931,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix()
else
{
/* Load the first key in this group into record. */
- result= file->index_read_map(record, group_prefix,
- make_prev_keypart_map(group_key_parts),
- HA_READ_AFTER_KEY);
+ result= file->ha_index_read_map(record, group_prefix,
+ make_prev_keypart_map(group_key_parts),
+ HA_READ_AFTER_KEY);
if (result)
DBUG_RETURN(result);
}
@@ -11007,7 +11010,8 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min_in_range()
HA_READ_AFTER_KEY : HA_READ_KEY_OR_NEXT;
}
- result= file->index_read_map(record, group_prefix, keypart_map, find_flag);
+ result= file->ha_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) &&
@@ -11055,6 +11059,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min_in_range()
/* Compare the found key with max_key. */
int cmp_res= key_cmp(index_info->key_part, max_key,
real_prefix_len + min_max_arg_len);
+ my_afree(max_key);
/*
The key is outside of the range if:
the interval is open and the key is equal to the maximum boundry
@@ -11146,7 +11151,8 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max_in_range()
HA_READ_BEFORE_KEY : HA_READ_PREFIX_LAST_OR_PREV;
}
- result= file->index_read_map(record, group_prefix, keypart_map, find_flag);
+ result= file->ha_index_read_map(record, group_prefix, keypart_map,
+ find_flag);
if (result)
{
@@ -11179,6 +11185,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max_in_range()
/* Compare the found key with min_key. */
int cmp_res= key_cmp(index_info->key_part, min_key,
real_prefix_len + min_max_arg_len);
+ my_afree(min_key);
/*
The key is outside of the range if:
the interval is open and the key is equal to the minimum boundry
@@ -11282,7 +11289,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= longlong2str(max_used_key_length, buf, 10) - buf;
+ length= longlong10_to_str(max_used_key_length, buf, 10) - buf;
used_lengths->append(buf, length);
}
@@ -11314,7 +11321,8 @@ 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: 0x%lx (%s) scans: %s", (long) tree, msg, tmp.ptr()));
+ DBUG_PRINT("info", ("SEL_TREE: 0x%lx (%s) scans: %s", (long) tree, msg,
+ tmp.c_ptr()));
DBUG_VOID_RETURN;
}
@@ -11337,7 +11345,7 @@ static void print_ror_scans_arr(TABLE *table, const char *msg,
}
if (!tmp.length())
tmp.append(STRING_WITH_LEN("(empty)"));
- DBUG_PRINT("info", ("ROR key scans (%s): %s", msg, tmp.ptr()));
+ DBUG_PRINT("info", ("ROR key scans (%s): %s", msg, tmp.c_ptr()));
DBUG_VOID_RETURN;
}
diff --git a/sql/opt_range.h b/sql/opt_range.h
index d4852671717..eaaf4a2a2a8 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -833,7 +833,7 @@ public:
~FT_SELECT() { file->ft_end(); }
int init() { return error=file->ft_init(); }
int reset() { return 0; }
- int get_next() { return error=file->ft_read(record); }
+ int get_next() { return error= file->ha_ft_read(record); }
int get_type() { return QS_TYPE_FULLTEXT; }
};
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index 7bb75c3175d..f8a81eac380 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -111,7 +111,7 @@ static int get_index_min_value(TABLE *table, TABLE_REF *ref,
int error;
if (!ref->key_length)
- error= table->file->index_first(table->record[0]);
+ error= table->file->ha_index_first(table->record[0]);
else
{
/*
@@ -133,10 +133,10 @@ static int get_index_min_value(TABLE *table, TABLE_REF *ref,
Closed interval: Either The MIN argument is non-nullable, or
we have a >= predicate for the MIN argument.
*/
- error= table->file->index_read_map(table->record[0],
- ref->key_buff,
- make_prev_keypart_map(ref->key_parts),
- HA_READ_KEY_OR_NEXT);
+ error= table->file->ha_index_read_map(table->record[0],
+ ref->key_buff,
+ make_prev_keypart_map(ref->key_parts),
+ HA_READ_KEY_OR_NEXT);
else
{
/*
@@ -148,10 +148,10 @@ static int get_index_min_value(TABLE *table, TABLE_REF *ref,
and it would not work.
*/
DBUG_ASSERT(prefix_len < ref->key_length);
- error= table->file->index_read_map(table->record[0],
- ref->key_buff,
- make_prev_keypart_map(ref->key_parts),
- HA_READ_AFTER_KEY);
+ error= table->file->ha_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
@@ -174,10 +174,10 @@ static int get_index_min_value(TABLE *table, TABLE_REF *ref,
key_cmp_if_same(table, ref->key_buff, ref->key, prefix_len)))
{
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);
+ error= table->file->ha_index_read_map(table->record[0],
+ ref->key_buff,
+ make_prev_keypart_map(ref->key_parts),
+ HA_READ_KEY_EXACT);
}
}
}
@@ -200,12 +200,12 @@ static int get_index_min_value(TABLE *table, TABLE_REF *ref,
static int get_index_max_value(TABLE *table, TABLE_REF *ref, uint range_fl)
{
return (ref->key_length ?
- table->file->index_read_map(table->record[0], ref->key_buff,
- make_prev_keypart_map(ref->key_parts),
- range_fl & NEAR_MAX ?
- HA_READ_BEFORE_KEY :
- HA_READ_PREFIX_LAST_OR_PREV) :
- table->file->index_last(table->record[0]));
+ table->file->ha_index_read_map(table->record[0], ref->key_buff,
+ make_prev_keypart_map(ref->key_parts),
+ range_fl & NEAR_MAX ?
+ HA_READ_BEFORE_KEY :
+ HA_READ_PREFIX_LAST_OR_PREV) :
+ table->file->ha_index_last(table->record[0]));
}
@@ -381,12 +381,11 @@ int opt_sum_query(THD *thd,
const_result= 0;
break;
}
- table->file->ha_index_init((uint) ref.key, 1);
-
- error= is_max ?
- get_index_max_value(table, &ref, range_fl) :
- get_index_min_value(table, &ref, item_field, range_fl,
- prefix_len);
+ if (!(error= table->file->ha_index_init((uint) ref.key, 1)))
+ error= (is_max ?
+ get_index_max_value(table, &ref, range_fl) :
+ get_index_min_value(table, &ref, item_field, range_fl,
+ prefix_len));
/* Verify that the read tuple indeed matches the search key */
if (!error && reckey_in_range(is_max, &ref, item_field->field,
@@ -660,6 +659,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
case Item_func::GE_FUNC:
break;
case Item_func::BETWEEN:
+ if (((Item_func_between*) cond)->negated)
+ DBUG_RETURN(FALSE);
between= 1;
break;
case Item_func::MULT_EQUAL_FUNC:
diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc
index 5d952021365..fdf818abb8e 100644
--- a/sql/opt_table_elimination.cc
+++ b/sql/opt_table_elimination.cc
@@ -1539,7 +1539,7 @@ Dep_value_table *Dep_analysis_context::create_table_value(TABLE *table)
for (uint i=0; i < table->s->keys; i++)
{
KEY *key= table->key_info + i;
- if ((key->flags & (HA_NOSAME | HA_END_SPACE_KEY)) == HA_NOSAME)
+ if (key->flags & HA_NOSAME)
{
Dep_module_key *key_dep;
if (!(key_dep= new Dep_module_key(tbl_dep, i, key->key_parts)))
@@ -1778,7 +1778,7 @@ static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl)
JOIN_TAB *tab= tbl->table->reginfo.join_tab;
if (!(join->const_table_map & tab->table->map))
{
- DBUG_PRINT("info", ("Eliminated table %s", table->alias));
+ DBUG_PRINT("info", ("Eliminated table %s", table->alias.c_ptr()));
tab->type= JT_CONST;
join->eliminated_tables |= table->map;
join->const_table_map|= table->map;
@@ -1813,7 +1813,7 @@ void Dep_analysis_context::dbug_print_deps()
fprintf(DBUG_FILE, " equality%ld: %s -> %s.%s\n",
(long)(eq_mod - equality_mods),
str.c_ptr(),
- eq_mod->field->table->table->alias,
+ eq_mod->field->table->table->alias.c_ptr(),
eq_mod->field->field->field_name);
}
else
@@ -1831,12 +1831,13 @@ void Dep_analysis_context::dbug_print_deps()
if ((table_dep= table_deps[i]))
{
/* Print table */
- fprintf(DBUG_FILE, " table %s\n", table_dep->table->alias);
+ fprintf(DBUG_FILE, " table %s\n", table_dep->table->alias.c_ptr());
/* Print fields */
for (Dep_value_field *field_dep= table_dep->fields; field_dep;
field_dep= field_dep->next_table_field)
{
- fprintf(DBUG_FILE, " field %s.%s ->", table_dep->table->alias,
+ fprintf(DBUG_FILE, " field %s.%s ->",
+ table_dep->table->alias.c_ptr(),
field_dep->field->field_name);
uint ofs= field_dep->bitmap_offset;
for (uint bit= ofs; bit < ofs + n_equality_mods; bit++)
diff --git a/sql/parse_file.h b/sql/parse_file.h
index 247cb147389..2ebcd2ec987 100644
--- a/sql/parse_file.h
+++ b/sql/parse_file.h
@@ -92,12 +92,12 @@ class File_parser: public Sql_alloc
{
char *buff, *start, *end;
LEX_STRING file_type;
- my_bool content_ok;
+ 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; }
+ 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,
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index f51d3fdf641..ac3042fd815 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -1106,8 +1106,8 @@ void partition_info::print_no_partition_found(TABLE *table)
if (part_expr->null_value)
buf_ptr= (char*)"NULL";
else
- longlong2str(err_value, buf,
- part_expr->unsigned_flag ? 10 : -10);
+ longlong10_to_str(err_value, buf,
+ part_expr->unsigned_flag ? 10 : -10);
my_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, MYF(0), buf_ptr);
dbug_tmp_restore_column_map(table->read_set, old_map);
}
diff --git a/sql/password.c b/sql/password.c
index c8aa90aa56d..27fbb07b6e3 100644
--- a/sql/password.c
+++ b/sql/password.c
@@ -190,7 +190,7 @@ void scramble_323(char *to, const char *message, const char *password)
*/
my_bool
-check_scramble_323(const char *scrambled, const char *message,
+check_scramble_323(const unsigned char *scrambled, const char *message,
ulong *hash_pass)
{
struct my_rnd_struct rand_st;
@@ -198,7 +198,7 @@ check_scramble_323(const char *scrambled, const char *message,
/* Big enough for checks. */
char buff[16], scrambled_buff[SCRAMBLE_LENGTH_323 + 1];
char *to, extra;
- const char *pos;
+ const uchar *pos;
/* Ensure that the scrambled message is null-terminated. */
memcpy(scrambled_buff, scrambled, SCRAMBLE_LENGTH_323);
@@ -218,7 +218,7 @@ check_scramble_323(const char *scrambled, const char *message,
to=buff;
while (*scrambled)
{
- if (*scrambled++ != (char) (*to++ ^ extra))
+ if (*scrambled++ != (uchar) (*to++ ^ extra))
return 1; /* Wrong password */
}
return 0;
@@ -485,7 +485,7 @@ scramble(char *to, const char *message, const char *password)
*/
my_bool
-check_scramble(const char *scramble_arg, const char *message,
+check_scramble(const uchar *scramble_arg, const char *message,
const uint8 *hash_stage2)
{
SHA1_CONTEXT sha1_context;
@@ -498,7 +498,7 @@ check_scramble(const char *scramble_arg, const char *message,
mysql_sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE);
mysql_sha1_result(&sha1_context, buf);
/* encrypt scramble */
- my_crypt((char *) buf, buf, (const uchar *) scramble_arg, SCRAMBLE_LENGTH);
+ my_crypt((char *) buf, buf, scramble_arg, SCRAMBLE_LENGTH);
/* now buf supposedly contains hash_stage1: so we can get hash_stage2 */
mysql_sha1_reset(&sha1_context);
mysql_sha1_input(&sha1_context, buf, SHA1_HASH_SIZE);
diff --git a/sql/procedure.h b/sql/procedure.h
index ceb586766b1..30a8a0efccb 100644
--- a/sql/procedure.h
+++ b/sql/procedure.h
@@ -42,7 +42,11 @@ public:
{
init_make_field(tmp_field,field_type());
}
- unsigned int size_of() { return sizeof(*this);}
+ unsigned int size_of() { return sizeof(*this);}
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor("proc");
+ }
};
class Item_proc_real :public Item_proc
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 055c166f5a5..3381cf2f1bb 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -343,24 +343,6 @@ static bool write_eof_packet(THD *thd, NET *net,
}
/**
- Please client to send scrambled_password in old format.
-
- @param thd thread handle
-
- @retval
- 0 ok
- @retval
- !0 error
-*/
-
-bool send_old_password_request(THD *thd)
-{
- NET *net= &thd->net;
- return my_net_write(net, eof_buff, 1) || net_flush(net);
-}
-
-
-/**
@param thd Thread handler
@param sql_errno The error code to send
@param err A pointer to the error message
@@ -652,6 +634,9 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
uint count= 0;
#endif
+ /* We have to reallocate it here as a stored procedure may have reset it */
+ (void) local_packet->alloc(thd->variables.net_buffer_length);
+
while ((item=it++))
{
char *pos;
diff --git a/sql/protocol.h b/sql/protocol.h
index 5d49aa28112..1560f7272b5 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -180,7 +180,6 @@ public:
void send_warning(THD *thd, uint sql_errno, const char *err=0);
bool net_send_error(THD *thd, uint sql_errno=0, const char *err=0);
void net_end_statement(THD *thd);
-bool 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);
diff --git a/sql/records.cc b/sql/records.cc
index 181afbad400..208405b0657 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -159,7 +159,8 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
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,
+
+bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
SQL_SELECT *select,
int use_record_cache, bool print_error,
bool disable_rr_cache)
@@ -208,7 +209,8 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
reinit_io_cache(info->io_cache,READ_CACHE,0L,0,0);
info->ref_pos=table->file->ref;
if (!table->file->inited)
- table->file->ha_rnd_init(0);
+ if (table->file->ha_rnd_init_with_error(0))
+ DBUG_RETURN(1);
/*
table->sort.addon_field is checked because if we use addon fields,
@@ -217,7 +219,6 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
*/
if (!disable_rr_cache &&
!table->sort.addon_field &&
- ! (specialflag & SPECIAL_SAFE_MODE) &&
thd->variables.read_rnd_buff_size &&
!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
(table->db_stat & HA_READ_ONLY ||
@@ -245,7 +246,8 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
else if (table->sort.record_pointers)
{
DBUG_PRINT("info",("using record_pointers"));
- table->file->ha_rnd_init(0);
+ if (table->file->ha_rnd_init_with_error(0))
+ DBUG_RETURN(1);
info->cache_pos=table->sort.record_pointers;
info->cache_end=info->cache_pos+
table->sort.found_records*info->ref_length;
@@ -256,7 +258,8 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
{
DBUG_PRINT("info",("using rr_sequential"));
info->read_record=rr_sequential;
- table->file->ha_rnd_init(1);
+ if (table->file->ha_rnd_init_with_error(1))
+ DBUG_RETURN(1);
/* We can use record cache if we don't update dynamic length tables */
if (!table->no_cache &&
(use_record_cache > 0 ||
@@ -274,7 +277,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
!table->file->pushed_cond)
table->file->cond_push(select->cond);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
} /* init_read_record */
@@ -330,6 +333,7 @@ static int rr_quick(READ_RECORD *info)
break;
}
}
+ update_virtual_fields(info->thd, info->table);
return tmp;
}
@@ -349,7 +353,7 @@ static int rr_quick(READ_RECORD *info)
static int rr_index_first(READ_RECORD *info)
{
- int tmp= info->file->index_first(info->record);
+ int tmp= info->file->ha_index_first(info->record);
info->read_record= rr_index;
if (tmp)
tmp= rr_handle_error(info, tmp);
@@ -375,7 +379,7 @@ static int rr_index_first(READ_RECORD *info)
static int rr_index(READ_RECORD *info)
{
- int tmp= info->file->index_next(info->record);
+ int tmp= info->file->ha_index_next(info->record);
if (tmp)
tmp= rr_handle_error(info, tmp);
return tmp;
@@ -385,7 +389,7 @@ static int rr_index(READ_RECORD *info)
int rr_sequential(READ_RECORD *info)
{
int tmp;
- while ((tmp=info->file->rnd_next(info->record)))
+ while ((tmp= info->file->ha_rnd_next(info->record)))
{
/*
rnd_next can return RECORD_DELETED for MyISAM when one thread is
@@ -397,6 +401,8 @@ int rr_sequential(READ_RECORD *info)
break;
}
}
+ if (!tmp)
+ update_virtual_fields(info->thd, info->table);
return tmp;
}
@@ -408,7 +414,7 @@ static int rr_from_tempfile(READ_RECORD *info)
{
if (my_b_read(info->io_cache,info->ref_pos,info->ref_length))
return -1; /* End of file */
- if (!(tmp=info->file->rnd_pos(info->record,info->ref_pos)))
+ if (!(tmp= info->file->ha_rnd_pos(info->record,info->ref_pos)))
break;
/* The following is extremely unlikely to happen */
if (tmp == HA_ERR_RECORD_DELETED ||
@@ -459,7 +465,7 @@ static int rr_from_pointers(READ_RECORD *info)
cache_pos= info->cache_pos;
info->cache_pos+= info->ref_length;
- if (!(tmp=info->file->rnd_pos(info->record,cache_pos)))
+ if (!(tmp= info->file->ha_rnd_pos(info->record,cache_pos)))
break;
/* The following is extremely unlikely to happen */
@@ -592,7 +598,7 @@ static int rr_from_cache(READ_RECORD *info)
record=uint3korr(position);
position+=3;
record_pos=info->cache+record*info->reclength;
- if ((error=(int16) info->file->rnd_pos(record_pos,info->ref_pos)))
+ if ((error=(int16) info->file->ha_rnd_pos(record_pos,info->ref_pos)))
{
record_pos[info->error_offset]=1;
shortstore(record_pos,error);
diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc
index 585334433df..1ccd3ccc0fe 100644
--- a/sql/rpl_filter.cc
+++ b/sql/rpl_filter.cc
@@ -48,6 +48,7 @@ Rpl_filter::~Rpl_filter()
}
+#ifndef MYSQL_CLIENT
/*
Returns true if table should be logged/replicated
@@ -132,6 +133,7 @@ Rpl_filter::tables_ok(const char* db, TABLE_LIST* tables)
!do_table_inited && !wild_do_table_inited);
}
+#endif
/*
Checks whether a db matches some do_db and ignore_db rules
@@ -523,6 +525,13 @@ Rpl_filter::get_wild_ignore_table(String* str)
}
+bool
+Rpl_filter::rewrite_db_is_empty()
+{
+ return rewrite_db.is_empty();
+}
+
+
const char*
Rpl_filter::get_rewrite_db(const char* db, size_t *new_len)
{
diff --git a/sql/rpl_filter.h b/sql/rpl_filter.h
index ff7e4081bb1..88951600e56 100644
--- a/sql/rpl_filter.h
+++ b/sql/rpl_filter.h
@@ -42,7 +42,9 @@ public:
/* Checks - returns true if ok to replicate/log */
- bool tables_ok(const char* db, TABLE_LIST* tables);
+#ifndef MYSQL_CLIENT
+ bool tables_ok(const char* db, TABLE_LIST *tables);
+#endif
bool db_ok(const char* db);
bool db_ok_with_wild_table(const char *db);
@@ -69,6 +71,7 @@ public:
void get_wild_do_table(String* str);
void get_wild_ignore_table(String* str);
+ bool rewrite_db_is_empty();
const char* get_rewrite_db(const char* db, size_t *new_len);
I_List<i_string>* get_do_db();
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index 47b528001dc..8bb1757d917 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -68,10 +68,10 @@ class Master_info : public Slave_reporting_capability
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
+ 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;
+ 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
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index b56cef3913f..3a9990bb20f 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -172,7 +172,8 @@ a file name for --relay-log-index option", opt_relaylog_index_name);
" 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);
+ "use '--log-basename=#' or '--relay-log=%s' to avoid "
+ "this problem.", ln);
name_warning_sent= 1;
}
/*
diff --git a/sql/scheduler.cc b/sql/scheduler.cc
index 5b8f834aecc..6dd93640dc5 100644
--- a/sql/scheduler.cc
+++ b/sql/scheduler.cc
@@ -376,7 +376,9 @@ void libevent_kill_thd_callback(int Fd, short, void*)
{
THD *thd= (THD*)list->data;
list= list_rest(list);
- if (thd->killed == THD::KILL_CONNECTION)
+ if (thd->killed == THD::KILL_CONNECTION ||
+ thd->killed == THD::KILL_SYSTEM_THREAD ||
+ thd->killed == THD::KILL_SERVER)
{
/*
Delete from libevent and add to the processing queue.
@@ -531,9 +533,10 @@ static void libevent_connection_close(THD *thd)
static bool libevent_should_close_connection(THD* thd)
{
- return thd->net.error ||
- thd->net.vio == 0 ||
- thd->killed == THD::KILL_CONNECTION;
+ return (thd->net.error ||
+ thd->net.vio == 0 ||
+ thd->killed == THD::KILL_CONNECTION ||
+ thd->killed == THD::KILL_SERVER);
}
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 40e26ea4930..b5b19f52c9e 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -61,7 +61,7 @@
#include <my_getopt.h>
#include <thr_alarm.h>
#include <myisam.h>
-#ifdef WITH_MARIA_STORAGE_ENGINE
+#ifdef WITH_ARIA_STORAGE_ENGINE
#include <maria.h>
#endif
#include <my_dir.h>
@@ -326,15 +326,18 @@ static sys_var_thd_ulong sys_interactive_timeout(&vars, "interactive_timeout",
static sys_var_thd_ulong sys_join_buffer_size(&vars, "join_buffer_size",
&SV::join_buff_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));
-static sys_var_key_cache_long sys_key_cache_division_limit(&vars, "key_cache_division_limit",
- offsetof(KEY_CACHE,
- param_division_limit));
-static sys_var_key_cache_long sys_key_cache_age_threshold(&vars, "key_cache_age_threshold",
- offsetof(KEY_CACHE,
- param_age_threshold));
+static sys_var_key_cache_long sys_key_cache_block_size(&vars,
+ "key_cache_block_size",
+ offsetof(KEY_CACHE,param_block_size));
+static sys_var_key_cache_long sys_key_cache_division_limit(&vars,
+ "key_cache_division_limit",
+ offsetof(KEY_CACHE, param_division_limit));
+static sys_var_key_cache_long sys_key_cache_age_threshold(&vars,
+ "key_cache_age_threshold",
+ offsetof(KEY_CACHE, param_age_threshold));
+static sys_var_key_cache_long sys_key_cache_partitions(&vars,
+ "key_cache_segments",
+ offsetof(KEY_CACHE, param_partitions));
static sys_var_const sys_language(&vars, "language",
OPT_GLOBAL, SHOW_CHAR,
(uchar*) language);
@@ -477,6 +480,10 @@ static sys_var_thd_enum sys_myisam_stats_method(&vars, "myisam_stats_met
&myisam_stats_method_typelib,
NULL);
+static sys_var_const sys_myisam_block_size(&vars, "myisam_block_size",
+ OPT_GLOBAL, SHOW_LONG,
+ (uchar*) &myisam_block_size);
+
#ifdef __NT__
/* purecov: begin inspected */
static sys_var_const sys_named_pipe(&vars, "named_pipe",
@@ -531,6 +538,9 @@ static sys_var_const sys_protocol_version(&vars, "protocol_version",
static sys_var_thd_ulong sys_read_buff_size(&vars, "read_buffer_size",
&SV::read_buff_size);
static sys_var_opt_readonly sys_readonly(&vars, "read_only", &opt_readonly);
+static sys_var_bool_ptr sys_userstat(&vars, "userstat",
+ &opt_userstat_running);
+
static sys_var_thd_ulong sys_read_rnd_buff_size(&vars, "read_rnd_buffer_size",
&SV::read_rnd_buff_size);
static sys_var_thd_ulong sys_div_precincrement(&vars, "div_precision_increment",
@@ -600,9 +610,8 @@ static sys_var_thd_ulong sys_trans_prealloc_size(&vars, "transaction_prealloc_si
&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);
+ &thread_handling,
+ &thread_handling_typelib);
#ifdef HAVE_QUERY_CACHE
static sys_var_long_ptr sys_query_cache_limit(&vars, "query_cache_limit",
@@ -902,6 +911,8 @@ static sys_var_thd_set sys_log_slow_verbosity(&vars,
/* Global read-only variable containing hostname */
static sys_var_const_str sys_hostname(&vars, "hostname", glob_hostname);
+static sys_var_const_str_ptr sys_log_basename(&vars, "log_basename",
+ &opt_log_basename);
#ifndef EMBEDDED_LIBRARY
static sys_var_const_str_ptr sys_repl_report_host(&vars, "report_host", &report_host);
@@ -965,6 +976,9 @@ static sys_var_readonly sys_myisam_mmap_size(&vars, "myisam_mmap_size",
SHOW_LONGLONG,
get_myisam_mmap_size);
+static sys_var_enum_const sys_plugin_maturity(&vars, "plugin_maturity",
+ &plugin_maturity,
+ &plugin_maturity_values);
bool sys_var::check(THD *thd, set_var *var)
{
@@ -980,7 +994,7 @@ bool sys_var_str::check(THD *thd, set_var *var)
if ((res=(*check_func)(thd, var)) < 0)
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0),
- name, var->value->str_value.ptr());
+ name, var->value->str_value.c_ptr());
return res;
}
@@ -1227,21 +1241,21 @@ extern void fix_delay_key_write(THD *thd, enum_var_type type)
switch ((enum_delay_key_write) delay_key_write_options) {
case DELAY_KEY_WRITE_NONE:
myisam_delay_key_write=0;
-#ifdef WITH_MARIA_STORAGE_ENGINE
+#ifdef WITH_ARIA_STORAGE_ENGINE
maria_delay_key_write= 0;
#endif
ha_open_options&= ~HA_OPEN_DELAY_KEY_WRITE;
break;
case DELAY_KEY_WRITE_ON:
myisam_delay_key_write=1;
-#ifdef WITH_MARIA_STORAGE_ENGINE
+#ifdef WITH_ARIA_STORAGE_ENGINE
maria_delay_key_write= 1;
#endif
ha_open_options&= ~HA_OPEN_DELAY_KEY_WRITE;
break;
case DELAY_KEY_WRITE_ALL:
myisam_delay_key_write=1;
-#ifdef WITH_MARIA_STORAGE_ENGINE
+#ifdef WITH_ARIA_STORAGE_ENGINE
maria_delay_key_write= 1;
#endif
ha_open_options|= HA_OPEN_DELAY_KEY_WRITE;
@@ -1660,12 +1674,6 @@ 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 (uchar*) enum_names->type_names[global_system_variables.*offset];
-}
-
bool sys_var_thd_ulong::check(THD *thd, set_var *var)
{
if (get_unsigned(thd, var, max_system_variables.*offset, GET_ULONG))
@@ -2249,11 +2257,15 @@ bool sys_var_character_set::check(THD *thd, set_var *var)
}
tmp= NULL;
}
- else if (!(tmp=get_charset_by_csname(res->c_ptr(),MY_CS_PRIMARY,MYF(0))) &&
- !(tmp=get_old_charset_by_name(res->c_ptr())))
+ else
{
- my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), res->c_ptr());
- return 1;
+ const char *name= res->c_ptr_safe();
+ if (!(tmp=get_charset_by_csname(name,MY_CS_PRIMARY,MYF(0))) &&
+ !(tmp=get_old_charset_by_name(name)))
+ {
+ my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), name);
+ return 1;
+ }
}
}
else // INT_RESULT
@@ -2543,7 +2555,15 @@ bool sys_var_key_cache_long::update(THD *thd, set_var *var)
pthread_mutex_unlock(&LOCK_global_system_variables);
- error= (bool) (ha_resize_key_cache(key_cache));
+ if (offset == offsetof(KEY_CACHE, param_block_size))
+ error= (bool) (ha_resize_key_cache(key_cache));
+ else
+ if (offset == offsetof(KEY_CACHE, param_division_limit) ||
+ offset == offsetof(KEY_CACHE, param_age_threshold))
+ error= (bool) (ha_change_key_cache_param(key_cache));
+ else
+ if (offset == offsetof(KEY_CACHE, param_partitions))
+ error= (bool) (ha_repartition_key_cache(key_cache));
pthread_mutex_lock(&LOCK_global_system_variables);
key_cache->in_init= 0;
@@ -2594,7 +2614,7 @@ static int sys_check_log_path(THD *thd, set_var *var)
if (!(res= var->value->val_str(&str)))
goto err;
- log_file_str= res->c_ptr();
+ log_file_str= res->c_ptr_safe();
bzero(&f_stat, sizeof(MY_STAT));
path_length= unpack_filename(path, log_file_str);
@@ -4089,7 +4109,7 @@ bool sys_var_thd_optimizer_switch::check(THD *thd, set_var *var)
optimizer_switch_typelib.count,
thd->variables.optimizer_switch,
global_system_variables.optimizer_switch,
- res->c_ptr_safe(), res->length(), NULL,
+ res->ptr(), res->length(), NULL,
&error, &error_len, &not_used);
if (error_len)
{
@@ -4174,6 +4194,7 @@ static KEY_CACHE *create_key_cache(const char *name, uint length)
key_cache->param_block_size= dflt_key_cache_var.param_block_size;
key_cache->param_division_limit= dflt_key_cache_var.param_division_limit;
key_cache->param_age_threshold= dflt_key_cache_var.param_age_threshold;
+ key_cache->param_partitions= dflt_key_cache_var.param_partitions;
}
}
DBUG_RETURN(key_cache);
diff --git a/sql/set_var.h b/sql/set_var.h
index 2e9812eca3b..95885357b83 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -377,21 +377,14 @@ public:
};
-class sys_var_enum_const :public sys_var
+class sys_var_enum_const :public sys_var_enum
{
- 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; }
+ sys_var_enum_const(sys_var_chain *chain, const char *name_arg,
+ uint *value_arg, TYPELIB *typelib)
+ :sys_var_enum(chain, name_arg, value_arg, typelib, 0)
+ { }
bool is_readonly() const { return 1; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
};
@@ -1432,6 +1425,7 @@ public:
my_free((uchar*) name, MYF(0));
}
friend bool process_key_caches(process_key_cache_t func);
+ friend int fill_key_cache_tables(THD *thd, TABLE_LIST *tables, COND *cond);
friend void delete_elements(I_List<NAMED_LIST> *list,
void (*free_element)(const char*, uchar*));
};
diff --git a/sql/share/CMakeLists.txt b/sql/share/CMakeLists.txt
new file mode 100644
index 00000000000..0c8ddf402d2
--- /dev/null
+++ b/sql/share/CMakeLists.txt
@@ -0,0 +1,54 @@
+# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+SET (dirs
+danish
+german
+slovak
+dutch
+greek
+norwegian
+spanish
+english
+hungarian
+norwegian-ny
+swedish
+italian
+polish
+ukrainian
+japanese
+portuguese
+romanian
+estonian
+korean
+russian
+czech
+french
+serbian
+)
+
+SET(files
+ errmsg.txt
+)
+
+FOREACH (dir ${dirs})
+ INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${dir}
+ DESTINATION share COMPONENT Server)
+ENDFOREACH()
+INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/charsets DESTINATION share COMPONENT Server
+ PATTERN "languages.html" EXCLUDE
+)
+
+INSTALL(FILES ${files} DESTINATION share COMPONENT Server)
diff --git a/sql/share/Makefile.am b/sql/share/Makefile.am
index 4a9123d6d25..892a720900e 100644
--- a/sql/share/Makefile.am
+++ b/sql/share/Makefile.am
@@ -15,7 +15,7 @@
## Process this file with automake to create Makefile.in
-EXTRA_DIST= errmsg.txt
+EXTRA_DIST= errmsg.txt CMakeLists.txt
dist-hook:
for dir in charsets @AVAILABLE_LANGUAGES@; do \
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 001bea9de88..d3d038e9b82 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -6017,7 +6017,7 @@ 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""
+ 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"
@@ -6213,3 +6213,39 @@ ER_DEBUG_SYNC_TIMEOUT
ER_DEBUG_SYNC_HIT_LIMIT
eng "debug sync point hit limit reached"
ger "Debug Sync Point Hit Limit erreicht"
+
+ER_VCOL_BASED_ON_VCOL
+ eng "A computed column cannot be based on a computed column"
+
+ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED
+ eng "Function or expression is not allowed for column '%s'"
+
+ER_DATA_CONVERSION_ERROR_FOR_VIRTUAL_COLUMN
+ eng "Generated value for computed column '%s' cannot be converted to type '%s'"
+
+ER_PRIMARY_KEY_BASED_ON_VIRTUAL_COLUMN
+ eng "Primary key cannot be defined upon a computed column"
+
+ER_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN
+ eng "Key/Index cannot be defined on a non-stored computed column"
+
+ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN
+ eng "Cannot define foreign key with %s clause on a computed column"
+
+ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN
+ eng "The value specified for computed column '%s' in table '%s' ignored"
+
+ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN
+ eng "This is not yet supported for computed columns"
+
+ER_CONST_EXPR_IN_VCOL
+ eng "Constant expression in computed column function is not allowed"
+
+ER_ROW_EXPR_FOR_VCOL
+ eng "Expression for computed column cannot return a row"
+ER_UNSUPPORTED_ENGINE_FOR_VIRTUAL_COLUMNS
+ eng "%s storage engine does not support computed columns"
+ER_UNKNOWN_OPTION
+ eng "Unknown option '%-.64s'"
+ER_BAD_OPTION_VALUE
+ eng "Incorrect value '%-.64s' for option '%-.64s'"
diff --git a/sql/slave.cc b/sql/slave.cc
index d779d8cdc9e..813cf20464e 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -843,6 +843,7 @@ bool is_network_error(uint errorno)
errorno == CR_SERVER_GONE_ERROR ||
errorno == CR_SERVER_LOST ||
errorno == ER_CON_COUNT_ERROR ||
+ errorno == ER_NEW_ABORTING_CONNECTION ||
errorno == ER_SERVER_SHUTDOWN)
return TRUE;
@@ -3986,11 +3987,11 @@ bool flush_relay_log_info(Relay_log_info* rli)
my_b_seek(file, 0L);
pos=strmov(buff, rli->group_relay_log_name);
*pos++='\n';
- pos=longlong2str(rli->group_relay_log_pos, pos, 10);
+ pos= longlong10_to_str(rli->group_relay_log_pos, pos, 10);
*pos++='\n';
pos=strmov(pos, rli->group_master_log_name);
*pos++='\n';
- pos=longlong2str(rli->group_master_log_pos, pos, 10);
+ pos=longlong10_to_str(rli->group_master_log_pos, pos, 10);
*pos='\n';
if (my_b_write(file, (uchar*) buff, (size_t) (pos-buff)+1))
error=1;
diff --git a/sql/sp.cc b/sql/sp.cc
index 6f8c351f637..efa5526fed9 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -16,8 +16,8 @@
*/
#include "mysql_priv.h"
-#include "sp.h"
#include "sp_head.h"
+#include "sp.h"
#include "sp_cache.h"
#include "sql_trigger.h"
@@ -25,7 +25,7 @@
static bool
create_string(THD *thd, String *buf,
- int sp_type,
+ stored_procedure_type sp_type,
const char *db, ulong dblen,
const char *name, ulong namelen,
const char *params, ulong paramslen,
@@ -35,7 +35,8 @@ create_string(THD *thd, String *buf,
const LEX_STRING *definer_user,
const LEX_STRING *definer_host);
static int
-db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
+db_load_routine(THD *thd, stored_procedure_type 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,
@@ -149,7 +150,8 @@ TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] =
{
{ 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',"
+ "'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY',"
+ "'NO_UNSIGNED_SUBTRACTION',"
"'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
"'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40',"
"'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
@@ -491,7 +493,8 @@ static TABLE *open_proc_table_for_update(THD *thd)
*/
static int
-db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table)
+db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name,
+ TABLE *table)
{
uchar key[MAX_KEY_LENGTH]; // db, name, optional key length type
DBUG_ENTER("db_find_routine_aux");
@@ -514,8 +517,9 @@ 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_map(table->record[0], 0, key, HA_WHOLE_KEY,
- HA_READ_KEY_EXACT))
+ if (table->file->ha_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);
@@ -543,7 +547,8 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table)
*/
static int
-db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
+db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
+ sp_head **sphp)
{
TABLE *table;
const char *params, *returns, *body;
@@ -742,7 +747,8 @@ Bad_db_error_handler::handle_error(uint sql_errno, const char *message,
static int
-db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
+db_load_routine(THD *thd, stored_procedure_type 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,
@@ -930,7 +936,7 @@ sp_returns_type(THD *thd, String &result, sp_head *sp)
*/
int
-sp_create_routine(THD *thd, int type, sp_head *sp)
+sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
{
int ret;
TABLE *table;
@@ -946,7 +952,8 @@ sp_create_routine(THD *thd, int type, sp_head *sp)
bool save_binlog_row_based;
DBUG_ENTER("sp_create_routine");
- DBUG_PRINT("enter", ("type: %d name: %.*s",type, (int) sp->m_name.length,
+ DBUG_PRINT("enter", ("type: %d name: %.*s", (int) type,
+ (int) sp->m_name.length,
sp->m_name.str));
String retstr(64);
retstr.set_charset(system_charset_info);
@@ -1144,7 +1151,7 @@ sp_create_routine(THD *thd, int type, sp_head *sp)
(sp->m_explicit_name ? sp->m_db.length : 0),
sp->m_name.str, sp->m_name.length,
sp->m_params.str, sp->m_params.length,
- retstr.c_ptr(), retstr.length(),
+ retstr.ptr(), retstr.length(),
sp->m_body.str, sp->m_body.length,
sp->m_chistics, &(thd->lex->definer->user),
&(thd->lex->definer->host)))
@@ -1156,7 +1163,7 @@ sp_create_routine(THD *thd, int type, sp_head *sp)
thd->variables.sql_mode= saved_mode;
/* Such a statement can always go directly to binlog, no trans cache */
if (thd->binlog_query(THD::MYSQL_QUERY_TYPE,
- log_query.c_ptr(), log_query.length(),
+ log_query.ptr(), log_query.length(),
FALSE, FALSE, 0))
ret= SP_INTERNAL_ERROR;
thd->variables.sql_mode= 0;
@@ -1191,7 +1198,7 @@ done:
*/
int
-sp_drop_routine(THD *thd, int type, sp_name *name)
+sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
{
TABLE *table;
int ret;
@@ -1251,14 +1258,16 @@ sp_drop_routine(THD *thd, int type, sp_name *name)
*/
int
-sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics)
+sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
+ st_sp_chistics *chistics)
{
TABLE *table;
int ret;
bool save_binlog_row_based;
DBUG_ENTER("sp_update_routine");
DBUG_PRINT("enter", ("type: %d name: %.*s",
- type, (int) name->m_name.length, name->m_name.str));
+ (int) type,
+ (int) name->m_name.length, name->m_name.str));
DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
type == TYPE_ENUM_FUNCTION);
@@ -1321,6 +1330,7 @@ sp_drop_db_routines(THD *thd, char *db)
TABLE *table;
int ret;
uint key_len;
+ uchar keybuf[MAX_KEY_LENGTH];
DBUG_ENTER("sp_drop_db_routines");
DBUG_PRINT("enter", ("db: %s", db));
@@ -1330,12 +1340,14 @@ sp_drop_db_routines(THD *thd, char *db)
table->field[MYSQL_PROC_FIELD_DB]->store(db, strlen(db), system_charset_info);
key_len= table->key_info->key_part[0].store_length;
+ table->field[MYSQL_PROC_FIELD_DB]->get_key_image(keybuf, key_len, Field::itRAW);
+
+
ret= SP_OK;
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))
+ if (!table->file->ha_index_read_map(table->record[0], keybuf, (key_part_map)1,
+ HA_READ_KEY_EXACT))
{
int nxtres;
bool deleted= FALSE;
@@ -1350,9 +1362,8 @@ sp_drop_db_routines(THD *thd, char *db)
nxtres= 0;
break;
}
- } while (! (nxtres= table->file->index_next_same(table->record[0],
- (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr,
- key_len)));
+ } while (!(nxtres= table->file->ha_index_next_same(table->record[0],
+ keybuf, key_len)));
if (nxtres != HA_ERR_END_OF_FILE)
ret= SP_KEY_NOT_FOUND;
if (deleted)
@@ -1384,7 +1395,7 @@ err:
*/
bool
-sp_show_create_routine(THD *thd, int type, sp_name *name)
+sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name)
{
bool err_status= TRUE;
sp_head *sp;
@@ -1442,8 +1453,8 @@ sp_show_create_routine(THD *thd, int type, sp_name *name)
*/
sp_head *
-sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp,
- bool cache_only)
+sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
+ sp_cache **cp, bool cache_only)
{
sp_head *sp;
ulong depth= (type == TYPE_ENUM_PROCEDURE ?
@@ -1600,7 +1611,7 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any)
*/
int
-sp_routine_exists_in_table(THD *thd, int type, sp_name *name)
+sp_routine_exists_in_table(THD *thd, stored_procedure_type type, sp_name *name)
{
TABLE *table;
int ret;
@@ -1767,7 +1778,7 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
*/
void sp_add_used_routine(LEX *lex, Query_arena *arena,
- sp_name *rt, char rt_type)
+ sp_name *rt, enum stored_procedure_type rt_type)
{
rt->set_routine_type(rt_type);
(void)add_used_routine(lex, arena, &rt->m_sroutines_key, 0);
@@ -1923,9 +1934,11 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
for (Sroutine_hash_entry *rt= start; rt; rt= rt->next)
{
sp_name name(thd, rt->key.str, rt->key.length);
- int type= rt->key.str[0];
+ stored_procedure_type type= (stored_procedure_type) rt->key.str[0];
sp_head *sp;
+ if (type == TYPE_ENUM_TRIGGER)
+ continue;
if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
&thd->sp_func_cache : &thd->sp_proc_cache),
&name)))
@@ -2114,7 +2127,7 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
*/
static bool
create_string(THD *thd, String *buf,
- int type,
+ stored_procedure_type type,
const char *db, ulong dblen,
const char *name, ulong namelen,
const char *params, ulong paramslen,
diff --git a/sql/sp.h b/sql/sp.h
index d7278913071..8a644aabcf4 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -42,26 +42,28 @@ int
sp_drop_db_routines(THD *thd, char *db);
sp_head *
-sp_find_routine(THD *thd, int type, sp_name *name,
+sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
sp_cache **cp, bool cache_only);
bool
sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any);
int
-sp_routine_exists_in_table(THD *thd, int type, sp_name *name);
+sp_routine_exists_in_table(THD *thd, stored_procedure_type type,
+ sp_name *name);
bool
-sp_show_create_routine(THD *thd, int type, sp_name *name);
+sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name);
int
-sp_create_routine(THD *thd, int type, sp_head *sp);
+sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp);
int
-sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics);
+sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
+ st_sp_chistics *chistics);
int
-sp_drop_routine(THD *thd, int type, sp_name *name);
+sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name);
/*
Procedures for pre-caching of stored routines and building table list
@@ -70,7 +72,7 @@ sp_drop_routine(THD *thd, int type, sp_name *name);
void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
bool *first_no_prelocking);
void sp_add_used_routine(LEX *lex, Query_arena *arena,
- sp_name *rt, char rt_type);
+ sp_name *rt, stored_procedure_type rt_type);
void sp_remove_not_own_routines(LEX *lex);
bool sp_update_sp_used_routines(HASH *dst, HASH *src);
int sp_cache_routines_and_add_tables(THD *thd, LEX *lex,
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 67ec830d902..f1f453bada5 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -388,8 +388,8 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
thd->abort_on_warning=
- thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES);
+ test(thd->variables.sql_mode &
+ (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES));
thd->transaction.stmt.modified_non_trans_table= FALSE;
/* Save the value in the field. Convert the value if needed. */
@@ -843,6 +843,8 @@ sp_head::create_result_field(uint field_max_length, const char *field_name,
m_return_field_def.interval,
field_name ? field_name : (const char *) m_name.str);
+ field->vcol_info= m_return_field_def.vcol_info;
+ field->stored_in_db= m_return_field_def.stored_in_db;
if (field)
field->init(table);
@@ -1384,7 +1386,10 @@ sp_head::execute(THD *thd)
If the DB has changed, the pointer has changed too, but the
original thd->db will then have been freed
*/
- if (cur_db_changed && thd->killed != THD::KILL_CONNECTION)
+ if (cur_db_changed &&
+ thd->killed != THD::KILL_CONNECTION &&
+ thd->killed != THD::KILL_SERVER &&
+ thd->killed != THD::KILL_SYSTEM_THREAD)
{
/*
Force switching back to the saved current database, because it may be
@@ -2123,6 +2128,8 @@ sp_head::reset_lex(THD *thd)
sublex->dec= NULL;
sublex->interval_list.empty();
sublex->type= 0;
+ sublex->uint_geom_type= 0;
+ sublex->vcol_info= 0;
DBUG_RETURN(FALSE);
}
@@ -2249,7 +2256,8 @@ sp_head::fill_field_definition(THD *thd, LEX *lex,
&lex->interval_list,
lex->charset ? lex->charset :
thd->variables.collation_database,
- lex->uint_geom_type))
+ lex->uint_geom_type,
+ lex->vcol_info, NULL))
return TRUE;
if (field_def->interval_list.elements)
diff --git a/sql/sp_head.h b/sql/sp_head.h
index cdf1786f2d5..29fa2ff1de2 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -30,11 +30,16 @@
@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
-#define TYPE_ENUM_PROCEDURE 2
-#define TYPE_ENUM_TRIGGER 3
+/*
+ Values for the type enum. This reflects the order of the enum declaration
+ in the CREATE TABLE command.
+*/
+enum stored_procedure_type
+{
+ TYPE_ENUM_FUNCTION=1,
+ TYPE_ENUM_PROCEDURE=2,
+ TYPE_ENUM_TRIGGER=3
+};
Item_result
sp_map_result_type(enum enum_field_types type);
@@ -136,9 +141,9 @@ public:
// Init. the qualified name from the db and name.
void init_qname(THD *thd); // thd for memroot allocation
- void set_routine_type(char type)
+ void set_routine_type(stored_procedure_type type)
{
- m_sroutines_key.str[0]= type;
+ m_sroutines_key.str[0]= (char) type;
}
~sp_name()
@@ -172,8 +177,7 @@ public:
HAS_SQLCOM_FLUSH= 4096
};
- /** TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */
- int m_type;
+ stored_procedure_type m_type;
uint m_flags; // Boolean attributes of a stored routine
Create_field m_return_field_def; /**< This is used for FUNCTIONs only. */
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index ee33b6f5325..2558055eda3 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -121,7 +121,7 @@ sp_rcontext::init_var_table(THD *thd)
return TRUE;
m_var_table->copy_blobs= TRUE;
- m_var_table->alias= "";
+ m_var_table->alias.set("", 0, table_alias_charset);
return FALSE;
}
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index 8da0a4bee82..15815e496dd 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -281,7 +281,7 @@ public:
int
close(THD *thd);
- inline my_bool
+ inline bool
is_open()
{
return test(server_side_cursor);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 89b70032642..0953af75b9c 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -31,6 +31,8 @@
#include <stdarg.h>
#include "sp_head.h"
#include "sp.h"
+#include <sql_common.h>
+#include <mysql/plugin_auth.h>
static const
TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
@@ -149,6 +151,87 @@ TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
const TABLE_FIELD_DEF
mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields};
+static LEX_STRING native_password_plugin_name= {
+ C_STRING_WITH_LEN("mysql_native_password")
+};
+
+static LEX_STRING old_password_plugin_name= {
+ C_STRING_WITH_LEN("mysql_old_password")
+};
+
+/// @todo make it configurable
+LEX_STRING *default_auth_plugin_name= &native_password_plugin_name;
+
+static plugin_ref native_password_plugin;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+static plugin_ref old_password_plugin;
+#endif
+
+/* Classes */
+
+struct acl_host_and_ip
+{
+ char *hostname;
+ long ip,ip_mask; // Used with masked ip:s
+};
+
+class ACL_ACCESS {
+public:
+ ulong sort;
+ ulong access;
+};
+
+/* ACL_HOST is used if no host is specified */
+
+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[SCRAMBLE_LENGTH+1]; // scrambled password in binary form
+ uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1
+ enum SSL_type ssl_type;
+ const char *ssl_cipher, *x509_issuer, *x509_subject;
+ LEX_STRING plugin;
+ LEX_STRING auth_string;
+
+ ACL_USER *copy(MEM_ROOT *root)
+ {
+ ACL_USER *dst= (ACL_USER *)alloc_root(root, sizeof(ACL_USER));
+ if (!dst)
+ return 0;
+ *dst= *this;
+ dst->user= safe_strdup_root(root, user);
+ dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
+ dst->x509_issuer= safe_strdup_root(root, x509_issuer);
+ dst->x509_subject= safe_strdup_root(root, x509_subject);
+ if (plugin.str == native_password_plugin_name.str ||
+ plugin.str == old_password_plugin_name.str)
+ dst->plugin= plugin;
+ else
+ dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
+ dst->auth_string.str = safe_strdup_root(root, auth_string.str);
+ dst->host.hostname= safe_strdup_root(root, host.hostname);
+ return dst;
+ }
+};
+
+class ACL_DB :public ACL_ACCESS
+{
+public:
+ acl_host_and_ip host;
+ char *user,*db;
+};
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
#define FIRST_NON_YN_FIELD 26
@@ -172,6 +255,24 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
#define IP_ADDR_STRLEN (3+1+3+1+3+1+3)
#define ACL_KEY_LENGTH (IP_ADDR_STRLEN+1+NAME_LEN+1+USERNAME_LENGTH+1)
+#if defined(HAVE_OPENSSL)
+/*
+ Without SSL the handshake consists of one packet. This packet
+ has both client capabilites and scrambled password.
+ 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 && !EMBEDDED_LIBRARY */
+
static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
static MEM_ROOT mem, memex;
static bool initialized=0;
@@ -187,9 +288,8 @@ static void init_check_host(void);
static void rebuild_check_host(void);
static ACL_USER *find_acl_user(const char *host, const char *user,
my_bool exact);
-static bool update_user_table(THD *thd, TABLE *table,
- const char *host, const char *user,
- const char *new_password, uint new_password_len);
+static bool update_user_table(THD *, TABLE *, const char *, const char *,
+ const char *, uint);
static void update_hostname(acl_host_and_ip *host, const char *hostname);
static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
const char *ip);
@@ -220,6 +320,48 @@ set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
acl_user->salt_len= 0;
}
+static char *fix_plugin_ptr(char *name)
+{
+ if (my_strcasecmp(system_charset_info, name,
+ native_password_plugin_name.str) == 0)
+ return native_password_plugin_name.str;
+ else
+ if (my_strcasecmp(system_charset_info, name,
+ old_password_plugin_name.str) == 0)
+ return old_password_plugin_name.str;
+ else
+ return name;
+}
+
+/**
+ Fix ACL::plugin pointer to point to a hard-coded string, if appropriate
+
+ Make sure that if ACL_USER's plugin is a built-in, then it points
+ to a hard coded string, not to an allocated copy. Run-time, for
+ authentication, we want to be able to detect built-ins by comparing
+ pointers, not strings.
+
+ Additionally - update the salt if the plugin is built-in.
+
+ @retval 0 the pointers were fixed
+ @retval 1 this ACL_USER uses a not built-in plugin
+*/
+static bool fix_user_plugin_ptr(ACL_USER *user)
+{
+ if (my_strcasecmp(system_charset_info, user->plugin.str,
+ native_password_plugin_name.str) == 0)
+ user->plugin= native_password_plugin_name;
+ else
+ if (my_strcasecmp(system_charset_info, user->plugin.str,
+ old_password_plugin_name.str) == 0)
+ user->plugin= old_password_plugin_name;
+ else
+ return true;
+
+ set_user_salt(user, user->auth_string.str, user->auth_string.length);
+ return false;
+}
+
/*
This after_update function is used when user.password is less than
SCRAMBLE_LENGTH bytes.
@@ -267,6 +409,19 @@ my_bool acl_init(bool dont_read_acl_tables)
(hash_get_key) acl_entry_get_key,
(hash_free_key) free,
&my_charset_utf8_bin);
+
+ /*
+ cache built-in native authentication plugins,
+ to avoid hash searches and a global mutex lock on every connect
+ */
+ native_password_plugin= my_plugin_lock_by_name(0,
+ &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
+ old_password_plugin= my_plugin_lock_by_name(0,
+ &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
+
+ if (!native_password_plugin || !old_password_plugin)
+ DBUG_RETURN(1);
+
if (dont_read_acl_tables)
{
DBUG_RETURN(0); /* purecov: tested */
@@ -326,8 +481,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
acl_cache->clear(1); // Clear locked hostname cache
init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
- init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0,
- FALSE);
+ if (init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0,
+ FALSE))
+ goto end;
+
table->use_all_columns();
VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50));
while (!(read_record_info.read_record(&read_record_info)))
@@ -376,7 +533,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
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);
+ if (init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0,
+ FALSE))
+ goto end;
+
table->use_all_columns();
VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
password_length= table->field[2]->field_length /
@@ -424,6 +584,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_USER user;
+ char *password;
+ uint password_len;
+
+ bzero(&user, sizeof(user));
update_hostname(&user.host, get_field(&mem, table->field[0]));
user.user= get_field(&mem, table->field[1]);
if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
@@ -435,27 +599,34 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
continue;
}
- const char *password= get_field(thd->mem_root, table->field[2]);
- uint password_len= password ? strlen(password) : 0;
+ password= get_field(&mem, table->field[2]);
+ password_len= password ? strlen(password) : 0;
+ user.auth_string.str= password ? password : const_cast<char*>("");
+ user.auth_string.length= password_len;
set_user_salt(&user, password, password_len);
- if (user.salt_len == 0 && password_len != 0)
- {
- switch (password_len) {
- case 45: /* 4.1: to be removed */
- sql_print_warning("Found 4.1 style password for user '%s@%s'. "
- "Ignoring user. "
- "You should change password for this user.",
- user.user ? user.user : "",
- user.host.hostname ? user.host.hostname : "");
- break;
- default:
- sql_print_warning("Found invalid password for user: '%s@%s'; "
- "Ignoring user", user.user ? user.user : "",
- user.host.hostname ? user.host.hostname : "");
- break;
- }
+
+ switch (password_len) {
+ case 0: /* no password */
+ case SCRAMBLED_PASSWORD_CHAR_LENGTH:
+ user.plugin= native_password_plugin_name;
+ break;
+ case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
+ user.plugin= old_password_plugin_name;
+ break;
+ case 45: /* 4.1: to be removed */
+ sql_print_warning("Found 4.1.0 style password for user '%s@%s'. "
+ "Ignoring user. "
+ "You should change password for this user.",
+ user.user ? user.user : "",
+ user.host.hostname ? user.host.hostname : "");
+ continue;
+ default:
+ sql_print_warning("Found invalid password for user: '%s@%s'; "
+ "Ignoring user", user.user ? user.user : "",
+ user.host.hostname ? user.host.hostname : "");
+ continue;
}
- else // password is correct
+
{
uint next_field;
user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
@@ -532,13 +703,35 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
ptr= get_field(thd->mem_root, table->field[next_field++]);
user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
}
- else
- user.user_resource.user_conn= 0;
+
+ if (table->s->fields >= 41)
+ {
+ /* We may have plugin & auth_String fields */
+ char *tmpstr= get_field(&mem, table->field[next_field++]);
+ if (tmpstr)
+ {
+ user.plugin.str= tmpstr;
+ user.plugin.length= strlen(user.plugin.str);
+ if (user.auth_string.length)
+ {
+ sql_print_warning("'user' entry '%s@%s' has both a password "
+ "and an authentication plugin specified. The "
+ "password will be ignored.",
+ user.user ? user.user : "",
+ user.host.hostname ? user.host.hostname : "");
+ }
+ user.auth_string.str= get_field(&mem, table->field[next_field++]);
+ if (!user.auth_string.str)
+ user.auth_string.str= const_cast<char*>("");
+ user.auth_string.length= strlen(user.auth_string.str);
+
+ fix_user_plugin_ptr(&user);
+ }
+ }
}
else
{
user.ssl_type=SSL_TYPE_NONE;
- bzero((char *)&(user.user_resource),sizeof(user.user_resource));
#ifndef TO_BE_REMOVED
if (table->s->fields <= 13)
{ // Without grant
@@ -564,7 +757,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
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);
+ if (init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0,
+ FALSE))
+ goto end;
+
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)))
@@ -641,6 +837,8 @@ void acl_free(bool end)
delete_dynamic(&acl_dbs);
delete_dynamic(&acl_wild_hosts);
hash_free(&acl_check_hosts);
+ plugin_unlock(0, native_password_plugin);
+ plugin_unlock(0, old_password_plugin);
if (!end)
acl_cache->clear(1); /* purecov: inspected */
else
@@ -843,246 +1041,10 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
/*
- Seek ACL entry for a user, check password, SSL cypher, and if
- everything is OK, update THD user data and USER_RESOURCES struct.
-
- IMPLEMENTATION
- This function does not check if the user has any sensible privileges:
- only user's existence and validity is checked.
- Note, that entire operation is protected by acl_cache_lock.
+ Gets user credentials without authentication and resource limit checks.
SYNOPSIS
acl_getroot()
- thd thread handle. If all checks are OK,
- thd->security_ctx->priv_user/master_access are updated.
- thd->security_ctx->host/ip/user are used for checks.
- mqh user resources; on success mqh is reset, else
- unchanged
- passwd scrambled & crypted password, received from client
- (to check): thd->scramble or thd->scramble_323 is
- used to decrypt passwd, so they must contain
- original random string,
- passwd_len length of passwd, must be one of 0, 8,
- SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH
- 'thd' and 'mqh' are updated on success; other params are IN.
-
- RETURN VALUE
- 0 success: thd->priv_user, thd->priv_host, thd->master_access, mqh are
- updated
- 1 user not found or authentication failure
- 2 user found, has long (4.1.1) salt, but passwd is in old (3.23) format.
- -1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
-*/
-
-int acl_getroot(THD *thd, USER_RESOURCES *mqh,
- const char *passwd, uint passwd_len)
-{
- ulong user_access= NO_ACCESS;
- int res= 1;
- ACL_USER *acl_user= 0;
- Security_context *sctx= thd->security_ctx;
- DBUG_ENTER("acl_getroot");
-
- if (!initialized)
- {
- /*
- here if mysqld's been started with --skip-grant-tables option.
- */
- sctx->skip_grants();
- bzero((char*) mqh, sizeof(*mqh));
- DBUG_RETURN(0);
- }
-
- VOID(pthread_mutex_lock(&acl_cache->lock));
-
- /*
- Find acl entry in user database. Note, that find_acl_user is not the same,
- because it doesn't take into account the case when user is not empty,
- but acl_user->user is empty
- */
-
- for (uint i=0 ; i < acl_users.elements ; i++)
- {
- ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
- if (!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user))
- {
- if (compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))
- {
- /* check password: it should be empty or valid */
- if (passwd_len == acl_user_tmp->salt_len)
- {
- if (acl_user_tmp->salt_len == 0 ||
- (acl_user_tmp->salt_len == SCRAMBLE_LENGTH ?
- check_scramble(passwd, thd->scramble, acl_user_tmp->salt) :
- check_scramble_323(passwd, thd->scramble,
- (ulong *) acl_user_tmp->salt)) == 0)
- {
- acl_user= acl_user_tmp;
- res= 0;
- }
- }
- else if (passwd_len == SCRAMBLE_LENGTH &&
- acl_user_tmp->salt_len == SCRAMBLE_LENGTH_323)
- res= -1;
- else if (passwd_len == SCRAMBLE_LENGTH_323 &&
- acl_user_tmp->salt_len == SCRAMBLE_LENGTH)
- res= 2;
- /* linear search complete: */
- break;
- }
- }
- }
- /*
- This was moved to separate tree because of heavy HAVE_OPENSSL case.
- If acl_user is not null, res is 0.
- */
-
- if (acl_user)
- {
- /* OK. User found and password checked continue validation */
-#ifdef HAVE_OPENSSL
- Vio *vio=thd->net.vio;
- SSL *ssl= (SSL*) vio->ssl_arg;
- X509 *cert;
-#endif
-
- /*
- At this point we know that user is allowed to connect
- from given host by given username/password pair. Now
- we check if SSL is required, if user is using SSL and
- if X509 certificate attributes are OK
- */
- switch (acl_user->ssl_type) {
- case SSL_TYPE_NOT_SPECIFIED: // Impossible
- case SSL_TYPE_NONE: // SSL is not required
- user_access= acl_user->access;
- break;
-#ifdef HAVE_OPENSSL
- case SSL_TYPE_ANY: // Any kind of SSL is ok
- if (vio_type(vio) == VIO_TYPE_SSL)
- user_access= acl_user->access;
- break;
- case SSL_TYPE_X509: /* Client should have any valid certificate. */
- /*
- Connections with non-valid certificates are dropped already
- in sslaccept() anyway, so we do not check validity here.
-
- We need to check for absence of SSL because without SSL
- we should reject connection.
- */
- if (vio_type(vio) == VIO_TYPE_SSL &&
- SSL_get_verify_result(ssl) == X509_V_OK &&
- (cert= SSL_get_peer_certificate(ssl)))
- {
- user_access= acl_user->access;
- X509_free(cert);
- }
- break;
- case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
- /*
- We do not check for absence of SSL because without SSL it does
- not pass all checks here anyway.
- If cipher name is specified, we compare it to actual cipher in
- use.
- */
- if (vio_type(vio) != VIO_TYPE_SSL ||
- SSL_get_verify_result(ssl) != X509_V_OK)
- break;
- if (acl_user->ssl_cipher)
- {
- DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
- acl_user->ssl_cipher,SSL_get_cipher(ssl)));
- if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl)))
- user_access= acl_user->access;
- else
- {
- if (global_system_variables.log_warnings)
- sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
- acl_user->ssl_cipher,
- SSL_get_cipher(ssl));
- break;
- }
- }
- /* Prepare certificate (if exists) */
- DBUG_PRINT("info",("checkpoint 1"));
- if (!(cert= SSL_get_peer_certificate(ssl)))
- {
- user_access=NO_ACCESS;
- break;
- }
- DBUG_PRINT("info",("checkpoint 2"));
- /* If X509 issuer is specified, we check it... */
- if (acl_user->x509_issuer)
- {
- DBUG_PRINT("info",("checkpoint 3"));
- char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
- DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
- acl_user->x509_issuer, ptr));
- if (strcmp(acl_user->x509_issuer, ptr))
- {
- if (global_system_variables.log_warnings)
- sql_print_information("X509 issuer mismatch: should be '%s' "
- "but is '%s'", acl_user->x509_issuer, ptr);
- free(ptr);
- X509_free(cert);
- user_access=NO_ACCESS;
- break;
- }
- user_access= acl_user->access;
- free(ptr);
- }
- DBUG_PRINT("info",("checkpoint 4"));
- /* X509 subject is specified, we check it .. */
- if (acl_user->x509_subject)
- {
- char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
- DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
- acl_user->x509_subject, ptr));
- if (strcmp(acl_user->x509_subject,ptr))
- {
- if (global_system_variables.log_warnings)
- sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
- acl_user->x509_subject, ptr);
- free(ptr);
- X509_free(cert);
- user_access=NO_ACCESS;
- break;
- }
- user_access= acl_user->access;
- free(ptr);
- }
- /* Deallocate the X509 certificate. */
- X509_free(cert);
- break;
-#else /* HAVE_OPENSSL */
- default:
- /*
- If we don't have SSL but SSL is required for this user the
- authentication should fail.
- */
- break;
-#endif /* HAVE_OPENSSL */
- }
- sctx->master_access= user_access;
- sctx->priv_user= acl_user->user ? sctx->user : (char *) "";
- *mqh= acl_user->user_resource;
-
- if (acl_user->host.hostname)
- strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
- else
- *sctx->priv_host= 0;
- }
- VOID(pthread_mutex_unlock(&acl_cache->lock));
- DBUG_RETURN(res);
-}
-
-
-/*
- This is like acl_getroot() above, but it doesn't check password,
- and we don't care about the user resources.
-
- SYNOPSIS
- acl_getroot_no_password()
sctx Context which should be initialized
user user name
host host name
@@ -1094,13 +1056,13 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh,
TRUE Error
*/
-bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
- char *ip, char *db)
+bool acl_getroot(Security_context *sctx, char *user, char *host,
+ char *ip, char *db)
{
int res= 1;
uint i;
ACL_USER *acl_user= 0;
- DBUG_ENTER("acl_getroot_no_password");
+ DBUG_ENTER("acl_getroot");
DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
(host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
@@ -1123,8 +1085,7 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
sctx->master_access= 0;
sctx->db_access= 0;
- sctx->priv_user= (char *) "";
- *sctx->priv_host= 0;
+ *sctx->priv_user= *sctx->priv_host= 0;
/*
Find acl entry in user database.
@@ -1166,7 +1127,11 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
}
}
sctx->master_access= acl_user->access;
- sctx->priv_user= acl_user->user ? user : (char *) "";
+
+ if (acl_user->user)
+ strmake(sctx->priv_user, user, USERNAME_LENGTH);
+ else
+ *sctx->priv_user= 0;
if (acl_user->host.hostname)
strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
@@ -1192,7 +1157,9 @@ static void acl_update_user(const char *user, const char *host,
const char *x509_issuer,
const char *x509_subject,
USER_RESOURCES *mqh,
- ulong privileges)
+ ulong privileges,
+ const LEX_STRING *plugin,
+ const LEX_STRING *auth)
{
safe_mutex_assert_owner(&acl_cache->lock);
@@ -1206,6 +1173,22 @@ static void acl_update_user(const char *user, const char *host,
(acl_user->host.hostname &&
!my_strcasecmp(system_charset_info, host, acl_user->host.hostname)))
{
+ if (plugin->str[0])
+ {
+ acl_user->plugin= *plugin;
+ acl_user->auth_string.str= auth->str ?
+ strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
+ acl_user->auth_string.length= auth->length;
+ if (fix_user_plugin_ptr(acl_user))
+ acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length);
+ }
+ else
+ if (password)
+ {
+ acl_user->auth_string.str= strmake_root(&mem, password, password_len);
+ acl_user->auth_string.length= password_len;
+ set_user_salt(acl_user, password, password_len);
+ }
acl_user->access=privileges;
if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
acl_user->user_resource.questions=mqh->questions;
@@ -1225,8 +1208,6 @@ static void acl_update_user(const char *user, const char *host,
acl_user->x509_subject= (x509_subject ?
strdup_root(&mem,x509_subject) : 0);
}
- if (password)
- set_user_salt(acl_user, password, password_len);
/* search complete: */
break;
}
@@ -1242,7 +1223,9 @@ static void acl_insert_user(const char *user, const char *host,
const char *x509_issuer,
const char *x509_subject,
USER_RESOURCES *mqh,
- ulong privileges)
+ ulong privileges,
+ const LEX_STRING *plugin,
+ const LEX_STRING *auth)
{
ACL_USER acl_user;
@@ -1250,6 +1233,24 @@ static void acl_insert_user(const char *user, const char *host,
acl_user.user=*user ? strdup_root(&mem,user) : 0;
update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
+ if (plugin->str[0])
+ {
+ acl_user.plugin= *plugin;
+ acl_user.auth_string.str= auth->str ?
+ strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
+ acl_user.auth_string.length= auth->length;
+ if (fix_user_plugin_ptr(&acl_user))
+ acl_user.plugin.str= strmake_root(&mem, plugin->str, plugin->length);
+ }
+ else
+ {
+ acl_user.plugin= password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 ?
+ old_password_plugin_name : native_password_plugin_name;
+ acl_user.auth_string.str= strmake_root(&mem, password, password_len);
+ acl_user.auth_string.length= password_len;
+ set_user_salt(&acl_user, password, password_len);
+ }
+
acl_user.access=privileges;
acl_user.user_resource = *mqh;
acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
@@ -1260,8 +1261,6 @@ static void acl_insert_user(const char *user, const char *host,
acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0;
acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0;
- set_user_salt(&acl_user, password, password_len);
-
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]))
@@ -1648,7 +1647,13 @@ bool change_password(THD *thd, const char *host, const char *user,
goto end;
}
/* update loaded acl entry: */
- set_user_salt(acl_user, new_password, new_password_len);
+ if (acl_user->plugin.str == native_password_plugin_name.str ||
+ acl_user->plugin.str == old_password_plugin_name.str)
+ {
+ acl_user->auth_string.str= strmake_root(&mem, new_password, new_password_len);
+ acl_user->auth_string.length= new_password_len;
+ set_user_salt(acl_user, new_password, new_password_len);
+ }
if (update_user_table(thd, table,
acl_user->host.hostname ? acl_user->host.hostname : "",
@@ -1856,9 +1861,9 @@ static bool update_user_table(THD *thd, TABLE *table,
key_copy((uchar *) user_key, table->record[0], table->key_info,
table->key_info->key_length);
- if (table->file->index_read_idx_map(table->record[0], 0,
- (uchar *) user_key, HA_WHOLE_KEY,
- HA_READ_KEY_EXACT))
+ if (table->file->ha_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 */
@@ -1949,9 +1954,9 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
key_copy(user_key, table->record[0], table->key_info,
table->key_info->key_length);
- if (table->file->index_read_idx_map(table->record[0], 0, user_key,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT))
+ if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
{
/* what == 'N' means revoke */
if (what == 'N')
@@ -1982,6 +1987,15 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
goto end;
}
+ else if (combo.plugin.str[0])
+ {
+ if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN))
+ {
+ my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str);
+ goto end;
+ }
+ }
+
old_row_exists = 0;
restore_record(table,s->default_values);
table->field[0]->store(combo.host.str,combo.host.length,
@@ -1995,7 +2009,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
{
old_row_exists = 1;
store_record(table,record[1]); // Save copy for update
- if (combo.password.str) // If password given
+ if (combo.password.str) // If password given
table->field[2]->store(password, password_len, system_charset_info);
else if (!rights && !revoke_grant &&
lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
@@ -2076,7 +2090,17 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
(mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
table->field[next_field+3]->store((longlong) mqh.user_conn, TRUE);
mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
+
+ next_field+=4;
+ if (table->s->fields >= 41 && combo.plugin.str[0])
+ {
+ table->field[next_field]->store(combo.plugin.str, combo.plugin.length,
+ system_charset_info);
+ table->field[next_field+1]->store(combo.auth.str, combo.auth.length,
+ system_charset_info);
+ }
}
+
if (old_row_exists)
{
/*
@@ -2120,7 +2144,9 @@ end:
lex->x509_issuer,
lex->x509_subject,
&lex->mqh,
- rights);
+ rights,
+ &combo.plugin,
+ &combo.auth);
else
acl_insert_user(combo.user.str, combo.host.str, password, password_len,
lex->ssl_type,
@@ -2128,7 +2154,9 @@ end:
lex->x509_issuer,
lex->x509_subject,
&lex->mqh,
- rights);
+ rights,
+ &combo.plugin,
+ &combo.auth);
}
DBUG_RETURN(error);
}
@@ -2172,9 +2200,9 @@ static int replace_db_table(TABLE *table, const char *db,
key_copy(user_key, table->record[0], table->key_info,
table->key_info->key_length);
- if (table->file->index_read_idx_map(table->record[0],0, user_key,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT))
+ if (table->file->ha_index_read_idx_map(table->record[0],0, user_key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
{
if (what == 'N')
{ // no row, no revoke
@@ -2406,8 +2434,9 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
col_privs->field[4]->store("",0, &my_charset_latin1);
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))
+ if (col_privs->file->ha_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();
@@ -2433,7 +2462,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
privs= cols= 0;
return;
}
- } while (!col_privs->file->index_next(col_privs->record[0]) &&
+ } while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
!key_cmp_if_same(col_privs,key,0,key_prefix_len));
col_privs->file->ha_index_end();
}
@@ -2577,8 +2606,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);
- if (table->file->index_read_map(table->record[0], user_key, HA_WHOLE_KEY,
- HA_READ_KEY_EXACT))
+ if (table->file->ha_index_read_map(table->record[0], user_key,
+ HA_WHOLE_KEY, HA_READ_KEY_EXACT))
{
if (revoke_grant)
{
@@ -2659,9 +2688,9 @@ static int replace_column_table(GRANT_TABLE *g_t,
key_copy(user_key, table->record[0], table->key_info,
key_prefix_length);
- if (table->file->index_read_map(table->record[0], user_key,
- (key_part_map)15,
- HA_READ_KEY_EXACT))
+ if (table->file->ha_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 */
@@ -2712,7 +2741,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
hash_delete(&g_t->hash_columns,(uchar*) grant_column);
}
}
- } while (!table->file->index_next(table->record[0]) &&
+ } while (!table->file->ha_index_next(table->record[0]) &&
!key_cmp_if_same(table, key, 0, key_prefix_length));
}
@@ -2774,9 +2803,9 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
key_copy(user_key, table->record[0], table->key_info,
table->key_info->key_length);
- if (table->file->index_read_idx_map(table->record[0], 0, user_key,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT))
+ if (table->file->ha_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
@@ -2899,10 +2928,10 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
TRUE);
store_record(table,record[1]); // store at pos 1
- if (table->file->index_read_idx_map(table->record[0], 0,
- (uchar*) table->field[0]->ptr,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT))
+ if (table->file->ha_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
@@ -3646,7 +3675,7 @@ static my_bool grant_load_procs_priv(TABLE *p_table)
p_table->file->ha_index_init(0, 1);
p_table->use_all_columns();
- if (!p_table->file->index_first(p_table->record[0]))
+ if (!p_table->file->ha_index_first(p_table->record[0]))
{
memex_ptr= &memex;
my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
@@ -3698,7 +3727,7 @@ static my_bool grant_load_procs_priv(TABLE *p_table)
goto end_unlock;
}
}
- while (!p_table->file->index_next(p_table->record[0]));
+ while (!p_table->file->ha_index_next(p_table->record[0]));
}
/* Return ok */
return_val= 0;
@@ -3748,7 +3777,7 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
t_table->use_all_columns();
c_table->use_all_columns();
- if (!t_table->file->index_first(t_table->record[0]))
+ if (!t_table->file->ha_index_first(t_table->record[0]))
{
memex_ptr= &memex;
my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
@@ -3783,7 +3812,7 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
goto end_unlock;
}
}
- while (!t_table->file->index_next(t_table->record[0]));
+ while (!t_table->file->ha_index_next(t_table->record[0]));
}
return_val=0; // Return ok
@@ -4055,6 +4084,8 @@ err:
{
char command[128];
get_privilege_desc(command, sizeof(command), want_access);
+ status_var_increment(thd->status_var.access_denied_errors);
+
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
command,
sctx->priv_user,
@@ -4468,16 +4499,14 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
ulong get_table_grant(THD *thd, TABLE_LIST *table)
{
ulong privilege;
-#ifndef EMBEDDED_LIBRARY
- Security_context *sctx= thd->security_ctx;
- const char *db = table->db ? table->db : thd->db;
-#endif
GRANT_TABLE *grant_table;
rw_rdlock(&LOCK_grant);
#ifdef EMBEDDED_LIBRARY
grant_table= NULL;
#else
+ Security_context *sctx= thd->security_ctx;
+ const char *db = table->db ? table->db : thd->db;
grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
table->table_name, 0);
#endif
@@ -4670,16 +4699,27 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
global.append(lex_user->host.str,lex_user->host.length,
system_charset_info);
global.append ('\'');
- if (acl_user->salt_len)
+ if (acl_user->plugin.str == native_password_plugin_name.str ||
+ acl_user->plugin.str == old_password_plugin_name.str)
{
- char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
- if (acl_user->salt_len == SCRAMBLE_LENGTH)
- make_password_from_salt(passwd_buff, acl_user->salt);
- else
- make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt);
- global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
- global.append(passwd_buff);
- global.append('\'');
+ if (acl_user->auth_string.length)
+ {
+ DBUG_ASSERT(acl_user->salt_len);
+ global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
+ global.append(acl_user->auth_string.str, acl_user->auth_string.length);
+ global.append('\'');
+ }
+ }
+ else
+ {
+ global.append(STRING_WITH_LEN(" IDENTIFIED VIA "));
+ global.append(acl_user->plugin.str, acl_user->plugin.length);
+ if (acl_user->auth_string.length)
+ {
+ global.append(STRING_WITH_LEN(" USING '"));
+ global.append(acl_user->auth_string.str, acl_user->auth_string.length);
+ global.append('\'');
+ }
}
/* "show grants" SSL related stuff */
if (acl_user->ssl_type == SSL_TYPE_ANY)
@@ -5302,9 +5342,9 @@ 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_map(table->record[0], 0,
- user_key, (key_part_map)3,
- HA_READ_KEY_EXACT)))
+ if ((error= table->file->ha_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 && error != HA_ERR_END_OF_FILE)
{
@@ -5339,7 +5379,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'",
table->s->table_name.str, user_str, host_str));
#endif
- while ((error= table->file->rnd_next(table->record[0])) !=
+ while ((error= table->file->ha_rnd_next(table->record[0])) !=
HA_ERR_END_OF_FILE)
{
if (error)
@@ -6392,38 +6432,44 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
tables->db= (char*)sp_db;
tables->table_name= tables->alias= (char*)sp_name;
- 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);
+ thd->make_lex_string(&combo->user,
+ combo->user.str, strlen(combo->user.str), 0);
+ thd->make_lex_string(&combo->host,
+ combo->host.str, strlen(combo->host.str), 0);
+ combo->password= empty_lex_str;
+ combo->plugin= empty_lex_str;
+ combo->auth= empty_lex_str;
- if(au && au->salt_len)
+ if(au)
{
- if (au->salt_len == SCRAMBLE_LENGTH)
- {
- make_password_from_salt(passwd_buff, au->salt);
- combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
- }
- else if (au->salt_len == SCRAMBLE_LENGTH_323)
+ if (au->salt_len)
{
- make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
- combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+ if (au->salt_len == SCRAMBLE_LENGTH)
+ {
+ make_password_from_salt(passwd_buff, au->salt);
+ combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
+ }
+ else if (au->salt_len == SCRAMBLE_LENGTH_323)
+ {
+ make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
+ combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+ }
+ else
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_PASSWD_LENGTH,
+ ER(ER_PASSWD_LENGTH), SCRAMBLED_PASSWORD_CHAR_LENGTH);
+ return TRUE;
+ }
+ combo->password.str= passwd_buff;
}
- else
+
+ if (au->plugin.str != native_password_plugin_name.str &&
+ au->plugin.str != old_password_plugin_name.str)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_PASSWD_LENGTH,
- ER(ER_PASSWD_LENGTH),
- SCRAMBLED_PASSWORD_CHAR_LENGTH);
- return TRUE;
+ combo->plugin= au->plugin;
+ combo->auth= au->auth_string;
}
- combo->password.str= passwd_buff;
- }
- else
- {
- combo->password.str= (char*)"";
- combo->password.length= 0;
}
if (user_list.push_back(combo))
@@ -6917,3 +6963,1487 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
}
#endif
+
+/****************************************************************************
+ AUTHENTICATION CODE
+ including initial connect handshake, invoking appropriate plugins,
+ client-server plugin negotiation, COM_CHANGE_USER, and native
+ MySQL authentication plugins.
+****************************************************************************/
+
+/* few defines to have less ifdef's in the code below */
+#ifdef EMBEDDED_LIBRARY
+#undef HAVE_OPENSSL
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+#define initialized 0
+#define check_for_max_user_connections(X,Y) 0
+#define get_or_create_user_conn(A,B,C,D) 0
+#endif
+#endif
+#ifndef HAVE_OPENSSL
+#define ssl_acceptor_fd 0
+#define sslaccept(A,B,C,D) 1
+#define NORMAL_HANDSHAKE_SIZE 6
+#endif
+
+/**
+ The internal version of what plugins know as MYSQL_PLUGIN_VIO,
+ basically the context of the authentication session
+*/
+struct MPVIO_EXT : public MYSQL_PLUGIN_VIO
+{
+ MYSQL_SERVER_AUTH_INFO auth_info;
+ THD *thd;
+ ACL_USER *acl_user; ///< a copy, independent from acl_users array
+ plugin_ref plugin; ///< what plugin we're under
+ LEX_STRING db; ///< db name from the handshake packet
+ /** when restarting a plugin this caches the last client reply */
+ struct {
+ char *plugin, *pkt; ///< pointers into NET::buff
+ uint pkt_len;
+ } cached_client_reply;
+ /** this caches the first plugin packet for restart request on the client */
+ struct {
+ char *pkt;
+ uint pkt_len;
+ } cached_server_packet;
+ int packets_read, packets_written; ///< counters for send/received packets
+ uint connect_errors; ///< if there were connect errors for this host
+ /** when plugin returns a failure this tells us what really happened */
+ enum { SUCCESS, FAILURE, RESTART } status;
+};
+
+
+/**
+ a helper function to report an access denied error in all the proper places
+*/
+
+static void login_failed_error(THD *thd)
+{
+ my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip,
+ thd->password ? ER(ER_YES) : ER(ER_NO));
+ general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip,
+ thd->password ? ER(ER_YES) : ER(ER_NO));
+ status_var_increment(thd->status_var.access_denied_errors);
+ /*
+ Log access denied messages to the error log when log-warnings = 2
+ so that the overhead of the general query log is not required to track
+ failed connections.
+ */
+ if (global_system_variables.log_warnings > 1)
+ {
+ sql_print_warning(ER(ER_ACCESS_DENIED_ERROR),
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip,
+ thd->password ? ER(ER_YES) : ER(ER_NO));
+ }
+}
+
+
+/**
+ sends a server handshake initialization packet, the very first packet
+ after the connection was established
+
+ Packet format:
+
+ Bytes Content
+ ----- ----
+ 1 protocol version (always 10)
+ n server version string, \0-terminated
+ 4 thread id
+ 8 first 8 bytes of the plugin provided data (scramble)
+ 1 \0 byte, terminating the first part of a scramble
+ 2 server capabilities (two lower bytes)
+ 1 server character set
+ 2 server status
+ 2 server capabilities (two upper bytes)
+ 1 length of the scramble
+ 10 reserved, always 0
+ n rest of the plugin provided data (at least 12 bytes)
+ 1 \0 byte, terminating the second part of a scramble
+
+ @retval 0 ok
+ @retval 1 error
+*/
+
+static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
+ const char *data, uint data_len)
+{
+ DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
+ DBUG_ASSERT(data_len <= 255);
+
+ THD *thd= mpvio->thd;
+ char *buff= (char *)my_alloca(1 + SERVER_VERSION_LENGTH + 1 + data_len + 64);
+ char scramble_buf[SCRAMBLE_LENGTH];
+ char *end= buff;
+
+ *end++= protocol_version;
+
+ thd->client_capabilities= CLIENT_BASIC_FLAGS;
+
+ if (data_len)
+ {
+ mpvio->cached_server_packet.pkt= (char*)thd->memdup(data, data_len);
+ mpvio->cached_server_packet.pkt_len= data_len;
+ }
+
+ if (data_len < SCRAMBLE_LENGTH)
+ {
+ if (data_len)
+ { /*
+ the first packet *must* have at least 20 bytes of a scramble.
+ if a plugin provided less, we pad it to 20 with zeros
+ */
+ memcpy(scramble_buf, data, data_len);
+ bzero(scramble_buf+data_len, SCRAMBLE_LENGTH-data_len);
+ data= scramble_buf;
+ }
+ else
+ {
+ /*
+ if the default plugin does not provide the data for the scramble at
+ all, we generate a scramble internally anyway, just in case the
+ user account (that will be known only later) uses a
+ native_password_plugin (which needs a scramble). If we don't send a
+ scramble now - wasting 20 bytes in the packet -
+ native_password_plugin will have to send it in a separate packet,
+ adding one more round trip.
+ */
+ create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
+ data= thd->scramble;
+ }
+ data_len= SCRAMBLE_LENGTH;
+ }
+
+ if (opt_using_transactions)
+ thd->client_capabilities|= CLIENT_TRANSACTIONS;
+
+ thd->client_capabilities|= CAN_CLIENT_COMPRESS;
+
+ if (ssl_acceptor_fd)
+ {
+ thd->client_capabilities |= CLIENT_SSL;
+ thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT;
+ }
+
+ end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
+ int4store((uchar*) end, mpvio->thd->thread_id);
+ end+= 4;
+
+ /*
+ 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= (char*)memcpy(end, data, SCRAMBLE_LENGTH_323);
+ end+= SCRAMBLE_LENGTH_323;
+ *end++= 0;
+
+ int2store(end, thd->client_capabilities);
+ /* write server characteristics: up to 16 bytes allowed */
+ end[2]=(char) default_charset_info->number;
+ int2store(end+3, mpvio->thd->server_status);
+ int2store(end+5, thd->client_capabilities >> 16);
+ end[7]= data_len;
+ bzero(end+8, 10);
+ end+= 18;
+ /* write scramble tail */
+ end= (char*)memcpy(end, data + SCRAMBLE_LENGTH_323,
+ data_len - SCRAMBLE_LENGTH_323);
+ end+= data_len - SCRAMBLE_LENGTH_323;
+ end= strmake(end, plugin_name(mpvio->plugin)->str,
+ plugin_name(mpvio->plugin)->length);
+
+ int res= my_net_write(&mpvio->thd->net, (uchar*) buff, (size_t) (end-buff)) ||
+ net_flush(&mpvio->thd->net);
+ my_afree(buff);
+ return res;
+}
+
+static bool secure_auth(THD *thd)
+{
+ if (!opt_secure_auth)
+ return 0;
+
+ /*
+ If the server is running in secure auth mode, short scrambles are
+ forbidden. Extra juggling to report the same error as the old code.
+ */
+ if (thd->client_capabilities & CLIENT_PROTOCOL_41)
+ {
+ my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
+ thd->security_ctx->user,
+ thd->security_ctx->host_or_ip);
+ general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
+ thd->security_ctx->user,
+ thd->security_ctx->host_or_ip);
+ }
+ else
+ {
+ my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
+ general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+ }
+ return 1;
+}
+
+/**
+ sends a "change plugin" packet, requesting a client to restart authentication
+ using a different authentication plugin
+
+ Packet format:
+
+ Bytes Content
+ ----- ----
+ 1 byte with the value 254
+ n client plugin to use, \0-terminated
+ n plugin provided data
+
+ In a special case of switching from native_password_plugin to
+ old_password_plugin, the packet contains only one - the first - byte,
+ plugin name is omitted, plugin data aren't needed as the scramble was
+ already sent. This one-byte packet is identical to the "use the short
+ scramble" packet in the protocol before plugins were introduced.
+
+ @retval 0 ok
+ @retval 1 error
+*/
+
+static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
+ const uchar *data, uint data_len)
+{
+ DBUG_ASSERT(mpvio->packets_written == 1);
+ DBUG_ASSERT(mpvio->packets_read == 1);
+ NET *net= &mpvio->thd->net;
+ static uchar switch_plugin_request_buf[]= { 254 };
+
+
+ mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
+
+ const char *client_auth_plugin=
+ ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
+
+ DBUG_ASSERT(client_auth_plugin);
+ DBUG_ASSERT(my_strcasecmp(system_charset_info, client_auth_plugin,
+ mpvio->cached_client_reply.plugin));
+
+ /*
+ we send an old "short 4.0 scramble request", if we need to request a
+ client to use 4.0 auth plugin (short scramble) and the scramble was
+ already sent to the client
+
+ below, cached_client_reply.plugin is the plugin name that client has used,
+ client_auth_plugin is derived from mysql.user table, for the given
+ user account, it's the plugin that the client need to use to login.
+ */
+ bool switch_from_long_to_short_scramble=
+ native_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
+ client_auth_plugin == old_password_plugin_name.str;
+
+ if (switch_from_long_to_short_scramble)
+ return secure_auth(mpvio->thd) ||
+ my_net_write(net, switch_plugin_request_buf, 1) ||
+ net_flush(net);
+
+ /*
+ We never request a client to switch from a short to long scramble.
+ Plugin-aware clients can do that, but traditionally it meant to
+ ask an old 4.0 client to use the new 4.1 authentication protocol.
+ */
+ bool switch_from_short_to_long_scramble=
+ old_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
+ client_auth_plugin == native_password_plugin_name.str;
+
+ if (switch_from_short_to_long_scramble)
+ {
+ my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
+ general_log_print(mpvio->thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+ return 1;
+ }
+
+ return net_write_command(net, switch_plugin_request_buf[0],
+ (uchar*)client_auth_plugin,
+ strlen(client_auth_plugin)+1,
+ (uchar*)data, data_len);
+}
+
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/**
+ Finds acl entry in user database for authentication purposes.
+
+ Finds a user and copies it into mpvio. Reports an authentication
+ failure if a user is not found.
+
+ @note find_acl_user is not the same, because it doesn't take into
+ account the case when user is not empty, but acl_user->user is empty
+
+ @retval 0 found
+ @retval 1 not found
+*/
+
+static bool find_mpvio_user(MPVIO_EXT *mpvio, Security_context *sctx)
+{
+ DBUG_ASSERT(mpvio->acl_user == 0);
+
+ pthread_mutex_lock(&acl_cache->lock);
+ for (uint i=0 ; i < acl_users.elements ; i++)
+ {
+ ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
+ if ((!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user)) &&
+ compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))
+ {
+ mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root);
+ break;
+ }
+ }
+ pthread_mutex_unlock(&acl_cache->lock);
+
+ if (!mpvio->acl_user)
+ {
+ login_failed_error(mpvio->thd);
+ return 1;
+ }
+
+ /* user account requires non-default plugin and the client is too old */
+ if (mpvio->acl_user->plugin.str != native_password_plugin_name.str &&
+ mpvio->acl_user->plugin.str != old_password_plugin_name.str &&
+ !(mpvio->thd->client_capabilities & CLIENT_PLUGIN_AUTH))
+ {
+ DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
+ native_password_plugin_name.str));
+ DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
+ old_password_plugin_name.str));
+ my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
+ general_log_print(mpvio->thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+ return 1;
+ }
+
+ mpvio->auth_info.user_name= sctx->user;
+ mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
+ strmake(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
+ mpvio->acl_user->user : "", USERNAME_LENGTH);
+
+ return 0;
+}
+#endif
+
+
+/* the packet format is described in send_change_user_packet() */
+static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
+{
+ THD *thd= mpvio->thd;
+ NET *net= &thd->net;
+ Security_context *sctx= thd->security_ctx;
+
+ char *user= (char*) net->read_pos;
+ char *end= user + packet_length;
+ /* Safe because there is always a trailing \0 at the end of the packet */
+ char *passwd= strend(user)+1;
+ uint user_len= passwd - user - 1;
+ char *db= passwd;
+ char db_buff[SAFE_NAME_LEN + 1]; // buffer to store db in utf8
+ char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
+ uint dummy_errors;
+
+ if (passwd >= end)
+ {
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+ return 1;
+ }
+
+ /*
+ 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+= passwd_len + 1;
+ /*
+ Database name is always NUL-terminated, so in case of empty database
+ the packet must contain at least the trailing '\0'.
+ */
+ if (db >= end)
+ {
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+ return 1;
+ }
+
+ uint db_len= strlen(db);
+
+ char *ptr= db + db_len + 1;
+
+ if (ptr+1 < end)
+ {
+ uint cs_number= uint2korr(ptr);
+ if (thd_init_client_charset(thd, cs_number))
+ return 1;
+ thd->update_charset();
+ }
+
+ /* Convert database and user names to utf8 */
+ db_len= copy_and_convert(db_buff, sizeof(db_buff)-1, system_charset_info,
+ db, db_len, thd->charset(), &dummy_errors);
+
+ user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
+ system_charset_info, user, user_len,
+ thd->charset(), &dummy_errors);
+
+ if (!(sctx->user= my_strndup(user_buff, user_len, MYF(MY_WME))))
+ return 1;
+
+ /* Clear variables that are allocated */
+ thd->user_connect= 0;
+ strmake(sctx->priv_user, sctx->user, USERNAME_LENGTH);
+
+ if (thd->make_lex_string(&mpvio->db, db_buff, db_len, 0) == 0)
+ return 1; /* The error is set by make_lex_string(). */
+
+ /*
+ 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.
+ */
+ thd->reset_db(NULL, 0);
+
+ if (!initialized)
+ {
+ // if mysqld's been started with --skip-grant-tables option
+ mpvio->status= MPVIO_EXT::SUCCESS;
+ return 0;
+ }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (find_mpvio_user(mpvio, sctx))
+ return 1;
+
+ char *client_plugin;
+ if (thd->client_capabilities & CLIENT_PLUGIN_AUTH)
+ {
+ client_plugin= ptr + 2;
+ if (client_plugin >= end)
+ {
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+ return 1;
+ }
+ client_plugin= fix_plugin_ptr(client_plugin);
+ }
+ else
+ {
+ if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
+ client_plugin= native_password_plugin_name.str;
+ else
+ {
+ client_plugin= old_password_plugin_name.str;
+ /*
+ For a passwordless accounts we use native_password_plugin.
+ But when an old 4.0 client connects to it, we change it to
+ old_password_plugin, otherwise MySQL will think that server
+ and client plugins don't match.
+ */
+ if (mpvio->acl_user->auth_string.length == 0)
+ mpvio->acl_user->plugin= old_password_plugin_name;
+ }
+ }
+
+ /* remember the data part of the packet, to present it to plugin in read_packet() */
+ mpvio->cached_client_reply.pkt= passwd;
+ mpvio->cached_client_reply.pkt_len= passwd_len;
+ mpvio->cached_client_reply.plugin= client_plugin;
+ mpvio->status= MPVIO_EXT::RESTART;
+#endif
+
+ return 0;
+}
+
+
+/* the packet format is described in send_client_reply_packet() */
+static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
+ uchar **buff, ulong pkt_len)
+{
+#ifndef EMBEDDED_LIBRARY
+ THD *thd= mpvio->thd;
+ NET *net= &thd->net;
+ char *end;
+
+ DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
+
+ if (pkt_len < MIN_HANDSHAKE_SIZE)
+ return packet_error;
+
+ /*
+ Protocol buffer is guaranteed to always end with \0. (see my_net_read())
+ As the code below depends on this, lets check that.
+ */
+ DBUG_ASSERT(net->read_pos[pkt_len] == 0);
+
+ if (mpvio->connect_errors)
+ reset_host_errors(&net->vio->remote.sin_addr);
+
+ ulong client_capabilities= uint2korr(net->read_pos);
+ if (client_capabilities & CLIENT_PROTOCOL_41)
+ {
+ if (pkt_len < 32)
+ return packet_error;
+ 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]));
+ if (thd_init_client_charset(thd, (uint) net->read_pos[8]))
+ return packet_error;
+ thd->update_charset();
+ end= (char*) net->read_pos+32;
+ }
+ else
+ {
+ if (pkt_len < 5)
+ return packet_error;
+ 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 client. */
+ thd->client_capabilities&= client_capabilities;
+
+ if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
+ thd->variables.sql_mode|= MODE_IGNORE_SPACE;
+
+ DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
+ if (thd->client_capabilities & CLIENT_SSL)
+ {
+ char error_string[1024] __attribute__((unused));
+
+ /* Do the SSL layering. */
+ if (!ssl_acceptor_fd)
+ return packet_error;
+
+ DBUG_PRINT("info", ("IO layer change in progress..."));
+ if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, error_string))
+ {
+ DBUG_PRINT("error", ("Failed to accept new SSL connection"));
+ return packet_error;
+ }
+
+ DBUG_PRINT("info", ("Reading user information over SSL layer"));
+ pkt_len= my_net_read(net);
+ if (pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE)
+ {
+ DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
+ pkt_len));
+ return packet_error;
+ }
+ }
+
+ if (end >= (char*) net->read_pos+ pkt_len +2)
+ return packet_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;
+ uint user_len= passwd - user - 1, db_len;
+ char *db= passwd;
+ char db_buff[SAFE_NAME_LEN + 1]; // buffer to store db in utf8
+ char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
+ uint dummy_errors;
+
+ /*
+ 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;
+
+ if (passwd + passwd_len + test(db) > (char *)net->read_pos + pkt_len)
+ return packet_error;
+
+ /* strlen() can't be easily deleted without changing protocol */
+ db_len= db ? strlen(db) : 0;
+
+ char *client_plugin= passwd + passwd_len + (db ? db_len + 1 : 0);
+
+ /* Since 4.1 all database names are stored in utf8 */
+ if (db)
+ {
+ db_len= copy_and_convert(db_buff, sizeof(db_buff)-1, system_charset_info,
+ db, db_len, thd->charset(), &dummy_errors);
+ db= db_buff;
+ }
+
+ user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
+ system_charset_info, user, user_len,
+ thd->charset(), &dummy_errors);
+ 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-= 2;
+ }
+
+ /*
+ Clip username to allowed length in characters (not bytes). This is
+ mostly for backward compatibility.
+ */
+ {
+ CHARSET_INFO *cs= system_charset_info;
+ int err;
+
+ user_len= (uint) cs->cset->well_formed_len(cs, user, user + user_len,
+ USERNAME_CHAR_LENGTH, &err);
+ user[user_len]= '\0';
+ }
+
+ Security_context *sctx= thd->security_ctx;
+
+ if (thd->make_lex_string(&mpvio->db, db, db_len, 0) == 0)
+ return packet_error; /* The error is set by make_lex_string(). */
+ if (sctx->user)
+ x_free(sctx->user);
+ if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME))))
+ return packet_error; /* The error is set by my_strdup(). */
+
+ /*
+ Clear thd->db as it points to something, that will be freed when
+ connection is closed. We don't want to accidentally free a wrong
+ pointer if connect failed.
+ */
+ thd->reset_db(NULL, 0);
+
+ if (!initialized)
+ {
+ // if mysqld's been started with --skip-grant-tables option
+ mpvio->status= MPVIO_EXT::SUCCESS;
+ return packet_error;
+ }
+
+ thd->password= passwd_len > 0;
+ if (find_mpvio_user(mpvio, sctx))
+ {
+ return packet_error;
+ }
+
+ if (thd->client_capabilities & CLIENT_PLUGIN_AUTH)
+ {
+ if (client_plugin >= (char *)net->read_pos + pkt_len)
+ return packet_error;
+ client_plugin= fix_plugin_ptr(client_plugin);
+ }
+ else
+ {
+ if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
+ client_plugin= native_password_plugin_name.str;
+ else
+ {
+ client_plugin= old_password_plugin_name.str;
+ /*
+ For a passwordless accounts we use native_password_plugin.
+ But when an old 4.0 client connects to it, we change it to
+ old_password_plugin, otherwise MySQL will think that server
+ and client plugins don't match.
+ */
+ if (mpvio->acl_user->auth_string.length == 0)
+ mpvio->acl_user->plugin= old_password_plugin_name;
+ }
+ }
+
+ /*
+ if the acl_user needs a different plugin to authenticate
+ (specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
+ we need to restart the authentication in the server.
+ But perhaps the client has already used the correct plugin -
+ in that case the authentication on the client may not need to be
+ restarted and a server auth plugin will read the data that the client
+ has just send. Cache them to return in the next server_mpvio_read_packet().
+ */
+ if (my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
+ plugin_name(mpvio->plugin)->str) != 0)
+ {
+ mpvio->cached_client_reply.pkt= passwd;
+ mpvio->cached_client_reply.pkt_len= passwd_len;
+ mpvio->cached_client_reply.plugin= client_plugin;
+ mpvio->status= MPVIO_EXT::RESTART;
+ return packet_error;
+ }
+
+ /*
+ ok, we don't need to restart the authentication on the server.
+ but if the client used the wrong plugin, we need to restart
+ the authentication on the client. Do it here, the server plugin
+ doesn't need to know.
+ */
+ const char *client_auth_plugin=
+ ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
+
+ if (client_auth_plugin &&
+ my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
+ {
+ mpvio->cached_client_reply.plugin= client_plugin;
+ if (send_plugin_request_packet(mpvio,
+ (uchar*)mpvio->cached_server_packet.pkt,
+ mpvio->cached_server_packet.pkt_len))
+ return packet_error;
+
+ passwd_len= my_net_read(&mpvio->thd->net);
+ passwd = (char*)mpvio->thd->net.read_pos;
+ }
+
+ *buff= (uchar*)passwd;
+ return passwd_len;
+#else
+ return 0;
+#endif
+}
+
+
+/**
+ vio->write_packet() callback method for server authentication plugins
+
+ This function is called by a server authentication plugin, when it wants
+ to send data to the client.
+
+ It transparently wraps the data into a handshake packet,
+ and handles plugin negotiation with the client. If necessary,
+ it escapes the plugin data, if it starts with a mysql protocol packet byte.
+*/
+
+static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
+ const uchar *packet, int packet_len)
+{
+ MPVIO_EXT *mpvio= (MPVIO_EXT*)param;
+ int res;
+
+ /* reset cached_client_reply */
+ mpvio->cached_client_reply.pkt= 0;
+ /* for the 1st packet we wrap plugin data into the handshake packet */
+ if (mpvio->packets_written == 0)
+ res= send_server_handshake_packet(mpvio, (char*)packet, packet_len);
+ else if (mpvio->status == MPVIO_EXT::RESTART)
+ res= send_plugin_request_packet(mpvio, packet, packet_len);
+ else if (packet_len > 0 && (*packet == 1 || *packet == 255 || *packet == 254))
+ {
+ /*
+ we cannot allow plugin data packet to start from 255 or 254 -
+ as the client will treat it as an error or "change plugin" packet.
+ We'll escape these bytes with \1. Consequently, we
+ have to escape \1 byte too.
+ */
+ res= net_write_command(&mpvio->thd->net, 1, (uchar*)"", 0,
+ packet, packet_len);
+ }
+ else
+ {
+ res= my_net_write(&mpvio->thd->net, packet, packet_len) ||
+ net_flush(&mpvio->thd->net);
+ }
+ mpvio->packets_written++;
+ return res;
+}
+
+
+/**
+ vio->read_packet() callback method for server authentication plugins
+
+ This function is called by a server authentication plugin, when it wants
+ to read data from the client.
+
+ It transparently extracts the client plugin data, if embedded into
+ a client authentication handshake packet, and handles plugin negotiation
+ with the client, if necessary.
+*/
+
+static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
+{
+ MPVIO_EXT *mpvio= (MPVIO_EXT*)param;
+ ulong pkt_len;
+
+ if (mpvio->packets_written == 0)
+ {
+ /*
+ plugin wants to read the data without sending anything first.
+ send an empty packet to force a server handshake packet to be sent
+ */
+ if (server_mpvio_write_packet(mpvio, 0, 0))
+ pkt_len= packet_error;
+ else
+ pkt_len= my_net_read(&mpvio->thd->net);
+ }
+ else
+ if (mpvio->cached_client_reply.pkt)
+ {
+ DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
+ DBUG_ASSERT(mpvio->packets_read > 0);
+ /*
+ if the have the data cached from the last server_mpvio_read_packet
+ (which can be the case if it's a restarted authentication)
+ and a client has used the correct plugin, then we can return the
+ cached data straight away and avoid one round trip.
+ */
+ const char *client_auth_plugin=
+ ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
+ if (client_auth_plugin == 0 ||
+ my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
+ client_auth_plugin) == 0)
+ {
+ mpvio->status= MPVIO_EXT::FAILURE;
+ *buf= (uchar*)mpvio->cached_client_reply.pkt;
+ mpvio->cached_client_reply.pkt= 0;
+ mpvio->packets_read++;
+ return (int)mpvio->cached_client_reply.pkt_len;
+ }
+ /*
+ But if the client has used the wrong plugin, the cached data are
+ useless. Furthermore, we have to send a "change plugin" request
+ to the client.
+ */
+ if (server_mpvio_write_packet(mpvio, 0, 0))
+ pkt_len= packet_error;
+ else
+ pkt_len= my_net_read(&mpvio->thd->net);
+ }
+ else
+ pkt_len= my_net_read(&mpvio->thd->net);
+
+ if (pkt_len == packet_error)
+ goto err;
+
+ mpvio->packets_read++;
+
+ /*
+ the 1st packet has the plugin data wrapped into the client authentication
+ handshake packet
+ */
+ if (mpvio->packets_read == 1)
+ {
+ pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
+ if (pkt_len == packet_error)
+ goto err;
+ }
+ else
+ *buf = mpvio->thd->net.read_pos;
+
+ return (int)pkt_len;
+
+err:
+ if (mpvio->status == MPVIO_EXT::FAILURE && !mpvio->thd->is_error())
+ {
+ inc_host_errors(&mpvio->thd->net.vio->remote.sin_addr);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0));
+ }
+ return -1;
+}
+
+
+/**
+ fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
+ connection
+*/
+
+static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
+ MYSQL_PLUGIN_VIO_INFO *info)
+{
+ MPVIO_EXT *mpvio= (MPVIO_EXT*)vio;
+ mpvio_info(mpvio->thd->net.vio, info);
+}
+
+
+static bool acl_check_ssl(THD *thd, ACL_USER *acl_user)
+{
+#if defined(HAVE_OPENSSL)
+ Vio *vio=thd->net.vio;
+ SSL *ssl= (SSL*) vio->ssl_arg;
+ X509 *cert;
+#endif
+
+ /*
+ At this point we know that user is allowed to connect
+ from given host by given username/password pair. Now
+ we check if SSL is required, if user is using SSL and
+ if X509 certificate attributes are OK
+ */
+ switch (acl_user->ssl_type) {
+ case SSL_TYPE_NOT_SPECIFIED: // Impossible
+ case SSL_TYPE_NONE: // SSL is not required
+ return 0;
+#if defined(HAVE_OPENSSL)
+ case SSL_TYPE_ANY: // Any kind of SSL is ok
+ return vio_type(vio) != VIO_TYPE_SSL;
+ case SSL_TYPE_X509: /* Client should have any valid certificate. */
+ /*
+ Connections with non-valid certificates are dropped already
+ in sslaccept() anyway, so we do not check validity here.
+
+ We need to check for absence of SSL because without SSL
+ we should reject connection.
+ */
+ if (vio_type(vio) == VIO_TYPE_SSL &&
+ SSL_get_verify_result(ssl) == X509_V_OK &&
+ (cert= SSL_get_peer_certificate(ssl)))
+ {
+ X509_free(cert);
+ return 0;
+ }
+ return 1;
+ case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
+ /* If a cipher name is specified, we compare it to actual cipher in use. */
+ if (vio_type(vio) != VIO_TYPE_SSL ||
+ SSL_get_verify_result(ssl) != X509_V_OK)
+ return 1;
+ if (acl_user->ssl_cipher)
+ {
+ DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
+ acl_user->ssl_cipher,SSL_get_cipher(ssl)));
+ if (strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl)))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
+ acl_user->ssl_cipher, SSL_get_cipher(ssl));
+ return 1;
+ }
+ }
+ /* Prepare certificate (if exists) */
+ if (!(cert= SSL_get_peer_certificate(ssl)))
+ return 1;
+ /* If X509 issuer is specified, we check it... */
+ if (acl_user->x509_issuer)
+ {
+ char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
+ DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
+ acl_user->x509_issuer, ptr));
+ if (strcmp(acl_user->x509_issuer, ptr))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_information("X509 issuer mismatch: should be '%s' "
+ "but is '%s'", acl_user->x509_issuer, ptr);
+ free(ptr);
+ X509_free(cert);
+ return 1;
+ }
+ free(ptr);
+ }
+ /* X509 subject is specified, we check it .. */
+ if (acl_user->x509_subject)
+ {
+ char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
+ DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
+ acl_user->x509_subject, ptr));
+ if (strcmp(acl_user->x509_subject,ptr))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
+ acl_user->x509_subject, ptr);
+ free(ptr);
+ X509_free(cert);
+ return 1;
+ }
+ free(ptr);
+ }
+ X509_free(cert);
+ return 0;
+#else /* HAVE_OPENSSL */
+ default:
+ /*
+ If we don't have SSL but SSL is required for this user the
+ authentication should fail.
+ */
+ return 1;
+#endif /* HAVE_OPENSSL */
+ }
+ return 1;
+}
+
+static int do_auth_once(THD *thd, LEX_STRING *auth_plugin_name,
+ MPVIO_EXT *mpvio)
+{
+ int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
+ bool unlock_plugin= false;
+ plugin_ref plugin;
+
+#ifdef EMBEDDED_LIBRARY
+ plugin= native_password_plugin;
+#else
+ if (auth_plugin_name->str == native_password_plugin_name.str)
+ plugin= native_password_plugin;
+ else if (auth_plugin_name->str == old_password_plugin_name.str)
+ plugin= old_password_plugin;
+ else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
+ MYSQL_AUTHENTICATION_PLUGIN)))
+ unlock_plugin= true;
+#endif
+
+ mpvio->plugin= plugin;
+ old_status= mpvio->status;
+
+ if (plugin)
+ {
+ st_mysql_auth *auth= (st_mysql_auth*)plugin_decl(plugin)->info;
+ res= auth->authenticate_user(mpvio, &mpvio->auth_info);
+
+ if (unlock_plugin)
+ plugin_unlock(thd, plugin);
+ }
+ else
+ {
+ /* Server cannot load the required plugin. */
+ my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
+ res= CR_ERROR;
+ }
+
+ /*
+ If the status was MPVIO_EXT::RESTART before the authenticate_user() call
+ it can never be MPVIO_EXT::RESTART after the call, because any call
+ to write_packet() or read_packet() will reset the status.
+
+ But (!) if a plugin never called a read_packet() or write_packet(), the
+ status will stay unchanged. We'll fix it, by resetting the status here.
+ */
+ if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART)
+ mpvio->status= MPVIO_EXT::FAILURE; // reset to the default
+
+ return res;
+}
+
+/**
+ Perform the handshake, authorize the client and update thd sctx variables.
+
+ @param thd thread handle
+ @param connect_errors number of previous failed connect attemps
+ from this host
+ @param com_change_user_pkt_len size of the COM_CHANGE_USER packet
+ (without the first, command, byte) or 0
+ if it's not a COM_CHANGE_USER (that is, if
+ it's a new connection)
+
+ @retval 0 success, thd is updated.
+ @retval 1 error
+*/
+
+bool acl_authenticate(THD *thd, uint connect_errors,
+ uint com_change_user_pkt_len)
+{
+ int res= CR_OK;
+ MPVIO_EXT mpvio;
+ LEX_STRING *auth_plugin_name= default_auth_plugin_name;
+ enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
+ : COM_CONNECT;
+ DBUG_ENTER("acl_authenticate");
+
+ compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
+
+ bzero(&mpvio, sizeof(mpvio));
+ mpvio.read_packet= server_mpvio_read_packet;
+ mpvio.write_packet= server_mpvio_write_packet;
+ mpvio.info= server_mpvio_info;
+ mpvio.thd= thd;
+ mpvio.connect_errors= connect_errors;
+ mpvio.status= MPVIO_EXT::FAILURE;
+
+ if (command == COM_CHANGE_USER)
+ {
+ mpvio.packets_written++; // pretend that a server handshake packet was sent
+ mpvio.packets_read++; // take COM_CHANGE_USER packet into account
+
+ if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
+ DBUG_RETURN(1);
+
+ DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
+ mpvio.status == MPVIO_EXT::SUCCESS);
+ }
+ else
+ {
+ /* mark the thd as having no scramble yet */
+ thd->scramble[SCRAMBLE_LENGTH]= 1;
+
+ /*
+ perform the first authentication attempt, with the default plugin.
+ This sends the server handshake packet, reads the client reply
+ with a user name, and performs the authentication if everyone has used
+ the correct plugin.
+ */
+ res= do_auth_once(thd, auth_plugin_name, &mpvio);
+ }
+
+ /*
+ retry the authentication, if - after receiving the user name -
+ we found that we need to switch to a non-default plugin
+ */
+ if (mpvio.status == MPVIO_EXT::RESTART)
+ {
+ DBUG_ASSERT(mpvio.acl_user);
+ DBUG_ASSERT(command == COM_CHANGE_USER ||
+ my_strcasecmp(system_charset_info, auth_plugin_name->str,
+ mpvio.acl_user->plugin.str));
+ auth_plugin_name= &mpvio.acl_user->plugin;
+ res= do_auth_once(thd, auth_plugin_name, &mpvio);
+ }
+
+ Security_context *sctx= thd->security_ctx;
+ ACL_USER *acl_user= mpvio.acl_user;
+
+ thd->password= mpvio.auth_info.password_used; // remember for error messages
+
+ /*
+ Log the command here so that the user can check the log
+ for the tried logins and also to detect break-in attempts.
+
+ if sctx->user is unset it's protocol failure, bad packet.
+ */
+ if (sctx->user)
+ {
+ if (strcmp(sctx->priv_user, sctx->user))
+ {
+ general_log_print(thd, command, "%s@%s as %s on %s",
+ sctx->user, sctx->host_or_ip,
+ sctx->priv_user[0] ? sctx->priv_user : "anonymous",
+ mpvio.db.str ? mpvio.db.str : (char*) "");
+ }
+ else
+ general_log_print(thd, command, (char*) "%s@%s on %s",
+ sctx->user, sctx->host_or_ip,
+ mpvio.db.str ? mpvio.db.str : (char*) "");
+ }
+
+ if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
+ {
+ DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
+
+ if (!thd->is_error())
+ login_failed_error(thd);
+ DBUG_RETURN(1);
+ }
+
+ if (initialized) // if not --skip-grant-tables
+ {
+ sctx->master_access= acl_user->access;
+ strmake(sctx->priv_user, mpvio.auth_info.authenticated_as, USERNAME_LENGTH);
+ if (acl_user->host.hostname)
+ strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
+ else
+ *sctx->priv_host= 0;
+
+ /*
+ OK. Let's check the SSL. Historically it was checked after the password,
+ as an additional layer, not instead of the password
+ (in which case it would've been a plugin too).
+ */
+ if (acl_check_ssl(thd, acl_user))
+ {
+ login_failed_error(thd);
+ DBUG_RETURN(1);
+ }
+
+ /* Don't allow the user to connect if he has done too many queries */
+ if ((acl_user->user_resource.questions || acl_user->user_resource.updates ||
+ acl_user->user_resource.conn_per_hour ||
+ acl_user->user_resource.user_conn || max_user_connections) &&
+ get_or_create_user_conn(thd,
+ (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
+ (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
+ &acl_user->user_resource))
+ DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
+ }
+ else
+ sctx->skip_grants();
+
+ 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))
+ {
+ /* Ensure we don't decrement thd->user_connections->connections twice */
+ thd->user_connect= 0;
+ status_var_increment(denied_connections);
+ DBUG_RETURN(1); // The error is set in check_for_max_user_connections()
+ }
+
+ 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,
+ sctx->host_or_ip, sctx->user, sctx->priv_user,
+ thd->password ? "yes": "no",
+ sctx->master_access, mpvio.db.str));
+
+ if (command == COM_CONNECT &&
+ !(thd->main_security_ctx.master_access & SUPER_ACL))
+ {
+ pthread_mutex_lock(&LOCK_connection_count);
+ bool count_ok= (*thd->scheduler->connection_count <=
+ *thd->scheduler->max_connections);
+ VOID(pthread_mutex_unlock(&LOCK_connection_count));
+ if (!count_ok)
+ { // too many connections
+ my_error(ER_CON_COUNT_ERROR, MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
+
+ /*
+ 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.
+ */
+ sctx->db_access=0;
+
+ /* Change a database if necessary */
+ if (mpvio.db.length)
+ {
+ if (mysql_change_db(thd, &mpvio.db, FALSE))
+ {
+ /* mysql_change_db() has pushed the error message. */
+ status_var_increment(thd->status_var.access_denied_errors);
+ DBUG_RETURN(1);
+ }
+ }
+
+ thd->net.net_skip_rest_factor= 2; // skip at most 2*max_packet_size
+
+ if (res == CR_OK_HANDSHAKE_COMPLETE)
+ thd->main_da.disable_status();
+ else
+ my_ok(thd);
+
+ /* Ready to handle queries */
+ DBUG_RETURN(0);
+}
+
+
+/**
+ MySQL Server Password Authentication Plugin
+
+ In the MySQL authentication protocol:
+ 1. the server sends the random scramble to the client
+ 2. client sends the encrypted password back to the server
+ 3. the server checks the password.
+*/
+
+static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
+ MYSQL_SERVER_AUTH_INFO *info)
+{
+ uchar *pkt;
+ int pkt_len;
+ MPVIO_EXT *mpvio=(MPVIO_EXT*)vio;
+ THD *thd=mpvio->thd;
+
+ /* generate the scramble, or reuse the old one */
+ if (thd->scramble[SCRAMBLE_LENGTH])
+ {
+ create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
+ /* and send it to the client */
+ if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
+ return CR_ERROR;
+ }
+
+ /* reply and authenticate */
+
+ /*
+ <digression>
+ This is more complex than it looks.
+
+ The plugin (we) may be called right after the client was connected -
+ and will need to send a scramble, read reply, authenticate.
+
+ Or the plugin may be called after another plugin has sent a scramble,
+ and read the reply. If the client has used the correct client-plugin,
+ we won't need to read anything here from the client, the client
+ has already sent a reply with everything we need for authentication.
+
+ Or the plugin may be called after another plugin has sent a scramble,
+ and read the reply, but the client has used the wrong client-plugin.
+ We'll need to sent a "switch to another plugin" packet to the
+ client and read the reply. "Use the short scramble" packet is a special
+ case of "switch to another plugin" packet.
+
+ Or, perhaps, the plugin may be called after another plugin has
+ done the handshake but did not send a useful scramble. We'll need
+ to send a scramble (and perhaps a "switch to another plugin" packet)
+ and read the reply.
+
+ Besides, a client may be an old one, that doesn't understand plugins.
+ Or doesn't even understand 4.0 scramble.
+
+ And we want to keep the same protocol on the wire unless non-native
+ plugins are involved.
+
+ Anyway, it still looks simple from a plugin point of view:
+ "send the scramble, read the reply and authenticate".
+ All the magic is transparently handled by the server.
+ </digression>
+ */
+
+ /* read the reply with the encrypted password */
+ if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
+ return CR_ERROR;
+
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+ return CR_OK;
+#endif
+
+ if (pkt_len == 0) /* no password */
+ return info->auth_string[0] ? CR_ERROR : CR_OK;
+
+ info->password_used = 1;
+ if (pkt_len == SCRAMBLE_LENGTH)
+ return info->auth_string[0] == 0 ||
+ check_scramble(pkt, thd->scramble, mpvio->acl_user->salt) ?
+ CR_ERROR : CR_OK;
+
+ inc_host_errors(&mpvio->thd->net.vio->remote.sin_addr);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0));
+ return CR_ERROR;
+}
+
+
+static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
+ MYSQL_SERVER_AUTH_INFO *info)
+{
+ uchar *pkt;
+ int pkt_len;
+ MPVIO_EXT *mpvio=(MPVIO_EXT*)vio;
+ THD *thd=mpvio->thd;
+
+ /* generate the scramble, or reuse the old one */
+ if (thd->scramble[SCRAMBLE_LENGTH])
+ {
+ create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
+ /* and send it to the client */
+ if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
+ return CR_ERROR;
+ }
+
+ /* read the reply and authenticate */
+ if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
+ return CR_ERROR;
+
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+ return CR_OK;
+#endif
+
+ /*
+ legacy: if switch_from_long_to_short_scramble,
+ the password is sent \0-terminated, the pkt_len is always 9 bytes.
+ We need to figure out the correct scramble length here.
+ */
+ if (pkt_len == SCRAMBLE_LENGTH_323+1)
+ pkt_len= strnlen((char*)pkt, pkt_len);
+
+ if (pkt_len == 0) /* no password */
+ return info->auth_string[0] ? CR_ERROR : CR_OK;
+
+ if (secure_auth(thd))
+ return CR_ERROR;
+
+ info->password_used = 1;
+
+ if (pkt_len == SCRAMBLE_LENGTH_323)
+ return info->auth_string[0] == 0 ||
+ check_scramble_323(pkt, thd->scramble,
+ (ulong *)mpvio->acl_user->salt) ? CR_ERROR : CR_OK;
+
+ inc_host_errors(&mpvio->thd->net.vio->remote.sin_addr);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0));
+ return CR_ERROR;
+}
+
+static struct st_mysql_auth native_password_handler=
+{
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+ native_password_plugin_name.str,
+ native_password_authenticate
+};
+
+static struct st_mysql_auth old_password_handler=
+{
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+ old_password_plugin_name.str,
+ old_password_authenticate
+};
+
+mysql_declare_plugin(mysql_password)
+{
+ MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
+ &native_password_handler, /* type descriptor */
+ native_password_plugin_name.str, /* Name */
+ "R.J.Silk, Sergei Golubchik", /* Author */
+ "Native MySQL authentication", /* Description */
+ PLUGIN_LICENSE_GPL, /* License */
+ NULL, /* Init function */
+ NULL, /* Deinit function */
+ 0x0100, /* Version (1.0) */
+ NULL, /* status variables */
+ NULL, /* system variables */
+ NULL /* config options */
+},
+{
+ MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
+ &old_password_handler, /* type descriptor */
+ old_password_plugin_name.str, /* Name */
+ "R.J.Silk, Sergei Golubchik", /* Author */
+ "Old MySQL-4.0 authentication", /* Description */
+ PLUGIN_LICENSE_GPL, /* License */
+ NULL, /* Init function */
+ NULL, /* Deinit function */
+ 0x0100, /* Version (1.0) */
+ NULL, /* status variables */
+ NULL, /* system variables */
+ NULL /* config options */
+}
+mysql_declare_plugin_end;
+
+maria_declare_plugin(mysql_password)
+{
+ MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
+ &native_password_handler, /* type descriptor */
+ native_password_plugin_name.str, /* Name */
+ "R.J.Silk, Sergei Golubchik", /* Author */
+ "Native MySQL authentication", /* Description */
+ PLUGIN_LICENSE_GPL, /* License */
+ NULL, /* Init function */
+ NULL, /* Deinit function */
+ 0x0100, /* Version (1.0) */
+ NULL, /* status variables */
+ NULL, /* system variables */
+ "1.0", /* String version */
+ MariaDB_PLUGIN_MATURITY_BETA /* Maturity */
+},
+{
+ MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
+ &old_password_handler, /* type descriptor */
+ old_password_plugin_name.str, /* Name */
+ "R.J.Silk, Sergei Golubchik", /* Author */
+ "Old MySQL-4.0 authentication", /* Description */
+ PLUGIN_LICENSE_GPL, /* License */
+ NULL, /* Init function */
+ NULL, /* Deinit function */
+ 0x0100, /* Version (1.0) */
+ NULL, /* status variables */
+ NULL, /* system variables */
+ "1.0", /* String version */
+ MariaDB_PLUGIN_MATURITY_BETA /* Maturity */
+}
+maria_declare_plugin_end;
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 29676b71c90..1be2ecd2ef0 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -164,53 +164,6 @@ enum mysql_db_table_field
extern const TABLE_FIELD_DEF mysql_db_table_def;
-/* Classes */
-
-struct acl_host_and_ip
-{
- char *hostname;
- long ip,ip_mask; // Used with masked ip:s
-};
-
-
-class ACL_ACCESS {
-public:
- ulong sort;
- ulong access;
-};
-
-
-/* ACL_HOST is used if no host is specified */
-
-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[SCRAMBLE_LENGTH+1]; // scrambled password in binary form
- uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 3.23, 20 - 4.1.1
- 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;
-};
-
/* prototypes */
bool hostname_requires_resolving(const char *hostname);
@@ -219,10 +172,9 @@ my_bool acl_reload(THD *thd);
void acl_free(bool end=0);
ulong acl_get(const char *host, const char *ip,
const char *user, const char *db, my_bool db_is_pattern);
-int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd,
- uint passwd_len);
-bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
- char *ip, char *db);
+bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len);
+bool acl_getroot(Security_context *sctx, char *user, char *host,
+ char *ip, char *db);
bool acl_check_host(const char *host, const char *ip);
int check_change_password(THD *thd, const char *host, const char *user,
char *password, uint password_len);
diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h
index 8807b40857e..b72ff62d592 100644
--- a/sql/sql_analyse.h
+++ b/sql/sql_analyse.h
@@ -71,7 +71,7 @@ class field_info :public Sql_alloc
protected:
ulong treemem, tree_elements, empty, nulls, min_length, max_length;
uint room_in_tree;
- my_bool found;
+ bool found;
TREE tree;
Item *item;
analyse *pc;
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 54921500e93..c8b1067425b 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -61,7 +61,7 @@ private:
bool
Prelock_error_handler::handle_error(uint sql_errno,
const char * /* message */,
- MYSQL_ERROR::enum_warning_level /* level */,
+ MYSQL_ERROR::enum_warning_level level,
THD * /* thd */)
{
if (sql_errno == ER_NO_SUCH_TABLE)
@@ -70,7 +70,8 @@ Prelock_error_handler::handle_error(uint sql_errno,
return TRUE;
}
- m_unhandled_errors++;
+ if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
+ m_unhandled_errors++;
return FALSE;
}
@@ -1393,6 +1394,12 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
table->s->table_name.str, (long) table));
+ if (table->file)
+ {
+ table->file->update_global_table_stats();
+ table->file->update_global_index_stats();
+ }
+
*table_ptr=table->next;
/*
When closing a MERGE parent or child table, detach the children first.
@@ -1475,12 +1482,11 @@ void close_temporary_tables(THD *thd)
/* 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];
- String s_query= String(buf, sizeof(buf), system_charset_info);
+ char buf[FN_REFLEN];
+ String s_query(buf, sizeof(buf), system_charset_info);
bool found_user_tables= FALSE;
- memcpy(buf, stub, stub_len);
+ s_query.copy(stub, sizeof(stub)-1, system_charset_info);
/*
Insertion sort of temp tables by pseudo_thread_id to build ordered list
@@ -1534,19 +1540,25 @@ void close_temporary_tables(THD *thd)
{
bool save_thread_specific_used= thd->thread_specific_used;
my_thread_id save_pseudo_thread_id= thd->variables.pseudo_thread_id;
+ char db_buf[FN_REFLEN];
+ String db(db_buf, sizeof(db_buf), system_charset_info);
+
/* Set pseudo_thread_id to be that of the processed table */
thd->variables.pseudo_thread_id= tmpkeyval(thd, table);
- String db;
- db.append(table->s->db.str);
+
+ db.copy(table->s->db.str, table->s->db.length, system_charset_info);
+ /* Reset s_query() if changed by previous loop */
+ s_query.length(sizeof(stub)-1);
+
/* Loop forward through all tables that belong to a common database
within the sublist of common pseudo_thread_id to create single
DROP query
*/
- for (s_query.length(stub_len);
+ for (;
table && is_user_table(table) &&
tmpkeyval(thd, table) == thd->variables.pseudo_thread_id &&
table->s->db.length == db.length() &&
- strcmp(table->s->db.str, db.ptr()) == 0;
+ memcmp(table->s->db.str, db.ptr(), db.length()) == 0;
table= next)
{
/*
@@ -1850,7 +1862,7 @@ int drop_temporary_table(THD *thd, TABLE_LIST *table_list)
/* 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);
+ my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
DBUG_RETURN(-1);
}
@@ -1873,7 +1885,7 @@ void close_temporary_table(THD *thd, TABLE *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));
+ (long) table, table->alias.c_ptr()));
/*
When closing a MERGE parent or child table, detach the children
@@ -1933,6 +1945,13 @@ void close_temporary(TABLE *table, bool free_share, bool delete_table)
DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
table->s->db.str, table->s->table_name.str));
+ /* in_use is not set for replication temporary tables during shutdown */
+ if (table->in_use)
+ {
+ table->file->update_global_table_stats();
+ table->file->update_global_index_stats();
+ }
+
free_io_cache(table);
closefrm(table, 0);
if (delete_table)
@@ -2337,7 +2356,8 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
table->tablenr=thd->current_tablenr++;
table->used_fields=0;
table->const_table=0;
- table->null_row= table->maybe_null= 0;
+ table->null_row= 0;
+ table->maybe_null= 0;
table->force_index= table->force_index_order= table->force_index_group= 0;
table->status=STATUS_NO_RECORD;
DBUG_RETURN(FALSE);
@@ -2599,7 +2619,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
("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);
+ my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
DBUG_RETURN(0);
}
table->query_id= thd->query_id;
@@ -2636,7 +2656,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
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) &&
+ if (!my_strcasecmp(system_charset_info, table->alias.c_ptr(), alias) &&
table->query_id != thd->query_id && /* skip tables already used */
!(thd->prelocked_mode && table->query_id) &&
!table->parent)
@@ -2991,25 +3011,23 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->alias_name_used= my_strcasecmp(table_alias_charset,
table->s->table_name.str, alias);
/* Fix alias if table name changes */
- if (strcmp(table->alias, alias))
- {
- uint length=(uint) strlen(alias)+1;
- table->alias= (char*) my_realloc((char*) table->alias, length,
- MYF(MY_WME));
- memcpy((char*) table->alias, alias, length);
- }
+ if (strcmp(table->alias.c_ptr(), alias))
+ table->alias.copy(alias, strlen(alias), table->alias.charset());
+
/* These variables are also set in reopen_table() */
table->tablenr=thd->current_tablenr++;
table->used_fields=0;
table->const_table=0;
- table->null_row= table->maybe_null= 0;
+ table->null_row= 0;
+ table->maybe_null= 0;
table->force_index= table->force_index_order= table->force_index_group= 0;
table->status=STATUS_NO_RECORD;
table->insert_values= 0;
table->fulltext_searched= 0;
+ table->file->ha_start_of_new_statement();
table->file->ft_handler= 0;
/*
- Check that there is no reference to a condtion from an earlier query
+ Check that there is no reference to a condition from an earlier query
(cf. Bug#58553).
*/
DBUG_ASSERT(table->file->pushed_cond == NULL);
@@ -3085,7 +3103,7 @@ bool reopen_table(TABLE *table)
#ifdef EXTRA_DEBUG
if (table->db_stat)
sql_print_error("Table %s had a open data handler in reopen_table",
- table->alias);
+ table->alias.c_ptr());
#endif
bzero((char*) &table_list, sizeof(TABLE_LIST));
table_list.db= table->s->db.str;
@@ -3096,7 +3114,7 @@ bool reopen_table(TABLE *table)
DBUG_RETURN(1); // Thread was killed
if (open_unireg_entry(thd, &tmp, &table_list,
- table->alias,
+ table->alias.c_ptr(),
table->s->table_cache_key.str,
table->s->table_cache_key.length,
thd->mem_root, 0))
@@ -3135,14 +3153,14 @@ bool reopen_table(TABLE *table)
VOID(closefrm(table, 1)); // close file, free everything
*table= tmp;
+ table->alias.move(tmp.alias);
table->default_column_bitmaps();
table->file->change_table_ptr(table, table->s);
- DBUG_ASSERT(table->alias != 0);
+ DBUG_ASSERT(table->alias.ptr() != 0);
for (field=table->field ; *field ; field++)
{
- (*field)->table= (*field)->orig_table= table;
- (*field)->table_name= &table->alias;
+ (*field)->init(table);
}
for (key=0 ; key < table->s->keys ; key++)
{
@@ -3295,7 +3313,7 @@ static bool reattach_merge(THD *thd)
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);
+ my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
error= TRUE;
mark_merge_parent_and_children_as_bad(table);
}
@@ -3388,7 +3406,6 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old)
*/
if (table->child_l && !table->children_attached)
merge_table_found= TRUE;
-
if (!tables)
{
/*
@@ -3448,8 +3465,7 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old)
}
continue;
}
- my_error(ER_CANT_REOPEN_TABLE, MYF(0),
- table->alias ? table->alias : table->s->table_name.str);
+ my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
unlink_open_table(thd, table, 0);
/* Restart loop, as one of the used tables may now be closed */
prev= &thd->open_tables;
@@ -3652,7 +3668,7 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock)
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 ? table->alias : ""));
+ DBUG_PRINT("loop", ("table_name: %s", table->alias.c_ptr()));
HASH_SEARCH_STATE state;
for (TABLE *search= (TABLE*) hash_first(&open_cache, (uchar*) key,
key_length, &state);
@@ -4446,7 +4462,7 @@ void detach_merge_children(TABLE *table, bool clear_refs)
Set alias to "" to ensure that table is not used if we are in
LOCK TABLES
*/
- ((char*) child_l->table->alias)[0]= 0;
+ child_l->table->alias.length(0);
/* Clear the table reference to force new assignment at next open. */
child_l->table= NULL;
@@ -4999,7 +5015,7 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table,
if ((int) lock_type >= (int) TL_WRITE_ALLOW_READ &&
(int) table->reginfo.lock_type < (int) TL_WRITE_ALLOW_READ)
{
- my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),table->alias);
+ my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),table->alias.c_ptr());
DBUG_RETURN(1);
}
if ((error=table->file->start_stmt(thd, lock_type)))
@@ -5830,6 +5846,9 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
table->covering_keys.intersect(field->part_of_key);
table->merge_keys.merge(field->part_of_key);
+ if (field->vcol_info)
+ table->mark_virtual_col(field);
+
if (thd->mark_used_columns == MARK_COLUMNS_READ)
bitmap= table->read_set;
else
@@ -6110,7 +6129,8 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
Field **field_ptr, *field;
uint cached_field_index= *cached_field_index_ptr;
DBUG_ENTER("find_field_in_table");
- DBUG_PRINT("enter", ("table: '%s', field name: '%s'", table->alias, name));
+ DBUG_PRINT("enter", ("table: '%s', field name: '%s'", table->alias.c_ptr(),
+ name));
/* We assume here that table->field < NO_CACHED_FIELD_INDEX = UINT_MAX */
if (cached_field_index < table->s->fields &&
@@ -8138,6 +8158,12 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
{
/* Mark fields as used to allow storage engine to optimze access */
bitmap_set_bit(field->table->read_set, field->field_index);
+ /*
+ Mark virtual fields for write and others that the virtual fields
+ depend on for read.
+ */
+ if (field->vcol_info)
+ field->table->mark_virtual_col(field);
if (table)
{
table->covering_keys.intersect(field->part_of_key);
@@ -8348,7 +8374,8 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values,
List_iterator_fast<Item> f(fields),v(values);
Item *value, *fld;
Item_field *field;
- TABLE *table= 0;
+ TABLE *table= 0, *vcol_table= 0;
+ bool abort_on_warning_saved= thd->abort_on_warning;
DBUG_ENTER("fill_record");
/*
@@ -8371,6 +8398,8 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values,
table->auto_increment_field_not_null= FALSE;
f.rewind();
}
+ else if (thd->lex->unit.insert_table_with_stored_vcol)
+ vcol_table= thd->lex->unit.insert_table_with_stored_vcol;
while ((fld= f++))
{
if (!(field= fld->filed_for_view_update()))
@@ -8383,14 +8412,40 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values,
table= rfield->table;
if (rfield == table->next_number_field)
table->auto_increment_field_not_null= TRUE;
+ if (rfield->vcol_info &&
+ value->type() != Item::DEFAULT_VALUE_ITEM &&
+ value->type() != Item::NULL_ITEM &&
+ table->s->table_category != TABLE_CATEGORY_TEMPORARY)
+ {
+ thd->abort_on_warning= FALSE;
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN,
+ ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN),
+ rfield->field_name, table->s->table_name.str);
+ thd->abort_on_warning= abort_on_warning_saved;
+ }
if ((value->save_in_field(rfield, 0) < 0) && !ignore_errors)
{
my_message(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), MYF(0));
goto err;
}
+ DBUG_ASSERT(vcol_table == 0 || vcol_table == table);
+ vcol_table= table;
}
+ /* Update virtual fields*/
+ thd->abort_on_warning= FALSE;
+ if (vcol_table)
+ {
+ if (vcol_table->vfield)
+ {
+ if (update_virtual_fields(thd, vcol_table, TRUE))
+ goto err;
+ }
+ }
+ thd->abort_on_warning= abort_on_warning_saved;
DBUG_RETURN(thd->is_error());
err:
+ thd->abort_on_warning= abort_on_warning_saved;
if (table)
table->auto_increment_field_not_null= FALSE;
DBUG_RETURN(TRUE);
@@ -8426,9 +8481,31 @@ fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields,
Table_triggers_list *triggers,
enum trg_event_type event)
{
- return (fill_record(thd, fields, values, ignore_errors) ||
- (triggers && triggers->process_triggers(thd, event,
- TRG_ACTION_BEFORE, TRUE)));
+ bool result;
+ result= (fill_record(thd, fields, values, ignore_errors) ||
+ (triggers && triggers->process_triggers(thd, event,
+ TRG_ACTION_BEFORE, TRUE)));
+ /*
+ Re-calculate virtual fields to cater for cases when base columns are
+ updated by the triggers.
+ */
+ if (!result && triggers)
+ {
+ TABLE *table= 0;
+ List_iterator_fast<Item> f(fields);
+ Item *fld;
+ Item_field *item_field;
+ if (fields.elements)
+ {
+ fld= (Item_field*)f++;
+ item_field= fld->filed_for_view_update();
+ if (item_field && item_field->field &&
+ (table= item_field->field->table) &&
+ table->vfield)
+ result= update_virtual_fields(thd, table, TRUE);
+ }
+ }
+ return result;
}
@@ -8456,38 +8533,63 @@ bool
fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors)
{
List_iterator_fast<Item> v(values);
+ List<TABLE> tbl_list;
Item *value;
TABLE *table= 0;
+ Field *field;
+ bool abort_on_warning_saved= thd->abort_on_warning;
DBUG_ENTER("fill_record");
- Field *field;
+ if (!*ptr)
+ {
+ /* No fields to update, quite strange!*/
+ DBUG_RETURN(0);
+ }
+
+ /*
+ On INSERT or UPDATE fields are checked to be from the same table,
+ thus we safely can take table from the first field.
+ */
+ table= (*ptr)->table;
+
/*
Reset the table->auto_increment_field_not_null as it is valid for
only one row.
*/
- if (*ptr)
- {
- /*
- On INSERT or UPDATE fields are checked to be from the same table,
- thus we safely can take table from the first field.
- */
- table= (*ptr)->table;
- table->auto_increment_field_not_null= FALSE;
- }
+ table->auto_increment_field_not_null= FALSE;
while ((field = *ptr++) && ! thd->is_error())
{
+ /* Ensure that all fields are from the same table */
+ DBUG_ASSERT(field->table == table);
+
value=v++;
- table= field->table;
if (field == table->next_number_field)
table->auto_increment_field_not_null= TRUE;
+ if (field->vcol_info &&
+ value->type() != Item::DEFAULT_VALUE_ITEM &&
+ value->type() != Item::NULL_ITEM &&
+ table->s->table_category != TABLE_CATEGORY_TEMPORARY)
+ {
+ thd->abort_on_warning= FALSE;
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN,
+ ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN),
+ field->field_name, table->s->table_name.str);
+ thd->abort_on_warning= abort_on_warning_saved;
+ }
if (value->save_in_field(field, 0) < 0)
goto err;
}
+ /* Update virtual fields*/
+ thd->abort_on_warning= FALSE;
+ if (table->vfield && update_virtual_fields(thd, table, TRUE))
+ goto err;
+ thd->abort_on_warning= abort_on_warning_saved;
DBUG_RETURN(thd->is_error());
err:
- if (table)
- table->auto_increment_field_not_null= FALSE;
+ thd->abort_on_warning= abort_on_warning_saved;
+ table->auto_increment_field_not_null= FALSE;
DBUG_RETURN(TRUE);
}
@@ -8521,9 +8623,22 @@ fill_record_n_invoke_before_triggers(THD *thd, Field **ptr,
Table_triggers_list *triggers,
enum trg_event_type event)
{
- return (fill_record(thd, ptr, values, ignore_errors) ||
- (triggers && triggers->process_triggers(thd, event,
- TRG_ACTION_BEFORE, TRUE)));
+ bool result;
+ result= (fill_record(thd, ptr, values, ignore_errors) ||
+ (triggers && triggers->process_triggers(thd, event,
+ TRG_ACTION_BEFORE, TRUE)));
+ /*
+ Re-calculate virtual fields to cater for cases when base columns are
+ updated by the triggers.
+ */
+ if (!result && triggers && *ptr)
+ {
+ TABLE *table= (*ptr)->table;
+ if (table->vfield)
+ result= update_virtual_fields(thd, table, TRUE);
+ }
+ return result;
+
}
@@ -8724,7 +8839,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
{
if (!in_use->killed)
{
- in_use->killed= THD::KILL_CONNECTION;
+ in_use->killed= THD::KILL_SYSTEM_THREAD;
pthread_mutex_lock(&in_use->mysys_var->mutex);
if (in_use->mysys_var->current_cond)
{
@@ -9058,7 +9173,7 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table
if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
! in_use->killed)
{
- in_use->killed= THD::KILL_CONNECTION;
+ in_use->killed= THD::KILL_SYSTEM_THREAD;
pthread_mutex_lock(&in_use->mysys_var->mutex);
if (in_use->mysys_var->current_cond)
{
diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h
index e07806a56ab..5385acc934f 100644
--- a/sql/sql_bitmap.h
+++ b/sql/sql_bitmap.h
@@ -60,13 +60,13 @@ public:
}
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); }
+ bool is_set(uint n) const { return bitmap_is_set(&map, n); }
+ bool is_prefix(uint n) const { return bitmap_is_prefix(&map, n); }
+ bool is_clear_all() const { return bitmap_is_clear_all(&map); }
+ bool is_set_all() const { return bitmap_is_set_all(&map); }
+ bool is_subset(const Bitmap& map2) const { return bitmap_is_subset(&map, &map2.map); }
+ bool is_overlapping(const Bitmap& map2) const { return bitmap_is_overlapping(&map, &map2.map); }
+ bool operator==(const Bitmap& map2) const { return bitmap_cmp(&map, &map2.map); }
char *print(char *buf) const
{
char *s=buf;
@@ -155,14 +155,14 @@ public:
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 test(map & (((ulonglong)1) << n)); }
- 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; }
+ bool is_set(uint n) const { return test(map & (((ulonglong)1) << n)); }
+ bool is_prefix(uint n) const { return map == (((ulonglong)1) << n)-1; }
+ bool is_clear_all() const { return map == (ulonglong)0; }
+ bool is_set_all() const { return map == ~(ulonglong)0; }
+ bool is_subset(const Bitmap<64>& map2) const { return !(map & ~map2.map); }
+ bool is_overlapping(const Bitmap<64>& map2) const { return (map & map2.map)!= 0; }
+ bool operator==(const Bitmap<64>& map2) const { return map == map2.map; }
+ char *print(char *buf) const { longlong2str(map,buf,16,1); return buf; }
ulonglong to_ulonglong() const { return map; }
class Iterator : public Table_map_iterator
{
diff --git a/sql/sql_builtin.cc.in b/sql/sql_builtin.cc.in
index 7ecd4918d7b..3ba47e3a14e 100644
--- a/sql/sql_builtin.cc.in
+++ b/sql/sql_builtin.cc.in
@@ -16,13 +16,12 @@
#include <my_global.h>
#include <mysql/plugin.h>
-typedef struct st_mysql_plugin builtin_plugin[];
+typedef struct st_maria_plugin builtin_maria_plugin[];
-extern builtin_plugin
- builtin_binlog_plugin@mysql_plugin_defs@;
+extern builtin_maria_plugin
+ builtin_maria_binlog_plugin, builtin_maria_mysql_password_plugin@maria_plugin_defs@;
-struct st_mysql_plugin *mysqld_builtins[]=
+struct st_maria_plugin *mariadb_builtins[]=
{
- builtin_binlog_plugin@mysql_plugin_defs@,(struct st_mysql_plugin *)0
+ builtin_maria_binlog_plugin, builtin_maria_mysql_password_plugin@maria_plugin_defs@,(struct st_maria_plugin *)0
};
-
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 4a073aa0485..de9447fa356 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -742,7 +742,7 @@ inline void Query_cache_query::lock_writing()
remove it.
*/
-my_bool Query_cache_query::try_lock_writing()
+bool Query_cache_query::try_lock_writing()
{
DBUG_ENTER("Query_cache_block::try_lock_writing");
if (rw_trywrlock(&lock)!=0)
@@ -2279,6 +2279,18 @@ void Query_cache::free_cache()
{
DBUG_ENTER("Query_cache::free_cache");
+ /* Destroy locks */
+ Query_cache_block *block= queries_blocks;
+ if (block)
+ {
+ do
+ {
+ Query_cache_query *query= block->query();
+ rwlock_destroy(&query->lock);
+ block= block->next;
+ } while (block != queries_blocks);
+ }
+
my_free((uchar*) cache, MYF(MY_ALLOW_ZERO_PTR));
make_disabled();
hash_free(&queries);
diff --git a/sql/sql_cache.h b/sql/sql_cache.h
index 1d98ae2e094..a3a91127129 100644
--- a/sql/sql_cache.h
+++ b/sql/sql_cache.h
@@ -124,7 +124,7 @@ struct Query_cache_block
block_type type;
TABLE_COUNTER_TYPE n_tables; // number of tables in query
- inline my_bool is_free(void) { return type == FREE; }
+ inline bool is_free(void) { return type == FREE; }
void init(ulong length);
void destroy();
inline uint headers_len();
@@ -165,7 +165,7 @@ struct Query_cache_query
}
void lock_writing();
void lock_reading();
- my_bool try_lock_writing();
+ bool try_lock_writing();
void unlock_writing();
void unlock_reading();
};
@@ -315,7 +315,7 @@ protected:
uint mem_bin_num, mem_bin_steps; // See at init_cache & find_bin
- my_bool initialized;
+ bool initialized;
/* Exclude/include from cyclic double linked list */
static void double_linked_list_exclude(Query_cache_block *point,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 49710d5dbcc..a31c4728720 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -110,6 +110,7 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root)
key_create_info(rhs.key_create_info),
columns(rhs.columns, mem_root),
name(rhs.name),
+ option_list(rhs.option_list),
generated(rhs.generated)
{
list_copy_and_replace_each_value(columns, mem_root);
@@ -199,6 +200,59 @@ bool foreign_key_prefix(Key *a, Key *b)
#endif
}
+/*
+ @brief
+ Check if the foreign key options are compatible with the specification
+ of the columns on which the key is created
+
+ @retval
+ FALSE The foreign key options are compatible with key columns
+ @retval
+ TRUE Otherwise
+*/
+bool Foreign_key::validate(List<Create_field> &table_fields)
+{
+ Create_field *sql_field;
+ Key_part_spec *column;
+ List_iterator<Key_part_spec> cols(columns);
+ List_iterator<Create_field> it(table_fields);
+ DBUG_ENTER("Foreign_key::validate");
+ while ((column= cols++))
+ {
+ it.rewind();
+ while ((sql_field= it++) &&
+ my_strcasecmp(system_charset_info,
+ column->field_name,
+ sql_field->field_name)) {}
+ if (!sql_field)
+ {
+ my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
+ DBUG_RETURN(TRUE);
+ }
+ if (type == Key::FOREIGN_KEY && sql_field->vcol_info)
+ {
+ if (delete_opt == FK_OPTION_SET_NULL)
+ {
+ my_error(ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN, MYF(0),
+ "ON DELETE SET NULL");
+ DBUG_RETURN(TRUE);
+ }
+ if (update_opt == FK_OPTION_SET_NULL)
+ {
+ my_error(ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN, MYF(0),
+ "ON UPDATE SET NULL");
+ DBUG_RETURN(TRUE);
+ }
+ if (update_opt == FK_OPTION_CASCADE)
+ {
+ my_error(ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN, MYF(0),
+ "ON UPDATE CASCADE");
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
/****************************************************************************
** Thread specific functions
@@ -610,7 +664,7 @@ THD::THD()
Open_tables_state(refresh_version), rli_fake(0),
lock_id(&main_lock_id),
user_time(0), in_sub_stmt(0),
- sql_log_bin_toplevel(false),
+ sql_log_bin_toplevel(false), log_all_errors(0),
binlog_table_maps(0), binlog_flags(0UL),
table_map_for_update(0),
arg_of_last_insert_id_function(FALSE),
@@ -683,6 +737,7 @@ THD::THD()
mysys_var=0;
binlog_evt_union.do_union= FALSE;
enable_slow_log= 0;
+
#ifndef DBUG_OFF
dbug_sentry=THD_SENTRY_MAGIC;
#endif
@@ -750,6 +805,7 @@ THD::THD()
thr_lock_owner_init(&main_lock_id, &lock_info);
m_internal_handler= NULL;
+ arena_for_cached_items= 0;
m_binlog_invoker= FALSE;
memset(&invoker_user, 0, sizeof(invoker_user));
memset(&invoker_host, 0, sizeof(invoker_host));
@@ -758,6 +814,7 @@ THD::THD()
void THD::push_internal_handler(Internal_error_handler *handler)
{
+ DBUG_ENTER("THD::push_internal_handler");
if (m_internal_handler)
{
handler->m_prev_internal_handler= m_internal_handler;
@@ -767,6 +824,7 @@ void THD::push_internal_handler(Internal_error_handler *handler)
{
m_internal_handler= handler;
}
+ DBUG_VOID_RETURN;
}
@@ -786,10 +844,11 @@ bool THD::handle_error(uint sql_errno, const char *message,
Internal_error_handler *THD::pop_internal_handler()
{
+ DBUG_ENTER("THD::pop_internal_handler");
DBUG_ASSERT(m_internal_handler != NULL);
Internal_error_handler *popped_handler= m_internal_handler;
m_internal_handler= m_internal_handler->m_prev_internal_handler;
- return popped_handler;
+ DBUG_RETURN(popped_handler);
}
extern "C"
@@ -886,14 +945,65 @@ void THD::init(void)
update_charset();
reset_current_stmt_binlog_row_based();
bzero((char *) &status_var, sizeof(status_var));
+ bzero((char *) &org_status_var, sizeof(org_status_var));
sql_log_bin_toplevel= options & OPTION_BIN_LOG;
-
+ select_commands= update_commands= other_commands= 0;
+ /* Set to handle counting of aborted connections */
+ userstat_running= opt_userstat_running;
+ last_global_update_time= current_connect_time= time(NULL);
#if defined(ENABLED_DEBUG_SYNC)
/* Initialize the Debug Sync Facility. See debug_sync.cc. */
debug_sync_init_thread(this);
#endif /* defined(ENABLED_DEBUG_SYNC) */
}
+
+/* Updates some status variables to be used by update_global_user_stats */
+
+void THD::update_stats(void)
+{
+ /* sql_command == SQLCOM_END in case of parse errors or quit */
+ if (lex->sql_command != SQLCOM_END)
+ {
+ /* A SQL query. */
+ if (lex->sql_command == SQLCOM_SELECT)
+ select_commands++;
+ else if (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND)
+ {
+ /* Ignore 'SHOW ' commands */
+ }
+ else if (is_update_query(lex->sql_command))
+ update_commands++;
+ else
+ other_commands++;
+ }
+}
+
+
+void THD::update_all_stats()
+{
+ time_t save_time;
+ ulonglong end_cpu_time, end_utime;
+ double busy_time, cpu_time;
+
+ /* This is set at start of query if opt_userstat_running was set */
+ if (!userstat_running)
+ return;
+
+ end_cpu_time= my_getcputime();
+ end_utime= my_micro_time_and_time(&save_time);
+ busy_time= (end_utime - start_utime) / 1000000.0;
+ cpu_time= (end_cpu_time - start_cpu_time) / 10000000.0;
+ /* In case there are bad values, 2629743 is the #seconds in a month. */
+ if (cpu_time > 2629743.0)
+ cpu_time= 0;
+ status_var_add(status_var.cpu_time, cpu_time);
+ status_var_add(status_var.busy_time, busy_time);
+
+ update_global_user_stats(this, TRUE, save_time);
+ userstat_running= 0;
+}
+
/*
Init THD for query processing.
@@ -971,6 +1081,11 @@ void THD::cleanup(void)
lock=locked_tables; locked_tables=0;
close_thread_tables(this);
}
+ if (user_connect)
+ {
+ decrease_user_connections(user_connect);
+ user_connect= 0; // Safety
+ }
wt_thd_destroy(&transaction.wt);
#if defined(ENABLED_DEBUG_SYNC)
@@ -1064,9 +1179,8 @@ THD::~THD()
from_var from this array
NOTES
- This function assumes that all variables are long/ulong.
- If this assumption will change, then we have to explictely add
- the other variables after the while loop
+ This function assumes that all variables at start are long/ulong and
+ other types are handled explicitely
*/
void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var)
@@ -1079,8 +1193,14 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var)
while (to != end)
*(to++)+= *(from++);
- to_var->bytes_received+= from_var->bytes_received;
- to_var->bytes_sent+= from_var->bytes_sent;
+ /* Handle the not ulong variables. See end of system_status_var */
+ to_var->bytes_received+= from_var->bytes_received;
+ to_var->bytes_sent+= from_var->bytes_sent;
+ to_var->rows_read+= from_var->rows_read;
+ to_var->rows_sent+= from_var->rows_sent;
+ to_var->binlog_bytes_written+= from_var->binlog_bytes_written;
+ to_var->cpu_time+= from_var->cpu_time;
+ to_var->busy_time+= from_var->busy_time;
}
/*
@@ -1093,7 +1213,8 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var)
dec_var minus this array
NOTE
- This function assumes that all variables are long/ulong.
+ This function assumes that all variables at start are long/ulong and
+ other types are handled explicitely
*/
void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
@@ -1107,8 +1228,15 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
while (to != end)
*(to++)+= *(from++) - *(dec++);
- to_var->bytes_received+= from_var->bytes_received - dec_var->bytes_received;;
- to_var->bytes_sent+= from_var->bytes_sent - dec_var->bytes_sent;
+ to_var->bytes_received+= from_var->bytes_received -
+ dec_var->bytes_received;
+ to_var->bytes_sent+= from_var->bytes_sent - dec_var->bytes_sent;
+ to_var->rows_read+= from_var->rows_read - dec_var->rows_read;
+ to_var->rows_sent+= from_var->rows_sent - dec_var->rows_sent;
+ to_var->binlog_bytes_written+= from_var->binlog_bytes_written -
+ dec_var->binlog_bytes_written;
+ to_var->cpu_time+= from_var->cpu_time - dec_var->cpu_time;
+ to_var->busy_time+= from_var->busy_time - dec_var->busy_time;
}
#define SECONDS_TO_WAIT_FOR_KILL 2
@@ -1127,6 +1255,15 @@ void THD::awake(THD::killed_state state_to_set)
THD_CHECK_SENTRY(this);
safe_mutex_assert_owner(&LOCK_thd_data);
+ if (global_system_variables.log_warnings > 3)
+ {
+ Security_context *sctx= security_ctx;
+ sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
+ thread_id,(db ? db : "unconnected"),
+ sctx->user ? sctx->user : "unauthenticated",
+ sctx->host_or_ip,
+ "KILLED");
+ }
killed= state_to_set;
if (state_to_set != THD::KILL_QUERY)
{
@@ -2472,26 +2609,32 @@ bool select_max_min_finder_subselect::cmp_real()
{
Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0);
double val1= cache->val_real(), val2= maxmin->val_real();
+
+ /* Ignore NULLs for ANY and keep them for ALL subqueries */
+ if (cache->null_value)
+ return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value);
+ if (maxmin->null_value)
+ return !is_all;
+
if (fmax)
- return (cache->null_value && !maxmin->null_value) ||
- (!cache->null_value && !maxmin->null_value &&
- val1 > val2);
- return (maxmin->null_value && !cache->null_value) ||
- (!cache->null_value && !maxmin->null_value &&
- val1 < val2);
+ return(val1 > val2);
+ return (val1 < val2);
}
bool select_max_min_finder_subselect::cmp_int()
{
Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0);
longlong val1= cache->val_int(), val2= maxmin->val_int();
+
+ /* Ignore NULLs for ANY and keep them for ALL subqueries */
+ if (cache->null_value)
+ return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value);
+ if (maxmin->null_value)
+ return !is_all;
+
if (fmax)
- return (cache->null_value && !maxmin->null_value) ||
- (!cache->null_value && !maxmin->null_value &&
- val1 > val2);
- return (maxmin->null_value && !cache->null_value) ||
- (!cache->null_value && !maxmin->null_value &&
- val1 < val2);
+ return(val1 > val2);
+ return (val1 < val2);
}
bool select_max_min_finder_subselect::cmp_decimal()
@@ -2499,13 +2642,16 @@ bool select_max_min_finder_subselect::cmp_decimal()
Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0);
my_decimal cval, *cvalue= cache->val_decimal(&cval);
my_decimal mval, *mvalue= maxmin->val_decimal(&mval);
+
+ /* Ignore NULLs for ANY and keep them for ALL subqueries */
+ if (cache->null_value)
+ return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value);
+ if (maxmin->null_value)
+ return !is_all;
+
if (fmax)
- return (cache->null_value && !maxmin->null_value) ||
- (!cache->null_value && !maxmin->null_value &&
- my_decimal_cmp(cvalue, mvalue) > 0) ;
- return (maxmin->null_value && !cache->null_value) ||
- (!cache->null_value && !maxmin->null_value &&
- my_decimal_cmp(cvalue,mvalue) < 0);
+ return (my_decimal_cmp(cvalue, mvalue) > 0) ;
+ return (my_decimal_cmp(cvalue,mvalue) < 0);
}
bool select_max_min_finder_subselect::cmp_str()
@@ -2518,13 +2664,16 @@ bool select_max_min_finder_subselect::cmp_str()
*/
val1= cache->val_str(&buf1);
val2= maxmin->val_str(&buf1);
+
+ /* Ignore NULLs for ANY and keep them for ALL subqueries */
+ if (cache->null_value)
+ return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value);
+ if (maxmin->null_value)
+ return !is_all;
+
if (fmax)
- return (cache->null_value && !maxmin->null_value) ||
- (!cache->null_value && !maxmin->null_value &&
- sortcmp(val1, val2, cache->collation.collation) > 0) ;
- return (maxmin->null_value && !cache->null_value) ||
- (!cache->null_value && !maxmin->null_value &&
- sortcmp(val1, val2, cache->collation.collation) < 0);
+ return (sortcmp(val1, val2, cache->collation.collation) > 0) ;
+ return (sortcmp(val1, val2, cache->collation.collation) < 0);
}
int select_exists_subselect::send_data(List<Item> &items)
@@ -2952,7 +3101,8 @@ void thd_increment_bytes_sent(ulong length)
{
THD *thd=current_thd;
if (likely(thd != 0))
- { /* current_thd==0 when close_connection() calls net_send_error() */
+ {
+ /* current_thd == 0 when close_connection() calls net_send_error() */
thd->status_var.bytes_sent+= length;
}
}
@@ -2978,9 +3128,9 @@ void THD::set_status_var_init()
void Security_context::init()
{
- host= user= priv_user= ip= 0;
+ host= user= ip= 0;
host_or_ip= "connecting host";
- priv_host[0]= '\0';
+ priv_user[0]= priv_host[0]= '\0';
master_access= 0;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
db_access= NO_ACCESS;
@@ -3004,8 +3154,7 @@ void Security_context::skip_grants()
/* privileges for the user are unknown everything is allowed */
host_or_ip= (char *)"";
master_access= ~NO_ACCESS;
- priv_user= (char *)"";
- *priv_host= '\0';
+ *priv_user= *priv_host= '\0';
}
@@ -3047,7 +3196,7 @@ bool Security_context::set_user(char *user_arg)
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
+ ultimately by means of acl_getroot), and make the
local instance active in the thread by re-setting
thd->security_ctx pointer.
@@ -3081,19 +3230,12 @@ change_security_context(THD *thd,
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 ||
+ needs_change= (strcmp(definer_user->str, thd->security_ctx->priv_user) ||
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,
+ if (acl_getroot(this, definer_user->str, definer_host->str,
definer_host->str, db->str))
{
my_error(ER_NO_SUCH_USER, MYF(0), definer_user->str,
@@ -3167,7 +3309,10 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup)
*/
extern "C" int thd_killed(const MYSQL_THD thd)
{
- return(thd->killed);
+ if (thd->killed == THD::NOT_KILLED || thd->killed == THD::KILL_BAD_DATA ||
+ thd->killed == THD::KILL_SYSTEM_THREAD)
+ return 0;
+ return thd->killed;
}
/**
@@ -3182,7 +3327,7 @@ extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd)
#ifdef INNODB_COMPATIBILITY_HOOKS
-extern "C" struct charset_info_st *thd_charset(MYSQL_THD thd)
+extern "C" const struct charset_info_st *thd_charset(MYSQL_THD thd)
{
return(thd->charset());
}
@@ -3406,8 +3551,9 @@ void THD::get_definer(LEX_USER *definer)
{
definer->user = invoker_user;
definer->host= invoker_host;
- definer->password.str= NULL;
- definer->password.length= 0;
+ definer->password= null_lex_str;
+ definer->plugin= empty_lex_str;
+ definer->auth= empty_lex_str;
}
else
#endif
@@ -3973,8 +4119,8 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
int errcode)
{
DBUG_ENTER("THD::binlog_query");
- DBUG_PRINT("enter", ("qtype: %s query: '%s'",
- show_query_type(qtype), query_arg));
+ DBUG_PRINT("enter", ("qtype: %s query: '%-.*s'",
+ show_query_type(qtype), (int) query_len, query_arg));
DBUG_ASSERT(query_arg && mysql_bin_log.is_open());
/*
@@ -4010,7 +4156,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
{
sql_print_warning("%s Statement: %.*s",
ER(ER_BINLOG_UNSAFE_STATEMENT),
- MYSQL_ERRMSG_SIZE, query_arg);
+ (int) min(MYSQL_ERRMSG_SIZE, query_len), query_arg);
binlog_flags|= BINLOG_FLAG_UNSAFE_STMT_PRINTED;
}
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 40a5d61e433..4bf1bc3964c 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -209,13 +209,15 @@ public:
KEY_CREATE_INFO key_create_info;
List<Key_part_spec> columns;
const char *name;
+ engine_option_value *option_list;
bool generated;
Key(enum Keytype type_par, const char *name_arg,
KEY_CREATE_INFO *key_info_arg,
- bool generated_arg, List<Key_part_spec> &cols)
+ bool generated_arg, List<Key_part_spec> &cols,
+ engine_option_value *create_opt)
:type(type_par), key_create_info(*key_info_arg), columns(cols),
- name(name_arg), generated(generated_arg)
+ name(name_arg), option_list(create_opt), generated(generated_arg)
{}
Key(const Key &rhs, MEM_ROOT *mem_root);
virtual ~Key() {}
@@ -244,7 +246,7 @@ public:
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),
+ :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL),
ref_table(table), ref_columns(ref_cols),
delete_opt(delete_opt_arg), update_opt(update_opt_arg),
match_opt(match_opt_arg)
@@ -256,6 +258,8 @@ public:
*/
virtual Key *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) Foreign_key(*this, mem_root); }
+ /* Used to validate foreign key options */
+ bool validate(List<Create_field> &table_fields);
};
typedef struct st_mysql_lock
@@ -295,6 +299,9 @@ struct system_variables
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.
+
+ Note that one should use my_bool instead of bool here, as the variables
+ are used with my_getopt.c
*/
ulong dynamic_variables_version;
char* dynamic_variables_ptr;
@@ -339,7 +346,6 @@ struct system_variables
ulong read_rnd_buff_size;
ulong div_precincrement;
ulong sortbuff_size;
- ulong thread_handling;
ulong tx_isolation;
ulong completion_type;
/* Determines which non-standard SQL behaviour should be enabled */
@@ -478,25 +484,31 @@ typedef struct system_status_var
ulong com_stmt_fetch;
ulong com_stmt_reset;
ulong com_stmt_close;
+ ulong empty_queries;
+ ulong access_denied_errors;
+ ulong lost_connections;
/*
Number of statements sent from the client
*/
ulong questions;
-
- ulonglong bytes_received;
- ulonglong bytes_sent;
/*
IMPORTANT!
SEE last_system_status_var DEFINITION BELOW.
Below 'last_system_status_var' are all variables that cannot be handled
automatically by add_to_status()/add_diff_to_status().
*/
+ ulonglong bytes_received;
+ ulonglong bytes_sent;
+ ulonglong rows_read;
+ ulonglong rows_sent;
+ ulonglong binlog_bytes_written;
double last_query_cost;
+ double cpu_time, busy_time;
} STATUS_VAR;
/*
This is used for 'SHOW STATUS'. It must be updated to the last ulong
- variable in system_status_var which is makes sens to add to the global
+ variable in system_status_var which is makes sense to add to the global
counter
*/
@@ -669,7 +681,7 @@ public:
Server_side_cursor *cursor;
inline char *query() { return query_string.str; }
- inline uint32 query_length() { return query_string.length; }
+ inline uint32 query_length() { return (uint32)query_string.length; }
void set_query_inner(char *query_arg, uint32 query_length_arg);
/**
@@ -804,7 +816,8 @@ public:
priv_user - The user privilege we are using. May be "" for anonymous user.
ip - client IP
*/
- char *host, *user, *priv_user, *ip;
+ char *host, *user, *ip;
+ char priv_user[USERNAME_LENGTH];
/* The host privilege we are using */
char priv_host[MAX_HOSTNAME];
/* points to host if host is available, otherwise points to ip */
@@ -1353,6 +1366,7 @@ public:
struct my_rnd_struct rand; // used for authentication
struct system_variables variables; // Changeable local variables
struct system_status_var status_var; // Per thread statistic vars
+ struct system_status_var org_status_var; // For user statistics
struct system_status_var *initial_status_var; /* used by show status */
THR_LOCK_INFO lock_info; // Locking info of this thread
THR_LOCK_OWNER main_lock_id; // To use for conventional queries
@@ -1453,6 +1467,10 @@ public:
uint in_sub_stmt;
/* TRUE when the current top has SQL_LOG_BIN ON */
bool sql_log_bin_toplevel;
+ /* True when opt_userstat_running is set at start of query */
+ bool userstat_running;
+ /* True if we want to log all errors */
+ bool log_all_errors;
/* container for handler's private per-connection data */
Ha_data ha_data[MAX_HA];
@@ -1815,7 +1833,9 @@ public:
NOT_KILLED=0,
KILL_BAD_DATA=1,
KILL_CONNECTION=ER_SERVER_SHUTDOWN,
+ KILL_SYSTEM_THREAD=ER_NEW_ABORTING_CONNECTION, /* Kill connection nicely */
KILL_QUERY=ER_QUERY_INTERRUPTED,
+ KILL_SERVER, /* Placeholder for shortdown */
KILLED_NO_VALUE /* means neither of the states */
};
killed_state volatile killed;
@@ -1880,7 +1900,7 @@ public:
bool no_warnings_for_error; /* no warnings on call to my_error() */
/* set during loop of derived table processing */
bool derived_tables_processing;
- my_bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */
+ bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */
sp_rcontext *spcont; // SP runtime context
sp_cache *sp_proc_cache;
@@ -1896,6 +1916,21 @@ public:
*/
LOG_INFO* current_linfo;
NET* slave_net; // network connection from slave -> m.
+
+ /*
+ Used to update global user stats. The global user stats are updated
+ occasionally with the 'diff' variables. After the update, the 'diff'
+ variables are reset to 0.
+ */
+ /* Time when the current thread connected to MySQL. */
+ time_t current_connect_time;
+ /* Last time when THD stats were updated in global_user_stats. */
+ time_t last_global_update_time;
+ /* Number of commands not reflected in global_user_stats yet. */
+ uint select_commands, update_commands, other_commands;
+ ulonglong start_cpu_time;
+ ulonglong start_bytes_received;
+
/* Used by the sys_var class to store temporary values */
union
{
@@ -1961,6 +1996,8 @@ public:
alloc_root.
*/
void init_for_queries();
+ void update_all_stats();
+ void update_stats(void);
void change_user(void);
void cleanup(void);
void cleanup_after_query();
@@ -2395,6 +2432,27 @@ public:
LEX_STRING get_invoker_user() { return invoker_user; }
LEX_STRING get_invoker_host() { return invoker_host; }
bool has_invoker() { return invoker_user.length > 0; }
+
+private:
+ /*
+ This reference points to the table arena when the expression
+ for a virtual column is being evaluated
+ */
+ Query_arena *arena_for_cached_items;
+
+public:
+ void reset_arena_for_cached_items(Query_arena *new_arena)
+ {
+ arena_for_cached_items= new_arena;
+ }
+ Query_arena *switch_to_arena_for_cached_items(Query_arena *backup)
+ {
+ if (!arena_for_cached_items)
+ return 0;
+ set_n_backup_active_arena(arena_for_cached_items, backup);
+ return backup;
+ }
+
private:
/** The current internal error handler for this thread, or NULL. */
Internal_error_handler *m_internal_handler;
@@ -2435,7 +2493,6 @@ private:
LEX_STRING invoker_host;
};
-
/** A short cut for thd->main_da.set_ok_status(). */
inline void
@@ -2734,7 +2791,7 @@ public:
};
-#if defined(WITH_MARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
+#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
#include <maria.h>
#define ENGINE_COLUMNDEF MARIA_COLUMNDEF
#else
@@ -2872,9 +2929,11 @@ class select_max_min_finder_subselect :public select_subselect
Item_cache *cache;
bool (select_max_min_finder_subselect::*op)();
bool fmax;
+ bool is_all;
public:
- select_max_min_finder_subselect(Item_subselect *item_arg, bool mx)
- :select_subselect(item_arg), cache(0), fmax(mx)
+ select_max_min_finder_subselect(Item_subselect *item_arg, bool mx,
+ bool all)
+ :select_subselect(item_arg), cache(0), fmax(mx), is_all(all)
{}
void cleanup();
int send_data(List<Item> &items);
@@ -2969,10 +3028,10 @@ class user_var_entry
Item_result type;
bool unsigned_flag;
- double val_real(my_bool *null_value);
- longlong val_int(my_bool *null_value) const;
- String *val_str(my_bool *null_value, String *str, uint decimals);
- my_decimal *val_decimal(my_bool *null_value, my_decimal *result);
+ double val_real(bool *null_value);
+ longlong val_int(bool *null_value) const;
+ String *val_str(bool *null_value, String *str, uint decimals);
+ my_decimal *val_decimal(bool *null_value, my_decimal *result);
DTCollation collation;
};
@@ -3179,4 +3238,150 @@ 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);
+/*
+ inline handler methods that need to know TABLE and THD structures
+*/
+inline void handler::increment_statistics(ulong SSV::*offset) const
+{
+ status_var_increment(table->in_use->status_var.*offset);
+}
+
+inline void handler::decrement_statistics(ulong SSV::*offset) const
+{
+ status_var_decrement(table->in_use->status_var.*offset);
+}
+
+inline int handler::ha_index_read_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ DBUG_ASSERT(inited==INDEX);
+ increment_statistics(&SSV::ha_read_key_count);
+ int error= index_read_map(buf, key, keypart_map, find_flag);
+ if (!error)
+ update_index_statistics();
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+inline int handler::ha_index_read_idx_map(uchar * buf, uint index,
+ const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ increment_statistics(&SSV::ha_read_key_count);
+ int error= index_read_idx_map(buf, index, key, keypart_map, find_flag);
+ if (!error)
+ {
+ rows_read++;
+ index_rows_read[index]++;
+ }
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+inline int handler::ha_index_next(uchar * buf)
+{
+ DBUG_ASSERT(inited==INDEX);
+ increment_statistics(&SSV::ha_read_next_count);
+ int error= index_next(buf);
+ if (!error)
+ update_index_statistics();
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+inline int handler::ha_index_prev(uchar * buf)
+{
+ DBUG_ASSERT(inited==INDEX);
+ increment_statistics(&SSV::ha_read_prev_count);
+ int error= index_prev(buf);
+ if (!error)
+ update_index_statistics();
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+inline int handler::ha_index_first(uchar * buf)
+{
+ DBUG_ASSERT(inited==INDEX);
+ increment_statistics(&SSV::ha_read_first_count);
+ int error= index_first(buf);
+ if (!error)
+ update_index_statistics();
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+inline int handler::ha_index_last(uchar * buf)
+{
+ DBUG_ASSERT(inited==INDEX);
+ increment_statistics(&SSV::ha_read_last_count);
+ int error= index_last(buf);
+ if (!error)
+ update_index_statistics();
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+inline int handler::ha_index_next_same(uchar *buf, const uchar *key,
+ uint keylen)
+{
+ DBUG_ASSERT(inited==INDEX);
+ increment_statistics(&SSV::ha_read_next_count);
+ int error= index_next_same(buf, key, keylen);
+ if (!error)
+ update_index_statistics();
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+inline int handler::ha_ft_read(uchar *buf)
+{
+ int error= ft_read(buf);
+ if (!error)
+ rows_read++;
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+inline int handler::ha_rnd_next(uchar *buf)
+{
+ increment_statistics(&SSV::ha_read_rnd_next_count);
+ int error= rnd_next(buf);
+ if (!error)
+ rows_read++;
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+inline int handler::ha_rnd_pos(uchar *buf, uchar *pos)
+{
+ increment_statistics(&SSV::ha_read_rnd_count);
+ int error= rnd_pos(buf, pos);
+ if (!error)
+ rows_read++;
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+inline int handler::ha_rnd_pos_by_record(uchar *buf)
+{
+ int error= rnd_pos_by_record(buf);
+ if (!error)
+ rows_read++;
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+inline int handler::ha_read_first_row(uchar *buf, uint primary_key)
+{
+ int error= read_first_row(buf, primary_key);
+ if (!error)
+ rows_read++;
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+
#endif /* MYSQL_SERVER */
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 7dd15ea731f..e32dde04de0 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -22,23 +22,12 @@
#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 */
+HASH global_user_stats, global_client_stats, global_table_stats;
+HASH global_index_stats;
+/* Protects the above global stats */
+extern pthread_mutex_t LOCK_global_user_client_stats;
+extern pthread_mutex_t LOCK_global_table_stats;
+extern pthread_mutex_t LOCK_global_index_stats;
#ifdef __WIN__
extern void win_install_sigabrt_handler();
@@ -51,9 +40,9 @@ extern void win_install_sigabrt_handler();
#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 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;
@@ -62,6 +51,7 @@ static int get_or_create_user_conn(THD *thd, const char *user,
DBUG_ASSERT(user != 0);
DBUG_ASSERT(host != 0);
+ DBUG_ASSERT(thd->user_connect == 0);
user_len= strlen(user);
temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1;
@@ -119,10 +109,9 @@ end:
1 error
*/
-static
int check_for_max_user_connections(THD *thd, USER_CONN *uc)
{
- int error=0;
+ int error= 1;
DBUG_ENTER("check_for_max_user_connections");
(void) pthread_mutex_lock(&LOCK_user_conn);
@@ -130,7 +119,6 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
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);
@@ -140,7 +128,6 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
"max_user_connections",
(long) uc->user_resources.user_conn);
- error= 1;
goto end;
}
if (uc->user_resources.conn_per_hour &&
@@ -149,10 +136,10 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
"max_connections_per_hour",
(long) uc->user_resources.conn_per_hour);
- error=1;
goto end;
}
uc->conn_per_hour++;
+ error= 0;
end:
if (error)
@@ -272,241 +259,6 @@ end:
#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));
- 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));
- 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)
- {
- bool count_ok= 1;
-
- if (!(thd->main_security_ctx.master_access & SUPER_ACL))
- {
- pthread_mutex_lock(&LOCK_connection_count);
- count_ok= (*thd->scheduler->connection_count <=
- *thd->scheduler->max_connections);
- 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);
- /*
- Allow the network layer to skip big packets. Although a malicious
- authenticated session might use this to trick the server to read
- big packets indefinitely, this is a previously established behavior
- that needs to be preserved as to not break backwards compatibility.
- */
- thd->net.net_skip_rest_factor= 2; // skip at most 2*max_packet_size
- 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.
@@ -529,10 +281,14 @@ extern "C" void free_user(struct user_conn *uc)
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);
+ if (hash_init(&hash_user_connections,system_charset_info,max_connections,
+ 0,0,
+ (hash_get_key) get_key_conn, (hash_free_key) free_user,
+ 0))
+ {
+ sql_print_error("Initializing hash_user_connections failed.");
+ exit(1);
+ }
#endif
}
@@ -585,6 +341,445 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0)
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
}
+/*****************************************************************************
+ Handle users statistics
+*****************************************************************************/
+
+/* 'mysql_system_user' is used for when the user is not defined for a THD. */
+static const char mysql_system_user[]= "#mysql_system#";
+
+// Returns 'user' if it's not NULL. Returns 'mysql_system_user' otherwise.
+static const char * get_valid_user_string(char* user)
+{
+ return user ? user : mysql_system_user;
+}
+
+/*
+ Returns string as 'IP' for the client-side of the connection represented by
+ 'client'. Does not allocate memory. May return "".
+*/
+
+static const char *get_client_host(THD *client)
+{
+ return client->security_ctx->host_or_ip[0] ?
+ client->security_ctx->host_or_ip :
+ client->security_ctx->host ? client->security_ctx->host : "";
+}
+
+extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= user_stats->user_name_length;
+ return (uchar*) user_stats->user;
+}
+
+void free_user_stats(USER_STATS* user_stats)
+{
+ my_free(user_stats, MYF(0));
+}
+
+void init_user_stats(USER_STATS *user_stats,
+ const char *user,
+ size_t user_length,
+ const char *priv_user,
+ uint total_connections,
+ uint concurrent_connections,
+ time_t connected_time,
+ double busy_time,
+ double cpu_time,
+ ulonglong bytes_received,
+ ulonglong bytes_sent,
+ ulonglong binlog_bytes_written,
+ ha_rows rows_sent,
+ ha_rows rows_read,
+ ha_rows rows_inserted,
+ ha_rows rows_deleted,
+ ha_rows rows_updated,
+ ulonglong select_commands,
+ ulonglong update_commands,
+ ulonglong other_commands,
+ ulonglong commit_trans,
+ ulonglong rollback_trans,
+ ulonglong denied_connections,
+ ulonglong lost_connections,
+ ulonglong access_denied_errors,
+ ulonglong empty_queries)
+{
+ DBUG_ENTER("init_user_stats");
+ DBUG_PRINT("enter", ("user: %s priv_user: %s", user, priv_user));
+
+ user_length= min(user_length, sizeof(user_stats->user)-1);
+ memcpy(user_stats->user, user, user_length);
+ user_stats->user[user_length]= 0;
+ user_stats->user_name_length= user_length;
+ strmake(user_stats->priv_user, priv_user, sizeof(user_stats->priv_user)-1);
+
+ user_stats->total_connections= total_connections;
+ user_stats->concurrent_connections= concurrent_connections;
+ user_stats->connected_time= connected_time;
+ user_stats->busy_time= busy_time;
+ user_stats->cpu_time= cpu_time;
+ user_stats->bytes_received= bytes_received;
+ user_stats->bytes_sent= bytes_sent;
+ user_stats->binlog_bytes_written= binlog_bytes_written;
+ user_stats->rows_sent= rows_sent;
+ user_stats->rows_updated= rows_updated;
+ user_stats->rows_read= rows_read;
+ user_stats->select_commands= select_commands;
+ user_stats->update_commands= update_commands;
+ user_stats->other_commands= other_commands;
+ user_stats->commit_trans= commit_trans;
+ user_stats->rollback_trans= rollback_trans;
+ user_stats->denied_connections= denied_connections;
+ user_stats->lost_connections= lost_connections;
+ user_stats->access_denied_errors= access_denied_errors;
+ user_stats->empty_queries= empty_queries;
+ DBUG_VOID_RETURN;
+}
+
+
+#ifdef COMPLEAT_PATCH_NOT_ADDED_YET
+
+void add_user_stats(USER_STATS *user_stats,
+ uint total_connections,
+ uint concurrent_connections,
+ time_t connected_time,
+ double busy_time,
+ double cpu_time,
+ ulonglong bytes_received,
+ ulonglong bytes_sent,
+ ulonglong binlog_bytes_written,
+ ha_rows rows_sent,
+ ha_rows rows_read,
+ ha_rows rows_inserted,
+ ha_rows rows_deleted,
+ ha_rows rows_updated,
+ ulonglong select_commands,
+ ulonglong update_commands,
+ ulonglong other_commands,
+ ulonglong commit_trans,
+ ulonglong rollback_trans,
+ ulonglong denied_connections,
+ ulonglong lost_connections,
+ ulonglong access_denied_errors,
+ ulonglong empty_queries)
+{
+ user_stats->total_connections+= total_connections;
+ user_stats->concurrent_connections+= concurrent_connections;
+ user_stats->connected_time+= connected_time;
+ user_stats->busy_time+= busy_time;
+ user_stats->cpu_time+= cpu_time;
+ user_stats->bytes_received+= bytes_received;
+ user_stats->bytes_sent+= bytes_sent;
+ user_stats->binlog_bytes_written+= binlog_bytes_written;
+ user_stats->rows_sent+= rows_sent;
+ user_stats->rows_inserted+= rows_inserted;
+ user_stats->rows_deleted+= rows_deleted;
+ user_stats->rows_updated+= rows_updated;
+ user_stats->rows_read+= rows_read;
+ user_stats->select_commands+= select_commands;
+ user_stats->update_commands+= update_commands;
+ user_stats->other_commands+= other_commands;
+ user_stats->commit_trans+= commit_trans;
+ user_stats->rollback_trans+= rollback_trans;
+ user_stats->denied_connections+= denied_connections;
+ user_stats->lost_connections+= lost_connections;
+ user_stats->access_denied_errors+= access_denied_errors;
+ user_stats->empty_queries+= empty_queries;
+}
+#endif
+
+
+void init_global_user_stats(void)
+{
+ if (hash_init(&global_user_stats, system_charset_info, max_connections,
+ 0, 0, (hash_get_key) get_key_user_stats,
+ (hash_free_key)free_user_stats, 0))
+ {
+ sql_print_error("Initializing global_user_stats failed.");
+ exit(1);
+ }
+}
+
+void init_global_client_stats(void)
+{
+ if (hash_init(&global_client_stats, system_charset_info, max_connections,
+ 0, 0, (hash_get_key) get_key_user_stats,
+ (hash_free_key)free_user_stats, 0))
+ {
+ sql_print_error("Initializing global_client_stats failed.");
+ exit(1);
+ }
+}
+
+extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= table_stats->table_name_length;
+ return (uchar*) table_stats->table;
+}
+
+extern "C" void free_table_stats(TABLE_STATS* table_stats)
+{
+ my_free(table_stats, MYF(0));
+}
+
+void init_global_table_stats(void)
+{
+ if (hash_init(&global_table_stats, system_charset_info, max_connections,
+ 0, 0, (hash_get_key) get_key_table_stats,
+ (hash_free_key)free_table_stats, 0)) {
+ sql_print_error("Initializing global_table_stats failed.");
+ exit(1);
+ }
+}
+
+extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= index_stats->index_name_length;
+ return (uchar*) index_stats->index;
+}
+
+extern "C" void free_index_stats(INDEX_STATS* index_stats)
+{
+ my_free(index_stats, MYF(0));
+}
+
+void init_global_index_stats(void)
+{
+ if (hash_init(&global_index_stats, system_charset_info, max_connections,
+ 0, 0, (hash_get_key) get_key_index_stats,
+ (hash_free_key)free_index_stats, 0))
+ {
+ sql_print_error("Initializing global_index_stats failed.");
+ exit(1);
+ }
+}
+
+
+void free_global_user_stats(void)
+{
+ hash_free(&global_user_stats);
+}
+
+void free_global_table_stats(void)
+{
+ hash_free(&global_table_stats);
+}
+
+void free_global_index_stats(void)
+{
+ hash_free(&global_index_stats);
+}
+
+void free_global_client_stats(void)
+{
+ hash_free(&global_client_stats);
+}
+
+/*
+ Increments the global stats connection count for an entry from
+ global_client_stats or global_user_stats. Returns 0 on success
+ and 1 on error.
+*/
+
+static bool increment_count_by_name(const char *name, size_t name_length,
+ const char *role_name,
+ HASH *users_or_clients, THD *thd)
+{
+ USER_STATS *user_stats;
+
+ if (!(user_stats= (USER_STATS*) hash_search(users_or_clients, (uchar*) name,
+ name_length)))
+ {
+ /* First connection for this user or client */
+ if (!(user_stats= ((USER_STATS*)
+ my_malloc(sizeof(USER_STATS),
+ MYF(MY_WME | MY_ZEROFILL)))))
+ return TRUE; // Out of memory
+
+ init_user_stats(user_stats, name, name_length, role_name,
+ 0, 0, // connections
+ 0, 0, 0, // time
+ 0, 0, 0, // bytes sent, received and written
+ 0, 0, // Rows sent and read
+ 0, 0, 0, // rows inserted, deleted and updated
+ 0, 0, 0, // select, update and other commands
+ 0, 0, // commit and rollback trans
+ thd->status_var.access_denied_errors,
+ 0, // lost connections
+ 0, // access denied errors
+ 0); // empty queries
+
+ if (my_hash_insert(users_or_clients, (uchar*)user_stats))
+ {
+ my_free(user_stats, 0);
+ return TRUE; // Out of memory
+ }
+ }
+ user_stats->total_connections++;
+ return FALSE;
+}
+
+
+/*
+ Increments the global user and client stats connection count.
+
+ @param use_lock if true, LOCK_global_user_client_stats will be locked
+
+ @retval 0 ok
+ @retval 1 error.
+*/
+
+#ifndef EMBEDDED_LIBRARY
+static bool increment_connection_count(THD* thd, bool use_lock)
+{
+ const char *user_string= get_valid_user_string(thd->main_security_ctx.user);
+ const char *client_string= get_client_host(thd);
+ bool return_value= FALSE;
+
+ if (!thd->userstat_running)
+ return FALSE;
+
+ if (use_lock)
+ pthread_mutex_lock(&LOCK_global_user_client_stats);
+
+ if (increment_count_by_name(user_string, strlen(user_string), user_string,
+ &global_user_stats, thd))
+ {
+ return_value= TRUE;
+ goto end;
+ }
+ if (increment_count_by_name(client_string, strlen(client_string),
+ user_string, &global_client_stats, thd))
+ {
+ return_value= TRUE;
+ goto end;
+ }
+
+end:
+ if (use_lock)
+ pthread_mutex_unlock(&LOCK_global_user_client_stats);
+ return return_value;
+}
+#endif
+
+/*
+ Used to update the global user and client stats
+*/
+
+static void update_global_user_stats_with_user(THD *thd,
+ USER_STATS *user_stats,
+ time_t now)
+{
+ DBUG_ASSERT(thd->userstat_running);
+
+ user_stats->connected_time+= now - thd->last_global_update_time;
+ user_stats->busy_time+= (thd->status_var.busy_time -
+ thd->org_status_var.busy_time);
+ user_stats->cpu_time+= (thd->status_var.cpu_time -
+ thd->org_status_var.cpu_time);
+ /*
+ This is handle specially as bytes_recieved is incremented BEFORE
+ org_status_var is copied.
+ */
+ user_stats->bytes_received+= (thd->org_status_var.bytes_received-
+ thd->start_bytes_received);
+ user_stats->bytes_sent+= (thd->status_var.bytes_sent -
+ thd->org_status_var.bytes_sent);
+ user_stats->binlog_bytes_written+=
+ (thd->status_var.binlog_bytes_written -
+ thd->org_status_var.binlog_bytes_written);
+ user_stats->rows_read+= (thd->status_var.rows_read -
+ thd->org_status_var.rows_read);
+ user_stats->rows_sent+= (thd->status_var.rows_sent -
+ thd->org_status_var.rows_sent);
+ user_stats->rows_inserted+= (thd->status_var.ha_write_count -
+ thd->org_status_var.ha_write_count);
+ user_stats->rows_deleted+= (thd->status_var.ha_delete_count -
+ thd->org_status_var.ha_delete_count);
+ user_stats->rows_updated+= (thd->status_var.ha_update_count -
+ thd->org_status_var.ha_update_count);
+ user_stats->select_commands+= thd->select_commands;
+ user_stats->update_commands+= thd->update_commands;
+ user_stats->other_commands+= thd->other_commands;
+ user_stats->commit_trans+= (thd->status_var.ha_commit_count -
+ thd->org_status_var.ha_commit_count);
+ user_stats->rollback_trans+= (thd->status_var.ha_rollback_count +
+ thd->status_var.ha_savepoint_rollback_count -
+ thd->org_status_var.ha_rollback_count -
+ thd->org_status_var.
+ ha_savepoint_rollback_count);
+ user_stats->access_denied_errors+=
+ (thd->status_var.access_denied_errors -
+ thd->org_status_var.access_denied_errors);
+ user_stats->empty_queries+= (thd->status_var.empty_queries -
+ thd->org_status_var.empty_queries);
+
+ /* The following can only contain 0 or 1 and then connection ends */
+ user_stats->denied_connections+= thd->status_var.access_denied_errors;
+ user_stats->lost_connections+= thd->status_var.lost_connections;
+}
+
+
+/* Updates the global stats of a user or client */
+void update_global_user_stats(THD *thd, bool create_user, time_t now)
+{
+ const char *user_string, *client_string;
+ USER_STATS *user_stats;
+ size_t user_string_length, client_string_length;
+ DBUG_ASSERT(thd->userstat_running);
+
+ user_string= get_valid_user_string(thd->main_security_ctx.user);
+ user_string_length= strlen(user_string);
+ client_string= get_client_host(thd);
+ client_string_length= strlen(client_string);
+
+ pthread_mutex_lock(&LOCK_global_user_client_stats);
+
+ // Update by user name
+ if ((user_stats= (USER_STATS*) hash_search(&global_user_stats,
+ (uchar*) user_string,
+ user_string_length)))
+ {
+ /* Found user. */
+ update_global_user_stats_with_user(thd, user_stats, now);
+ }
+ else
+ {
+ /* Create the entry */
+ if (create_user)
+ {
+ increment_count_by_name(user_string, user_string_length, user_string,
+ &global_user_stats, thd);
+ }
+ }
+
+ /* Update by client IP */
+ if ((user_stats= (USER_STATS*)hash_search(&global_client_stats,
+ (uchar*) client_string,
+ client_string_length)))
+ {
+ // Found by client IP
+ update_global_user_stats_with_user(thd, user_stats, now);
+ }
+ else
+ {
+ // Create the entry
+ if (create_user)
+ {
+ increment_count_by_name(client_string, client_string_length,
+ user_string, &global_client_stats, thd);
+ }
+ }
+ /* Reset variables only used for counting */
+ thd->select_commands= thd->update_commands= thd->other_commands= 0;
+ thd->last_global_update_time= now;
+
+ pthread_mutex_unlock(&LOCK_global_user_client_stats);
+}
+
/**
Set thread character set variables from the given ID
@@ -665,9 +860,8 @@ bool init_new_connection_handler_thread()
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)
+ 0 success, thd is updated.
+ 1 error
*/
#ifndef EMBEDDED_LIBRARY
@@ -675,11 +869,6 @@ static int check_connection(THD *thd)
{
uint connect_errors= 0;
NET *net= &thd->net;
- ulong pkt_len= 0;
- char *end, *user, *passwd, *db;
- uint user_len, dummy_errors, passwd_len, db_len;
- char db_buff[SAFE_NAME_LEN*2 + 1]; // buffer to store db in utf8
- char user_buff[USERNAME_LENGTH*2 + 1]; // buffer to store user in utf8
DBUG_PRINT("info",
("New connection received on %s", vio_description(net->vio)));
@@ -741,210 +930,10 @@ static int check_connection(THD *thd)
}
vio_keepalive(net->vio, TRUE);
- ulong server_capabilites;
- {
- /* buff[] needs to big enough to hold the server_version variable */
- char buff[SERVER_VERSION_LENGTH + 1 + SCRAMBLE_LENGTH + 1 + 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)
- goto 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 1; /* The error is set by alloc(). */
- /*
- Protocol buffer is guaranteed to always end with \0. (see my_net_read())
- As the code below depends on this, lets check that.
- */
- DBUG_ASSERT(net->read_pos[pkt_len] == 0);
-
- thd->client_capabilities= uint2korr(net->read_pos);
- if (thd->client_capabilities & CLIENT_PROTOCOL_41)
- {
- if (pkt_len < 32)
- goto error;
-
- 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]));
- if (thd_init_client_charset(thd, (uint) net->read_pos[8]))
- return 1;
- thd->update_charset();
- end= (char*) net->read_pos+32;
- }
- else
- {
- if (pkt_len < 5)
- goto error;
-
- 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)
- {
- char error_string[1024];
- /* Do the SSL layering. */
- if (!ssl_acceptor_fd)
- goto error;
- DBUG_PRINT("info", ("IO layer change in progress..."));
- if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, error_string))
- {
- DBUG_PRINT("error", ("Failed to accept new SSL connection"));
- goto 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));
- goto error;
- }
- }
-#endif /* HAVE_OPENSSL */
-
- if (end >= (char*) net->read_pos+ pkt_len +2)
- goto 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;
-
- user= end;
- passwd= strend(user)+1;
- user_len= passwd - user - 1;
- db= passwd;
-
- /*
- 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.
-
- strlen() is safe as packet[pkt_len] is guranteed to be 0
- */
- 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;
-
- if (passwd + passwd_len + test(db) > (char *)net->read_pos + pkt_len)
- goto error;
-
- /* strlen() can't be easily deleted without changing protocol */
- db_len= db ? strlen(db) : 0;
-
- /* 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;
- }
-
- /*
- Clip username to allowed length in characters (not bytes). This is
- mostly for backward compatibility.
- */
- {
- CHARSET_INFO *cs= system_charset_info;
- int err;
-
- user_len= (uint) cs->cset->well_formed_len(cs, user, user + user_len,
- USERNAME_CHAR_LENGTH, &err);
- user[user_len]= '\0';
- }
-
- 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);
-
-error:
- inc_host_errors(&thd->remote.sin_addr);
- my_error(ER_HANDSHAKE_ERROR, MYF(0));
- return 1;
+ return acl_authenticate(thd, connect_errors, 0);
}
@@ -1016,6 +1005,14 @@ bool login_connection(THD *thd)
/* 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);
+
+ /* Updates global user connection stats. */
+ if (increment_connection_count(thd, TRUE))
+ {
+ net_send_error(thd, ER_OUTOFMEMORY); // Out of memory
+ DBUG_RETURN(1);
+ }
+
DBUG_RETURN(0);
}
@@ -1031,12 +1028,22 @@ void end_connection(THD *thd)
{
NET *net= &thd->net;
plugin_thdvar_cleanup(thd);
+
if (thd->user_connect)
+ {
+ /*
+ We decrease this variable early to make it easy to log again quickly.
+ This code is not critical as we will in any case do this test
+ again in thd->cleanup()
+ */
decrease_user_connections(thd->user_connect);
+ thd->user_connect= 0;
+ }
if (thd->killed || (net->error && net->vio != 0))
{
statistic_increment(aborted_threads,&LOCK_status);
+ status_var_increment(thd->status_var.lost_connections);
}
if (net->error && net->vio != 0)
@@ -1163,15 +1170,20 @@ pthread_handler_t handle_one_connection(void *arg)
for (;;)
{
NET *net= &thd->net;
+ bool create_user= TRUE;
lex_start(thd);
if (login_connection(thd))
+ {
+ create_user= FALSE;
goto end_thread;
+ }
prepare_new_connection_state(thd);
while (!net->error && net->vio != 0 &&
- !(thd->killed == THD::KILL_CONNECTION))
+ thd->killed != THD::KILL_CONNECTION &&
+ thd->killed != THD::KILL_SERVER)
{
if (do_command(thd))
break;
@@ -1180,12 +1192,14 @@ pthread_handler_t handle_one_connection(void *arg)
end_thread:
close_connection(thd, 0, 1);
+ if (thd->userstat_running)
+ update_global_user_stats(thd, create_user, time(NULL));
+
if (thd->scheduler->end_thread(thd,1))
return 0; // Probably no-threads
/*
- If end_thread() returns, we are either running with
- thread-handler=no-threads or this thread has been schedule to
+ If end_thread() returns, this thread has been schedule to
handle the next connection.
*/
thd= current_thd;
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
index eca507a5d50..630ba787c72 100644
--- a/sql/sql_cursor.cc
+++ b/sql/sql_cursor.cc
@@ -610,7 +610,7 @@ int Materialized_cursor::open(JOIN *join __attribute__((unused)))
thd->set_n_backup_active_arena(this, &backup_arena);
/* Create a list of fields and start sequential scan */
rc= result->prepare(item_list, &fake_unit);
- if (!rc && !(rc= table->file->ha_rnd_init(TRUE)))
+ if (!rc && !(rc= table->file->ha_rnd_init_with_error(TRUE)))
is_rnd_inited= 1;
thd->restore_active_arena(this, &backup_arena);
@@ -657,7 +657,7 @@ void Materialized_cursor::fetch(ulong num_rows)
result->begin_dataset();
for (fetch_limit+= num_rows; fetch_count < fetch_limit; fetch_count++)
{
- if ((res= table->file->rnd_next(table->record[0])))
+ if ((res= table->file->ha_rnd_next(table->record[0])))
break;
/* Send data only if the read was successful. */
/*
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 460b41349b3..2a2efe27952 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -20,6 +20,7 @@
#include "mysql_priv.h"
#include <mysys_err.h>
+#include "sp_head.h"
#include "sp.h"
#include "events.h"
#include <my_dir.h>
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index f6a1654ec74..29cd3262a72 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -132,7 +132,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
- there should be no delete triggers associated with the table.
*/
if (!using_limit && const_cond_result &&
- !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) &&
(thd->lex->sql_command == SQLCOM_TRUNCATE ||
(!thd->current_stmt_binlog_row_based &&
!(table->triggers && table->triggers->has_delete_triggers()))))
@@ -268,8 +267,15 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
free_underlaid_joins(thd, select_lex);
DBUG_RETURN(TRUE);
}
- if (usable_index==MAX_KEY || (select && select->quick))
- init_read_record(&info, thd, table, select, 1, 1, FALSE);
+ if (usable_index == MAX_KEY || (select && select->quick))
+ {
+ if (init_read_record(&info, thd, table, select, 1, 1, FALSE))
+ {
+ delete select;
+ free_underlaid_joins(thd, select_lex);
+ DBUG_RETURN(TRUE);
+ }
+ }
else
init_read_record_idx(&info, thd, table, 1, usable_index);
@@ -307,6 +313,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
while (!(error=info.read_record(&info)) && !thd->killed &&
! thd->is_error())
{
+ update_virtual_fields(thd, table);
thd->examined_row_count++;
// thd->is_error() is tested to disallow delete row on error
if (!select || select->skip_record(thd) > 0)
@@ -947,7 +954,10 @@ int multi_delete::do_table_deletes(TABLE *table, bool ignore)
READ_RECORD info;
ha_rows last_deleted= deleted;
DBUG_ENTER("do_deletes_for_table");
- init_read_record(&info, thd, table, NULL, 0, 1, FALSE);
+
+ if (init_read_record(&info, thd, table, NULL, 0, 1, FALSE))
+ DBUG_RETURN(1);
+
/*
Ignore any rows not found in reference tables as they may already have
been deleted by foreign key handling
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 15a2a1298f0..60d3740931e 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -583,11 +583,11 @@ retry:
{
/* Check if we read from the same index. */
DBUG_ASSERT((uint) keyno == table->file->get_index());
- error= table->file->index_next(table->record[0]);
+ error= table->file->ha_index_next(table->record[0]);
}
else
{
- error= table->file->rnd_next(table->record[0]);
+ error= table->file->ha_rnd_next(table->record[0]);
}
break;
}
@@ -597,13 +597,13 @@ retry:
{
table->file->ha_index_or_rnd_end();
table->file->ha_index_init(keyno, 1);
- error= table->file->index_first(table->record[0]);
+ error= table->file->ha_index_first(table->record[0]);
}
else
{
table->file->ha_index_or_rnd_end();
if (!(error= table->file->ha_rnd_init(1)))
- error= table->file->rnd_next(table->record[0]);
+ error= table->file->ha_rnd_next(table->record[0]);
}
mode=RNEXT;
break;
@@ -615,7 +615,7 @@ retry:
{
if ((error= table->file->can_continue_handler_scan()))
break;
- error=table->file->index_prev(table->record[0]);
+ error= table->file->ha_index_prev(table->record[0]);
break;
}
/* else fall through */
@@ -623,13 +623,13 @@ retry:
DBUG_ASSERT(keyname != 0);
table->file->ha_index_or_rnd_end();
table->file->ha_index_init(keyno, 1);
- error= table->file->index_last(table->record[0]);
+ error= table->file->ha_index_last(table->record[0]);
mode=RPREV;
break;
case RNEXT_SAME:
/* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */
DBUG_ASSERT(keyname != 0);
- error= table->file->index_next_same(table->record[0], key, key_len);
+ error= table->file->ha_index_next_same(table->record[0], key, key_len);
break;
case RKEY:
{
@@ -669,8 +669,8 @@ retry:
table->file->ha_index_or_rnd_end();
table->file->ha_index_init(keyno, 1);
key_copy(key, table->record[0], table->key_info + keyno, key_len);
- error= table->file->index_read_map(table->record[0],
- key, keypart_map, ha_rkey_mode);
+ error= table->file->ha_index_read_map(table->record[0],
+ key, keypart_map, ha_rkey_mode);
mode=rkey_to_rnext[(int)ha_rkey_mode];
break;
}
@@ -695,6 +695,8 @@ retry:
}
goto ok;
}
+ /* Generate values for virtual fields */
+ update_virtual_fields(thd, table);
if (cond && !cond->val_int())
continue;
if (num_rows >= offset_limit_cnt)
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index a00fc1003c2..a93fd23a57f 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -184,11 +184,14 @@ int search_topics(THD *thd, TABLE *topics, struct st_find_field *find_fields,
SQL_SELECT *select, List<String> *names,
String *name, String *description, String *example)
{
- DBUG_ENTER("search_topics");
int count= 0;
-
READ_RECORD read_record_info;
- init_read_record(&read_record_info, thd, topics, select, 1, 0, FALSE);
+ DBUG_ENTER("search_topics");
+
+ /* Should never happen. As this is part of help, we can ignore this */
+ if (init_read_record(&read_record_info, thd, topics, select, 1, 0, FALSE))
+ DBUG_RETURN(0);
+
while (!read_record_info.read_record(&read_record_info))
{
if (!select->cond->val_int()) // Doesn't match like
@@ -224,11 +227,13 @@ int search_topics(THD *thd, TABLE *topics, struct st_find_field *find_fields,
int search_keyword(THD *thd, TABLE *keywords, struct st_find_field *find_fields,
SQL_SELECT *select, int *key_id)
{
- DBUG_ENTER("search_keyword");
int count= 0;
-
READ_RECORD read_record_info;
- init_read_record(&read_record_info, thd, keywords, select, 1, 0, FALSE);
+ DBUG_ENTER("search_keyword");
+ /* Should never happen. As this is part of help, we can ignore this */
+ if (init_read_record(&read_record_info, thd, keywords, select, 1, 0, FALSE))
+ DBUG_RETURN(0);
+
while (!read_record_info.read_record(&read_record_info) && count<2)
{
if (!select->cond->val_int()) // Dosn't match like
@@ -296,13 +301,13 @@ int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations,
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_map(relations->record[0],
- buff, (key_part_map) 1,
- HA_READ_KEY_EXACT);
+ int key_res= relations->file->ha_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]))
+ key_res= relations->file->ha_index_next(relations->record[0]))
{
uchar topic_id_buff[8];
longlong topic_id= rtopic_id->val_int();
@@ -310,8 +315,8 @@ int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations,
field->store((longlong) topic_id, TRUE);
field->get_key_image(topic_id_buff, field->pack_length(), Field::itRAW);
- if (!topics->file->index_read_map(topics->record[0], topic_id_buff,
- (key_part_map)1, HA_READ_KEY_EXACT))
+ if (!topics->file->ha_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);
@@ -349,10 +354,11 @@ int search_categories(THD *thd, TABLE *categories,
Field *pcat_id= find_fields[help_category_help_category_id].field;
int count= 0;
READ_RECORD read_record_info;
-
DBUG_ENTER("search_categories");
- init_read_record(&read_record_info, thd, categories, select,1,0,FALSE);
+ /* Should never happen. As this is part of help, we can ignore this */
+ if (init_read_record(&read_record_info, thd, categories, select,1,0,FALSE))
+ DBUG_RETURN(0);
while (!read_record_info.read_record(&read_record_info))
{
if (select && !select->cond->val_int())
@@ -383,10 +389,13 @@ int search_categories(THD *thd, TABLE *categories,
void get_all_items_for_category(THD *thd, TABLE *items, Field *pfname,
SQL_SELECT *select, List<String> *res)
{
+ READ_RECORD read_record_info;
DBUG_ENTER("get_all_items_for_category");
- READ_RECORD read_record_info;
- init_read_record(&read_record_info, thd, items, select,1,0,FALSE);
+ /* Should never happen. As this is part of help, we can ignore this */
+ if (init_read_record(&read_record_info, thd, items, select,1,0,FALSE))
+ DBUG_VOID_RETURN;
+
while (!read_record_info.read_record(&read_record_info))
{
if (!select->cond->val_int())
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 171e49aa7d6..99bebe827a4 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -75,15 +75,6 @@ static void unlink_blobs(register TABLE *table);
#endif
static bool check_view_insertability(THD *thd, TABLE_LIST *view);
-/* Define to force use of my_malloc() if the allocated memory block is big */
-
-#ifndef HAVE_ALLOCA
-#define my_safe_alloca(size, min_length) my_alloca(size)
-#define my_safe_afree(ptr, size, min_length) my_afree(ptr)
-#else
-#define my_safe_alloca(size, min_length) ((size <= min_length) ? my_alloca(size) : my_malloc(size,MYF(0)))
-#define my_safe_afree(ptr, size, min_length) if (size > min_length) my_free(ptr,MYF(0))
-#endif
/*
Check that insert/update fields are from the same single table of a view.
@@ -283,6 +274,9 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
}
}
}
+ /* Mark virtual columns used in the insert statement */
+ if (table->vfield)
+ table->mark_virtual_columns_for_write(TRUE);
// For the values we need select_priv
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege);
@@ -1276,7 +1270,6 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
if (mysql_prepare_insert_check_table(thd, table_list, fields, select_insert))
DBUG_RETURN(TRUE);
-
/* Prepare the fields in the statement. */
if (values)
{
@@ -1329,6 +1322,18 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
if (!table)
table= table_list->table;
+ if (!fields.elements && table->vfield)
+ {
+ for (Field **vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++)
+ {
+ if ((*vfield_ptr)->stored_in_db)
+ {
+ thd->lex->unit.insert_table_with_stored_vcol= table;
+ break;
+ }
+ }
+ }
+
if (!select_insert)
{
Item *fake_conds= 0;
@@ -1455,7 +1460,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
goto err;
if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
{
- if (table->file->rnd_pos(table->record[1],table->file->dup_ref))
+ if (table->file->ha_rnd_pos(table->record[1],table->file->dup_ref))
goto err;
}
else
@@ -1476,9 +1481,10 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
}
}
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))))
+ if ((error= (table->file->ha_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)
@@ -1778,8 +1784,10 @@ public:
table(0),tables_in_use(0),stacked_inserts(0), status(0), dead(0),
group_count(0)
{
- thd.security_ctx->user=thd.security_ctx->priv_user=(char*) delayed_user;
+ thd.security_ctx->user=(char*) delayed_user;
thd.security_ctx->host=(char*) my_localhost;
+ strmake(thd.security_ctx->priv_user, thd.security_ctx->user,
+ USERNAME_LENGTH);
thd.current_tablenr=0;
thd.version=refresh_version;
thd.command=COM_DELAYED_INSERT;
@@ -2070,6 +2078,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
TABLE *copy;
TABLE_SHARE *share;
uchar *bitmap;
+ char *copy_tmp;
DBUG_ENTER("Delayed_insert::get_local_table");
/* First request insert thread to get a lock */
@@ -2102,20 +2111,21 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
the other record buffers and alignment are unnecessary.
*/
thd_proc_info(client_thd, "allocating local table");
- copy= (TABLE*) client_thd->alloc(sizeof(*copy)+
- (share->fields+1)*sizeof(Field**)+
- share->reclength +
- share->column_bitmap_size*2);
- if (!copy)
+ copy_tmp= (char*) client_thd->alloc(sizeof(*copy)+
+ (share->fields+1)*sizeof(Field**)+
+ share->reclength +
+ share->column_bitmap_size*3);
+ if (!copy_tmp)
goto error;
/* Copy the TABLE object. */
+ copy= new (copy_tmp) TABLE;
*copy= *table;
/* We don't need to change the file handler here */
/* Assign the pointers for the field pointers array and the record. */
field= copy->field= (Field**) (copy + 1);
bitmap= (uchar*) (field + share->fields + 1);
- copy->record[0]= (bitmap + share->column_bitmap_size * 2);
+ copy->record[0]= (bitmap + share->column_bitmap_size*3);
memcpy((char*) copy->record[0], (char*) table->record[0], share->reclength);
/*
Make a copy of all fields.
@@ -2157,10 +2167,13 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
copy->def_read_set.bitmap= (my_bitmap_map*) bitmap;
copy->def_write_set.bitmap= ((my_bitmap_map*)
(bitmap + share->column_bitmap_size));
+ copy->def_vcol_set.bitmap= ((my_bitmap_map*)
+ (bitmap + 2*share->column_bitmap_size));
copy->tmp_set.bitmap= 0; // To catch errors
- bzero((char*) bitmap, share->column_bitmap_size*2);
+ bzero((char*) bitmap, share->column_bitmap_size*3);
copy->read_set= &copy->def_read_set;
copy->write_set= &copy->def_write_set;
+ copy->vcol_set= &copy->def_vcol_set;
DBUG_RETURN(copy);
@@ -2307,7 +2320,7 @@ void kill_delayed_threads(void)
Delayed_insert *di;
while ((di= it++))
{
- di->thd.killed= THD::KILL_CONNECTION;
+ di->thd.killed= THD::KILL_SYSTEM_THREAD;
pthread_mutex_lock(&di->thd.LOCK_thd_data);
if (di->thd.mysys_var)
{
@@ -2392,7 +2405,8 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di)
for (;;)
{
- if (thd->killed == THD::KILL_CONNECTION)
+ if (thd->killed == THD::KILL_CONNECTION ||
+ thd->killed == THD::KILL_SYSTEM_THREAD || thd->killed == THD::KILL_SERVER)
{
uint lock_count;
/*
@@ -2440,7 +2454,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di)
break;
if (error == ETIMEDOUT || error == ETIME)
{
- thd->killed= THD::KILL_CONNECTION;
+ thd->killed= THD::KILL_SYSTEM_THREAD;
break;
}
}
@@ -2473,7 +2487,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di)
{
/* Fatal error */
di->dead= 1;
- thd->killed= THD::KILL_CONNECTION;
+ thd->killed= THD::KILL_SYSTEM_THREAD;
}
pthread_cond_broadcast(&di->cond_client);
}
@@ -2483,7 +2497,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di)
{
/* Some fatal error */
di->dead= 1;
- thd->killed= THD::KILL_CONNECTION;
+ thd->killed= THD::KILL_SYSTEM_THREAD;
}
}
di->status=0;
@@ -2543,7 +2557,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
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;
+ thd->killed=abort_loop ? THD::KILL_SYSTEM_THREAD : THD::NOT_KILLED;
pthread_mutex_unlock(&LOCK_thread_count);
/*
@@ -2577,7 +2591,7 @@ end:
di->table=0;
di->dead= 1; // If error
- thd->killed= THD::KILL_CONNECTION; // If error
+ thd->killed= THD::KILL_SYSTEM_THREAD; // If error
pthread_mutex_unlock(&di->mutex);
close_thread_tables(thd); // Free the table
@@ -2656,7 +2670,7 @@ bool Delayed_insert::handle_inserts(void)
max_rows= delayed_insert_limit;
if (thd.killed || table->needs_reopen_or_name_lock())
{
- thd.killed= THD::KILL_CONNECTION;
+ thd.killed= THD::KILL_SYSTEM_THREAD;
max_rows= ULONG_MAX; // Do as much as possible
}
@@ -3495,7 +3509,7 @@ int select_create::write_to_binlog(bool is_trans, int errcode)
Avoid to use thd->binlog_query() twice, otherwise it will print the unsafe
warning twice.
*/
- Query_log_event ev(thd, query.c_ptr_safe(), query.length(), is_trans,
+ Query_log_event ev(thd, query.ptr(), query.length(), is_trans,
FALSE, errcode);
return mysql_bin_log.write(&ev);
}
@@ -3577,7 +3591,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
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;
+ tmp_table.null_row= 0;
+ tmp_table.maybe_null= 0;
while ((item=it++))
{
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index cbb3782114a..30b53f49b5a 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -23,8 +23,8 @@
#include "item_create.h"
#include <m_ctype.h>
#include <hash.h>
-#include "sp.h"
#include "sp_head.h"
+#include "sp.h"
/*
We are using pointer to this variable for distinguishing between assignment
@@ -334,6 +334,7 @@ void lex_start(THD *thd)
lex->reset_query_tables_list(FALSE);
lex->expr_allows_subselect= TRUE;
lex->use_only_table_context= FALSE;
+ lex->parse_vcol_expr= FALSE;
lex->name.str= 0;
lex->name.length= 0;
@@ -784,9 +785,9 @@ int MYSQLlex(void *arg, void *yythd)
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
LEX *lex= thd->lex;
YYSTYPE *yylval=(YYSTYPE*) arg;
- CHARSET_INFO *cs= thd->charset();
- uchar *state_map= cs->state_map;
- uchar *ident_map= cs->ident_map;
+ CHARSET_INFO *const cs= thd->charset();
+ const uchar *const state_map= cs->state_map;
+ const uchar *const ident_map= cs->ident_map;
LINT_INIT(c);
lip->yylval=yylval; // The global state
@@ -1592,6 +1593,7 @@ void st_select_lex_unit::init_query()
item_list.empty();
describe= 0;
found_rows_for_union= 0;
+ insert_table_with_stored_vcol= 0;
}
void st_select_lex::init_query()
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 3706d4c30ae..acdbab62076 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -120,6 +120,8 @@ enum enum_sql_command {
SQLCOM_SHOW_CREATE_TRIGGER,
SQLCOM_ALTER_DB_UPGRADE,
SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES,
+ SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
+ SQLCOM_SHOW_CLIENT_STATS,
/*
When a command is added here, be sure it's also added in mysqld.cc
@@ -532,6 +534,13 @@ public:
bool describe; /* union exec() called for EXPLAIN */
Procedure *last_procedure; /* Pointer to procedure, if such exists */
+ /*
+ Insert table with stored virtual columns.
+ This is used only in those rare cases
+ when the list of inserted values is empty.
+ */
+ TABLE *insert_table_with_stored_vcol;
+
void init_query();
st_select_lex_unit* master_unit();
st_select_lex* outer_select();
@@ -959,6 +968,8 @@ 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};
+extern const LEX_STRING null_lex_str;
+extern const LEX_STRING empty_lex_str;
struct Sroutine_hash_entry;
@@ -1598,6 +1609,7 @@ typedef struct st_lex : public Query_tables_list
LEX_USER *grant_user;
XID *xid;
THD *thd;
+ Virtual_column_info *vcol_info;
/* maintain a list of used plugins for this LEX */
DYNAMIC_ARRAY plugins;
@@ -1681,6 +1693,14 @@ typedef struct st_lex : public Query_tables_list
syntax error back.
*/
bool expr_allows_subselect;
+ /*
+ A special command "PARSE_VCOL_EXPR" is defined for the parser
+ to translate a defining expression of a virtual column into an
+ Item object.
+ The following flag is used to prevent other applications to use
+ this command.
+ */
+ bool parse_vcol_expr;
thr_lock_type lock_option;
enum SSL_type ssl_type; /* defined in violite.h */
@@ -1786,6 +1806,11 @@ typedef struct st_lex : public Query_tables_list
const char *stmt_definition_end;
/**
+ Collects create options for Field and KEY
+ */
+ engine_option_value *option_list, *option_list_last;
+
+ /**
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
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 23105d84a9d..3a1dbef4f3f 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -614,11 +614,15 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
size_t pl= 0;
List<Item> fv;
Item *item, *val;
- String pfield, pfields;
int n;
const char *tbl= table_name_arg;
const char *tdb= (thd->db != NULL ? thd->db : db_arg);
- String string_buf;
+ char name_buffer[SAFE_NAME_LEN*2];
+ char command_buffer[1024];
+ String string_buf(name_buffer, sizeof(name_buffer),
+ system_charset_info);
+ String pfields(command_buffer, sizeof(command_buffer),
+ system_charset_info);
if (!thd->db || strcmp(db_arg, thd->db))
{
@@ -627,7 +631,7 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
prefix table name with database name so that it
becomes a FQ name.
*/
- string_buf.set_charset(system_charset_info);
+ string_buf.length(0);
string_buf.append(db_arg);
string_buf.append("`");
string_buf.append(".");
@@ -648,6 +652,7 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
/*
prepare fields-list and SET if needed; print_query won't do that for us.
*/
+ pfields.length(0);
if (!thd->lex->field_list.is_empty())
{
List_iterator<Item> li(thd->lex->field_list);
@@ -692,8 +697,8 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
}
}
- p= pfields.c_ptr_safe();
- pl= strlen(p);
+ p= pfields.c_ptr_safe();
+ pl= pfields.length();
if (!(load_data_query= (char *)thd->alloc(lle.get_query_buffer_length() + 1 + pl)))
return TRUE;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 41f308a2ce4..5e45c06996e 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -33,7 +33,7 @@
#include "debug_sync.h"
#include <rpl_mi.h>
-#ifdef WITH_MARIA_STORAGE_ENGINE
+#ifdef WITH_ARIA_STORAGE_ENGINE
#include "../storage/maria/ha_maria.h"
#endif
@@ -189,7 +189,7 @@ bool end_active_trans(THD *thd)
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
if (ha_commit(thd))
error=1;
-#ifdef WITH_MARIA_STORAGE_ENGINE
+#ifdef WITH_ARIA_STORAGE_ENGINE
ha_maria::implicit_commit(thd, TRUE);
#endif
}
@@ -346,10 +346,14 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_CLIENT_STATS]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_USER_STATS]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_TABLE_STATS]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_INDEX_STATS]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND |
- CF_SHOW_TABLE_COMMAND |
- CF_REEXECUTION_FRAGILE);
+ sql_command_flags[SQLCOM_SHOW_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);
@@ -448,9 +452,8 @@ static void handle_bootstrap_impl(THD *thd)
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;
+ thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
+ thd->security_ctx->priv_user[0]= 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
@@ -582,7 +585,6 @@ end:
return 0;
}
-
/**
@brief Check access privs for a MERGE table and fix children lock types.
@@ -793,7 +795,17 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion)
if (res < 0)
my_error(thd->killed_errno(), MYF(0));
else if ((res == 0) && do_release)
+ {
thd->killed= THD::KILL_CONNECTION;
+ if (global_system_variables.log_warnings > 3)
+ {
+ Security_context *sctx= &thd->main_security_ctx;
+ sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
+ thd->thread_id,(thd->db ? thd->db : "unconnected"),
+ sctx->user ? sctx->user : "unauthenticated",
+ sctx->host_or_ip, "RELEASE");
+ }
+ }
DBUG_RETURN(res);
}
@@ -844,6 +856,8 @@ bool do_command(THD *thd)
net_new_transaction(net);
+ /* Save for user statistics */
+ thd->start_bytes_received= thd->status_var.bytes_received;
packet_length= my_net_read(net);
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.start_new_query();
@@ -1120,105 +1134,41 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
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
- /*
- Old clients send null-terminated string ('\0' for empty string) for
- password. New clients send the size (1 byte) + string (not null
- terminated, so also '\0' for empty string).
+ /* acl_authenticate() takes the data from net->read_pos */
+ net->read_pos= (uchar*)packet;
- 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[SAFE_NAME_LEN+1]; // buffer to store db in utf8
- char *db= 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;
+ uint save_db_length= thd->db_length;
+ char *save_db= thd->db;
+ USER_CONN *save_user_connect= thd->user_connect;
Security_context save_security_ctx= *thd->security_ctx;
- USER_CONN *save_user_connect;
-
- db+= passwd_len + 1;
- /*
- 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;
- }
- db_length= strlen(db);
-
- char *ptr= db + db_length + 1;
- uint cs_number= 0;
-
- if (ptr < packet_end)
- {
- CHARSET_INFO *cs;
- if (ptr + 2 > packet_end)
- {
- my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
- break;
- }
-
- if ((cs_number= uint2korr(ptr)) &&
- (cs= get_charset(cs_number, MYF(0))) &&
- !is_supported_parser_charset(cs))
- {
- /* Disallow non-supported parser character sets: UCS2, UTF16, UTF32 */
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client",
- cs->csname);
- break;
- }
- }
-
- /* Convert database name to utf8 */
- db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
- system_charset_info, db, db_length,
- thd->charset(), &dummy_errors)]= 0;
- db= db_buff;
-
- /* Save user and privileges */
- 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))))
- {
- thd->security_ctx->user= save_security_ctx.user;
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- break;
- }
-
- /* Clear variables that are allocated */
+ CHARSET_INFO *save_character_set_client=
+ thd->variables.character_set_client;
+ CHARSET_INFO *save_collation_connection=
+ thd->variables.collation_connection;
+ CHARSET_INFO *save_character_set_results=
+ thd->variables.character_set_results;
+
+ /* Ensure we don't free security_ctx->user in case we have to revert */
+ thd->security_ctx->user= 0;
thd->user_connect= 0;
- thd->security_ctx->priv_user= thd->security_ctx->user;
- res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE);
- if (res)
+ if (acl_authenticate(thd, 0, packet_length))
{
+ /* Free user if allocated by acl_authenticate */
x_free(thd->security_ctx->user);
*thd->security_ctx= save_security_ctx;
+ if (thd->user_connect)
+ decrease_user_connections(thd->user_connect);
thd->user_connect= save_user_connect;
- thd->db= save_db;
- thd->db_length= save_db_length;
+ thd->reset_db(save_db, save_db_length);
+ thd->variables.character_set_client= save_character_set_client;
+ thd->variables.collation_connection= save_collation_connection;
+ thd->variables.character_set_results= save_character_set_results;
+ thd->update_charset();
}
else
{
@@ -1229,17 +1179,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
x_free(save_db);
x_free(save_security_ctx.user);
-
- if (cs_number)
- {
- /*
- We have checked charset earlier,
- so thd_init_client_charset cannot fail.
- */
- if (thd_init_client_charset(thd, cs_number))
- DBUG_ASSERT(0);
- thd->update_charset();
- }
}
break;
}
@@ -1296,7 +1235,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
char *beginning_of_next_stmt= (char*) end_of_stmt;
-#ifdef WITH_MARIA_STORAGE_ENGINE
+#ifdef WITH_ARIA_STORAGE_ENGINE
ha_maria::implicit_commit(thd, FALSE);
#endif
@@ -1411,7 +1350,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
table_list.select_lex= &(thd->lex->select_lex);
lex_start(thd);
- mysql_reset_thd_for_next_command(thd);
+ mysql_reset_thd_for_next_command(thd, opt_userstat_running);
thd->lex->
select_lex.table_list.link_in_list(&table_list,
@@ -1628,7 +1567,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
char *end= buff + length;
length+= my_snprintf(end, buff_len - length - 1,
- end," Memory in use: %ldK Max memory used: %ldK",
+ " Memory in use: %ldK Max memory used: %ldK",
(sf_malloc_cur_memory+1023L)/1024L,
(sf_malloc_max_memory+1023L)/1024L);
}
@@ -1721,7 +1660,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->transaction.stmt.reset();
-#ifdef WITH_MARIA_STORAGE_ENGINE
+#ifdef WITH_ARIA_STORAGE_ENGINE
ha_maria::implicit_commit(thd, FALSE);
#endif
@@ -1735,6 +1674,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* Free tables */
close_thread_tables(thd);
+ /* Update status; Must be done after close_thread_tables */
+ thd->update_all_stats();
+
if (sql_command_flags[thd->lex->sql_command] & CF_CHANGES_DATA)
{
net_end_statement(thd);
@@ -1752,6 +1694,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd_proc_info(thd, 0);
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
+
+ /* Check that some variables are reset properly */
+ DBUG_ASSERT(thd->abort_on_warning == 0);
DBUG_RETURN(error);
}
@@ -1910,6 +1855,12 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
thd->profiling.discard_current_query();
#endif
break;
+ case SCH_USER_STATS:
+ case SCH_CLIENT_STATS:
+ if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
+ DBUG_RETURN(1);
+ case SCH_TABLE_STATS:
+ case SCH_INDEX_STATS:
case SCH_OPEN_TABLES:
case SCH_VARIABLES:
case SCH_STATUS:
@@ -2353,6 +2304,10 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_COLLATIONS:
case SQLCOM_SHOW_STORAGE_ENGINES:
case SQLCOM_SHOW_PROFILE:
+ case SQLCOM_SHOW_CLIENT_STATS:
+ case SQLCOM_SHOW_USER_STATS:
+ case SQLCOM_SHOW_TABLE_STATS:
+ case SQLCOM_SHOW_INDEX_STATS:
case SQLCOM_SELECT:
thd->status_var.last_query_cost= 0.0;
if (all_tables)
@@ -3176,9 +3131,7 @@ end_with_restore_list:
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
thd->query_plan_flags|= QPLAN_ADMIN;
- res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
- mysql_recreate_table(thd, first_table) :
- mysql_optimize_table(thd, first_table, &lex->check_opt);
+ res= mysql_optimize_table(thd, first_table, &lex->check_opt);
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
@@ -4445,8 +4398,8 @@ end_with_restore_list:
if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
lex->sql_command == SQLCOM_CREATE_PROCEDURE))
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_PROC_AUTO_GRANT_FAIL,
- ER(ER_PROC_AUTO_GRANT_FAIL));
+ ER_PROC_AUTO_GRANT_FAIL, ER(ER_PROC_AUTO_GRANT_FAIL));
+ thd->clear_error();
}
/*
@@ -4643,9 +4596,10 @@ create_sp_error:
*/
/* Conditionally writes to binlog */
- int type= lex->sql_command == SQLCOM_ALTER_PROCEDURE ?
- TYPE_ENUM_PROCEDURE :
- TYPE_ENUM_FUNCTION;
+ stored_procedure_type type;
+ type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ?
+ TYPE_ENUM_PROCEDURE :
+ TYPE_ENUM_FUNCTION);
sp_result= sp_update_routine(thd,
type,
@@ -4673,8 +4627,8 @@ create_sp_error:
case SQLCOM_DROP_FUNCTION:
{
int sp_result;
- int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
- TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
+ stored_procedure_type type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
+ TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
sp_result= sp_routine_exists_in_table(thd, type, lex->spname);
mysql_reset_errors(thd, 0);
@@ -4701,9 +4655,10 @@ create_sp_error:
#endif
/* Conditionally writes to binlog */
- int type= lex->sql_command == SQLCOM_DROP_PROCEDURE ?
- TYPE_ENUM_PROCEDURE :
- TYPE_ENUM_FUNCTION;
+ stored_procedure_type type;
+ type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
+ TYPE_ENUM_PROCEDURE :
+ TYPE_ENUM_FUNCTION);
sp_result= sp_drop_routine(thd, type, lex->spname);
}
@@ -5147,6 +5102,7 @@ create_sp_error:
break;
}
thd_proc_info(thd, "query end");
+ thd->update_stats();
/*
Binlog-related cleanup:
@@ -5220,9 +5176,8 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
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());
+ ER_YES, str.c_ptr_safe());
}
if (res)
result->abort();
@@ -5240,6 +5195,10 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
delete result;
}
}
+ /* Count number of empty select queries */
+ if (!thd->sent_row_count)
+ status_var_increment(thd->status_var.empty_queries);
+ status_var_add(thd->status_var.rows_sent, thd->sent_row_count);
return res;
}
@@ -5455,6 +5414,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if (!no_errors)
{
const char *db_name= db ? db : thd->db;
+ status_var_increment(thd->status_var.access_denied_errors);
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
sctx->priv_user, sctx->priv_host, db_name);
}
@@ -5487,12 +5447,15 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
{ // We can never grant this
DBUG_PRINT("error",("No possible access"));
if (!no_errors)
+ {
+ status_var_increment(thd->status_var.access_denied_errors);
my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
sctx->priv_user,
sctx->priv_host,
(thd->password ?
ER(ER_YES) :
ER(ER_NO))); /* purecov: tested */
+ }
DBUG_RETURN(TRUE); /* purecov: tested */
}
@@ -5518,11 +5481,14 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
DBUG_PRINT("error",("Access denied"));
if (!no_errors)
+ {
+ status_var_increment(thd->status_var.access_denied_errors);
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
sctx->priv_user, sctx->priv_host,
(db ? db : (thd->db ?
thd->db :
"unknown"))); /* purecov: tested */
+ }
DBUG_RETURN(TRUE); /* purecov: tested */
}
@@ -5551,6 +5517,7 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
if (!thd->col_access && check_grant_db(thd, dst_db_name))
{
+ status_var_increment(thd->status_var.access_denied_errors);
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
thd->security_ctx->priv_user,
thd->security_ctx->priv_host,
@@ -5613,14 +5580,14 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
{
TABLE_LIST *org_tables= tables;
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;
+ uint i;
/*
The check that first_not_own_table is not reached is for the case when
the given table list refers to the list for prelocking (contains tables
of other queries). For simple queries first_not_own_table is 0.
*/
- for (; i < number && tables != first_not_own_table;
+ for (i=0; i < number && tables != first_not_own_table;
tables= tables->next_global, i++)
{
if (tables->security_ctx)
@@ -5632,9 +5599,12 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
(want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
{
if (!no_errors)
+ {
+ status_var_increment(thd->status_var.access_denied_errors);
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
sctx->priv_user, sctx->priv_host,
INFORMATION_SCHEMA_NAME.str);
+ }
return TRUE;
}
/*
@@ -5798,6 +5768,7 @@ bool check_global_access(THD *thd, ulong want_access)
return 0;
get_privilege_desc(command, sizeof(command), want_access);
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
+ status_var_increment(thd->status_var.access_denied_errors);
return 1;
#else
return 0;
@@ -5901,7 +5872,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
Call it after we use THD for queries, not before.
*/
-void mysql_reset_thd_for_next_command(THD *thd)
+void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat)
{
DBUG_ENTER("mysql_reset_thd_for_next_command");
DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */
@@ -5946,6 +5917,15 @@ void mysql_reset_thd_for_next_command(THD *thd)
thd->total_warn_count=0; // Warnings for this query
thd->rand_used= 0;
thd->sent_row_count= thd->examined_row_count= 0;
+
+ /* Copy data for user stats */
+ if ((thd->userstat_running= calculate_userstat))
+ {
+ thd->start_cpu_time= my_getcputime();
+ memcpy(&thd->org_status_var, &thd->status_var, sizeof(thd->status_var));
+ thd->select_commands= thd->update_commands= thd->other_commands= 0;
+ }
+
thd->query_plan_flags= QPLAN_INIT;
thd->query_plan_fsort_passes= 0;
@@ -6137,7 +6117,6 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
const char ** found_semicolon)
{
DBUG_ENTER("mysql_parse");
-
DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
/*
@@ -6157,7 +6136,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
FIXME: cleanup the dependencies in the code to simplify this.
*/
lex_start(thd);
- mysql_reset_thd_for_next_command(thd);
+ mysql_reset_thd_for_next_command(thd, opt_userstat_running);
if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0)
{
@@ -6235,10 +6214,11 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
}
else
{
+ /* Update statistics for getting the query from the cache */
+ thd->lex->sql_command= SQLCOM_SELECT;
/* There are no multi queries in the cache. */
*found_semicolon= NULL;
}
-
DBUG_VOID_RETURN;
}
@@ -6264,7 +6244,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *rawbuf, uint length)
if (!(error= parser_state.init(thd, rawbuf, length)))
{
lex_start(thd);
- mysql_reset_thd_for_next_command(thd);
+ mysql_reset_thd_for_next_command(thd, opt_userstat_running);
if (!parse_sql(thd, & parser_state, NULL) &&
all_tables_not_ok(thd, lex->select_lex.table_list.first))
@@ -6292,7 +6272,9 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
LEX_STRING *comment,
char *change,
List<String> *interval_list, CHARSET_INFO *cs,
- uint uint_geom_type)
+ uint uint_geom_type,
+ Virtual_column_info *vcol_info,
+ engine_option_value *create_options)
{
register Create_field *new_field;
LEX *lex= thd->lex;
@@ -6310,7 +6292,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
key= new Key(Key::PRIMARY, NullS,
&default_key_create_info,
- 0, lex->col_list);
+ 0, lex->col_list, NULL);
lex->alter_info.key_list.push_back(key);
lex->col_list.empty();
}
@@ -6320,7 +6302,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
key= new Key(Key::UNIQUE, NullS,
&default_key_create_info, 0,
- lex->col_list);
+ lex->col_list, NULL);
lex->alter_info.key_list.push_back(key);
lex->col_list.empty();
}
@@ -6378,7 +6360,8 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
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))
+ interval_list, cs, uint_geom_type, vcol_info,
+ create_options))
DBUG_RETURN(1);
lex->alter_info.create_list.push_back(new_field);
@@ -7141,6 +7124,13 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
result= 1;
}
}
+ if (((options & (REFRESH_SLOW_QUERY_LOG | REFRESH_LOG)) ==
+ REFRESH_SLOW_QUERY_LOG))
+ {
+ /* We are only flushing slow query log */
+ logger.flush_slow_log(thd);
+ }
+
#ifdef HAVE_QUERY_CACHE
if (options & REFRESH_QUERY_CACHE_FREE)
{
@@ -7258,14 +7248,43 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
pthread_mutex_unlock(&LOCK_active_mi);
}
#endif
- if (options & REFRESH_USER_RESOURCES)
- reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
- if (*write_to_binlog != -1)
- *write_to_binlog= tmp_write_to_binlog;
- /*
- If the query was killed then this function must fail.
- */
- return result || (thd ? thd->killed : 0);
+ if (options & REFRESH_USER_RESOURCES)
+ reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
+ if (options & REFRESH_TABLE_STATS)
+ {
+ pthread_mutex_lock(&LOCK_global_table_stats);
+ free_global_table_stats();
+ init_global_table_stats();
+ pthread_mutex_unlock(&LOCK_global_table_stats);
+ }
+ if (options & REFRESH_INDEX_STATS)
+ {
+ pthread_mutex_lock(&LOCK_global_index_stats);
+ free_global_index_stats();
+ init_global_index_stats();
+ pthread_mutex_unlock(&LOCK_global_index_stats);
+ }
+ if (options & (REFRESH_USER_STATS | REFRESH_CLIENT_STATS))
+ {
+ pthread_mutex_lock(&LOCK_global_user_client_stats);
+ if (options & REFRESH_USER_STATS)
+ {
+ free_global_user_stats();
+ init_global_user_stats();
+ }
+ if (options & REFRESH_CLIENT_STATS)
+ {
+ free_global_client_stats();
+ init_global_client_stats();
+ }
+ pthread_mutex_unlock(&LOCK_global_user_client_stats);
+ }
+ if (*write_to_binlog != -1)
+ *write_to_binlog= tmp_write_to_binlog;
+ /*
+ If the query was killed then this function must fail.
+ */
+ return result || (thd ? thd->killed : 0);
}
@@ -7301,7 +7320,6 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
VOID(pthread_mutex_unlock(&LOCK_thread_count));
if (tmp)
{
-
/*
If we're SUPER, we can KILL anything, including system-threads.
No further checks.
@@ -7317,6 +7335,10 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
If user of both killer and killee are non-NULL, proceed with
slayage if both are string-equal.
+
+ It's ok to also kill DELAYED threads with KILL_CONNECTION instead of
+ KILL_SYSTEM_THREAD; The difference is that KILL_CONNECTION may be
+ faster and do a harder kill than KILL_SYSTEM_THREAD;
*/
if ((thd->security_ctx->master_access & SUPER_ACL) ||
@@ -7884,8 +7906,9 @@ void get_default_definer(THD *thd, LEX_USER *definer)
definer->host.str= (char *) sctx->priv_host;
definer->host.length= strlen(definer->host.str);
- definer->password.str= NULL;
- definer->password.length= 0;
+ definer->password= null_lex_str;
+ definer->plugin= empty_lex_str;
+ definer->auth= empty_lex_str;
}
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index c810963b701..9b6bc0cd4b0 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -1006,7 +1006,8 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
const nesting_map saved_allow_sum_func= thd->lex->allow_sum_func;
thd->lex->allow_sum_func= 0;
- error= func_expr->fix_fields(thd, (Item**)&func_expr);
+ if (!(error= func_expr->fix_fields(thd, (Item**)&func_expr)))
+ func_expr->walk(&Item::vcol_in_partition_func_processor, 0, NULL);
/*
Restore agg_field/agg_func and allow_sum_func,
@@ -1928,7 +1929,7 @@ static int add_int(File fptr, longlong number)
static int add_uint(File fptr, ulonglong number)
{
char buff[32];
- longlong2str(number, buff, 10);
+ longlong2str(number, buff, 10, 1);
return add_string(fptr, buff);
}
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index f44813f3224..67c59322a55 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -18,10 +18,11 @@
#include "mysql_priv.h"
#include <my_pthread.h>
#include <my_getopt.h>
+#include <mysql/plugin_auth.h>
#define REPORT_TO_LOG 1
#define REPORT_TO_USER 2
-extern struct st_mysql_plugin *mysqld_builtins[];
+extern struct st_maria_plugin *mariadb_builtins[];
/**
@note The order of the enumeration is critical.
@@ -38,6 +39,18 @@ static TYPELIB global_plugin_typelib=
char *opt_plugin_load= NULL;
char *opt_plugin_dir_ptr;
char opt_plugin_dir[FN_REFLEN];
+uint plugin_maturity;
+
+/*
+ not really needed now, this map will become essential when we add more
+ maturity levels. We cannot change existing maturity constants,
+ so the next value - even if it will be MariaDB_PLUGIN_MATURITY_VERY_BUGGY -
+ will inevitably be larger than MariaDB_PLUGIN_MATURITY_STABLE.
+ To be able to compare them we use this mapping array
+*/
+uint plugin_maturity_map[]=
+{ 0, 1, 2, 3, 4, 5, 6 };
+
/*
When you ad a new plugin type, add both a string and make sure that the
init and deinit array are correctly updated.
@@ -48,7 +61,10 @@ const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{ C_STRING_WITH_LEN("STORAGE ENGINE") },
{ C_STRING_WITH_LEN("FTPARSER") },
{ C_STRING_WITH_LEN("DAEMON") },
- { C_STRING_WITH_LEN("INFORMATION SCHEMA") }
+ { C_STRING_WITH_LEN("INFORMATION SCHEMA") },
+ { C_STRING_WITH_LEN("AUDIT") },
+ { C_STRING_WITH_LEN("REPLICATION") },
+ { C_STRING_WITH_LEN("AUTHENTICATION") }
};
extern int initialize_schema_table(st_plugin_int *plugin);
@@ -61,12 +77,12 @@ extern int finalize_schema_table(st_plugin_int *plugin);
*/
plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
- 0,ha_initialize_handlerton,0,0,initialize_schema_table
+ 0,ha_initialize_handlerton,0,0,initialize_schema_table, 0, 0, 0
};
plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
- 0,ha_finalize_handlerton,0,0,finalize_schema_table
+ 0,ha_finalize_handlerton,0,0,finalize_schema_table, 0, 0, 0
};
#ifdef HAVE_DLOPEN
@@ -76,6 +92,14 @@ 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;
+static const char *maria_plugin_interface_version_sym=
+ "_maria_plugin_interface_version_";
+static const char *maria_sizeof_st_plugin_sym=
+ "_maria_sizeof_struct_st_plugin_";
+static const char *maria_plugin_declarations_sym=
+ "_maria_plugin_declarations_";
+static int min_maria_plugin_interface_version=
+ MARIA_PLUGIN_INTERFACE_VERSION & ~0xFF;
#endif
/* Note that 'int version' must be the first field of every plugin
@@ -87,7 +111,10 @@ static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
MYSQL_HANDLERTON_INTERFACE_VERSION,
MYSQL_FTPARSER_INTERFACE_VERSION,
MYSQL_DAEMON_INTERFACE_VERSION,
- MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
+ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
+ 0xff00, /* audit plugins are supported in a later versions */
+ 0xff00, /* replication plugins are supported in a later versions */
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION
};
static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
@@ -95,10 +122,15 @@ static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
MYSQL_HANDLERTON_INTERFACE_VERSION,
MYSQL_FTPARSER_INTERFACE_VERSION,
MYSQL_DAEMON_INTERFACE_VERSION,
- MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
+ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
+ 0x0000, /* audit plugins are supported in a later versions */
+ 0x0000, /* replication plugins are supported in a later versions */
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION
};
-static bool initialized= 0;
+/* support for Services */
+
+#include "sql_plugin_services.h"
/*
A mutex LOCK_plugin must be acquired before accessing the
@@ -112,6 +144,8 @@ static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM];
static bool reap_needed= false;
static int plugin_array_version=0;
+static bool initialized= 0;
+
/*
write-lock on LOCK_system_variables_hash is required before modifying
the following variables/structures
@@ -199,7 +233,7 @@ 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 **);
-static bool register_builtin(struct st_mysql_plugin *, struct st_plugin_int *,
+static bool register_builtin(struct st_maria_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);
@@ -224,6 +258,22 @@ extern bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
extern bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists);
#endif /* EMBEDDED_LIBRARY */
+static void report_error(int where_to, uint error, ...)
+{
+ va_list args;
+ if (where_to & REPORT_TO_USER)
+ {
+ va_start(args, error);
+ my_printv_error(error, ER(error), MYF(0), args);
+ va_end(args);
+ }
+ if (where_to & REPORT_TO_LOG)
+ {
+ va_start(args, error);
+ error_log_print(ERROR_LEVEL, ER(error), args);
+ va_end(args);
+ }
+}
/**
Check if the provided path is valid in the sense that it does cause
@@ -275,7 +325,7 @@ static const char *item_val_str(struct st_mysql_value *value,
Lets be nice and create a temporary string since the
buffer was too small
*/
- return current_thd->strmake(res->c_ptr_quick(), res->length());
+ return current_thd->strmake(res->ptr(), res->length());
}
@@ -360,11 +410,230 @@ static inline void free_plugin_mem(struct st_plugin_dl *p)
}
+/**
+ Reads data from mysql plugin interface
+
+ @param plugin_dl Structure where the data should be put
+ @param sym Reverence on version info
+ @param dlpath Path to the module
+ @param report What errors should be reported
+
+ @retval FALSE OK
+ @retval TRUE ERROR
+*/
+
+#ifdef HAVE_DLOPEN
+static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl,
+ void *sym, char *dlpath,
+ int report)
+{
+ DBUG_ENTER("read_maria_plugin_info");
+ /* Determine interface version */
+ if (!sym)
+ {
+ free_plugin_mem(plugin_dl);
+ report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_interface_version_sym);
+ DBUG_RETURN(TRUE);
+ }
+ plugin_dl->mariaversion= 0;
+ plugin_dl->mysqlversion= *(int *)sym;
+ /* Versioning */
+ if (plugin_dl->mysqlversion < min_plugin_interface_version ||
+ (plugin_dl->mysqlversion >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8))
+ {
+ free_plugin_mem(plugin_dl);
+ report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0,
+ "plugin interface version mismatch");
+ DBUG_RETURN(TRUE);
+ }
+ /* Find plugin declarations */
+ if (!(sym= dlsym(plugin_dl->handle, plugin_declarations_sym)))
+ {
+ free_plugin_mem(plugin_dl);
+ report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_declarations_sym);
+ DBUG_RETURN(TRUE);
+ }
+
+ /* convert mysql declaration to maria one */
+ {
+ int i;
+ uint sizeof_st_plugin;
+ struct st_mysql_plugin *old;
+ struct st_maria_plugin *cur;
+ char *ptr= (char *)sym;
+
+ if ((sym= dlsym(plugin_dl->handle, sizeof_st_plugin_sym)))
+ sizeof_st_plugin= *(int *)sym;
+ else
+ {
+ DBUG_ASSERT(min_plugin_interface_version == 0);
+ sizeof_st_plugin= (int)offsetof(struct st_mysql_plugin, version);
+ }
+
+ for (i= 0;
+ ((struct st_mysql_plugin *)(ptr + i * sizeof_st_plugin))->info;
+ i++)
+ /* no op */;
+
+ cur= (struct st_maria_plugin*)
+ my_malloc((i + 1) * sizeof(struct st_maria_plugin),
+ MYF(MY_ZEROFILL|MY_WME));
+ if (!cur)
+ {
+ free_plugin_mem(plugin_dl);
+ report_error(report, ER_OUTOFMEMORY, (int) plugin_dl->dl.length);
+ DBUG_RETURN(TRUE);
+ }
+ /*
+ 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++)
+ {
+
+ cur[i].type= old->type;
+ cur[i].info= old->info;
+ cur[i].name= old->name;
+ cur[i].author= old->author;
+ cur[i].descr= old->descr;
+ cur[i].license= old->license;
+ cur[i].init= old->init;
+ cur[i].deinit= old->deinit;
+ cur[i].version= old->version;
+ cur[i].status_vars= old->status_vars;
+ cur[i].system_vars= old->system_vars;
+ /*
+ Something like this should be added to process
+ new mysql plugin versions:
+ if (plugin_dl->mysqlversion > 0x0101)
+ {
+ cur[i].newfield= CONSTANT_MEANS_UNKNOWN;
+ }
+ else
+ {
+ cur[i].newfield= old->newfield;
+ }
+ */
+ /* Maria only fields */
+ cur[i].version_info= "Unknown";
+ cur[i].maturity= MariaDB_PLUGIN_MATURITY_UNKNOWN;
+ }
+ plugin_dl->allocated= true;
+ plugin_dl->plugins= (struct st_maria_plugin *)cur;
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Reads data from maria plugin interface
+
+ @param plugin_dl Structure where the data should be put
+ @param sym Reverence on version info
+ @param dlpath Path to the module
+ @param report what errors should be reported
+
+ @retval FALSE OK
+ @retval TRUE ERROR
+*/
+
+static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
+ void *sym, char *dlpath,
+ int report)
+{
+ DBUG_ENTER("read_maria_plugin_info");
+
+ /* Determine interface version */
+ if (!(sym))
+ {
+ /*
+ Actually this branch impossible because in case of absence of maria
+ version we try mysql version.
+ */
+ free_plugin_mem(plugin_dl);
+ report_error(report, ER_CANT_FIND_DL_ENTRY,
+ maria_plugin_interface_version_sym);
+ DBUG_RETURN(TRUE);
+ }
+ plugin_dl->mariaversion= *(int *)sym;
+ plugin_dl->mysqlversion= 0;
+ /* Versioning */
+ if (plugin_dl->mariaversion < min_maria_plugin_interface_version ||
+ (plugin_dl->mariaversion >> 8) > (MARIA_PLUGIN_INTERFACE_VERSION >> 8))
+ {
+ free_plugin_mem(plugin_dl);
+ report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0,
+ "plugin interface version mismatch");
+ DBUG_RETURN(TRUE);
+ }
+ /* Find plugin declarations */
+ if (!(sym= dlsym(plugin_dl->handle, maria_plugin_declarations_sym)))
+ {
+ free_plugin_mem(plugin_dl);
+ report_error(report, ER_CANT_FIND_DL_ENTRY, maria_plugin_declarations_sym);
+ DBUG_RETURN(TRUE);
+ }
+ if (plugin_dl->mariaversion != MARIA_PLUGIN_INTERFACE_VERSION)
+ {
+ uint sizeof_st_plugin;
+ struct st_maria_plugin *old, *cur;
+ char *ptr= (char *)sym;
+
+ if ((sym= dlsym(plugin_dl->handle, maria_sizeof_st_plugin_sym)))
+ sizeof_st_plugin= *(int *)sym;
+ else
+ {
+ free_plugin_mem(plugin_dl);
+ report_error(report, ER_CANT_FIND_DL_ENTRY, maria_sizeof_st_plugin_sym);
+ DBUG_RETURN(TRUE);
+ }
+
+ if (sizeof_st_plugin != sizeof(st_mysql_plugin))
+ {
+ int i;
+ for (i= 0;
+ ((struct st_maria_plugin *)(ptr + i * sizeof_st_plugin))->info;
+ i++)
+ /* no op */;
+
+ cur= (struct st_maria_plugin*)
+ my_malloc((i + 1) * sizeof(struct st_maria_plugin),
+ MYF(MY_ZEROFILL|MY_WME));
+ if (!cur)
+ {
+ free_plugin_mem(plugin_dl);
+ report_error(report, ER_OUTOFMEMORY, (int) plugin_dl->dl.length);
+ DBUG_RETURN(TRUE);
+ }
+ /*
+ 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_maria_plugin *)(ptr + i * sizeof_st_plugin))->info;
+ i++)
+ memcpy(cur + i, old, min(sizeof(cur[i]), sizeof_st_plugin));
+
+ sym= cur;
+ plugin_dl->allocated= true;
+ }
+ }
+ plugin_dl->plugins= (struct st_maria_plugin *)sym;
+
+ DBUG_RETURN(FALSE);
+}
+#endif /* HAVE_DLOPEN */
+
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;
+ uint plugin_dir_len, dummy_errors, dlpathlen, i;
struct st_plugin_dl *tmp, plugin_dl;
void *sym;
DBUG_ENTER("plugin_dl_add");
@@ -381,10 +650,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
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 | REPORT_TO_USER)) == REPORT_TO_LOG)
- sql_print_error("%s", ER(ER_UDF_NO_PATHS));
+ report_error(report, ER_UDF_NO_PATHS);
DBUG_RETURN(0);
}
/* If this dll is already loaded just increase ref_count. */
@@ -409,119 +675,52 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
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);
+ report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, errno, errmsg);
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))
+
+ /* Checks which plugin interface present and reads info */
+ if (!(sym= dlsym(plugin_dl.handle, maria_plugin_interface_version_sym)))
{
- 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);
+ if (read_mysql_plugin_info(&plugin_dl,
+ dlsym(plugin_dl.handle,
+ plugin_interface_version_sym),
+ dlpath,
+ report))
+ DBUG_RETURN(0);
}
- /* Find plugin declarations */
- if (!(sym= dlsym(plugin_dl.handle, plugin_declarations_sym)))
+ else
{
- 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 (read_maria_plugin_info(&plugin_dl, sym, dlpath, report))
+ DBUG_RETURN(0);
}
- if (plugin_dl.version != MYSQL_PLUGIN_INTERFACE_VERSION)
+ /* link the services in */
+ for (i= 0; i < array_elements(list_of_services); i++)
{
- 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
- }
-
- if (sizeof_st_plugin != sizeof(st_mysql_plugin))
+ if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name)))
{
- for (i= 0;
- ((struct st_mysql_plugin *)(ptr+i*sizeof_st_plugin))->info;
- i++)
- /* no op */;
-
- cur= (struct st_mysql_plugin*)
- my_malloc((i+1)*sizeof(struct st_mysql_plugin), MYF(MY_ZEROFILL|MY_WME));
- if (!cur)
+ uint ver= (uint)(intptr) *(void **)sym;
+ if (ver > list_of_services[i].version ||
+ (ver >> 8) < (list_of_services[i].version >> 8))
{
- free_plugin_mem(&plugin_dl);
- if (report & REPORT_TO_USER)
- my_error(ER_OUTOFMEMORY, MYF(0), (int) plugin_dl.dl.length);
- if (report & REPORT_TO_LOG)
- sql_print_error(ER(ER_OUTOFMEMORY), (int) plugin_dl.dl.length);
+ char buf[MYSQL_ERRMSG_SIZE];
+ my_snprintf(buf, sizeof(buf),
+ "service '%s' interface version mismatch",
+ list_of_services[i].name);
+ report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0, buf);
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.allocated= true;
+ *(void **)sym= list_of_services[i].service;
}
}
- 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), static_cast<int>(plugin_dl.dl.length));
- if (report & REPORT_TO_LOG)
- sql_print_error(ER(ER_OUTOFMEMORY),
- static_cast<int>(plugin_dl.dl.length));
+ report_error(report, ER_OUTOFMEMORY, (int) plugin_dl.dl.length);
DBUG_RETURN(0);
}
plugin_dl.dl.length= copy_and_convert(plugin_dl.dl.str, plugin_dl.dl.length,
@@ -532,21 +731,13 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl)))
{
free_plugin_mem(&plugin_dl);
- if (report & REPORT_TO_USER)
- my_error(ER_OUTOFMEMORY, MYF(0),
- static_cast<int>(sizeof(struct st_plugin_dl)));
- if (report & REPORT_TO_LOG)
- sql_print_error(ER(ER_OUTOFMEMORY),
- static_cast<int>(sizeof(struct st_plugin_dl)));
+ report_error(report, ER_OUTOFMEMORY, (int) 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");
+ report_error(report, ER_FEATURE_DISABLED, "plugin", "HAVE_DLOPEN");
DBUG_RETURN(0);
#endif
}
@@ -665,7 +856,7 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc CALLER_INFO_PROTO)
/*
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.
+ double unlocks to aid resolving reference counting problems.
*/
if (!(plugin= (plugin_ref) my_malloc_ci(sizeof(pi), MYF(MY_WME))))
DBUG_RETURN(NULL);
@@ -673,7 +864,7 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc CALLER_INFO_PROTO)
*plugin= pi;
#endif
pi->ref_count++;
- DBUG_PRINT("info",("thd: 0x%lx plugin: \"%s\" ref_count: %d",
+ DBUG_PRINT("lock",("thd: 0x%lx plugin: \"%s\" LOCK ref_count: %d",
(long) current_thd, pi->name.str, pi->ref_count));
if (lex)
@@ -764,14 +955,11 @@ static bool plugin_add(MEM_ROOT *tmp_root,
int *argc, char **argv, int report)
{
struct st_plugin_int tmp;
- struct st_mysql_plugin *plugin;
+ struct st_maria_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);
+ report_error(report, ER_UDF_EXISTS, name->str);
DBUG_RETURN(TRUE);
}
/* Clear the whole struct to catch future extensions. */
@@ -798,10 +986,18 @@ static bool plugin_add(MEM_ROOT *tmp_root,
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);
+ report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, 0, buf);
+ goto err;
+ }
+ if (plugin_maturity_map[plugin->maturity] < plugin_maturity)
+ {
+ char buf[256];
+ strxnmov(buf, sizeof(buf) - 1, "Loading of ",
+ plugin_maturity_names[plugin->maturity],
+ " plugins is prohibited by --plugin-maturity=",
+ plugin_maturity_names[plugin_maturity],
+ NullS);
+ report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, 0, buf);
goto err;
}
tmp.plugin= plugin;
@@ -830,10 +1026,7 @@ static bool plugin_add(MEM_ROOT *tmp_root,
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);
+ report_error(report, ER_CANT_FIND_DL_ENTRY, name->str);
err:
plugin_dl_del(dl);
DBUG_RETURN(TRUE);
@@ -989,8 +1182,6 @@ static void intern_plugin_unlock(LEX *lex, plugin_ref plugin)
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)
{
/*
@@ -1010,6 +1201,9 @@ static void intern_plugin_unlock(LEX *lex, plugin_ref plugin)
DBUG_ASSERT(pi->ref_count);
pi->ref_count--;
+ DBUG_PRINT("lock",("thd: 0x%lx plugin: \"%s\" UNLOCK ref_count: %d",
+ (long) current_thd, pi->name.str, pi->ref_count));
+
if (pi->state == PLUGIN_IS_DELETED && !pi->ref_count)
reap_needed= true;
@@ -1040,6 +1234,9 @@ void plugin_unlock_list(THD *thd, plugin_ref *list, uint count)
{
LEX *lex= thd ? thd->lex : 0;
DBUG_ENTER("plugin_unlock_list");
+ if (count == 0)
+ DBUG_VOID_RETURN;
+
DBUG_ASSERT(list);
pthread_mutex_lock(&LOCK_plugin);
while (count--)
@@ -1175,8 +1372,8 @@ int plugin_init(int *argc, char **argv, int flags)
{
uint i;
bool is_myisam;
- struct st_mysql_plugin **builtins;
- struct st_mysql_plugin *plugin;
+ struct st_maria_plugin **builtins;
+ struct st_maria_plugin *plugin;
struct st_plugin_int tmp, *plugin_ptr, **reap;
MEM_ROOT tmp_root;
bool reaped_mandatory_plugin= FALSE;
@@ -1215,7 +1412,7 @@ int plugin_init(int *argc, char **argv, int flags)
/*
First we register builtin plugins
*/
- for (builtins= mysqld_builtins; *builtins; builtins++)
+ for (builtins= mariadb_builtins; *builtins; builtins++)
{
for (plugin= *builtins; plugin->info; plugin++)
{
@@ -1338,7 +1535,7 @@ err:
}
-static bool register_builtin(struct st_mysql_plugin *plugin,
+static bool register_builtin(struct st_maria_plugin *plugin,
struct st_plugin_int *tmp,
struct st_plugin_int **ptr)
{
@@ -1374,7 +1571,7 @@ static bool register_builtin(struct st_mysql_plugin *plugin,
RETURN
false - plugin registered successfully
*/
-bool plugin_register_builtin(THD *thd, struct st_mysql_plugin *plugin)
+bool plugin_register_builtin(THD *thd, struct st_maria_plugin *plugin)
{
struct st_plugin_int tmp, *ptr;
bool result= true;
@@ -1456,7 +1653,12 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
goto end;
}
table= tables.table;
- init_read_record(&read_record_info, new_thd, table, NULL, 1, 0, FALSE);
+ if (init_read_record(&read_record_info, new_thd, table, NULL, 1, 0, FALSE))
+ {
+ sql_print_error("Could not initialize init_read_record; Plugins not "
+ "loaded");
+ goto end;
+ }
table->use_all_columns();
while (!(error= read_record_info.read_record(&read_record_info)))
{
@@ -1503,7 +1705,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
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;
+ struct st_maria_plugin *plugin;
char *p= buffer;
DBUG_ENTER("plugin_load_list");
while (list)
@@ -1845,10 +2047,10 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name)
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))
+ if (! table->file->ha_index_read_idx_map(table->record[0], 0,
+ (uchar *)table->field[0]->ptr,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
{
int error;
/*
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index c63e1d4acff..936c27a2290 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -29,16 +29,6 @@ class sys_var;
#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;
@@ -64,8 +54,9 @@ struct st_plugin_dl
{
LEX_STRING dl;
void *handle;
- struct st_mysql_plugin *plugins;
- int version;
+ struct st_maria_plugin *plugins;
+ int mysqlversion;
+ int mariaversion;
bool allocated;
uint ref_count; /* number of plugins loaded from the library */
};
@@ -75,7 +66,7 @@ struct st_plugin_dl
struct st_plugin_int
{
LEX_STRING name;
- struct st_mysql_plugin *plugin;
+ struct st_maria_plugin *plugin;
struct st_plugin_dl *plugin_dl;
uint state;
uint ref_count; /* number of threads using the plugin */
@@ -118,6 +109,9 @@ extern char *opt_plugin_load;
extern char *opt_plugin_dir_ptr;
extern char opt_plugin_dir[FN_REFLEN];
extern const LEX_STRING plugin_type_names[];
+extern uint plugin_maturity;
+extern TYPELIB plugin_maturity_values;
+extern const char *plugin_maturity_names[];
extern int plugin_init(int *argc, char **argv, int init_flags);
extern void plugin_shutdown(void);
diff --git a/sql/sql_plugin_services.h b/sql/sql_plugin_services.h
new file mode 100644
index 00000000000..14a2a16561a
--- /dev/null
+++ b/sql/sql_plugin_services.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2009 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 */
+
+/* support for Services */
+#include <service_versions.h>
+
+struct st_service_ref {
+ const char *name;
+ uint version;
+ void *service;
+};
+
+static struct my_snprintf_service_st my_snprintf_handler = {
+ my_snprintf,
+ my_vsnprintf
+};
+
+static struct thd_alloc_service_st thd_alloc_handler= {
+ thd_alloc,
+ thd_calloc,
+ thd_strdup,
+ thd_strmake,
+ thd_memdup,
+ thd_make_lex_string
+};
+
+static struct st_service_ref list_of_services[] __attribute__((unused)) =
+{
+ { "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler },
+ { "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler }
+};
+
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 1c0ee55b79d..2c062deacd0 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2101,14 +2101,13 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
Prepared_statement *stmt;
bool error;
DBUG_ENTER("mysqld_stmt_prepare");
-
DBUG_PRINT("prep_query", ("%s", packet));
/* First of all clear possible warnings from the previous command */
- mysql_reset_thd_for_next_command(thd);
+ mysql_reset_thd_for_next_command(thd, opt_userstat_running);
if (! (stmt= new Prepared_statement(thd)))
- DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
+ goto end; /* out of memory: error is set in Sql_alloc */
if (thd->stmt_map.insert(thd, stmt))
{
@@ -2116,7 +2115,7 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
The error is set in the insert. The statement itself
will be also deleted there (this is how the hash works).
*/
- DBUG_VOID_RETURN;
+ goto end;
}
/* Reset warnings from previous command */
@@ -2143,6 +2142,7 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
thd->protocol= save_protocol;
/* check_prepared_statemnt sends the metadata packet in case of success */
+end:
DBUG_VOID_RETURN;
}
@@ -2189,7 +2189,7 @@ static const char *get_dynamic_sql_string(LEX *lex, uint *query_len)
lex->prepared_stmt_code.length))
&& entry->value)
{
- my_bool is_var_null;
+ bool is_var_null;
var_value= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
/*
NULL value of variable checked early as entry->value so here
@@ -2478,7 +2478,7 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
packet+= 9; /* stmt_id + 5 bytes of flags */
/* First of all clear possible warnings from the previous command */
- mysql_reset_thd_for_next_command(thd);
+ mysql_reset_thd_for_next_command(thd, opt_userstat_running);
if (!(stmt= find_prepared_statement(thd, stmt_id)))
{
@@ -2577,7 +2577,8 @@ void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length)
DBUG_ENTER("mysqld_stmt_fetch");
/* First of all clear possible warnings from the previous command */
- mysql_reset_thd_for_next_command(thd);
+ mysql_reset_thd_for_next_command(thd, opt_userstat_running);
+
status_var_increment(thd->status_var.com_stmt_fetch);
if (!(stmt= find_prepared_statement(thd, stmt_id)))
{
@@ -2643,7 +2644,7 @@ void mysqld_stmt_reset(THD *thd, char *packet)
DBUG_ENTER("mysqld_stmt_reset");
/* First of all clear possible warnings from the previous command */
- mysql_reset_thd_for_next_command(thd);
+ mysql_reset_thd_for_next_command(thd, opt_userstat_running);
status_var_increment(thd->status_var.com_stmt_reset);
if (!(stmt= find_prepared_statement(thd, stmt_id)))
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 8b7261a8712..ebb31290c96 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -37,7 +37,7 @@
#include <my_bit.h>
#include <hash.h>
#include <ft_global.h>
-#if defined(WITH_MARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
+#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
#include "../storage/maria/ha_maria.h"
#define TMP_ENGINE_HTON maria_hton
#else
@@ -2960,8 +2960,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
!table->fulltext_searched &&
!table->pos_in_table_list->embedding)
{
- if ((table->key_info[key].flags & (HA_NOSAME | HA_END_SPACE_KEY))
- == HA_NOSAME)
+ if (table->key_info[key].flags & HA_NOSAME)
{
if (const_ref == eq_part)
{ // Found everything for ref.
@@ -5124,7 +5123,7 @@ optimize_straight_join(JOIN *join, table_map join_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
+ value, the longer the optimization time and possibly the better the
resulting plan. The lower the value, the fewer alternative plans are
estimated, but the more likely to get a bad QEP.
@@ -5618,7 +5617,7 @@ static void calc_used_field_length(THD *thd, JOIN_TAB *join_tab)
{
uint null_fields,blobs,fields,rec_length;
Field **f_ptr,*field;
- MY_BITMAP *read_set= join_tab->table->read_set;;
+ 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++)
@@ -5941,8 +5940,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
DBUG_RETURN(0);
if (j->type == JT_CONST)
j->table->const_table= 1;
- else if (((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY |
- HA_END_SPACE_KEY)) != HA_NOSAME) ||
+ else if (((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) != HA_NOSAME) ||
keyparts != keyinfo->key_parts || null_ref_key)
{
/* Must read with repeat */
@@ -6204,9 +6202,10 @@ 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,
- QT_ORDINARY););
+ DBUG_EXECUTE("where",
+ print_where(notnull,
+ referred_tab->table->alias.c_ptr(),
+ QT_ORDINARY););
add_cond_and_fix(&referred_tab->select_cond, notnull);
}
}
@@ -6465,7 +6464,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
}
if (tmp || !cond || tab->type == JT_REF)
{
- DBUG_EXECUTE("where",print_where(tmp,tab->table->alias, QT_ORDINARY););
+ DBUG_EXECUTE("where",
+ print_where(tmp,tab->table->alias.c_ptr(),
+ QT_ORDINARY););
SQL_SELECT *sel= tab->select= ((SQL_SELECT*)
thd->memdup((uchar*) select,
sizeof(*select)));
@@ -6504,7 +6505,9 @@ 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, QT_ORDINARY););
+ DBUG_EXECUTE("where",
+ print_where(tmp,tab->table->alias.c_ptr(),
+ QT_ORDINARY););
if (tab->quick)
{
/* Use quick key read if it's a constant and it's not used
@@ -7266,8 +7269,6 @@ eq_ref_table(JOIN *join, ORDER *start_order, JOIN_TAB *tab)
static bool
only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables)
{
- if (specialflag & SPECIAL_SAFE_MODE)
- return 0; // skip this optimize /* purecov: inspected */
tables&= ~PSEUDO_TABLE_BITS;
for (JOIN_TAB **tab=join->map2table ; tables ; tab++, tables>>=1)
{
@@ -9723,6 +9724,8 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field,
table->s->db_create_options|= HA_OPTION_PACK_RECORD;
else if (org_field->type() == FIELD_TYPE_DOUBLE)
((Field_double *) new_field)->not_fixed= TRUE;
+ new_field->vcol_info= 0;
+ new_field->stored_in_db= TRUE;
}
return new_field;
}
@@ -10041,9 +10044,13 @@ 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,
+ bitmap_init(&table->def_vcol_set,
(my_bitmap_map*) (bitmaps+ bitmap_buffer_size(field_count)),
field_count, FALSE);
+ bitmap_init(&table->tmp_set,
+ (my_bitmap_map*) (bitmaps+ 2*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;
@@ -10144,7 +10151,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
/*
No need to change table name to lower case as we are only creating
- MyISAM, Maria or HEAP tables here
+ MyISAM, Aria or HEAP tables here
*/
fn_format(path, path, mysql_tmpdir, "",
MY_REPLACE_EXT|MY_UNPACK_FILENAME);
@@ -10196,7 +10203,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
&tmpname, (uint) strlen(path)+1,
&group_buff, (group && ! using_unique_constraint ?
param->group_length : 0),
- &bitmaps, bitmap_buffer_size(field_count)*2,
+ &bitmaps, bitmap_buffer_size(field_count)*3,
NullS))
{
if (temp_pool_slot != MY_BIT_NONE)
@@ -10225,7 +10232,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
thd->mem_root= &table->mem_root;
table->field=reg_field;
- table->alias= table_alias;
+ table->alias.set(table_alias, strlen(table_alias), table_alias_charset);
+
table->reginfo.lock_type=TL_WRITE; /* Will be updated */
table->db_stat=HA_OPEN_KEYFILE+HA_OPEN_RNDFILE;
table->map=1;
@@ -10591,7 +10599,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
null_count=(null_count+7) & ~7; // move to next byte
// fix table name in field entry
- field->table_name= &table->alias;
+ field->set_table_name(&table->alias);
}
param->copy_field_end=copy;
@@ -10835,7 +10843,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list)
&share, sizeof(*share),
&field, (field_count + 1) * sizeof(Field*),
&blob_field, (field_count+1) *sizeof(uint),
- &bitmaps, bitmap_buffer_size(field_count)*2,
+ &bitmaps, bitmap_buffer_size(field_count)*3,
NullS))
return 0;
@@ -10926,8 +10934,9 @@ error:
static bool open_tmp_table(TABLE *table)
{
int error;
- if ((error=table->file->ha_open(table, table->s->table_name.str,O_RDWR,
- HA_OPEN_TMP_TABLE | HA_OPEN_INTERNAL_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;
@@ -10938,9 +10947,9 @@ static bool open_tmp_table(TABLE *table)
}
-#if defined(WITH_MARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
+#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
-/* Create internal Maria temporary table */
+/* Create internal Aria temporary table */
static bool create_internal_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
ulonglong options)
@@ -11020,7 +11029,7 @@ static bool create_internal_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
seg->null_pos= (uint) (field->null_ptr - (uchar*) table->record[0]);
/*
We are using a GROUP BY on something that contains NULL
- In this case we have to tell Maria that two NULL should
+ In this case we have to tell Aria that two NULL should
on INSERT be regarded at the same value
*/
if (!using_unique_constraint)
@@ -11034,10 +11043,22 @@ static bool create_internal_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
OPTION_BIG_TABLES)
create_info.data_file_length= ~(ulonglong) 0;
+ /*
+ The logic for choosing the record format:
+ The STATIC_RECORD format is the fastest one, because it's so simple,
+ so we use this by default for short rows.
+ BLOCK_RECORD caches both row and data, so this is generally faster than
+ DYNAMIC_RECORD. The one exception is when we write to tmp table and
+ want to use keys for duplicate elimination as with BLOCK RECORD
+ we first write the row, then check for key conflicts and then we have to
+ delete the row. The cases when this can happen is when there is
+ a group by and no sum functions or if distinct is used.
+ */
if ((error= maria_create(share->table_name.str,
- share->reclength < 64 &&
- !share->blob_fields ? STATIC_RECORD :
- BLOCK_RECORD,
+ (share->reclength < 64 &&
+ !share->blob_fields ? STATIC_RECORD :
+ ((!param->sum_func_count && table->group) ||
+ table->distinct) ? DYNAMIC_RECORD : BLOCK_RECORD),
share->keys, &keydef,
(uint) (param->recinfo-param->start_recinfo),
param->start_recinfo,
@@ -11066,7 +11087,7 @@ bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
return create_internal_tmp_table_from_heap2(thd, table, param, error,
ignore_last_dupp_key_error,
maria_hton,
- "converting HEAP to Maria");
+ "converting HEAP to Aria");
}
#else
@@ -11253,7 +11274,8 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
if (table->file->indexes_are_disabled())
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->file->ha_rnd_init_with_error(1))
+ DBUG_RETURN(1);
if (table->no_rows)
{
new_table.file->extra(HA_EXTRA_NO_ROWS);
@@ -11278,7 +11300,7 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
is safe as this is a temporary MyISAM table without timestamp/autoincrement
or partitioning.
*/
- while (!table->file->rnd_next(new_table.record[1]))
+ while (!table->file->ha_rnd_next(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 ;);
@@ -11338,7 +11360,7 @@ free_tmp_table(THD *thd, TABLE *entry)
MEM_ROOT own_root= entry->mem_root;
const char *save_proc_info;
DBUG_ENTER("free_tmp_table");
- DBUG_PRINT("enter",("table: %s",entry->alias));
+ DBUG_PRINT("enter",("table: %s",entry->alias.c_ptr()));
save_proc_info=thd->proc_info;
thd_proc_info(thd, "removing tmp table");
@@ -11823,6 +11845,8 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
}
DBUG_PRINT("info", ("select cond 0x%lx", (ulong)select_cond));
+ update_virtual_fields(join->thd, join_tab->table);
+
if (select_cond)
{
select_cond_result= test(select_cond->val_int());
@@ -12049,6 +12073,8 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last)
join->thd->send_kill_message();
return NESTED_LOOP_KILLED; // Aborted by user /* purecov: inspected */
}
+ if (rc == NESTED_LOOP_OK)
+ update_virtual_fields(join->thd, join_tab->table);
if (rc == NESTED_LOOP_OK &&
(!join_tab->cache.select ||
(err= join_tab->cache.select->skip_record(join->thd)) != 0 ))
@@ -12127,10 +12153,10 @@ int safe_index_read(JOIN_TAB *tab)
{
int error;
TABLE *table= tab->table;
- 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= table->file->ha_index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts),
+ HA_READ_KEY_EXACT)))
return report_error(table, error);
return 0;
}
@@ -12234,8 +12260,8 @@ join_read_system(JOIN_TAB *tab)
int error;
if (table->status & STATUS_GARBAGE) // If first read
{
- if ((error=table->file->read_first_row(table->record[0],
- table->s->primary_key)))
+ if ((error= table->file->ha_read_first_row(table->record[0],
+ table->s->primary_key)))
{
if (error != HA_ERR_END_OF_FILE)
return report_error(table, error);
@@ -12243,6 +12269,7 @@ join_read_system(JOIN_TAB *tab)
empty_record(table); // Make empty record
return -1;
}
+ update_virtual_fields(tab->join->thd, table);
store_record(table,record[1]);
}
else if (!table->status) // Only happens with left join
@@ -12277,10 +12304,10 @@ join_read_const(JOIN_TAB *tab)
error=HA_ERR_KEY_NOT_FOUND;
else
{
- 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);
+ error= table->file->ha_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)
{
@@ -12291,6 +12318,7 @@ join_read_const(JOIN_TAB *tab)
return report_error(table, error);
return -1;
}
+ update_virtual_fields(tab->join->thd, table);
store_record(table,record[1]);
}
else if (!(table->status & ~STATUS_NULL_ROW)) // Only happens with left join
@@ -12334,10 +12362,10 @@ join_read_key(JOIN_TAB *tab)
tab->read_record.file->unlock_row();
tab->ref.has_record= FALSE;
}
- 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);
+ error= table->file->ha_index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts),
+ HA_READ_KEY_EXACT);
if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
return report_error(table, error);
@@ -12418,10 +12446,10 @@ join_read_always_key(JOIN_TAB *tab)
if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
return -1;
- 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= table->file->ha_index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts),
+ HA_READ_KEY_EXACT)))
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
return report_error(table, error);
@@ -12452,9 +12480,10 @@ join_read_last_key(JOIN_TAB *tab)
}
if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
return -1;
- 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= table->file->ha_index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts),
+ HA_READ_PREFIX_LAST)))
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
return report_error(table, error);
@@ -12479,9 +12508,9 @@ join_read_next_same(READ_RECORD *info)
TABLE *table= info->table;
JOIN_TAB *tab=table->reginfo.join_tab;
- if ((error=table->file->index_next_same(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length)))
+ if ((error= table->file->ha_index_next_same(table->record[0],
+ tab->ref.key_buff,
+ tab->ref.key_length)))
{
if (error != HA_ERR_END_OF_FILE)
return report_error(table, error);
@@ -12499,7 +12528,7 @@ join_read_prev_same(READ_RECORD *info)
TABLE *table= info->table;
JOIN_TAB *tab=table->reginfo.join_tab;
- if ((error=table->file->index_prev(table->record[0])))
+ if ((error= table->file->ha_index_prev(table->record[0])))
return report_error(table, error);
if (key_cmp_if_same(table, tab->ref.key_buff, tab->ref.key,
tab->ref.key_length))
@@ -12524,7 +12553,7 @@ int rr_sequential(READ_RECORD *info);
int init_read_record_seq(JOIN_TAB *tab)
{
tab->read_record.read_record= rr_sequential;
- if (tab->read_record.file->ha_rnd_init(1))
+ if (tab->read_record.file->ha_rnd_init_with_error(1))
return 1;
return (*tab->read_record.read_record)(&tab->read_record);
}
@@ -12544,8 +12573,9 @@ join_init_read_record(JOIN_TAB *tab)
{
if (tab->select && tab->select->quick && tab->select->quick->reset())
return 1;
- init_read_record(&tab->read_record, tab->join->thd, tab->table,
- tab->select,1,1, FALSE);
+ if (init_read_record(&tab->read_record, tab->join->thd, tab->table,
+ tab->select,1,1, FALSE))
+ return 1;
return (*tab->read_record.read_record)(&tab->read_record);
}
@@ -12568,7 +12598,7 @@ join_read_first(JOIN_TAB *tab)
error= table->file->ha_index_init(tab->index, tab->sorted);
if (!error)
error= table->file->prepare_index_scan();
- if (error || (error=tab->table->file->index_first(tab->table->record[0])))
+ if (error || (error=tab->table->file->ha_index_first(tab->table->record[0])))
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
report_error(table, error);
@@ -12582,8 +12612,9 @@ static int
join_read_next(READ_RECORD *info)
{
int error;
- if ((error=info->file->index_next(info->record)))
+ if ((error= info->file->ha_index_next(info->record)))
return report_error(info->table, error);
+
return 0;
}
@@ -12606,8 +12637,9 @@ join_read_last(JOIN_TAB *tab)
error= table->file->ha_index_init(tab->index, 1);
if (!error)
error= table->file->prepare_index_scan();
- if (error || (error= tab->table->file->index_last(tab->table->record[0])))
+ if (error || (error= tab->table->file->ha_index_last(tab->table->record[0])))
return report_error(table, error);
+
return 0;
}
@@ -12616,7 +12648,7 @@ static int
join_read_prev(READ_RECORD *info)
{
int error;
- if ((error= info->file->index_prev(info->record)))
+ if ((error= info->file->ha_index_prev(info->record)))
return report_error(info->table, error);
return 0;
}
@@ -12641,7 +12673,7 @@ join_ft_read_first(JOIN_TAB *tab)
#endif
table->file->ft_init();
- if ((error= table->file->ft_read(table->record[0])))
+ if ((error= table->file->ha_ft_read(table->record[0])))
return report_error(table, error);
return 0;
}
@@ -12650,7 +12682,7 @@ static int
join_ft_read_next(READ_RECORD *info)
{
int error;
- if ((error= info->file->ft_read(info->table->record[0])))
+ if ((error= info->file->ha_ft_read(info->table->record[0])))
return report_error(info->table, error);
return 0;
}
@@ -12960,7 +12992,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
int error;
join->found_records++;
- if ((error=table->file->ha_write_row(table->record[0])))
+ if ((error= table->file->ha_write_row(table->record[0])))
{
if (!table->file->is_fatal_error(error, HA_CHECK_DUP))
goto end;
@@ -13015,15 +13047,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_map(table->record[1],
- join->tmp_table_param.group_buff,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT))
+ if (!table->file->ha_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->ha_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 */
@@ -13047,7 +13079,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
init_tmptable_sum_functions(join->sum_funcs);
if (copy_funcs(join->tmp_table_param.items_to_copy, join->thd))
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
- if ((error=table->file->ha_write_row(table->record[0])))
+ if ((error= table->file->ha_write_row(table->record[0])))
{
if (create_internal_tmp_table_from_heap(join->thd, table,
&join->tmp_table_param,
@@ -13089,7 +13121,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (copy_funcs(join->tmp_table_param.items_to_copy, join->thd))
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
- if (!(error=table->file->ha_write_row(table->record[0])))
+ if (!(error= table->file->ha_write_row(table->record[0])))
join->send_records++; // New group
else
{
@@ -13098,15 +13130,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->dup_ref))
+ if (table->file->ha_rnd_pos(table->record[1],table->file->dup_ref))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
restore_record(table,record[1]);
update_tmptable_sum_func(join->sum_funcs,table);
- if ((error=table->file->ha_update_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 */
@@ -14587,8 +14619,10 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
org_record=(char*) (record=table->record[0])+offset;
new_record=(char*) table->record[1]+offset;
- file->ha_rnd_init(1);
- error=file->rnd_next(record);
+ if (file->ha_rnd_init_with_error(1))
+ DBUG_RETURN(1);
+
+ error= file->ha_rnd_next(record);
for (;;)
{
if (thd->killed)
@@ -14601,7 +14635,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
{
if (error == HA_ERR_RECORD_DELETED)
{
- error= file->rnd_next(record);
+ error= file->ha_rnd_next(record);
continue;
}
if (error == HA_ERR_END_OF_FILE)
@@ -14610,9 +14644,9 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
}
if (having && !having->val_int())
{
- if ((error=file->ha_delete_row(record)))
+ if ((error= file->ha_delete_row(record)))
goto err;
- error=file->rnd_next(record);
+ error= file->ha_rnd_next(record);
continue;
}
if (copy_blobs(first_field))
@@ -14627,7 +14661,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
bool found=0;
for (;;)
{
- if ((error=file->rnd_next(record)))
+ if ((error= file->ha_rnd_next(record)))
{
if (error == HA_ERR_RECORD_DELETED)
continue;
@@ -14637,7 +14671,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
}
if (compare_record(table, first_field) == 0)
{
- if ((error=file->ha_delete_row(record)))
+ if ((error= file->ha_delete_row(record)))
goto err;
}
else if (!found)
@@ -14716,7 +14750,9 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
DBUG_RETURN(1);
}
- file->ha_rnd_init(1);
+ if ((error= file->ha_rnd_init(1)))
+ goto err;
+
key_pos=key_buffer;
for (;;)
{
@@ -14727,7 +14763,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
error=0;
goto err;
}
- if ((error=file->rnd_next(record)))
+ if ((error= file->ha_rnd_next(record)))
{
if (error == HA_ERR_RECORD_DELETED)
continue;
@@ -14737,7 +14773,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
}
if (having && !having->val_int())
{
- if ((error=file->ha_delete_row(record)))
+ if ((error= file->ha_delete_row(record)))
goto err;
continue;
}
@@ -14754,7 +14790,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
if (hash_search(&hash, org_key_pos, key_length))
{
/* Duplicated found ; Remove the row */
- if ((error=file->ha_delete_row(record)))
+ if ((error= file->ha_delete_row(record)))
goto err;
}
else
@@ -16180,6 +16216,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);
+ str.extra_allocation(1024);
item->print(&str, QT_ORDINARY);
item_field->name= sql_strmake(str.ptr(),str.length());
}
@@ -17133,8 +17170,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_string(key_info->name,
strlen(key_info->name),
system_charset_info));
- length= longlong2str(tab->ref.key_length, keylen_str_buf, 10) -
- keylen_str_buf;
+ length= (longlong10_to_str(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++)
@@ -17152,8 +17189,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
register uint length;
item_list.push_back(new Item_string(key_info->name,
strlen(key_info->name),cs));
- length= longlong2str(key_info->key_length, keylen_str_buf, 10) -
- keylen_str_buf;
+ length= (longlong10_to_str(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));
diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc
index 2dd27c2b217..61a36d1bbea 100644
--- a/sql/sql_servers.cc
+++ b/sql/sql_servers.cc
@@ -185,8 +185,9 @@ static bool servers_load(THD *thd, TABLE_LIST *tables)
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);
+ if (init_read_record(&read_record_info,thd,table=tables[0].table,NULL,1,0,
+ FALSE))
+ DBUG_RETURN(1);
while (!(read_record_info.read_record(&read_record_info)))
{
/* return_val is already TRUE, so no need to set */
@@ -529,10 +530,10 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server)
system_charset_info);
/* read index until record is that specified in server_name */
- if ((error= table->file->index_read_idx_map(table->record[0], 0,
- (uchar *)table->field[0]->ptr,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT)))
+ if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
+ (uchar *)table->field[0]->ptr,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT)))
{
/* if not found, err */
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
@@ -872,10 +873,10 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server)
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= table->file->ha_index_read_idx_map(table->record[0], 0,
+ (uchar *)table->field[0]->ptr,
+ ~(longlong)0,
+ HA_READ_KEY_EXACT)))
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
table->file->print_error(error, MYF(0));
@@ -929,10 +930,10 @@ delete_server_record(TABLE *table,
/* 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= table->file->ha_index_read_idx_map(table->record[0], 0,
+ (uchar *)table->field[0]->ptr,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT)))
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
table->file->print_error(error, MYF(0));
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index bb9a4a1e062..1d5d53114a3 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -19,10 +19,11 @@
#include "mysql_priv.h"
#include "sql_select.h" // For select_describe
+#include "create_options.h"
#include "sql_show.h"
#include "repl_failsafe.h"
-#include "sp.h"
#include "sp_head.h"
+#include "sp.h"
#include "sql_trigger.h"
#include "authors.h"
#include "contributors.h"
@@ -96,11 +97,21 @@ 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 const LEX_STRING maturity_name[]={
+ { C_STRING_WITH_LEN("Unknown") },
+ { C_STRING_WITH_LEN("Experimental") },
+ { C_STRING_WITH_LEN("Alpha") },
+ { C_STRING_WITH_LEN("Beta") },
+ { C_STRING_WITH_LEN("Gamma") },
+ { C_STRING_WITH_LEN("Stable") }};
+
+
static 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_maria_plugin *plug= plugin_decl(plugin);
struct st_plugin_dl *plugin_dl= plugin_dlib(plugin);
CHARSET_INFO *cs= system_charset_info;
char version_buf[20];
@@ -145,7 +156,7 @@ static my_bool show_plugins(THD *thd, plugin_ref plugin,
table->field[5]->set_notnull();
table->field[6]->store(version_buf,
make_version_string(version_buf, sizeof(version_buf),
- plugin_dl->version),
+ plugin_dl->mariaversion),
cs);
table->field[6]->set_notnull();
}
@@ -188,6 +199,26 @@ static my_bool show_plugins(THD *thd, plugin_ref plugin,
}
table->field[9]->set_notnull();
+ if ((uint) plug->maturity <= MariaDB_PLUGIN_MATURITY_STABLE)
+ table->field[10]->store(maturity_name[plug->maturity].str,
+ maturity_name[plug->maturity].length,
+ cs);
+ else
+ {
+ DBUG_ASSERT(0);
+ table->field[10]->store("Unknown", 7, cs);
+ }
+ table->field[10]->set_notnull();
+
+ if (plug->version_info)
+ {
+ table->field[11]->store(plug->version_info,
+ strlen(plug->version_info), cs);
+ table->field[11]->set_notnull();
+ }
+ else
+ table->field[11]->set_null();
+
return schema_table_store_record(thd, table);
}
@@ -780,7 +811,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
protocol->store(table_list->schema_table->table_name,
system_charset_info);
else
- protocol->store(table_list->table->alias, system_charset_info);
+ protocol->store(table_list->table->alias.c_ptr(), system_charset_info);
}
if (table_list->view)
@@ -826,6 +857,7 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
sctx->master_access);
if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname))
{
+ status_var_increment(thd->status_var.access_denied_errors);
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
sctx->priv_user, sctx->host_or_ip, dbname);
general_log_print(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
@@ -1017,7 +1049,7 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
/*
The identifier must be quoted as it includes a quote character or
- it's a keyword
+ it's a keyword
*/
VOID(packet->reserve(length*2 + 2));
@@ -1143,7 +1175,7 @@ static bool get_field_default_value(THD *thd, TABLE *table,
if (field_type == MYSQL_TYPE_BIT)
{
longlong dec= field->val_int();
- char *ptr= longlong2str(dec, tmp + 2, 2);
+ char *ptr= longlong2str(dec, tmp + 2, 2, 1);
uint32 length= (uint32) (ptr - tmp);
tmp[0]= 'b';
tmp[1]= '\'';
@@ -1177,6 +1209,31 @@ static bool get_field_default_value(THD *thd, TABLE *table,
return has_default;
}
+
+/**
+ Appends list of options to string
+
+ @param thd thread handler
+ @param packet string to append
+ @param opt list of options
+*/
+
+static void append_create_options(THD *thd, String *packet,
+ engine_option_value *opt)
+{
+ for(; opt; opt= opt->next)
+ {
+ DBUG_ASSERT(opt->value.str);
+ packet->append(' ');
+ append_identifier(thd, packet, opt->name.str, opt->name.length);
+ packet->append('=');
+ if (opt->quoted_value)
+ append_unescaped(packet, opt->value.str, opt->value.length);
+ else
+ packet->append(opt->value.str, opt->value.length);
+ }
+}
+
/*
Build a CREATE TABLE statement for a table.
@@ -1243,7 +1300,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
else
{
if (lower_case_table_names == 2)
- alias= table->alias;
+ alias= table->alias.c_ptr();
else
{
alias= share->table_name.str;
@@ -1315,6 +1372,19 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
}
}
+ if (field->vcol_info)
+ {
+ packet->append(STRING_WITH_LEN(" AS ("));
+ packet->append(field->vcol_info->expr_str.str,
+ field->vcol_info->expr_str.length,
+ system_charset_info);
+ packet->append(STRING_WITH_LEN(")"));
+ if (field->stored_in_db)
+ packet->append(STRING_WITH_LEN(" PERSISTENT"));
+ else
+ packet->append(STRING_WITH_LEN(" VIRTUAL"));
+ }
+
if (flags & NOT_NULL_FLAG)
packet->append(STRING_WITH_LEN(" NOT NULL"));
else if (field->type() == MYSQL_TYPE_TIMESTAMP)
@@ -1326,7 +1396,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" NULL"));
}
- if (get_field_default_value(thd, table, field, &def_value, 1))
+ if (!field->vcol_info &&
+ get_field_default_value(thd, table, field, &def_value, 1))
{
packet->append(STRING_WITH_LEN(" DEFAULT "));
packet->append(def_value.ptr(), def_value.length(), system_charset_info);
@@ -1345,6 +1416,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" COMMENT "));
append_unescaped(packet, field->comment.str, field->comment.length);
}
+ append_create_options(thd, packet, field->option_list);
}
key_info= table->key_info;
@@ -1416,6 +1488,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
append_identifier(thd, packet, parser_name->str, parser_name->length);
packet->append(STRING_WITH_LEN(" */ "));
}
+ append_create_options(thd, packet, key_info->option_list);
}
/*
@@ -1577,6 +1650,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" CONNECTION="));
append_unescaped(packet, share->connect_string.str, share->connect_string.length);
}
+ append_create_options(thd, packet, share->option_list);
append_directory(thd, packet, "DATA", create_info.data_file_name);
append_directory(thd, packet, "INDEX", create_info.index_file_name);
}
@@ -1863,7 +1937,9 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
pthread_mutex_lock(&tmp->LOCK_thd_data);
if ((mysys_var= tmp->mysys_var))
pthread_mutex_lock(&mysys_var->mutex);
- thd_info->proc_info= (char*) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0);
+ thd_info->proc_info= (char*) (tmp->killed != THD::NOT_KILLED &&
+ tmp->killed != THD::KILL_BAD_DATA ?
+ "Killed" : 0);
#ifndef EMBEDDED_LIBRARY
thd_info->state_info= (char*) (tmp->locked ? "Locked" :
tmp->net.reading_or_writing ?
@@ -1981,7 +2057,9 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
if ((mysys_var= tmp->mysys_var))
pthread_mutex_lock(&mysys_var->mutex);
/* COMMAND */
- if ((val= (char *) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0)))
+ if ((val= (char *) ((tmp->killed != THD::NOT_KILLED &&
+ tmp->killed != THD::KILL_BAD_DATA ?
+ "Killed" : 0))))
table->field[4]->store(val, strlen(val), cs);
else
table->field[4]->store(command_name[tmp->command].str,
@@ -2213,11 +2291,7 @@ void remove_status_vars(SHOW_VAR *list)
}
}
-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 *variables,
@@ -2256,7 +2330,7 @@ static bool show_status_array(THD *thd, const char *wild,
strnmov(prefix_end, variables->name, len);
name_buffer[sizeof(name_buffer)-1]=0; /* Safety */
if (ucase_names)
- make_upper(name_buffer);
+ my_caseup_str(system_charset_info, name_buffer);
restore_record(table, s->default_values);
table->field[0]->store(name_buffer, strlen(name_buffer),
@@ -2350,14 +2424,6 @@ static bool show_status_array(THD *thd, const char *wild,
end= strend(pos);
break;
}
- case SHOW_KEY_CACHE_LONG:
- value= (char*) dflt_key_cache + (ulong)value;
- end= int10_to_str(*(long*) value, buff, 10);
- break;
- case SHOW_KEY_CACHE_LONGLONG:
- value= (char*) dflt_key_cache + (ulong)value;
- end= longlong10_to_str(*(longlong*) value, buff, 10);
- break;
case SHOW_UNDEF:
break; // Return empty string
case SHOW_SYS: // Cannot happen
@@ -2383,6 +2449,323 @@ end:
DBUG_RETURN(res);
}
+#ifdef COMPLEAT_PATCH_NOT_ADDED_YET
+/*
+ Aggregate values for mapped_user entries by their role.
+
+ SYNOPSIS
+ aggregate_user_stats
+ all_user_stats - input to aggregate
+ agg_user_stats - returns aggregated values
+
+ RETURN
+ 0 - OK
+ 1 - error
+*/
+
+static int aggregate_user_stats(HASH *all_user_stats, HASH *agg_user_stats)
+{
+ DBUG_ENTER("aggregate_user_stats");
+ if (hash_init(agg_user_stats, system_charset_info,
+ max(all_user_stats->records, 1),
+ 0, 0, (hash_get_key)get_key_user_stats,
+ (hash_free_key)free_user_stats, 0))
+ {
+ sql_print_error("Malloc in aggregate_user_stats failed");
+ DBUG_RETURN(1);
+ }
+
+ for (uint i= 0; i < all_user_stats->records; i++)
+ {
+ USER_STATS *user= (USER_STATS*)hash_element(all_user_stats, i);
+ USER_STATS *agg_user;
+ uint name_length= strlen(user->priv_user);
+
+ if (!(agg_user= (USER_STATS*) hash_search(agg_user_stats,
+ (uchar*)user->priv_user,
+ name_length)))
+ {
+ // First entry for this role.
+ if (!(agg_user= (USER_STATS*) my_malloc(sizeof(USER_STATS),
+ MYF(MY_WME | MY_ZEROFILL))))
+ {
+ sql_print_error("Malloc in aggregate_user_stats failed");
+ DBUG_RETURN(1);
+ }
+
+ init_user_stats(agg_user, user->priv_user, name_length,
+ user->priv_user,
+ user->total_connections, user->concurrent_connections,
+ user->connected_time, user->busy_time, user->cpu_time,
+ user->bytes_received, user->bytes_sent,
+ user->binlog_bytes_written,
+ user->rows_sent, user->rows_read,
+ user->rows_inserted, user->rows_deleted,
+ user->rows_updated,
+ user->select_commands, user->update_commands,
+ user->other_commands,
+ user->commit_trans, user->rollback_trans,
+ user->denied_connections, user->lost_connections,
+ user->access_denied_errors, user->empty_queries);
+
+ if (my_hash_insert(agg_user_stats, (uchar*) agg_user))
+ {
+ /* Out of memory */
+ my_free(agg_user, 0);
+ sql_print_error("Malloc in aggregate_user_stats failed");
+ DBUG_RETURN(1);
+ }
+ }
+ else
+ {
+ /* Aggregate with existing values for this role. */
+ add_user_stats(agg_user,
+ user->total_connections, user->concurrent_connections,
+ user->connected_time, user->busy_time, user->cpu_time,
+ user->bytes_received, user->bytes_sent,
+ user->binlog_bytes_written,
+ user->rows_sent, user->rows_read,
+ user->rows_inserted, user->rows_deleted,
+ user->rows_updated,
+ user->select_commands, user->update_commands,
+ user->other_commands,
+ user->commit_trans, user->rollback_trans,
+ user->denied_connections, user->lost_connections,
+ user->access_denied_errors, user->empty_queries);
+ }
+ }
+ DBUG_PRINT("exit", ("aggregated %lu input into %lu output entries",
+ all_user_stats->records, agg_user_stats->records));
+ DBUG_RETURN(0);
+}
+#endif
+
+/*
+ Write result to network for SHOW USER_STATISTICS
+
+ SYNOPSIS
+ send_user_stats
+ all_user_stats - values to return
+ table - I_S table
+
+ RETURN
+ 0 - OK
+ 1 - error
+*/
+
+int send_user_stats(THD* thd, HASH *all_user_stats, TABLE *table)
+{
+ DBUG_ENTER("send_user_stats");
+
+ for (uint i= 0; i < all_user_stats->records; i++)
+ {
+ uint j= 0;
+ USER_STATS *user_stats= (USER_STATS*) hash_element(all_user_stats, i);
+
+ table->field[j++]->store(user_stats->user, user_stats->user_name_length,
+ system_charset_info);
+ table->field[j++]->store((longlong)user_stats->total_connections,TRUE);
+ table->field[j++]->store((longlong)user_stats->concurrent_connections, TRUE);
+ table->field[j++]->store((longlong)user_stats->connected_time, TRUE);
+ table->field[j++]->store((double)user_stats->busy_time);
+ table->field[j++]->store((double)user_stats->cpu_time);
+ table->field[j++]->store((longlong)user_stats->bytes_received, TRUE);
+ table->field[j++]->store((longlong)user_stats->bytes_sent, TRUE);
+ table->field[j++]->store((longlong)user_stats->binlog_bytes_written, TRUE);
+ table->field[j++]->store((longlong)user_stats->rows_read, TRUE);
+ table->field[j++]->store((longlong)user_stats->rows_sent, TRUE);
+ table->field[j++]->store((longlong)user_stats->rows_deleted, TRUE);
+ table->field[j++]->store((longlong)user_stats->rows_inserted, TRUE);
+ table->field[j++]->store((longlong)user_stats->rows_updated, TRUE);
+ table->field[j++]->store((longlong)user_stats->select_commands, TRUE);
+ table->field[j++]->store((longlong)user_stats->update_commands, TRUE);
+ table->field[j++]->store((longlong)user_stats->other_commands, TRUE);
+ table->field[j++]->store((longlong)user_stats->commit_trans, TRUE);
+ table->field[j++]->store((longlong)user_stats->rollback_trans, TRUE);
+ table->field[j++]->store((longlong)user_stats->denied_connections, TRUE);
+ table->field[j++]->store((longlong)user_stats->lost_connections, TRUE);
+ table->field[j++]->store((longlong)user_stats->access_denied_errors, TRUE);
+ table->field[j++]->store((longlong)user_stats->empty_queries, TRUE);
+ if (schema_table_store_record(thd, table))
+ {
+ DBUG_PRINT("error", ("store record error"));
+ DBUG_RETURN(1);
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+/*
+ Process SHOW USER_STATISTICS
+
+ SYNOPSIS
+ mysqld_show_user_stats
+ thd - current thread
+ wild - limit results to the entry for this user
+ with_roles - when true, display role for mapped users
+
+ RETURN
+ 0 - OK
+ 1 - error
+*/
+
+int fill_schema_user_stats(THD* thd, TABLE_LIST* tables, COND* cond)
+{
+ TABLE *table= tables->table;
+ int result;
+ DBUG_ENTER("fill_schema_user_stats");
+
+ if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
+ DBUG_RETURN(1);
+
+ /*
+ Iterates through all the global stats and sends them to the client.
+ Pattern matching on the client IP is supported.
+ */
+
+ pthread_mutex_lock(&LOCK_global_user_client_stats);
+ result= send_user_stats(thd, &global_user_stats, table) != 0;
+ pthread_mutex_unlock(&LOCK_global_user_client_stats);
+
+ DBUG_PRINT("exit", ("result: %d", result));
+ DBUG_RETURN(result);
+}
+
+/*
+ Process SHOW CLIENT_STATISTICS
+
+ SYNOPSIS
+ mysqld_show_client_stats
+ thd - current thread
+ wild - limit results to the entry for this client
+
+ RETURN
+ 0 - OK
+ 1 - error
+*/
+
+int fill_schema_client_stats(THD* thd, TABLE_LIST* tables, COND* cond)
+{
+ TABLE *table= tables->table;
+ int result;
+ DBUG_ENTER("fill_schema_client_stats");
+
+ if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
+ DBUG_RETURN(1);
+
+ /*
+ Iterates through all the global stats and sends them to the client.
+ Pattern matching on the client IP is supported.
+ */
+
+ pthread_mutex_lock(&LOCK_global_user_client_stats);
+ result= send_user_stats(thd, &global_client_stats, table) != 0;
+ pthread_mutex_unlock(&LOCK_global_user_client_stats);
+
+ DBUG_PRINT("exit", ("result: %d", result));
+ DBUG_RETURN(result);
+}
+
+
+/* Fill information schema table with table statistics */
+
+int fill_schema_table_stats(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+ TABLE *table= tables->table;
+ DBUG_ENTER("fill_schema_table_stats");
+
+ pthread_mutex_lock(&LOCK_global_table_stats);
+ for (uint i= 0; i < global_table_stats.records; i++)
+ {
+ char *end_of_schema;
+ TABLE_STATS *table_stats=
+ (TABLE_STATS*)hash_element(&global_table_stats, i);
+ TABLE_LIST tmp_table;
+ size_t schema_length, table_name_length;
+
+ end_of_schema= strend(table_stats->table);
+ schema_length= (size_t) (end_of_schema - table_stats->table);
+ table_name_length= strlen(table_stats->table + schema_length + 1);
+
+ bzero((char*) &tmp_table,sizeof(tmp_table));
+ tmp_table.db= table_stats->table;
+ tmp_table.table_name= end_of_schema+1;
+ tmp_table.grant.privilege= 0;
+ if (check_access(thd, SELECT_ACL | EXTRA_ACL, tmp_table.db,
+ &tmp_table.grant.privilege, 0, 0,
+ is_schema_db(tmp_table.db)) ||
+ check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX,
+ 1))
+ continue;
+
+ table->field[0]->store(table_stats->table, schema_length,
+ system_charset_info);
+ table->field[1]->store(table_stats->table + schema_length+1,
+ table_name_length, system_charset_info);
+ table->field[2]->store((longlong)table_stats->rows_read, TRUE);
+ table->field[3]->store((longlong)table_stats->rows_changed, TRUE);
+ table->field[4]->store((longlong)table_stats->rows_changed_x_indexes,
+ TRUE);
+ if (schema_table_store_record(thd, table))
+ {
+ VOID(pthread_mutex_unlock(&LOCK_global_table_stats));
+ DBUG_RETURN(1);
+ }
+ }
+ pthread_mutex_unlock(&LOCK_global_table_stats);
+ DBUG_RETURN(0);
+}
+
+
+/* Fill information schema table with index statistics */
+
+int fill_schema_index_stats(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+ TABLE *table= tables->table;
+ DBUG_ENTER("fill_schema_index_stats");
+
+ pthread_mutex_lock(&LOCK_global_index_stats);
+ for (uint i= 0; i < global_index_stats.records; i++)
+ {
+ INDEX_STATS *index_stats =
+ (INDEX_STATS*) hash_element(&global_index_stats, i);
+ TABLE_LIST tmp_table;
+ char *index_name;
+ size_t schema_name_length, table_name_length, index_name_length;
+
+ bzero((char*) &tmp_table,sizeof(tmp_table));
+ tmp_table.db= index_stats->index;
+ tmp_table.table_name= strend(index_stats->index)+1;
+ tmp_table.grant.privilege= 0;
+ if (check_access(thd, SELECT_ACL | EXTRA_ACL, tmp_table.db,
+ &tmp_table.grant.privilege, 0, 0,
+ is_schema_db(tmp_table.db)) ||
+ check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
+ continue;
+
+ index_name= strend(tmp_table.table_name)+1;
+ schema_name_length= (tmp_table.table_name - index_stats->index) -1;
+ table_name_length= (index_name - tmp_table.table_name)-1;
+ index_name_length= (index_stats->index_name_length - schema_name_length -
+ table_name_length - 3);
+
+ table->field[0]->store(tmp_table.db, schema_name_length,
+ system_charset_info);
+ table->field[1]->store(tmp_table.table_name, table_name_length,
+ system_charset_info);
+ table->field[2]->store(index_name, index_name_length, system_charset_info);
+ table->field[3]->store((longlong)index_stats->rows_read, TRUE);
+
+ if (schema_table_store_record(thd, table))
+ {
+ VOID(pthread_mutex_unlock(&LOCK_global_index_stats));
+ DBUG_RETURN(1);
+ }
+ }
+ pthread_mutex_unlock(&LOCK_global_index_stats);
+ DBUG_RETURN(0);
+}
+
/* collect status for all running threads */
@@ -3722,7 +4105,8 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
}
else
{
- char option_buff[350],*ptr;
+ char option_buff[350];
+ String str(option_buff,sizeof(option_buff), system_charset_info);
TABLE *show_table= tables->table;
TABLE_SHARE *share= show_table->s;
handler *file= show_table->file;
@@ -3755,53 +4139,58 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
table->field[4]->store(tmp_buff, strlen(tmp_buff), cs);
table->field[5]->store((longlong) share->frm_version, TRUE);
- ptr=option_buff;
+ str.length(0);
if (share->min_rows)
{
- ptr=strmov(ptr," min_rows=");
- ptr=longlong10_to_str(share->min_rows,ptr,10);
+ str.qs_append(STRING_WITH_LEN(" min_rows="));
+ str.qs_append(share->min_rows);
}
if (share->max_rows)
{
- ptr=strmov(ptr," max_rows=");
- ptr=longlong10_to_str(share->max_rows,ptr,10);
+ str.qs_append(STRING_WITH_LEN(" max_rows="));
+ str.qs_append(share->max_rows);
}
if (share->avg_row_length)
{
- ptr=strmov(ptr," avg_row_length=");
- ptr=longlong10_to_str(share->avg_row_length,ptr,10);
+ str.qs_append(STRING_WITH_LEN(" avg_row_length="));
+ str.qs_append(share->avg_row_length);
}
if (share->db_create_options & HA_OPTION_PACK_KEYS)
- ptr=strmov(ptr," pack_keys=1");
+ str.qs_append(STRING_WITH_LEN(" pack_keys=1"));
if (share->db_create_options & HA_OPTION_NO_PACK_KEYS)
- ptr=strmov(ptr," pack_keys=0");
+ str.qs_append(STRING_WITH_LEN(" pack_keys=0"));
/* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */
if (share->db_create_options & HA_OPTION_CHECKSUM)
- ptr=strmov(ptr," checksum=1");
+ str.qs_append(STRING_WITH_LEN(" checksum=1"));
if (share->page_checksum != HA_CHOICE_UNDEF)
- ptr= strxmov(ptr, " page_checksum=",
- ha_choice_values[(uint) share->page_checksum], NullS);
+ {
+ str.qs_append(STRING_WITH_LEN(" page_checksum="));
+ str.qs_append(ha_choice_values[(uint) share->page_checksum]);
+ }
if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE)
- ptr=strmov(ptr," delay_key_write=1");
+ str.qs_append(STRING_WITH_LEN(" delay_key_write=1"));
if (share->row_type != ROW_TYPE_DEFAULT)
- ptr=strxmov(ptr, " row_format=",
- ha_row_type[(uint) share->row_type],
- NullS);
+ {
+ str.qs_append(STRING_WITH_LEN(" row_format="));
+ str.qs_append(ha_row_type[(uint) share->row_type]);
+ }
if (share->key_block_size)
{
- ptr= strmov(ptr, " key_block_size=");
- ptr= longlong10_to_str(share->key_block_size, ptr, 10);
+ str.qs_append(STRING_WITH_LEN(" key_block_size="));
+ str.qs_append(share->key_block_size);
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (is_partitioned)
- ptr= strmov(ptr, " partitioned");
+ str.qs_append(STRING_WITH_LEN(" 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);
+ {
+ str.qs_append(STRING_WITH_LEN(" transactional="));
+ str.qs_append(ha_choice_values[(uint) share->transactional]);
+ }
+ append_create_options(thd, &str, share->option_list);
+ if (str.length())
+ table->field[19]->store(str.ptr()+1, str.length()-1, cs);
tmp_buff= (share->table_charset ?
share->table_charset->name : "default");
@@ -4114,6 +4503,8 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
field->unireg_check != Field::TIMESTAMP_DN_FIELD)
table->field[16]->store(STRING_WITH_LEN("on update CURRENT_TIMESTAMP"),
cs);
+ if (field->vcol_info)
+ table->field[16]->store(STRING_WITH_LEN("VIRTUAL"), cs);
table->field[18]->store(field->comment.str, field->comment.length, cs);
if (schema_table_store_record(thd, table))
@@ -4170,7 +4561,7 @@ static my_bool iter_schema_engines(THD *thd, plugin_ref plugin,
if (plugin_state(plugin) != PLUGIN_IS_READY)
{
- struct st_mysql_plugin *plug= plugin_decl(plugin);
+ struct st_maria_plugin *plug= plugin_decl(plugin);
if (!(wild && wild[0] &&
wild_case_compare(scs, plug->name,wild)))
{
@@ -4412,7 +4803,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
DBUG_RETURN(1);
}
proc_table->file->ha_index_init(0, 1);
- if ((res= proc_table->file->index_first(proc_table->record[0])))
+ if ((res= proc_table->file->ha_index_first(proc_table->record[0])))
{
res= (res == HA_ERR_END_OF_FILE) ? 0 : 1;
goto err;
@@ -4422,7 +4813,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
res= 1;
goto err;
}
- while (!proc_table->file->index_next(proc_table->record[0]))
+ while (!proc_table->file->ha_index_next(proc_table->record[0]))
{
if (store_schema_proc(thd, table, proc_table, wild, full_access, definer))
{
@@ -5330,7 +5721,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
if (et.load_from_row(thd, event_table))
{
- my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), event_table->alias);
+ my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), event_table->alias.c_ptr());
DBUG_RETURN(1);
}
@@ -5659,6 +6050,81 @@ struct schema_table_ref
ST_SCHEMA_TABLE *schema_table;
};
+ST_FIELD_INFO user_stats_fields_info[]=
+{
+ {"USER", USERNAME_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
+ {"TOTAL_CONNECTIONS", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections",SKIP_OPEN_TABLE},
+ {"CONCURRENT_CONNECTIONS", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections",SKIP_OPEN_TABLE},
+ {"CONNECTED_TIME", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time",SKIP_OPEN_TABLE},
+ {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Busy_time",SKIP_OPEN_TABLE},
+ {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Cpu_time",SKIP_OPEN_TABLE},
+ {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Bytes_received",SKIP_OPEN_TABLE},
+ {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Bytes_sent",SKIP_OPEN_TABLE},
+ {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Binlog_bytes_written",SKIP_OPEN_TABLE},
+ {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE},
+ {"ROWS_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_sent",SKIP_OPEN_TABLE},
+ {"ROWS_DELETED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_deleted",SKIP_OPEN_TABLE},
+ {"ROWS_INSERTED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_inserted",SKIP_OPEN_TABLE},
+ {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_updated",SKIP_OPEN_TABLE},
+ {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Select_commands",SKIP_OPEN_TABLE},
+ {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Update_commands",SKIP_OPEN_TABLE},
+ {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Other_commands",SKIP_OPEN_TABLE},
+ {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Commit_transactions",SKIP_OPEN_TABLE},
+ {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rollback_transactions",SKIP_OPEN_TABLE},
+ {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Denied_connections",SKIP_OPEN_TABLE},
+ {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Lost_connections",SKIP_OPEN_TABLE},
+ {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Access_denied",SKIP_OPEN_TABLE},
+ {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Empty_queries",SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
+};
+
+ST_FIELD_INFO client_stats_fields_info[]=
+{
+ {"CLIENT", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Client",SKIP_OPEN_TABLE},
+ {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Total_connections",SKIP_OPEN_TABLE},
+ {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Concurrent_connections",SKIP_OPEN_TABLE},
+ {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Connected_time",SKIP_OPEN_TABLE},
+ {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Busy_time",SKIP_OPEN_TABLE},
+ {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Cpu_time",SKIP_OPEN_TABLE},
+ {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Bytes_received",SKIP_OPEN_TABLE},
+ {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Bytes_sent",SKIP_OPEN_TABLE},
+ {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Binlog_bytes_written",SKIP_OPEN_TABLE},
+ {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE},
+ {"ROWS_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_sent",SKIP_OPEN_TABLE},
+ {"ROWS_DELETED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_deleted",SKIP_OPEN_TABLE},
+ {"ROWS_INSERTED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_inserted",SKIP_OPEN_TABLE},
+ {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_updated",SKIP_OPEN_TABLE},
+ {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Select_commands",SKIP_OPEN_TABLE},
+ {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Update_commands",SKIP_OPEN_TABLE},
+ {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Other_commands",SKIP_OPEN_TABLE},
+ {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Commit_transactions",SKIP_OPEN_TABLE},
+ {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rollback_transactions",SKIP_OPEN_TABLE},
+ {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Denied_connections",SKIP_OPEN_TABLE},
+ {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Lost_connections",SKIP_OPEN_TABLE},
+ {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Access_denied",SKIP_OPEN_TABLE},
+ {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Empty_queries",SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
+};
+
+
+ST_FIELD_INFO table_stats_fields_info[]=
+{
+ {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema",SKIP_OPEN_TABLE},
+ {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name",SKIP_OPEN_TABLE},
+ {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE},
+ {"ROWS_CHANGED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_changed",SKIP_OPEN_TABLE},
+ {"ROWS_CHANGED_X_INDEXES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_changed_x_#indexes",SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
+};
+
+ST_FIELD_INFO index_stats_fields_info[]=
+{
+ {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema",SKIP_OPEN_TABLE},
+ {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name",SKIP_OPEN_TABLE},
+ {"INDEX_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Index_name",SKIP_OPEN_TABLE},
+ {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0,0}
+};
/*
Find schema_tables elment by name
@@ -6298,6 +6764,90 @@ int fill_schema_files(THD *thd, TABLE_LIST *tables, COND *cond)
}
+static
+int store_key_cache_table_record(THD *thd, TABLE *table,
+ const char *name, uint name_length,
+ KEY_CACHE *key_cache,
+ uint partitions, uint partition_no)
+{
+ KEY_CACHE_STATISTICS keycache_stats;
+ uint err;
+ DBUG_ENTER("store_key_cache_table_record");
+
+ get_key_cache_statistics(key_cache, partition_no, &keycache_stats);
+
+ if (!key_cache->key_cache_inited || keycache_stats.mem_size == 0)
+ DBUG_RETURN(0);
+
+ restore_record(table, s->default_values);
+ table->field[0]->store(name, name_length, system_charset_info);
+ if (partitions == 0)
+ table->field[1]->set_null();
+ else
+ {
+ table->field[1]->set_notnull();
+ table->field[1]->store((long) partitions, TRUE);
+ }
+
+ if (partition_no == 0)
+ table->field[2]->set_null();
+ else
+ {
+ table->field[2]->set_notnull();
+ table->field[2]->store((long) partition_no, TRUE);
+ }
+ table->field[3]->store(keycache_stats.mem_size, TRUE);
+ table->field[4]->store(keycache_stats.block_size, TRUE);
+ table->field[5]->store(keycache_stats.blocks_used, TRUE);
+ table->field[6]->store(keycache_stats.blocks_unused, TRUE);
+ table->field[7]->store(keycache_stats.blocks_changed, TRUE);
+ table->field[8]->store(keycache_stats.read_requests, TRUE);
+ table->field[9]->store(keycache_stats.reads, TRUE);
+ table->field[10]->store(keycache_stats.write_requests, TRUE);
+ table->field[11]->store(keycache_stats.writes, TRUE);
+
+ err= schema_table_store_record(thd, table);
+ DBUG_RETURN(err);
+}
+
+
+int fill_key_cache_tables(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+ TABLE *table= tables->table;
+ I_List_iterator<NAMED_LIST> it(key_caches);
+ NAMED_LIST *element;
+ DBUG_ENTER("fill_key_cache_tables");
+
+ while ((element= it++))
+ {
+ KEY_CACHE *key_cache= (KEY_CACHE *) element->data;
+
+ if (!key_cache->key_cache_inited)
+ continue;
+
+ uint partitions= key_cache->partitions;
+ DBUG_ASSERT(partitions <= MAX_KEY_CACHE_PARTITIONS);
+
+ if (partitions)
+ {
+ for (uint i= 0; i < partitions; i++)
+ {
+ if (store_key_cache_table_record(thd, table,
+ element->name, element->name_length,
+ key_cache, partitions, i+1))
+ DBUG_RETURN(1);
+ }
+ }
+
+ if (store_key_cache_table_record(thd, table,
+ element->name, element->name_length,
+ key_cache, partitions, 0))
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
ST_FIELD_INFO schema_fields_info[]=
{
{"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
@@ -6783,6 +7333,8 @@ ST_FIELD_INFO plugin_fields_info[]=
{"PLUGIN_AUTHOR", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
{"PLUGIN_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
{"PLUGIN_LICENSE", 80, MYSQL_TYPE_STRING, 0, 1, "License", SKIP_OPEN_TABLE},
+ {"PLUGIN_MATURITY", 12, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"PLUGIN_AUTH_VERSION", 80, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
@@ -6875,6 +7427,35 @@ ST_FIELD_INFO referential_constraints_fields_info[]=
};
+ST_FIELD_INFO keycache_fields_info[]=
+{
+ {"KEY_CACHE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"SEGMENTS", 3, MYSQL_TYPE_LONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED) , 0, SKIP_OPEN_TABLE},
+ {"SEGMENT_NUMBER", 3, MYSQL_TYPE_LONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE},
+ {"FULL_SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE},
+ {"BLOCK_SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE },
+ {"USED_BLOCKS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_UNSIGNED), "Key_blocks_used", SKIP_OPEN_TABLE},
+ {"UNUSED_BLOCKS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_UNSIGNED), "Key_blocks_unused", SKIP_OPEN_TABLE},
+ {"DIRTY_BLOCKS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_UNSIGNED), "Key_blocks_not_flushed", SKIP_OPEN_TABLE},
+ {"READ_REQUESTS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_UNSIGNED), "Key_read_requests", SKIP_OPEN_TABLE},
+ {"READS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_UNSIGNED), "Key_reads", SKIP_OPEN_TABLE},
+ {"WRITE_REQUESTS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_UNSIGNED), "Key_write_requests", SKIP_OPEN_TABLE},
+ {"WRITES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_UNSIGNED), "Key_writes", SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+
/*
Description of ST_FIELD_INFO in table.h
@@ -6886,6 +7467,8 @@ 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, 0},
+ {"CLIENT_STATISTICS", client_stats_fields_info, create_schema_table,
+ fill_schema_client_stats, make_old_format, 0, -1, -1, 0, 0},
{"COLLATIONS", collation_fields_info, create_schema_table,
fill_schema_collation, make_old_format, 0, -1, -1, 0, 0},
{"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info,
@@ -6910,6 +7493,10 @@ ST_SCHEMA_TABLE schema_tables[]=
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},
+ {"INDEX_STATISTICS", index_stats_fields_info, create_schema_table,
+ fill_schema_index_stats, make_old_format, 0, -1, -1, 0, 0},
+ {"KEY_CACHES", keycache_fields_info, create_schema_table,
+ fill_key_cache_tables, make_old_format, 0, -1,-1, 0, 0},
{"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table,
get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0,
OPEN_TABLE_ONLY},
@@ -6951,11 +7538,15 @@ ST_SCHEMA_TABLE schema_tables[]=
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, 0},
+ {"TABLE_STATISTICS", table_stats_fields_info, create_schema_table,
+ fill_schema_table_stats, make_old_format, 0, -1, -1, 0, 0},
{"TRIGGERS", triggers_fields_info, create_schema_table,
get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0,
OPEN_TABLE_ONLY},
{"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table,
fill_schema_user_privileges, 0, 0, -1, -1, 0, 0},
+ {"USER_STATISTICS", user_stats_fields_info, create_schema_table,
+ fill_schema_user_stats, make_old_format, 0, -1, -1, 0, 0},
{"VARIABLES", variables_fields_info, create_schema_table, fill_variables,
make_old_format, 0, 0, -1, 1, 0},
{"VIEWS", view_fields_info, create_schema_table,
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index d56766f8994..c14dee91c73 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -28,9 +28,11 @@
#ifdef HAVE_FCONVERT
#include <floatingpoint.h>
#endif
-
#include "sql_string.h"
+#ifdef MYSQL_CLIENT
+#error Attempt to use server-side sql_string on client. Use client/sql_string.cc
+#endif
/*****************************************************************************
** String functions
*****************************************************************************/
@@ -447,7 +449,7 @@ bool String::append(const String &s)
{
if (s.length())
{
- if (realloc(str_length+s.length()))
+ if (realloc_with_extra_if_needed(str_length+s.length()))
return TRUE;
memcpy(Ptr+str_length,s.ptr(),s.length());
str_length+=s.length();
@@ -472,7 +474,7 @@ bool String::append(const char *s,uint32 arg_length)
{
uint32 add_length=arg_length * str_charset->mbmaxlen;
uint dummy_errors;
- if (realloc(str_length+ add_length))
+ if (realloc_with_extra_if_needed(str_length+ add_length))
return TRUE;
str_length+= copy_and_convert(Ptr+str_length, add_length, str_charset,
s, arg_length, &my_charset_latin1,
@@ -483,7 +485,7 @@ bool String::append(const char *s,uint32 arg_length)
/*
For an ASCII compatinble string we can just append.
*/
- if (realloc(str_length+arg_length))
+ if (realloc_with_extra_if_needed(str_length+arg_length))
return TRUE;
memcpy(Ptr+str_length,s,arg_length);
str_length+=arg_length;
@@ -514,14 +516,14 @@ bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs)
{
uint32 add_length= arg_length / cs->mbminlen * str_charset->mbmaxlen;
uint dummy_errors;
- if (realloc(str_length + add_length))
+ if (realloc_with_extra_if_needed(str_length + add_length))
return TRUE;
str_length+= copy_and_convert(Ptr+str_length, add_length, str_charset,
s, arg_length, cs, &dummy_errors);
}
else
{
- if (realloc(str_length + arg_length))
+ if (realloc_with_extra_if_needed(str_length + arg_length))
return TRUE;
memcpy(Ptr + str_length, s, arg_length);
str_length+= arg_length;
@@ -533,7 +535,7 @@ bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs)
#ifdef TO_BE_REMOVED
bool String::append(FILE* file, uint32 arg_length, myf my_flags)
{
- if (realloc(str_length+arg_length))
+ if (realloc_with_extra_if_needed(str_length+arg_length))
return TRUE;
if (my_fread(file, (uchar*) Ptr + str_length, arg_length, my_flags))
{
@@ -547,7 +549,7 @@ bool String::append(FILE* file, uint32 arg_length, myf my_flags)
bool String::append(IO_CACHE* file, uint32 arg_length)
{
- if (realloc(str_length+arg_length))
+ if (realloc_with_extra_if_needed(str_length+arg_length))
return TRUE;
if (my_b_read(file, (uchar*) Ptr + str_length, arg_length))
{
@@ -563,7 +565,7 @@ bool String::append_with_prefill(const char *s,uint32 arg_length,
{
int t_length= arg_length > full_length ? arg_length : full_length;
- if (realloc(str_length + t_length))
+ if (realloc_with_extra_if_needed(str_length + t_length))
return TRUE;
t_length= full_length - arg_length;
if (t_length > 0)
@@ -672,7 +674,7 @@ bool String::replace(uint32 offset,uint32 arg_length,
{
if (diff)
{
- if (realloc(str_length+(uint32) diff))
+ if (realloc_with_extra_if_needed(str_length+(uint32) diff))
return TRUE;
bmove_upp((uchar*) Ptr+str_length+diff, (uchar*) Ptr+str_length,
str_length-offset-arg_length);
@@ -723,10 +725,10 @@ void String::qs_append(int i)
str_length+= (int) (end-buff);
}
-void String::qs_append(uint i)
+void String::qs_append(ulonglong i)
{
char *buff= Ptr + str_length;
- char *end= int10_to_str(i, buff, 10);
+ char *end= longlong10_to_str(i, buff,10);
str_length+= (int) (end-buff);
}
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 44e7c1894bb..a4bc2f53f3a 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -1,5 +1,6 @@
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2008-2011 Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,6 +18,9 @@
/* This file is originally from the mysql distribution. Coded by monty */
+#ifndef MYSQL_SQL_STRING_H_INCLUDED
+#define MYSQL_SQL_STRING_H_INCLUDED
+
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
@@ -25,6 +29,10 @@
#define NOT_FIXED_DEC 31
#endif
+#ifdef MYSQL_CLIENT
+#error Attempt to use server-side sql_string on client. Use client/sql_string.h
+#endif
+
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);
@@ -49,23 +57,24 @@ uint convert_to_printable(char *to, size_t to_len,
class String
{
char *Ptr;
- uint32 str_length,Alloced_length;
+ uint32 str_length,Alloced_length, extra_alloc;
bool alloced;
CHARSET_INFO *str_charset;
public:
String()
{
- Ptr=0; str_length=Alloced_length=0; alloced=0;
+ Ptr=0; str_length=Alloced_length=extra_alloc=0; alloced=0;
str_charset= &my_charset_bin;
}
String(uint32 length_arg)
{
- alloced=0; Alloced_length=0; (void) real_alloc(length_arg);
+ alloced=0; Alloced_length= extra_alloc= 0; (void) real_alloc(length_arg);
str_charset= &my_charset_bin;
}
String(const char *str, CHARSET_INFO *cs)
{
- Ptr=(char*) str; str_length=(uint) strlen(str); Alloced_length=0; alloced=0;
+ Ptr=(char*) str; str_length= (uint32) strlen(str);
+ Alloced_length= extra_alloc= 0; alloced=0;
str_charset=cs;
}
/*
@@ -75,18 +84,18 @@ public:
*/
String(const char *str,uint32 len, CHARSET_INFO *cs)
{
- Ptr=(char*) str; str_length=len; Alloced_length=0; alloced=0;
+ Ptr=(char*) str; str_length=len; Alloced_length= extra_alloc=0; alloced=0;
str_charset=cs;
}
String(char *str,uint32 len, CHARSET_INFO *cs)
{
- Ptr=(char*) str; Alloced_length=str_length=len; alloced=0;
+ Ptr=(char*) str; Alloced_length=str_length=len; extra_alloc= 0; alloced=0;
str_charset=cs;
}
String(const String &str)
{
Ptr=str.Ptr ; str_length=str.str_length ;
- Alloced_length=str.Alloced_length; alloced=0;
+ Alloced_length=str.Alloced_length; extra_alloc= 0; alloced=0;
str_charset=str.str_charset;
}
static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
@@ -106,8 +115,10 @@ public:
inline CHARSET_INFO *charset() const { return str_charset; }
inline uint32 length() const { return str_length;}
inline uint32 alloced_length() const { return Alloced_length;}
+ inline uint32 extra_allocation() const { return extra_alloc;}
inline char& operator [] (uint32 i) const { return Ptr[i]; }
inline void length(uint32 len) { str_length=len ; }
+ inline void extra_allocation(uint32 len) { extra_alloc= len; }
inline bool is_empty() const { return (str_length == 0); }
inline void mark_as_const() { Alloced_length= 0;}
inline const char *ptr() const { return Ptr; }
@@ -139,11 +150,9 @@ public:
{
DBUG_ASSERT(&str != this);
free();
- Ptr=(char*) str.ptr()+offset; str_length=arg_length; alloced=0;
+ Ptr=(char*) str.ptr()+offset; str_length=arg_length;
if (str.Alloced_length)
Alloced_length=str.Alloced_length-offset;
- else
- Alloced_length=0;
str_charset=str.str_charset;
}
@@ -159,13 +168,13 @@ public:
inline void set(char *str,uint32 arg_length, CHARSET_INFO *cs)
{
free();
- Ptr=(char*) str; str_length=Alloced_length=arg_length ; alloced=0;
+ Ptr=(char*) str; str_length=Alloced_length=arg_length;
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;
+ Ptr=(char*) str; str_length=arg_length;
str_charset=cs;
}
bool set_ascii(const char *str, uint32 arg_length);
@@ -216,11 +225,11 @@ public:
if (alloced)
{
alloced=0;
- Alloced_length=0;
my_free(Ptr,MYF(0));
- Ptr=0;
- str_length=0; /* Safety */
}
+ Alloced_length= extra_alloc= 0;
+ Ptr=0;
+ str_length=0; /* Safety */
}
inline bool alloc(uint32 arg_length)
{
@@ -230,9 +239,21 @@ public:
}
bool real_alloc(uint32 arg_length); // Empties old string
bool realloc(uint32 arg_length);
- inline void shrink(uint32 arg_length) // Shrink buffer
+ bool realloc_with_extra(uint32 arg_length)
+ {
+ if (extra_alloc < 4096)
+ extra_alloc= extra_alloc*2+128;
+ return realloc(arg_length + extra_alloc);
+ }
+ bool realloc_with_extra_if_needed(uint32 arg_length)
{
if (arg_length < Alloced_length)
+ return 0;
+ return realloc_with_extra(arg_length);
+ }
+ inline void shrink(uint32 arg_length) // Shrink buffer
+ {
+ if (ALIGN_SIZE(arg_length+1) < Alloced_length)
{
char *new_ptr;
if (!(new_ptr=(char*) my_realloc(Ptr,arg_length,MYF(0))))
@@ -259,7 +280,6 @@ public:
DBUG_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;
}
@@ -275,6 +295,14 @@ public:
bool set_or_copy_aligned(const char *s, uint32 arg_length, CHARSET_INFO *cs);
bool copy(const char*s,uint32 arg_length, CHARSET_INFO *csfrom,
CHARSET_INFO *csto, uint *errors);
+ void move(String &s)
+ {
+ free();
+ Ptr=s.Ptr ; str_length=s.str_length ; Alloced_length=s.Alloced_length;
+ extra_alloc= s.extra_alloc;
+ alloced= s.alloced;
+ s.alloced= 0;
+ }
bool append(const String &s);
bool append(const char *s);
bool append(const char *s,uint32 arg_length);
@@ -294,7 +322,7 @@ public:
}
else
{
- if (realloc(str_length+1))
+ if (realloc_with_extra(str_length + 1))
return 1;
Ptr[str_length++]=chr;
}
@@ -305,6 +333,7 @@ public:
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);
+ friend class Field;
uint32 numchars();
int charpos(int i,uint32 offset=0);
@@ -349,6 +378,10 @@ public:
int4store(Ptr + position,value);
}
+ void qs_append(const char *str)
+ {
+ qs_append(str, (uint32)strlen(str));
+ }
void qs_append(const char *str, uint32 len);
void qs_append(double d);
void qs_append(double *d);
@@ -358,7 +391,15 @@ public:
str_length++;
}
void qs_append(int i);
- void qs_append(uint i);
+ void qs_append(uint i)
+ {
+ qs_append((ulonglong)i);
+ }
+ void qs_append(ulong i)
+ {
+ qs_append((ulonglong)i);
+ }
+ void qs_append(ulonglong i);
/* Inline (general) functions used by the protocol functions */
@@ -413,3 +454,5 @@ static inline bool check_if_only_end_space(CHARSET_INFO *cs, char *str,
{
return str+ cs->cset->scan(cs, str, end, MY_SEQ_SPACES) == end;
}
+
+#endif // MYSQL_SQL_STRING_H_INCLUDED
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 7328157182d..bb062615966 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -21,6 +21,7 @@
#include <hash.h>
#include <myisam.h>
#include <my_dir.h>
+#include "create_options.h"
#include "sp_head.h"
#include "sql_trigger.h"
#include "sql_show.h"
@@ -45,17 +46,10 @@ static int copy_data_between_tables(TABLE *from,TABLE *to,
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);
+static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *,
+ bool, uint *, handler *, KEY **, uint *, int);
+static bool mysql_prepare_alter_table(THD *, TABLE *, HA_CREATE_INFO *,
+ Alter_info *);
static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list);
#ifndef DBUG_OFF
@@ -2121,7 +2115,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
{
if (!foreign_key_error)
my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
- wrong_tables.c_ptr());
+ wrong_tables.c_ptr_safe());
else
my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
error= 1;
@@ -2258,10 +2252,10 @@ static int sort_keys(KEY *a, KEY *b)
{
if (!(b_flags & HA_NOSAME))
return -1;
- if ((a_flags ^ b_flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
+ if ((a_flags ^ b_flags) & HA_NULL_PART_KEY)
{
/* Sort NOT NULL keys before other keys */
- return (a_flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
+ return (a_flags & HA_NULL_PART_KEY) ? 1 : -1;
}
if (a->name == primary_key_name)
return -1;
@@ -2543,7 +2537,12 @@ int prepare_create_field(Create_field *sql_field,
(sql_field->decimals << FIELDFLAG_DEC_SHIFT));
break;
}
- if (!(sql_field->flags & NOT_NULL_FLAG))
+ if (sql_field->flags & NOT_NULL_FLAG)
+ DBUG_PRINT("info", ("1"));
+ if (sql_field->vcol_info)
+ DBUG_PRINT("info", ("2"));
+ if (!(sql_field->flags & NOT_NULL_FLAG) ||
+ (sql_field->vcol_info)) /* Make virtual columns allow NULL values */
sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL;
if (sql_field->flags & NO_DEFAULT_VALUE_FLAG)
sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT;
@@ -2857,6 +2856,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
null_fields--;
sql_field->flags= dup_field->flags;
sql_field->interval= dup_field->interval;
+ sql_field->vcol_info= dup_field->vcol_info;
+ sql_field->stored_in_db= dup_field->stored_in_db;
it2.remove(); // Remove first (create) definition
select_field_pos--;
break;
@@ -2889,7 +2890,28 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
sql_field->offset= record_offset;
if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
auto_increment++;
- record_offset+= sql_field->pack_length;
+ if (parse_option_list(thd, &sql_field->option_struct,
+ sql_field->option_list,
+ create_info->db_type->field_options, FALSE,
+ thd->mem_root))
+ DBUG_RETURN(TRUE);
+ /*
+ For now skip fields that are not physically stored in the database
+ (virtual fields) and update their offset later
+ (see the next loop).
+ */
+ if (sql_field->stored_in_db)
+ record_offset+= sql_field->pack_length;
+ }
+ /* Update virtual fields' offset*/
+ it.rewind();
+ while ((sql_field=it++))
+ {
+ if (!sql_field->stored_in_db)
+ {
+ sql_field->offset= record_offset;
+ record_offset+= sql_field->pack_length;
+ }
}
if (timestamps_with_niladic > 1)
{
@@ -2939,6 +2961,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (key->type == Key::FOREIGN_KEY)
{
fk_key_count++;
+ if (((Foreign_key *)key)->validate(alter_info->create_list))
+ DBUG_RETURN(TRUE);
Foreign_key *fk_key= (Foreign_key*) key;
if (fk_key->ref_columns.elements &&
fk_key->ref_columns.elements != fk_key->columns.elements)
@@ -3069,6 +3093,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_info->key_part=key_part_info;
key_info->usable_key_parts= key_number;
key_info->algorithm= key->key_create_info.algorithm;
+ key_info->option_list= key->option_list;
+ if (parse_option_list(thd, &key_info->option_struct,
+ key_info->option_list,
+ create_info->db_type->index_options, FALSE,
+ thd->mem_root))
+ DBUG_RETURN(TRUE);
if (key->type == Key::FULLTEXT)
{
@@ -3234,6 +3264,17 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
}
#endif
+ if (!sql_field->stored_in_db)
+ {
+ /* Key fields must always be physically stored. */
+ my_error(ER_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (key->type == Key::PRIMARY && sql_field->vcol_info)
+ {
+ my_error(ER_PRIMARY_KEY_BASED_ON_VIRTUAL_COLUMN, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
if (!(sql_field->flags & NOT_NULL_FLAG))
{
if (key->type == Key::PRIMARY)
@@ -3445,6 +3486,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
}
+ if (parse_option_list(thd, &create_info->option_struct,
+ create_info->option_list,
+ file->partition_ht()->table_options, FALSE,
+ thd->mem_root))
+ DBUG_RETURN(TRUE);
+
DBUG_RETURN(FALSE);
}
@@ -3957,12 +4004,16 @@ bool mysql_create_table_no_lock(THD *thd,
}
/* Give warnings for not supported table options */
- if (create_info->transactional && !file->ht->commit)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
- ER_ILLEGAL_HA_CREATE_OPTION,
- ER(ER_ILLEGAL_HA_CREATE_OPTION),
- file->engine_name()->str,
- "TRANSACTIONAL=1");
+#if defined(WITH_ARIA_STORAGE_ENGINE)
+ extern handlerton *maria_hton;
+ if (file->ht != maria_hton)
+#endif
+ if (create_info->transactional)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_ILLEGAL_HA_CREATE_OPTION,
+ ER(ER_ILLEGAL_HA_CREATE_OPTION),
+ file->engine_name()->str,
+ "TRANSACTIONAL=1");
VOID(pthread_mutex_lock(&LOCK_open));
if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
@@ -5789,6 +5840,7 @@ compare_tables(TABLE *table,
KEY_PART_INFO *key_part;
KEY_PART_INFO *end;
THD *thd= table->in_use;
+ uint i;
/*
Remember if the new definition has new VARCHAR column;
create_info->varchar will be reset in mysql_prepare_create_table.
@@ -5879,6 +5931,12 @@ compare_tables(TABLE *table,
DBUG_RETURN(0);
}
+ if ((create_info->fields_option_struct= (ha_field_option_struct**)
+ thd->calloc(sizeof(void*) * table->s->fields)) == NULL ||
+ (create_info->indexes_option_struct= (ha_index_option_struct**)
+ thd->calloc(sizeof(void*) * table->s->keys)) == NULL)
+ DBUG_RETURN(1);
+
/*
Use transformed info to evaluate possibility of fast ALTER TABLE
but use the preserved field to persist modifications.
@@ -5886,15 +5944,19 @@ compare_tables(TABLE *table,
new_field_it.init(alter_info->create_list);
tmp_new_field_it.init(tmp_alter_info.create_list);
- /* Go through fields and check if the original ones are compatible
+ /*
+ Go through fields and check if the original ones are compatible
with new table.
*/
- for (f_ptr= table->field, new_field= new_field_it++,
+ for (i= 0, 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++,
+ i++, f_ptr++, new_field= new_field_it++,
tmp_new_field= tmp_new_field_it++)
{
+ DBUG_ASSERT(i < table->s->fields);
+ create_info->fields_option_struct[i]= tmp_new_field->option_struct;
+
/* Make sure we have at least the default charset in use. */
if (!new_field->charset)
new_field->charset= create_info->default_table_charset;
@@ -5908,6 +5970,19 @@ compare_tables(TABLE *table,
DBUG_RETURN(0);
}
+ /*
+ Check if the altered column is computed and either
+ is stored or is used in the partitioning expression.
+ TODO: Mark such a column with an alter flag only if
+ the defining expression has changed.
+ */
+ if (field->vcol_info &&
+ (field->stored_in_db || field->vcol_info->is_in_partitioning_expr()))
+ {
+ *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+ DBUG_RETURN(0);
+ }
+
/* Don't pack rows in old tables if the user has requested this. */
if (create_info->row_type == ROW_TYPE_DYNAMIC ||
(tmp_new_field->flags & BLOB_FLAG) ||
@@ -6035,7 +6110,9 @@ compare_tables(TABLE *table,
for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
{
/* Search an old key with the same name. */
- for (table_key= table->key_info; table_key < table_key_end; table_key++)
+ for (i= 0, table_key= table->key_info;
+ table_key < table_key_end;
+ i++, table_key++)
{
if (! strcmp(table_key->name, new_key->name))
break;
@@ -6054,6 +6131,11 @@ compare_tables(TABLE *table,
}
DBUG_PRINT("info", ("index added: '%s'", new_key->name));
}
+ else
+ {
+ DBUG_ASSERT(i < table->s->keys);
+ create_info->indexes_option_struct[i]= new_key->option_struct;
+ }
}
/* Check if changes are compatible with current handler without a copy */
@@ -6229,6 +6311,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
restore_record(table, s->default_values); // Empty record for DEFAULT
+ create_info->option_list= merge_engine_table_options(table->s->option_list,
+ create_info->option_list, thd->mem_root);
/*
First collect all fields from table which isn't in drop_list
*/
@@ -6276,6 +6360,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (def)
{ // Field is changed
def->field=field;
+ if (field->stored_in_db != def->stored_in_db)
+ {
+ my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN, MYF(0));
+ goto err;
+ }
if (!def->after)
{
new_create_list.push_back(def);
@@ -6491,7 +6580,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
key= new Key(key_type, key_name,
&key_create_info,
test(key_info->flags & HA_GENERATED_KEY),
- key_parts);
+ key_parts, key_info->option_list);
new_key_list.push_back(key);
}
}
@@ -6499,6 +6588,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
Key *key;
while ((key=key_it++)) // Add new keys
{
+ if (key->type == Key::FOREIGN_KEY &&
+ ((Foreign_key *)key)->validate(new_create_list))
+ goto err;
if (key->type != Key::FOREIGN_KEY)
new_key_list.push_back(key);
if (key->name &&
@@ -6959,7 +7051,7 @@ view_err:
error= 0;
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
- table->alias);
+ table->alias.c_ptr());
}
/*
@@ -7023,7 +7115,7 @@ view_err:
error= 0;
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
- table->alias);
+ table->alias.c_ptr());
}
if (!error)
@@ -7723,27 +7815,6 @@ view_err:
if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
DBUG_RETURN(TRUE);
- if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME))
- {
- /*
- For the alter table to be properly flushed to the logs, we
- have to open the new table. If not, we get a problem on server
- shutdown. But we do not need to attach MERGE children.
- */
- char path[FN_REFLEN];
- TABLE *t_table;
- build_table_filename(path + 1, sizeof(path) - 1, new_db, table_name, "", 0);
- t_table= open_temporary_table(thd, path, new_db, tmp_name, 0);
- if (t_table)
- {
- intern_close_table(t_table);
- my_free(t_table, MYF(0));
- }
- else
- sql_print_warning("Could not open table %s.%s after rename\n",
- new_db,table_name);
- ha_flush_logs(old_db_type);
- }
table_list->table=0; // For query cache
query_cache_invalidate3(thd, table_list, 0);
@@ -7952,7 +8023,9 @@ copy_data_between_tables(TABLE *from,TABLE *to,
/* 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);
+ to->mark_virtual_columns_for_write(TRUE);
+ if (init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE))
+ goto err;
errpos= 4;
if (ignore)
to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
@@ -7966,6 +8039,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
error= 1;
break;
}
+ update_virtual_fields(thd, from);
thd->row_count++;
/* Return error if source table isn't empty. */
if (error_if_not_empty)
@@ -7986,6 +8060,12 @@ copy_data_between_tables(TABLE *from,TABLE *to,
copy_ptr->do_copy(copy_ptr);
}
prev_insert_id= to->file->next_insert_id;
+ update_virtual_fields(thd, to, TRUE);
+ if (thd->is_error())
+ {
+ error= 1;
+ break;
+ }
error=to->file->ha_write_row(to->record[0]);
to->auto_increment_field_not_null= FALSE;
if (error)
@@ -8191,7 +8271,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
goto err;
}
ha_checksum row_crc= 0;
- int error= t->file->rnd_next(t->record[0]);
+ int error= t->file->ha_rnd_next(t->record[0]);
if (unlikely(error))
{
if (error == HA_ERR_RECORD_DELETED)
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 887f746251b..04adf6dcadc 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -60,14 +60,15 @@ print_where(COND *cond,const char *info, enum_query_type query_type)
{
if (cond)
{
- char buff[256];
+ char buff[1024];
String str(buff,(uint32) sizeof(buff), system_charset_info);
str.length(0);
+ str.extra_allocation(1024);
cond->print(&str, query_type);
str.append('\0');
DBUG_LOCK_FILE;
(void) fprintf(DBUG_FILE,"\nWHERE:(%s) ",info);
- (void) fputs(str.ptr(),DBUG_FILE);
+ (void) fputs(str.c_ptr_safe(),DBUG_FILE);
(void) fputc('\n',DBUG_FILE);
DBUG_UNLOCK_FILE;
}
@@ -159,7 +160,7 @@ void TEST_filesort(SORT_FIELD *sortorder,uint s_length)
out.append('\0'); // Purify doesn't like c_ptr()
DBUG_LOCK_FILE;
VOID(fputs("\nInfo about FILESORT\n",DBUG_FILE));
- fprintf(DBUG_FILE,"Sortorder: %s\n",out.ptr());
+ fprintf(DBUG_FILE,"Sortorder: %s\n",out.c_ptr_safe());
DBUG_UNLOCK_FILE;
DBUG_VOID_RETURN;
}
@@ -194,7 +195,7 @@ TEST_join(JOIN *join)
TABLE *form=tab->table;
char key_map_buff[128];
fprintf(DBUG_FILE,"%-16.16s type: %-7s q_keys: %s refs: %d key: %d len: %d\n",
- form->alias,
+ form->alias.c_ptr(),
join_type_str[tab->type],
tab->keys.print(key_map_buff),
tab->ref.key_parts,
@@ -218,7 +219,7 @@ TEST_join(JOIN *join)
if (tab->ref.key_parts)
{
fprintf(DBUG_FILE,
- " refs: %s\n", ref_key_parts[i].ptr());
+ " refs: %s\n", ref_key_parts[i].c_ptr_safe());
}
}
DBUG_UNLOCK_FILE;
@@ -444,11 +445,15 @@ static int print_key_cache_status(const char *name, KEY_CACHE *key_cache)
}
else
{
+ KEY_CACHE_STATISTICS stats;
+ get_key_cache_statistics(key_cache, 0, &stats);
+
printf("%s\n\
Buffer_size: %10lu\n\
Block_size: %10lu\n\
Division_limit: %10lu\n\
-Age_limit: %10lu\n\
+Age_threshold: %10lu\n\
+Partitions: %10lu\n\
blocks used: %10lu\n\
not flushed: %10lu\n\
w_requests: %10s\n\
@@ -458,11 +463,13 @@ reads: %10s\n\n",
name,
(ulong) key_cache->param_buff_size, key_cache->param_block_size,
key_cache->param_division_limit, key_cache->param_age_threshold,
- key_cache->blocks_used,key_cache->global_blocks_changed,
- llstr(key_cache->global_cache_w_requests,llbuff1),
- llstr(key_cache->global_cache_write,llbuff2),
- llstr(key_cache->global_cache_r_requests,llbuff3),
- llstr(key_cache->global_cache_read,llbuff4));
+ key_cache->param_partitions,
+ (ulong)stats.blocks_used,
+ (ulong)stats.blocks_changed,
+ llstr(stats.write_requests,llbuff1),
+ llstr(stats.writes,llbuff2),
+ llstr(stats.read_requests,llbuff3),
+ llstr(stats.reads,llbuff4));
}
return 0;
}
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 3de7aded51e..fb9abc06f6b 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -853,7 +853,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
stmt_query->append(stmt_definition.str, stmt_definition.length);
- trg_def->str= stmt_query->c_ptr();
+ trg_def->str= stmt_query->c_ptr_safe();
trg_def->length= stmt_query->length();
/* Create trigger definition file. */
@@ -1090,10 +1090,7 @@ void Table_triggers_list::set_table(TABLE *new_table)
{
trigger_table= new_table;
for (Field **field= new_table->triggers->record1_field ; *field ; field++)
- {
- (*field)->table= (*field)->orig_table= new_table;
- (*field)->table_name= &new_table->alias;
- }
+ (*field)->init(new_table);
}
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index 063321f9902..2906e69fc75 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -154,7 +154,13 @@ void udf_init()
}
table= tables.table;
- init_read_record(&read_record_info, new_thd, table, NULL,1,0,FALSE);
+ if (init_read_record(&read_record_info, new_thd, table, NULL,1,0,FALSE))
+ {
+ sql_print_error("Could not initialize init_read_record; udf's not "
+ "loaded");
+ goto end;
+ }
+
table->use_all_columns();
while (!(error= read_record_info.read_record(&read_record_info)))
{
@@ -571,10 +577,10 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
goto err;
table->use_all_columns();
table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
- if (!table->file->index_read_idx_map(table->record[0], 0,
- (uchar*) table->field[0]->ptr,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT))
+ if (!table->file->ha_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->ha_delete_row(table->record[0])))
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 8041de66ade..70572b406e5 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -160,7 +160,7 @@ static void prepare_record_for_error_message(int error, TABLE *table)
/* 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);
+ (void) table->file->ha_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))
@@ -477,7 +477,10 @@ int mysql_update(THD *thd,
*/
if (used_index == MAX_KEY || (select && select->quick))
- init_read_record(&info, thd, table, select, 0, 1, FALSE);
+ {
+ if (init_read_record(&info, thd, table, select, 0, 1, FALSE))
+ goto err;
+ }
else
init_read_record_idx(&info, thd, table, 1, used_index);
@@ -486,6 +489,7 @@ int mysql_update(THD *thd,
while (!(error=info.read_record(&info)) && !thd->killed)
{
+ update_virtual_fields(thd, table);
thd->examined_row_count++;
if (!select || (error= select->skip_record(thd)) > 0)
{
@@ -552,7 +556,8 @@ int mysql_update(THD *thd,
if (select && select->quick && select->quick->reset())
goto err;
table->file->try_semi_consistent_read(1);
- init_read_record(&info, thd, table, select, 0, 1, FALSE);
+ if (init_read_record(&info, thd, table, select, 0, 1, FALSE))
+ goto err;
updated= found= 0;
/*
@@ -599,6 +604,7 @@ int mysql_update(THD *thd,
while (!(error=info.read_record(&info)) && !thd->killed)
{
+ update_virtual_fields(thd, table);
thd->examined_row_count++;
if (!select || select->skip_record(thd) > 0)
{
@@ -1611,7 +1617,8 @@ loop_end:
do
{
Field_string *field= new Field_string(tbl->file->ref_length, 0,
- tbl->alias, &my_charset_bin);
+ tbl->alias.c_ptr(),
+ &my_charset_bin);
if (!field)
DBUG_RETURN(1);
field->init(tbl);
@@ -1908,7 +1915,7 @@ int multi_update::do_updates()
TABLE_LIST *cur_table;
int local_error= 0;
ha_rows org_updated;
- TABLE *table, *tmp_table;
+ TABLE *table, *tmp_table, *err_table;
List_iterator_fast<TABLE> check_opt_it(unupdated_check_opt_tables);
DBUG_ENTER("multi_update::do_updates");
@@ -1926,14 +1933,21 @@ int multi_update::do_updates()
org_updated= updated;
tmp_table= tmp_tables[cur_table->shared];
tmp_table->file->extra(HA_EXTRA_CACHE); // Change to read cache
- (void) table->file->ha_rnd_init(0);
+ if ((local_error= table->file->ha_rnd_init(0)))
+ {
+ err_table= table;
+ goto err;
+ }
table->file->extra(HA_EXTRA_NO_CACHE);
check_opt_it.rewind();
while(TABLE *tbl= check_opt_it++)
{
- if (tbl->file->ha_rnd_init(1))
+ if ((local_error= tbl->file->ha_rnd_init(1)))
+ {
+ err_table= tbl;
goto err;
+ }
tbl->file->extra(HA_EXTRA_CACHE);
}
@@ -1953,21 +1967,28 @@ int multi_update::do_updates()
}
copy_field_end=copy_field_ptr;
- if ((local_error = tmp_table->file->ha_rnd_init(1)))
+ if ((local_error= tmp_table->file->ha_rnd_init(1)))
+ {
+ err_table= tmp_table;
goto err;
+ }
can_compare_record= records_are_comparable(table);
for (;;)
{
if (thd->killed && trans_safe)
- goto err;
- if ((local_error=tmp_table->file->rnd_next(tmp_table->record[0])))
+ {
+ thd->fatal_error();
+ goto err2;
+ }
+ if ((local_error= tmp_table->file->ha_rnd_next(tmp_table->record[0])))
{
if (local_error == HA_ERR_END_OF_FILE)
break;
if (local_error == HA_ERR_RECORD_DELETED)
continue; // May happen on dup key
+ err_table= tmp_table;
goto err;
}
@@ -1977,12 +1998,15 @@ int multi_update::do_updates()
uint field_num= 0;
do
{
- if((local_error=
- tbl->file->rnd_pos(tbl->record[0],
- (uchar *) tmp_table->field[field_num]->ptr)))
+ if ((local_error=
+ tbl->file->ha_rnd_pos(tbl->record[0],
+ (uchar*) tmp_table->field[field_num]->ptr)))
+ {
+ err_table= tbl;
goto err;
+ }
field_num++;
- } while((tbl= check_opt_it++));
+ } while ((tbl= check_opt_it++));
table->status|= STATUS_UPDATED;
store_record(table,record[1]);
@@ -2007,7 +2031,10 @@ int multi_update::do_updates()
if (error == VIEW_CHECK_SKIP)
continue;
else if (error == VIEW_CHECK_ERROR)
- goto err;
+ {
+ thd->fatal_error();
+ goto err2;
+ }
}
if ((local_error=table->file->ha_update_row(table->record[1],
table->record[0])) &&
@@ -2015,7 +2042,10 @@ int multi_update::do_updates()
{
if (!ignore ||
table->file->is_fatal_error(local_error, HA_CHECK_DUP_KEY))
+ {
+ err_table= table;
goto err;
+ }
}
if (local_error != HA_ERR_RECORD_IS_THE_SAME)
updated++;
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index df9df89f972..2fc6918e49f 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -18,8 +18,8 @@
#include "mysql_priv.h"
#include "sql_select.h"
#include "parse_file.h"
-#include "sp.h"
#include "sp_head.h"
+#include "sp.h"
#include "sp_cache.h"
#define MD5_BUFF_LENGTH 33
@@ -848,7 +848,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
thd->variables.sql_mode|= sql_mode;
}
- DBUG_PRINT("info", ("View: %s", view_query.ptr()));
+ DBUG_PRINT("info", ("View: %s", view_query.c_ptr_safe()));
/* fill structure */
view->source= thd->lex->create_view_select;
@@ -1215,7 +1215,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
+ MODE_PIPES_AS_CONCAT affect expression parsing
+ MODE_ANSI_QUOTES affect expression parsing
+ MODE_IGNORE_SPACE affect expression parsing
- - MODE_NOT_USED not used :)
+ - MODE_IGNORE_BAD_TABLE_OPTIONS affect only CREATE/ALTER TABLE parsing
* MODE_ONLY_FULL_GROUP_BY affect execution
* MODE_NO_UNSIGNED_SUBTRACTION affect execution
- MODE_NO_DIR_IN_CREATE affect table creation only
@@ -1720,7 +1720,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
}
if (non_existant_views.length())
{
- my_error(ER_BAD_TABLE_ERROR, MYF(0), non_existant_views.c_ptr());
+ my_error(ER_BAD_TABLE_ERROR, MYF(0), non_existant_views.c_ptr_safe());
}
something_wrong= error || wrong_object_name || non_existant_views.length();
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 29fc39f9cc6..5f94bcd8bf3 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -46,6 +46,7 @@
#include "sp_rcontext.h"
#include "sp.h"
#include "event_parse_data.h"
+#include "create_options.h"
#include <myisam.h>
#include <myisammrg.h>
@@ -58,6 +59,7 @@
int yylex(void *yylval, void *yythd);
const LEX_STRING null_lex_str= {0,0};
+const LEX_STRING empty_lex_str= { (char*) "", 0 };
#define yyoverflow(A,B,C,D,E,F) \
{ \
@@ -608,6 +610,7 @@ static bool add_create_index_prepare (LEX *lex, Table_ident *table)
lex->alter_info.flags= ALTER_ADD_INDEX;
lex->col_list.empty();
lex->change= NullS;
+ lex->option_list= NULL;
return FALSE;
}
@@ -617,7 +620,7 @@ static bool add_create_index (LEX *lex, Key::Keytype type, const char *name,
{
Key *key;
key= new Key(type, name, info ? info : &lex->key_create_info, generated,
- lex->col_list);
+ lex->col_list, lex->option_list);
if (key == NULL)
return TRUE;
@@ -711,6 +714,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token ALGORITHM_SYM
%token ALL /* SQL-2003-R */
%token ALTER /* SQL-2003-R */
+%token ALWAYS_SYM
%token ANALYZE_SYM
%token AND_AND_SYM /* OPERATOR */
%token AND_SYM /* SQL-2003-R */
@@ -760,6 +764,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token CHECK_SYM /* SQL-2003-R */
%token CIPHER_SYM
%token CLIENT_SYM
+%token CLIENT_STATS_SYM
%token CLOSE_SYM /* SQL-2003-R */
%token COALESCE /* SQL-2003-N */
%token CODE_SYM
@@ -877,6 +882,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token FULLTEXT_SYM
%token FUNCTION_SYM /* SQL-2003-R */
%token GE
+%token GENERATED_SYM
%token GEOMETRYCOLLECTION
%token GEOMETRY_SYM
%token GET_FORMAT /* MYSQL-FUNC */
@@ -906,6 +912,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token IMPORT
%token INDEXES
%token INDEX_SYM
+%token INDEX_STATS_SYM
%token INFILE
%token INITIAL_SIZE_SYM
%token INNER_SYM /* SQL-2003-R */
@@ -1054,11 +1061,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token PAGE_CHECKSUM_SYM
%token PARAM_MARKER
%token PARSER_SYM
+%token PARSE_VCOL_EXPR_SYM
%token PARTIAL /* SQL-2003-N */
%token PARTITIONING_SYM
%token PARTITIONS_SYM
%token PARTITION_SYM /* SQL-2003-R */
%token PASSWORD
+%token PERSISTENT_SYM
%token PHASE_SYM
%token PLUGINS_SYM
%token PLUGIN_SYM
@@ -1147,6 +1156,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SIGNED_SYM
%token SIMPLE_SYM /* SQL-2003-N */
%token SLAVE
+%token SLOW_SYM
%token SMALLINT /* SQL-2003-R */
%token SNAPSHOT_SYM
%token SOCKET_SYM
@@ -1191,6 +1201,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token TABLES
%token TABLESPACE
%token TABLE_REF_PRIORITY
+%token TABLE_STATS_SYM
%token TABLE_SYM /* SQL-2003-R */
%token TABLE_CHECKSUM_SYM
%token TEMPORARY /* SQL-2003-N */
@@ -1238,6 +1249,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token UPGRADE_SYM
%token USAGE /* SQL-2003-N */
%token USER /* SQL-2003-R */
+%token USER_STATS_SYM
%token USE_FRM
%token USE_SYM
%token USING /* SQL-2003-R */
@@ -1252,7 +1264,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token VARIANCE_SYM
%token VARYING /* SQL-2003-R */
%token VAR_SAMP_SYM
+%token VIA_SYM
%token VIEW_SYM /* SQL-2003-N */
+%token VIRTUAL_SYM
%token WAIT_SYM
%token WARNINGS
%token WEEK_SYM
@@ -1314,7 +1328,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
text_string opt_gconcat_separator
%type <num>
- type int_type real_type order_dir lock_option
+ type int_type real_type order_dir lock_option field_def
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
@@ -1474,6 +1488,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
key_using_alg
server_def server_options_list server_option
definer_opt no_definer definer
+ parse_vcol_expr vcol_opt_specifier vcol_opt_attribute
+ vcol_opt_attribute_list vcol_attribute
END_OF_INPUT
%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
@@ -1606,6 +1622,7 @@ statement:
| load
| lock
| optimize
+ | parse_vcol_expr
| partition_entry
| preload
| prepare
@@ -2336,6 +2353,7 @@ sp_init_param:
lex->interval_list.empty();
lex->uint_geom_type= 0;
+ lex->vcol_info= 0;
}
;
@@ -3891,7 +3909,11 @@ create2:
;
create2a:
- field_list ')' opt_create_table_options
+ field_list ')'
+ {
+ Lex->create_info.option_list= NULL;
+ }
+ opt_create_table_options
opt_partitioning
create3 {}
| opt_partitioning
@@ -4770,6 +4792,30 @@ create_table_option:
Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL;
Lex->create_info.transactional= $3;
}
+ | IDENT_sys equal TEXT_STRING_sys
+ {
+ new (YYTHD->mem_root)
+ engine_option_value($1, $3, true, &Lex->create_info.option_list,
+ &Lex->option_list_last);
+ }
+ | IDENT_sys equal ident
+ {
+ new (YYTHD->mem_root)
+ engine_option_value($1, $3, false, &Lex->create_info.option_list,
+ &Lex->option_list_last);
+ }
+ | IDENT_sys equal real_ulonglong_num
+ {
+ new (YYTHD->mem_root)
+ engine_option_value($1, $3, &Lex->create_info.option_list,
+ &Lex->option_list_last, YYTHD->mem_root);
+ }
+ | IDENT_sys equal DEFAULT
+ {
+ new (YYTHD->mem_root)
+ engine_option_value($1, &Lex->create_info.option_list,
+ &Lex->option_list_last);
+ }
;
default_charset:
@@ -4891,25 +4937,33 @@ column_def:
;
key_def:
- normal_key_type opt_ident key_alg '(' key_list ')' normal_key_options
+ normal_key_type opt_ident key_alg '(' key_list ')'
+ { Lex->option_list= NULL; }
+ normal_key_options
{
if (add_create_index (Lex, $1, $2))
MYSQL_YYABORT;
}
| fulltext opt_key_or_index opt_ident init_key_options
- '(' key_list ')' fulltext_key_options
+ '(' key_list ')'
+ { Lex->option_list= NULL; }
+ fulltext_key_options
{
if (add_create_index (Lex, $1, $3))
MYSQL_YYABORT;
}
| spatial opt_key_or_index opt_ident init_key_options
- '(' key_list ')' spatial_key_options
+ '(' key_list ')'
+ { Lex->option_list= NULL; }
+ spatial_key_options
{
if (add_create_index (Lex, $1, $3))
MYSQL_YYABORT;
}
| opt_constraint constraint_key_type opt_ident key_alg
- '(' key_list ')' normal_key_options
+ '(' key_list ')'
+ { Lex->option_list= NULL; }
+ normal_key_options
{
if (add_create_index (Lex, $2, $3 ? $3 : $1))
MYSQL_YYABORT;
@@ -4928,6 +4982,7 @@ key_def:
if (key == NULL)
MYSQL_YYABORT;
lex->alter_info.key_list.push_back(key);
+ lex->option_list= NULL;
if (add_create_index (lex, Key::MULTIPLE, key_name,
&default_key_create_info, 1))
MYSQL_YYABORT;
@@ -4971,8 +5026,10 @@ field_spec:
lex->default_value= lex->on_update_value= 0;
lex->comment=null_lex_str;
lex->charset=NULL;
+ lex->vcol_info= 0;
+ lex->option_list= NULL;
}
- type opt_attribute
+ field_def
{
LEX *lex=Lex;
if (add_field_to_list(lex->thd, &$1, (enum enum_field_types) $3,
@@ -4980,8 +5037,98 @@ field_spec:
lex->default_value, lex->on_update_value,
&lex->comment,
lex->change,&lex->interval_list,lex->charset,
- lex->uint_geom_type))
+ lex->uint_geom_type,
+ lex->vcol_info, lex->option_list))
+ MYSQL_YYABORT;
+ }
+ ;
+
+field_def:
+ type opt_attribute {}
+ | type opt_generated_always AS '(' virtual_column_func ')'
+ vcol_opt_specifier
+ vcol_opt_attribute
+ {
+ $$= (enum enum_field_types)MYSQL_TYPE_VIRTUAL;
+ Lex->vcol_info->set_field_type((enum enum_field_types) $1);
+ }
+ ;
+
+opt_generated_always:
+ /* empty */
+ | GENERATED_SYM ALWAYS_SYM {}
+ ;
+
+vcol_opt_specifier:
+ /* empty */
+ {
+ Lex->vcol_info->set_stored_in_db_flag(FALSE);
+ }
+ | VIRTUAL_SYM
+ {
+ Lex->vcol_info->set_stored_in_db_flag(FALSE);
+ }
+ | PERSISTENT_SYM
+ {
+ Lex->vcol_info->set_stored_in_db_flag(TRUE);
+ }
+ ;
+
+vcol_opt_attribute:
+ /* empty */ {}
+ | vcol_opt_attribute_list {}
+ ;
+
+vcol_opt_attribute_list:
+ vcol_opt_attribute_list vcol_attribute {}
+ | vcol_attribute
+ ;
+
+vcol_attribute:
+ UNIQUE_SYM
+ {
+ LEX *lex=Lex;
+ lex->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; }
+ ;
+
+parse_vcol_expr:
+ PARSE_VCOL_EXPR_SYM '(' virtual_column_func ')'
+ {
+ /*
+ "PARSE_VCOL_EXPR" can only be used by the SQL server
+ when reading a '*.frm' file.
+ Prevent the end user from invoking this command.
+ */
+ if (!Lex->parse_vcol_expr)
+ {
+ my_message(ER_SYNTAX_ERROR, ER(ER_SYNTAX_ERROR), MYF(0));
MYSQL_YYABORT;
+ }
+ }
+ ;
+
+virtual_column_func:
+ remember_name expr remember_end
+ {
+ Lex->vcol_info= new Virtual_column_info();
+ if (!Lex->vcol_info)
+ {
+ mem_alloc_error(sizeof(Virtual_column_info));
+ MYSQL_YYABORT;
+ }
+ uint expr_len= (uint)($3 - $1) - 1;
+ Lex->vcol_info->expr_str.str= (char* ) sql_memdup($1 + 1, expr_len);
+ Lex->vcol_info->expr_str.length= expr_len;
+ Lex->vcol_info->expr_item= $2;
}
;
@@ -5311,6 +5458,29 @@ attribute:
Lex->charset=$2;
}
}
+ | IDENT_sys equal TEXT_STRING_sys
+ {
+ new (YYTHD->mem_root)
+ engine_option_value($1, $3, true, &Lex->option_list,
+ &Lex->option_list_last);
+ }
+ | IDENT_sys equal ident
+ {
+ new (YYTHD->mem_root)
+ engine_option_value($1, $3, false, &Lex->option_list,
+ &Lex->option_list_last);
+ }
+ | IDENT_sys equal real_ulonglong_num
+ {
+ new (YYTHD->mem_root)
+ engine_option_value($1, $3, &Lex->option_list,
+ &Lex->option_list_last, YYTHD->mem_root);
+ }
+ | IDENT_sys equal DEFAULT
+ {
+ new (YYTHD->mem_root)
+ engine_option_value($1, &Lex->option_list, &Lex->option_list_last);
+ }
;
now_or_signed_literal:
@@ -5600,6 +5770,29 @@ key_using_alg:
all_key_opt:
KEY_BLOCK_SIZE opt_equal ulong_num
{ Lex->key_create_info.block_size= $3; }
+ | IDENT_sys equal TEXT_STRING_sys
+ {
+ new (YYTHD->mem_root)
+ engine_option_value($1, $3, true, &Lex->option_list,
+ &Lex->option_list_last);
+ }
+ | IDENT_sys equal ident
+ {
+ new (YYTHD->mem_root)
+ engine_option_value($1, $3, false, &Lex->option_list,
+ &Lex->option_list_last);
+ }
+ | IDENT_sys equal real_ulonglong_num
+ {
+ new (YYTHD->mem_root)
+ engine_option_value($1, $3, &Lex->option_list,
+ &Lex->option_list_last, YYTHD->mem_root);
+ }
+ | IDENT_sys equal DEFAULT
+ {
+ new (YYTHD->mem_root)
+ engine_option_value($1, &Lex->option_list, &Lex->option_list_last);
+ }
;
normal_key_opt:
@@ -6088,6 +6281,7 @@ alter_list_item:
LEX *lex=Lex;
lex->change= $3.str;
lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
+ lex->option_list= NULL;
}
field_spec opt_place
| MODIFY_SYM opt_column field_ident
@@ -6098,8 +6292,10 @@ alter_list_item:
lex->comment=null_lex_str;
lex->charset= NULL;
lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
+ lex->vcol_info= 0;
+ lex->option_list= NULL;
}
- type opt_attribute
+ field_def
{
LEX *lex=Lex;
if (add_field_to_list(lex->thd,&$3,
@@ -6108,7 +6304,8 @@ alter_list_item:
lex->default_value, lex->on_update_value,
&lex->comment,
$3.str, &lex->interval_list, lex->charset,
- lex->uint_geom_type))
+ lex->uint_geom_type,
+ lex->vcol_info, lex->option_list))
MYSQL_YYABORT;
}
opt_place
@@ -6215,8 +6412,7 @@ alter_list_item:
}
| create_table_options_space_separated
{
- LEX *lex=Lex;
- lex->alter_info.flags|= ALTER_OPTIONS;
+ Lex->alter_info.flags|= ALTER_OPTIONS;
}
| FORCE_SYM
{
@@ -6599,8 +6795,6 @@ cache_keys_spec:
{
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
@@ -9325,6 +9519,7 @@ ulonglong_num:
real_ulonglong_num:
NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
| ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
+ | HEX_NUM { $$= strtoull($1.str, (char**) 0, 16); }
| LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
| dec_num_error { MYSQL_YYABORT; }
;
@@ -10387,6 +10582,34 @@ show_param:
{
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
}
+ | CLIENT_STATS_SYM
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_CLIENT_STATS;
+ if (prepare_schema_table(YYTHD, lex, 0, SCH_CLIENT_STATS))
+ MYSQL_YYABORT;
+ }
+ | USER_STATS_SYM
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_USER_STATS;
+ if (prepare_schema_table(YYTHD, lex, 0, SCH_USER_STATS))
+ MYSQL_YYABORT;
+ }
+ | TABLE_STATS_SYM
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_TABLE_STATS;
+ if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_STATS))
+ MYSQL_YYABORT;
+ }
+ | INDEX_STATS_SYM
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_INDEX_STATS;
+ if (prepare_schema_table(YYTHD, lex, 0, SCH_INDEX_STATS))
+ MYSQL_YYABORT;
+ }
| CREATE PROCEDURE sp_name
{
LEX *lex= Lex;
@@ -10595,6 +10818,16 @@ flush_option:
{ Lex->type|= REFRESH_STATUS; }
| SLAVE
{ Lex->type|= REFRESH_SLAVE; }
+ | SLOW_SYM QUERY_SYM LOGS_SYM
+ { Lex->type |= REFRESH_SLOW_QUERY_LOG; }
+ | CLIENT_STATS_SYM
+ { Lex->type|= REFRESH_CLIENT_STATS; }
+ | USER_STATS_SYM
+ { Lex->type|= REFRESH_USER_STATS; }
+ | TABLE_STATS_SYM
+ { Lex->type|= REFRESH_TABLE_STATS; }
+ | INDEX_STATS_SYM
+ { Lex->type|= REFRESH_INDEX_STATS; }
| MASTER_SYM
{ Lex->type|= REFRESH_MASTER; }
| DES_KEY_FILE
@@ -11592,6 +11825,9 @@ user:
$$->user = $1;
$$->host.str= (char *) "%";
$$->host.length= 1;
+ $$->password= null_lex_str;
+ $$->plugin= empty_lex_str;
+ $$->auth= empty_lex_str;
if (check_string_char_length(&$$->user, ER(ER_USERNAME),
USERNAME_CHAR_LENGTH,
@@ -11604,6 +11840,9 @@ user:
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
MYSQL_YYABORT;
$$->user = $1; $$->host=$3;
+ $$->password= null_lex_str;
+ $$->plugin= empty_lex_str;
+ $$->auth= empty_lex_str;
if (check_string_char_length(&$$->user, ER(ER_USERNAME),
USERNAME_CHAR_LENGTH,
@@ -11697,6 +11936,7 @@ keyword_sp:
| AGAINST {}
| AGGREGATE_SYM {}
| ALGORITHM_SYM {}
+ | ALWAYS_SYM {}
| ANY_SYM {}
| AT_SYM {}
| AUTHORS_SYM {}
@@ -11714,6 +11954,7 @@ keyword_sp:
| CHAIN_SYM {}
| CHANGED {}
| CIPHER_SYM {}
+ | CLIENT_STATS_SYM {}
| CLIENT_SYM {}
| COALESCE {}
| CODE_SYM {}
@@ -11766,6 +12007,7 @@ keyword_sp:
| FIRST_SYM {}
| FIXED_SYM {}
| FRAC_SECOND_SYM {}
+ | GENERATED_SYM {}
| GEOMETRY_SYM {}
| GEOMETRYCOLLECTION {}
| GET_FORMAT {}
@@ -11775,6 +12017,7 @@ keyword_sp:
| HOSTS_SYM {}
| HOUR_SYM {}
| IDENTIFIED_SYM {}
+ | INDEX_STATS_SYM {}
| INVOKER_SYM {}
| IMPORT {}
| INDEXES {}
@@ -11853,6 +12096,7 @@ keyword_sp:
| PARTITIONING_SYM {}
| PARTITIONS_SYM {}
| PASSWORD {}
+ | PERSISTENT_SYM {}
| PHASE_SYM {}
| PLUGIN_SYM {}
| PLUGINS_SYM {}
@@ -11898,6 +12142,7 @@ keyword_sp:
| SIMPLE_SYM {}
| SHARE_SYM {}
| SHUTDOWN {}
+ | SLOW_SYM {}
| SNAPSHOT_SYM {}
| SOUNDS_SYM {}
| SOURCE_SYM {}
@@ -11917,6 +12162,7 @@ keyword_sp:
| SUSPEND_SYM {}
| SWAPS_SYM {}
| SWITCHES_SYM {}
+ | TABLE_STATS_SYM {}
| TABLES {}
| TABLE_CHECKSUM_SYM {}
| TABLESPACE {}
@@ -11942,9 +12188,11 @@ keyword_sp:
| UNKNOWN_SYM {}
| UNTIL_SYM {}
| USER {}
+ | USER_STATS_SYM {}
| USE_FRM {}
| VARIABLES {}
| VIEW_SYM {}
+ | VIRTUAL_SYM {}
| VALUE_SYM {}
| WARNINGS {}
| WAIT_SYM {}
@@ -12857,6 +13105,18 @@ grant_user:
}
| user IDENTIFIED_SYM BY PASSWORD TEXT_STRING
{ $$= $1; $1->password= $5; }
+ | user IDENTIFIED_SYM VIA_SYM ident_or_text
+ {
+ $$= $1;
+ $1->plugin= $4;
+ $1->auth= empty_lex_str;
+ }
+ | user IDENTIFIED_SYM VIA_SYM ident_or_text USING TEXT_STRING_sys
+ {
+ $$= $1;
+ $1->plugin= $4;
+ $1->auth= $6;
+ }
| user
{ $$= $1; $1->password= null_lex_str; }
;
@@ -12887,7 +13147,7 @@ column_list_id:
while ((point=iter++))
{
if (!my_strcasecmp(system_charset_info,
- point->column.ptr(), new_str->ptr()))
+ point->column.c_ptr(), new_str->c_ptr()))
break;
}
lex->grant_tot_col|= lex->which_columns;
@@ -13545,6 +13805,7 @@ sf_tail:
lex->length= lex->dec= NULL;
lex->interval_list.empty();
lex->type= 0;
+ lex->vcol_info= 0;
}
type /* $11 */
{ /* $12 */
diff --git a/sql/structs.h b/sql/structs.h
index f3642852c4a..ee6a2ffdcfd 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -71,6 +71,8 @@ typedef struct st_key_part_info { /* Info about a key part */
uint8 null_bit; /* Position to null_bit */
} KEY_PART_INFO ;
+class engine_option_value;
+struct ha_index_option_struct;
typedef struct st_key {
uint key_length; /* Tot length of key */
@@ -79,6 +81,7 @@ typedef struct st_key {
uint extra_length;
uint usable_key_parts; /* Should normally be = key_parts */
uint block_size;
+ uint name_length;
enum ha_key_alg algorithm;
/*
Note that parser is used when the table is opened for use, and
@@ -91,6 +94,8 @@ typedef struct st_key {
};
KEY_PART_INFO *key_part;
char *name; /* Name of key */
+ /* Unique name for cache; db + \0 + table_name + \0 + key_name + \0 */
+ uchar *cache_name;
/*
Array of AVG(#records with the same field value) for 1st ... Nth key part.
0 means 'not known'.
@@ -101,6 +106,9 @@ typedef struct st_key {
int bdb_return_if_eq;
} handler;
struct st_table *table;
+ /** reference to the list of options or NULL */
+ engine_option_value *option_list;
+ ha_index_option_struct *option_struct; /* structure with parsed options */
} KEY;
@@ -178,7 +186,7 @@ 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_STRING user, host, password, plugin, auth;
} LEX_USER;
/*
@@ -240,6 +248,111 @@ typedef struct user_conn {
USER_RESOURCES user_resources;
} USER_CONN;
+typedef struct st_user_stats
+{
+ char user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
+ // Account name the user is mapped to when this is a user from mapped_user.
+ // Otherwise, the same value as user.
+ char priv_user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
+ uint user_name_length;
+ uint total_connections;
+ uint concurrent_connections;
+ time_t connected_time; // in seconds
+ double busy_time; // in seconds
+ double cpu_time; // in seconds
+ ulonglong bytes_received;
+ ulonglong bytes_sent;
+ ulonglong binlog_bytes_written;
+ ha_rows rows_read, rows_sent;
+ ha_rows rows_updated, rows_deleted, rows_inserted;
+ ulonglong select_commands, update_commands, other_commands;
+ ulonglong commit_trans, rollback_trans;
+ ulonglong denied_connections, lost_connections;
+ ulonglong access_denied_errors;
+ ulonglong empty_queries;
+} USER_STATS;
+
+/* Lookup function for hash tables with USER_STATS entries */
+extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
+ my_bool not_used __attribute__((unused)));
+
+/* Free all memory for a hash table with USER_STATS entries */
+extern void free_user_stats(USER_STATS* user_stats);
+
+/* Intialize an instance of USER_STATS */
+extern void
+init_user_stats(USER_STATS *user_stats,
+ const char *user,
+ size_t user_length,
+ const char *priv_user,
+ uint total_connections,
+ uint concurrent_connections,
+ time_t connected_time,
+ double busy_time,
+ double cpu_time,
+ ulonglong bytes_received,
+ ulonglong bytes_sent,
+ ulonglong binlog_bytes_written,
+ ha_rows rows_sent,
+ ha_rows rows_read,
+ ha_rows rows_inserted,
+ ha_rows rows_deleted,
+ ha_rows rows_updated,
+ ulonglong select_commands,
+ ulonglong update_commands,
+ ulonglong other_commands,
+ ulonglong commit_trans,
+ ulonglong rollback_trans,
+ ulonglong denied_connections,
+ ulonglong lost_connections,
+ ulonglong access_denied_errors,
+ ulonglong empty_queries);
+
+/* Increment values of an instance of USER_STATS */
+extern void
+add_user_stats(USER_STATS *user_stats,
+ uint total_connections,
+ uint concurrent_connections,
+ time_t connected_time,
+ double busy_time,
+ double cpu_time,
+ ulonglong bytes_received,
+ ulonglong bytes_sent,
+ ulonglong binlog_bytes_written,
+ ha_rows rows_sent,
+ ha_rows rows_read,
+ ha_rows rows_inserted,
+ ha_rows rows_deleted,
+ ha_rows rows_updated,
+ ulonglong select_commands,
+ ulonglong update_commands,
+ ulonglong other_commands,
+ ulonglong commit_trans,
+ ulonglong rollback_trans,
+ ulonglong denied_connections,
+ ulonglong lost_connections,
+ ulonglong access_denied_errors,
+ ulonglong empty_queries);
+
+typedef struct st_table_stats
+{
+ char table[NAME_LEN * 2 + 2]; // [db] + '\0' + [table] + '\0'
+ uint table_name_length;
+ ulonglong rows_read, rows_changed;
+ ulonglong rows_changed_x_indexes;
+ /* Stores enum db_type, but forward declarations cannot be done */
+ int engine_type;
+} TABLE_STATS;
+
+typedef struct st_index_stats
+{
+ // [db] + '\0' + [table] + '\0' + [index] + '\0'
+ char index[NAME_LEN * 3 + 3];
+ uint index_name_length; /* Length of 'index' */
+ ulonglong rows_read;
+} INDEX_STATS;
+
+
/* Bits in form->update */
#define REG_MAKE_DUPP 1 /* Make a copy of record when read */
#define REG_NEW_RECORD 2 /* Write a new record if not found */
diff --git a/sql/table.cc b/sql/table.cc
index 0bfa74dba75..554e1a6e55e 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -21,6 +21,7 @@
#include "mysql_priv.h"
#include "sql_trigger.h"
+#include "create_options.h"
#include <m_ctype.h>
#include "my_md5.h"
@@ -36,6 +37,12 @@ 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")};
+/*
+ Keyword added as a prefix when parsing the defining expression for a
+ virtual column read from the column definition saved in the frm file
+*/
+LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
+
/* Functions defined in this file */
void open_table_error(TABLE_SHARE *share, int error, int db_errno,
@@ -680,12 +687,14 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
uint interval_count, interval_parts, read_length, int_length;
uint db_create_options, keys, key_parts, n_length;
uint key_info_length, com_length, null_bit_pos;
- uint extra_rec_buf_length;
+ uint vcol_screen_length;
+ uint extra_rec_buf_length, options_len;
uint i,j;
bool use_hash;
- char *keynames, *names, *comment_pos;
+ char *keynames, *names, *comment_pos, *vcol_screen_pos;
uchar *record;
- uchar *disk_buff, *strpos, *null_flags, *null_pos;
+ uchar *disk_buff, *strpos, *null_flags, *null_pos, *options;
+ uchar *buff= 0;
ulong pos, record_offset, *rec_per_key, rec_buff_length;
handler *handler_file= 0;
KEY *keyinfo;
@@ -698,6 +707,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
bool null_bits_are_used;
DBUG_ENTER("open_binary_frm");
+ LINT_INIT(options);
+ LINT_INIT(options_len);
+
new_field_pack_flag= head[27];
new_frm_ver= (head[2] - FRM_VER);
field_pack_length= new_frm_ver < 2 ? 11 : 17;
@@ -803,7 +815,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
for (i=0 ; i < keys ; i++, keyinfo++)
{
- keyinfo->table= 0; // Updated in open_frm
if (new_frm_ver >= 3)
{
keyinfo->flags= (uint) uint2korr(strpos) ^ HA_NOSAME;
@@ -855,6 +866,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
strpos+= (strmov(keynames, (char *) strpos) - keynames)+1;
share->reclength = uint2korr((head+16));
+ share->stored_rec_length= share->reclength;
if (*(head+26) == 1)
share->system= 1; /* one-record-database */
#ifdef HAVE_CRYPTED_FRM
@@ -872,15 +884,14 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if ((n_length= uint4korr(head+55)))
{
/* Read extra data segment */
- uchar *buff, *next_chunk, *buff_end;
+ uchar *next_chunk, *buff_end;
DBUG_PRINT("info", ("extra segment size is %u bytes", n_length));
if (!(next_chunk= buff= (uchar*) my_malloc(n_length+1, MYF(MY_WME))))
goto err;
if (my_pread(file, buff, n_length, record_offset + share->reclength,
MYF(MY_NABP)))
{
- my_free(buff, MYF(0));
- goto err;
+ goto free_and_err;
}
share->connect_string.length= uint2korr(buff);
if (!(share->connect_string.str= strmake_root(&share->mem_root,
@@ -888,8 +899,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
share->connect_string.
length)))
{
- my_free(buff, MYF(0));
- goto err;
+ goto free_and_err;
}
next_chunk+= share->connect_string.length + 2;
buff_end= buff + n_length;
@@ -909,8 +919,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
plugin_data(tmp_plugin, handlerton *)))
{
/* bad file, legacy_db_type did not match the name */
- my_free(buff, MYF(0));
- goto err;
+ goto free_and_err;
}
/*
tmp_plugin is locked with a local lock.
@@ -939,8 +948,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
error= 8;
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
"--skip-partition");
- my_free(buff, MYF(0));
- goto err;
+ goto free_and_err;
}
plugin_unlock(NULL, share->db_plugin);
share->db_plugin= ha_lock_engine(NULL, partition_hton);
@@ -955,8 +963,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
error= 8;
name.str[name.length]= 0;
my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str);
- my_free(buff, MYF(0));
- goto err;
+ goto free_and_err;
/* purecov: end */
}
next_chunk+= str_db_type_length + 2;
@@ -972,34 +979,19 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
memdup_root(&share->mem_root, next_chunk + 4,
partition_info_len + 1)))
{
- my_free(buff, MYF(0));
- goto err;
+ goto free_and_err;
}
}
#else
if (partition_info_len)
{
DBUG_PRINT("info", ("WITH_PARTITION_STORAGE_ENGINE is not defined"));
- my_free(buff, MYF(0));
- goto err;
+ goto free_and_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
+ if (share->mysql_version >= 50110 && next_chunk < buff_end)
{
/* New auto_partitioned indicator introduced in 5.1.11 */
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -1017,8 +1009,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{
DBUG_PRINT("error",
("fulltext key uses parser that is not defined in .frm"));
- my_free(buff, MYF(0));
- goto err;
+ goto free_and_err;
}
parser_name.str= (char*) next_chunk;
parser_name.length= strlen((char*) next_chunk);
@@ -1028,12 +1019,22 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (! keyinfo->parser)
{
my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), parser_name.str);
- my_free(buff, MYF(0));
- goto err;
+ goto free_and_err;
}
}
}
- my_free(buff, MYF(0));
+ DBUG_ASSERT(next_chunk <= buff_end);
+ if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS)
+ {
+ /*
+ store options position, but skip till the time we will
+ know number of fields
+ */
+ options_len= uint4korr(next_chunk);
+ options= next_chunk + 4;
+ next_chunk+= options_len + 4;
+ }
+ DBUG_ASSERT(next_chunk <= buff_end);
}
share->key_block_size= uint2korr(head+62);
@@ -1043,21 +1044,21 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
share->rec_buff_length= rec_buff_length;
if (!(record= (uchar *) alloc_root(&share->mem_root,
rec_buff_length)))
- goto err; /* purecov: inspected */
+ goto free_and_err; /* purecov: inspected */
share->default_values= record;
if (my_pread(file, record, (size_t) share->reclength,
record_offset, MYF(MY_NABP)))
- goto err; /* purecov: inspected */
+ goto free_and_err; /* purecov: inspected */
VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0)));
if (my_read(file, head,288,MYF(MY_NABP)))
- goto err;
+ goto free_and_err;
#ifdef HAVE_CRYPTED_FRM
if (crypted)
{
crypted->decode((char*) head+256,288-256);
if (sint2korr(head+284) != 0) // Should be 0
- goto err; // Wrong password
+ goto free_and_err; // Wrong password
}
#endif
@@ -1069,11 +1070,15 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
int_length= uint2korr(head+274);
share->null_fields= uint2korr(head+282);
com_length= uint2korr(head+284);
+ vcol_screen_length= uint2korr(head+286);
+ share->vfields= 0;
+ share->stored_fields= share->fields;
share->comment.length= (int) (head[46]);
share->comment.str= strmake_root(&share->mem_root, (char*) head+47,
share->comment.length);
- 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));
+ DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d vcol_screen_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length, vcol_screen_length));
+
if (!(field_ptr = (Field **)
alloc_root(&share->mem_root,
@@ -1081,14 +1086,16 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
interval_count*sizeof(TYPELIB)+
(share->fields+interval_parts+
keys+3)*sizeof(char *)+
- (n_length+int_length+com_length)))))
- goto err; /* purecov: inspected */
+ (n_length+int_length+com_length+
+ vcol_screen_length)))))
+ goto free_and_err; /* purecov: inspected */
share->field= field_ptr;
read_length=(uint) (share->fields * field_pack_length +
- pos+ (uint) (n_length+int_length+com_length));
+ pos+ (uint) (n_length+int_length+com_length+
+ vcol_screen_length));
if (read_string(file,(uchar**) &disk_buff,read_length))
- goto err; /* purecov: inspected */
+ goto free_and_err; /* purecov: inspected */
#ifdef HAVE_CRYPTED_FRM
if (crypted)
{
@@ -1107,11 +1114,15 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
memcpy((char*) names, strpos+(share->fields*field_pack_length),
(uint) (n_length+int_length));
comment_pos= names+(n_length+int_length);
- memcpy(comment_pos, disk_buff+read_length-com_length, com_length);
+ memcpy(comment_pos, disk_buff+read_length-com_length-vcol_screen_length,
+ com_length);
+ vcol_screen_pos= names+(n_length+int_length+com_length);
+ memcpy(vcol_screen_pos, disk_buff+read_length-vcol_screen_length,
+ vcol_screen_length);
fix_type_pointers(&interval_array, &share->fieldnames, 1, &names);
if (share->fieldnames.count != share->fields)
- goto err;
+ goto free_and_err;
fix_type_pointers(&interval_array, share->intervals, interval_count,
&names);
@@ -1125,7 +1136,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
uint count= (uint) (interval->count + 1) * sizeof(uint);
if (!(interval->type_lengths= (uint *) alloc_root(&share->mem_root,
count)))
- goto err;
+ goto free_and_err;
for (count= 0; count < interval->count; count++)
{
char *val= (char*) interval->type_names[count];
@@ -1141,7 +1152,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
/* Allocate handler */
if (!(handler_file= get_new_handler(share, thd->mem_root,
share->db_type())))
- goto err;
+ goto free_and_err;
record= share->default_values-1; /* Fieldstart = 1 */
null_bits_are_used= share->null_fields != 0;
@@ -1176,10 +1187,14 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++)
{
uint pack_flag, interval_nr, unireg_type, recpos, field_length;
+ uint vcol_info_length=0;
+ uint vcol_expr_length=0;
enum_field_types field_type;
CHARSET_INFO *charset=NULL;
Field::geometry_type geom_type= Field::GEOM_GEOMETRY;
LEX_STRING comment;
+ Virtual_column_info *vcol_info= 0;
+ bool fld_stored_in_db= TRUE;
if (new_frm_ver >= 3)
{
@@ -1200,7 +1215,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
charset= &my_charset_bin;
#else
error= 4; // unsupported field type
- goto err;
+ goto free_and_err;
#endif
}
else
@@ -1211,9 +1226,21 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{
error= 5; // Unknown or unavailable charset
errarg= (int) strpos[14];
- goto err;
+ goto free_and_err;
}
}
+
+ if ((uchar)field_type == (uchar)MYSQL_TYPE_VIRTUAL)
+ {
+ DBUG_ASSERT(interval_nr); // Expect non-null expression
+ /*
+ The interval_id byte in the .frm file stores the length of the
+ expression statement for a virtual column.
+ */
+ vcol_info_length= interval_nr;
+ interval_nr= 0;
+ }
+
if (!comment_length)
{
comment.str= (char*) "";
@@ -1225,6 +1252,34 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
comment.length= comment_length;
comment_pos+= comment_length;
}
+
+ if (vcol_info_length)
+ {
+ /*
+ Get virtual column data stored in the .frm file as follows:
+ byte 1 = 1 (always 1 to allow for future extensions)
+ byte 2 = sql_type
+ byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored)
+ byte 4-... = virtual column expression (text data)
+ */
+ vcol_info= new Virtual_column_info();
+ if ((uint)vcol_screen_pos[0] != 1)
+ {
+ error= 4;
+ goto free_and_err;
+ }
+ field_type= (enum_field_types) (uchar) vcol_screen_pos[1];
+ fld_stored_in_db= (bool) (uint) vcol_screen_pos[2];
+ vcol_expr_length= vcol_info_length-(uint)FRM_VCOL_HEADER_SIZE;
+ if (!(vcol_info->expr_str.str=
+ (char *)memdup_root(&share->mem_root,
+ vcol_screen_pos+(uint)FRM_VCOL_HEADER_SIZE,
+ vcol_expr_length)))
+ goto free_and_err;
+ vcol_info->expr_str.length= vcol_expr_length;
+ vcol_screen_pos+= vcol_info_length;
+ share->vfields++;
+ }
}
else
{
@@ -1310,11 +1365,13 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (!reg_field) // Not supported field type
{
error= 4;
- goto err; /* purecov: inspected */
+ goto free_and_err; /* purecov: inspected */
}
reg_field->field_index= i;
reg_field->comment=comment;
+ reg_field->vcol_info= vcol_info;
+ reg_field->stored_in_db= fld_stored_in_db;
if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag))
{
null_bits_are_used= 1;
@@ -1338,7 +1395,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
share->timestamp_field_offset= i;
if (use_hash)
- if (my_hash_insert(&share->name_hash, (uchar*) field_ptr) )
+ {
+ if (my_hash_insert(&share->name_hash,
+ (uchar*) field_ptr))
{
/*
Set return code 8 here to indicate that an error has
@@ -1346,10 +1405,20 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
sent (OOM).
*/
error= 8;
- goto err;
+ goto free_and_err;
}
+ }
+ if (!reg_field->stored_in_db)
+ {
+ share->stored_fields--;
+ if (share->stored_rec_length>=recpos)
+ share->stored_rec_length= recpos-1;
+ }
}
*field_ptr=0; // End marker
+ /* Sanity checks: */
+ DBUG_ASSERT(share->fields>=share->stored_fields);
+ DBUG_ASSERT(share->reclength>=share->stored_rec_length);
/* Fix key->name and key_part->field */
if (key_parts)
@@ -1364,6 +1433,19 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{
uint usable_parts= 0;
keyinfo->name=(char*) share->keynames.type_names[key];
+ keyinfo->name_length= strlen(keyinfo->name);
+ keyinfo->cache_name=
+ (uchar*) alloc_root(&share->mem_root,
+ share->table_cache_key.length+
+ keyinfo->name_length + 1);
+ if (keyinfo->cache_name) // If not out of memory
+ {
+ uchar *pos= keyinfo->cache_name;
+ memcpy(pos, share->table_cache_key.str, share->table_cache_key.length);
+ memcpy(pos + share->table_cache_key.length, keyinfo->name,
+ keyinfo->name_length+1);
+ }
+
/* Fix fulltext keys for old .frm files */
if (share->key_info[key].flags & HA_FULLTEXT)
share->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT;
@@ -1400,7 +1482,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (!key_part->fieldnr)
{
error= 4; // Wrong file
- goto err;
+ goto free_and_err;
}
field= key_part->field= share->field[key_part->fieldnr-1];
key_part->type= field->key_type();
@@ -1426,12 +1508,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
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;
@@ -1517,6 +1593,15 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
*/
if (field->real_maybe_null())
key_part->key_part_flag|= HA_NULL_PART;
+ /*
+ Sometimes we can compare key parts for equality with memcmp.
+ But not always.
+ */
+ if (!(key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART |
+ HA_BIT_PART)) &&
+ key_part->type != HA_KEYTYPE_FLOAT &&
+ key_part->type == HA_KEYTYPE_DOUBLE)
+ key_part->key_part_flag|= HA_CAN_MEMCMP;
}
keyinfo->usable_key_parts= usable_parts; // Filesort
@@ -1565,6 +1650,16 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
null_length, 255);
}
+ if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS)
+ {
+ DBUG_ASSERT(options_len);
+ if (engine_table_options_frm_read(options, options_len, share))
+ goto free_and_err;
+ }
+ if (parse_engine_table_options(thd, handler_file->partition_ht(), share))
+ goto free_and_err;
+ my_free(buff, MYF(MY_ALLOW_ZERO_PTR));
+
if (share->found_next_number_field)
{
reg_field= *share->found_next_number_field;
@@ -1626,6 +1721,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
#endif
DBUG_RETURN (0);
+ free_and_err:
+ my_free(buff, MYF(MY_ALLOW_ZERO_PTR));
err:
share->error= error;
share->open_errno= my_errno;
@@ -1644,6 +1741,316 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
DBUG_RETURN(error);
} /* open_binary_frm */
+/*
+ @brief
+ Clear GET_FIXED_FIELDS_FLAG in all fields of a table
+
+ @param
+ table The table for whose fields the flags are to be cleared
+
+ @note
+ This routine is used for error handling purposes.
+
+ @return
+ 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;
+}
+
+
+/*
+ @brief
+ Perform semantic analysis of the defining expression for a virtual column
+
+ @param
+ thd The thread object
+ @param
+ table The table containing the virtual column
+ @param
+ vcol_field The virtual field whose defining expression is to be analyzed
+
+ @details
+ The function performs semantic analysis of the defining expression for
+ the virtual column vcol_field. The expression is used to compute the
+ values of this column.
+
+ @note
+ The function exploits the fact that the fix_fields method sets the flag
+ GET_FIXED_FIELDS_FLAG for all fields in the item tree.
+ This flag must always be unset before returning from this function
+ since it is used for other purposes as well.
+
+ @retval
+ TRUE An error occurred, something was wrong with the function
+ @retval
+ FALSE Otherwise
+*/
+
+bool fix_vcol_expr(THD *thd,
+ TABLE *table,
+ Field *vcol_field)
+{
+ Virtual_column_info *vcol_info= vcol_field->vcol_info;
+ Item* func_expr= vcol_info->expr_item;
+ uint dir_length, home_dir_length;
+ bool result= TRUE;
+ TABLE_LIST tables;
+ TABLE_LIST *save_table_list, *save_first_table, *save_last_table;
+ int error;
+ Name_resolution_context *context;
+ const char *save_where;
+ char* db_name;
+ char db_name_string[FN_REFLEN];
+ bool save_use_only_table_context;
+ Field **ptr, *field;
+ enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
+ DBUG_ASSERT(func_expr);
+ DBUG_ENTER("fix_vcol_expr");
+
+ /*
+ Set-up the TABLE_LIST object to be a list with a single table
+ Set the object to zero to create NULL pointers and set alias
+ and real name to table name and get database name from file name.
+ */
+
+ bzero((void*)&tables, sizeof(TABLE_LIST));
+ tables.alias= tables.table_name= (char*) table->s->table_name.str;
+ tables.table= table;
+ tables.next_local= 0;
+ tables.next_name_resolution_table= 0;
+ strmov(db_name_string, table->s->normalized_path.str);
+ dir_length= dirname_length(db_name_string);
+ db_name_string[dir_length - 1]= 0;
+ home_dir_length= dirname_length(db_name_string);
+ db_name= &db_name_string[home_dir_length];
+ tables.db= db_name;
+
+ thd->mark_used_columns= MARK_COLUMNS_NONE;
+
+ context= thd->lex->current_context();
+ table->map= 1; //To ensure correct calculation of const item
+ table->get_fields_in_item_tree= TRUE;
+ save_table_list= context->table_list;
+ save_first_table= context->first_name_resolution_table;
+ save_last_table= context->last_name_resolution_table;
+ context->table_list= &tables;
+ context->first_name_resolution_table= &tables;
+ context->last_name_resolution_table= NULL;
+ func_expr->walk(&Item::change_context_processor, 0, (uchar*) context);
+ save_where= thd->where;
+ thd->where= "virtual column function";
+
+ /* Save the context before fixing the fields*/
+ save_use_only_table_context= thd->lex->use_only_table_context;
+ thd->lex->use_only_table_context= TRUE;
+ /* Fix fields referenced to by the virtual column function */
+ error= func_expr->fix_fields(thd, (Item**)0);
+ /* Restore the original context*/
+ thd->lex->use_only_table_context= save_use_only_table_context;
+ context->table_list= save_table_list;
+ context->first_name_resolution_table= save_first_table;
+ context->last_name_resolution_table= save_last_table;
+
+ if (unlikely(error))
+ {
+ DBUG_PRINT("info",
+ ("Field in virtual column expression does not belong to the table"));
+ goto end;
+ }
+ thd->where= save_where;
+ if (unlikely(func_expr->result_type() == ROW_RESULT))
+ {
+ my_error(ER_ROW_EXPR_FOR_VCOL, MYF(0));
+ goto end;
+ }
+#ifdef PARANOID
+ /*
+ Walk through the Item tree checking if all items are valid
+ to be part of the virtual column
+ */
+ error= func_expr->walk(&Item::check_vcol_func_processor, 0, NULL);
+ if (error)
+ {
+ my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), field_name);
+ goto end;
+ }
+#endif
+ if (unlikely(func_expr->const_item()))
+ {
+ my_error(ER_CONST_EXPR_IN_VCOL, MYF(0));
+ goto end;
+ }
+ /* Ensure that this virtual column is not based on another virtual field. */
+ ptr= table->field;
+ while ((field= *(ptr++)))
+ {
+ if ((field->flags & GET_FIXED_FIELDS_FLAG) &&
+ (field->vcol_info))
+ {
+ my_error(ER_VCOL_BASED_ON_VCOL, MYF(0));
+ goto end;
+ }
+ }
+ result= FALSE;
+
+end:
+
+ /* Clear GET_FIXED_FIELDS_FLAG for the fields of the table */
+ clear_field_flag(table);
+
+ table->get_fields_in_item_tree= FALSE;
+ thd->mark_used_columns= save_mark_used_columns;
+ table->map= 0; //Restore old value
+
+ DBUG_RETURN(result);
+}
+
+/*
+ @brief
+ Unpack the definition of a virtual column from its linear representation
+
+ @parm
+ thd The thread object
+ @param
+ table The table containing the virtual column
+ @param
+ field The field for the virtual
+ @param
+ vcol_expr The string representation of the defining expression
+ @param[out]
+ error_reported The flag to inform the caller that no other error
+ messages are to be generated
+
+ @details
+ The function takes string representation 'vcol_expr' of the defining
+ expression for the virtual field 'field' of the table 'table' and
+ parses it, building an item object for it. The pointer to this item is
+ placed into in field->vcol_info.expr_item. After this the function performs
+ semantic analysis of the item by calling the the function fix_vcol_expr.
+ Since the defining expression is part of the table definition the item for
+ it is created in table->memroot within the special arena TABLE::expr_arena.
+
+ @note
+ Before passing 'vcol_expr" to the parser the function embraces it in
+ parenthesis and prepands it a special keyword.
+
+ @retval
+ FALSE If a success
+ @retval
+ TRUE Otherwise
+*/
+bool unpack_vcol_info_from_frm(THD *thd,
+ TABLE *table,
+ Field *field,
+ LEX_STRING *vcol_expr,
+ bool *error_reported)
+{
+ bool rc;
+ char *vcol_expr_str;
+ int str_len;
+ CHARSET_INFO *old_character_set_client;
+ Query_arena *backup_stmt_arena_ptr;
+ Query_arena backup_arena;
+ Query_arena *vcol_arena= 0;
+ Parser_state parser_state;
+ DBUG_ENTER("unpack_vcol_info_from_frm");
+ DBUG_ASSERT(vcol_expr);
+
+ old_character_set_client= thd->variables.character_set_client;
+ backup_stmt_arena_ptr= thd->stmt_arena;
+
+ /*
+ Step 1: Construct the input string for the parser.
+ The string to be parsed has to be of the following format:
+ "PARSE_VCOL_EXPR (<expr_string_from_frm>)".
+ */
+
+ if (!(vcol_expr_str= (char*) alloc_root(&table->mem_root,
+ vcol_expr->length +
+ parse_vcol_keyword.length + 3)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ memcpy(vcol_expr_str,
+ (char*) parse_vcol_keyword.str,
+ parse_vcol_keyword.length);
+ str_len= parse_vcol_keyword.length;
+ memcpy(vcol_expr_str + str_len, "(", 1);
+ str_len++;
+ memcpy(vcol_expr_str + str_len,
+ (char*) vcol_expr->str,
+ vcol_expr->length);
+ str_len+= vcol_expr->length;
+ memcpy(vcol_expr_str + str_len, ")", 1);
+ str_len++;
+ memcpy(vcol_expr_str + str_len, "\0", 1);
+ str_len++;
+
+ if (parser_state.init(thd, vcol_expr_str, str_len))
+ goto err;
+
+ /*
+ Step 2: Setup thd for parsing.
+ */
+ vcol_arena= table->expr_arena;
+ if (!vcol_arena)
+ {
+ Query_arena expr_arena(&table->mem_root, Query_arena::INITIALIZED);
+ if (!(vcol_arena= (Query_arena *) alloc_root(&table->mem_root,
+ sizeof(Query_arena))))
+ goto err;
+ *vcol_arena= expr_arena;
+ table->expr_arena= vcol_arena;
+ }
+ thd->set_n_backup_active_arena(vcol_arena, &backup_arena);
+ thd->stmt_arena= vcol_arena;
+
+ thd->lex->parse_vcol_expr= TRUE;
+
+ /*
+ Step 3: Use the parser to build an Item object from vcol_expr_str.
+ */
+ if (parse_sql(thd, &parser_state, NULL))
+ {
+ goto err;
+ }
+ /* From now on use vcol_info generated by the parser. */
+ field->vcol_info= thd->lex->vcol_info;
+
+ /* Validate the Item tree. */
+ if (fix_vcol_expr(thd, table, field))
+ {
+ *error_reported= TRUE;
+ field->vcol_info= 0;
+ goto err;
+ }
+ rc= FALSE;
+ goto end;
+
+err:
+ rc= TRUE;
+ thd->lex->parse_vcol_expr= FALSE;
+ thd->free_items();
+end:
+ thd->stmt_arena= backup_stmt_arena_ptr;
+ if (vcol_arena)
+ thd->restore_active_arena(vcol_arena, &backup_arena);
+ thd->variables.character_set_client= old_character_set_client;
+
+ DBUG_RETURN(rc);
+}
+
+/*
+ Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE
+*/
/*
Open a table based on a TABLE_SHARE
@@ -1678,7 +2085,8 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
uint records, i, bitmap_size;
bool error_reported= FALSE;
uchar *record, *bitmaps;
- Field **field_ptr;
+ Field **field_ptr, **vfield_ptr;
+ uint8 save_context_analysis_only= thd->lex->context_analysis_only;
DBUG_ENTER("open_table_from_share");
DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str,
share->table_name.str, (long) outparam));
@@ -1686,6 +2094,8 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
/* Parsing of partitioning information from .frm needs thd->lex set up. */
DBUG_ASSERT(thd->lex->is_lex_started);
+ thd->lex->context_analysis_only= 0; // not a view
+
error= 1;
bzero((char*) outparam, sizeof(*outparam));
outparam->in_use= thd;
@@ -1695,7 +2105,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
- if (!(outparam->alias= my_strdup(alias, MYF(MY_WME))))
+ if (outparam->alias.copy(alias, strlen(alias), table_alias_charset))
goto err;
outparam->quick_keys.init();
outparam->covering_keys.init();
@@ -1832,6 +2242,34 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
}
}
+ /*
+ Process virtual columns, if any.
+ */
+ if (!(vfield_ptr = (Field **) alloc_root(&outparam->mem_root,
+ (uint) ((share->vfields+1)*
+ sizeof(Field*)))))
+ goto err;
+
+ outparam->vfield= vfield_ptr;
+
+ for (field_ptr= outparam->field; *field_ptr; field_ptr++)
+ {
+ if ((*field_ptr)->vcol_info)
+ {
+ if (unpack_vcol_info_from_frm(thd,
+ outparam,
+ *field_ptr,
+ &(*field_ptr)->vcol_info->expr_str,
+ &error_reported))
+ {
+ error= 4; // in case no error is reported
+ goto err;
+ }
+ *(vfield_ptr++)= *field_ptr;
+ }
+ }
+ *vfield_ptr= 0; // End marker
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (share->partition_info_len && outparam->file)
{
@@ -1899,17 +2337,30 @@ partititon_err:
}
#endif
+ /* Check virtual columns against table's storage engine. */
+ if (share->vfields &&
+ !(outparam->file &&
+ (outparam->file->ha_table_flags() & HA_CAN_VIRTUAL_COLUMNS)))
+ {
+ my_error(ER_UNSUPPORTED_ENGINE_FOR_VIRTUAL_COLUMNS, MYF(0),
+ plugin_name(share->db_plugin)->str);
+ error_reported= TRUE;
+ goto err;
+ }
+
/* Allocate bitmaps */
bitmap_size= share->column_bitmap_size;
- if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*3)))
+ if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*4)))
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,
+ bitmap_init(&outparam->def_vcol_set,
(my_bitmap_map*) (bitmaps+bitmap_size*2), share->fields, FALSE);
+ bitmap_init(&outparam->tmp_set,
+ (my_bitmap_map*) (bitmaps+bitmap_size*3), share->fields, FALSE);
outparam->default_column_bitmaps();
/* The table struct is now initialized; Open the table */
@@ -1973,6 +2424,7 @@ partititon_err:
HA_HAS_OWN_BINLOGGING);
thd->status_var.opened_tables++;
+ thd->lex->context_analysis_only= save_context_analysis_only;
DBUG_RETURN (0);
err:
@@ -1985,8 +2437,9 @@ partititon_err:
#endif
outparam->file= 0; // For easier error checking
outparam->db_stat=0;
+ thd->lex->context_analysis_only= save_context_analysis_only;
free_root(&outparam->mem_root, MYF(0)); // Safe to call on bzero'd root
- my_free((char*) outparam->alias, MYF(MY_ALLOW_ZERO_PTR));
+ outparam->alias.free();
DBUG_RETURN (error);
}
@@ -2012,12 +2465,15 @@ int closefrm(register TABLE *table, bool free_share)
table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
error=table->file->close();
}
- my_free((char*) table->alias, MYF(MY_ALLOW_ZERO_PTR));
- table->alias= 0;
+ table->alias.free();
+ if (table->expr_arena)
+ table->expr_arena->free_items();
if (table->field)
{
for (Field **ptr=table->field ; *ptr ; ptr++)
+ {
delete *ptr;
+ }
table->field= 0;
}
delete table->file;
@@ -2485,6 +2941,7 @@ File create_frm(THD *thd, const char *name, const char *db,
ulong length;
uchar fill[IO_SIZE];
int create_flags= O_RDWR | O_TRUNC;
+ DBUG_ENTER("create_frm");
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
create_flags|= O_EXCL | O_NOFOLLOW;
@@ -2566,7 +3023,7 @@ File create_frm(THD *thd, const char *name, const char *db,
{
VOID(my_close(file,MYF(0)));
VOID(my_delete(name,MYF(0)));
- return(-1);
+ DBUG_RETURN(-1);
}
}
}
@@ -2577,7 +3034,7 @@ File create_frm(THD *thd, const char *name, const char *db,
else
my_error(ER_CANT_CREATE_TABLE,MYF(0),table,my_errno);
}
- return (file);
+ DBUG_RETURN(file);
} /* create_frm */
@@ -2596,6 +3053,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
create_info->comment= share->comment;
create_info->transactional= share->transactional;
create_info->page_checksum= share->page_checksum;
+ create_info->option_list= share->option_list;
DBUG_VOID_RETURN;
}
@@ -2848,7 +3306,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
const TABLE_FIELD_TYPE *field_def= table_def->field;
DBUG_ENTER("table_check_intact");
DBUG_PRINT("info",("table: %s expected_count: %d",
- table->alias, table_def->count));
+ table->alias.c_ptr(), table_def->count));
/* Whether the table definition has already been validated. */
if (table->s->table_field_def_cache == table_def)
@@ -2863,15 +3321,15 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
{
report_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE,
ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE),
- table->alias, table_def->count, table->s->fields,
- static_cast<int>(table->s->mysql_version),
- MYSQL_VERSION_ID);
+ table->alias.c_ptr(), table_def->count, table->s->fields,
+ (int) table->s->mysql_version, MYSQL_VERSION_ID);
DBUG_RETURN(TRUE);
}
else if (MYSQL_VERSION_ID == table->s->mysql_version)
{
report_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED,
- ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED), table->alias,
+ ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED),
+ table->alias.c_ptr(),
table_def->count, table->s->fields);
DBUG_RETURN(TRUE);
}
@@ -2883,11 +3341,13 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
is backward compatible.
*/
}
- char buffer[STRING_BUFFER_USUAL_SIZE];
+ char buffer[1024];
for (i=0 ; i < table_def->count; i++, field_def++)
{
String sql_type(buffer, sizeof(buffer), system_charset_info);
sql_type.length(0);
+ /* Allocate min 256 characters at once */
+ sql_type.extra_allocation(256);
if (i < table->s->fields)
{
Field *field= table->field[i];
@@ -2902,7 +3362,8 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
*/
report_error(0, "Incorrect definition of table %s.%s: "
"expected column '%s' at position %d, found '%s'.",
- table->s->db.str, table->alias, field_def->name.str, i,
+ table->s->db.str, table->alias.c_ptr(),
+ field_def->name.str, i,
field->field_name);
}
field->sql_type(sql_type);
@@ -2928,7 +3389,8 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
{
report_error(0, "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,
+ "%s, found type %s.", table->s->db.str,
+ table->alias.c_ptr(),
field_def->name.str, i, field_def->type.str,
sql_type.c_ptr_safe());
error= TRUE;
@@ -2938,7 +3400,8 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
report_error(0, "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,
+ "character set.", table->s->db.str,
+ table->alias.c_ptr(),
field_def->name.str, i, field_def->cset.str);
error= TRUE;
}
@@ -2948,7 +3411,8 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
report_error(0, "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,
+ "character set '%s'.", table->s->db.str,
+ table->alias.c_ptr(),
field_def->name.str, i, field_def->cset.str,
field->charset()->csname);
error= TRUE;
@@ -2959,7 +3423,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
report_error(0, "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->s->db.str, table->alias.c_ptr(),
field_def->name.str, i, field_def->type.str);
error= TRUE;
}
@@ -3763,11 +4227,8 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd)
{
DBUG_PRINT("info", ("This table is suid view => load contest"));
DBUG_ASSERT(view && view_sctx);
- if (acl_getroot_no_password(view_sctx,
- definer.user.str,
- definer.host.str,
- definer.host.str,
- thd->db))
+ if (acl_getroot(view_sctx, definer.user.str, definer.host.str,
+ definer.host.str, thd->db))
{
if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) ||
(thd->lex->sql_command == SQLCOM_SHOW_FIELDS))
@@ -4360,9 +4821,10 @@ 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);
+ bitmap_clear_all(&table->def_vcol_set);
*/
- bzero((char*) def_read_set.bitmap, s->column_bitmap_size*2);
- column_bitmaps_set(&def_read_set, &def_write_set);
+ bzero((char*) def_read_set.bitmap, s->column_bitmap_size*3);
+ column_bitmaps_set(&def_read_set, &def_write_set, &def_vcol_set);
}
@@ -4467,7 +4929,14 @@ void st_table::mark_columns_used_by_index_no_reset(uint index,
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);
+ if (key_part->field->vcol_info &&
+ key_part->field->vcol_info->expr_item)
+ key_part->field->vcol_info->
+ expr_item->walk(&Item::register_field_in_bitmap,
+ 1, (uchar *) bitmap);
+ }
}
@@ -4594,6 +5063,8 @@ void st_table::mark_columns_needed_for_update()
file->column_bitmaps_signal();
}
}
+ /* Mark all virtual columns needed for update */
+ mark_virtual_columns_for_write(FALSE);
DBUG_VOID_RETURN;
}
@@ -4620,9 +5091,109 @@ void st_table::mark_columns_needed_for_insert()
}
if (found_next_number_field)
mark_auto_increment_column();
+ /* Mark virtual columns for insert */
+ mark_virtual_columns_for_write(TRUE);
}
+/*
+ @brief Mark a column as virtual used by the query
+
+ @param field the field for the column to be marked
+
+ @details
+ The function marks the column for 'field' as virtual (computed)
+ in the bitmap vcol_set.
+ If the column is marked for the first time the expression to compute
+ the column is traversed and all columns that are occurred there are
+ marked in the read_set of the table.
+
+ @retval
+ TRUE if column is marked for the first time
+ @retval
+ FALSE otherwise
+*/
+
+bool st_table::mark_virtual_col(Field *field)
+{
+ bool res;
+ DBUG_ASSERT(field->vcol_info);
+ if (!(res= bitmap_fast_test_and_set(vcol_set, field->field_index)))
+ {
+ Item *vcol_item= field->vcol_info->expr_item;
+ DBUG_ASSERT(vcol_item);
+ vcol_item->walk(&Item::register_field_in_read_map, 1, (uchar *) 0);
+ }
+ return res;
+}
+
+
+/*
+ @brief Mark virtual columns for update/insert commands
+
+ @param insert_fl <-> virtual columns are marked for insert command
+
+ @details
+ The function marks virtual columns used in a update/insert commands
+ in the vcol_set bitmap.
+ For an insert command a virtual column is always marked in write_set if
+ it is a stored column.
+ If a virtual column is from write_set it is always marked in vcol_set.
+ If a stored virtual column is not from write_set but it is computed
+ through columns from write_set it is also marked in vcol_set, and,
+ besides, it is added to write_set.
+
+ @return void
+
+ @note
+ Let table t1 have columns a,b,c and let column c be a stored virtual
+ column computed through columns a and b. Then for the query
+ UPDATE t1 SET a=1
+ column c will be placed into vcol_set and into write_set while
+ column b will be placed into read_set.
+ If column c was a virtual column, but not a stored virtual column
+ then it would not be added to any of the sets. Column b would not
+ be added to read_set either.
+*/
+
+void st_table::mark_virtual_columns_for_write(bool insert_fl)
+{
+ Field **vfield_ptr, *tmp_vfield;
+ bool bitmap_updated= FALSE;
+
+ for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
+ {
+ tmp_vfield= *vfield_ptr;
+ if (bitmap_is_set(write_set, tmp_vfield->field_index))
+ bitmap_updated= mark_virtual_col(tmp_vfield);
+ else if (tmp_vfield->stored_in_db)
+ {
+ bool mark_fl= insert_fl;
+ if (!mark_fl)
+ {
+ MY_BITMAP *save_read_set;
+ Item *vcol_item= tmp_vfield->vcol_info->expr_item;
+ DBUG_ASSERT(vcol_item);
+ bitmap_clear_all(&tmp_set);
+ save_read_set= read_set;
+ read_set= &tmp_set;
+ vcol_item->walk(&Item::register_field_in_read_map, 1, (uchar *) 0);
+ read_set= save_read_set;
+ bitmap_intersect(&tmp_set, write_set);
+ mark_fl= !bitmap_is_clear_all(&tmp_set);
+ }
+ if (mark_fl)
+ {
+ bitmap_set_bit(write_set, tmp_vfield->field_index);
+ mark_virtual_col(tmp_vfield);
+ bitmap_updated= TRUE;
+ }
+ }
+ }
+ if (bitmap_updated)
+ file->column_bitmaps_signal();
+}
+
/**
@brief Check if this is part of a MERGE table with attached children.
@@ -4630,7 +5201,7 @@ void st_table::mark_columns_needed_for_insert()
@retval TRUE children are attached
@retval FALSE no MERGE part or children not attached
- @detail
+ @details
A MERGE table consists of a parent TABLE and zero or more child
TABLEs. Each of these TABLEs is called a part of a MERGE table.
*/
@@ -4891,6 +5462,57 @@ size_t max_row_length(TABLE *table, const uchar *data)
return length;
}
+/*
+ @brief Compute values for virtual columns used in query
+
+ @param thd Thread handle
+ @param table The TABLE object
+ @param for_write Requests to compute only fields needed for write
+
+ @details
+ The function computes the values of the virtual columns of the table and
+ stores them in the table record buffer.
+ Only fields from vcol_set are computed, and, when the flag for_write is not
+ set to TRUE, a virtual field is computed only if it's not stored.
+ The flag for_write is set to TRUE for row insert/update operations.
+
+ @retval
+ 0 Success
+ @retval
+ >0 Error occurred when storing a virtual field value
+*/
+
+int update_virtual_fields(THD *thd, TABLE *table, bool for_write)
+{
+ DBUG_ENTER("update_virtual_fields");
+ Field **vfield_ptr, *vfield;
+ int error= 0;
+ if (!table || !table->vfield)
+ DBUG_RETURN(0);
+
+ thd->reset_arena_for_cached_items(table->expr_arena);
+ /* Iterate over virtual fields in the table */
+ for (vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++)
+ {
+ vfield= (*vfield_ptr);
+ DBUG_ASSERT(vfield->vcol_info && vfield->vcol_info->expr_item);
+ /* Only update those fields that are marked in the vcol_set bitmap */
+ if (bitmap_is_set(table->vcol_set, vfield->field_index) &&
+ (for_write || !vfield->stored_in_db))
+ {
+ /* Compute the actual value of the virtual fields */
+ error= vfield->vcol_info->expr_item->save_in_field(vfield, 0);
+ DBUG_PRINT("info", ("field '%s' - updated", vfield->field_name));
+ }
+ else
+ {
+ DBUG_PRINT("info", ("field '%s' - skipped", vfield->field_name));
+ }
+ }
+ thd->reset_arena_for_cached_items(0);
+ DBUG_RETURN(0);
+}
+
/*****************************************************************************
** Instansiate templates
*****************************************************************************/
diff --git a/sql/table.h b/sql/table.h
index 687d0296e1e..1b7713022ce 100644
--- a/sql/table.h
+++ b/sql/table.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 */
+#ifndef SQL_TABLE_INCLUDED
+#define SQL_TABLE_INCLUDED
/* Structs that defines the TABLE */
@@ -25,6 +27,7 @@ class st_select_lex;
class partition_info;
class COND_EQUAL;
class Security_context;
+class Query_arena;
/*************************************************************************/
@@ -339,6 +342,8 @@ typedef struct st_table_share
#ifdef NOT_YET
struct st_table *open_tables; /* link to open tables */
#endif
+ engine_option_value *option_list; /* text options for table */
+ ha_table_option_struct *option_struct; /* structure with parsed options */
/* The following is copied to each TABLE on OPEN */
Field **field;
@@ -381,6 +386,8 @@ typedef struct st_table_share
ulong version, mysql_version;
ulong timestamp_offset; /* Set to offset+1 of record */
ulong reclength; /* Recordlength */
+ /* Stored record length. No generated-only virtual fields are included */
+ ulong stored_rec_length;
plugin_ref db_plugin; /* storage engine plugin */
inline handlerton *db_type() const /* table_type for handler */
@@ -406,6 +413,8 @@ typedef struct st_table_share
*/
uint null_bytes_for_compare;
uint fields; /* Number of fields */
+ /* Number of stored fields, generated-only virtual fields are not included */
+ uint stored_fields;
uint rec_buff_length; /* Size of table->record[] buffer */
uint keys, key_parts;
uint max_key_length, max_unique_length, total_key_length;
@@ -427,6 +436,7 @@ typedef struct st_table_share
uint error, open_errno, errarg; /* error from open_table_def() */
uint column_bitmap_size;
uchar frm_version;
+ uint vfields; /* Number of computed (virtual) fields */
bool null_field_first;
bool system; /* Set if system table (one record) */
bool crypted; /* If .frm file is crypted */
@@ -705,16 +715,17 @@ struct st_table {
Field *next_number_field; /* Set if next_number is activated */
Field *found_next_number_field; /* Set on open */
Field_timestamp *timestamp_field;
+ Field **vfield; /* Pointer to virtual fields*/
/* Table's triggers, 0 if there are no of them */
Table_triggers_list *triggers;
TABLE_LIST *pos_in_table_list;/* Element referring to this table */
ORDER *group;
- const char *alias; /* alias or table name */
+ String 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 */
+ MY_BITMAP def_read_set, def_write_set, def_vcol_set, tmp_set;
+ MY_BITMAP *read_set, *write_set, *vcol_set; /* Active column sets */
/*
The ID of the query that opened and is using this table. Has different
meanings depending on the table type.
@@ -782,7 +793,7 @@ struct st_table {
/* number of select if it is derived table */
uint derived_select_number;
int current_lock; /* Type of lock on table */
- my_bool copy_blobs; /* copy_blobs when storing */
+ bool copy_blobs; /* copy_blobs when storing */
/*
0 or JOIN_TYPE_{LEFT|RIGHT}. Currently this is only compared to 0.
@@ -794,34 +805,34 @@ struct st_table {
If true, the current table row is considered to have all columns set to
NULL, including columns declared as "not null" (see maybe_null).
*/
- my_bool null_row;
+ 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;
+ bool force_index;
/**
Flag set when the statement contains FORCE INDEX FOR ORDER BY
See TABLE_LIST::process_index_hints().
*/
- my_bool force_index_order;
+ bool force_index_order;
/**
Flag set when the statement contains FORCE INDEX FOR GROUP BY
See TABLE_LIST::process_index_hints().
*/
- my_bool force_index_group;
- my_bool distinct,const_table,no_rows;
+ bool force_index_group;
+ bool distinct,const_table,no_rows;
/**
If set, the optimizer has found that row retrieval should access index
tree only.
*/
- my_bool key_read;
- my_bool no_keyread;
+ bool key_read;
+ bool no_keyread;
/*
Placeholder for an open table which prevents other connections
from taking name-locks on this table. Typically used with
@@ -839,30 +850,38 @@ struct st_table {
object associated with it (db_stat is always 0), but please do
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;
+ bool open_placeholder;
+ bool locked_by_logger;
+ bool no_replicate;
+ bool locked_by_name;
+ bool fulltext_searched;
+ bool no_cache;
/* To signal that the table is associated with a HANDLER statement */
- my_bool open_by_handler;
+ 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.
Used only in the MODE_NO_AUTO_VALUE_ON_ZERO mode.
*/
- 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 */
+ bool auto_increment_field_not_null;
+ bool insert_or_update; /* Can be used by the handler */
+ bool alias_name_used; /* true if table_name is alias */
+ bool get_fields_in_item_tree; /* Signal to fix_field */
/* If MERGE children attached to parent. See top comment in ha_myisammrg.cc */
- my_bool children_attached;
+ bool children_attached;
REGINFO reginfo; /* field connections */
MEM_ROOT mem_root;
GRANT_INFO grant;
FILESORT_INFO sort;
+ /*
+ The arena which the items for expressions from the table definition
+ are associated with.
+ Currently only the items of the expressions for virtual columns are
+ associated with this arena.
+ TODO: To attach the partitioning expressions to this arena.
+ */
+ Query_arena *expr_arena;
#ifdef WITH_PARTITION_STORAGE_ENGINE
partition_info *part_info; /* Partition related information */
bool no_partitions_used; /* If true, all partitions have been pruned away */
@@ -880,6 +899,8 @@ struct st_table {
void mark_columns_needed_for_update(void);
void mark_columns_needed_for_delete(void);
void mark_columns_needed_for_insert(void);
+ bool mark_virtual_col(Field *field);
+ void mark_virtual_columns_for_write(bool insert_fl);
inline void column_bitmaps_set(MY_BITMAP *read_set_arg,
MY_BITMAP *write_set_arg)
{
@@ -888,12 +909,30 @@ struct st_table {
if (file)
file->column_bitmaps_signal();
}
+ inline void column_bitmaps_set(MY_BITMAP *read_set_arg,
+ MY_BITMAP *write_set_arg,
+ MY_BITMAP *vcol_set_arg)
+ {
+ read_set= read_set_arg;
+ write_set= write_set_arg;
+ vcol_set= vcol_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 column_bitmaps_set_no_signal(MY_BITMAP *read_set_arg,
+ MY_BITMAP *write_set_arg,
+ MY_BITMAP *vcol_set_arg)
+ {
+ read_set= read_set_arg;
+ write_set= write_set_arg;
+ vcol_set= vcol_set_arg;
+ }
inline void use_all_columns()
{
column_bitmaps_set(&s->all_set, &s->all_set);
@@ -902,6 +941,7 @@ struct st_table {
{
read_set= &def_read_set;
write_set= &def_write_set;
+ vcol_set= &def_vcol_set;
}
/* Is table open or should be treated as such by name-locking? */
inline bool is_name_opened() { return db_stat || open_placeholder; }
@@ -957,6 +997,7 @@ typedef struct st_foreign_key_info
enum enum_schema_tables
{
SCH_CHARSETS= 0,
+ SCH_CLIENT_STATS,
SCH_COLLATIONS,
SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
SCH_COLUMNS,
@@ -966,6 +1007,8 @@ enum enum_schema_tables
SCH_FILES,
SCH_GLOBAL_STATUS,
SCH_GLOBAL_VARIABLES,
+ SCH_INDEX_STATS,
+ SCH_KEY_CACHES,
SCH_KEY_COLUMN_USAGE,
SCH_OPEN_TABLES,
SCH_PARTITIONS,
@@ -984,8 +1027,10 @@ enum enum_schema_tables
SCH_TABLE_CONSTRAINTS,
SCH_TABLE_NAMES,
SCH_TABLE_PRIVILEGES,
+ SCH_TABLE_STATS,
SCH_TRIGGERS,
SCH_USER_PRIVILEGES,
+ SCH_USER_STATS,
SCH_VARIABLES,
SCH_VIEWS
};
@@ -1822,3 +1867,4 @@ static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set,
size_t max_row_length(TABLE *table, const uchar *data);
+#endif
diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc
index c96171c4e74..7afb8027a2c 100644
--- a/sql/thr_malloc.cc
+++ b/sql/thr_malloc.cc
@@ -65,11 +65,13 @@ void init_sql_alloc(MEM_ROOT *mem_root, uint block_size, uint pre_alloc)
}
+#ifndef MYSQL_CLIENT
void *sql_alloc(size_t Size)
{
MEM_ROOT *root= *my_pthread_getspecific_ptr(MEM_ROOT**,THR_MALLOC);
return alloc_root(root,Size);
}
+#endif
void *sql_calloc(size_t size)
diff --git a/sql/tztime.cc b/sql/tztime.cc
index 3cdc1853c14..6bb295b98c6 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -216,7 +216,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
ALIGN_SIZE(sp->typecnt *
sizeof(TRAN_TYPE_INFO)) +
#ifdef ABBR_ARE_USED
- ALIGN_SIZE(sp->charcnt) +
+ ALIGN_SIZE(sp->charcnt+1) +
#endif
sp->leapcnt * sizeof(LS_INFO))))
return 1;
@@ -229,7 +229,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO));
#ifdef ABBR_ARE_USED
sp->chars= tzinfo_buf;
- tzinfo_buf+= ALIGN_SIZE(sp->charcnt);
+ tzinfo_buf+= ALIGN_SIZE(sp->charcnt+1);
#endif
sp->lsis= (LS_INFO *)tzinfo_buf;
@@ -1678,7 +1678,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
tz_leapcnt= 0;
- res= table->file->index_first(table->record[0]);
+ res= table->file->ha_index_first(table->record[0]);
while (!res)
{
@@ -1700,7 +1700,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans,
tz_lsis[tz_leapcnt-1].ls_corr));
- res= table->file->index_next(table->record[0]);
+ res= table->file->ha_index_next(table->record[0]);
}
(void)table->file->ha_index_end();
@@ -1815,6 +1815,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
uint tzid, ttid;
my_time_t ttime;
char buff[MAX_FIELD_WIDTH];
+ uchar keybuff[32];
+ Field *field;
String abbr(buff, sizeof(buff), &my_charset_latin1);
char *alloc_buff, *tz_name_buff;
/*
@@ -1867,8 +1869,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
*/
(void)table->file->ha_index_init(0, 1);
- if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
- HA_WHOLE_KEY, HA_READ_KEY_EXACT))
+ if (table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
+ HA_WHOLE_KEY, HA_READ_KEY_EXACT))
{
#ifdef EXTRA_DEBUG
/*
@@ -1892,11 +1894,16 @@ 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);
+ field= table->field[0];
+ field->store((longlong) tzid, TRUE);
+ DBUG_ASSERT(field->key_length() <= sizeof(keybuff));
+ field->get_key_image(keybuff,
+ min(field->key_length(), sizeof(keybuff)),
+ Field::itRAW);
(void)table->file->ha_index_init(0, 1);
- if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
- HA_WHOLE_KEY, HA_READ_KEY_EXACT))
+ if (table->file->ha_index_read_map(table->record[0], keybuff,
+ HA_WHOLE_KEY, HA_READ_KEY_EXACT))
{
sql_print_error("Can't find description of time zone '%u'", tzid);
goto end;
@@ -1919,11 +1926,16 @@ 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);
+ field= table->field[0];
+ field->store((longlong) tzid, TRUE);
+ DBUG_ASSERT(field->key_length() <= sizeof(keybuff));
+ field->get_key_image(keybuff,
+ min(field->key_length(), sizeof(keybuff)),
+ Field::itRAW);
(void)table->file->ha_index_init(0, 1);
- res= table->file->index_read_map(table->record[0], table->field[0]->ptr,
- (key_part_map)1, HA_READ_KEY_EXACT);
+ res= table->file->ha_index_read_map(table->record[0], keybuff,
+ (key_part_map)1, HA_READ_KEY_EXACT);
while (!res)
{
ttid= (uint)table->field[1]->val_int();
@@ -1970,8 +1982,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
tmp_tz_info.typecnt= ttid + 1;
- res= table->file->index_next_same(table->record[0],
- table->field[0]->ptr, 4);
+ res= table->file->ha_index_next_same(table->record[0], keybuff, 4);
}
if (res != HA_ERR_END_OF_FILE)
@@ -1993,8 +2004,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
table->field[0]->store((longlong) tzid, TRUE);
(void)table->file->ha_index_init(0, 1);
- res= table->file->index_read_map(table->record[0], table->field[0]->ptr,
- (key_part_map)1, HA_READ_KEY_EXACT);
+ res= table->file->ha_index_read_map(table->record[0], keybuff,
+ (key_part_map)1, HA_READ_KEY_EXACT);
while (!res)
{
ttime= (my_time_t)table->field[1]->val_int();
@@ -2023,8 +2034,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
("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],
- table->field[0]->ptr, 4);
+ res= table->file->ha_index_next_same(table->record[0], keybuff, 4);
}
/*
diff --git a/sql/unireg.cc b/sql/unireg.cc
index a5d9edbf181..dde755462dd 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -25,11 +25,15 @@
*/
#include "mysql_priv.h"
+#include "create_options.h"
#include <m_ctype.h>
#include <assert.h>
#define FCOMP 17 /* Bytes for a packed field */
+/* threshold for safe_alloca */
+#define ALLOCA_THRESHOLD 2048
+
static uchar * pack_screens(List<Create_field> &create_fields,
uint *info_length, uint *screens, bool small_file);
static uint pack_keys(uchar *keybuff,uint key_count, KEY *key_info,
@@ -108,6 +112,7 @@ bool mysql_create_frm(THD *thd, const char *file_name,
ulong key_buff_length;
File file;
ulong filepos, data_offset;
+ uint options_len;
uchar fileinfo[64],forminfo[288],*keybuff;
uchar *screen_buff;
char buff[128];
@@ -183,6 +188,18 @@ bool mysql_create_frm(THD *thd, const char *file_name,
create_info->extra_size+= key_info[i].parser_name->length + 1;
}
+ options_len= engine_table_options_frm_length(create_info->option_list,
+ create_fields,
+ keys, key_info);
+ DBUG_PRINT("info", ("Options length: %u", options_len));
+ if (options_len)
+ {
+ create_info->table_options|= HA_OPTION_TEXT_CREATE_OPTIONS;
+ create_info->extra_size+= (options_len + 4);
+ }
+ else
+ create_info->table_options&= ~HA_OPTION_TEXT_CREATE_OPTIONS;
+
if ((file=create_frm(thd, file_name, db, table, reclength, fileinfo,
create_info, keys)) < 0)
{
@@ -302,6 +319,7 @@ bool mysql_create_frm(THD *thd, const char *file_name,
if (my_write(file, (uchar*) buff, 6, MYF_RW))
goto err;
}
+
for (i= 0; i < keys; i++)
{
if (key_info[i].parser_name)
@@ -312,6 +330,24 @@ bool mysql_create_frm(THD *thd, const char *file_name,
}
}
+ if (options_len)
+ {
+ uchar *optbuff= (uchar *)my_safe_alloca(options_len + 4, ALLOCA_THRESHOLD);
+ my_bool error;
+ DBUG_PRINT("info", ("Create options length: %u", options_len));
+ if (!optbuff)
+ goto err;
+ int4store(optbuff, options_len);
+ engine_table_options_frm_image(optbuff + 4,
+ create_info->option_list,
+ create_fields,
+ keys, key_info);
+ error= my_write(file, optbuff, options_len + 4, MYF_RW);
+ my_safe_afree(optbuff, options_len + 4, ALLOCA_THRESHOLD);
+ if (error)
+ goto err;
+ }
+
VOID(my_seek(file,filepos,MY_SEEK_SET,MYF(0)));
if (my_write(file, forminfo, 288, MYF_RW) ||
my_write(file, screen_buff, info_length, MYF_RW) ||
@@ -591,7 +627,7 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
{
uint length,int_count,int_length,no_empty, int_parts;
uint time_stamp_pos,null_fields;
- ulong reclength, totlength, n_length, com_length;
+ ulong reclength, totlength, n_length, com_length, vcol_info_length;
DBUG_ENTER("pack_header");
if (create_fields.elements > MAX_FIELDS)
@@ -602,8 +638,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
totlength= 0L;
reclength= data_offset;
- no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields=
- com_length=0;
+ no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields=0;
+ com_length=vcol_info_length=0;
n_length=2L;
/* Check fields */
@@ -632,6 +668,30 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
field->field_name, static_cast<ulong>(tmp_len));
field->comment.length= tmp_len;
}
+ if (field->vcol_info)
+ {
+ tmp_len=
+ system_charset_info->cset->charpos(system_charset_info,
+ field->vcol_info->expr_str.str,
+ field->vcol_info->expr_str.str +
+ field->vcol_info->expr_str.length,
+ VIRTUAL_COLUMN_EXPRESSION_MAXLEN);
+
+ if (tmp_len < field->vcol_info->expr_str.length)
+ {
+ my_error(ER_WRONG_STRING_LENGTH, MYF(0),
+ field->vcol_info->expr_str.str,"VIRTUAL COLUMN EXPRESSION",
+ (uint) VIRTUAL_COLUMN_EXPRESSION_MAXLEN);
+ DBUG_RETURN(1);
+ }
+ /*
+ Sum up the length of the expression string and the length of the
+ mandatory header to the total length of info on the defining
+ expressions saved in the frm file for virtual columns.
+ */
+ vcol_info_length+= field->vcol_info->expr_str.length+
+ (uint)FRM_VCOL_HEADER_SIZE;
+ }
totlength+= field->length;
com_length+= field->comment.length;
@@ -651,8 +711,6 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
!time_stamp_pos)
time_stamp_pos= (uint) field->offset+ (uint) data_offset + 1;
length=field->pack_length;
- /* Ensure we don't have any bugs when generating offsets */
- DBUG_ASSERT(reclength == field->offset + data_offset);
if ((uint) field->offset+ (uint) data_offset+ length > reclength)
reclength=(uint) (field->offset+ data_offset + length);
n_length+= (ulong) strlen(field->field_name)+1;
@@ -719,7 +777,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
/* Hack to avoid bugs with small static rows in MySQL */
reclength=max(file->min_record_length(table_options),reclength);
if (info_length+(ulong) create_fields.elements*FCOMP+288+
- n_length+int_length+com_length > 65535L || int_count > 255)
+ n_length+int_length+com_length+vcol_info_length > 65535L ||
+ int_count > 255)
{
my_message(ER_TOO_MANY_FIELDS, ER(ER_TOO_MANY_FIELDS), MYF(0));
DBUG_RETURN(1);
@@ -727,7 +786,7 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
bzero((char*)forminfo,288);
length=(info_length+create_fields.elements*FCOMP+288+n_length+int_length+
- com_length);
+ com_length+vcol_info_length);
int2store(forminfo,length);
forminfo[256] = (uint8) screens;
int2store(forminfo+258,create_fields.elements);
@@ -744,7 +803,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
int2store(forminfo+280,22); /* Rows needed */
int2store(forminfo+282,null_fields);
int2store(forminfo+284,com_length);
- /* Up to forminfo+288 is free to use for additional information */
+ int2store(forminfo+286,vcol_info_length);
+ /* forminfo+288 is free to use for additional information */
DBUG_RETURN(0);
} /* pack_header */
@@ -783,7 +843,7 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
ulong data_offset)
{
reg2 uint i;
- uint int_count, comment_length=0;
+ uint int_count, comment_length= 0, vcol_info_length=0;
uchar buff[MAX_FIELD_WIDTH];
Create_field *field;
DBUG_ENTER("pack_fields");
@@ -796,6 +856,7 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
while ((field=it++))
{
uint recpos;
+ uint cur_vcol_expr_len= 0;
buff[0]= (uchar) field->row;
buff[1]= (uchar) field->col;
buff[2]= (uchar) field->sc_length;
@@ -818,6 +879,17 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
buff[14]= (uchar) field->charset->number;
else
buff[14]= 0; // Numerical
+ if (field->vcol_info)
+ {
+ /*
+ Use the interval_id place in the .frm file to store the length of
+ the additional data saved for the virtual field
+ */
+ buff[12]= cur_vcol_expr_len= field->vcol_info->expr_str.length +
+ (uint)FRM_VCOL_HEADER_SIZE;
+ vcol_info_length+= cur_vcol_expr_len+(uint)FRM_VCOL_HEADER_SIZE;
+ buff[13]= (uchar) MYSQL_TYPE_VIRTUAL;
+ }
int2store(buff+15, field->comment.length);
comment_length+= field->comment.length;
set_if_bigger(int_count,field->interval_id);
@@ -912,6 +984,34 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
DBUG_RETURN(1);
}
}
+ if (vcol_info_length)
+ {
+ it.rewind();
+ int_count=0;
+ while ((field=it++))
+ {
+ /*
+ Pack each virtual field as follows:
+ byte 1 = 1 (always 1 to allow for future extensions)
+ byte 2 = sql_type
+ byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored)
+ byte 4-... = virtual column expression (text data)
+ */
+ if (field->vcol_info && field->vcol_info->expr_str.length)
+ {
+ buff[0]= (uchar)1;
+ buff[1]= (uchar) field->sql_type;
+ buff[2]= (uchar) field->stored_in_db;
+ if (my_write(file, buff, 3, MYF_RW))
+ DBUG_RETURN(1);
+ if (my_write(file,
+ (uchar*) field->vcol_info->expr_str.str,
+ field->vcol_info->expr_str.length,
+ MYF_RW))
+ DBUG_RETURN(1);
+ }
+ }
+ }
DBUG_RETURN(0);
}
diff --git a/sql/unireg.h b/sql/unireg.h
index fd9d4e846bc..33cd5d1006d 100644
--- a/sql/unireg.h
+++ b/sql/unireg.h
@@ -213,6 +213,13 @@
#define DEFAULT_KEY_CACHE_NAME "default"
+/* The length of the header part for each virtual column in the .frm file */
+#define FRM_VCOL_HEADER_SIZE 3
+
+/* Maximum length of the defining expression for a virtual columns */
+#define VIRTUAL_COLUMN_EXPRESSION_MAXLEN 255 - FRM_VCOL_HEADER_SIZE
+
+
/* Include prototypes for unireg */
#include "mysqld_error.h"
diff --git a/sql/winservice.c b/sql/winservice.c
new file mode 100644
index 00000000000..562f047fa79
--- /dev/null
+++ b/sql/winservice.c
@@ -0,0 +1,247 @@
+/*
+ Get Properties of an existing mysqld Windows service
+*/
+
+#include <windows.h>
+#include <winsvc.h>
+#include "winservice.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+
+/*
+ Get version from an executable file
+*/
+void get_file_version(const char *path, int *major, int *minor, int *patch)
+{
+ DWORD version_handle;
+ char *ver= 0;
+ VS_FIXEDFILEINFO info;
+ UINT len;
+ DWORD size;
+ void *p;
+ *major= *minor= *patch= 0;
+
+ size= GetFileVersionInfoSize(path, &version_handle);
+ if (size == 0)
+ return;
+ ver= (char *)malloc(size);
+ if(!GetFileVersionInfo(path, version_handle, size, ver))
+ goto end;
+
+ if(!VerQueryValue(ver,"\\",&p,&len))
+ goto end;
+ memcpy(&info,p ,sizeof(VS_FIXEDFILEINFO));
+
+ *major= (info.dwFileVersionMS & 0xFFFF0000) >> 16;
+ *minor= (info.dwFileVersionMS & 0x0000FFFF);
+ *patch= (info.dwFileVersionLS & 0xFFFF0000) >> 16;
+end:
+ free(ver);
+}
+
+void normalize_path(char *path, size_t size)
+{
+ char buf[MAX_PATH];
+ if (*path== '"')
+ {
+ char *p;
+ strcpy_s(buf, MAX_PATH, path+1);
+ p= strchr(buf, '"');
+ if (p)
+ *p=0;
+ }
+ else
+ strcpy_s(buf, MAX_PATH, path);
+ GetFullPathName(buf, MAX_PATH, buf, NULL);
+ strcpy_s(path, size, buf);
+}
+
+/*
+ Retrieve some properties from windows mysqld service binary path.
+ We're interested in ini file location and datadir, and also in version of
+ the data. We tolerate missing mysqld.exe.
+
+ Note that this function carefully avoids using mysql libraries (e.g dbug),
+ since it is used in unusual environments (windows installer, MFC), where we
+ do not have much control over how threads are created and destroyed, so we
+ cannot assume MySQL thread initilization here.
+*/
+int get_mysql_service_properties(const wchar_t *bin_path,
+ mysqld_service_properties *props)
+{
+ int numargs;
+ wchar_t mysqld_path[MAX_PATH + 4];
+ wchar_t *file_part;
+ wchar_t **args= NULL;
+ int retval= 1;
+ BOOL have_inifile;
+
+ props->datadir[0]= 0;
+ props->inifile[0]= 0;
+ props->mysqld_exe[0]= 0;
+ props->version_major= 0;
+ props->version_minor= 0;
+ props->version_patch= 0;
+
+ args= CommandLineToArgvW(bin_path, &numargs);
+ if(numargs == 2)
+ {
+ /*
+ There are rare cases where service config does not have
+ --defaults-filein the binary parth . There services were registered with
+ plain mysqld --install, the data directory is next to "bin" in this case.
+ Service name (second parameter) must be MySQL.
+ */
+ if(wcscmp(args[1], L"MySQL") != 0)
+ goto end;
+ have_inifile= FALSE;
+ }
+ else if(numargs == 3)
+ {
+ have_inifile= TRUE;
+ }
+ else
+ {
+ goto end;
+ }
+
+ if(have_inifile && wcsncmp(args[1], L"--defaults-file=", 16) != 0)
+ goto end;
+
+ GetFullPathNameW(args[0], MAX_PATH, mysqld_path, &file_part);
+
+ if(wcsstr(mysqld_path, L".exe") == NULL)
+ wcscat(mysqld_path, L".exe");
+
+ if(wcsicmp(file_part, L"mysqld.exe") != 0 &&
+ wcsicmp(file_part, L"mysqld.exe") != 0 &&
+ wcsicmp(file_part, L"mysqld-nt.exe") != 0)
+ {
+ /* The service executable is not mysqld. */
+ goto end;
+ }
+
+ wcstombs(props->mysqld_exe, mysqld_path, MAX_PATH);
+ /* If mysqld.exe exists, try to get its version from executable */
+ if (GetFileAttributes(props->mysqld_exe) != INVALID_FILE_ATTRIBUTES)
+ {
+ get_file_version(props->mysqld_exe, &props->version_major,
+ &props->version_minor, &props->version_patch);
+ }
+
+ if (have_inifile)
+ {
+ /* We have --defaults-file in service definition. */
+ wcstombs(props->inifile, args[1]+16, MAX_PATH);
+ normalize_path(props->inifile, MAX_PATH);
+ if (GetFileAttributes(props->inifile) != INVALID_FILE_ATTRIBUTES)
+ {
+ GetPrivateProfileString("mysqld", "datadir", NULL, props->datadir, MAX_PATH,
+ props->inifile);
+ }
+ else
+ {
+ /*
+ Service will start even with invalid .ini file, using lookup for
+ datadir relative to mysqld.exe. This is equivalent to the case no ini
+ file used.
+ */
+ props->inifile[0]= 0;
+ have_inifile= FALSE;
+ }
+ }
+
+ if(!have_inifile)
+ {
+ /*
+ Hard, although a rare case, we're guessing datadir and defaults-file.
+ On Windows, defaults-file is traditionally install-root\my.ini
+ and datadir is install-root\data
+ */
+ char install_root[MAX_PATH];
+ int i;
+ char *p;
+
+ /*
+ Get the install root(parent of bin directory where mysqld.exe)
+ is located.
+ */
+ strcpy_s(install_root, MAX_PATH, props->mysqld_exe);
+ for (i=0; i< 2; i++)
+ {
+ p= strrchr(install_root, '\\');
+ if(!p)
+ goto end;
+ *p= 0;
+ }
+
+ /* Look for my.ini, my.cnf in the install root */
+ sprintf_s(props->inifile, MAX_PATH, "%s\\my.ini", install_root);
+ if (GetFileAttributes(props->inifile) == INVALID_FILE_ATTRIBUTES)
+ {
+ sprintf_s(props->inifile, MAX_PATH, "%s\\my.cnf", install_root);
+ }
+ if (GetFileAttributes(props->inifile) != INVALID_FILE_ATTRIBUTES)
+ {
+ /* Ini file found, get datadir from there */
+ GetPrivateProfileString("mysqld", "datadir", NULL, props->datadir,
+ MAX_PATH, props->inifile);
+ }
+ else
+ {
+ /* No ini file */
+ props->inifile[0]= 0;
+ }
+
+ /* Try datadir in install directory.*/
+ if (props->datadir[0] == 0)
+ {
+ sprintf_s(props->datadir, MAX_PATH, "%s\\data", install_root);
+ }
+ }
+
+ if (props->datadir[0])
+ {
+ normalize_path(props->datadir, MAX_PATH);
+ /* Check if datadir really exists */
+ if (GetFileAttributes(props->datadir) == INVALID_FILE_ATTRIBUTES)
+ goto end;
+ }
+ else
+ {
+ /* There is no datadir in ini file, bail out.*/
+ goto end;
+ }
+
+ /*
+ If version could not be determined so far, try mysql_upgrade_info in
+ database directory.
+ */
+ if(props->version_major == 0)
+ {
+ char buf[MAX_PATH];
+ FILE *mysql_upgrade_info;
+
+ sprintf_s(buf, MAX_PATH, "%s\\mysql_upgrade_info", props->datadir);
+ mysql_upgrade_info= fopen(buf, "r");
+ if(mysql_upgrade_info)
+ {
+ if (fgets(buf, MAX_PATH, mysql_upgrade_info))
+ {
+ int major,minor,patch;
+ if (sscanf(buf, "%d.%d.%d", &major, &minor, &patch) == 3)
+ {
+ props->version_major= major;
+ props->version_minor= minor;
+ props->version_patch= patch;
+ }
+ }
+ }
+ }
+ retval = 0;
+end:
+ LocalFree((HLOCAL)args);
+ return retval;
+} \ No newline at end of file
diff --git a/sql/winservice.h b/sql/winservice.h
new file mode 100644
index 00000000000..8957413783f
--- /dev/null
+++ b/sql/winservice.h
@@ -0,0 +1,24 @@
+/*
+ Extract properties of a windows service binary path
+*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <windows.h>
+typedef struct mysqld_service_properties_st
+{
+ char mysqld_exe[MAX_PATH];
+ char inifile[MAX_PATH];
+ char datadir[MAX_PATH];
+ int version_major;
+ int version_minor;
+ int version_patch;
+} mysqld_service_properties;
+
+extern int get_mysql_service_properties(const wchar_t *bin_path,
+ mysqld_service_properties *props);
+
+#ifdef __cplusplus
+}
+#endif