summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt52
-rw-r--r--sql/MSG00001.binbin184 -> 36 bytes
-rw-r--r--sql/backup.cc403
-rw-r--r--sql/backup.h34
-rw-r--r--sql/compat56.cc21
-rw-r--r--sql/compat56.h13
-rw-r--r--sql/datadict.cc29
-rw-r--r--sql/datadict.h3
-rw-r--r--sql/derived_handler.cc125
-rw-r--r--sql/derived_handler.h85
-rw-r--r--sql/encryption.cc3
-rw-r--r--sql/event_data_objects.cc40
-rw-r--r--sql/event_data_objects.h6
-rw-r--r--sql/event_parse_data.cc18
-rw-r--r--sql/event_scheduler.cc24
-rw-r--r--sql/events.cc59
-rw-r--r--sql/field.cc2146
-rw-r--r--sql/field.h821
-rw-r--r--sql/field_conv.cc37
-rw-r--r--sql/filesort.cc155
-rw-r--r--sql/filesort.h3
-rw-r--r--sql/gcalc_tools.h6
-rw-r--r--sql/ha_partition.cc261
-rw-r--r--sql/ha_partition.h395
-rw-r--r--sql/ha_sequence.cc2
-rw-r--r--sql/ha_sequence.h2
-rw-r--r--sql/handle_connections_win.cc613
-rw-r--r--sql/handle_connections_win.h20
-rw-r--r--sql/handler.cc1172
-rw-r--r--sql/handler.h393
-rw-r--r--sql/init.cc4
-rw-r--r--sql/init.h1
-rw-r--r--sql/innodb_priv.h4
-rw-r--r--sql/item.cc1616
-rw-r--r--sql/item.h1852
-rw-r--r--sql/item_buff.cc29
-rw-r--r--sql/item_cmpfunc.cc895
-rw-r--r--sql/item_cmpfunc.h160
-rw-r--r--sql/item_create.cc211
-rw-r--r--sql/item_create.h15
-rw-r--r--sql/item_func.cc823
-rw-r--r--sql/item_func.h566
-rw-r--r--sql/item_geofunc.cc2
-rw-r--r--sql/item_geofunc.h2
-rw-r--r--sql/item_inetfunc.cc627
-rw-r--r--sql/item_inetfunc.h60
-rw-r--r--sql/item_jsonfunc.cc17
-rw-r--r--sql/item_row.cc4
-rw-r--r--sql/item_row.h30
-rw-r--r--sql/item_strfunc.cc207
-rw-r--r--sql/item_strfunc.h79
-rw-r--r--sql/item_subselect.cc151
-rw-r--r--sql/item_subselect.h30
-rw-r--r--sql/item_sum.cc322
-rw-r--r--sql/item_sum.h122
-rw-r--r--sql/item_timefunc.cc977
-rw-r--r--sql/item_timefunc.h708
-rw-r--r--sql/item_vers.cc9
-rw-r--r--sql/item_vers.h2
-rw-r--r--sql/item_windowfunc.cc21
-rw-r--r--sql/item_windowfunc.h39
-rw-r--r--sql/item_xmlfunc.cc129
-rw-r--r--sql/lex.h5
-rw-r--r--sql/lex_string.h4
-rw-r--r--sql/lock.cc200
-rw-r--r--sql/log.cc497
-rw-r--r--sql/log.h38
-rw-r--r--sql/log_event.cc268
-rw-r--r--sql/log_event.h53
-rw-r--r--sql/mdl.cc636
-rw-r--r--sql/mdl.h134
-rw-r--r--sql/mem_root_array.h3
-rw-r--r--sql/message.h29
-rw-r--r--sql/message.mc3
-rw-r--r--sql/message.rc2
-rw-r--r--sql/multi_range_read.cc131
-rw-r--r--sql/multi_range_read.h16
-rw-r--r--sql/my_decimal.cc52
-rw-r--r--sql/my_decimal.h148
-rw-r--r--sql/my_json_writer.cc107
-rw-r--r--sql/my_json_writer.h506
-rw-r--r--sql/mysql_install_db.cc43
-rw-r--r--sql/mysql_upgrade_service.cc4
-rw-r--r--sql/mysqld.cc2335
-rw-r--r--sql/mysqld.h75
-rw-r--r--sql/opt_index_cond_pushdown.cc15
-rw-r--r--sql/opt_range.cc1618
-rw-r--r--sql/opt_range.h15
-rw-r--r--sql/opt_range_mrr.cc22
-rw-r--r--sql/opt_split.cc2
-rw-r--r--sql/opt_subselect.cc1293
-rw-r--r--sql/opt_subselect.h8
-rw-r--r--sql/opt_sum.cc2
-rw-r--r--sql/opt_table_elimination.cc37
-rw-r--r--sql/opt_trace.cc745
-rw-r--r--sql/opt_trace.h210
-rw-r--r--sql/opt_trace_context.h135
-rw-r--r--sql/partition_element.h3
-rw-r--r--sql/partition_info.h2
-rw-r--r--sql/procedure.h18
-rw-r--r--sql/protocol.cc333
-rw-r--r--sql/protocol.h39
-rw-r--r--sql/repl_failsafe.cc158
-rw-r--r--sql/repl_failsafe.h7
-rw-r--r--sql/rowid_filter.cc663
-rw-r--r--sql/rowid_filter.h470
-rw-r--r--sql/rpl_gtid.cc443
-rw-r--r--sql/rpl_gtid.h19
-rw-r--r--sql/rpl_mi.cc10
-rw-r--r--sql/rpl_mi.h6
-rw-r--r--sql/rpl_parallel.cc56
-rw-r--r--sql/rpl_parallel.h2
-rw-r--r--sql/rpl_record.cc23
-rw-r--r--sql/rpl_rli.cc134
-rw-r--r--sql/rpl_rli.h28
-rw-r--r--sql/select_handler.cc173
-rw-r--r--sql/select_handler.h72
-rw-r--r--sql/semisync_master_ack_receiver.cc3
-rw-r--r--sql/service_wsrep.cc368
-rw-r--r--sql/session_tracker.cc7
-rw-r--r--sql/set_var.cc2
-rw-r--r--sql/set_var.h9
-rw-r--r--sql/share/errmsg-utf8.txt58
-rw-r--r--sql/signal_handler.cc3
-rw-r--r--sql/slave.cc289
-rw-r--r--sql/slave.h2
-rw-r--r--sql/sp.cc9
-rw-r--r--sql/sp.h3
-rw-r--r--sql/sp_head.cc160
-rw-r--r--sql/sp_head.h33
-rw-r--r--sql/sp_pcontext.h1
-rw-r--r--sql/sp_rcontext.cc27
-rw-r--r--sql/sql_acl.cc4199
-rw-r--r--sql/sql_acl_getsort.ic205
-rw-r--r--sql/sql_admin.cc91
-rw-r--r--sql/sql_alloc.h3
-rw-r--r--sql/sql_alter.cc40
-rw-r--r--sql/sql_analyse.cc103
-rw-r--r--sql/sql_analyze_stmt.h79
-rw-r--r--sql/sql_array.h13
-rw-r--r--sql/sql_base.cc820
-rw-r--r--sql/sql_base.h36
-rw-r--r--sql/sql_basic_types.h310
-rw-r--r--sql/sql_binlog.cc6
-rw-r--r--sql/sql_bitmap.h282
-rw-r--r--sql/sql_cache.cc15
-rw-r--r--sql/sql_class.cc685
-rw-r--r--sql/sql_class.h634
-rw-r--r--sql/sql_cmd.h1
-rw-r--r--sql/sql_connect.cc41
-rw-r--r--sql/sql_const.h6
-rw-r--r--sql/sql_cte.cc32
-rw-r--r--sql/sql_cte.h28
-rw-r--r--sql/sql_db.cc2
-rw-r--r--sql/sql_delete.cc167
-rw-r--r--sql/sql_derived.cc441
-rw-r--r--sql/sql_derived.h1
-rw-r--r--sql/sql_do.cc2
-rw-r--r--sql/sql_error.cc2
-rw-r--r--sql/sql_error.h59
-rw-r--r--sql/sql_explain.cc171
-rw-r--r--sql/sql_explain.h46
-rw-r--r--sql/sql_handler.cc14
-rw-r--r--sql/sql_handler.h1
-rw-r--r--sql/sql_help.cc11
-rw-r--r--sql/sql_insert.cc327
-rw-r--r--sql/sql_join_cache.cc2
-rw-r--r--sql/sql_lex.cc2602
-rw-r--r--sql/sql_lex.h729
-rw-r--r--sql/sql_list.h60
-rw-r--r--sql/sql_load.cc124
-rw-r--r--sql/sql_mode.cc2
-rw-r--r--sql/sql_mode.h2
-rw-r--r--sql/sql_parse.cc1256
-rw-r--r--sql/sql_parse.h3
-rw-r--r--sql/sql_partition.cc69
-rw-r--r--sql/sql_partition_admin.cc4
-rw-r--r--sql/sql_plugin.cc147
-rw-r--r--sql/sql_plugin.h5
-rw-r--r--sql/sql_plugin_services.ic64
-rw-r--r--sql/sql_prepare.cc209
-rw-r--r--sql/sql_priv.h27
-rw-r--r--sql/sql_profile.cc4
-rw-r--r--sql/sql_reload.cc83
-rw-r--r--sql/sql_repl.cc348
-rw-r--r--sql/sql_repl.h11
-rw-r--r--sql/sql_select.cc2546
-rw-r--r--sql/sql_select.h188
-rw-r--r--sql/sql_sequence.cc8
-rw-r--r--sql/sql_sequence.h4
-rw-r--r--sql/sql_show.cc700
-rw-r--r--sql/sql_signal.cc2
-rw-r--r--sql/sql_sort.h1
-rw-r--r--sql/sql_statistics.cc305
-rw-r--r--sql/sql_statistics.h27
-rw-r--r--sql/sql_string.cc214
-rw-r--r--sql/sql_string.h975
-rw-r--r--sql/sql_table.cc1366
-rw-r--r--sql/sql_table.h6
-rw-r--r--sql/sql_test.cc39
-rw-r--r--sql/sql_test.h2
-rw-r--r--sql/sql_time.cc378
-rw-r--r--sql/sql_time.h120
-rw-r--r--sql/sql_trigger.cc31
-rw-r--r--sql/sql_truncate.cc74
-rw-r--r--sql/sql_tvc.cc17
-rw-r--r--sql/sql_type.cc3166
-rw-r--r--sql/sql_type.h3441
-rw-r--r--sql/sql_type_int.h50
-rw-r--r--sql/sql_type_json.cc55
-rw-r--r--sql/sql_type_json.h38
-rw-r--r--sql/sql_udf.cc3
-rw-r--r--sql/sql_udf.h15
-rw-r--r--sql/sql_union.cc77
-rw-r--r--sql/sql_update.cc242
-rw-r--r--sql/sql_view.cc65
-rw-r--r--sql/sql_yacc.yy3275
-rw-r--r--sql/sql_yacc_ora.yy3191
-rw-r--r--sql/strfunc.cc16
-rw-r--r--sql/structs.h91
-rw-r--r--sql/sys_vars.cc302
-rw-r--r--sql/sys_vars.ic17
-rw-r--r--sql/table.cc1524
-rw-r--r--sql/table.h218
-rw-r--r--sql/table_cache.cc70
-rw-r--r--sql/table_cache.h5
-rw-r--r--sql/temporary_tables.cc53
-rw-r--r--sql/threadpool.h9
-rw-r--r--sql/threadpool_common.cc11
-rw-r--r--sql/threadpool_generic.cc64
-rw-r--r--sql/threadpool_win.cc98
-rw-r--r--sql/transaction.cc402
-rw-r--r--sql/transaction.h8
-rw-r--r--sql/tztime.cc12
-rw-r--r--sql/udf_example.c139
-rw-r--r--sql/udf_example.def7
-rw-r--r--sql/unireg.cc279
-rw-r--r--sql/unireg.h14
-rw-r--r--sql/vers_string.h6
-rw-r--r--sql/vers_utils.h39
-rw-r--r--sql/wsrep_applier.cc320
-rw-r--r--sql/wsrep_applier.h67
-rw-r--r--sql/wsrep_binlog.cc419
-rw-r--r--sql/wsrep_binlog.h61
-rw-r--r--sql/wsrep_check_opts.cc6
-rw-r--r--sql/wsrep_client_service.cc345
-rw-r--r--sql/wsrep_client_service.h74
-rw-r--r--sql/wsrep_client_state.h47
-rw-r--r--sql/wsrep_condition_variable.h54
-rw-r--r--sql/wsrep_dummy.cc134
-rw-r--r--sql/wsrep_high_priority_service.cc714
-rw-r--r--sql/wsrep_high_priority_service.h135
-rw-r--r--sql/wsrep_hton.cc675
-rw-r--r--sql/wsrep_mutex.h50
-rw-r--r--sql/wsrep_mysqld.cc2948
-rw-r--r--sql/wsrep_mysqld.h418
-rw-r--r--sql/wsrep_notify.cc71
-rw-r--r--sql/wsrep_plugin.cc53
-rw-r--r--sql/wsrep_priv.h24
-rw-r--r--sql/wsrep_schema.cc1428
-rw-r--r--sql/wsrep_schema.h144
-rw-r--r--sql/wsrep_server_service.cc393
-rw-r--r--sql/wsrep_server_service.h101
-rw-r--r--sql/wsrep_server_state.cc85
-rw-r--r--sql/wsrep_server_state.h74
-rw-r--r--sql/wsrep_sst.cc845
-rw-r--r--sql/wsrep_sst.h30
-rw-r--r--sql/wsrep_storage_service.cc206
-rw-r--r--sql/wsrep_storage_service.h49
-rw-r--r--sql/wsrep_thd.cc1015
-rw-r--r--sql/wsrep_thd.h292
-rw-r--r--sql/wsrep_trans_observer.h556
-rw-r--r--sql/wsrep_types.h29
-rw-r--r--sql/wsrep_utils.cc108
-rw-r--r--sql/wsrep_utils.h76
-rw-r--r--sql/wsrep_var.cc651
-rw-r--r--sql/wsrep_var.h14
-rw-r--r--sql/wsrep_xid.cc90
-rw-r--r--sql/wsrep_xid.h12
-rw-r--r--sql/xa.cc877
-rw-r--r--sql/xa.h44
281 files changed, 55140 insertions, 26902 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 5f5d7daf1a5..e620c99cf8a 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -16,21 +16,27 @@
IF(WITH_WSREP AND NOT EMBEDDED_LIBRARY)
- SET(WSREP_INCLUDES ${CMAKE_SOURCE_DIR}/wsrep)
SET(WSREP_SOURCES
+ wsrep_client_service.cc
+ wsrep_high_priority_service.cc
+ wsrep_server_service.cc
+ wsrep_storage_service.cc
+ wsrep_server_state.cc
+ wsrep_utils.cc
+ wsrep_xid.cc
wsrep_check_opts.cc
- wsrep_hton.cc
- wsrep_mysqld.cc
+ wsrep_mysqld.cc
wsrep_notify.cc
wsrep_sst.cc
- wsrep_utils.cc
wsrep_var.cc
wsrep_binlog.cc
wsrep_applier.cc
wsrep_thd.cc
- wsrep_xid.cc
+ wsrep_schema.cc
+ wsrep_plugin.cc
+ service_wsrep.cc
)
- SET(WSREP_LIB wsrep)
+ SET(WSREP_LIB wsrep-lib wsrep_api_v26)
ELSE()
SET(WSREP_SOURCES wsrep_dummy.cc)
ENDIF()
@@ -42,7 +48,6 @@ ${PCRE_INCLUDES}
${ZLIB_INCLUDE_DIR}
${SSL_INCLUDE_DIRS}
${CMAKE_BINARY_DIR}/sql
-${WSREP_INCLUDES}
)
@@ -96,7 +101,7 @@ SET (SQL_SOURCE
sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc
debug_sync.cc
sql_repl.cc sql_select.cc sql_show.cc sql_state.c
- group_by_handler.cc
+ group_by_handler.cc derived_handler.cc select_handler.cc
sql_statistics.cc sql_string.cc lex_string.h
sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc
sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc
@@ -121,23 +126,25 @@ SET (SQL_SOURCE
opt_index_cond_pushdown.cc opt_subselect.cc
opt_table_elimination.cc sql_expression_cache.cc
gcalc_slicescan.cc gcalc_tools.cc
- threadpool_common.cc ../sql-common/mysql_async.c
+ ../sql-common/mysql_async.c
my_apc.cc mf_iocache_encr.cc item_jsonfunc.cc
my_json_writer.cc
rpl_gtid.cc rpl_parallel.cc
semisync.cc semisync_master.cc semisync_slave.cc
semisync_master_ack_receiver.cc
sql_schema.cc
- sql_type.cc sql_mode.cc
+ sql_type.cc sql_mode.cc sql_type_json.cc
item_windowfunc.cc sql_window.cc
sql_cte.cc
item_vers.cc
sql_sequence.cc sql_sequence.h ha_sequence.h
sql_tvc.cc sql_tvc.h
opt_split.cc
+ rowid_filter.cc rowid_filter.h
+ opt_trace.cc
${WSREP_SOURCES}
table_cache.cc encryption.cc temporary_tables.cc
- proxy_protocol.cc
+ proxy_protocol.cc backup.cc xa.cc
${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc
${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc
${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.cc
@@ -146,16 +153,21 @@ SET (SQL_SOURCE
${MYSYS_LIBWRAP_SOURCE}
)
-IF (CMAKE_SYSTEM_NAME MATCHES "Linux" OR
- CMAKE_SYSTEM_NAME MATCHES "Windows" OR
- CMAKE_SYSTEM_NAME MATCHES "SunOS" OR
- HAVE_KQUEUE)
+IF ((CMAKE_SYSTEM_NAME MATCHES "Linux" OR
+ CMAKE_SYSTEM_NAME MATCHES "SunOS" OR
+ WIN32 OR
+ HAVE_KQUEUE)
+ AND (NOT DISABLE_THREADPOOL))
ADD_DEFINITIONS(-DHAVE_POOL_OF_THREADS)
IF(WIN32)
SET(SQL_SOURCE ${SQL_SOURCE} threadpool_win.cc)
ENDIF()
SET(SQL_SOURCE ${SQL_SOURCE} threadpool_generic.cc)
+ SET(SQL_SOURCE ${SQL_SOURCE} threadpool_common.cc)
+ENDIF()
+IF(WIN32)
+ SET(SQL_SOURCE ${SQL_SOURCE} handle_connections_win.cc)
ENDIF()
MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY
@@ -395,10 +407,6 @@ ADD_CUSTOM_TARGET(distclean
IF(INSTALL_LAYOUT STREQUAL "STANDALONE")
-# Copy db.opt into data/test/
-SET(DBOPT_FILE ${CMAKE_SOURCE_DIR}/support-files/db.opt )
-INSTALL(FILES ${DBOPT_FILE} DESTINATION data/test COMPONENT DataFiles)
-
# Install initial database on windows
IF(WIN32 AND TARGET mysqld AND NOT CMAKE_CROSSCOMPILING)
@@ -425,12 +433,6 @@ IF(WIN32 AND TARGET mysqld AND NOT CMAKE_CROSSCOMPILING)
ALL
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep
)
- INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data DESTINATION .
- COMPONENT DataFiles
- PATTERN "initdb.dep" EXCLUDE
- PATTERN "bootstrap.sql" EXCLUDE
- PATTERN "aria*" EXCLUDE
- )
ELSE()
# Not windows or cross compiling, just install an empty directory
INSTALL(FILES ${DUMMY_FILE} DESTINATION data/mysql COMPONENT DataFiles)
diff --git a/sql/MSG00001.bin b/sql/MSG00001.bin
index 89f547694f5..5c1cd0badeb 100644
--- a/sql/MSG00001.bin
+++ b/sql/MSG00001.bin
Binary files differ
diff --git a/sql/backup.cc b/sql/backup.cc
new file mode 100644
index 00000000000..cff14415d96
--- /dev/null
+++ b/sql/backup.cc
@@ -0,0 +1,403 @@
+/* Copyright (c) 2018, 2020, MariaDB Corporation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 */
+
+/*
+ Implementation of BACKUP STAGE, an interface for external backup tools.
+
+ TODO:
+ - At backup_start() we call ha_prepare_for_backup() for all active
+ storage engines. If someone tries to load a new storage engine
+ that requires prepare_for_backup() for it to work, that storage
+ engines has to be blocked from loading until backup finishes.
+ As we currently don't have any loadable storage engine that
+ requires this and we have not implemented that part.
+ This can easily be done by adding a
+ PLUGIN_CANT_BE_LOADED_WHILE_BACKUP_IS_RUNNING flag to
+ maria_declare_plugin and check this before calling
+ plugin_initialize()
+*/
+
+#include "mariadb.h"
+#include "sql_class.h"
+#include "sql_base.h" // flush_tables
+#include "sql_insert.h" // kill_delayed_threads
+#include "sql_handler.h" // mysql_ha_cleanup_no_free
+#include <my_sys.h>
+
+static const char *stage_names[]=
+{"START", "FLUSH", "BLOCK_DDL", "BLOCK_COMMIT", "END", 0};
+
+TYPELIB backup_stage_names=
+{ array_elements(stage_names)-1, "", stage_names, 0 };
+
+static MDL_ticket *backup_flush_ticket;
+
+static bool backup_start(THD *thd);
+static bool backup_flush(THD *thd);
+static bool backup_block_ddl(THD *thd);
+static bool backup_block_commit(THD *thd);
+
+/**
+ Run next stage of backup
+*/
+
+void backup_init()
+{
+ backup_flush_ticket= 0;
+}
+
+bool run_backup_stage(THD *thd, backup_stages stage)
+{
+ backup_stages next_stage;
+ DBUG_ENTER("run_backup_stage");
+
+ if (thd->current_backup_stage == BACKUP_FINISHED)
+ {
+ if (stage != BACKUP_START)
+ {
+ my_error(ER_BACKUP_NOT_RUNNING, MYF(0));
+ DBUG_RETURN(1);
+ }
+ next_stage= BACKUP_START;
+ }
+ else
+ {
+ if ((uint) thd->current_backup_stage >= (uint) stage)
+ {
+ my_error(ER_BACKUP_WRONG_STAGE, MYF(0), stage_names[stage],
+ stage_names[thd->current_backup_stage]);
+ DBUG_RETURN(1);
+ }
+ if (stage == BACKUP_END)
+ {
+ /*
+ If end is given, jump directly to stage end. This is to allow one
+ to abort backup quickly.
+ */
+ next_stage= stage;
+ }
+ else
+ {
+ /* Go trough all not used stages until we reach 'stage' */
+ next_stage= (backup_stages) ((uint) thd->current_backup_stage + 1);
+ }
+ }
+
+ do
+ {
+ bool res= false;
+ backup_stages previous_stage= thd->current_backup_stage;
+ thd->current_backup_stage= next_stage;
+ switch (next_stage) {
+ case BACKUP_START:
+ if (!(res= backup_start(thd)))
+ break;
+ /* Reset backup stage to start for next backup try */
+ previous_stage= BACKUP_FINISHED;
+ break;
+ case BACKUP_FLUSH:
+ res= backup_flush(thd);
+ break;
+ case BACKUP_WAIT_FOR_FLUSH:
+ res= backup_block_ddl(thd);
+ break;
+ case BACKUP_LOCK_COMMIT:
+ res= backup_block_commit(thd);
+ break;
+ case BACKUP_END:
+ res= backup_end(thd);
+ break;
+ case BACKUP_FINISHED:
+ DBUG_ASSERT(0);
+ }
+ if (res)
+ {
+ thd->current_backup_stage= previous_stage;
+ my_error(ER_BACKUP_STAGE_FAILED, MYF(0), stage_names[(uint) stage]);
+ DBUG_RETURN(1);
+ }
+ next_stage= (backup_stages) ((uint) next_stage + 1);
+ } while ((uint) next_stage <= (uint) stage);
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Start the backup
+
+ - Wait for previous backup to stop running
+ - Start service to log changed tables (TODO)
+ - Block purge of redo files (Required at least for Aria)
+ - An handler can optionally do a checkpoint of all tables,
+ to speed up the recovery stage of the backup.
+*/
+
+static bool backup_start(THD *thd)
+{
+ MDL_request mdl_request;
+ DBUG_ENTER("backup_start");
+
+ thd->current_backup_stage= BACKUP_FINISHED; // For next test
+ if (thd->has_read_only_protection())
+ DBUG_RETURN(1);
+ thd->current_backup_stage= BACKUP_START;
+
+ if (thd->locked_tables_mode)
+ {
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_START, MDL_EXPLICIT);
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(1);
+
+ backup_flush_ticket= mdl_request.ticket;
+
+ ha_prepare_for_backup();
+ DBUG_RETURN(0);
+}
+
+/**
+ backup_flush()
+
+ - FLUSH all changes for not active non transactional tables, except
+ for statistics and log tables. Close the tables, to ensure they
+ are marked as closed after backup.
+
+ - BLOCK all NEW write locks for all non transactional tables
+ (except statistics and log tables). Already granted locks are
+ not affected (Running statements with non transaction tables will
+ continue running).
+
+ - The following DDL's doesn't have to be blocked as they can't set
+ the table in a non consistent state:
+ CREATE, RENAME, DROP
+*/
+
+static bool backup_flush(THD *thd)
+{
+ DBUG_ENTER("backup_flush");
+ /*
+ Lock all non transactional normal tables to be used in new DML's
+ */
+ if (thd->mdl_context.upgrade_shared_lock(backup_flush_ticket,
+ MDL_BACKUP_FLUSH,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(1);
+
+ /*
+ Free unused tables and table shares so that mariabackup knows what
+ is safe to copy
+ */
+ tc_purge(false);
+ tdc_purge(true);
+
+ DBUG_RETURN(0);
+}
+
+/**
+ backup_block_ddl()
+
+ - Kill all insert delay handlers, to ensure that all non transactional
+ tables are closed (can be improved in the future).
+
+ - Close handlers as other threads may wait for these, which can cause deadlocks.
+
+ - Wait for all statements using write locked non-transactional tables to end.
+
+ - Mark all not used active non transactional tables (except
+ statistics and log tables) to be closed with
+ handler->extra(HA_EXTRA_FLUSH)
+
+ - Block TRUNCATE TABLE, CREATE TABLE, DROP TABLE and RENAME
+ TABLE. Block also start of a new ALTER TABLE and the final rename
+ phase of ALTER TABLE. Running ALTER TABLES are not blocked. Both normal
+ and inline ALTER TABLE'S should be blocked when copying is completed but
+ before final renaming of the tables / new table is activated.
+ This will probably require a callback from the InnoDB code.
+*/
+
+static bool backup_block_ddl(THD *thd)
+{
+ DBUG_ENTER("backup_block_ddl");
+
+ kill_delayed_threads();
+ mysql_ha_cleanup_no_free(thd);
+
+ /* Wait until all non trans statements has ended */
+ if (thd->mdl_context.upgrade_shared_lock(backup_flush_ticket,
+ MDL_BACKUP_WAIT_FLUSH,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(1);
+
+ /*
+ Remove not used tables from the table share. Flush all changes to
+ non transaction tables and mark those that are not in use in write
+ operations as closed. From backup purposes it's not critical if
+ flush_tables() returns an error. It's ok to continue with next
+ backup stage even if we got an error.
+ */
+ (void) flush_tables(thd, FLUSH_NON_TRANS_TABLES);
+ thd->clear_error();
+
+ /*
+ block new DDL's, in addition to all previous blocks
+ We didn't do this lock above, as we wanted DDL's to be executed while
+ we wait for non transactional tables (which may take a while).
+ */
+ if (thd->mdl_context.upgrade_shared_lock(backup_flush_ticket,
+ MDL_BACKUP_WAIT_DDL,
+ thd->variables.lock_wait_timeout))
+ {
+ /*
+ Could be a timeout. Downgrade lock to what is was before this function
+ was called so that this function can be called again
+ */
+ backup_flush_ticket->downgrade_lock(MDL_BACKUP_FLUSH);
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+/**
+ backup_block_commit()
+
+ Block commits, writes to log and statistics tables and binary log
+*/
+
+static bool backup_block_commit(THD *thd)
+{
+ DBUG_ENTER("backup_block_commit");
+ if (thd->mdl_context.upgrade_shared_lock(backup_flush_ticket,
+ MDL_BACKUP_WAIT_COMMIT,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(1);
+
+ /* We can ignore errors from flush_tables () */
+ (void) flush_tables(thd, FLUSH_SYS_TABLES);
+
+ if (mysql_bin_log.is_open())
+ {
+ mysql_mutex_lock(mysql_bin_log.get_log_lock());
+ mysql_file_sync(mysql_bin_log.get_log_file()->file,
+ MYF(MY_WME|MY_SYNC_FILESIZE));
+ mysql_mutex_unlock(mysql_bin_log.get_log_lock());
+ }
+ thd->clear_error();
+
+ DBUG_RETURN(0);
+}
+
+/**
+ backup_end()
+
+ Safe to run, even if backup has not been run by this thread.
+ This is for example the case when a THD ends.
+*/
+
+bool backup_end(THD *thd)
+{
+ DBUG_ENTER("backup_end");
+
+ if (thd->current_backup_stage != BACKUP_FINISHED)
+ {
+ ha_end_backup();
+ thd->current_backup_stage= BACKUP_FINISHED;
+ thd->mdl_context.release_lock(backup_flush_ticket);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/**
+ backup_set_alter_copy_lock()
+
+ @param thd
+ @param table From table that is part of ALTER TABLE. This is only used
+ for the assert to ensure we use this function correctly.
+
+ Downgrades the MDL_BACKUP_DDL lock to MDL_BACKUP_ALTER_COPY to allow
+ copy of altered table to proceed under MDL_BACKUP_WAIT_DDL
+
+ Note that in some case when using non transactional tables,
+ the lock may be of type MDL_BACKUP_DML.
+*/
+
+void backup_set_alter_copy_lock(THD *thd, TABLE *table)
+{
+ MDL_ticket *ticket= thd->mdl_backup_ticket;
+
+ /* Ticket maybe NULL in case of LOCK TABLES or for temporary tables*/
+ DBUG_ASSERT(ticket || thd->locked_tables_mode ||
+ table->s->tmp_table != NO_TMP_TABLE);
+ if (ticket)
+ ticket->downgrade_lock(MDL_BACKUP_ALTER_COPY);
+}
+
+/**
+ backup_reset_alter_copy_lock
+
+ Upgrade the lock of the original ALTER table MDL_BACKUP_DDL
+ Can fail if MDL lock was killed
+*/
+
+bool backup_reset_alter_copy_lock(THD *thd)
+{
+ bool res= 0;
+ MDL_ticket *ticket= thd->mdl_backup_ticket;
+
+ /* Ticket maybe NULL in case of LOCK TABLES or for temporary tables*/
+ if (ticket)
+ res= thd->mdl_context.upgrade_shared_lock(ticket, MDL_BACKUP_DDL,
+ thd->variables.lock_wait_timeout);
+ return res;
+}
+
+
+/*****************************************************************************
+ Backup locks
+ These functions are used by maria_backup to ensure that there are no active
+ ddl's on the object the backup is going to copy
+*****************************************************************************/
+
+
+bool backup_lock(THD *thd, TABLE_LIST *table)
+{
+ /* We should leave the previous table unlocked in case of errors */
+ backup_unlock(thd);
+ if (thd->locked_tables_mode)
+ {
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ return 1;
+ }
+ table->mdl_request.duration= MDL_EXPLICIT;
+ if (thd->mdl_context.acquire_lock(&table->mdl_request,
+ thd->variables.lock_wait_timeout))
+ return 1;
+ thd->mdl_backup_lock= table->mdl_request.ticket;
+ return 0;
+}
+
+
+/* Release old backup lock if it exists */
+
+void backup_unlock(THD *thd)
+{
+ if (thd->mdl_backup_lock)
+ thd->mdl_context.release_lock(thd->mdl_backup_lock);
+ thd->mdl_backup_lock= 0;
+}
diff --git a/sql/backup.h b/sql/backup.h
new file mode 100644
index 00000000000..8d8a28b6082
--- /dev/null
+++ b/sql/backup.h
@@ -0,0 +1,34 @@
+#ifndef BACKUP_INCLUDED
+#define BACKUP_INCLUDED
+/* Copyright (c) 2018, MariaDB Corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+enum backup_stages
+{
+ BACKUP_START, BACKUP_FLUSH, BACKUP_WAIT_FOR_FLUSH, BACKUP_LOCK_COMMIT,
+ BACKUP_END, BACKUP_FINISHED
+};
+
+extern TYPELIB backup_stage_names;
+
+void backup_init();
+bool run_backup_stage(THD *thd, backup_stages stage);
+bool backup_end(THD *thd);
+void backup_set_alter_copy_lock(THD *thd, TABLE *altered_table);
+bool backup_reset_alter_copy_lock(THD *thd);
+
+bool backup_lock(THD *thd, TABLE_LIST *table);
+void backup_unlock(THD *thd);
+#endif /* BACKUP_INCLUDED */
diff --git a/sql/compat56.cc b/sql/compat56.cc
index 58cf5287cee..3d8574419d3 100644
--- a/sql/compat56.cc
+++ b/sql/compat56.cc
@@ -20,6 +20,19 @@
#include "myisampack.h"
#include "my_time.h"
+
+static const int my_max_usec_value[7]
+{
+ 0,
+ 900000,
+ 990000,
+ 999000,
+ 999900,
+ 999990,
+ 999999
+};
+
+
/*** MySQL56 TIME low-level memory and disk representation routines ***/
/*
@@ -397,19 +410,21 @@ void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec)
case 0:
default:
tm->tv_usec= 0;
- break;
+ return;
case 1:
case 2:
tm->tv_usec= ((int) ptr[4]) * 10000;
break;
case 3:
case 4:
- tm->tv_usec= mi_sint2korr(ptr + 4) * 100;
+ tm->tv_usec= (uint) mi_uint2korr(ptr + 4) * 100;
break;
case 5:
case 6:
- tm->tv_usec= mi_sint3korr(ptr + 4);
+ tm->tv_usec= (uint) mi_uint3korr(ptr + 4);
}
+ // The binary data my be corrupt. Cut fractional seconds to the valid range.
+ set_if_smaller(tm->tv_usec, my_max_usec_value[dec]);
}
diff --git a/sql/compat56.h b/sql/compat56.h
index 7f72c26c03a..65cd36dacfd 100644
--- a/sql/compat56.h
+++ b/sql/compat56.h
@@ -19,10 +19,19 @@
/** MySQL56 routines and macros **/
+
+/*
+ Buffer size for a native TIMESTAMP representation, for use with NativBuffer.
+ 4 bytes for seconds
+ 3 bytes for microseconds
+ 1 byte for the trailing '\0' (class Native reserves extra 1 byte for '\0')
+*/
+#define STRING_BUFFER_TIMESTAMP_BINARY_SIZE 8 /* 4 + 3 + 1 */
+
#define MY_PACKED_TIME_GET_INT_PART(x) ((x) >> 24)
#define MY_PACKED_TIME_GET_FRAC_PART(x) ((x) % (1LL << 24))
-#define MY_PACKED_TIME_MAKE(i, f) ((((longlong) (i)) << 24) + (f))
-#define MY_PACKED_TIME_MAKE_INT(i) ((((longlong) (i)) << 24))
+#define MY_PACKED_TIME_MAKE(i, f) ((((ulonglong) (i)) << 24) + (f))
+#define MY_PACKED_TIME_MAKE_INT(i) ((((ulonglong) (i)) << 24))
longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *);
longlong TIME_to_longlong_time_packed(const MYSQL_TIME *);
diff --git a/sql/datadict.cc b/sql/datadict.cc
index 882f0b7232e..da8376d8b1a 100644
--- a/sql/datadict.cc
+++ b/sql/datadict.cc
@@ -181,39 +181,24 @@ err:
@param thd Thread context.
@param db Name of the database to which the table belongs to.
@param name Table name.
- @param path For temporary tables only - path to table files.
- Otherwise NULL (the path is calculated from db and table names).
@retval FALSE Success.
@retval TRUE Error.
*/
-bool dd_recreate_table(THD *thd, const char *db, const char *table_name,
- const char *path)
+bool dd_recreate_table(THD *thd, const char *db, const char *table_name)
{
- bool error= TRUE;
HA_CREATE_INFO create_info;
char path_buf[FN_REFLEN + 1];
DBUG_ENTER("dd_recreate_table");
+ /* There should be a exclusive metadata lock on the table. */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
+ MDL_EXCLUSIVE));
create_info.init();
-
- if (path)
- create_info.options|= HA_LEX_CREATE_TMP_TABLE;
- else
- {
- build_table_filename(path_buf, sizeof(path_buf) - 1,
- db, table_name, "", 0);
- path= path_buf;
-
- /* There should be a exclusive metadata lock on the table. */
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
- MDL_EXCLUSIVE));
- }
-
+ build_table_filename(path_buf, sizeof(path_buf) - 1,
+ db, table_name, "", 0);
/* Attempt to reconstruct the table. */
- error= ha_create_table(thd, path, db, table_name, &create_info, NULL);
-
- DBUG_RETURN(error);
+ DBUG_RETURN(ha_create_table(thd, path_buf, db, table_name, &create_info, 0));
}
diff --git a/sql/datadict.h b/sql/datadict.h
index 648b176d2ac..cbdf788deb6 100644
--- a/sql/datadict.h
+++ b/sql/datadict.h
@@ -47,7 +47,6 @@ static inline bool dd_frm_is_view(THD *thd, char *path)
return dd_frm_type(thd, path, NULL, &not_used2) == TABLE_TYPE_VIEW;
}
-bool dd_recreate_table(THD *thd, const char *db, const char *table_name,
- const char *path = NULL);
+bool dd_recreate_table(THD *thd, const char *db, const char *table_name);
#endif // DATADICT_INCLUDED
diff --git a/sql/derived_handler.cc b/sql/derived_handler.cc
new file mode 100644
index 00000000000..f48b95cbf76
--- /dev/null
+++ b/sql/derived_handler.cc
@@ -0,0 +1,125 @@
+/*
+ Copyright (c) 2018, 2019 MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
+#include "mariadb.h"
+#include "sql_priv.h"
+#include "sql_select.h"
+#include "derived_handler.h"
+
+
+/**
+ The methods of the Pushdown_derived class.
+
+ The objects of this class are used for pushdown of the derived tables
+ into engines. The main method of the class is Pushdown_derived::execute()
+ that initiates execution of the query specifying a derived by a foreign
+ engine, receives the rows of the result set and put them in a temporary
+ table on the server side.
+
+ The method uses only the functions of the derived_handle interface to do
+ this. The constructor of the class gets this interface as a parameter.
+
+ Currently a derived tables pushed into an engine is always materialized.
+ It could be changed if the cases when the tables is used as driving table.
+*/
+
+
+Pushdown_derived::Pushdown_derived(TABLE_LIST *tbl, derived_handler *h)
+ : derived(tbl), handler(h)
+{
+ is_analyze= handler->thd->lex->analyze_stmt;
+}
+
+
+Pushdown_derived::~Pushdown_derived()
+{
+ delete handler;
+}
+
+
+int Pushdown_derived::execute()
+{
+ int err;
+ THD *thd= handler->thd;
+ TABLE *table= handler->table;
+ TMP_TABLE_PARAM *tmp_table_param= handler->tmp_table_param;
+
+ DBUG_ENTER("Pushdown_query::execute");
+
+ if ((err= handler->init_scan()))
+ goto error;
+
+ if (is_analyze)
+ {
+ handler->end_scan();
+ DBUG_RETURN(0);
+ }
+
+ while (!(err= handler->next_row()))
+ {
+ if (unlikely(thd->check_killed()))
+ {
+ handler->end_scan();
+ DBUG_RETURN(-1);
+ }
+
+ if ((err= table->file->ha_write_tmp_row(table->record[0])))
+ {
+ bool is_duplicate;
+ if (likely(!table->file->is_fatal_error(err, HA_CHECK_DUP)))
+ continue; // Distinct elimination
+
+ if (create_internal_tmp_table_from_heap(thd, table,
+ tmp_table_param->start_recinfo,
+ &tmp_table_param->recinfo,
+ err, 1, &is_duplicate))
+ DBUG_RETURN(1);
+ if (is_duplicate)
+ continue;
+ }
+ }
+
+ if (err != 0 && err != HA_ERR_END_OF_FILE)
+ goto error;
+
+ if ((err= handler->end_scan()))
+ goto error_2;
+
+ DBUG_RETURN(0);
+
+error:
+ handler->end_scan();
+error_2:
+ handler->print_error(err, MYF(0));
+ DBUG_RETURN(-1); // Error not sent to client
+}
+
+
+void derived_handler::print_error(int error, myf errflag)
+{
+ my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str);
+}
+
+
+void derived_handler::set_derived(TABLE_LIST *tbl)
+{
+ derived= tbl;
+ table= tbl->table;
+ unit= tbl->derived;
+ select= unit->first_select();
+ tmp_table_param= ((select_unit *)(unit->result))->get_tmp_table_param();
+}
+
diff --git a/sql/derived_handler.h b/sql/derived_handler.h
new file mode 100644
index 00000000000..171165bbe6f
--- /dev/null
+++ b/sql/derived_handler.h
@@ -0,0 +1,85 @@
+/*
+ Copyright (c) 2016, 2017 MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef DERIVED_HANDLER_INCLUDED
+#define DERIVED_HANDLER_INCLUDED
+
+#include "mariadb.h"
+#include "sql_priv.h"
+
+class TMP_TABLE_PARAM;
+
+typedef class st_select_lex_unit SELECT_LEX_UNIT;
+
+/**
+ @class derived_handler
+
+ This interface class is to be used for execution of queries that specify
+ derived table by foreign engines
+*/
+
+class derived_handler
+{
+public:
+ THD *thd;
+ handlerton *ht;
+
+ TABLE_LIST *derived;
+
+ /*
+ Temporary table where all results should be stored in record[0]
+ The table has a field for every item from the select list of
+ the specification of derived.
+ */
+ TABLE *table;
+
+ /* The parameters if the temporary table used at its creation */
+ TMP_TABLE_PARAM *tmp_table_param;
+
+ SELECT_LEX_UNIT *unit; // Specifies the derived table
+
+ SELECT_LEX *select; // The first select of the specification
+
+ derived_handler(THD *thd_arg, handlerton *ht_arg)
+ : thd(thd_arg), ht(ht_arg), derived(0),table(0), tmp_table_param(0),
+ unit(0), select(0) {}
+ virtual ~derived_handler() {}
+
+ /*
+ Functions to scan data. All these returns 0 if ok, error code in case
+ of error
+ */
+
+ /* Initialize the process of producing rows of the derived table */
+ virtual int init_scan()= 0;
+
+ /*
+ Put the next produced row of the derived in table->record[0] and return 0.
+ Return HA_ERR_END_OF_FILE if there are no more rows, return other error
+ number in case of fatal error.
+ */
+ virtual int next_row()= 0;
+
+ /* End prodicing rows */
+ virtual int end_scan()=0;
+
+ /* Report errors */
+ virtual void print_error(int error, myf errflag);
+
+ void set_derived(TABLE_LIST *tbl);
+};
+
+#endif /* DERIVED_HANDLER_INCLUDED */
diff --git a/sql/encryption.cc b/sql/encryption.cc
index 493d72ae346..13239b91910 100644
--- a/sql/encryption.cc
+++ b/sql/encryption.cc
@@ -18,6 +18,7 @@
#include "log.h"
#include "sql_plugin.h"
#include <my_crypt.h>
+#include <violite.h>
/* there can be only one encryption plugin enabled */
static plugin_ref encryption_manager= 0;
@@ -63,6 +64,8 @@ int initialize_encryption_plugin(st_plugin_int *plugin)
if (encryption_manager)
return 1;
+ vio_check_ssl_init();
+
if (plugin->plugin->init && plugin->plugin->init(plugin))
{
sql_print_error("Plugin '%s' init function returned error.",
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
index b5788eecc8d..bc6d4587064 100644
--- a/sql/event_data_objects.cc
+++ b/sql/event_data_objects.cc
@@ -32,7 +32,9 @@
#include "event_db_repository.h"
#include "sp_head.h"
#include "sql_show.h" // append_definer, append_identifier
-
+#ifdef WITH_WSREP
+#include "wsrep_trans_observer.h"
+#endif /* WITH_WSREP */
/**
@addtogroup Event_Scheduler
@{
@@ -480,14 +482,24 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
uint not_used;
if (!starts_null)
{
- table->field[ET_FIELD_STARTS]->get_date(&time, TIME_NO_ZERO_DATE);
+ /*
+ The expected data type for these columns in mysql.events:
+ starts, ends, execute_at, last_executed
+ is DATETIME. No nanosecond truncation should normally be needed,
+ unless the DBA changes them, e.g. to VARCHAR, DECIMAL, etc.
+ For this unexpected case let's use the default round mode,
+ according to the current session settings.
+ */
+ table->field[ET_FIELD_STARTS]->get_date(&time, TIME_NO_ZERO_DATE |
+ thd->temporal_round_mode());
starts= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
}
ends_null= table->field[ET_FIELD_ENDS]->is_null();
if (!ends_null)
{
- table->field[ET_FIELD_ENDS]->get_date(&time, TIME_NO_ZERO_DATE);
+ table->field[ET_FIELD_ENDS]->get_date(&time, TIME_NO_ZERO_DATE |
+ thd->temporal_round_mode());
ends= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
}
@@ -503,8 +515,8 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
DBUG_ASSERT(!(starts_null && ends_null && !expression && execute_at_null));
if (!expression && !execute_at_null)
{
- if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time,
- TIME_NO_ZERO_DATE))
+ if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time, TIME_NO_ZERO_DATE |
+ thd->temporal_round_mode()))
DBUG_RETURN(TRUE);
execute_at= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
}
@@ -536,8 +548,8 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
if (!table->field[ET_FIELD_LAST_EXECUTED]->is_null())
{
- table->field[ET_FIELD_LAST_EXECUTED]->get_date(&time,
- TIME_NO_ZERO_DATE);
+ table->field[ET_FIELD_LAST_EXECUTED]->get_date(&time, TIME_NO_ZERO_DATE |
+ thd->temporal_round_mode());
last_executed= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
}
@@ -655,7 +667,7 @@ my_time_t
add_interval(MYSQL_TIME *ltime, const Time_zone *time_zone,
interval_type scale, INTERVAL interval)
{
- if (date_add_interval(ltime, scale, interval))
+ if (date_add_interval(current_thd, ltime, scale, interval))
return 0;
uint not_used;
@@ -759,8 +771,8 @@ bool get_next_time(const Time_zone *time_zone, my_time_t *next,
if (seconds)
{
- longlong seconds_diff;
- long microsec_diff;
+ ulonglong seconds_diff;
+ ulong microsec_diff;
bool negative= calc_time_diff(&local_now, &local_start, 1,
&seconds_diff, &microsec_diff);
if (!negative)
@@ -1344,6 +1356,10 @@ Event_job_data::execute(THD *thd, bool drop)
thd->reset_for_next_command();
+#ifdef WITH_WSREP
+ wsrep_open(thd);
+ wsrep_before_command(thd);
+#endif /* WITH_WSREP */
/*
MySQL parser currently assumes that current database is either
present in THD or all names in all statements are fully specified.
@@ -1518,6 +1534,10 @@ end:
if (save_sctx)
event_sctx.restore_security_context(thd, save_sctx);
#endif
+#ifdef WITH_WSREP
+ wsrep_after_command_ignore_result(thd);
+ wsrep_close(thd);
+#endif /* WITH_WSREP */
thd->lex->unit.cleanup();
thd->end_statement();
thd->cleanup_after_query();
diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h
index 33e9b457411..eb4f8590512 100644
--- a/sql/event_data_objects.h
+++ b/sql/event_data_objects.h
@@ -33,11 +33,7 @@ struct TABLE;
class Event_queue_element_for_exec
{
public:
- Event_queue_element_for_exec()
- {
- dbname.str= NULL; dbname.length= 0;
- name.str= NULL; name.length= 0;
- };
+ Event_queue_element_for_exec() : dbname{nullptr, 0}, name{nullptr, 0} {}
~Event_queue_element_for_exec();
bool
diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc
index 1ec2ab877fe..d2a168e538e 100644
--- a/sql/event_parse_data.cc
+++ b/sql/event_parse_data.cc
@@ -216,7 +216,13 @@ Event_parse_data::init_execute_at(THD *thd)
(starts_null && ends_null)));
DBUG_ASSERT(starts_null && ends_null);
- if (item_execute_at->get_date(&ltime, TIME_NO_ZERO_DATE))
+ /*
+ The expected data type is DATETIME. No nanoseconds truncation should
+ normally be needed. Using the default rounding mode.
+ See more comments in event_data_object.cc.
+ */
+ if (item_execute_at->get_date(thd, &ltime, TIME_NO_ZERO_DATE |
+ thd->temporal_round_mode()))
goto wrong_value;
ltime_utc= TIME_to_timestamp(thd,&ltime,&not_used);
@@ -275,7 +281,7 @@ Event_parse_data::init_interval(THD *thd)
if (item_expression->fix_fields(thd, &item_expression))
goto wrong_value;
- if (get_interval_value(item_expression, interval, &interval_tmp))
+ if (get_interval_value(thd, item_expression, interval, &interval_tmp))
goto wrong_value;
expression= 0;
@@ -378,7 +384,8 @@ Event_parse_data::init_starts(THD *thd)
if (item_starts->fix_fields(thd, &item_starts))
goto wrong_value;
- if (item_starts->get_date(&ltime, TIME_NO_ZERO_DATE))
+ if (item_starts->get_date(thd, &ltime, TIME_NO_ZERO_DATE |
+ thd->temporal_round_mode()))
goto wrong_value;
ltime_utc= TIME_to_timestamp(thd, &ltime, &not_used);
@@ -433,7 +440,8 @@ Event_parse_data::init_ends(THD *thd)
goto error_bad_params;
DBUG_PRINT("info", ("convert to TIME"));
- if (item_ends->get_date(&ltime, TIME_NO_ZERO_DATE))
+ if (item_ends->get_date(thd, &ltime, TIME_NO_ZERO_DATE |
+ thd->temporal_round_mode()))
goto error_bad_params;
ltime_utc= TIME_to_timestamp(thd, &ltime, &not_used);
@@ -472,7 +480,7 @@ Event_parse_data::report_bad_value(const char *item_name, Item *bad_item)
{
char buff[120];
String str(buff,(uint32) sizeof(buff), system_charset_info);
- String *str2= bad_item->fixed? bad_item->val_str(&str):NULL;
+ String *str2= bad_item->is_fixed() ? bad_item->val_str(&str) : NULL;
my_error(ER_WRONG_VALUE, MYF(0), item_name, str2? str2->c_ptr_safe():"NULL");
}
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index 2ebe1f5bb69..a4d010b19a8 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -151,7 +151,7 @@ deinit_event_thread(THD *thd)
{
thd->proc_info= "Clearing";
DBUG_PRINT("exit", ("Event thread finishing"));
- unlink_not_visible_thd(thd);
+ server_threads.erase(thd);
delete thd;
}
@@ -186,7 +186,7 @@ pre_init_event_thread(THD* thd)
thd->net.read_timeout= slave_net_timeout;
thd->variables.option_bits|= OPTION_AUTO_IS_NULL;
thd->client_capabilities|= CLIENT_MULTI_RESULTS;
- add_to_active_threads(thd);
+ server_threads.insert(thd);
/*
Guarantees that we will see the thread in SHOW PROCESSLIST though its
@@ -681,20 +681,20 @@ end:
Event_scheduler::workers_count()
*/
+static my_bool workers_count_callback(THD *thd, uint32_t *count)
+{
+ if (thd->system_thread == SYSTEM_THREAD_EVENT_WORKER)
+ ++*count;
+ return 0;
+}
+
+
uint
Event_scheduler::workers_count()
{
- THD *tmp;
- uint count= 0;
-
+ uint32_t count= 0;
DBUG_ENTER("Event_scheduler::workers_count");
- mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
- I_List_iterator<THD> it(threads);
- while ((tmp=it++))
- if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER)
- ++count;
- mysql_mutex_unlock(&LOCK_thread_count);
- DBUG_PRINT("exit", ("%d", count));
+ server_threads.iterate(workers_count_callback, &count);
DBUG_RETURN(count);
}
diff --git a/sql/events.cc b/sql/events.cc
index 7c7c5c0c2ac..5e15b92dc49 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -336,7 +336,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data)
if (check_access(thd, EVENT_ACL, parse_data->dbname.str, NULL, NULL, 0, 0))
DBUG_RETURN(TRUE);
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
if (lock_object_name(thd, MDL_key::EVENT,
parse_data->dbname.str, parse_data->name.str))
@@ -401,7 +401,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data)
my_message_sql(ER_STARTUP,
"Event Error: An error occurred while creating query "
"string, before writing it into binary log.",
- MYF(ME_NOREFRESH));
+ MYF(ME_ERROR_LOG));
ret= true;
}
else
@@ -425,10 +425,10 @@ Events::create_event(THD *thd, Event_parse_data *parse_data)
}
DBUG_RETURN(ret);
-
-WSREP_ERROR_LABEL:
- DBUG_RETURN(TRUE);
-
+#ifdef WITH_WSREP
+wsrep_error_label:
+ DBUG_RETURN(true);
+#endif
}
@@ -556,9 +556,10 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
thd->restore_stmt_binlog_format(save_binlog_format);
DBUG_RETURN(ret);
-
-WSREP_ERROR_LABEL:
- DBUG_RETURN(TRUE);
+#ifdef WITH_WSREP
+wsrep_error_label:
+ DBUG_RETURN(true);
+#endif
}
@@ -623,9 +624,10 @@ Events::drop_event(THD *thd, const LEX_CSTRING *dbname,
thd->restore_stmt_binlog_format(save_binlog_format);
DBUG_RETURN(ret);
-
-WSREP_ERROR_LABEL:
- DBUG_RETURN(TRUE);
+#ifdef WITH_WSREP
+wsrep_error_label:
+ DBUG_RETURN(true);
+#endif
}
@@ -656,8 +658,16 @@ Events::drop_schema_events(THD *thd, const char *db)
*/
if (event_queue)
event_queue->drop_schema_events(thd, &db_lex);
- db_repository->drop_schema_events(thd, &db_lex);
-
+ if (db_repository)
+ db_repository->drop_schema_events(thd, &db_lex);
+ else
+ {
+ if ((db_repository= new Event_db_repository))
+ {
+ db_repository->drop_schema_events(thd, &db_lex);
+ delete db_repository;
+ }
+ }
DBUG_VOID_RETURN;
}
@@ -830,12 +840,13 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
*/
if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
{
- DBUG_ASSERT(thd->lex->select_lex.db.str);
- if (!is_infoschema_db(&thd->lex->select_lex.db) && // There is no events in I_S
- check_access(thd, EVENT_ACL, thd->lex->select_lex.db.str,
+ DBUG_ASSERT(thd->lex->first_select_lex()->db.str);
+ if (!is_infoschema_db(&thd->lex->first_select_lex()->db) && // There is no events in I_S
+ check_access(thd, EVENT_ACL, thd->lex->first_select_lex()->db.str,
NULL, NULL, 0, 0))
DBUG_RETURN(1);
- db= normalize_db_name(thd->lex->select_lex.db.str, db_tmp, sizeof(db_tmp));
+ db= normalize_db_name(thd->lex->first_select_lex()->db.str,
+ db_tmp, sizeof(db_tmp));
}
ret= db_repository->fill_schema_events(thd, tables, db);
@@ -930,7 +941,7 @@ Events::init(THD *thd, bool opt_noacl_or_bootstrap)
my_message(ER_STARTUP,
"Event Scheduler: An error occurred when initializing "
"system tables. Disabling the Event Scheduler.",
- MYF(ME_NOREFRESH));
+ MYF(ME_ERROR_LOG));
/* Disable the scheduler since the system tables are not up to date */
opt_event_scheduler= EVENTS_OFF;
goto end;
@@ -952,7 +963,7 @@ Events::init(THD *thd, bool opt_noacl_or_bootstrap)
{
my_message_sql(ER_STARTUP,
"Event Scheduler: Error while loading from mysql.event table.",
- MYF(ME_NOREFRESH));
+ MYF(ME_ERROR_LOG));
res= TRUE; /* fatal error: request unireg_abort */
goto end;
}
@@ -1169,7 +1180,7 @@ Events::load_events_from_db(THD *thd)
{
my_message_sql(ER_STARTUP,
"Event Scheduler: Failed to open table mysql.event",
- MYF(ME_NOREFRESH));
+ MYF(ME_ERROR_LOG));
DBUG_RETURN(TRUE);
}
@@ -1195,7 +1206,7 @@ Events::load_events_from_db(THD *thd)
"Event Scheduler: "
"Error while loading events from mysql.event. "
"The table probably contains bad data or is corrupted",
- MYF(ME_NOREFRESH));
+ MYF(ME_ERROR_LOG));
delete et;
goto end;
}
@@ -1274,9 +1285,9 @@ Events::load_events_from_db(THD *thd)
}
my_printf_error(ER_STARTUP,
"Event Scheduler: Loaded %d event%s",
- MYF(ME_NOREFRESH |
+ MYF(ME_ERROR_LOG |
(global_system_variables.log_warnings) ?
- ME_JUST_INFO: 0),
+ ME_NOTE: 0),
count, (count == 1) ? "" : "s");
ret= FALSE;
diff --git a/sql/field.cc b/sql/field.cc
index 76d9a3ccf95..89c51288de8 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -33,11 +33,6 @@
#include "rpl_rli.h" // Pull in Relay_log_info
#include "slave.h" // Pull in rpl_master_has_bug()
#include "strfunc.h" // find_type2, find_set
-#include "sql_time.h" // str_to_datetime_with_warn,
- // str_to_time_with_warn,
- // TIME_to_timestamp,
- // make_time, make_date,
- // make_truncated_value_warning
#include "tztime.h" // struct Time_zone
#include "filesort.h" // change_double_for_sort
#include "log_event.h" // class Table_map_log_event
@@ -63,20 +58,30 @@ const char field_separator=',';
((ulong) ((1LL << MY_MIN(arg, 4) * 8) - 1))
// Column marked for read or the field set to read out of record[0]
-#define ASSERT_COLUMN_MARKED_FOR_READ \
- DBUG_ASSERT(!table || \
- (!table->read_set || \
- bitmap_is_set(table->read_set, field_index) || \
- (!(ptr >= table->record[0] && \
- ptr < table->record[0] + table->s->reclength))))
-
-#define ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED \
- DBUG_ASSERT(!table || \
- (!table->write_set || \
- bitmap_is_set(table->write_set, field_index) || \
- (!(ptr >= table->record[0] && \
- ptr < table->record[0] + table->s->reclength))) || \
- (table->vcol_set && bitmap_is_set(table->vcol_set, field_index)))
+inline bool Field::marked_for_read() const
+{
+ return !table ||
+ (!table->read_set ||
+ bitmap_is_set(table->read_set, field_index) ||
+ (!(ptr >= table->record[0] &&
+ ptr < table->record[0] + table->s->reclength)));
+}
+
+/*
+ The name of this function is a bit misleading as in 10.4 we don't
+ have to test anymore if the field is computed. Instead we mark
+ changed fields with DBUG_FIX_WRITE_SET() in table.cc
+*/
+
+inline bool Field::marked_for_write_or_computed() const
+{
+ return (!table ||
+ (!table->write_set ||
+ bitmap_is_set(table->write_set, field_index) ||
+ (!(ptr >= table->record[0] &&
+ ptr < table->record[0] + table->s->reclength))));
+}
+
#define FLAGSTR(S,F) ((S) & (F) ? #F " " : "")
@@ -93,15 +98,14 @@ const int FIELDTYPE_LAST= 254;
const int FIELDTYPE_NUM= FIELDTYPE_TEAR_FROM + (FIELDTYPE_LAST -
FIELDTYPE_TEAR_TO);
-static inline int field_type2index (enum_field_types field_type)
+static inline int merge_type2index(enum_field_types merge_type)
{
- DBUG_ASSERT(real_type_to_type(field_type) < FIELDTYPE_TEAR_FROM ||
- real_type_to_type(field_type) > FIELDTYPE_TEAR_TO);
- DBUG_ASSERT(field_type <= FIELDTYPE_LAST);
- field_type= real_type_to_type(field_type);
- if (field_type < FIELDTYPE_TEAR_FROM)
- return field_type;
- return FIELDTYPE_TEAR_FROM + (field_type - FIELDTYPE_TEAR_TO) - 1;
+ DBUG_ASSERT(merge_type < FIELDTYPE_TEAR_FROM ||
+ merge_type > FIELDTYPE_TEAR_TO);
+ DBUG_ASSERT(merge_type <= FIELDTYPE_LAST);
+ if (merge_type < FIELDTYPE_TEAR_FROM)
+ return merge_type;
+ return FIELDTYPE_TEAR_FROM + (merge_type - FIELDTYPE_TEAR_TO) - 1;
}
@@ -926,31 +930,37 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
}
};
-/**
- Return type of which can carry value of both given types in UNION result.
-
- @param a type for merging
- @param b type for merging
-
- @return
- type of field
-*/
-
-enum_field_types Field::field_type_merge(enum_field_types a,
- enum_field_types b)
-{
- return field_types_merge_rules[field_type2index(a)]
- [field_type2index(b)];
-}
const Type_handler *
Type_handler::aggregate_for_result_traditional(const Type_handler *a,
const Type_handler *b)
{
- enum_field_types ta= a->real_field_type();
- enum_field_types tb= b->real_field_type();
- return
- Type_handler::get_handler_by_real_type(Field::field_type_merge(ta, tb));
+ if (a == b)
+ {
+ /*
+ If two traditional handlers are equal, quickly return "a".
+ Some handlers (e.g. Type_handler_bool) pretend to be traditional,
+ but in fact they are not traditional in full extent, they are
+ only sub-types for now (and don't have a corresponding Field_xxx yet).
+ Here we preserve such handlers during aggregation.
+ As a result, COALESCE(true,true) preserves the "boolean" data type.
+
+ Need to do this conversion for deprecated data types,
+ similar to what field_type_merge_rules[][] does.
+ */
+ switch (a->field_type()) {
+ case MYSQL_TYPE_DECIMAL: return &type_handler_newdecimal;
+ case MYSQL_TYPE_DATE: return &type_handler_newdate;
+ case MYSQL_TYPE_VAR_STRING: return &type_handler_varchar;
+ default: break;
+ }
+ return a;
+ }
+ enum_field_types ta= a->traditional_merge_field_type();
+ enum_field_types tb= b->traditional_merge_field_type();
+ enum_field_types res= field_types_merge_rules[merge_type2index(ta)]
+ [merge_type2index(tb)];
+ return Type_handler::get_handler_by_real_type(res);
}
@@ -1381,7 +1391,7 @@ void Field::error_generated_column_function_is_not_allowed(THD *thd,
QT_ITEM_IDENT_SKIP_DB_NAMES |
QT_ITEM_IDENT_SKIP_TABLE_NAMES));
my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED,
- MYF(error ? 0 : ME_JUST_WARNING),
+ MYF(error ? 0 : ME_WARNING),
tmp.c_ptr_safe(), vcol_info->get_vcol_type_name(),
const_cast<const char*>(field_name.str));
}
@@ -1397,8 +1407,10 @@ bool Field::check_vcol_sql_mode_dependency(THD *thd, vcol_init_mode mode) const
DBUG_ASSERT(vcol_info);
if ((flags & PART_KEY_FLAG) != 0 || stored_in_db())
{
+ Sql_mode_dependency valdep= vcol_info->expr->value_depends_on_sql_mode();
+ sql_mode_t cnvdep= conversion_depends_on_sql_mode(thd, vcol_info->expr);
Sql_mode_dependency dep=
- vcol_info->expr->value_depends_on_sql_mode() &
+ (valdep | Sql_mode_dependency(0, cnvdep)) &
Sql_mode_dependency(~0, ~can_handle_sql_mode_dependency_on_store());
if (dep)
{
@@ -1412,6 +1424,14 @@ bool Field::check_vcol_sql_mode_dependency(THD *thd, vcol_init_mode mode) const
}
+bool Field::make_empty_rec_store_default_value(THD *thd, Item *item)
+{
+ DBUG_ASSERT(!(flags & BLOB_FLAG));
+ int res= item->save_in_field(this, true);
+ return res != 0 && res != 3;
+}
+
+
/**
Numeric fields base class constructor.
*/
@@ -1733,7 +1753,7 @@ int Field::warn_if_overflow(int op_result)
String *Field::val_int_as_str(String *val_buffer, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
CHARSET_INFO *cs= &my_charset_bin;
uint length;
longlong value= val_int();
@@ -1872,11 +1892,9 @@ int Field::store(const char *to, size_t length, CHARSET_INFO *cs,
}
-int Field::store_timestamp(my_time_t ts, ulong sec_part)
+int Field::store_timestamp_dec(const timeval &ts, uint dec)
{
- MYSQL_TIME ltime;
- get_thd()->timestamp_to_TIME(&ltime, ts, sec_part, 0);
- return store_time_dec(&ltime, decimals());
+ return store_time_dec(Datetime(get_thd(), ts).get_mysql_time(), dec);
}
/**
@@ -1976,14 +1994,6 @@ Field::unpack(uchar* to, const uchar *from, const uchar *from_end,
}
-my_decimal *Field::val_decimal(my_decimal *decimal)
-{
- /* This never have to be called */
- DBUG_ASSERT(0);
- return 0;
-}
-
-
void Field_num::add_zerofill_and_unsigned(String &res) const
{
if (unsigned_flag)
@@ -2019,7 +2029,7 @@ void Field::make_send_field(Send_field *field)
}
field->col_name= field_name;
field->length=field_length;
- field->type=type();
+ field->set_handler(type_handler());
field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
field->decimals= 0;
}
@@ -2085,7 +2095,7 @@ longlong Field::convert_decimal2longlong(const my_decimal *val,
int Field_int::store_decimal(const my_decimal *val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int err= 0;
longlong i= convert_decimal2longlong(val, unsigned_flag, &err);
return MY_TEST(err | store(i, unsigned_flag));
@@ -2108,26 +2118,25 @@ int Field_int::store_decimal(const my_decimal *val)
my_decimal* Field_int::val_decimal(my_decimal *decimal_value)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
longlong nr= val_int();
int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value);
return decimal_value;
}
-bool Field_int::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+bool Field_int::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
- longlong nr= val_int();
- bool neg= !(flags & UNSIGNED_FLAG) && nr < 0;
- return int_to_datetime_with_warn(neg, neg ? -nr : nr, ltime, fuzzydate,
- table->s, field_name.str);
+ DBUG_ASSERT(marked_for_read());
+ Longlong_hybrid nr(val_int(), (flags & UNSIGNED_FLAG));
+ return int_to_datetime_with_warn(get_thd(), nr, ltime,
+ fuzzydate, table->s, field_name.str);
}
-bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id)
+bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate, ulonglong trx_id)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
DBUG_ASSERT(ltime);
if (!table || !table->s)
return true;
@@ -2253,7 +2262,7 @@ void Field_num::make_send_field(Send_field *field)
int Field_str::store_decimal(const my_decimal *d)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
double val;
/* TODO: use decimal2string? */
int err= warn_if_overflow(my_decimal2double(E_DEC_FATAL_ERROR &
@@ -2264,7 +2273,7 @@ int Field_str::store_decimal(const my_decimal *d)
my_decimal *Field_str::val_decimal(my_decimal *decimal_value)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
longlong nr= val_int();
int2my_decimal(E_DEC_FATAL_ERROR, nr, 0, decimal_value);
return decimal_value;
@@ -2305,17 +2314,42 @@ uint Field::fill_cache_field(CACHE_FIELD *copy)
}
-bool Field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+bool Field::get_date(MYSQL_TIME *to, date_mode_t mode)
{
- char buff[40];
- String tmp(buff,sizeof(buff),&my_charset_bin),*res;
- if (!(res=val_str(&tmp)) ||
- str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
- ltime, fuzzydate))
- return 1;
- return 0;
+ StringBuffer<40> tmp;
+ Temporal::Warn_push warn(get_thd(), nullptr, nullptr, nullptr, to, mode);
+ Temporal_hybrid *t= new(to) Temporal_hybrid(get_thd(), &warn,
+ val_str(&tmp), mode);
+ return !t->is_valid_temporal();
}
+
+longlong Field::val_datetime_packed(THD *thd)
+{
+ MYSQL_TIME ltime, tmp;
+ if (get_date(&ltime, Datetime::Options_cmp(thd)))
+ return 0;
+ if (ltime.time_type != MYSQL_TIMESTAMP_TIME)
+ return pack_time(&ltime);
+ if (time_to_datetime_with_warn(thd, &ltime, &tmp, TIME_CONV_NONE))
+ return 0;
+ return pack_time(&tmp);
+}
+
+
+longlong Field::val_time_packed(THD *thd)
+{
+ MYSQL_TIME ltime;
+ Time::Options_cmp opt(thd);
+ if (get_date(&ltime, opt))
+ return 0;
+ if (ltime.time_type == MYSQL_TIMESTAMP_TIME)
+ return pack_time(&ltime);
+ // Conversion from DATETIME or DATE to TIME is needed
+ return Time(thd, &ltime, opt).to_packed();
+}
+
+
/**
This is called when storing a date in a string.
@@ -2325,7 +2359,7 @@ bool Field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
int Field::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
char buff[MAX_DATE_STRING_REP_LENGTH];
uint length= (uint) my_TIME_to_str(ltime, buff, dec);
/* Avoid conversion when field character set is ASCII compatible */
@@ -2383,6 +2417,36 @@ Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table,
}
+/**
+ Create field for temporary table from given field.
+
+ @param thd Thread handler
+ @param table Temporary table
+ @param maybe_null_arg If the result field should be NULL-able,
+ even if the original field is NOT NULL, e.g. for:
+ - OUTER JOIN fields
+ - WITH ROLLUP fields
+ - arguments of aggregate functions, e.g. SUM(column1)
+ @retval NULL, on error
+ @retval pointer to the new field created, on success.
+*/
+
+Field *Field::create_tmp_field(MEM_ROOT *mem_root, TABLE *new_table,
+ bool maybe_null_arg)
+{
+ Field *new_field;
+
+ if ((new_field= make_new_field(mem_root, new_table, new_table == table)))
+ {
+ new_field->init_for_tmp_table(this, new_table);
+ new_field->flags|= flags & NO_DEFAULT_VALUE_FLAG;
+ if (maybe_null_arg)
+ new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join
+ }
+ return new_field;
+}
+
+
/* This is used to generate a field in TABLE from TABLE_SHARE */
Field *Field::clone(MEM_ROOT *root, TABLE *new_table)
@@ -2442,6 +2506,15 @@ void Field_null::sql_type(String &res) const
}
+bool Field_null::is_equal(const Column_definition &new_field) const
+{
+ DBUG_ASSERT(!compression_method());
+ return new_field.type_handler() == type_handler() &&
+ new_field.charset == field_charset &&
+ new_field.length == max_display_length();
+}
+
+
/****************************************************************************
Field_row, e.g. for ROW-type SP variables
****************************************************************************/
@@ -2540,7 +2613,7 @@ void Field_decimal::overflow(bool negative)
int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(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;
@@ -2906,7 +2979,7 @@ int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs)
int Field_decimal::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
if (unsigned_flag && nr < 0)
{
overflow(1);
@@ -2944,7 +3017,7 @@ int Field_decimal::store(double nr)
int Field_decimal::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
char buff[22];
uint length, int_part;
char fyllchar;
@@ -2980,7 +3053,7 @@ int Field_decimal::store(longlong nr, bool unsigned_val)
double Field_decimal::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
int not_used;
char *end_not_used;
return my_strntod(&my_charset_bin, (char*) ptr, field_length, &end_not_used,
@@ -2989,7 +3062,7 @@ double Field_decimal::val_real(void)
longlong Field_decimal::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
int not_used;
if (unsigned_flag)
return my_strntoull(&my_charset_bin, (char*) ptr, field_length, 10, NULL,
@@ -3002,7 +3075,7 @@ longlong Field_decimal::val_int(void)
String *Field_decimal::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
uchar *str;
size_t tmp_length;
@@ -3176,7 +3249,7 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value,
Otherwise sets maximal number that can be stored in the field.
@param decimal_value my_decimal
- @param [OUT] native_error the error returned by my_decimal2binary().
+ @param [OUT] native_error the error returned by my_decimal::to_binary().
@retval
0 ok
@@ -3187,7 +3260,7 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value,
bool Field_new_decimal::store_value(const my_decimal *decimal_value,
int *native_error)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
DBUG_ENTER("Field_new_decimal::store_value");
#ifndef DBUG_OFF
@@ -3214,8 +3287,8 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value,
}
#endif
- *native_error= my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
- decimal_value, ptr, precision, dec);
+ *native_error= decimal_value->to_binary(ptr, precision, dec,
+ E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW);
if (unlikely(*native_error == E_DEC_OVERFLOW))
{
@@ -3223,7 +3296,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value,
DBUG_PRINT("info", ("overflow"));
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
set_value_on_overflow(&buff, decimal_value->sign());
- my_decimal2binary(E_DEC_FATAL_ERROR, &buff, ptr, precision, dec);
+ buff.to_binary(ptr, precision, dec);
error= 1;
}
DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr,
@@ -3245,7 +3318,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value)
int Field_new_decimal::store(const char *from, size_t length,
CHARSET_INFO *charset_arg)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
my_decimal decimal_value;
THD *thd= get_thd();
DBUG_ENTER("Field_new_decimal::store(char*)");
@@ -3329,7 +3402,7 @@ int Field_new_decimal::store(const char *from, size_t length,
int Field_new_decimal::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
my_decimal decimal_value;
int err;
THD *thd= get_thd();
@@ -3354,7 +3427,7 @@ int Field_new_decimal::store(double nr)
int Field_new_decimal::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
my_decimal decimal_value;
int err;
@@ -3376,7 +3449,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_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
return store_value(decimal_value);
}
@@ -3388,40 +3461,9 @@ int Field_new_decimal::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
}
-double Field_new_decimal::val_real(void)
-{
- ASSERT_COLUMN_MARKED_FOR_READ;
- double dbl;
- my_decimal decimal_value;
- my_decimal2double(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), &dbl);
- return dbl;
-}
-
-
-longlong Field_new_decimal::val_int(void)
-{
- ASSERT_COLUMN_MARKED_FOR_READ;
- longlong i;
- my_decimal decimal_value;
- my_decimal2int(E_DEC_FATAL_ERROR, val_decimal(&decimal_value),
- unsigned_flag, &i);
- return i;
-}
-
-
-ulonglong Field_new_decimal::val_uint(void)
-{
- ASSERT_COLUMN_MARKED_FOR_READ;
- longlong i;
- my_decimal decimal_value;
- my_decimal2int(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), true, &i);
- return i;
-}
-
-
my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
DBUG_ENTER("Field_new_decimal::val_decimal");
binary2my_decimal(E_DEC_FATAL_ERROR, ptr, decimal_value,
precision, dec);
@@ -3431,28 +3473,6 @@ my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value)
}
-String *Field_new_decimal::val_str(String *val_buffer,
- String *val_ptr __attribute__((unused)))
-{
- ASSERT_COLUMN_MARKED_FOR_READ;
- my_decimal decimal_value;
- uint fixed_precision= zerofill ? precision : 0;
- my_decimal2string(E_DEC_FATAL_ERROR, val_decimal(&decimal_value),
- fixed_precision, dec, '0', val_buffer);
- val_buffer->set_charset(&my_charset_numeric);
- return val_buffer;
-}
-
-
-bool Field_new_decimal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
-{
- my_decimal value;
- return decimal_to_datetime_with_warn(val_decimal(&value),
- ltime, fuzzydate, table->s,
- field_name.str);
-}
-
-
int Field_new_decimal::cmp(const uchar *a,const uchar*b)
{
return memcmp(a, b, bin_size);
@@ -3528,15 +3548,15 @@ bool Field_new_decimal::compatible_field_size(uint field_metadata,
}
-uint Field_new_decimal::is_equal(Create_field *new_field)
+bool Field_new_decimal::is_equal(const Column_definition &new_field) const
{
- return ((new_field->type_handler() == type_handler()) &&
- ((new_field->flags & UNSIGNED_FLAG) ==
+ return ((new_field.type_handler() == type_handler()) &&
+ ((new_field.flags & UNSIGNED_FLAG) ==
(uint) (flags & UNSIGNED_FLAG)) &&
- ((new_field->flags & AUTO_INCREMENT_FLAG) ==
+ ((new_field.flags & AUTO_INCREMENT_FLAG) <=
(uint) (flags & AUTO_INCREMENT_FLAG)) &&
- (new_field->length == max_display_length()) &&
- (new_field->decimals == dec));
+ (new_field.length == max_display_length()) &&
+ (new_field.decimals == dec));
}
@@ -3606,8 +3626,8 @@ Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx,
if (const_item->field_type() != MYSQL_TYPE_NEWDECIMAL ||
const_item->decimal_scale() != decimals())
{
- my_decimal *val, val_buffer, val_buffer2;
- if (!(val= const_item->val_decimal(&val_buffer)))
+ VDec val(const_item);
+ if (val.is_null())
{
DBUG_ASSERT(0);
return const_item;
@@ -3617,9 +3637,9 @@ Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx,
See comments about truncation in the same place in
Field_time::get_equal_const_item().
*/
- my_decimal_round(E_DEC_FATAL_ERROR, val, decimals(), true, &val_buffer2);
- return new (thd->mem_root) Item_decimal(thd, field_name.str,
- &val_buffer2,
+ my_decimal tmp;
+ val.round_to(&tmp, decimals(), TRUNCATE);
+ return new (thd->mem_root) Item_decimal(thd, field_name.str, &tmp,
decimals(), field_length);
}
break;
@@ -3645,7 +3665,7 @@ int Field_int::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
int Field_tiny::store(const char *from,size_t len,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error;
longlong rnd;
@@ -3657,7 +3677,7 @@ int Field_tiny::store(const char *from,size_t len,CHARSET_INFO *cs)
int Field_tiny::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
nr=rint(nr);
if (unsigned_flag)
@@ -3700,7 +3720,7 @@ int Field_tiny::store(double nr)
int Field_tiny::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
if (unsigned_flag)
@@ -3745,7 +3765,7 @@ int Field_tiny::store(longlong nr, bool unsigned_val)
double Field_tiny::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
int tmp= unsigned_flag ? (int) ptr[0] :
(int) ((signed char*) ptr)[0];
return (double) tmp;
@@ -3754,7 +3774,7 @@ double Field_tiny::val_real(void)
longlong Field_tiny::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
int tmp= unsigned_flag ? (int) ptr[0] :
(int) ((signed char*) ptr)[0];
return (longlong) tmp;
@@ -3764,7 +3784,7 @@ longlong Field_tiny::val_int(void)
String *Field_tiny::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
long nr= unsigned_flag ? (long) ptr[0] : (long) ((signed char*) ptr)[0];
return val_str_from_long(val_buffer, 5, -10, nr);
}
@@ -3805,7 +3825,7 @@ void Field_tiny::sql_type(String &res) const
int Field_short::store(const char *from,size_t len,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int store_tmp;
int error;
longlong rnd;
@@ -3819,7 +3839,7 @@ int Field_short::store(const char *from,size_t len,CHARSET_INFO *cs)
int Field_short::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
int16 res;
nr=rint(nr);
@@ -3864,7 +3884,7 @@ int Field_short::store(double nr)
int Field_short::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
int16 res;
@@ -3912,7 +3932,7 @@ int Field_short::store(longlong nr, bool unsigned_val)
double Field_short::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
short j;
j=sint2korr(ptr);
return unsigned_flag ? (double) (unsigned short) j : (double) j;
@@ -3920,7 +3940,7 @@ double Field_short::val_real(void)
longlong Field_short::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
short j;
j=sint2korr(ptr);
return unsigned_flag ? (longlong) (unsigned short) j : (longlong) j;
@@ -3930,7 +3950,7 @@ longlong Field_short::val_int(void)
String *Field_short::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
short j= sint2korr(ptr);
long nr= unsigned_flag ? (long) (unsigned short) j : (long) j;
return val_str_from_long(val_buffer, 7, -10, nr);
@@ -3979,7 +3999,7 @@ void Field_short::sql_type(String &res) const
int Field_medium::store(const char *from,size_t len,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int store_tmp;
int error;
longlong rnd;
@@ -3993,7 +4013,7 @@ int Field_medium::store(const char *from,size_t len,CHARSET_INFO *cs)
int Field_medium::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
nr=rint(nr);
if (unsigned_flag)
@@ -4039,7 +4059,7 @@ int Field_medium::store(double nr)
int Field_medium::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
if (unsigned_flag)
@@ -4088,7 +4108,7 @@ int Field_medium::store(longlong nr, bool unsigned_val)
double Field_medium::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
return (double) j;
}
@@ -4096,7 +4116,7 @@ double Field_medium::val_real(void)
longlong Field_medium::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
return (longlong) j;
}
@@ -4105,7 +4125,7 @@ longlong Field_medium::val_int(void)
String *Field_medium::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
long nr= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
return val_str_from_long(val_buffer, 10, -10, nr);
}
@@ -4131,7 +4151,7 @@ String *Field_int::val_str_from_long(String *val_buffer,
bool Field_medium::send_binary(Protocol *protocol)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
return protocol->store_long(Field_medium::val_int());
}
@@ -4177,7 +4197,7 @@ void Field_medium::sql_type(String &res) const
int Field_long::store(const char *from,size_t len,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
long store_tmp;
int error;
longlong rnd;
@@ -4191,7 +4211,7 @@ int Field_long::store(const char *from,size_t len,CHARSET_INFO *cs)
int Field_long::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
int32 res;
nr=rint(nr);
@@ -4236,7 +4256,7 @@ int Field_long::store(double nr)
int Field_long::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
int32 res;
@@ -4282,7 +4302,7 @@ int Field_long::store(longlong nr, bool unsigned_val)
double Field_long::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
int32 j;
j=sint4korr(ptr);
return unsigned_flag ? (double) (uint32) j : (double) j;
@@ -4290,7 +4310,7 @@ double Field_long::val_real(void)
longlong Field_long::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
int32 j;
/* See the comment in Field_long::store(long long) */
DBUG_ASSERT(!table || table->in_use == current_thd);
@@ -4302,7 +4322,7 @@ longlong Field_long::val_int(void)
String *Field_long::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
long nr= unsigned_flag ? (long) uint4korr(ptr) : sint4korr(ptr);
return val_str_from_long(val_buffer, 12, unsigned_flag ? 10 : -10, nr);
}
@@ -4310,7 +4330,7 @@ String *Field_long::val_str(String *val_buffer,
bool Field_long::send_binary(Protocol *protocol)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
return protocol->store_long(Field_long::val_int());
}
@@ -4350,7 +4370,7 @@ void Field_long::sql_type(String &res) const
int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
char *end;
ulonglong tmp;
@@ -4373,7 +4393,7 @@ int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs)
int Field_longlong::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
Converter_double_to_longlong conv(nr, unsigned_flag);
if (unlikely(conv.error()))
@@ -4386,7 +4406,7 @@ int Field_longlong::store(double nr)
int Field_longlong::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
if (unlikely(nr < 0)) // Only possible error
@@ -4410,7 +4430,7 @@ int Field_longlong::store(longlong nr, bool unsigned_val)
double Field_longlong::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
longlong j;
j=sint8korr(ptr);
/* The following is open coded to avoid a bug in gcc 3.3 */
@@ -4425,7 +4445,7 @@ double Field_longlong::val_real(void)
longlong Field_longlong::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
longlong j;
j=sint8korr(ptr);
return j;
@@ -4455,7 +4475,7 @@ String *Field_longlong::val_str(String *val_buffer,
bool Field_longlong::send_binary(Protocol *protocol)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
return protocol->store_longlong(Field_longlong::val_int(), unsigned_flag);
}
@@ -4497,14 +4517,14 @@ void Field_longlong::sql_type(String &res) const
void Field_longlong::set_max()
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
set_notnull();
int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX);
}
bool Field_longlong::is_max()
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
if (unsigned_flag)
{
ulonglong j;
@@ -4534,7 +4554,7 @@ int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs)
int Field_float::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= truncate_double(&nr, field_length,
not_fixed ? NOT_FIXED_DEC : dec,
unsigned_flag, FLT_MAX);
@@ -4563,7 +4583,7 @@ int Field_float::store(longlong nr, bool unsigned_val)
double Field_float::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
float j;
float4get(j,ptr);
return ((double) j);
@@ -4580,7 +4600,7 @@ longlong Field_float::val_int(void)
String *Field_float::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
if (Float(ptr).to_string(val_buffer, dec))
@@ -4643,7 +4663,7 @@ void Field_float::sort_string(uchar *to,uint length __attribute__((unused)))
bool Field_float::send_binary(Protocol *protocol)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
return protocol->store((float) Field_float::val_real(), dec, (String*) 0);
}
@@ -4694,7 +4714,7 @@ int Field_double::store(const char *from,size_t len,CHARSET_INFO *cs)
int Field_double::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= truncate_double(&nr, field_length,
not_fixed ? NOT_FIXED_DEC : dec,
unsigned_flag, DBL_MAX);
@@ -4841,13 +4861,6 @@ Converter_double_to_longlong::push_warning(THD *thd,
}
-int Field_real::store_decimal(const my_decimal *dm)
-{
- double dbl;
- my_decimal2double(E_DEC_FATAL_ERROR, dm, &dbl);
- return store(dbl);
-}
-
int Field_real::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
{
return store(TIME_to_double(ltime));
@@ -4856,7 +4869,7 @@ int Field_real::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
double Field_double::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
double j;
float8get(j,ptr);
return j;
@@ -4874,17 +4887,17 @@ longlong Field_double::val_int_from_real(bool want_unsigned_result)
my_decimal *Field_real::val_decimal(my_decimal *decimal_value)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
double2my_decimal(E_DEC_FATAL_ERROR, val_real(), decimal_value);
return decimal_value;
}
-bool Field_real::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+bool Field_real::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
double nr= val_real();
- return double_to_datetime_with_warn(nr, ltime, fuzzydate,
+ return double_to_datetime_with_warn(get_thd(), nr, ltime, fuzzydate,
table->s, field_name.str);
}
@@ -4912,7 +4925,7 @@ Item *Field_real::get_equal_const_item(THD *thd, const Context &ctx,
String *Field_double::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
double nr;
float8get(nr,ptr);
@@ -5060,151 +5073,192 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
}
+sql_mode_t
+Field_timestamp::conversion_depends_on_sql_mode(THD *thd, Item *expr) const
+{
+ return expr->datetime_precision(thd) > decimals() ?
+ MODE_TIME_ROUND_FRACTIONAL : 0;
+}
+
+
int Field_timestamp::save_in_field(Field *to)
{
ulong sec_part;
my_time_t ts= get_timestamp(&sec_part);
- return to->store_timestamp(ts, sec_part);
+ return to->store_timestamp_dec(Timeval(ts, sec_part), decimals());
}
my_time_t Field_timestamp::get_timestamp(const uchar *pos,
ulong *sec_part) const
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
*sec_part= 0;
return sint4korr(pos);
}
-int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time,
- const ErrConv *str,
- int was_cut,
- bool have_smth_to_conv)
+bool Field_timestamp::val_native(Native *to)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
- uint error = 0;
- my_time_t timestamp;
+ DBUG_ASSERT(marked_for_read());
+ my_time_t sec= (my_time_t) sint4korr(ptr);
+ return Timestamp_or_zero_datetime(Timestamp(sec, 0), sec == 0).
+ to_native(to, 0);
+}
- if (MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) || !have_smth_to_conv)
- {
- error= 1;
- set_datetime_warning(WARN_DATA_TRUNCATED,
- str, MYSQL_TIMESTAMP_DATETIME, 1);
- }
- else if (MYSQL_TIME_WARN_HAVE_NOTES(was_cut))
+
+int Field_timestamp::store_TIME_with_warning(THD *thd, const Datetime *dt,
+ const ErrConv *str, int was_cut)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ static const Timestamp zero(0, 0);
+
+ // Handle totally bad values
+ if (!dt->is_valid_datetime())
{
- error= 3;
- set_datetime_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED,
- str, MYSQL_TIMESTAMP_DATETIME, 1);
+ set_datetime_warning(WARN_DATA_TRUNCATED, str, "datetime", 1);
+ store_TIMESTAMP(zero);
+ return 1;
}
- /* Only convert a correct date (not a zero date) */
- if (have_smth_to_conv && l_time->month)
+
+ // Handle values that do not need DATETIME to TIMESTAMP conversion
+ if (!dt->get_mysql_time()->month)
{
- uint conversion_error;
- timestamp= TIME_to_timestamp(thd, l_time, &conversion_error);
- if (timestamp == 0 && l_time->second_part == 0)
- conversion_error= ER_WARN_DATA_OUT_OF_RANGE;
- if (unlikely(conversion_error))
- {
- set_datetime_warning(conversion_error,
- str, MYSQL_TIMESTAMP_DATETIME, !error);
- error= 1;
- }
+ /*
+ Zero date is allowed by the current sql_mode. Store zero timestamp.
+ Return success or a warning about non-fatal truncation, e.g.:
+ INSERT INTO t1 (ts) VALUES ('0000-00-00 00:00:00 some tail');
+ */
+ store_TIMESTAMP(zero);
+ return store_TIME_return_code_with_warnings(was_cut, str, "datetime");
}
- else
+
+ // Convert DATETIME to TIMESTAMP
+ uint conversion_error;
+ const MYSQL_TIME *l_time= dt->get_mysql_time();
+ my_time_t timestamp= TIME_to_timestamp(thd, l_time, &conversion_error);
+ if (timestamp == 0 && l_time->second_part == 0)
{
- timestamp= 0;
- l_time->second_part= 0;
+ set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, "datetime", 1);
+ store_TIMESTAMP(zero);
+ return 1; // date was fine but pointed to a DST gap
}
- store_TIME(timestamp, l_time->second_part);
- return error;
-}
+ // Store the value
+ DBUG_ASSERT(!dt->fraction_remainder(decimals()));
+ store_TIMESTAMP(Timestamp(timestamp, l_time->second_part));
-static bool
-copy_or_convert_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
-{
- if (from->time_type == MYSQL_TIMESTAMP_TIME)
- return time_to_datetime(thd, from, to);
- *to= *from;
- return false;
+ // Calculate return value and send warnings if needed
+ if (unlikely(conversion_error)) // e.g. DATETIME in the DST gap
+ {
+ set_datetime_warning(conversion_error, str, "datetime", 1);
+ return 1;
+ }
+ return store_TIME_return_code_with_warnings(was_cut, str, "datetime");
}
-sql_mode_t Field_timestamp::sql_mode_for_timestamp(THD *thd) const
+date_conv_mode_t Timestamp::sql_mode_for_timestamp(THD *thd)
{
// We don't want to store invalid or fuzzy datetime values in TIMESTAMP
- return (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE;
+ return date_conv_mode_t((thd->variables.sql_mode & MODE_NO_ZERO_DATE) |
+ MODE_NO_ZERO_IN_DATE);
}
int Field_timestamp::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
- int unused;
+ int warn;
ErrConvTime str(ltime);
THD *thd= get_thd();
- MYSQL_TIME l_time;
- bool valid= !copy_or_convert_to_datetime(thd, ltime, &l_time) &&
- !check_date(&l_time, pack_time(&l_time) != 0,
- sql_mode_for_timestamp(thd), &unused);
- return store_TIME_with_warning(thd, &l_time, &str, false, valid);
+ Datetime dt(thd, &warn, ltime, Timestamp::DatetimeOptions(thd), decimals());
+ return store_TIME_with_warning(thd, &dt, &str, warn);
}
int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs)
{
- MYSQL_TIME l_time;
- MYSQL_TIME_STATUS status;
- bool have_smth_to_conv;
ErrConvString str(from, len, cs);
THD *thd= get_thd();
-
- have_smth_to_conv= !str_to_datetime(cs, from, len, &l_time,
- sql_mode_for_timestamp(thd), &status);
- return store_TIME_with_warning(thd, &l_time, &str,
- status.warnings, have_smth_to_conv);
+ MYSQL_TIME_STATUS st;
+ Datetime dt(thd, &st, from, len, cs, Timestamp::DatetimeOptions(thd), decimals());
+ return store_TIME_with_warning(thd, &dt, &str, st.warnings);
}
int Field_timestamp::store(double nr)
{
- MYSQL_TIME l_time;
int error;
ErrConvDouble str(nr);
THD *thd= get_thd();
-
- longlong tmp= double_to_datetime(nr, &l_time, sql_mode_for_timestamp(thd),
- &error);
- return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1);
+ Datetime dt(thd, &error, nr, Timestamp::DatetimeOptions(thd), decimals());
+ return store_TIME_with_warning(thd, &dt, &str, error);
}
int Field_timestamp::store(longlong nr, bool unsigned_val)
{
- MYSQL_TIME l_time;
int error;
- ErrConvInteger str(nr, unsigned_val);
+ Longlong_hybrid tmp(nr, unsigned_val);
+ ErrConvInteger str(tmp);
THD *thd= get_thd();
+ Datetime dt(&error, tmp, Timestamp::DatetimeOptions(thd));
+ return store_TIME_with_warning(thd, &dt, &str, error);
+}
- longlong tmp= number_to_datetime(nr, 0, &l_time, sql_mode_for_timestamp(thd),
- &error);
- return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1);
+
+int Field_timestamp::store_timestamp_dec(const timeval &ts, uint dec)
+{
+ int warn= 0;
+ time_round_mode_t mode= Datetime::default_round_mode(get_thd());
+ store_TIMESTAMP(Timestamp(ts).round(decimals(), mode, &warn));
+ if (warn)
+ {
+ /*
+ We're here if rounding would overflow outside of the supported TIMESTAMP
+ range, so truncation happened instead:
+ CREATE TABLE t1 (a TIMESTAMP(6));
+ INSERT INTO t1 VALUES ('maximum-possible-timestamp.999999');
+ ALTER TABLE t1 MODIFY a TIMESTAMP(5);
+ SELECT * FROM t1; --> 'maximum-possible-timestamp.99999' (5 digits)
+ Raise a warning, like DATETIME does for '9999-12-31 23:59:59.999999'.
+ */
+ set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ }
+ if (ts.tv_sec == 0 && ts.tv_usec == 0 &&
+ get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE)
+ return zero_time_stored_return_code_with_warning();
+ return 0;
}
-int Field_timestamp::store_timestamp(my_time_t ts, ulong sec_part)
+int Field_timestamp::zero_time_stored_return_code_with_warning()
{
- store_TIME(ts, sec_part);
- if (ts == 0 && sec_part == 0 &&
- get_thd()->variables.sql_mode & TIME_NO_ZERO_DATE)
+ if (get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE)
{
ErrConvString s(
STRING_WITH_LEN("0000-00-00 00:00:00.000000") - (decimals() ? 6 - decimals() : 7),
system_charset_info);
- set_datetime_warning(WARN_DATA_TRUNCATED, &s, MYSQL_TIMESTAMP_DATETIME, 1);
+ set_datetime_warning(WARN_DATA_TRUNCATED, &s, "datetime", 1);
return 1;
}
return 0;
+
+}
+
+
+int Field_timestamp::store_native(const Native &value)
+{
+ if (!value.length()) // Zero datetime
+ {
+ reset();
+ return zero_time_stored_return_code_with_warning();
+ }
+ /*
+ The exact second precision is not important here.
+ Field_timestamp*::store_timestamp_dec() do not use the "dec" parameter.
+ Passing TIME_SECOND_PART_DIGITS is OK.
+ */
+ return store_timestamp_dec(Timestamp(value).tv(), TIME_SECOND_PART_DIGITS);
}
@@ -5217,7 +5271,7 @@ double Field_timestamp::val_real(void)
longlong Field_timestamp::val_int(void)
{
MYSQL_TIME ltime;
- if (get_date(&ltime, TIME_NO_ZERO_DATE))
+ if (get_date(&ltime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd())))
return 0;
return ltime.year * 10000000000LL + ltime.month * 100000000LL +
@@ -5237,7 +5291,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
to= (char*) val_buffer->ptr();
val_buffer->length(field_length);
- if (get_date(&ltime, TIME_NO_ZERO_DATE))
+ if (get_date(&ltime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd())))
{ /* Zero time is "000000" */
val_ptr->set(zero_timestamp, field_length, &my_charset_numeric);
return val_ptr;
@@ -5305,11 +5359,11 @@ Field_timestamp::validate_value_in_record(THD *thd, const uchar *record) const
DBUG_ASSERT(!is_null_in_record(record));
ulong sec_part;
return !get_timestamp(ptr_in_record(record), &sec_part) && !sec_part &&
- (sql_mode_for_dates(thd) & TIME_NO_ZERO_DATE) != 0;
+ bool(sql_mode_for_dates(thd) & TIME_NO_ZERO_DATE) != false;
}
-bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Field_timestamp::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
ulong sec_part;
my_time_t ts= get_timestamp(&sec_part);
@@ -5320,7 +5374,7 @@ bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
bool Field_timestamp::send_binary(Protocol *protocol)
{
MYSQL_TIME ltime;
- Field_timestamp::get_date(&ltime, 0);
+ Field_timestamp::get_date(&ltime, date_mode_t(0));
return protocol->store(&ltime, 0);
}
@@ -5359,7 +5413,7 @@ void Field_timestamp::sql_type(String &res) const
int Field_timestamp::set_time()
{
set_notnull();
- store_TIME(get_thd()->query_start(), 0);
+ store_TIMESTAMP(Timestamp(get_thd()->query_start(), 0));
return 0;
}
@@ -5450,24 +5504,36 @@ static longlong read_lowendian(const uchar *from, uint bytes)
}
}
-void Field_timestamp_hires::store_TIME(my_time_t timestamp, ulong sec_part)
+void Field_timestamp_hires::store_TIMEVAL(const timeval &tv)
{
- mi_int4store(ptr, timestamp);
- store_bigendian(sec_part_shift(sec_part, dec), ptr+4, sec_part_bytes(dec));
+ mi_int4store(ptr, tv.tv_sec);
+ store_bigendian(sec_part_shift(tv.tv_usec, dec), ptr+4, sec_part_bytes(dec));
}
my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos,
ulong *sec_part) const
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
*sec_part= (long)sec_part_unshift(read_bigendian(pos+4, sec_part_bytes(dec)), dec);
return mi_uint4korr(pos);
}
+
+bool Field_timestamp_hires::val_native(Native *to)
+{
+ DBUG_ASSERT(marked_for_read());
+ struct timeval tm;
+ tm.tv_sec= mi_uint4korr(ptr);
+ tm.tv_usec= (ulong) sec_part_unshift(read_bigendian(ptr+4, sec_part_bytes(dec)), dec);
+ return Timestamp_or_zero_datetime(Timestamp(tm), tm.tv_sec == 0).
+ to_native(to, dec);
+}
+
+
double Field_timestamp_with_dec::val_real(void)
{
MYSQL_TIME ltime;
- if (get_date(&ltime, TIME_NO_ZERO_DATE))
+ if (get_date(&ltime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd())))
return 0;
return ltime.year * 1e10 + ltime.month * 1e8 +
@@ -5478,29 +5544,17 @@ double Field_timestamp_with_dec::val_real(void)
my_decimal *Field_timestamp_with_dec::val_decimal(my_decimal *d)
{
MYSQL_TIME ltime;
- get_date(&ltime, 0);
+ get_date(&ltime, date_mode_t(0));
return TIME_to_my_decimal(&ltime, d);
}
int Field_timestamp::store_decimal(const my_decimal *d)
{
- ulonglong nr;
- ulong sec_part;
int error;
- MYSQL_TIME ltime;
- longlong tmp;
THD *thd= get_thd();
ErrConvDecimal str(d);
-
- if (my_decimal2seconds(d, &nr, &sec_part))
- {
- tmp= -1;
- error= 2;
- }
- else
- tmp= number_to_datetime(nr, sec_part, &ltime, sql_mode_for_timestamp(thd),
- &error);
- return store_TIME_with_warning(thd, &ltime, &str, error, tmp != -1);
+ Datetime dt(thd, &error, d, Timestamp::DatetimeOptions(thd), decimals());
+ return store_TIME_with_warning(thd, &dt, &str, error);
}
int Field_timestamp_with_dec::set_time()
@@ -5508,14 +5562,15 @@ int Field_timestamp_with_dec::set_time()
THD *thd= get_thd();
set_notnull();
// Avoid writing microseconds into binlog for FSP=0
- store_TIME(thd->query_start(), decimals() ? thd->query_start_sec_part() : 0);
+ ulong msec= decimals() ? thd->query_start_sec_part() : 0;
+ store_TIMESTAMP(Timestamp(thd->query_start(), msec).trunc(decimals()));
return 0;
}
bool Field_timestamp_with_dec::send_binary(Protocol *protocol)
{
MYSQL_TIME ltime;
- Field_timestamp::get_date(&ltime, 0);
+ Field_timestamp::get_date(&ltime, date_mode_t(0));
return protocol->store(&ltime, dec);
}
@@ -5544,19 +5599,15 @@ void Field_timestamp_with_dec::make_send_field(Send_field *field)
** MySQL-5.6 compatible TIMESTAMP(N)
**************************************************************/
-void Field_timestampf::store_TIME(my_time_t timestamp, ulong sec_part)
+void Field_timestampf::store_TIMEVAL(const timeval &tm)
{
- struct timeval tm;
- tm.tv_sec= timestamp;
- tm.tv_usec= sec_part;
- my_timeval_trunc(&tm, dec);
my_timestamp_to_binary(&tm, ptr, dec);
}
void Field_timestampf::set_max()
{
DBUG_ENTER("Field_timestampf::set_max");
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
DBUG_ASSERT(dec == TIME_SECOND_PART_DIGITS);
set_notnull();
@@ -5569,7 +5620,7 @@ void Field_timestampf::set_max()
bool Field_timestampf::is_max()
{
DBUG_ENTER("Field_timestampf::is_max");
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
DBUG_RETURN(mi_sint4korr(ptr) == TIMESTAMP_MAX_VALUE &&
mi_sint3korr(ptr + 4) == TIME_MAX_SECOND_PART);
@@ -5585,6 +5636,19 @@ my_time_t Field_timestampf::get_timestamp(const uchar *pos,
}
+bool Field_timestampf::val_native(Native *to)
+{
+ DBUG_ASSERT(marked_for_read());
+ // Check if it's '0000-00-00 00:00:00' rather than a real timestamp
+ if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0)
+ {
+ to->length(0);
+ return false;
+ }
+ return Field::val_native(to);
+}
+
+
/*************************************************************/
sql_mode_t Field_temporal::can_handle_sql_mode_dependency_on_store() const
{
@@ -5592,16 +5656,16 @@ sql_mode_t Field_temporal::can_handle_sql_mode_dependency_on_store() const
}
-uint Field_temporal::is_equal(Create_field *new_field)
+bool Field_temporal::is_equal(const Column_definition &new_field) const
{
- return new_field->type_handler() == type_handler() &&
- new_field->length == max_display_length();
+ return new_field.type_handler() == type_handler() &&
+ new_field.length == max_display_length();
}
void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level,
const ErrConv *str, int was_cut,
- timestamp_type ts_type)
+ const char *typestr)
{
/*
error code logic:
@@ -5614,9 +5678,9 @@ void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level,
a DATE field and non-zero time part is thrown away.
*/
if (was_cut & MYSQL_TIME_WARN_TRUNCATED)
- set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED, str, ts_type, 1);
+ set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED, str, typestr, 1);
if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE)
- set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, ts_type, 1);
+ set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, typestr, 1);
}
@@ -5630,107 +5694,68 @@ void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level,
3 Datetime value that was cut (warning level NOTE)
This is used by opt_range.cc:get_mm_leaf().
*/
-int Field_temporal_with_date::store_TIME_with_warning(MYSQL_TIME *ltime,
- const ErrConv *str,
- int was_cut,
- int have_smth_to_conv)
+int Field_datetime::store_TIME_with_warning(const Datetime *dt,
+ const ErrConv *str,
+ int was_cut)
{
- Sql_condition::enum_warning_level trunc_level= Sql_condition::WARN_LEVEL_WARN;
- int ret= 2;
-
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
-
- if (was_cut == 0 && have_smth_to_conv == 0) // special case: zero date
- {
- was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
- }
- else if (!have_smth_to_conv)
- {
- bzero(ltime, sizeof(*ltime));
- was_cut= MYSQL_TIME_WARN_TRUNCATED;
- ret= 1;
- }
- else if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) &&
- (MYSQL_TIME_WARN_HAVE_NOTES(was_cut) ||
- (type_handler()->mysql_timestamp_type() == MYSQL_TIMESTAMP_DATE &&
- (ltime->hour || ltime->minute || ltime->second || ltime->second_part))))
- {
- trunc_level= Sql_condition::WARN_LEVEL_NOTE;
- was_cut|= MYSQL_TIME_WARN_TRUNCATED;
- ret= 3;
- }
- set_warnings(trunc_level, str, was_cut,
- type_handler()->mysql_timestamp_type());
- store_TIME(ltime);
- return was_cut ? ret : 0;
+ DBUG_ASSERT(marked_for_write_or_computed());
+ // Handle totally bad values
+ if (!dt->is_valid_datetime())
+ return store_invalid_with_warning(str, was_cut, "datetime");
+ // Store the value
+ DBUG_ASSERT(!dt->fraction_remainder(decimals()));
+ store_datetime(*dt);
+ // Caclulate return value and send warnings if needed
+ return store_TIME_return_code_with_warnings(was_cut, str, "datetime");
}
-int Field_temporal_with_date::store(const char *from, size_t len, CHARSET_INFO *cs)
+int Field_datetime::store(const char *from, size_t len, CHARSET_INFO *cs)
{
- MYSQL_TIME ltime;
- MYSQL_TIME_STATUS status;
- THD *thd= get_thd();
+ MYSQL_TIME_STATUS st;
ErrConvString str(from, len, cs);
- bool func_res= !str_to_datetime(cs, from, len, &ltime,
- sql_mode_for_dates(thd),
- &status);
- return store_TIME_with_warning(&ltime, &str, status.warnings, func_res);
+ THD *thd= get_thd();
+ Datetime dt(thd, &st, from, len, cs, Datetime::Options(thd), decimals());
+ return store_TIME_with_warning(&dt, &str, st.warnings);
}
-
-int Field_temporal_with_date::store(double nr)
+int Field_datetime::store(double nr)
{
- int error= 0;
- MYSQL_TIME ltime;
- THD *thd= get_thd();
+ int error;
ErrConvDouble str(nr);
-
- longlong tmp= double_to_datetime(nr, &ltime,
- (uint) sql_mode_for_dates(thd), &error);
- return store_TIME_with_warning(&ltime, &str, error, tmp != -1);
+ THD *thd= get_thd();
+ Datetime dt(thd, &error, nr, Datetime::Options(thd), decimals());
+ return store_TIME_with_warning(&dt, &str, error);
}
-int Field_temporal_with_date::store(longlong nr, bool unsigned_val)
+int Field_datetime::store(longlong nr, bool unsigned_val)
{
int error;
- MYSQL_TIME ltime;
- longlong tmp;
+ Longlong_hybrid tmp(nr, unsigned_val);
+ ErrConvInteger str(tmp);
THD *thd= get_thd();
- ErrConvInteger str(nr, unsigned_val);
-
- tmp= number_to_datetime(nr, 0, &ltime, sql_mode_for_dates(thd), &error);
-
- return store_TIME_with_warning(&ltime, &str, error, tmp != -1);
+ Datetime dt(&error, tmp, Datetime::Options(thd));
+ return store_TIME_with_warning(&dt, &str, error);
}
-
-int Field_temporal_with_date::store_time_dec(const MYSQL_TIME *ltime, uint dec)
+int Field_datetime::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
- int error= 0, have_smth_to_conv= 1;
+ int error;
ErrConvTime str(ltime);
- MYSQL_TIME l_time;
+ THD *thd= get_thd();
+ Datetime dt(thd, &error, ltime, Datetime::Options(thd), decimals());
+ return store_TIME_with_warning(&dt, &str, error);
+}
- if (copy_or_convert_to_datetime(get_thd(), ltime, &l_time))
- {
- /*
- Set have_smth_to_conv and error in a way to have
- store_TIME_with_warning do bzero().
- */
- have_smth_to_conv= false;
- error= MYSQL_TIME_WARN_OUT_OF_RANGE;
- }
- else
- {
- /*
- We don't perform range checking here since values stored in TIME
- structure always fit into DATETIME range.
- */
- have_smth_to_conv= !check_date(&l_time, pack_time(&l_time) != 0,
- sql_mode_for_dates(get_thd()), &error);
- }
- return store_TIME_with_warning(&l_time, &str, error, have_smth_to_conv);
+
+int Field_datetime::store_decimal(const my_decimal *d)
+{
+ int error;
+ ErrConvDecimal str(d);
+ THD *thd= get_thd();
+ Datetime tm(thd, &error, d, Datetime::Options(thd), decimals());
+ return store_TIME_with_warning(&tm, &str, error);
}
@@ -5740,14 +5765,14 @@ Field_temporal_with_date::validate_value_in_record(THD *thd,
{
DBUG_ASSERT(!is_null_in_record(record));
MYSQL_TIME ltime;
- return get_TIME(&ltime, ptr_in_record(record), sql_mode_for_dates(thd));
+ return get_TIME(&ltime, ptr_in_record(record), Datetime::Options(thd));
}
my_decimal *Field_temporal::val_decimal(my_decimal *d)
{
MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
+ if (get_date(&ltime, date_mode_t(0)))
{
bzero(&ltime, sizeof(ltime));
ltime.time_type= type_handler()->mysql_timestamp_type();
@@ -5780,26 +5805,27 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd,
const_item->field_type() != MYSQL_TYPE_TIMESTAMP) ||
const_item->decimals != decimals())
{
- Datetime dt(thd, const_item, 0);
+ Datetime::Options opt(TIME_CONV_NONE, thd);
+ Datetime dt(thd, const_item, opt, decimals());
if (!dt.is_valid_datetime())
return NULL;
/*
See comments about truncation in the same place in
Field_time::get_equal_const_item().
*/
- return new (thd->mem_root) Item_datetime_literal(thd,
- dt.get_mysql_time(),
- decimals());
+ return new (thd->mem_root) Item_datetime_literal(thd, &dt, decimals());
}
break;
case ANY_SUBST:
if (!is_temporal_type_with_date(const_item->field_type()))
{
- Datetime dt(thd, const_item, TIME_FUZZY_DATES | TIME_INVALID_DATES);
+ Datetime dt= type_handler()->field_type() == MYSQL_TYPE_TIMESTAMP ?
+ Datetime(thd, const_item, Timestamp::DatetimeOptions(thd)) :
+ Datetime(thd, const_item, Datetime::Options_cmp(thd));
if (!dt.is_valid_datetime())
return NULL;
return new (thd->mem_root)
- Item_datetime_literal_for_invalid_dates(thd, dt.get_mysql_time(),
+ Item_datetime_literal_for_invalid_dates(thd, &dt,
dt.get_mysql_time()->
second_part ?
TIME_SECOND_PART_DIGITS : 0);
@@ -5816,36 +5842,26 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd,
** In number context: HHMMSS
** Stored as a 3 byte unsigned int
****************************************************************************/
-int Field_time::store_TIME_with_warning(MYSQL_TIME *ltime,
- const ErrConv *str,
- int was_cut,
- int have_smth_to_conv)
+sql_mode_t
+Field_time::conversion_depends_on_sql_mode(THD *thd, Item *expr) const
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ return expr->time_precision(thd) > decimals() ?
+ MODE_TIME_ROUND_FRACTIONAL : 0;
+}
- if (!have_smth_to_conv)
- {
- bzero(ltime, sizeof(*ltime));
- store_TIME(ltime);
- set_warnings(Sql_condition::WARN_LEVEL_WARN, str, MYSQL_TIME_WARN_TRUNCATED);
- return 1;
- }
- if (ltime->year != 0 || ltime->month != 0)
- {
- ltime->year= ltime->month= ltime->day= 0;
- was_cut|= MYSQL_TIME_NOTE_TRUNCATED;
- }
- my_time_trunc(ltime, decimals());
- store_TIME(ltime);
- if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) &&
- MYSQL_TIME_WARN_HAVE_NOTES(was_cut))
- {
- set_warnings(Sql_condition::WARN_LEVEL_NOTE, str,
- was_cut | MYSQL_TIME_WARN_TRUNCATED);
- return 3;
- }
- set_warnings(Sql_condition::WARN_LEVEL_WARN, str, was_cut);
- return was_cut ? 2 : 0;
+
+int Field_time::store_TIME_with_warning(const Time *t,
+ const ErrConv *str, int warn)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ // Handle totally bad values
+ if (!t->is_valid_time())
+ return store_invalid_with_warning(str, warn, "time");
+ // Store the value
+ DBUG_ASSERT(!t->fraction_remainder(decimals()));
+ store_TIME(*t);
+ // Calculate return value and send warnings if needed
+ return store_TIME_return_code_with_warnings(warn, str, "time");
}
@@ -5862,88 +5878,56 @@ void Field_time::store_TIME(const MYSQL_TIME *ltime)
int Field_time::store(const char *from,size_t len,CHARSET_INFO *cs)
{
- MYSQL_TIME ltime;
- MYSQL_TIME_STATUS status;
ErrConvString str(from, len, cs);
- bool have_smth_to_conv=
- !str_to_time(cs, from, len, &ltime, sql_mode_for_dates(get_thd()),
- &status);
-
- return store_TIME_with_warning(&ltime, &str,
- status.warnings, have_smth_to_conv);
-}
-
-
-/**
- subtract a given number of days from DATETIME, return TIME
-
- optimized version of calc_time_diff()
-
- @note it might generate TIME values outside of the valid TIME range!
-*/
-static void calc_datetime_days_diff(MYSQL_TIME *ltime, long days)
-{
- long daydiff= calc_daynr(ltime->year, ltime->month, ltime->day) - days;
- ltime->year= ltime->month= 0;
- if (daydiff >=0 )
- {
- ltime->day= daydiff;
- ltime->time_type= MYSQL_TIMESTAMP_TIME;
- }
- else
- {
- longlong timediff= ((((daydiff * 24LL +
- ltime->hour) * 60LL +
- ltime->minute) * 60LL +
- ltime->second) * 1000000LL +
- ltime->second_part);
- unpack_time(timediff, ltime, MYSQL_TIMESTAMP_TIME);
- }
+ MYSQL_TIME_STATUS st;
+ THD *thd= get_thd();
+ /*
+ Unlike number-to-time conversion, we need to additionally pass
+ MODE_NO_ZERO_DATE here (if it presents in the current sql_mode):
+ SET sql_mode='STRICT_ALL_TABLES,NO_ZERO_DATE';
+ INSERT INTO t1 VALUES ('0000-00-00 00:00:00'); -- error
+ INSERT INTO t1 VALUES (0); -- ok
+ In the first INSERT we have a zero date.
+ In the second INSERT we don't have a zero date (it is just a zero time).
+ */
+ Time::Options opt(sql_mode_for_dates(thd), thd);
+ Time tm(thd, &st, from, len, cs, opt, decimals());
+ return store_TIME_with_warning(&tm, &str, st.warnings);
}
int Field_time::store_time_dec(const MYSQL_TIME *ltime, uint dec)
{
- MYSQL_TIME l_time= *ltime;
ErrConvTime str(ltime);
- int was_cut= 0;
-
- if (curdays && l_time.time_type != MYSQL_TIMESTAMP_TIME)
- calc_datetime_days_diff(&l_time, curdays);
-
- int have_smth_to_conv= !check_time_range(&l_time, decimals(), &was_cut);
- return store_TIME_with_warning(&l_time, &str, was_cut, have_smth_to_conv);
+ int warn;
+ Time tm(&warn, ltime, curdays, Time::Options(get_thd()), decimals());
+ return store_TIME_with_warning(&tm, &str, warn);
}
int Field_time::store(double nr)
{
- MYSQL_TIME ltime;
ErrConvDouble str(nr);
int was_cut;
- bool neg= nr < 0;
- if (neg)
- nr= -nr;
- int have_smth_to_conv= !number_to_time(neg, (ulonglong) nr,
- (ulong)((nr - floor(nr)) * TIME_SECOND_PART_FACTOR),
- &ltime, &was_cut);
-
- return store_TIME_with_warning(&ltime, &str, was_cut, have_smth_to_conv);
+ Time tm(get_thd(), &was_cut, nr, Time::Options(get_thd()), decimals());
+ return store_TIME_with_warning(&tm, &str, was_cut);
}
int Field_time::store(longlong nr, bool unsigned_val)
{
- MYSQL_TIME ltime;
- ErrConvInteger str(nr, unsigned_val);
+ Longlong_hybrid tmp(nr, unsigned_val);
+ ErrConvInteger str(tmp);
int was_cut;
- if (nr < 0 && unsigned_val)
- nr= 99991231235959LL + 1;
- int have_smth_to_conv= !number_to_time(nr < 0,
- (ulonglong) (nr < 0 ? -nr : nr),
- 0, &ltime, &was_cut);
-
- return store_TIME_with_warning(&ltime, &str, was_cut, have_smth_to_conv);
+ THD *thd= get_thd();
+ /*
+ Need fractional digit truncation if nr overflows to '838:59:59.999999'.
+ The constructor used below will always truncate (never round).
+ We don't need to care to overwrite the default session rounding mode
+ from HALF_UP to TRUNCATE.
+ */
+ Time tm(thd, &was_cut, tmp, Time::Options(thd), decimals());
+ return store_TIME_with_warning(&tm, &str, was_cut);
}
@@ -5971,14 +5955,14 @@ Field *Field_time::new_key_field(MEM_ROOT *root, TABLE *new_table,
double Field_time::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
uint32 j= (uint32) uint3korr(ptr);
return (double) j;
}
longlong Field_time::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
return (longlong) sint3korr(ptr);
}
@@ -5992,9 +5976,9 @@ longlong Field_time::val_int(void)
String *Field_time::val_str(String *str,
String *unused __attribute__((unused)))
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
MYSQL_TIME ltime;
- get_date(&ltime, TIME_TIME_ONLY);
+ get_date(&ltime, Datetime::Options(TIME_TIME_ONLY, get_thd()));
str->alloc(field_length + 1);
str->length(my_time_to_str(&ltime, const_cast<char*>(str->ptr()), decimals()));
str->set_charset(&my_charset_numeric);
@@ -6002,9 +5986,10 @@ String *Field_time::val_str(String *str,
}
-bool Field_time::check_zero_in_date_with_warn(ulonglong fuzzydate)
+bool Field_time::check_zero_in_date_with_warn(date_mode_t fuzzydate)
{
- if (!(fuzzydate & TIME_TIME_ONLY) && (fuzzydate & TIME_NO_ZERO_IN_DATE))
+ date_conv_mode_t tmp= date_conv_mode_t(fuzzydate);
+ if (!(tmp & TIME_TIME_ONLY) && (tmp & TIME_NO_ZERO_IN_DATE))
{
THD *thd= get_thd();
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
@@ -6024,7 +6009,7 @@ bool Field_time::check_zero_in_date_with_warn(ulonglong fuzzydate)
DATE_FORMAT(time, "%l.%i %p")
*/
-bool Field_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Field_time::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
if (check_zero_in_date_with_warn(fuzzydate))
return true;
@@ -6046,10 +6031,28 @@ bool Field_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
}
+int Field_time::store_native(const Native &value)
+{
+ Time t(value);
+ DBUG_ASSERT(t.is_valid_time());
+ store_TIME(t);
+ return 0;
+}
+
+
+bool Field_time::val_native(Native *to)
+{
+ MYSQL_TIME ltime;
+ get_date(&ltime, date_mode_t(0));
+ int warn;
+ return Time(&warn, &ltime, 0).to_native(to, decimals());
+}
+
+
bool Field_time::send_binary(Protocol *protocol)
{
MYSQL_TIME ltime;
- get_date(&ltime, TIME_TIME_ONLY);
+ get_date(&ltime, Time::Options(TIME_TIME_ONLY, get_thd()));
return protocol->store_time(&ltime, decimals());
}
@@ -6098,16 +6101,10 @@ void Field_time_hires::store_TIME(const MYSQL_TIME *ltime)
int Field_time::store_decimal(const my_decimal *d)
{
- ulonglong nr;
- ulong sec_part;
ErrConvDecimal str(d);
- MYSQL_TIME ltime;
int was_cut;
- bool neg= my_decimal2seconds(d, &nr, &sec_part);
-
- int have_smth_to_conv= !number_to_time(neg, nr, sec_part, &ltime, &was_cut);
-
- return store_TIME_with_warning(&ltime, &str, was_cut, have_smth_to_conv);
+ Time tm(get_thd(), &was_cut, d, Time::Options(get_thd()), decimals());
+ return store_TIME_with_warning(&tm, &str, was_cut);
}
@@ -6147,14 +6144,27 @@ bool Field_time::can_be_substituted_to_equal_item(const Context &ctx,
Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
Item *const_item)
{
+ /*
+ Old mode conversion from DATETIME with non-zero YYYYMMDD part
+ to TIME works very inconsistently. Possible variants:
+ - truncate the YYYYMMDD part
+ - add (MM*33+DD)*24 to hours
+ - add (MM*31+DD)*24 to hours
+ Let's disallow propagation of DATETIME with non-zero YYYYMMDD
+ as an equal constant for a TIME field.
+ */
+ Time::datetime_to_time_mode_t mode=
+ (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) ?
+ Time::DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY :
+ Time::DATETIME_TO_TIME_MINUS_CURRENT_DATE;
+
switch (ctx.subst_constraint()) {
case ANY_SUBST:
if (const_item->field_type() != MYSQL_TYPE_TIME)
{
- MYSQL_TIME ltime;
// Get the value of const_item with conversion from DATETIME to TIME
- ulonglong fuzzydate= Time::comparison_flags_for_get_date();
- if (const_item->get_time_with_conversion(thd, &ltime, fuzzydate))
+ Time tm(get_thd(), const_item, Time::Options_cmp(thd, mode));
+ if (!tm.is_valid_time())
return NULL;
/*
Replace a DATE/DATETIME constant to a TIME constant:
@@ -6166,8 +6176,9 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
(assuming CURRENT_DATE is '2015-08-30'
*/
- return new (thd->mem_root) Item_time_literal(thd, &ltime,
- ltime.second_part ?
+ return new (thd->mem_root) Item_time_literal(thd, &tm,
+ tm.get_mysql_time()->
+ second_part ?
TIME_SECOND_PART_DIGITS :
0);
}
@@ -6176,9 +6187,6 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
if (const_item->field_type() != MYSQL_TYPE_TIME ||
const_item->decimals != decimals())
{
- MYSQL_TIME ltime;
- if (const_item->get_time_with_conversion(thd, &ltime, TIME_TIME_ONLY))
- return NULL;
/*
Note, the value returned in "ltime" can have more fractional
digits that decimals(). The Item_time_literal constructor will
@@ -6193,7 +6201,11 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
The optimized WHERE will return with "Impossible WHERE", without
having to do the full table scan.
*/
- return new (thd->mem_root) Item_time_literal(thd, &ltime, decimals());
+ Time tm(thd, const_item, Time::Options(TIME_TIME_ONLY, thd, mode),
+ decimals());
+ if (!tm.is_valid_time())
+ return NULL;
+ return new (thd->mem_root) Item_time_literal(thd, &tm, decimals());
}
break;
}
@@ -6203,22 +6215,22 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
longlong Field_time_with_dec::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
MYSQL_TIME ltime;
- get_date(&ltime, TIME_TIME_ONLY);
+ get_date(&ltime, Time::Options(TIME_TIME_ONLY, get_thd()));
longlong val= TIME_to_ulonglong_time(&ltime);
return ltime.neg ? -val : val;
}
double Field_time_with_dec::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
MYSQL_TIME ltime;
- get_date(&ltime, TIME_TIME_ONLY);
+ get_date(&ltime, Time::Options(TIME_TIME_ONLY, get_thd()));
return TIME_to_double(&ltime);
}
-bool Field_time_hires::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Field_time_hires::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
if (check_zero_in_date_with_warn(fuzzydate))
return true;
@@ -6270,7 +6282,7 @@ void Field_timef::store_TIME(const MYSQL_TIME *ltime)
my_time_packed_to_binary(tmp, ptr, dec);
}
-bool Field_timef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Field_timef::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
if (check_zero_in_date_with_warn(fuzzydate))
return true;
@@ -6279,6 +6291,33 @@ bool Field_timef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
return false;
}
+
+longlong Field_timef::val_time_packed(THD *thd)
+{
+ DBUG_ASSERT(marked_for_read());
+ longlong tmp= my_time_packed_from_binary(ptr, dec);
+ MYSQL_TIME ltime;
+ TIME_from_longlong_time_packed(&ltime, tmp);
+ return pack_time(&ltime);
+}
+
+
+int Field_timef::store_native(const Native &value)
+{
+ DBUG_ASSERT(value.length() == my_time_binary_length(dec));
+ DBUG_ASSERT(Time(value).is_valid_time());
+ memcpy(ptr, value.ptr(), value.length());
+ return 0;
+}
+
+
+bool Field_timef::val_native(Native *to)
+{
+ uint32 binlen= my_time_binary_length(dec);
+ return to->copy((const char*) ptr, binlen);
+}
+
+
/****************************************************************************
** year type
** Save in a byte the year 0, 1901->2155
@@ -6287,7 +6326,7 @@ bool Field_timef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
int Field_year::store(const char *from, size_t len,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
char *end;
int error;
longlong nr= cs->cset->strntoull10rnd(cs, from, len, 0, &end, &error);
@@ -6335,7 +6374,7 @@ int Field_year::store(double nr)
int Field_year::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155)
{
*ptr= 0;
@@ -6360,13 +6399,14 @@ int Field_year::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
if (Field_year::store(ltime->year, 0))
return 1;
- set_datetime_warning(WARN_DATA_TRUNCATED, &str, ltime->time_type, 1);
+ const char *typestr= Temporal::type_name_by_timestamp_type(ltime->time_type);
+ set_datetime_warning(WARN_DATA_TRUNCATED, &str, typestr, 1);
return 0;
}
bool Field_year::send_binary(Protocol *protocol)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
ulonglong tmp= Field_year::val_int();
return protocol->store_short(tmp);
}
@@ -6380,7 +6420,7 @@ double Field_year::val_real(void)
longlong Field_year::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
DBUG_ASSERT(field_length == 2 || field_length == 4);
int tmp= (int) ptr[0];
if (field_length != 4)
@@ -6404,12 +6444,13 @@ String *Field_year::val_str(String *val_buffer,
}
-bool Field_year::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+bool Field_year::get_date(MYSQL_TIME *ltime,date_mode_t fuzzydate)
{
int tmp= (int) ptr[0];
if (tmp || field_length != 4)
tmp+= 1900;
- return int_to_datetime_with_warn(false, tmp * 10000,
+ return int_to_datetime_with_warn(get_thd(),
+ Longlong_hybrid(tmp * 10000, true),
ltime, fuzzydate, table->s, field_name.str);
}
@@ -6422,6 +6463,71 @@ void Field_year::sql_type(String &res) const
}
+/*****************************************************************************/
+
+int Field_date_common::store_TIME_with_warning(const Datetime *dt,
+ const ErrConv *str,
+ int was_cut)
+{
+ DBUG_ASSERT(marked_for_write_or_computed());
+ // Handle totally bad values
+ if (!dt->is_valid_datetime())
+ return store_invalid_with_warning(str, was_cut, "date");
+ // Store the value
+ if (!dt->hhmmssff_is_zero())
+ was_cut|= MYSQL_TIME_NOTE_TRUNCATED;
+ store_datetime(*dt);
+ // Caclulate return value and send warnings if needed
+ return store_TIME_return_code_with_warnings(was_cut, str, "date");
+}
+
+int Field_date_common::store(const char *from, size_t len, CHARSET_INFO *cs)
+{
+ MYSQL_TIME_STATUS st;
+ ErrConvString str(from, len, cs);
+ THD *thd= get_thd();
+ Datetime dt(thd, &st, from, len, cs, Date::Options(thd), 0);
+ return store_TIME_with_warning(&dt, &str, st.warnings);
+}
+
+int Field_date_common::store(double nr)
+{
+ int error;
+ ErrConvDouble str(nr);
+ THD *thd= get_thd();
+ Datetime dt(thd, &error, nr, Date::Options(thd), 0);
+ return store_TIME_with_warning(&dt, &str, error);
+}
+
+int Field_date_common::store(longlong nr, bool unsigned_val)
+{
+ int error;
+ Longlong_hybrid tmp(nr, unsigned_val);
+ ErrConvInteger str(tmp);
+ THD *thd= get_thd();
+ Datetime dt(&error, tmp, Date::Options(thd));
+ return store_TIME_with_warning(&dt, &str, error);
+}
+
+int Field_date_common::store_time_dec(const MYSQL_TIME *ltime, uint dec)
+{
+ int error;
+ ErrConvTime str(ltime);
+ THD *thd= get_thd();
+ Datetime dt(thd, &error, ltime, Date::Options(thd), 0);
+ return store_TIME_with_warning(&dt, &str, error);
+}
+
+int Field_date_common::store_decimal(const my_decimal *d)
+{
+ int error;
+ ErrConvDecimal str(d);
+ THD *thd= get_thd();
+ Datetime tm(thd, &error, d, Date::Options(thd), 0);
+ return store_TIME_with_warning(&tm, &str, error);
+}
+
+
/****************************************************************************
** date type
** In string context: YYYY-MM-DD
@@ -6429,7 +6535,7 @@ void Field_year::sql_type(String &res) const
** Stored as a 4 byte unsigned int
****************************************************************************/
-void Field_date::store_TIME(MYSQL_TIME *ltime)
+void Field_date::store_TIME(const MYSQL_TIME *ltime)
{
uint tmp= ltime->year*10000L + ltime->month*100+ltime->day;
int4store(ptr,tmp);
@@ -6448,7 +6554,7 @@ bool Field_date::send_binary(Protocol *protocol)
double Field_date::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
int32 j;
j=sint4korr(ptr);
return (double) (uint32) j;
@@ -6457,7 +6563,7 @@ double Field_date::val_real(void)
longlong Field_date::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
int32 j;
j=sint4korr(ptr);
return (longlong) (uint32) j;
@@ -6465,9 +6571,9 @@ longlong Field_date::val_int(void)
bool Field_date::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
- ulonglong fuzzydate) const
+ date_mode_t fuzzydate) const
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
int32 tmp= sint4korr(pos);
ltime->year= (int) ((uint32) tmp/10000L % 10000);
ltime->month= (int) ((uint32) tmp/100 % 100);
@@ -6482,7 +6588,7 @@ String *Field_date::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
MYSQL_TIME ltime;
- get_TIME(&ltime, ptr, 0);
+ get_TIME(&ltime, ptr, date_mode_t(0));
val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
uint length= (uint) my_date_to_str(&ltime,
const_cast<char*>(val_buffer->ptr()));
@@ -6522,7 +6628,7 @@ void Field_date::sql_type(String &res) const
** In number context: YYYYMMDD
****************************************************************************/
-void Field_newdate::store_TIME(MYSQL_TIME *ltime)
+void Field_newdate::store_TIME(const MYSQL_TIME *ltime)
{
uint tmp= ltime->year*16*32 + ltime->month*32+ltime->day;
int3store(ptr,tmp);
@@ -6532,21 +6638,21 @@ void Field_newdate::store_TIME(MYSQL_TIME *ltime)
bool Field_newdate::send_binary(Protocol *protocol)
{
MYSQL_TIME tm;
- Field_newdate::get_date(&tm,0);
+ Field_newdate::get_date(&tm, date_mode_t(0));
return protocol->store_date(&tm);
}
double Field_newdate::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
return (double) Field_newdate::val_int();
}
longlong Field_newdate::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
ulong j= uint3korr(ptr);
j= (j % 32L)+(j / 32L % 16L)*100L + (j/(16L*32L))*10000L;
return (longlong) j;
@@ -6556,7 +6662,7 @@ longlong Field_newdate::val_int(void)
String *Field_newdate::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
val_buffer->alloc(field_length);
val_buffer->length(field_length);
uint32 tmp=(uint32) uint3korr(ptr);
@@ -6584,9 +6690,9 @@ String *Field_newdate::val_str(String *val_buffer,
bool Field_newdate::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
- ulonglong fuzzydate) const
+ date_mode_t fuzzydate) const
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
uint32 tmp=(uint32) uint3korr(pos);
ltime->day= tmp & 31;
ltime->month= (tmp >> 5) & 15;
@@ -6597,6 +6703,14 @@ bool Field_newdate::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
}
+longlong Field_newdate::val_datetime_packed(THD *thd)
+{
+ MYSQL_TIME ltime;
+ Field_newdate::get_date(&ltime, date_mode_t(0));
+ return pack_time(&ltime);
+}
+
+
int Field_newdate::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
uint32 a,b;
@@ -6627,8 +6741,14 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx,
case ANY_SUBST:
if (!is_temporal_type_with_date(const_item->field_type()))
{
- // Get the value of const_item with conversion from TIME to DATETIME
- Datetime dt(thd, const_item, TIME_FUZZY_DATES | TIME_INVALID_DATES);
+ /*
+ DATE is compared to DATETIME-alike non-temporal values
+ (such as VARCHAR, DECIMAL) as DATETIME, e.g.:
+ WHERE date_column=20010101235959.0000009
+ So here we convert the constant to DATETIME normally.
+ In case if TIME_ROUND_FRACTIONAL is enabled, nanoseconds will round.
+ */
+ Datetime dt(thd, const_item, Datetime::Options_cmp(thd));
if (!dt.is_valid_datetime())
return NULL;
/*
@@ -6644,21 +6764,28 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx,
*/
if (!dt.hhmmssff_is_zero())
return new (thd->mem_root)
- Item_datetime_literal_for_invalid_dates(thd, dt.get_mysql_time(),
+ Item_datetime_literal_for_invalid_dates(thd, &dt,
dt.get_mysql_time()->
second_part ?
TIME_SECOND_PART_DIGITS : 0);
- return new (thd->mem_root)
- Item_date_literal_for_invalid_dates(thd, Date(&dt).get_mysql_time());
+ Date d(&dt);
+ return new (thd->mem_root) Item_date_literal_for_invalid_dates(thd, &d);
}
break;
case IDENTITY_SUBST:
if (const_item->field_type() != MYSQL_TYPE_DATE)
{
- Date d(thd, const_item, 0);
- if (!d.is_valid_date())
+ /*
+ DATE is compared to non-temporal as DATETIME.
+ We need to convert to DATETIME first, taking into account the
+ current session rounding mode (even though this is IDENTITY_SUBSTS!),
+ then convert the result to DATE.
+ */
+ Datetime dt(thd, const_item, Datetime::Options(TIME_CONV_NONE, thd));
+ if (!dt.is_valid_datetime())
return NULL;
- return new (thd->mem_root) Item_date_literal(thd, d.get_mysql_time());
+ Date d(&dt);
+ return new (thd->mem_root) Item_date_literal(thd, &d);
}
break;
}
@@ -6673,16 +6800,25 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx,
** Stored as a 8 byte unsigned int. Should sometimes be change to a 6 byte int.
****************************************************************************/
-void Field_datetime::store_TIME(MYSQL_TIME *ltime)
+void Field_datetime::store_TIME(const MYSQL_TIME *ltime)
{
ulonglong tmp= TIME_to_ulonglong_datetime(ltime);
int8store(ptr,tmp);
}
+
+sql_mode_t
+Field_datetime::conversion_depends_on_sql_mode(THD *thd, Item *expr) const
+{
+ return expr->datetime_precision(thd) > decimals() ?
+ MODE_TIME_ROUND_FRACTIONAL : 0;
+}
+
+
bool Field_datetime::send_binary(Protocol *protocol)
{
MYSQL_TIME tm;
- Field_datetime::get_date(&tm, 0);
+ Field_datetime::get_date(&tm, date_mode_t(0));
return protocol->store(&tm, 0);
}
@@ -6694,7 +6830,7 @@ double Field_datetime::val_real(void)
longlong Field_datetime::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
longlong j;
j=sint8korr(ptr);
return j;
@@ -6707,7 +6843,7 @@ String *Field_datetime::val_str(String *val_buffer,
val_buffer->alloc(field_length);
val_buffer->length(field_length);
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
ulonglong tmp;
long part1,part2;
char *pos;
@@ -6748,9 +6884,9 @@ String *Field_datetime::val_str(String *val_buffer,
}
bool Field_datetime::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
- ulonglong fuzzydate) const
+ date_mode_t fuzzydate) const
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
longlong tmp= sint8korr(pos);
uint32 part1,part2;
part1=(uint32) (tmp/1000000LL);
@@ -6807,48 +6943,28 @@ void Field_datetime::sql_type(String &res) const
int Field_datetime::set_time()
{
THD *thd= table->in_use;
- MYSQL_TIME now_time;
- thd->variables.time_zone->gmt_sec_to_TIME(&now_time, thd->query_start());
- now_time.second_part= thd->query_start_sec_part();
set_notnull();
- store_TIME(&now_time);
- thd->time_zone_used= 1;
+ // Here we always truncate (not round), no matter what sql_mode is
+ if (decimals())
+ store_datetime(Datetime(thd, Timeval(thd->query_start(),
+ thd->query_start_sec_part())
+ ).trunc(decimals()));
+ else
+ store_datetime(Datetime(thd, Timeval(thd->query_start(), 0)));
return 0;
}
-void Field_datetime_hires::store_TIME(MYSQL_TIME *ltime)
+void Field_datetime_hires::store_TIME(const MYSQL_TIME *ltime)
{
ulonglong packed= sec_part_shift(pack_time(ltime), dec);
store_bigendian(packed, ptr, Field_datetime_hires::pack_length());
}
-int Field_temporal_with_date::store_decimal(const my_decimal *d)
-{
- ulonglong nr;
- ulong sec_part;
- int error;
- MYSQL_TIME ltime;
- longlong tmp;
- THD *thd= get_thd();
- ErrConvDecimal str(d);
-
- if (my_decimal2seconds(d, &nr, &sec_part))
- {
- tmp= -1;
- error= 2;
- }
- else
- tmp= number_to_datetime(nr, sec_part, &ltime, sql_mode_for_dates(thd),
- &error);
-
- return store_TIME_with_warning(&ltime, &str, error, tmp != -1);
-}
-
bool Field_datetime_with_dec::send_binary(Protocol *protocol)
{
MYSQL_TIME ltime;
- get_date(&ltime, 0);
+ get_date(&ltime, date_mode_t(0));
return protocol->store(&ltime, dec);
}
@@ -6856,14 +6972,14 @@ bool Field_datetime_with_dec::send_binary(Protocol *protocol)
double Field_datetime_with_dec::val_real(void)
{
MYSQL_TIME ltime;
- get_date(&ltime, 0);
+ get_date(&ltime, date_mode_t(0));
return TIME_to_double(&ltime);
}
longlong Field_datetime_with_dec::val_int(void)
{
MYSQL_TIME ltime;
- get_date(&ltime, 0);
+ get_date(&ltime, date_mode_t(0));
return TIME_to_ulonglong_datetime(&ltime);
}
@@ -6872,7 +6988,7 @@ String *Field_datetime_with_dec::val_str(String *str,
String *unused __attribute__((unused)))
{
MYSQL_TIME ltime;
- get_date(&ltime, 0);
+ get_date(&ltime, date_mode_t(0));
str->alloc(field_length+1);
str->length(field_length);
my_datetime_to_str(&ltime, (char*) str->ptr(), dec);
@@ -6882,9 +6998,9 @@ String *Field_datetime_with_dec::val_str(String *str,
bool Field_datetime_hires::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
- ulonglong fuzzydate) const
+ date_mode_t fuzzydate) const
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
ulonglong packed= read_bigendian(pos, Field_datetime_hires::pack_length());
unpack_time(sec_part_unshift(packed, dec), ltime, MYSQL_TIMESTAMP_DATETIME);
return validate_MMDD(packed, ltime->month, ltime->day, fuzzydate);
@@ -6915,22 +7031,31 @@ int Field_datetimef::reset()
return 0;
}
-void Field_datetimef::store_TIME(MYSQL_TIME *ltime)
+void Field_datetimef::store_TIME(const MYSQL_TIME *ltime)
{
- my_time_trunc(ltime, decimals());
longlong tmp= TIME_to_longlong_datetime_packed(ltime);
my_datetime_packed_to_binary(tmp, ptr, dec);
}
bool Field_datetimef::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
- ulonglong fuzzydate) const
+ date_mode_t fuzzydate) const
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
longlong tmp= my_datetime_packed_from_binary(pos, dec);
TIME_from_longlong_datetime_packed(ltime, tmp);
return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
}
+longlong Field_datetimef::val_datetime_packed(THD *thd)
+{
+ DBUG_ASSERT(marked_for_read());
+ longlong tmp= my_datetime_packed_from_binary(ptr, dec);
+ MYSQL_TIME ltime;
+ TIME_from_longlong_datetime_packed(&ltime, tmp);
+ return pack_time(&ltime);
+}
+
+
/****************************************************************************
** string type
** A string may be varchar or binary
@@ -6975,6 +7100,7 @@ Field_longstr::check_string_copy_error(const String_copier *copier,
/* Ignore errors from internal expressions */
if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION)
{
+ DBUG_ASSERT(sizeof(tmp) >= convert_to_printable_required_length(6));
convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
set_warning_truncated_wrong_value("string", tmp);
}
@@ -7033,7 +7159,7 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end,
int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
uint copy_length;
int rc;
@@ -7079,7 +7205,7 @@ int Field_str::store(longlong nr, bool unsigned_val)
int Field_str::store(double nr)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
uint local_char_length= MY_MIN(sizeof(buff),
field_length / field_charset->mbmaxlen);
@@ -7100,25 +7226,20 @@ int Field_str::store(double nr)
return store(buff, (uint)length, &my_charset_numeric);
}
-uint Field::is_equal(Create_field *new_field)
-{
- return new_field->type_handler() == type_handler();
-}
-
-
-uint Field_str::is_equal(Create_field *new_field)
+bool Field_string::is_equal(const Column_definition &new_field) const
{
- return new_field->type_handler() == type_handler() &&
- new_field->charset == field_charset &&
- new_field->length == max_display_length();
+ DBUG_ASSERT(!compression_method());
+ return new_field.type_handler() == type_handler() &&
+ new_field.char_length == char_length() &&
+ new_field.charset == field_charset &&
+ new_field.length == max_display_length();
}
int Field_longstr::store_decimal(const my_decimal *d)
{
- char buff[DECIMAL_MAX_STR_LENGTH+1];
- String str(buff, sizeof(buff), &my_charset_numeric);
- my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str);
+ StringBuffer<DECIMAL_MAX_STR_LENGTH+1> str;
+ d->to_string(&str);
return store(str.ptr(), str.length(), str.charset());
}
@@ -7202,7 +7323,7 @@ Field_string::Warn_filter_string::Warn_filter_string(const THD *thd,
double Field_string::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
return Converter_strntod_with_warn(get_thd(),
Warn_filter_string(thd, this),
@@ -7214,7 +7335,7 @@ double Field_string::val_real(void)
longlong Field_string::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
return Converter_strntoll_with_warn(thd, Warn_filter_string(thd, this),
Field_string::charset(),
@@ -7238,7 +7359,7 @@ sql_mode_t Field_string::can_handle_sql_mode_dependency_on_store() const
String *Field_string::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
/* See the comment for Field_long::store(long long) */
DBUG_ASSERT(!table || table->in_use == current_thd);
size_t length;
@@ -7256,7 +7377,7 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)),
my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
Converter_str2my_decimal_with_warn(thd,
Warn_filter_string(thd, this),
@@ -7347,11 +7468,12 @@ void Field_string::sql_type(String &res) const
size_t length;
length= cs->cset->snprintf(cs,(char*) res.ptr(),
- res.alloced_length(), "%s(%d)",
+ res.alloced_length(), "%s(%d)%s",
(type() == MYSQL_TYPE_VAR_STRING ?
(has_charset() ? "varchar" : "varbinary") :
(has_charset() ? "char" : "binary")),
- (int) field_length / charset()->mbmaxlen);
+ (int) field_length / charset()->mbmaxlen,
+ type() == MYSQL_TYPE_VAR_STRING ? "/*old*/" : "");
res.length(length);
if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
has_charset() && (charset()->state & MY_CS_BINSORT))
@@ -7621,7 +7743,7 @@ bool Field_varstring::memcpy_field_possible(const Field *from) const
int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
uint copy_length;
int rc;
@@ -7638,7 +7760,7 @@ int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs)
double Field_varstring::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
return Converter_strntod_with_warn(thd, Warn_filter(thd),
Field_varstring::charset(),
@@ -7649,7 +7771,7 @@ double Field_varstring::val_real(void)
longlong Field_varstring::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
return Converter_strntoll_with_warn(thd, Warn_filter(thd),
Field_varstring::charset(),
@@ -7661,7 +7783,7 @@ longlong Field_varstring::val_int(void)
String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
val_ptr->set((const char*) get_data(), get_length(), field_charset);
return val_ptr;
}
@@ -7669,7 +7791,7 @@ String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
Converter_str2my_decimal_with_warn(thd, Warn_filter(thd),
E_DEC_FATAL_ERROR,
@@ -8056,20 +8178,13 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table,
return res;
}
-uint Field_varstring::is_equal(Create_field *new_field)
+bool Field_varstring::is_equal(const Column_definition &new_field) const
{
- if (new_field->type_handler() == type_handler() &&
- new_field->charset == field_charset &&
- !new_field->compression_method() == !compression_method())
- {
- if (new_field->length == field_length)
- return IS_EQUAL_YES;
- if (new_field->length > field_length &&
- ((new_field->length <= 255 && field_length <= 255) ||
- (new_field->length > 255 && field_length > 255)))
- return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer variable length
- }
- return IS_EQUAL_NO;
+ return new_field.type_handler() == type_handler() &&
+ new_field.length == field_length &&
+ new_field.char_length == char_length() &&
+ !new_field.compression_method() == !compression_method() &&
+ new_field.charset == field_charset;
}
@@ -8225,7 +8340,7 @@ String *Field_longstr::uncompress(String *val_buffer, String *val_ptr,
int Field_varstring_compressed::store(const char *from, size_t length,
CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
uint compressed_length;
int rc= compress((char*) get_data(), field_length, from, (uint) length,
Field_varstring_compressed::max_display_length(),
@@ -8238,14 +8353,14 @@ int Field_varstring_compressed::store(const char *from, size_t length,
String *Field_varstring_compressed::val_str(String *val_buffer, String *val_ptr)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
return uncompress(val_buffer, val_ptr, get_data(), get_length());
}
double Field_varstring_compressed::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
String buf;
val_str(&buf, &buf);
@@ -8256,7 +8371,7 @@ double Field_varstring_compressed::val_real(void)
longlong Field_varstring_compressed::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
String buf;
val_str(&buf, &buf);
@@ -8351,7 +8466,7 @@ int Field_blob::copy_value(Field_blob *from)
int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
size_t copy_length, new_length;
uint copy_len;
char *tmp;
@@ -8447,7 +8562,7 @@ oom_error:
double Field_blob::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
char *blob;
memcpy(&blob, ptr+packlength, sizeof(char*));
if (!blob)
@@ -8461,7 +8576,7 @@ double Field_blob::val_real(void)
longlong Field_blob::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
char *blob;
memcpy(&blob, ptr+packlength, sizeof(char*));
if (!blob)
@@ -8476,7 +8591,7 @@ longlong Field_blob::val_int(void)
String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
char *blob;
memcpy(&blob, ptr+packlength, sizeof(char*));
if (!blob)
@@ -8489,7 +8604,7 @@ String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
my_decimal *Field_blob::val_decimal(my_decimal *decimal_value)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
const char *blob;
size_t length;
memcpy(&blob, ptr+packlength, sizeof(const uchar*));
@@ -8843,19 +8958,45 @@ uint Field_blob::max_packed_col_length(uint max_length)
again an already compressed field just because compression method changes.
*/
-uint Field_blob::is_equal(Create_field *new_field)
+bool Field_blob::is_equal(const Column_definition &new_field) const
+{
+ return new_field.type_handler() == type_handler() &&
+ !new_field.compression_method() == !compression_method() &&
+ new_field.pack_length == pack_length() &&
+ new_field.charset == field_charset;
+}
+
+
+void Field_blob::make_send_field(Send_field *field)
+{
+ /*
+ Historically all BLOB variant Fields are displayed as MYSQL_TYPE_BLOB
+ in the result set metadata. Note, Item can work differently and
+ display the exact BLOB type, such as
+ MYSQL_TYPE_{TINY_BLOB|BLOB|MEDIUM_BLOB|LONG_BLOB}.
+ QQ: this should be made consistent eventually.
+ */
+ Field_longstr::make_send_field(field);
+ field->set_handler(&type_handler_blob);
+}
+
+
+bool Field_blob::make_empty_rec_store_default_value(THD *thd, Item *item)
{
- return new_field->type_handler() == type_handler() &&
- new_field->charset == field_charset &&
- new_field->pack_length == pack_length() &&
- !new_field->compression_method() == !compression_method();
+ DBUG_ASSERT(flags & BLOB_FLAG);
+ int res= item->save_in_field(this, true);
+ DBUG_ASSERT(res != 3); // Field_blob never returns 3
+ if (res)
+ return true; // E.g. truncation happened
+ reset(); // Clear the pointer to a String, it should not be written to frm
+ return false;
}
int Field_blob_compressed::store(const char *from, size_t length,
CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
uint compressed_length;
uint max_length= max_data_length();
uint to_length= (uint) MY_MIN(max_length,
@@ -8882,14 +9023,14 @@ oom:
String *Field_blob_compressed::val_str(String *val_buffer, String *val_ptr)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
return uncompress(val_buffer, val_ptr, get_ptr(), get_length());
}
double Field_blob_compressed::val_real(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
String buf;
val_str(&buf, &buf);
@@ -8900,7 +9041,7 @@ double Field_blob_compressed::val_real(void)
longlong Field_blob_compressed::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
THD *thd= get_thd();
String buf;
val_str(&buf, &buf);
@@ -9139,16 +9280,16 @@ Field::geometry_type Field_geom::geometry_type_merge(geometry_type a,
}
-uint Field_geom::is_equal(Create_field *new_field)
+bool Field_geom::is_equal(const Column_definition &new_field) const
{
- return new_field->type_handler() == type_handler() &&
+ return new_field.type_handler() == type_handler() &&
/*
- Allow ALTER..INPLACE to supertype (GEOMETRY),
e.g. POINT to GEOMETRY or POLYGON to GEOMETRY.
- Allow ALTER..INPLACE to the same geometry type: POINT -> POINT
*/
- (new_field->geom_type == geom_type ||
- new_field->geom_type == GEOM_GEOMETRY);
+ (new_field.geom_type == geom_type ||
+ new_field.geom_type == GEOM_GEOMETRY);
}
@@ -9220,7 +9361,7 @@ void Field_enum::store_type(ulonglong value)
int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int err= 0;
char buff[STRING_BUFFER_USUAL_SIZE];
String tmpstr(buff,sizeof(buff), &my_charset_bin);
@@ -9272,7 +9413,7 @@ int Field_enum::store(double nr)
int Field_enum::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
if ((ulonglong) nr > typelib->count || nr == 0)
{
@@ -9296,7 +9437,7 @@ double Field_enum::val_real(void)
longlong Field_enum::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
return read_lowendian(ptr, packlength);
}
@@ -9403,7 +9544,7 @@ Field *Field_enum::make_new_field(MEM_ROOT *root, TABLE *new_table,
int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
bool got_warning= 0;
int err= 0;
char *not_used;
@@ -9443,7 +9584,7 @@ int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs)
int Field_set::store(longlong nr, bool unsigned_val)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int error= 0;
ulonglong max_nr;
@@ -9584,22 +9725,22 @@ bool Field_enum::eq_def(const Field *field) const
alteration purposes. Fields are equal if they retain the same
pack length and if new members are added to the end of the list.
- @return IS_EQUAL_YES if fields are compatible.
- IS_EQUAL_NO otherwise.
+ @return true if fields are compatible.
+ false otherwise.
*/
-uint Field_enum::is_equal(Create_field *new_field)
+bool Field_enum::is_equal(const Column_definition &new_field) const
{
- TYPELIB *values= new_field->interval;
+ TYPELIB *values= new_field.interval;
/*
The fields are compatible if they have the same flags,
type, charset and have the same underlying length.
*/
- if (new_field->type_handler() != type_handler() ||
- new_field->charset != field_charset ||
- new_field->pack_length != pack_length())
- return IS_EQUAL_NO;
+ if (new_field.type_handler() != type_handler() ||
+ new_field.charset != field_charset ||
+ new_field.pack_length != pack_length())
+ return false;
/*
Changing the definition of an ENUM or SET column by adding a new
@@ -9607,13 +9748,13 @@ uint Field_enum::is_equal(Create_field *new_field)
values only alters table metadata and not table data.
*/
if (typelib->count > values->count)
- return IS_EQUAL_NO;
+ return false;
/* Check whether there are modification before the end. */
- if (! compare_type_names(field_charset, typelib, new_field->interval))
- return IS_EQUAL_NO;
+ if (! compare_type_names(field_charset, typelib, new_field.interval))
+ return false;
- return IS_EQUAL_YES;
+ return true;
}
@@ -9659,14 +9800,35 @@ bool Field_num::eq_def(const Field *field) const
and retain the same pack length.
*/
-uint Field_num::is_equal(Create_field *new_field)
+bool Field_num::is_equal(const Column_definition &new_field) const
{
- return ((new_field->type_handler() == type_handler()) &&
- ((new_field->flags & UNSIGNED_FLAG) ==
- (uint) (flags & UNSIGNED_FLAG)) &&
- ((new_field->flags & AUTO_INCREMENT_FLAG) ==
- (uint) (flags & AUTO_INCREMENT_FLAG)) &&
- (new_field->pack_length == pack_length()));
+ if (((new_field.flags & UNSIGNED_FLAG) != (flags & UNSIGNED_FLAG)) ||
+ ((new_field.flags & AUTO_INCREMENT_FLAG) > (flags & AUTO_INCREMENT_FLAG)))
+ return false;
+
+ const Type_handler *th= type_handler(), *new_th = new_field.type_handler();
+
+ if (th == new_th && new_field.pack_length == pack_length())
+ return true;
+ /* FIXME: Test and consider returning true for the following:
+ TINYINT UNSIGNED to BIT(8)
+ SMALLINT UNSIGNED to BIT(16)
+ MEDIUMINT UNSIGNED to BIT(24)
+ INT UNSIGNED to BIT(32)
+ BIGINT UNSIGNED to BIT(64)
+
+ BIT(1..7) to TINYINT, or BIT(1..8) to TINYINT UNSIGNED
+ BIT(9..15) to SMALLINT, or BIT(9..16) to SMALLINT UNSIGNED
+ BIT(17..23) to MEDIUMINT, or BIT(17..24) to MEDIUMINT UNSIGNED
+ BIT(25..31) to INT, or BIT(25..32) to INT UNSIGNED
+ BIT(57..63) to BIGINT, or BIT(57..64) to BIGINT UNSIGNED
+
+ Note: InnoDB stores integers in big-endian format, and BIT appears
+ to use big-endian format. For storage engines that use little-endian
+ format for integers, we can only return true for the TINYINT
+ conversion. */
+
+ return false;
}
@@ -9813,16 +9975,16 @@ Field *Field_bit::new_key_field(MEM_ROOT *root, TABLE *new_table,
}
-uint Field_bit::is_equal(Create_field *new_field)
+bool Field_bit::is_equal(const Column_definition &new_field) const
{
- return new_field->type_handler() == type_handler() &&
- new_field->length == max_display_length();
+ return new_field.type_handler() == type_handler() &&
+ new_field.length == max_display_length();
}
int Field_bit::store(const char *from, size_t length, CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int delta;
for (; length && !*from; from++, length--) // skip left 0's
@@ -9899,7 +10061,7 @@ double Field_bit::val_real(void)
longlong Field_bit::val_int(void)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
ulonglong bits= 0;
if (bit_len)
{
@@ -9924,7 +10086,7 @@ longlong Field_bit::val_int(void)
String *Field_bit::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
char buff[sizeof(longlong)];
uint length= MY_MIN(pack_length(), sizeof(longlong));
ulonglong bits= val_int();
@@ -9940,7 +10102,7 @@ String *Field_bit::val_str(String *val_buffer,
my_decimal *Field_bit::val_decimal(my_decimal *deciaml_value)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(marked_for_read());
int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 1, deciaml_value);
return deciaml_value;
}
@@ -9992,7 +10154,7 @@ int Field_bit::key_cmp(const uchar *str, uint length)
}
-int Field_bit::cmp_offset(uint row_offset)
+int Field_bit::cmp_offset(my_ptrdiff_t row_offset)
{
if (bit_len)
{
@@ -10258,7 +10420,7 @@ Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg,
int Field_bit_as_char::store(const char *from, size_t length, CHARSET_INFO *cs)
{
- ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+ DBUG_ASSERT(marked_for_write_or_computed());
int delta;
uchar bits= (uchar) (field_length & 7);
@@ -10622,6 +10784,13 @@ bool Column_definition::fix_attributes_temporal_with_time(uint int_part_length)
}
+bool Column_definition::validate_check_constraint(THD *thd)
+{
+ return check_constraint &&
+ check_expression(check_constraint, &field_name, VCOL_CHECK_FIELD);
+}
+
+
bool Column_definition::check(THD *thd)
{
DBUG_ENTER("Column_definition::check");
@@ -10630,15 +10799,14 @@ bool Column_definition::check(THD *thd)
if (vcol_info)
{
DBUG_ASSERT(vcol_info->expr);
- vcol_info->set_field_type(real_field_type());
+ vcol_info->set_handler(type_handler());
if (check_expression(vcol_info, &field_name, vcol_info->stored_in_db
? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL))
DBUG_RETURN(TRUE);
}
- if (check_constraint &&
- check_expression(check_constraint, &field_name, VCOL_CHECK_FIELD))
- DBUG_RETURN(1);
+ if (type_handler()->Column_definition_validate_check_constraint(thd, this))
+ DBUG_RETURN(TRUE);
if (default_value)
{
@@ -10762,322 +10930,90 @@ uint pack_length_to_packflag(uint type)
}
-Field *make_field(TABLE_SHARE *share,
- MEM_ROOT *mem_root,
- uchar *ptr, uint32 field_length,
- uchar *null_pos, uchar null_bit,
- uint pack_flag,
- const Type_handler *handler,
- CHARSET_INFO *field_charset,
- Field::geometry_type geom_type, uint srid,
- Field::utype unireg_check,
- TYPELIB *interval,
- const LEX_CSTRING *field_name,
- uint32 flags)
+uint Column_definition_attributes::pack_flag_to_pack_length() const
{
- uchar *UNINIT_VAR(bit_ptr);
- uchar UNINIT_VAR(bit_offset);
+ uint type= f_packtype(pack_flag); // 0..15
+ DBUG_ASSERT(type < 16);
+ switch (type) {
+ case MYSQL_TYPE_TINY: return 1;
+ case MYSQL_TYPE_SHORT: return 2;
+ case MYSQL_TYPE_LONG: return 4;
+ case MYSQL_TYPE_LONGLONG: return 8;
+ case MYSQL_TYPE_INT24: return 3;
+ }
+ return 0; // This should not happen
+}
+
+Field *Column_definition_attributes::make_field(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const Record_addr *rec,
+ const Type_handler *handler,
+ const LEX_CSTRING *field_name,
+ uint32 flags)
+ const
+{
+ DBUG_ASSERT(length <= UINT_MAX32);
DBUG_PRINT("debug", ("field_type: %s, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s",
- handler->name().ptr(), field_length, interval,
+ handler->name().ptr(), (uint) length, interval,
FLAGSTR(pack_flag, FIELDFLAG_BINARY),
FLAGSTR(pack_flag, FIELDFLAG_INTERVAL),
FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
FLAGSTR(pack_flag, FIELDFLAG_PACK),
FLAGSTR(pack_flag, FIELDFLAG_BLOB)));
- if (handler == &type_handler_row)
- {
- DBUG_ASSERT(field_length == 0);
- DBUG_ASSERT(f_maybe_null(pack_flag));
- return new (mem_root) Field_row(ptr, field_name);
- }
-
- if (handler->real_field_type() == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag))
- {
- bit_ptr= null_pos;
- bit_offset= null_bit;
- if (f_maybe_null(pack_flag)) // if null field
- {
- bit_ptr+= (null_bit == 7); // shift bit_ptr and bit_offset
- bit_offset= (bit_offset + 1) & 7;
- }
- }
-
- if (!f_maybe_null(pack_flag))
- {
- null_pos=0;
- null_bit=0;
- }
- else
- {
- null_bit= ((uchar) 1) << null_bit;
- }
-
-
- if (f_is_alpha(pack_flag))
- {
- if (!f_is_packed(pack_flag))
- {
- enum_field_types field_type= handler->real_field_type();
- if (field_type == MYSQL_TYPE_STRING ||
- field_type == MYSQL_TYPE_DECIMAL || // 3.23 or 4.0 string
- field_type == MYSQL_TYPE_VAR_STRING)
- return new (mem_root)
- Field_string(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- field_charset);
- if (field_type == MYSQL_TYPE_VARCHAR)
- {
- if (unireg_check == Field::TMYSQL_COMPRESSED)
- return new (mem_root)
- Field_varstring_compressed(
- ptr, field_length,
- HA_VARCHAR_PACKLENGTH(field_length),
- null_pos, null_bit,
- unireg_check, field_name,
- share, field_charset, zlib_compression_method);
-
- return new (mem_root)
- Field_varstring(ptr,field_length,
- HA_VARCHAR_PACKLENGTH(field_length),
- null_pos,null_bit,
- unireg_check, field_name,
- share,
- field_charset);
- }
- return 0; // Error
- }
-
- // MYSQL_TYPE_VAR_STRING is handled above
- DBUG_ASSERT(f_packtype(pack_flag) != MYSQL_TYPE_VAR_STRING);
- const Type_handler *tmp;
- tmp= Type_handler::get_handler_by_real_type((enum_field_types)
- f_packtype(pack_flag));
- uint pack_length= tmp->calc_pack_length(field_length);
-
-#ifdef HAVE_SPATIAL
- if (f_is_geom(pack_flag))
- {
- status_var_increment(current_thd->status_var.feature_gis);
- return new (mem_root)
- Field_geom(ptr,null_pos,null_bit,
- unireg_check, field_name, share,
- pack_length, geom_type, srid);
- }
-#endif
- if (f_is_blob(pack_flag))
- {
- if (unireg_check == Field::TMYSQL_COMPRESSED)
- return new (mem_root)
- Field_blob_compressed(ptr, null_pos, null_bit,
- unireg_check, field_name, share,
- pack_length, field_charset, zlib_compression_method);
-
- return new (mem_root)
- Field_blob(ptr,null_pos,null_bit,
- unireg_check, field_name, share,
- pack_length, field_charset);
- }
- if (interval)
- {
- if (f_is_enum(pack_flag))
- return new (mem_root)
- Field_enum(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- pack_length, interval, field_charset);
- else
- return new (mem_root)
- Field_set(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- pack_length, interval, field_charset);
- }
- }
-
- switch (handler->real_field_type()) {
- case MYSQL_TYPE_DECIMAL:
- return new (mem_root)
- Field_decimal(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- f_decimals(pack_flag),
- f_is_zerofill(pack_flag) != 0,
- f_is_dec(pack_flag) == 0);
- case MYSQL_TYPE_NEWDECIMAL:
- return new (mem_root)
- Field_new_decimal(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- f_decimals(pack_flag),
- f_is_zerofill(pack_flag) != 0,
- f_is_dec(pack_flag) == 0);
- case MYSQL_TYPE_FLOAT:
- {
- int decimals= f_decimals(pack_flag);
- if (decimals == FLOATING_POINT_DECIMALS)
- decimals= NOT_FIXED_DEC;
- return new (mem_root)
- Field_float(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- decimals,
- f_is_zerofill(pack_flag) != 0,
- f_is_dec(pack_flag)== 0);
- }
- case MYSQL_TYPE_DOUBLE:
- {
- int decimals= f_decimals(pack_flag);
- if (decimals == FLOATING_POINT_DECIMALS)
- decimals= NOT_FIXED_DEC;
- return new (mem_root)
- Field_double(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- decimals,
- f_is_zerofill(pack_flag) != 0,
- f_is_dec(pack_flag)== 0);
- }
- case MYSQL_TYPE_TINY:
- return new (mem_root)
- Field_tiny(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- f_is_zerofill(pack_flag) != 0,
- f_is_dec(pack_flag) == 0);
- case MYSQL_TYPE_SHORT:
- return new (mem_root)
- Field_short(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- f_is_zerofill(pack_flag) != 0,
- f_is_dec(pack_flag) == 0);
- case MYSQL_TYPE_INT24:
- return new (mem_root)
- Field_medium(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- f_is_zerofill(pack_flag) != 0,
- f_is_dec(pack_flag) == 0);
- case MYSQL_TYPE_LONG:
- return new (mem_root)
- Field_long(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- f_is_zerofill(pack_flag) != 0,
- f_is_dec(pack_flag) == 0);
- case MYSQL_TYPE_LONGLONG:
- if (flags & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG))
- {
- return new (mem_root)
- Field_vers_trx_id(ptr, field_length, null_pos, null_bit,
- unireg_check, field_name,
- f_is_zerofill(pack_flag) != 0,
- f_is_dec(pack_flag) == 0);
- }
- else
- {
- return new (mem_root)
- Field_longlong(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name,
- f_is_zerofill(pack_flag) != 0,
- f_is_dec(pack_flag) == 0);
- }
- case MYSQL_TYPE_TIMESTAMP:
- {
- uint dec= field_length > MAX_DATETIME_WIDTH ?
- field_length - MAX_DATETIME_WIDTH - 1: 0;
- return new_Field_timestamp(mem_root, ptr, null_pos, null_bit, unireg_check,
- field_name, share, dec);
- }
- case MYSQL_TYPE_TIMESTAMP2:
- {
- uint dec= field_length > MAX_DATETIME_WIDTH ?
- field_length - MAX_DATETIME_WIDTH - 1: 0;
- return new (mem_root)
- Field_timestampf(ptr, null_pos, null_bit, unireg_check,
- field_name, share, dec);
- }
- case MYSQL_TYPE_YEAR:
- return new (mem_root)
- Field_year(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name);
- case MYSQL_TYPE_DATE:
- return new (mem_root)
- Field_date(ptr,null_pos,null_bit,
- unireg_check, field_name);
- case MYSQL_TYPE_NEWDATE:
- return new (mem_root)
- Field_newdate(ptr,null_pos,null_bit,
- unireg_check, field_name);
- case MYSQL_TYPE_TIME:
- {
- uint dec= field_length > MIN_TIME_WIDTH ?
- field_length - MIN_TIME_WIDTH - 1: 0;
- return new_Field_time(mem_root, ptr, null_pos, null_bit, unireg_check,
- field_name, dec);
- }
- case MYSQL_TYPE_TIME2:
- {
- uint dec= field_length > MIN_TIME_WIDTH ?
- field_length - MIN_TIME_WIDTH - 1: 0;
- return new (mem_root)
- Field_timef(ptr, null_pos, null_bit, unireg_check,
- field_name, dec);
- }
- case MYSQL_TYPE_DATETIME:
- {
- uint dec= field_length > MAX_DATETIME_WIDTH ?
- field_length - MAX_DATETIME_WIDTH - 1: 0;
- return new_Field_datetime(mem_root, ptr, null_pos, null_bit, unireg_check,
- field_name, dec);
- }
- case MYSQL_TYPE_DATETIME2:
- {
- uint dec= field_length > MAX_DATETIME_WIDTH ?
- field_length - MAX_DATETIME_WIDTH - 1: 0;
- return new (mem_root)
- Field_datetimef(ptr, null_pos, null_bit, unireg_check,
- field_name, dec);
- }
- case MYSQL_TYPE_NULL:
- return new (mem_root)
- Field_null(ptr, field_length, unireg_check, field_name,
- field_charset);
- case MYSQL_TYPE_BIT:
- return (f_bit_as_char(pack_flag) ?
- new (mem_root)
- Field_bit_as_char(ptr, field_length, null_pos, null_bit,
- unireg_check, field_name) :
- new (mem_root)
- Field_bit(ptr, field_length, null_pos, null_bit, bit_ptr,
- bit_offset, unireg_check, field_name));
-
- default: // Impossible (Wrong version)
- break;
- }
- return 0;
+ Record_addr addr(rec->ptr(), f_maybe_null(pack_flag) ? rec->null() :
+ Bit_addr());
+ /*
+ Special code for the BIT-alike data types
+ who store data bits together with NULL-bits.
+ */
+ Bit_addr bit(rec->null());
+ if (f_maybe_null(pack_flag))
+ bit.inc();
+ return handler->make_table_field_from_def(share, mem_root, field_name,
+ addr, bit, this, flags);
}
+
bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item) const
{
- return item->type() == Item::DATE_ITEM;
+ return item->is_of_type(Item::CONST_ITEM, TIME_RESULT);
}
+Column_definition_attributes::Column_definition_attributes(const Field *field)
+ :length(field->character_octet_length() / field->charset()->mbmaxlen),
+ unireg_check(field->unireg_check),
+ interval(NULL),
+ charset(field->charset()), // May be NULL ptr
+ srid(0),
+ geom_type(Field::GEOM_GEOMETRY),
+ pack_flag(0)
+{}
+
+
/** Create a field suitable for create of table. */
Column_definition::Column_definition(THD *thd, Field *old_field,
Field *orig_field)
+ :Column_definition_attributes(old_field)
{
on_update= NULL;
field_name= old_field->field_name;
- length= old_field->field_length;
flags= old_field->flags;
- unireg_check=old_field->unireg_check;
pack_length=old_field->pack_length();
key_length= old_field->key_length();
set_handler(old_field->type_handler());
- charset= old_field->charset(); // May be NULL ptr
comment= old_field->comment;
decimals= old_field->decimals();
vcol_info= old_field->vcol_info;
option_list= old_field->option_list;
- pack_flag= 0;
compression_method_ptr= 0;
versioning= VERSIONING_NOT_SET;
invisible= old_field->invisible;
+ interval_list.empty(); // prepare_interval_field() needs this
+ char_length= (uint) length;
if (orig_field)
{
@@ -11095,66 +11031,9 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
check_constraint= 0;
}
- switch (real_field_type()) {
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- length/= charset->mbmaxlen;
- key_length/= charset->mbmaxlen;
- break;
- case MYSQL_TYPE_STRING:
- /* Change CHAR -> VARCHAR if dynamic record length */
- if (old_field->type() == MYSQL_TYPE_VAR_STRING)
- set_handler(&type_handler_varchar);
- /* fall through */
-
- case MYSQL_TYPE_ENUM:
- case MYSQL_TYPE_SET:
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_VAR_STRING:
- /* This is corrected in create_length_to_internal_length */
- length= (length+charset->mbmaxlen-1) / charset->mbmaxlen -
- MY_TEST(old_field->compression_method());
- break;
-#ifdef HAVE_SPATIAL
- case MYSQL_TYPE_GEOMETRY:
- geom_type= ((Field_geom*)old_field)->geom_type;
- srid= ((Field_geom*)old_field)->srid;
- break;
-#endif
- case MYSQL_TYPE_YEAR:
- if (length != 4)
- {
- char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1];
- my_snprintf(buff, sizeof(buff), "YEAR(%llu)", length);
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_WARN_DEPRECATED_SYNTAX,
- ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX),
- buff, "YEAR(4)");
- }
- break;
- case MYSQL_TYPE_FLOAT:
- case MYSQL_TYPE_DOUBLE:
- /*
- Floating points are stored with FLOATING_POINT_DECIMALS but internally
- in MariaDB used with NOT_FIXED_DEC, which is >= FLOATING_POINT_DECIMALS.
- */
- if (decimals >= FLOATING_POINT_DECIMALS)
- decimals= NOT_FIXED_DEC;
- break;
- default:
- break;
- }
-
- if (flags & (ENUM_FLAG | SET_FLAG))
- interval= ((Field_enum*) old_field)->typelib;
- else
- interval=0;
-
- interval_list.empty(); // prepare_interval_field() needs this
+ type_handler()->Column_definition_reuse_fix_attributes(thd, this, old_field);
- char_length= (uint)length;
+ type_handler()->Column_definition_implicit_upgrade(this);
/*
Copy the default (constant/function) from the column object orig_field, if
@@ -11197,16 +11076,26 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
CREATE TABLE t1 (a INT) AS SELECT a FROM t2;
See Type_handler::Column_definition_redefine_stage1()
for data type specific code.
+
+ @param this - The field definition corresponding to the expression
+ in the "AS SELECT.." part.
+
+ @param dup_field - The field definition from the "CREATE TABLE (...)" part.
+ It has already underwent prepare_stage1(), so
+ must be fully initialized:
+ -- dup_field->charset is set and BINARY
+ sorting style is applied, see find_bin_collation().
+
+ @param file - The table handler
*/
void
Column_definition::redefine_stage1_common(const Column_definition *dup_field,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
{
set_handler(dup_field->type_handler());
default_value= dup_field->default_value;
- charset= dup_field->charset ? dup_field->charset :
- schema->default_table_charset;
+ DBUG_ASSERT(dup_field->charset); // Set by prepare_stage1()
+ charset= dup_field->charset;
length= dup_field->char_length;
pack_length= dup_field->pack_length;
key_length= dup_field->key_length;
@@ -11217,6 +11106,9 @@ Column_definition::redefine_stage1_common(const Column_definition *dup_field,
vcol_info= dup_field->vcol_info;
invisible= dup_field->invisible;
check_constraint= dup_field->check_constraint;
+ comment= dup_field->comment;
+ option_list= dup_field->option_list;
+ versioning= dup_field->versioning;
}
@@ -11237,11 +11129,11 @@ Column_definition::redefine_stage1_common(const Column_definition *dup_field,
uint32 Field_blob::char_length() const
{
- return Field_blob::octet_length();
+ return Field_blob::character_octet_length();
}
-uint32 Field_blob::octet_length() const
+uint32 Field_blob::character_octet_length() const
{
switch (packlength)
{
@@ -11338,6 +11230,13 @@ Column_definition::set_compressed_deprecated_column_attribute(THD *thd,
}
+Send_field::Send_field(THD *thd, Item *item)
+{
+ item->make_send_field(thd, this);
+ normalize();
+}
+
+
/**
maximum possible display length for blob.
@@ -11431,14 +11330,25 @@ Field::set_warning(Sql_condition::enum_warning_level level, uint code,
void Field::set_datetime_warning(Sql_condition::enum_warning_level level,
uint code, const ErrConv *str,
- timestamp_type ts_type, int cuted_increment)
+ const char *typestr, int cuted_increment)
const
{
THD *thd= get_thd();
if (thd->really_abort_on_warning() && level >= Sql_condition::WARN_LEVEL_WARN)
- make_truncated_value_warning(thd, level, str, ts_type,
- table->s->db.str, table->s->table_name.str,
- field_name.str);
+ {
+ /*
+ field_name.str can be NULL when field is not in the select list:
+ SET SESSION SQL_MODE= 'STRICT_ALL_TABLES,NO_ZERO_DATE';
+ CREATE OR REPLACE TABLE t2 SELECT 1 AS f FROM t1 GROUP BY FROM_DAYS(d);
+ Can't call push_warning_truncated_value_for_field() directly here,
+ as it expect a non-NULL name.
+ */
+ thd->push_warning_wrong_or_truncated_value(level, false, typestr,
+ str->ptr(),
+ table->s->db.str,
+ table->s->table_name.str,
+ field_name.str);
+ }
else
set_warning(level, code, cuted_increment);
}
@@ -11599,3 +11509,115 @@ bool Field::val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to)
thd->variables.sql_mode= sql_mode_backup;
return rc;
}
+
+
+void Field::print_key_value(String *out, uint32 length)
+{
+ if (charset() == &my_charset_bin)
+ print_key_value_binary(out, ptr, length);
+ else
+ val_str(out);
+}
+
+
+void Field_string::print_key_value(String *out, uint32 length)
+{
+ if (charset() == &my_charset_bin)
+ {
+ size_t len= field_charset->cset->lengthsp(field_charset, (const char*) ptr, length);
+ print_key_value_binary(out, ptr, static_cast<uint32>(len));
+ }
+ else
+ {
+ THD *thd= get_thd();
+ sql_mode_t sql_mode_backup= thd->variables.sql_mode;
+ thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
+ val_str(out,out);
+ thd->variables.sql_mode= sql_mode_backup;
+ }
+}
+
+
+void Field_varstring::print_key_value(String *out, uint32 length)
+{
+ if (charset() == &my_charset_bin)
+ print_key_value_binary(out, get_data(), get_length());
+ else
+ val_str(out,out);
+}
+
+
+void Field_blob::print_key_value(String *out, uint32 length)
+{
+ if (charset() == &my_charset_bin)
+ {
+ uchar *blob;
+ memcpy(&blob, ptr+packlength, sizeof(uchar*));
+ print_key_value_binary(out, blob, get_length());
+ }
+ else
+ val_str(out, out);
+}
+
+
+/*
+ @brief Print value of the key part
+
+ @param
+ out Output string
+ key value of the key
+ length Length of field in bytes,
+ excluding NULL flag and length bytes
+*/
+
+
+void
+Field::print_key_part_value(String *out, const uchar* key, uint32 length)
+{
+ StringBuffer<128> tmp(system_charset_info);
+ uint null_byte= 0;
+ if (real_maybe_null())
+ {
+ /*
+ Byte 0 of key is the null-byte. If set, key is NULL.
+ Otherwise, print the key value starting immediately after the
+ null-byte
+ */
+ if (*key)
+ {
+ out->append(STRING_WITH_LEN("NULL"));
+ return;
+ }
+ null_byte++; // Skip null byte
+ }
+
+ set_key_image(key + null_byte, length);
+ print_key_value(&tmp, length);
+ if (charset() == &my_charset_bin)
+ out->append(tmp.ptr(), tmp.length(), tmp.charset());
+ else
+ tmp.print(out, system_charset_info);
+}
+
+
+void Field::print_key_value_binary(String *out, const uchar* key, uint32 length)
+{
+ out->append_semi_hex((const char*)key, length, charset());
+}
+
+
+Virtual_column_info* Virtual_column_info::clone(THD *thd)
+{
+ Virtual_column_info* dst= new (thd->mem_root) Virtual_column_info(*this);
+ if (!dst)
+ return NULL;
+ if (expr)
+ {
+ dst->expr= expr->build_clone(thd);
+ if (!dst->expr)
+ return NULL;
+ }
+ if (!thd->make_lex_string(&dst->name, name.str, name.length))
+ return NULL;
+ return dst;
+};
diff --git a/sql/field.h b/sql/field.h
index a3385736c0a..477c64727b3 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -48,6 +48,9 @@ class Item_equal;
class Virtual_tmp_table;
class Qualified_column_ident;
class Table_ident;
+class SEL_ARG;
+class RANGE_OPT_PARAM;
+struct KEY_PART;
enum enum_check_fields
{
@@ -461,38 +464,13 @@ inline bool is_temporal_type_with_date(enum_field_types type)
case MYSQL_TYPE_DATETIME2:
case MYSQL_TYPE_TIMESTAMP2:
DBUG_ASSERT(0); // field->real_type() should not get to here.
- /* fall through */
+ return false;
default:
return false;
}
}
-/**
- Convert temporal real types as returned by field->real_type()
- to field type as returned by field->type().
-
- @param real_type Real type.
- @retval Field type.
-*/
-inline enum_field_types real_type_to_type(enum_field_types real_type)
-{
- switch (real_type)
- {
- case MYSQL_TYPE_TIME2:
- return MYSQL_TYPE_TIME;
- case MYSQL_TYPE_DATETIME2:
- return MYSQL_TYPE_DATETIME;
- case MYSQL_TYPE_TIMESTAMP2:
- return MYSQL_TYPE_TIMESTAMP;
- case MYSQL_TYPE_NEWDATE:
- return MYSQL_TYPE_DATE;
- /* Note: NEWDECIMAL is a type, not only a real_type */
- default: return real_type;
- }
-}
-
-
enum enum_vcol_info_type
{
VCOL_GENERATED_VIRTUAL, VCOL_GENERATED_STORED,
@@ -546,7 +524,8 @@ static inline const char *vcol_type_name(enum_vcol_info_type type)
- whether the field is used in a partitioning expression
*/
-class Virtual_column_info: public Sql_alloc
+class Virtual_column_info: public Sql_alloc,
+ private Type_handler_hybrid_field_type
{
private:
enum_vcol_info_type vcol_type; /* Virtual column expression type */
@@ -554,7 +533,6 @@ 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 used in a partitioning expression */
bool in_partitioning_expr;
@@ -564,20 +542,21 @@ public:
bool utf8; /* Already in utf8 */
bool automatic_name;
Item *expr;
- LEX_CSTRING name; /* Name of constraint */
+ Lex_ident name; /* Name of constraint */
/* see VCOL_* (VCOL_FIELD_REF, ...) */
uint flags;
Virtual_column_info()
- : vcol_type((enum_vcol_info_type)VCOL_TYPE_NONE),
- field_type((enum enum_field_types)MYSQL_TYPE_VIRTUAL),
+ :Type_handler_hybrid_field_type(&type_handler_null),
+ vcol_type((enum_vcol_info_type)VCOL_TYPE_NONE),
in_partitioning_expr(FALSE), stored_in_db(FALSE),
utf8(TRUE), automatic_name(FALSE), expr(NULL), flags(0)
{
name.str= NULL;
name.length= 0;
};
- ~Virtual_column_info() {}
+ Virtual_column_info* clone(THD *thd);
+ ~Virtual_column_info() {};
enum_vcol_info_type get_vcol_type() const
{
return vcol_type;
@@ -591,14 +570,11 @@ public:
DBUG_ASSERT(vcol_type != VCOL_TYPE_NONE);
return vcol_type_name(vcol_type);
}
- enum_field_types get_real_type() const
- {
- return field_type;
- }
- void set_field_type(enum_field_types fld_type)
+ void set_handler(const Type_handler *handler)
{
/* Calling this function can only be done once. */
- field_type= fld_type;
+ DBUG_ASSERT(type_handler() == &type_handler_null);
+ Type_handler_hybrid_field_type::set_handler(handler);
}
bool is_stored() const
{
@@ -636,7 +612,9 @@ protected:
static void do_field_int(Copy_field *copy);
static void do_field_real(Copy_field *copy);
static void do_field_string(Copy_field *copy);
- static void do_field_temporal(Copy_field *copy);
+ static void do_field_date(Copy_field *copy);
+ static void do_field_temporal(Copy_field *copy, date_mode_t fuzzydate);
+ static void do_field_datetime(Copy_field *copy);
static void do_field_timestamp(Copy_field *copy);
static void do_field_decimal(Copy_field *copy);
public:
@@ -651,6 +629,9 @@ public:
static void operator delete(void *ptr, MEM_ROOT *mem_root)
{ DBUG_ASSERT(0); }
+ bool marked_for_read() const;
+ bool marked_for_write_or_computed() const;
+
/**
Used by System Versioning.
*/
@@ -714,7 +695,7 @@ public:
enum imagetype { itRAW, itMBR};
utype unireg_check;
- uint32 field_length; // Length of field
+ const uint32 field_length; // Length of field
uint32 flags;
uint16 field_index; // field number in fields array
uchar null_bit; // Bit used to test null bit
@@ -799,13 +780,32 @@ public:
@retval false - conversion is needed
*/
virtual bool memcpy_field_possible(const Field *from) const= 0;
+ virtual bool make_empty_rec_store_default_value(THD *thd, Item *item);
+ virtual void make_empty_rec_reset(THD *thd)
+ {
+ reset();
+ }
virtual int store(const char *to, size_t length,CHARSET_INFO *cs)=0;
virtual int store_hex_hybrid(const char *str, size_t length);
virtual int store(double nr)=0;
virtual int store(longlong nr, bool unsigned_val)=0;
virtual int store_decimal(const my_decimal *d)=0;
virtual int store_time_dec(const MYSQL_TIME *ltime, uint dec);
- virtual int store_timestamp(my_time_t timestamp, ulong sec_part);
+ virtual int store_timestamp_dec(const timeval &ts, uint dec);
+ int store_timestamp(my_time_t timestamp, ulong sec_part)
+ {
+ return store_timestamp_dec(Timeval(timestamp, sec_part),
+ TIME_SECOND_PART_DIGITS);
+ }
+ /**
+ Store a value represented in native format
+ */
+ virtual int store_native(const Native &value)
+ {
+ DBUG_ASSERT(0);
+ reset();
+ return 0;
+ }
int store_time(const MYSQL_TIME *ltime)
{ return store_time_dec(ltime, TIME_SECOND_PART_DIGITS); }
int store(const char *to, size_t length, CHARSET_INFO *cs,
@@ -850,7 +850,7 @@ public:
return nr < 0 ? 0 : (ulonglong) nr;
}
virtual bool val_bool(void)= 0;
- virtual my_decimal *val_decimal(my_decimal *);
+ virtual my_decimal *val_decimal(my_decimal *)=0;
inline String *val_str(String *str) { return val_str(str, str); }
/*
val_str(buf1, buf2) gets two buffers and should use them as follows:
@@ -865,6 +865,11 @@ public:
This trickery is used to decrease a number of malloc calls.
*/
virtual String *val_str(String*,String *)=0;
+ virtual bool val_native(Native *to)
+ {
+ DBUG_ASSERT(!is_null());
+ return to->copy((const char *) ptr, pack_length());
+ }
String *val_int_as_str(String *val_buffer, bool unsigned_flag);
/*
Return the field value as a LEX_CSTRING, without padding to full length
@@ -887,6 +892,10 @@ public:
to be quoted when used in constructing an SQL query.
*/
virtual bool str_needs_quotes() { return FALSE; }
+ const Type_handler *type_handler_for_comparison() const
+ {
+ return type_handler()->type_handler_for_comparison();
+ }
Item_result result_type () const
{
return type_handler()->result_type();
@@ -895,7 +904,6 @@ public:
{
return type_handler()->cmp_type();
}
- static enum_field_types field_type_merge(enum_field_types, enum_field_types);
virtual bool eq(Field *field)
{
return (ptr == field->ptr && null_ptr == field->null_ptr &&
@@ -1091,7 +1099,7 @@ public:
{ return cmp(a, b); }
virtual int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U)
{ return memcmp(a,b,pack_length()); }
- virtual int cmp_offset(uint row_offset)
+ virtual int cmp_offset(my_ptrdiff_t row_offset)
{ return cmp(ptr,ptr+row_offset); }
virtual int cmp_binary_offset(uint row_offset)
{ return cmp_binary(ptr, ptr+row_offset); };
@@ -1223,6 +1231,11 @@ public:
{
return 0;
}
+ virtual sql_mode_t conversion_depends_on_sql_mode(THD *thd,
+ Item *expr) const
+ {
+ return (sql_mode_t) 0;
+ }
virtual sql_mode_t can_handle_sql_mode_dependency_on_store() const
{
return 0;
@@ -1274,6 +1287,12 @@ public:
virtual Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_ptr, uint32 length,
uchar *new_null_ptr, uint new_null_bit);
+ Field *create_tmp_field(MEM_ROOT *root, TABLE *new_table,
+ bool maybe_null_arg);
+ Field *create_tmp_field(MEM_ROOT *root, TABLE *new_table)
+ {
+ return create_tmp_field(root, new_table, maybe_null());
+ }
Field *clone(MEM_ROOT *mem_root, TABLE *new_table);
Field *clone(MEM_ROOT *mem_root, TABLE *new_table, my_ptrdiff_t diff);
inline void move_field(uchar *ptr_arg,uchar *null_ptr_arg,uchar null_bit_arg)
@@ -1376,14 +1395,15 @@ public:
virtual uint max_packed_col_length(uint max_length)
{ return max_length;}
- uint offset(uchar *record) const
+ uint offset(const uchar *record) const
{
return (uint) (ptr - record);
}
void copy_from_tmp(int offset);
uint fill_cache_field(struct st_cache_field *copy);
- virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool get_time(MYSQL_TIME *ltime) { return get_date(ltime, TIME_TIME_ONLY); }
+ virtual bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ virtual longlong val_datetime_packed(THD *thd);
+ virtual longlong val_time_packed(THD *thd);
virtual TYPELIB *get_typelib() const { return NULL; }
virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; }
virtual CHARSET_INFO *charset_for_protocol(void) const
@@ -1396,6 +1416,9 @@ public:
virtual int set_time() { return 1; }
bool set_warning(Sql_condition::enum_warning_level, unsigned int code,
int cuted_increment, ulong current_row=0) const;
+ virtual void print_key_value(String *out, uint32 length);
+ void print_key_part_value(String *out, const uchar *key, uint32 length);
+ void print_key_value_binary(String *out, const uchar* key, uint32 length);
protected:
bool set_warning(unsigned int code, int cuted_increment) const
{
@@ -1406,13 +1429,13 @@ protected:
return set_warning(Sql_condition::WARN_LEVEL_NOTE, code, cuted_increment);
}
void set_datetime_warning(Sql_condition::enum_warning_level, uint code,
- const ErrConv *str, timestamp_type ts_type,
+ const ErrConv *str, const char *typestr,
int cuted_increment) const;
void set_datetime_warning(uint code,
- const ErrConv *str, timestamp_type ts_type,
+ const ErrConv *str, const char *typestr,
int cuted_increment) const
{
- set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, code, str, ts_type,
+ set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, code, str, typestr,
cuted_increment);
}
void set_warning_truncated_wrong_value(const char *type, const char *value);
@@ -1422,6 +1445,59 @@ protected:
}
int warn_if_overflow(int op_result);
Copy_func *get_identical_copy_func() const;
+ bool can_optimize_scalar_range(const RANGE_OPT_PARAM *param,
+ const KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op,
+ const Item *value) const;
+ uchar *make_key_image(MEM_ROOT *mem_root, const KEY_PART *key_part);
+ SEL_ARG *get_mm_leaf_int(RANGE_OPT_PARAM *param, KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op, Item *value,
+ bool unsigned_field);
+ /*
+ Make a leaf tree for the cases when the value was stored
+ to the field exactly, without any truncation, rounding or adjustments.
+ For example, if we stored an INT value into an INT column,
+ and value->save_in_field_no_warnings() returned 0,
+ we know that the value was stored exactly.
+ */
+ SEL_ARG *stored_field_make_mm_leaf_exact(RANGE_OPT_PARAM *param,
+ KEY_PART *key_part,
+ scalar_comparison_op op,
+ Item *value);
+ /*
+ Make a leaf tree for the cases when we don't know if
+ the value was stored to the field without any data loss,
+ or was modified to a smaller or a greater value.
+ Used for the data types whose methods Field::store*()
+ silently adjust the value. This is the most typical case.
+ */
+ SEL_ARG *stored_field_make_mm_leaf(RANGE_OPT_PARAM *param,
+ KEY_PART *key_part,
+ scalar_comparison_op op, Item *value);
+ /*
+ Make a leaf tree when an INT value was stored into a field of INT type,
+ and some truncation happened. Tries to adjust the range search condition
+ when possible, e.g. "tinytint < 300" -> "tinyint <= 127".
+ Can also return SEL_ARG_IMPOSSIBLE(), and NULL (not sargable).
+ */
+ SEL_ARG *stored_field_make_mm_leaf_bounded_int(RANGE_OPT_PARAM *param,
+ KEY_PART *key_part,
+ scalar_comparison_op op,
+ Item *value,
+ bool unsigned_field);
+ /*
+ Make a leaf tree when some truncation happened during
+ value->save_in_field_no_warning(this), and we cannot yet adjust the range
+ search condition for the current combination of the field and the value
+ data types.
+ Returns SEL_ARG_IMPOSSIBLE() for "=" and "<=>".
+ Returns NULL (not sargable) for other comparison operations.
+ */
+ SEL_ARG *stored_field_make_mm_leaf_truncated(RANGE_OPT_PARAM *prm,
+ scalar_comparison_op,
+ Item *value);
public:
void set_table_name(String *alias)
{
@@ -1432,6 +1508,19 @@ public:
orig_table= table= table_arg;
set_table_name(&table_arg->alias);
}
+ virtual void init_for_tmp_table(Field *org_field, TABLE *new_table)
+ {
+ init(new_table);
+ orig_table= org_field->orig_table;
+ vcol_info= 0;
+ cond_selectivity= 1.0;
+ next_equal_field= NULL;
+ option_list= NULL;
+ option_struct= NULL;
+ if (org_field->type() == MYSQL_TYPE_VAR_STRING ||
+ org_field->type() == MYSQL_TYPE_VARCHAR)
+ new_table->s->db_create_options|= HA_OPTION_PACK_RECORD;
+ }
void init_for_make_new_field(TABLE *new_table_arg, TABLE *orig_table_arg)
{
init(new_table_arg);
@@ -1448,23 +1537,35 @@ public:
/* maximum possible display length */
virtual uint32 max_display_length() const= 0;
/**
- Whether a field being created is compatible with a existing one.
-
- Used by the ALTER TABLE code to evaluate whether the new definition
- of a table is compatible with the old definition so that it can
- determine if data needs to be copied over (table data change).
+ Whether a field being created has the samle type.
+ Used by the ALTER TABLE
*/
- virtual uint is_equal(Create_field *new_field);
+ virtual bool is_equal(const Column_definition &new_field) const= 0;
+ // Used as double dispatch pattern: calls virtual method of handler
+ virtual bool
+ can_be_converted_by_engine(const Column_definition &new_type) const
+ {
+ return false;
+ }
/* convert decimal to longlong with overflow check */
longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag,
int *err);
+ /*
+ Maximum number of bytes in character representation.
+ - For string types it is equal to the field capacity, in bytes.
+ - For non-string types it represents the longest possible string length
+ after conversion to string.
+ */
+ virtual uint32 character_octet_length() const
+ {
+ return field_length;
+ }
/* The max. number of characters */
virtual uint32 char_length() const
{
return field_length / charset()->mbmaxlen;
}
-
- virtual geometry_type get_geometry_type()
+ virtual geometry_type get_geometry_type() const
{
/* shouldn't get here. */
DBUG_ASSERT(0);
@@ -1592,6 +1693,10 @@ public:
const Item *item,
bool is_eq_func) const;
+ virtual SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op, Item *value)= 0;
+
bool can_optimize_outer_join_table_elimination(const Item_bool_func *cond,
const Item *item) const
{
@@ -1735,7 +1840,7 @@ public:
{
return to->store(val_int(), MY_TEST(flags & UNSIGNED_FLAG));
}
- uint is_equal(Create_field *new_field);
+ bool is_equal(const Column_definition &new_field) const;
uint row_pack_length() const { return pack_length(); }
uint32 pack_length_from_metadata(uint field_metadata) {
uint32 length= pack_length();
@@ -1747,6 +1852,9 @@ public:
{
return pos_in_interval_val_real(min, max);
}
+ SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op, Item *value);
};
@@ -1784,6 +1892,7 @@ public:
enum Derivation derivation(void) const { return field_derivation; }
bool binary() const { return field_charset == &my_charset_bin; }
uint32 max_display_length() const { return field_length; }
+ uint32 character_octet_length() const { return field_length; }
uint32 char_length() const { return field_length / field_charset->mbmaxlen; }
Information_schema_character_attributes
information_schema_character_attributes() const
@@ -1795,14 +1904,16 @@ public:
my_decimal *val_decimal(my_decimal *);
bool val_bool() { return val_real() != 0e0; }
virtual bool str_needs_quotes() { return TRUE; }
- uint is_equal(Create_field *new_field);
bool eq_cmp_as_binary() { return MY_TEST(flags & BINARY_FLAG); }
- virtual uint length_size() { return 0; }
+ virtual uint length_size() const { return 0; }
double pos_in_interval(Field *min, Field *max)
{
return pos_in_interval_val_str(min, max, length_size());
}
bool test_if_equality_guarantees_uniqueness(const Item *const_item) const;
+ SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op, Item *value);
};
/* base class for Field_string, Field_varstring and Field_blob */
@@ -1918,9 +2029,9 @@ public:
decimals() == from->decimals() &&
field_length >= from->field_length;
}
- int store_decimal(const my_decimal *);
+ int store_decimal(const my_decimal *dec) { return store(dec->to_double()); }
int store_time_dec(const MYSQL_TIME *ltime, uint dec);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
my_decimal *val_decimal(my_decimal *);
bool val_bool() { return val_real() != 0e0; }
uint32 max_display_length() const { return field_length; }
@@ -2001,8 +2112,8 @@ public:
}
int save_in_field(Field *to)
{
- my_decimal buff;
- return to->store_decimal(val_decimal(&buff));
+ my_decimal tmp(ptr, precision, dec);
+ return to->store_decimal(&tmp);
}
bool memcpy_field_possible(const Field *from) const
{
@@ -2021,17 +2132,34 @@ public:
int store(longlong nr, bool unsigned_val);
int store_time_dec(const MYSQL_TIME *ltime, uint dec);
int store_decimal(const my_decimal *);
- double val_real(void);
- longlong val_int(void);
- ulonglong val_uint(void);
+ double val_real(void)
+ {
+ return my_decimal(ptr, precision, dec).to_double();
+ }
+ longlong val_int(void)
+ {
+ return my_decimal(ptr, precision, dec).to_longlong(unsigned_flag);
+ }
+ ulonglong val_uint(void)
+ {
+ return (ulonglong) my_decimal(ptr, precision, dec).to_longlong(true);
+ }
my_decimal *val_decimal(my_decimal *);
- String *val_str(String*, String *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ String *val_str(String *val_buffer, String *val_ptr __attribute__((unused)))
+ {
+ uint fixed_precision= zerofill ? precision : 0;
+ return my_decimal(ptr, precision, dec).
+ to_string(val_buffer, fixed_precision, dec, '0');
+ }
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ {
+ my_decimal nr(ptr, precision, dec);
+ return decimal_to_datetime_with_warn(get_thd(), &nr, ltime,
+ fuzzydate, table->s, field_name.str);
+ }
bool val_bool()
{
- my_decimal decimal_value;
- my_decimal *val= val_decimal(&decimal_value);
- return val ? !my_decimal_is_zero(val) : 0;
+ return my_decimal(ptr, precision, dec).to_bool();
}
int cmp(const uchar *, const uchar *);
void sort_string(uchar *buff, uint length);
@@ -2049,7 +2177,7 @@ public:
uint row_pack_length() const { return pack_length(); }
bool compatible_field_size(uint field_metadata, Relay_log_info *rli,
uint16 mflags, int *order_var);
- uint is_equal(Create_field *new_field);
+ bool is_equal(const Column_definition &new_field) const;
virtual const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, uint param_data);
Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item);
};
@@ -2082,7 +2210,7 @@ public:
return nr < 0 && !unsigned_flag ? 0 : (ulonglong) nr;
}
int store_time_dec(const MYSQL_TIME *ltime, uint dec);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
virtual const Type_limits_int *type_limits_int() const= 0;
uint32 max_display_length() const
{
@@ -2112,6 +2240,12 @@ public:
uint32 prec= type_limits_int()->precision();
return Information_schema_numeric_attributes(prec, 0);
}
+ SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op, Item *value)
+ {
+ return get_mm_leaf_int(param, key_part, cond, op, value, unsigned_flag);
+ }
};
@@ -2379,8 +2513,8 @@ public:
{}
const Type_handler *type_handler() const { return &type_handler_vers_trx_id; }
uint size_of() const { return sizeof(*this); }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate, ulonglong trx_id);
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
return get_date(ltime, fuzzydate, (ulonglong) val_int());
}
@@ -2487,6 +2621,11 @@ public:
if (dec_arg >= FLOATING_POINT_DECIMALS)
dec_arg= NOT_FIXED_DEC;
}
+ void init_for_tmp_table(Field *org_field, TABLE *new_table)
+ {
+ Field::init_for_tmp_table(org_field, new_table);
+ not_fixed= true;
+ }
const Type_handler *type_handler() const { return &type_handler_double; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; }
int store(const char *to,size_t length,CHARSET_INFO *charset);
@@ -2548,6 +2687,7 @@ public:
my_decimal *val_decimal(my_decimal *) { return 0; }
String *val_str(String *value,String *value2)
{ value2->length(0); return value2;}
+ bool is_equal(const Column_definition &new_field) const;
int cmp(const uchar *a, const uchar *b) { return 0;}
void sort_string(uchar *buff, uint length) {}
uint32 pack_length() const { return 0; }
@@ -2572,6 +2712,35 @@ class Field_temporal: public Field {
protected:
Item *get_equal_const_item_datetime(THD *thd, const Context &ctx,
Item *const_item);
+ void set_warnings(Sql_condition::enum_warning_level trunc_level,
+ const ErrConv *str, int was_cut, const char *typestr);
+ int store_TIME_return_code_with_warnings(int warn, const ErrConv *str,
+ const char *typestr)
+ {
+ if (!MYSQL_TIME_WARN_HAVE_WARNINGS(warn) &&
+ MYSQL_TIME_WARN_HAVE_NOTES(warn))
+ {
+ set_warnings(Sql_condition::WARN_LEVEL_NOTE, str,
+ warn | MYSQL_TIME_WARN_TRUNCATED, typestr);
+ return 3;
+ }
+ set_warnings(Sql_condition::WARN_LEVEL_WARN, str, warn, typestr);
+ return warn ? 2 : 0;
+ }
+ int store_invalid_with_warning(const ErrConv *str, int was_cut,
+ const char *typestr)
+ {
+ DBUG_ASSERT(was_cut);
+ reset();
+ Sql_condition::enum_warning_level level= Sql_condition::WARN_LEVEL_WARN;
+ if (was_cut & MYSQL_TIME_WARN_ZERO_DATE)
+ {
+ set_warnings(level, str, MYSQL_TIME_WARN_OUT_OF_RANGE, typestr);
+ return 2;
+ }
+ set_warnings(level, str, MYSQL_TIME_WARN_TRUNCATED, typestr);
+ return 1;
+ }
public:
Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
@@ -2588,7 +2757,8 @@ public:
int save_in_field(Field *to)
{
MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
+ // For temporal types no truncation needed. Rounding mode is not important.
+ if (get_date(&ltime, TIME_CONV_NONE | TIME_FRAC_NONE))
return to->reset();
return to->store_time_dec(&ltime, decimals());
}
@@ -2601,14 +2771,12 @@ public:
CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
bool binary() const { return true; }
bool val_bool() { return val_real() != 0e0; }
- uint is_equal(Create_field *new_field);
+ bool is_equal(const Column_definition &new_field) const;
bool eq_def(const Field *field) const
{
return (Field::eq_def(field) && decimals() == field->decimals());
}
my_decimal *val_decimal(my_decimal*);
- void set_warnings(Sql_condition::enum_warning_level trunc_level,
- const ErrConv *str, int was_cut, timestamp_type ts_type);
double pos_in_interval(Field *min, Field *max)
{
return pos_in_interval_val_real(min, max);
@@ -2623,6 +2791,9 @@ public:
{
return true;
}
+ SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op, Item *value);
};
@@ -2635,18 +2806,20 @@ public:
*/
class Field_temporal_with_date: public Field_temporal {
protected:
- int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str,
- int was_cut, int have_smth_to_conv);
- virtual void store_TIME(MYSQL_TIME *ltime) = 0;
+ virtual void store_TIME(const MYSQL_TIME *ltime) = 0;
+ void store_datetime(const Datetime &dt)
+ {
+ return store_TIME(dt.get_mysql_time());
+ }
virtual bool get_TIME(MYSQL_TIME *ltime, const uchar *pos,
- ulonglong fuzzydate) const = 0;
+ date_mode_t fuzzydate) const = 0;
bool validate_MMDD(bool not_zero_date, uint month, uint day,
- ulonglong fuzzydate) const
+ date_mode_t fuzzydate) const
{
if (!not_zero_date)
- return fuzzydate & TIME_NO_ZERO_DATE;
+ return bool(fuzzydate & TIME_NO_ZERO_DATE);
if (!month || !day)
- return fuzzydate & TIME_NO_ZERO_IN_DATE;
+ return bool(fuzzydate & TIME_NO_ZERO_IN_DATE);
return false;
}
public:
@@ -2657,20 +2830,23 @@ public:
:Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
{}
- int store(const char *to, size_t length, CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int store_time_dec(const MYSQL_TIME *ltime, uint dec);
- int store_decimal(const my_decimal *);
bool validate_value_in_record(THD *thd, const uchar *record) const;
};
class Field_timestamp :public Field_temporal {
protected:
- sql_mode_t sql_mode_for_timestamp(THD *thd) const;
- int store_TIME_with_warning(THD *, MYSQL_TIME *, const ErrConv *,
- int warnings, bool have_smth_to_conv);
+ int store_TIME_with_warning(THD *, const Datetime *,
+ const ErrConv *, int warn);
+ virtual void store_TIMEVAL(const timeval &tv)
+ {
+ int4store(ptr, tv.tv_sec);
+ }
+ void store_TIMESTAMP(const Timestamp &ts)
+ {
+ store_TIMEVAL(ts.tv());
+ }
+ int zero_time_stored_return_code_with_warning();
public:
Field_timestamp(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
@@ -2680,12 +2856,13 @@ public:
const Type_handler *type_handler() const { return &type_handler_timestamp; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
Copy_func *get_copy_func(const Field *from) const;
+ sql_mode_t conversion_depends_on_sql_mode(THD *, Item *) const;
int store(const char *to,size_t length,CHARSET_INFO *charset);
int store(double nr);
int store(longlong nr, bool unsigned_val);
int store_time_dec(const MYSQL_TIME *ltime, uint dec);
int store_decimal(const my_decimal *);
- int store_timestamp(my_time_t timestamp, ulong sec_part);
+ int store_timestamp_dec(const timeval &ts, uint dec);
int save_in_field(Field *to);
double val_real(void);
longlong val_int(void);
@@ -2703,11 +2880,19 @@ public:
{
return get_timestamp(ptr, sec_part);
}
- virtual void store_TIME(my_time_t timestamp, ulong sec_part)
+ /*
+ This method is used by storage/perfschema and
+ Item_func_now_local::save_in_field().
+ */
+ void store_TIME(my_time_t ts, ulong sec_part)
{
- int4store(ptr,timestamp);
+ int warn;
+ time_round_mode_t mode= Datetime::default_round_mode(get_thd());
+ store_TIMESTAMP(Timestamp(ts, sec_part).round(decimals(), mode, &warn));
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ int store_native(const Native &value);
+ bool val_native(Native *to);
uchar *pack(uchar *to, const uchar *from,
uint max_length __attribute__((unused)))
{
@@ -2775,6 +2960,7 @@ class Field_timestamp_hires :public Field_timestamp_with_dec {
{
return Type_handler_timestamp::sec_part_bytes(dec);
}
+ void store_TIMEVAL(const timeval &tv);
public:
Field_timestamp_hires(uchar *ptr_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
@@ -2786,8 +2972,8 @@ public:
{
DBUG_ASSERT(dec);
}
+ bool val_native(Native *to);
my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const;
- void store_TIME(my_time_t timestamp, ulong sec_part);
int cmp(const uchar *,const uchar *);
uint32 pack_length() const { return 4 + sec_part_bytes(dec); }
uint size_of() const { return sizeof(*this); }
@@ -2803,6 +2989,7 @@ class Field_timestampf :public Field_timestamp_with_dec {
*metadata_ptr= (uchar) decimals();
return 1;
}
+ void store_TIMEVAL(const timeval &tv);
public:
Field_timestampf(uchar *ptr_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
@@ -2831,12 +3018,12 @@ public:
}
void set_max();
bool is_max();
- void store_TIME(my_time_t timestamp, ulong sec_part);
my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const;
my_time_t get_timestamp(ulong *sec_part) const
{
return get_timestamp(ptr, sec_part);
}
+ bool val_native(Native *to);
uint size_of() const { return sizeof(*this); }
};
@@ -2849,7 +3036,10 @@ public:
:Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, 1, 1)
{}
- const Type_handler *type_handler() const { return &type_handler_year; }
+ const Type_handler *type_handler() const
+ {
+ return field_length == 2 ? &type_handler_year2 : &type_handler_year;
+ }
Copy_func *get_copy_func(const Field *from) const
{
if (eq_def(from))
@@ -2863,7 +3053,7 @@ public:
return do_field_string;
}
case TIME_RESULT:
- return do_field_temporal;
+ return do_field_date;
case DECIMAL_RESULT:
return do_field_decimal;
case REAL_RESULT:
@@ -2884,7 +3074,7 @@ public:
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool send_binary(Protocol *protocol);
Information_schema_numeric_attributes
information_schema_numeric_attributes() const
@@ -2896,18 +3086,44 @@ public:
};
-class Field_date :public Field_temporal_with_date {
- void store_TIME(MYSQL_TIME *ltime);
- bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
+class Field_date_common: public Field_temporal_with_date
+{
+protected:
+ int store_TIME_with_warning(const Datetime *ltime, const ErrConv *str,
+ int was_cut);
+public:
+ Field_date_common(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg,
+ const LEX_CSTRING *field_name_arg)
+ :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH,
+ null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg)
+ {}
+ Copy_func *get_copy_func(const Field *from) const;
+ SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op, Item *value);
+ int store(const char *to, size_t length, CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, bool unsigned_val);
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec);
+ int store_decimal(const my_decimal *);
+};
+
+
+class Field_date :public Field_date_common
+{
+ void store_TIME(const MYSQL_TIME *ltime);
+ bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const;
public:
Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg)
- :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg) {}
+ :Field_date_common(ptr_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg) {}
const Type_handler *type_handler() const { return &type_handler_date; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{ return Field_date::get_TIME(ltime, ptr, fuzzydate); }
double val_real(void);
longlong val_int(void);
@@ -2931,14 +3147,15 @@ public:
};
-class Field_newdate :public Field_temporal_with_date {
- void store_TIME(MYSQL_TIME *ltime);
- bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
+class Field_newdate :public Field_date_common
+{
+ void store_TIME(const MYSQL_TIME *ltime);
+ bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const;
public:
Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg)
- :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg)
+ :Field_date_common(ptr_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg)
{}
const Type_handler *type_handler() const { return &type_handler_newdate; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; }
@@ -2951,8 +3168,9 @@ public:
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 3; }
void sql_type(String &str) const;
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{ return Field_newdate::get_TIME(ltime, ptr, fuzzydate); }
+ longlong val_datetime_packed(THD *thd);
uint size_of() const { return sizeof(*this); }
Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item);
};
@@ -2967,14 +3185,12 @@ class Field_time :public Field_temporal {
long curdays;
protected:
virtual void store_TIME(const MYSQL_TIME *ltime);
- int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str,
- int was_cut, int have_smth_to_conv);
- void set_warnings(Sql_condition::enum_warning_level level,
- const ErrConv *str, int was_cut)
+ void store_TIME(const Time &t)
{
- Field_temporal::set_warnings(level, str, was_cut, MYSQL_TIMESTAMP_TIME);
+ return store_TIME(t.get_mysql_time());
}
- bool check_zero_in_date_with_warn(ulonglong fuzzydate);
+ int store_TIME_with_warning(const Time *ltime, const ErrConv *str, int warn);
+ bool check_zero_in_date_with_warn(date_mode_t fuzzydate);
static void do_field_time(Copy_field *copy);
public:
Field_time(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
@@ -3000,6 +3216,9 @@ public:
return real_type() == from->real_type() &&
decimals() == from->decimals();
}
+ sql_mode_t conversion_depends_on_sql_mode(THD *, Item *) const;
+ int store_native(const Native &value);
+ bool val_native(Native *to);
int store_time_dec(const MYSQL_TIME *ltime, uint dec);
int store(const char *to,size_t length,CHARSET_INFO *charset);
int store(double nr);
@@ -3008,7 +3227,7 @@ public:
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool send_binary(Protocol *protocol);
int cmp(const uchar *,const uchar *);
void sort_string(uchar *buff,uint length);
@@ -3069,7 +3288,7 @@ public:
((TIME_MAX_VALUE_SECONDS+1LL)*TIME_SECOND_PART_FACTOR), dec);
}
int reset(void);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
int cmp(const uchar *,const uchar *);
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return Type_handler_time::hires_bytes(dec); }
@@ -3120,14 +3339,20 @@ public:
return memcmp(a_ptr, b_ptr, pack_length());
}
int reset();
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ longlong val_time_packed(THD *thd);
+ int store_native(const Native &value);
+ bool val_native(Native *to);
uint size_of() const { return sizeof(*this); }
};
class Field_datetime :public Field_temporal_with_date {
- void store_TIME(MYSQL_TIME *ltime);
- bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
+ void store_TIME(const MYSQL_TIME *ltime);
+ bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const;
+protected:
+ int store_TIME_with_warning(const Datetime *ltime, const ErrConv *str,
+ int was_cut);
public:
Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
@@ -3141,6 +3366,12 @@ public:
}
const Type_handler *type_handler() const { return &type_handler_datetime; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; }
+ sql_mode_t conversion_depends_on_sql_mode(THD *, Item *) const;
+ int store(const char *to, size_t length, CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, bool unsigned_val);
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec);
+ int store_decimal(const my_decimal *);
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
@@ -3149,7 +3380,7 @@ public:
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 8; }
void sql_type(String &str) const;
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{ return Field_datetime::get_TIME(ltime, ptr, fuzzydate); }
int set_time();
uchar *pack(uchar* to, const uchar *from,
@@ -3212,8 +3443,8 @@ public:
DATETIME(1..6)
*/
class Field_datetime_hires :public Field_datetime_with_dec {
- void store_TIME(MYSQL_TIME *ltime);
- bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
+ void store_TIME(const MYSQL_TIME *ltime);
+ bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const;
public:
Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
@@ -3225,7 +3456,7 @@ public:
}
int cmp(const uchar *,const uchar *);
uint32 pack_length() const { return Type_handler_datetime::hires_bytes(dec); }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{ return Field_datetime_hires::get_TIME(ltime, ptr, fuzzydate); }
uint size_of() const { return sizeof(*this); }
};
@@ -3235,8 +3466,8 @@ public:
DATETIME(0..6) - MySQL56 version
*/
class Field_datetimef :public Field_datetime_with_dec {
- void store_TIME(MYSQL_TIME *ltime);
- bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
+ void store_TIME(const MYSQL_TIME *ltime);
+ bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const;
int save_field_metadata(uchar *metadata_ptr)
{
*metadata_ptr= (uchar) decimals();
@@ -3267,8 +3498,9 @@ public:
return memcmp(a_ptr, b_ptr, pack_length());
}
int reset();
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{ return Field_datetimef::get_TIME(ltime, ptr, fuzzydate); }
+ longlong val_datetime_packed(THD *thd);
uint size_of() const { return sizeof(*this); }
};
@@ -3377,6 +3609,11 @@ public:
void sort_string(uchar *buff,uint length);
void sql_type(String &str) const;
void sql_rpl_type(String*) const;
+ bool is_equal(const Column_definition &new_field) const;
+ bool can_be_converted_by_engine(const Column_definition &new_type) const
+ {
+ return table->file->can_convert_string(this, new_type);
+ }
virtual uchar *pack(uchar *to, const uchar *from,
uint max_length);
virtual const uchar *unpack(uchar* to, const uchar *from,
@@ -3403,6 +3640,7 @@ public:
virtual uint get_key_image(uchar *buff,uint length, imagetype type);
sql_mode_t value_depends_on_sql_mode() const;
sql_mode_t can_handle_sql_mode_dependency_on_store() const;
+ void print_key_value(String *out, uint32 length);
private:
int save_field_metadata(uchar *first_byte);
};
@@ -3501,9 +3739,14 @@ public:
Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_ptr, uint32 length,
uchar *new_null_ptr, uint new_null_bit);
- uint is_equal(Create_field *new_field);
+ bool is_equal(const Column_definition &new_field) const;
+ bool can_be_converted_by_engine(const Column_definition &new_type) const
+ {
+ return table->file->can_convert_varstring(this, new_type);
+ }
void hash(ulong *nr, ulong *nr2);
- uint length_size() { return length_bytes; }
+ uint length_size() const { return length_bytes; }
+ void print_key_value(String *out, uint32 length);
private:
int save_field_metadata(uchar *first_byte);
};
@@ -3539,6 +3782,7 @@ private:
str.append(STRING_WITH_LEN(" /*!100301 COMPRESSED*/"));
}
uint32 max_display_length() const { return field_length - 1; }
+ uint32 character_octet_length() const { return field_length - 1; }
uint32 char_length() const
{
return (field_length - 1) / field_charset->mbmaxlen;
@@ -3671,10 +3915,11 @@ public:
Information_schema_character_attributes
information_schema_character_attributes() const
{
- uint32 octets= Field_blob::octet_length();
+ uint32 octets= Field_blob::character_octet_length();
uint32 chars= octets / field_charset->mbminlen;
return Information_schema_character_attributes(octets, chars);
}
+ void make_send_field(Send_field *);
Copy_func *get_copy_func(const Field *from) const
{
/*
@@ -3703,6 +3948,7 @@ public:
!compression_method() == !from->compression_method() &&
!table->copy_blobs;
}
+ bool make_empty_rec_store_default_value(THD *thd, Item *item);
int store(const char *to, size_t length, CHARSET_INFO *charset);
using Field_str::store;
double val_real(void);
@@ -3836,8 +4082,13 @@ public:
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
uint32 max_display_length() const;
uint32 char_length() const;
- uint32 octet_length() const;
- uint is_equal(Create_field *new_field);
+ uint32 character_octet_length() const;
+ bool is_equal(const Column_definition &new_field) const;
+ bool can_be_converted_by_engine(const Column_definition &new_type) const
+ {
+ return table->file->can_convert_blob(this, new_type);
+ }
+ void print_key_value(String *out, uint32 length);
friend void TABLE::remember_blob_values(String *blob_storage);
friend void TABLE::restore_blob_values(String *blob_storage);
@@ -3928,11 +4179,35 @@ public:
{
return Information_schema_character_attributes();
}
+ void make_send_field(Send_field *to)
+ {
+ Field_longstr::make_send_field(to);
+ }
bool can_optimize_range(const Item_bool_func *cond,
const Item *item,
bool is_eq_func) const;
void sql_type(String &str) const;
- uint is_equal(Create_field *new_field);
+ Copy_func *get_copy_func(const Field *from) const
+ {
+ if (type_handler() == from->type_handler() &&
+ (geom_type == GEOM_GEOMETRY ||
+ geom_type == static_cast<const Field_geom*>(from)->geom_type))
+ return get_identical_copy_func();
+ return do_conv_blob;
+ }
+ bool memcpy_field_possible(const Field *from) const
+ {
+ return type_handler() == from->type_handler() &&
+ (geom_type == GEOM_GEOMETRY ||
+ geom_type == static_cast<const Field_geom*>(from)->geom_type) &&
+ !table->copy_blobs;
+ }
+ bool is_equal(const Column_definition &new_field) const;
+ bool can_be_converted_by_engine(const Column_definition &new_type) const
+ {
+ return table->file->can_convert_geom(this, new_type);
+ }
+
int store(const char *to, size_t length, CHARSET_INFO *charset);
int store(double nr);
int store(longlong nr, bool unsigned_val);
@@ -3955,9 +4230,13 @@ public:
bool load_data_set_null(THD *thd);
bool load_data_set_no_data(THD *thd, bool fixed_format);
- geometry_type get_geometry_type() { return geom_type; };
+ geometry_type get_geometry_type() const { return geom_type; };
static geometry_type geometry_type_merge(geometry_type, geometry_type);
uint get_srid() { return srid; }
+ void print_key_value(String *out, uint32 length)
+ {
+ out->append(STRING_WITH_LEN("unprintable_geometry_value"));
+ }
};
uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields);
@@ -4016,6 +4295,16 @@ public:
return save_in_field_str(to);
}
bool memcpy_field_possible(const Field *from) const { return false; }
+ void make_empty_rec_reset(THD *thd)
+ {
+ if (flags & NOT_NULL_FLAG)
+ {
+ set_notnull();
+ store((longlong) 1, true);
+ }
+ else
+ reset();
+ }
int store(const char *to,size_t length,CHARSET_INFO *charset);
int store(double nr);
int store(longlong nr, bool unsigned_val);
@@ -4063,7 +4352,7 @@ public:
bool is_eq_func) const;
private:
int save_field_metadata(uchar *first_byte);
- uint is_equal(Create_field *new_field);
+ bool is_equal(const Column_definition &new_field) const;
};
@@ -4082,6 +4371,11 @@ public:
{
flags=(flags & ~ENUM_FLAG) | SET_FLAG;
}
+ void make_empty_rec_reset(THD *thd)
+ {
+ Field::make_empty_rec_reset(thd);
+ }
+
int store_field(Field *from) { return from->save_in_field(this); }
int store(const char *to,size_t length,CHARSET_INFO *charset);
int store(double nr) { return Field_set::store((longlong) nr, FALSE); }
@@ -4170,7 +4464,7 @@ public:
int key_cmp(const uchar *a, const uchar *b)
{ return cmp_binary((uchar *) a, (uchar *) b); }
int key_cmp(const uchar *str, uint length);
- int cmp_offset(uint row_offset);
+ int cmp_offset(my_ptrdiff_t row_offset);
bool update_min(Field *min_val, bool force_update)
{
longlong val= val_int();
@@ -4237,14 +4531,31 @@ public:
bit_ptr == ((Field_bit *)field)->bit_ptr &&
bit_ofs == ((Field_bit *)field)->bit_ofs);
}
- uint is_equal(Create_field *new_field);
+ bool is_equal(const Column_definition &new_field) const;
void move_field_offset(my_ptrdiff_t ptr_diff)
{
Field::move_field_offset(ptr_diff);
- bit_ptr= ADD_TO_PTR(bit_ptr, ptr_diff, uchar*);
+
+ /*
+ clang does not like when things are added to a null pointer, even if
+ it is never referenced.
+ */
+ if (bit_ptr)
+ bit_ptr= ADD_TO_PTR(bit_ptr, ptr_diff, uchar*);
}
void hash(ulong *nr, ulong *nr2);
+ SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op, Item *value)
+ {
+ return get_mm_leaf_int(param, key_part, cond, op, value, true);
+ }
+ void print_key_value(String *out, uint32 length)
+ {
+ val_int_as_str(out, 1);
+ }
+
private:
virtual size_t do_last_null_byte() const;
int save_field_metadata(uchar *first_byte);
@@ -4289,21 +4600,58 @@ public:
extern const LEX_CSTRING null_clex_str;
-Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root,
- uchar *ptr, uint32 field_length,
- uchar *null_pos, uchar null_bit,
- uint pack_flag, const Type_handler *handler,
- CHARSET_INFO *cs,
- Field::geometry_type geom_type, uint srid,
- Field::utype unireg_check,
- TYPELIB *interval, const LEX_CSTRING *field_name,
- uint32 flags);
+class Column_definition_attributes
+{
+public:
+ /*
+ At various stages in execution this can be length of field in bytes or
+ max number of characters.
+ */
+ ulonglong length;
+ Field::utype unireg_check;
+ TYPELIB *interval; // Which interval to use
+ CHARSET_INFO *charset;
+ uint32 srid;
+ Field::geometry_type geom_type;
+ uint pack_flag;
+ Column_definition_attributes()
+ :length(0),
+ unireg_check(Field::NONE),
+ interval(NULL),
+ charset(&my_charset_bin),
+ srid(0),
+ geom_type(Field::GEOM_GEOMETRY),
+ pack_flag(0)
+ { }
+ Column_definition_attributes(const Field *field);
+ Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const Record_addr *rec,
+ const Type_handler *handler,
+ const LEX_CSTRING *field_name,
+ uint32 flags) const;
+ uint temporal_dec(uint intlen) const
+ {
+ return (uint) (length > intlen ? length - intlen - 1 : 0);
+ }
+ uint pack_flag_to_pack_length() const;
+ void frm_pack_basic(uchar *buff) const;
+ void frm_pack_charset(uchar *buff) const;
+ void frm_unpack_basic(const uchar *buff);
+ bool frm_unpack_charset(TABLE_SHARE *share, const uchar *buff);
+ CHARSET_INFO *explicit_or_derived_charset(const Column_derived_attributes
+ *derived_attr) const
+ {
+ return charset ? charset : derived_attr->charset();
+ }
+};
+
/*
Create field class for CREATE TABLE
*/
class Column_definition: public Sql_alloc,
- public Type_handler_hybrid_field_type
+ public Type_handler_hybrid_field_type,
+ public Column_definition_attributes
{
/**
Create "interval" from "interval_list".
@@ -4358,11 +4706,6 @@ public:
WITHOUT_VERSIONING
};
Item *on_update; // ON UPDATE NOW()
- /*
- At various stages in execution this can be length of field in bytes or
- max number of characters.
- */
- ulonglong length;
field_visibility_t invisible;
/*
The value of `length' as set by parser: is the number of characters
@@ -4370,15 +4713,9 @@ public:
*/
uint32 char_length;
uint decimals, flags, pack_length, key_length;
- Field::utype unireg_check;
- TYPELIB *interval; // Which interval to use
List<String> interval_list;
- CHARSET_INFO *charset;
- uint32 srid;
- Field::geometry_type geom_type;
engine_option_value *option_list;
- uint pack_flag;
/*
This is additinal data provided for any computed(virtual) field.
@@ -4392,18 +4729,17 @@ public:
enum_column_versioning versioning;
+ Table_period_info *period;
+
Column_definition()
:Type_handler_hybrid_field_type(&type_handler_null),
compression_method_ptr(0),
comment(null_clex_str),
- on_update(NULL), length(0), invisible(VISIBLE), char_length(0),
- decimals(0),
- flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE),
- interval(0), charset(&my_charset_bin),
- srid(0), geom_type(Field::GEOM_GEOMETRY),
- option_list(NULL), pack_flag(0),
+ on_update(NULL), invisible(VISIBLE), char_length(0), decimals(0),
+ flags(0), pack_length(0), key_length(0),
+ option_list(NULL),
vcol_info(0), default_value(0), check_constraint(0),
- versioning(VERSIONING_NOT_SET)
+ versioning(VERSIONING_NOT_SET), period(NULL)
{
interval_list.empty();
}
@@ -4441,6 +4777,15 @@ public:
void create_length_to_internal_length_bit();
void create_length_to_internal_length_newdecimal();
+ /*
+ Prepare the "charset" member for string data types,
+ such as CHAR, VARCHAR, TEXT, ENUM, SET:
+ - derive the charset if not specified explicitly
+ - find a _bin collation if the BINARY comparison style was specified, e.g.:
+ CREATE TABLE t1 (a VARCHAR(10) BINARY) CHARSET utf8;
+ */
+ bool prepare_charset_for_string(const Column_derived_attributes *dattr);
+
/**
Prepare a SET/ENUM field.
Create "interval" from "interval_list" if needed, and adjust "length".
@@ -4476,7 +4821,13 @@ public:
bool sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root);
bool prepare_stage1(THD *thd, MEM_ROOT *mem_root,
- handler *file, ulonglong table_flags);
+ handler *file, ulonglong table_flags,
+ const Column_derived_attributes *derived_attr);
+ void prepare_stage1_simple(CHARSET_INFO *cs)
+ {
+ charset= cs;
+ create_length_to_internal_length_simple();
+ }
bool prepare_stage1_typelib(THD *thd, MEM_ROOT *mem_root,
handler *file, ulonglong table_flags);
bool prepare_stage1_string(THD *thd, MEM_ROOT *mem_root,
@@ -4484,15 +4835,19 @@ public:
bool prepare_stage1_bit(THD *thd, MEM_ROOT *mem_root,
handler *file, ulonglong table_flags);
+ bool bulk_alter(const Column_derived_attributes *derived_attr,
+ const Column_bulk_alter_attributes *bulk_attr)
+ {
+ return type_handler()->Column_definition_bulk_alter(this,
+ derived_attr,
+ bulk_attr);
+ }
void redefine_stage1_common(const Column_definition *dup_field,
- const handler *file,
- const Schema_specification_st *schema);
- bool redefine_stage1(const Column_definition *dup_field, const handler *file,
- const Schema_specification_st *schema)
+ const handler *file);
+ bool redefine_stage1(const Column_definition *dup_field, const handler *file)
{
const Type_handler *handler= dup_field->type_handler();
- return handler->Column_definition_redefine_stage1(this, dup_field,
- file, schema);
+ return handler->Column_definition_redefine_stage1(this, dup_field, file);
}
bool prepare_stage2(handler *handler, ulonglong table_flags);
bool prepare_stage2_blob(handler *handler,
@@ -4510,6 +4865,7 @@ public:
bool fix_attributes_bit();
bool check(THD *thd);
+ bool validate_check_constraint(THD *thd);
bool stored_in_db() const { return !vcol_info || vcol_info->stored_in_db; }
@@ -4531,20 +4887,18 @@ public:
}
Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root,
- uchar *ptr, uchar *null_pos, uchar null_bit,
+ const Record_addr *addr,
const LEX_CSTRING *field_name_arg) const
{
- return ::make_field(share, mem_root, ptr,
- (uint32)length, null_pos, null_bit,
- pack_flag, type_handler(), charset,
- geom_type, srid, unireg_check, interval,
- field_name_arg, flags);
+ return Column_definition_attributes::make_field(share, mem_root, addr,
+ type_handler(),
+ field_name_arg, flags);
}
Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root,
const LEX_CSTRING *field_name_arg) const
{
- return make_field(share, mem_root, (uchar *) 0, (uchar *) "", 0,
- field_name_arg);
+ Record_addr addr(true);
+ return make_field(share, mem_root, &addr, field_name_arg);
}
/* Return true if default is an expression that must be saved explicitely */
bool has_default_expression();
@@ -4616,6 +4970,14 @@ public:
}
return 0;
}
+ static Row_definition_list *make(MEM_ROOT *mem_root, Spvar_definition *var)
+ {
+ Row_definition_list *list;
+ if (!(list= new (mem_root) Row_definition_list()))
+ return NULL;
+ return list->push_back(var, mem_root) ? NULL : list;
+ }
+ bool append_uniq(MEM_ROOT *thd, Spvar_definition *var);
bool adjust_formal_params_to_actual_params(THD *thd, List<Item> *args);
bool adjust_formal_params_to_actual_params(THD *thd,
Item **args, uint arg_count);
@@ -4760,7 +5122,7 @@ public:
/** structure with parsed options (for comparing fields in ALTER TABLE) */
ha_field_option_struct *option_struct;
uint offset;
- uint8 interval_id; // For rea_create_table
+ uint8 interval_id;
bool create_if_not_exists; // Used in ALTER TABLE IF NOT EXISTS
Create_field():
@@ -4787,15 +5149,86 @@ public:
A class for sending info to the client
*/
-class Send_field :public Sql_alloc {
- public:
+class Send_field :public Sql_alloc,
+ public Type_handler_hybrid_field_type
+{
+public:
const char *db_name;
const char *table_name,*org_table_name;
LEX_CSTRING col_name, org_col_name;
ulong length;
uint flags, decimals;
- enum_field_types type;
Send_field() {}
+ Send_field(Field *field)
+ {
+ field->make_send_field(this);
+ DBUG_ASSERT(table_name != 0);
+ normalize();
+ }
+ Send_field(THD *thd, Item *item);
+ Send_field(Field *field,
+ const char *db_name_arg,
+ const char *table_name_arg)
+ :Type_handler_hybrid_field_type(field->type_handler()),
+ db_name(db_name_arg),
+ table_name(table_name_arg),
+ org_table_name(table_name_arg),
+ col_name(field->field_name),
+ org_col_name(field->field_name),
+ length(field->field_length),
+ flags(field->table->maybe_null ?
+ (field->flags & ~NOT_NULL_FLAG) : field->flags),
+ decimals(field->decimals())
+ {
+ normalize();
+ }
+
+private:
+ void normalize()
+ {
+ /* limit number of decimals for float and double */
+ if (type_handler()->field_type() == MYSQL_TYPE_FLOAT ||
+ type_handler()->field_type() == MYSQL_TYPE_DOUBLE)
+ set_if_smaller(decimals, FLOATING_POINT_DECIMALS);
+ }
+public:
+ // This should move to Type_handler eventually
+ uint32 max_char_length(CHARSET_INFO *cs) const
+ {
+ return type_handler()->field_type() >= MYSQL_TYPE_TINY_BLOB &&
+ type_handler()->field_type() <= MYSQL_TYPE_BLOB ?
+ length / cs->mbminlen :
+ length / cs->mbmaxlen;
+ }
+ uint32 max_octet_length(CHARSET_INFO *from, CHARSET_INFO *to) const
+ {
+ /*
+ For TEXT/BLOB columns, field_length describes the maximum data
+ length in bytes. There is no limit to the number of characters
+ that a TEXT column can store, as long as the data fits into
+ the designated space.
+ For the rest of textual columns, field_length is evaluated as
+ char_count * mbmaxlen, where character count is taken from the
+ definition of the column. In other words, the maximum number
+ of characters here is limited by the column definition.
+
+ When one has a LONG TEXT column with a single-byte
+ character set, and the connection character set is multi-byte, the
+ client may get fields longer than UINT_MAX32, due to
+ <character set column> -> <character set connection> conversion.
+ In that case column max length would not fit into the 4 bytes
+ reserved for it in the protocol. So we cut it here to UINT_MAX32.
+ */
+ return char_to_byte_length_safe(max_char_length(from), to->mbmaxlen);
+ }
+
+ // This should move to Type_handler eventually
+ bool is_sane() const
+ {
+ return (decimals <= FLOATING_POINT_DECIMALS ||
+ (type_handler()->field_type() != MYSQL_TYPE_FLOAT &&
+ type_handler()->field_type() != MYSQL_TYPE_DOUBLE));
+ }
};
@@ -4870,7 +5303,7 @@ bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name,
#define FIELDFLAG_DEC_SHIFT 8
#define FIELDFLAG_MAX_DEC 63U
-#define MTYP_TYPENR(type) (type & 127U) /* Remove bits from type */
+#define MTYP_TYPENR(type) ((type) & 127U) // Remove bits from type
#define f_is_dec(x) ((x) & FIELDFLAG_DECIMAL)
#define f_is_num(x) ((x) & FIELDFLAG_NUMBER)
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index 88fdb1387a3..7ec93e032e6 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -413,8 +413,8 @@ void Field::do_field_real(Copy_field *copy)
void Field::do_field_decimal(Copy_field *copy)
{
- my_decimal value;
- copy->to_field->store_decimal(copy->from_field->val_decimal(&value));
+ my_decimal value(copy->from_field);
+ copy->to_field->store_decimal(&value);
}
@@ -425,24 +425,32 @@ void Field::do_field_timestamp(Copy_field *copy)
}
-void Field::do_field_temporal(Copy_field *copy)
+void Field::do_field_temporal(Copy_field *copy, date_mode_t fuzzydate)
{
MYSQL_TIME ltime;
// TODO: we now need to check result
- if (copy->from_field->get_date(&ltime, 0))
+ if (copy->from_field->get_date(&ltime, fuzzydate))
copy->to_field->reset();
else
copy->to_field->store_time_dec(&ltime, copy->from_field->decimals());
}
+void Field::do_field_datetime(Copy_field *copy)
+{
+ return do_field_temporal(copy, Datetime::Options(TIME_CONV_NONE, current_thd));
+}
+
+
+void Field::do_field_date(Copy_field *copy)
+{
+ return do_field_temporal(copy, Date::Options(TIME_CONV_NONE));
+}
+
+
void Field_time::do_field_time(Copy_field *copy)
{
- MYSQL_TIME ltime;
- if (copy->from_field->get_date(&ltime, TIME_TIME_ONLY))
- copy->to_field->reset();
- else
- copy->to_field->store_time_dec(&ltime, copy->from_field->decimals());
+ return do_field_temporal(copy, Time::Options(current_thd));
}
@@ -720,13 +728,20 @@ void Copy_field::set(Field *to,Field *from,bool save)
Field::Copy_func *Field_timestamp::get_copy_func(const Field *from) const
{
Field::Copy_func *copy= Field_temporal::get_copy_func(from);
- if (copy == do_field_temporal && from->type() == MYSQL_TYPE_TIMESTAMP)
+ if (copy == do_field_datetime && from->type() == MYSQL_TYPE_TIMESTAMP)
return do_field_timestamp;
else
return copy;
}
+Field::Copy_func *Field_date_common::get_copy_func(const Field *from) const
+{
+ Field::Copy_func *copy= Field_temporal::get_copy_func(from);
+ return copy == do_field_datetime ? do_field_date : copy;
+}
+
+
Field::Copy_func *Field_temporal::get_copy_func(const Field *from) const
{
/* If types are not 100 % identical then convert trough get_date() */
@@ -739,7 +754,7 @@ Field::Copy_func *Field_temporal::get_copy_func(const Field *from) const
if (!eq_def(from) ||
(table->in_use->variables.sql_mode &
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE)))
- return do_field_temporal;
+ return do_field_datetime;
return get_identical_copy_func();
}
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 823e98b3a8a..aa25474be1a 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -55,9 +55,7 @@ static bool save_index(Sort_param *param, uint count,
static uint suffix_length(ulong string_length);
static uint sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
bool *multi_byte_charset);
-static SORT_ADDON_FIELD *get_addon_fields(ulong max_length_for_sort_data,
- Field **ptabfield,
- uint sortlength,
+static SORT_ADDON_FIELD *get_addon_fields(TABLE *table, uint sortlength,
LEX_STRING *addon_buf);
static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
uchar *buff, uchar *buff_end);
@@ -66,7 +64,6 @@ static bool check_if_pq_applicable(Sort_param *param, SORT_INFO *info,
ha_rows records, size_t memory_available);
void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
- ulong max_length_for_sort_data,
ha_rows maxrows, bool sort_positions)
{
DBUG_ASSERT(addon_field == 0 && addon_buf.length == 0);
@@ -80,8 +77,7 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
Get the descriptors of all fields whose values are appended
to sorted fields and get its total length in addon_buf.length
*/
- addon_field= get_addon_fields(max_length_for_sort_data,
- table->field, sort_length, &addon_buf);
+ addon_field= get_addon_fields(table, sort_length, &addon_buf);
}
if (addon_field)
{
@@ -185,9 +181,7 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
param.init_for_filesort(sortlength(thd, filesort->sortorder, s_length,
&multi_byte_charset),
- table,
- thd->variables.max_length_for_sort_data,
- max_rows, filesort->sort_positions);
+ table, max_rows, filesort->sort_positions);
sort->addon_buf= param.addon_buf;
sort->addon_field= param.addon_field;
@@ -254,7 +248,7 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
}
if (memory_available < min_sort_memory)
{
- my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR));
+ my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR_LOG + ME_FATAL));
goto err;
}
tracker->report_sort_buffer_size(sort->sort_buffer_size());
@@ -714,7 +708,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
uchar *ref_pos, *next_pos, ref_buff[MAX_REFLENGTH];
TABLE *sort_form;
handler *file;
- MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set;
+ MY_BITMAP *save_read_set, *save_write_set;
Item *sort_cond;
ha_rows retval;
DBUG_ENTER("find_all_keys");
@@ -749,13 +743,11 @@ static ha_rows find_all_keys(THD *thd, Sort_param *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 */
DBUG_ASSERT(save_read_set != &sort_form->tmp_set);
bitmap_clear_all(&sort_form->tmp_set);
- sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set,
- &sort_form->tmp_set);
+ sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set);
register_used_fields(param);
if (quick_select)
select->quick->add_used_key_part_to_set();
@@ -813,16 +805,12 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
*/
MY_BITMAP *tmp_read_set= sort_form->read_set;
MY_BITMAP *tmp_write_set= sort_form->write_set;
- MY_BITMAP *tmp_vcol_set= sort_form->vcol_set;
if (select->cond->with_subquery())
- sort_form->column_bitmaps_set(save_read_set, save_write_set,
- save_vcol_set);
+ sort_form->column_bitmaps_set(save_read_set, save_write_set);
write_record= (select->skip_record(thd) > 0);
if (select->cond->with_subquery())
- sort_form->column_bitmaps_set(tmp_read_set,
- tmp_write_set,
- tmp_vcol_set);
+ sort_form->column_bitmaps_set(tmp_read_set, tmp_write_set);
}
else
write_record= true;
@@ -868,7 +856,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
}
/* Signal we should use original column read and write maps */
- sort_form->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set);
+ sort_form->column_bitmaps_set(save_read_set, save_write_set);
if (unlikely(thd->is_error()))
DBUG_RETURN(HA_POS_ERROR);
@@ -876,8 +864,8 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
DBUG_PRINT("test",("error: %d indexpos: %d",error,indexpos));
if (unlikely(error != HA_ERR_END_OF_FILE))
{
- file->print_error(error,MYF(ME_ERROR | ME_WAITTANG)); // purecov: inspected
- DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
+ file->print_error(error,MYF(ME_ERROR_LOG));
+ DBUG_RETURN(HA_POS_ERROR);
}
if (indexpos && idx &&
write_keys(param, fs_info, idx, buffpek_pointers, tempfile))
@@ -889,7 +877,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
DBUG_RETURN(retval);
err:
- sort_form->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set);
+ sort_form->column_bitmaps_set(save_read_set, save_write_set);
DBUG_RETURN(HA_POS_ERROR);
} /* find_all_keys */
@@ -1067,7 +1055,10 @@ Type_handler_temporal_result::make_sort_key(uchar *to, Item *item,
Sort_param *param) const
{
MYSQL_TIME buf;
- if (item->get_date_result(&buf, TIME_INVALID_DATES))
+ // This is a temporal type. No nanoseconds. Rounding mode is not important.
+ DBUG_ASSERT(item->cmp_type() == TIME_RESULT);
+ static const Temporal::Options opt(TIME_INVALID_DATES, TIME_FRAC_NONE);
+ if (item->get_date_result(current_thd, &buf, opt))
{
DBUG_ASSERT(item->maybe_null);
DBUG_ASSERT(item->null_value);
@@ -1081,6 +1072,39 @@ Type_handler_temporal_result::make_sort_key(uchar *to, Item *item,
void
+Type_handler_timestamp_common::make_sort_key(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
+{
+ THD *thd= current_thd;
+ uint binlen= my_timestamp_binary_length(item->decimals);
+ Timestamp_or_zero_datetime_native_null native(thd, item);
+ if (native.is_null() || native.is_zero_datetime())
+ {
+ // NULL or '0000-00-00 00:00:00'
+ bzero(to, item->maybe_null ? binlen + 1 : binlen);
+ }
+ else
+ {
+ if (item->maybe_null)
+ *to++= 1;
+ if (native.length() != binlen)
+ {
+ /*
+ Some items can return native representation with a different
+ number of fractional digits, e.g.: GREATEST(ts_3, ts_4) can
+ return a value with 3 fractional digits, although its fractional
+ precision is 4. Re-pack with a proper precision now.
+ */
+ Timestamp(native).to_native(&native, item->datetime_precision(thd));
+ }
+ DBUG_ASSERT(native.length() == binlen);
+ memcpy((char *) to, native.ptr(), binlen);
+ }
+}
+
+
+void
Type_handler::make_sort_key_longlong(uchar *to,
bool maybe_null,
bool null_value,
@@ -1126,9 +1150,8 @@ Type_handler_decimal_result::make_sort_key(uchar *to, Item *item,
}
*to++= 1;
}
- my_decimal2binary(E_DEC_FATAL_ERROR, dec_val, to,
- item->max_length - (item->decimals ? 1 : 0),
- item->decimals);
+ dec_val->to_binary(to, item->max_length - (item->decimals ? 1 : 0),
+ item->decimals);
}
@@ -1888,6 +1911,15 @@ Type_handler_temporal_result::sortlength(THD *thd,
void
+Type_handler_timestamp_common::sortlength(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *sortorder) const
+{
+ sortorder->length= my_timestamp_binary_length(item->decimals);
+}
+
+
+void
Type_handler_int_result::sortlength(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *sortorder) const
@@ -1974,6 +2006,30 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
return length;
}
+bool filesort_use_addons(TABLE *table, uint sortlength,
+ uint *length, uint *fields, uint *null_fields)
+{
+ Field **pfield, *field;
+ *length= *fields= *null_fields= 0;
+
+ for (pfield= table->field; (field= *pfield) ; pfield++)
+ {
+ if (!bitmap_is_set(table->read_set, field->field_index))
+ continue;
+ if (field->flags & BLOB_FLAG)
+ return false;
+ (*length)+= field->max_packed_col_length(field->pack_length());
+ if (field->maybe_null())
+ (*null_fields)++;
+ (*fields)++;
+ }
+ if (!*fields)
+ return false;
+ (*length)+= (*null_fields+7)/8;
+
+ return *length + sortlength <
+ table->in_use->variables.max_length_for_sort_data;
+}
/**
Get descriptors of fields appended to sorted fields and
@@ -1981,7 +2037,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
The function first finds out what fields are used in the result set.
Then it calculates the length of the buffer to store the values of
- these fields together with the value of sort values.
+ these fields together with the value of sort values.
If the calculated length is not greater than max_length_for_sort_data
the function allocates memory for an array of descriptors containing
layouts for the values of the non-sorted fields in the buffer and
@@ -2003,16 +2059,13 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
*/
static SORT_ADDON_FIELD *
-get_addon_fields(ulong max_length_for_sort_data,
- Field **ptabfield, uint sortlength, LEX_STRING *addon_buf)
+get_addon_fields(TABLE *table, uint sortlength, LEX_STRING *addon_buf)
{
Field **pfield;
Field *field;
SORT_ADDON_FIELD *addonf;
- uint length= 0;
- uint fields= 0;
- uint null_fields= 0;
- MY_BITMAP *read_set= (*ptabfield)->table->read_set;
+ uint length, fields, null_fields;
+ MY_BITMAP *read_set= table->read_set;
DBUG_ENTER("get_addon_fields");
/*
@@ -2021,40 +2074,28 @@ get_addon_fields(ulong max_length_for_sort_data,
Note for future refinement:
This this a too strong condition.
Actually we need only the fields referred in the
- result set. And for some of them it makes sense to use
+ result set. And for some of them it makes sense to use
the values directly from sorted fields.
But beware the case when item->cmp_type() != item->result_type()
*/
addon_buf->str= 0;
addon_buf->length= 0;
- for (pfield= ptabfield; (field= *pfield) ; pfield++)
- {
- if (!bitmap_is_set(read_set, field->field_index))
- continue;
- if (field->flags & BLOB_FLAG)
- DBUG_RETURN(0);
- length+= field->max_packed_col_length(field->pack_length());
- if (field->maybe_null())
- null_fields++;
- fields++;
- }
- if (!fields)
- DBUG_RETURN(0);
- length+= (null_fields+7)/8;
+ // see remove_const() for HA_SLOW_RND_POS explanation
+ if (table->file->ha_table_flags() & HA_SLOW_RND_POS)
+ sortlength= 0;
- if (length+sortlength > max_length_for_sort_data ||
- !my_multi_malloc(MYF(MY_WME | MY_THREAD_SPECIFIC),
- &addonf, sizeof(SORT_ADDON_FIELD) * (fields+1),
- &addon_buf->str, length,
- NullS))
+ if (!filesort_use_addons(table, sortlength, &length, &fields, &null_fields) ||
+ !my_multi_malloc(MYF(MY_WME | MY_THREAD_SPECIFIC), &addonf,
+ sizeof(SORT_ADDON_FIELD) * (fields+1),
+ &addon_buf->str, length, NullS))
DBUG_RETURN(0);
addon_buf->length= length;
length= (null_fields+7)/8;
null_fields= 0;
- for (pfield= ptabfield; (field= *pfield) ; pfield++)
+ for (pfield= table->field; (field= *pfield) ; pfield++)
{
if (!bitmap_is_set(read_set, field->field_index))
continue;
@@ -2076,7 +2117,7 @@ get_addon_fields(ulong max_length_for_sort_data,
addonf++;
}
addonf->field= 0; // Put end marker
-
+
DBUG_PRINT("info",("addon_length: %d",length));
DBUG_RETURN(addonf-fields);
}
diff --git a/sql/filesort.h b/sql/filesort.h
index 6926d92c4eb..5f79a5095cc 100644
--- a/sql/filesort.h
+++ b/sql/filesort.h
@@ -161,6 +161,9 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
Filesort_tracker* tracker, JOIN *join=NULL,
table_map first_table_bit=0);
+bool filesort_use_addons(TABLE *table, uint sortlength,
+ uint *length, uint *fields, uint *null_fields);
+
void change_double_for_sort(double nr,uchar *to);
#endif /* FILESORT_INCLUDED */
diff --git a/sql/gcalc_tools.h b/sql/gcalc_tools.h
index 77da791f0b9..e625b355d95 100644
--- a/sql/gcalc_tools.h
+++ b/sql/gcalc_tools.h
@@ -184,7 +184,11 @@ class Gcalc_result_receiver
double first_x, first_y, prev_x, prev_y;
double shape_area;
public:
- Gcalc_result_receiver() : collection_result(FALSE), n_shapes(0), n_holes(0)
+Gcalc_result_receiver() :
+ n_points(0),
+ common_shapetype(Gcalc_function::shape_point),
+ collection_result(FALSE), n_shapes(0), n_holes(0),
+ cur_shape(Gcalc_function::shape_point), shape_pos(0)
{}
int start_shape(Gcalc_function::shape_type shape);
int add_point(double x, double y);
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index bf9d6af997f..24e08501bed 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2005, 2019, Oracle and/or its affiliates.
- Copyright (c) 2009, 2020, MariaDB
+ Copyright (c) 2009, 2021, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1367,7 +1367,7 @@ bool print_admin_msg(THD* thd, uint len,
protocol->store(msgbuf, msg_length, system_charset_info);
if (protocol->write())
{
- sql_print_error("Failed on my_net_write, writing to stderr instead: %s\n",
+ sql_print_error("Failed on my_net_write, writing to stderr instead: %s",
msgbuf);
goto err;
}
@@ -2833,6 +2833,8 @@ bool ha_partition::create_handler_file(const char *name)
}
}
(void) mysql_file_close(file, MYF(0));
+ if (result)
+ mysql_file_delete(key_file_partition, file_name, MYF(MY_WME));
}
else
result= TRUE;
@@ -3660,11 +3662,13 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
err_handler:
DEBUG_SYNC(ha_thd(), "partition_open_error");
- file= &m_file[m_tot_parts - 1];
- while (file-- != m_file)
+ DBUG_ASSERT(m_tot_parts > 0);
+ for (uint i= m_tot_parts - 1; ; --i)
{
- if (bitmap_is_set(&m_opened_partitions, (uint)(file - m_file)))
- (*file)->ha_close();
+ if (bitmap_is_set(&m_opened_partitions, i))
+ m_file[i]->ha_close();
+ if (!i)
+ break;
}
err_alloc:
free_partition_bitmaps();
@@ -4272,7 +4276,7 @@ void ha_partition::try_semi_consistent_read(bool yes)
determining which partition the row should be written to.
*/
-int ha_partition::write_row(uchar * buf)
+int ha_partition::write_row(const uchar * buf)
{
uint32 part_id;
int error;
@@ -4826,6 +4830,42 @@ ha_rows ha_partition::guess_bulk_insert_rows()
}
+void ha_partition::sum_copy_info(handler *file)
+{
+ copy_info.records+= file->copy_info.records;
+ copy_info.touched+= file->copy_info.touched;
+ copy_info.copied+= file->copy_info.copied;
+ copy_info.deleted+= file->copy_info.deleted;
+ copy_info.updated+= file->copy_info.updated;
+}
+
+
+void ha_partition::sum_copy_infos()
+{
+ handler **file_array;
+ bzero(&copy_info, sizeof(copy_info));
+ file_array= m_file;
+ do
+ {
+ if (bitmap_is_set(&(m_opened_partitions), (uint)(file_array - m_file)))
+ sum_copy_info(*file_array);
+ } while (*(++file_array));
+}
+
+void ha_partition::reset_copy_info()
+{
+ handler **file_array;
+ bzero(&copy_info, sizeof(copy_info));
+ file_array= m_file;
+ do
+ {
+ if (bitmap_is_set(&(m_opened_partitions), (uint)(file_array - m_file)))
+ bzero(&(*file_array)->copy_info, sizeof(copy_info));
+ } while (*(++file_array));
+}
+
+
+
/*
Finish a large batch of insert rows
@@ -4857,6 +4897,7 @@ int ha_partition::end_bulk_insert()
int tmp;
if ((tmp= m_file[i]->ha_end_bulk_insert()))
error= tmp;
+ sum_copy_info(m_file[i]);
}
bitmap_clear_all(&m_bulk_insert_started);
DBUG_RETURN(error);
@@ -5308,7 +5349,7 @@ bool ha_partition::init_record_priority_queue()
/* Initialize priority queue, initialized to reading forward. */
int (*cmp_func)(void *, uchar *, uchar *);
void *cmp_arg= (void*) this;
- if (!m_using_extended_keys && !(table_flags() & HA_CMP_REF_IS_EXPENSIVE))
+ if (!m_using_extended_keys && !(table_flags() & HA_SLOW_CMP_REF))
cmp_func= cmp_key_rowid_part_id;
else
cmp_func= cmp_key_part_id;
@@ -5794,12 +5835,6 @@ int ha_partition::index_read_idx_map(uchar *buf, uint index,
get_partition_set(table, buf, index, &m_start_key, &m_part_spec);
- /*
- We have either found exactly 1 partition
- (in which case start_part == end_part)
- or no matching partitions (start_part > end_part)
- */
- DBUG_ASSERT(m_part_spec.start_part >= m_part_spec.end_part);
/* The start part is must be marked as used. */
DBUG_ASSERT(m_part_spec.start_part > m_part_spec.end_part ||
bitmap_is_set(&(m_part_info->read_partitions),
@@ -6303,19 +6338,22 @@ ha_rows ha_partition::multi_range_read_info_const(uint keyno,
uint ret_mrr_mode= 0;
range_seq_t seq_it;
part_id_range save_part_spec;
+ Cost_estimate part_cost;
DBUG_ENTER("ha_partition::multi_range_read_info_const");
DBUG_PRINT("enter", ("partition this: %p", this));
m_mrr_new_full_buffer_size= 0;
save_part_spec= m_part_spec;
+ cost->reset();
+
seq_it= seq->init(seq_init_param, n_ranges, *mrr_mode);
if (unlikely((error= multi_range_key_create_key(seq, seq_it))))
{
if (likely(error == HA_ERR_END_OF_FILE)) // No keys in range
{
rows= 0;
- goto calc_cost;
+ goto end;
}
/*
This error means that we can't do multi_range_read for the moment
@@ -6344,18 +6382,20 @@ ha_rows ha_partition::multi_range_read_info_const(uint keyno,
ha_rows tmp_rows;
uint tmp_mrr_mode;
m_mrr_buffer_size[i]= 0;
+ part_cost.reset();
tmp_mrr_mode= *mrr_mode;
tmp_rows= (*file)->
multi_range_read_info_const(keyno, &m_part_seq_if,
&m_partition_part_key_multi_range_hld[i],
m_part_mrr_range_length[i],
&m_mrr_buffer_size[i],
- &tmp_mrr_mode, cost);
+ &tmp_mrr_mode, &part_cost);
if (tmp_rows == HA_POS_ERROR)
{
m_part_spec= save_part_spec;
DBUG_RETURN(HA_POS_ERROR);
}
+ cost->add(&part_cost);
rows+= tmp_rows;
ret_mrr_mode|= tmp_mrr_mode;
m_mrr_new_full_buffer_size+= m_mrr_buffer_size[i];
@@ -6363,15 +6403,8 @@ ha_rows ha_partition::multi_range_read_info_const(uint keyno,
} while (*(++file));
*mrr_mode= ret_mrr_mode;
-calc_cost:
+end:
m_part_spec= save_part_spec;
- cost->reset();
- cost->avg_io_cost= 1;
- if ((*mrr_mode & HA_MRR_INDEX_ONLY) && rows > 2)
- cost->io_count= keyread_time(keyno, n_ranges, (uint) rows);
- else
- cost->io_count= read_time(keyno, n_ranges, rows);
- cost->cpu_cost= (double) rows / TIME_FOR_COMPARE + 0.01;
DBUG_RETURN(rows);
}
@@ -6385,9 +6418,12 @@ ha_rows ha_partition::multi_range_read_info(uint keyno, uint n_ranges,
uint i;
handler **file;
ha_rows rows= 0;
+ Cost_estimate part_cost;
DBUG_ENTER("ha_partition::multi_range_read_info");
DBUG_PRINT("enter", ("partition this: %p", this));
+ cost->reset();
+
m_mrr_new_full_buffer_size= 0;
file= m_file;
do
@@ -6395,22 +6431,20 @@ ha_rows ha_partition::multi_range_read_info(uint keyno, uint n_ranges,
i= (uint)(file - m_file);
if (bitmap_is_set(&(m_part_info->read_partitions), (i)))
{
+ ha_rows tmp_rows;
m_mrr_buffer_size[i]= 0;
- if ((rows= (*file)->multi_range_read_info(keyno, n_ranges, keys,
- key_parts,
- &m_mrr_buffer_size[i],
- mrr_mode, cost)))
+ part_cost.reset();
+ if ((tmp_rows= (*file)->multi_range_read_info(keyno, n_ranges, keys,
+ key_parts,
+ &m_mrr_buffer_size[i],
+ mrr_mode, &part_cost)))
DBUG_RETURN(rows);
+ cost->add(&part_cost);
+ rows+= tmp_rows;
m_mrr_new_full_buffer_size+= m_mrr_buffer_size[i];
}
} while (*(++file));
- cost->reset();
- cost->avg_io_cost= 1;
- if (*mrr_mode & HA_MRR_INDEX_ONLY)
- cost->io_count= keyread_time(keyno, n_ranges, (uint) rows);
- else
- cost->io_count= read_time(keyno, n_ranges, rows);
DBUG_RETURN(0);
}
@@ -6427,6 +6461,7 @@ int ha_partition::multi_range_read_init(RANGE_SEQ_IF *seq,
DBUG_ENTER("ha_partition::multi_range_read_init");
DBUG_PRINT("enter", ("partition this: %p", this));
+ eq_range= 0;
m_seq_if= seq;
m_seq= seq->init(seq_init_param, n_ranges, mrr_mode);
if (unlikely((error= multi_range_key_create_key(seq, m_seq))))
@@ -6855,7 +6890,7 @@ FT_INFO *ha_partition::ft_init_ext(uint flags, uint inx, String *key)
sizeof(FT_INFO *) * m_tot_parts,
NullS)))
{
- my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL));
DBUG_RETURN(NULL);
}
ft_target->part_ft_info= tmp_ft_info;
@@ -8255,6 +8290,7 @@ int ha_partition::info(uint flag)
stats.delete_length= 0;
stats.check_time= 0;
stats.checksum= 0;
+ stats.checksum_null= TRUE;
for (i= bitmap_get_first_set(&m_part_info->read_partitions);
i < m_tot_parts;
i= bitmap_get_next_set(&m_part_info->read_partitions, i))
@@ -8268,7 +8304,11 @@ int ha_partition::info(uint flag)
stats.delete_length+= file->stats.delete_length;
if (file->stats.check_time > stats.check_time)
stats.check_time= file->stats.check_time;
- stats.checksum+= file->stats.checksum;
+ if (!file->stats.checksum_null)
+ {
+ stats.checksum+= file->stats.checksum;
+ stats.checksum_null= FALSE;
+ }
}
if (stats.records && stats.records < 2 &&
!(m_file[0]->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT))
@@ -8427,6 +8467,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info,
stat_info->update_time= file->stats.update_time;
stat_info->check_time= file->stats.check_time;
stat_info->check_sum= file->stats.checksum;
+ stat_info->check_sum_null= file->stats.checksum_null;
}
@@ -9020,7 +9061,6 @@ int ha_partition::extra(enum ha_extra_function operation)
case HA_EXTRA_STARTING_ORDERED_INDEX_SCAN:
case HA_EXTRA_BEGIN_ALTER_COPY:
case HA_EXTRA_END_ALTER_COPY:
- case HA_EXTRA_FAKE_START_STMT:
DBUG_RETURN(loop_partitions(extra_cb, &operation));
default:
{
@@ -9393,6 +9433,43 @@ double ha_partition::scan_time()
/**
+ @brief
+ Caculate time to scan the given index (index only scan)
+
+ @param inx Index number to scan
+
+ @return time for scanning index inx
+*/
+
+double ha_partition::key_scan_time(uint inx)
+{
+ double scan_time= 0;
+ uint i;
+ DBUG_ENTER("ha_partition::key_scan_time");
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
+ scan_time+= m_file[i]->key_scan_time(inx);
+ DBUG_RETURN(scan_time);
+}
+
+
+double ha_partition::keyread_time(uint inx, uint ranges, ha_rows rows)
+{
+ double read_time= 0;
+ uint i;
+ DBUG_ENTER("ha_partition::keyread_time");
+ if (!ranges)
+ DBUG_RETURN(handler::keyread_time(inx, ranges, rows));
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
+ read_time+= m_file[i]->keyread_time(inx, ranges, rows);
+ DBUG_RETURN(read_time);
+}
+
+
+/**
Find number of records in a range.
@param inx Index number
@param min_key Start of range
@@ -9829,7 +9906,7 @@ void ha_partition::print_error(int error, myf errflag)
append_row_to_str(str);
/* Log this error, so the DBA can notice it and fix it! */
- sql_print_error("Table '%-192s' corrupted: row in wrong partition: %s\n"
+ sql_print_error("Table '%-192s' corrupted: row in wrong partition: %s"
"Please REPAIR the table!",
table->s->table_name.str,
str.c_ptr_safe());
@@ -10623,6 +10700,64 @@ void ha_partition::init_table_handle_for_HANDLER()
}
+/**
+ Calculate the checksum of the table (all partitions)
+*/
+
+int ha_partition::pre_calculate_checksum()
+{
+ int error;
+ DBUG_ENTER("ha_partition::pre_calculate_checksum");
+ m_pre_calling= TRUE;
+ if ((table_flags() & (HA_HAS_OLD_CHECKSUM | HA_HAS_NEW_CHECKSUM)))
+ {
+ handler **file= m_file;
+ do
+ {
+ if ((error= (*file)->pre_calculate_checksum()))
+ {
+ DBUG_RETURN(error);
+ }
+ } while (*(++file));
+ }
+ DBUG_RETURN(0);
+}
+
+
+int ha_partition::calculate_checksum()
+{
+ int error;
+ stats.checksum= 0;
+ stats.checksum_null= TRUE;
+
+ DBUG_ENTER("ha_partition::calculate_checksum");
+ if (!m_pre_calling)
+ {
+ if ((error= pre_calculate_checksum()))
+ {
+ m_pre_calling= FALSE;
+ DBUG_RETURN(error);
+ }
+ }
+ m_pre_calling= FALSE;
+
+ handler **file= m_file;
+ do
+ {
+ if ((error= (*file)->calculate_checksum()))
+ {
+ DBUG_RETURN(error);
+ }
+ if (!(*file)->stats.checksum_null)
+ {
+ stats.checksum+= (*file)->stats.checksum;
+ stats.checksum_null= FALSE;
+ }
+ } while (*(++file));
+ DBUG_RETURN(0);
+}
+
+
/****************************************************************************
MODULE enable/disable indexes
****************************************************************************/
@@ -10745,8 +10880,6 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair)
{
/* Only need to read the partitioning fields. */
bitmap_union(table->read_set, &m_part_info->full_part_field_set);
- if (table->vcol_set)
- bitmap_union(table->vcol_set, &m_part_info->full_part_field_set);
}
if ((result= m_file[read_part_id]->ha_rnd_init(1)))
@@ -11107,6 +11240,7 @@ bool ha_partition::start_bulk_update()
do
{
+ bzero(&(*file)->copy_info, sizeof((*file)->copy_info));
if ((*file)->start_bulk_update())
DBUG_RETURN(TRUE);
} while (*(++file));
@@ -11164,6 +11298,7 @@ int ha_partition::end_bulk_update()
if ((tmp= (*file)->end_bulk_update()))
error= tmp;
} while (*(++file));
+ sum_copy_infos();
DBUG_RETURN(error);
}
@@ -11259,6 +11394,7 @@ int ha_partition::end_bulk_delete()
if ((tmp= (*file)->end_bulk_delete()))
error= tmp;
} while (*(++file));
+ sum_copy_infos();
DBUG_RETURN(error);
}
@@ -11375,11 +11511,13 @@ int ha_partition::pre_direct_update_rows_init(List<Item> *update_fields)
0 Success
*/
-int ha_partition::direct_update_rows(ha_rows *update_rows_result)
+int ha_partition::direct_update_rows(ha_rows *update_rows_result,
+ ha_rows *found_rows_result)
{
int error;
bool rnd_seq= FALSE;
ha_rows update_rows= 0;
+ ha_rows found_rows= 0;
uint32 i;
DBUG_ENTER("ha_partition::direct_update_rows");
@@ -11391,6 +11529,7 @@ int ha_partition::direct_update_rows(ha_rows *update_rows_result)
}
*update_rows_result= 0;
+ *found_rows_result= 0;
for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
{
handler *file= m_file[i];
@@ -11406,7 +11545,8 @@ int ha_partition::direct_update_rows(ha_rows *update_rows_result)
}
if (unlikely((error= (m_pre_calling ?
(file)->pre_direct_update_rows() :
- (file)->ha_direct_update_rows(&update_rows)))))
+ (file)->ha_direct_update_rows(&update_rows,
+ &found_rows)))))
{
if (rnd_seq)
{
@@ -11418,6 +11558,7 @@ int ha_partition::direct_update_rows(ha_rows *update_rows_result)
DBUG_RETURN(error);
}
*update_rows_result+= update_rows;
+ *found_rows_result+= found_rows;
}
if (rnd_seq)
{
@@ -11453,7 +11594,7 @@ int ha_partition::pre_direct_update_rows()
DBUG_ENTER("ha_partition::pre_direct_update_rows");
save_m_pre_calling= m_pre_calling;
m_pre_calling= TRUE;
- error= direct_update_rows(&not_used);
+ error= direct_update_rows(&not_used, &not_used);
m_pre_calling= save_m_pre_calling;
DBUG_RETURN(error);
}
@@ -11699,6 +11840,40 @@ void ha_partition::clear_top_table_fields()
DBUG_VOID_RETURN;
}
+bool
+ha_partition::can_convert_string(const Field_string* field,
+ const Column_definition& new_type) const
+{
+ for (uint index= 0; index < m_tot_parts; index++)
+ {
+ if (!m_file[index]->can_convert_string(field, new_type))
+ return false;
+ }
+ return true;
+}
+
+bool
+ha_partition::can_convert_varstring(const Field_varstring* field,
+ const Column_definition& new_type) const{
+ for (uint index= 0; index < m_tot_parts; index++)
+ {
+ if (!m_file[index]->can_convert_varstring(field, new_type))
+ return false;
+ }
+ return true;
+}
+
+bool
+ha_partition::can_convert_blob(const Field_blob* field,
+ const Column_definition& new_type) const
+{
+ for (uint index= 0; index < m_tot_parts; index++)
+ {
+ if (!m_file[index]->can_convert_blob(field, new_type))
+ return false;
+ }
+ return true;
+}
struct st_mysql_storage_engine partition_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index b16675e868f..08b7d7cf8f2 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -3,7 +3,7 @@
/*
Copyright (c) 2005, 2012, Oracle and/or its affiliates.
- Copyright (c) 2009, 2013, Monty Program Ab & SkySQL Ab.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -428,6 +428,9 @@ private:
MY_BITMAP m_locked_partitions;
/** Stores shared auto_increment etc. */
Partition_share *part_share;
+ void sum_copy_info(handler *file);
+ void sum_copy_infos();
+ void reset_copy_info() override;
/** Temporary storage for new partitions Handler_shares during ALTER */
List<Parts_share_refs> m_new_partitions_share_refs;
/** Sorted array of partition ids in descending order of number of rows. */
@@ -460,16 +463,16 @@ public:
return NO_CURRENT_PART_ID;
}
Partition_share *get_part_share() { return part_share; }
- handler *clone(const char *name, MEM_ROOT *mem_root);
- virtual void set_part_info(partition_info *part_info)
+ handler *clone(const char *name, MEM_ROOT *mem_root) override;
+ virtual void set_part_info(partition_info *part_info) override
{
m_part_info= part_info;
m_is_sub_partitioned= part_info->is_sub_partitioned();
}
- virtual void return_record_by_parent();
+ void return_record_by_parent() override;
- virtual bool vers_can_native(THD *thd)
+ bool vers_can_native(THD *thd) override
{
if (thd->lex->part_info)
{
@@ -528,31 +531,30 @@ public:
object needed in opening the object in openfrm
-------------------------------------------------------------------------
*/
- virtual int delete_table(const char *from);
- virtual int rename_table(const char *from, const char *to);
- virtual int create(const char *name, TABLE *form,
- HA_CREATE_INFO *create_info);
- virtual int create_partitioning_metadata(const char *name,
- const char *old_name, int action_flag);
- virtual void update_create_info(HA_CREATE_INFO *create_info);
- virtual char *update_table_comment(const char *comment);
- virtual int change_partitions(HA_CREATE_INFO *create_info,
- const char *path,
- ulonglong * const copied,
- ulonglong * const deleted,
- const uchar *pack_frm_data,
- size_t pack_frm_len);
- virtual int drop_partitions(const char *path);
- virtual int rename_partitions(const char *path);
- bool get_no_parts(const char *name, uint *num_parts)
+ int delete_table(const char *from) override;
+ int rename_table(const char *from, const char *to) override;
+ int create(const char *name, TABLE *form,
+ HA_CREATE_INFO *create_info) override;
+ int create_partitioning_metadata(const char *name,
+ const char *old_name, int action_flag)
+ override;
+ void update_create_info(HA_CREATE_INFO *create_info) override;
+ char *update_table_comment(const char *comment) override;
+ int change_partitions(HA_CREATE_INFO *create_info, const char *path,
+ ulonglong * const copied, ulonglong * const deleted,
+ const uchar *pack_frm_data, size_t pack_frm_len)
+ override;
+ int drop_partitions(const char *path) override;
+ int rename_partitions(const char *path) override;
+ bool get_no_parts(const char *, uint *num_parts) override
{
DBUG_ENTER("ha_partition::get_no_parts");
*num_parts= m_tot_parts;
DBUG_RETURN(0);
}
- virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share);
- virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
- uint table_changes);
+ void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share) override;
+ bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
+ uint table_changes) override;
void update_part_create_info(HA_CREATE_INFO *create_info, uint part_id)
{
m_file[part_id]->update_create_info(create_info);
@@ -591,7 +593,7 @@ private:
bool is_subpart);
bool populate_partition_name_hash();
Partition_share *get_share();
- bool set_ha_share_ref(Handler_share **ha_share);
+ bool set_ha_share_ref(Handler_share **ha_share) override;
void fix_data_dir(char* path);
bool init_partition_bitmaps();
void free_partition_bitmaps();
@@ -611,8 +613,8 @@ public:
being used for normal queries (not before meta-data changes always.
If the object was opened it will also be closed before being deleted.
*/
- virtual int open(const char *name, int mode, uint test_if_locked);
- virtual int close(void);
+ int open(const char *name, int mode, uint test_if_locked) override;
+ int close() override;
/*
-------------------------------------------------------------------------
@@ -627,31 +629,31 @@ public:
and these go directly to the handlers supporting transactions
-------------------------------------------------------------------------
*/
- virtual THR_LOCK_DATA **store_lock(THD * thd, THR_LOCK_DATA ** to,
- enum thr_lock_type lock_type);
- virtual int external_lock(THD * thd, int lock_type);
- LEX_CSTRING *engine_name() { return hton_name(partition_ht()); }
+ THR_LOCK_DATA **store_lock(THD * thd, THR_LOCK_DATA ** to,
+ enum thr_lock_type lock_type) override;
+ int external_lock(THD * thd, int lock_type) override;
+ LEX_CSTRING *engine_name() override { return hton_name(partition_ht()); }
/*
When table is locked a statement is started by calling start_stmt
instead of external_lock
*/
- virtual int start_stmt(THD * thd, thr_lock_type lock_type);
+ int start_stmt(THD * thd, thr_lock_type lock_type) override;
/*
Lock count is number of locked underlying handlers (I assume)
*/
- virtual uint lock_count(void) const;
+ uint lock_count() const override;
/*
Call to unlock rows not to be updated in transaction
*/
- virtual void unlock_row();
+ void unlock_row() override;
/*
Check if semi consistent read
*/
- virtual bool was_semi_consistent_read();
+ bool was_semi_consistent_read() override;
/*
Call to hint about semi consistent read
*/
- virtual void try_semi_consistent_read(bool);
+ void try_semi_consistent_read(bool) override;
/*
NOTE: due to performance and resource issues with many partitions,
@@ -685,28 +687,28 @@ public:
start_bulk_insert and end_bulk_insert is called before and after a
number of calls to write_row.
*/
- virtual int write_row(uchar * buf);
- virtual bool start_bulk_update();
- virtual int exec_bulk_update(ha_rows *dup_key_found);
- virtual int end_bulk_update();
- virtual int bulk_update_row(const uchar *old_data, const uchar *new_data,
- ha_rows *dup_key_found);
- virtual int update_row(const uchar * old_data, const uchar * new_data);
- virtual int direct_update_rows_init(List<Item> *update_fields);
- virtual int pre_direct_update_rows_init(List<Item> *update_fields);
- virtual int direct_update_rows(ha_rows *update_rows);
- virtual int pre_direct_update_rows();
- virtual bool start_bulk_delete();
- virtual int end_bulk_delete();
- virtual int delete_row(const uchar * buf);
- virtual int direct_delete_rows_init();
- virtual int pre_direct_delete_rows_init();
- virtual int direct_delete_rows(ha_rows *delete_rows);
- virtual int pre_direct_delete_rows();
- virtual int delete_all_rows(void);
- virtual int truncate();
- virtual void start_bulk_insert(ha_rows rows, uint flags);
- virtual int end_bulk_insert();
+ int write_row(const uchar * buf) override;
+ bool start_bulk_update() override;
+ int exec_bulk_update(ha_rows *dup_key_found) override;
+ int end_bulk_update() override;
+ int bulk_update_row(const uchar *old_data, const uchar *new_data,
+ ha_rows *dup_key_found) override;
+ int update_row(const uchar * old_data, const uchar * new_data) override;
+ int direct_update_rows_init(List<Item> *update_fields) override;
+ int pre_direct_update_rows_init(List<Item> *update_fields) override;
+ int direct_update_rows(ha_rows *update_rows, ha_rows *found_rows) override;
+ int pre_direct_update_rows() override;
+ bool start_bulk_delete() override;
+ int end_bulk_delete() override;
+ int delete_row(const uchar * buf) override;
+ int direct_delete_rows_init() override;
+ int pre_direct_delete_rows_init() override;
+ int direct_delete_rows(ha_rows *delete_rows) override;
+ int pre_direct_delete_rows() override;
+ int delete_all_rows() override;
+ int truncate() override;
+ void start_bulk_insert(ha_rows rows, uint flags) override;
+ int end_bulk_insert() override;
private:
ha_rows guess_bulk_insert_rows();
void start_part_bulk_insert(THD *thd, uint part_id);
@@ -722,7 +724,7 @@ public:
*/
int truncate_partition(Alter_info *, bool *binlog_stmt);
- virtual bool is_fatal_error(int error, uint flags)
+ bool is_fatal_error(int error, uint flags) override
{
if (!handler::is_fatal_error(error, flags) ||
error == HA_ERR_NO_PARTITION_FOUND ||
@@ -757,12 +759,12 @@ public:
position it to the start of the table, no need to deallocate
and allocate it again
*/
- virtual int rnd_init(bool scan);
- virtual int rnd_end();
- virtual int rnd_next(uchar * buf);
- virtual int rnd_pos(uchar * buf, uchar * pos);
- virtual int rnd_pos_by_record(uchar *record);
- virtual void position(const uchar * record);
+ int rnd_init(bool scan) override;
+ int rnd_end() override;
+ int rnd_next(uchar * buf) override;
+ int rnd_pos(uchar * buf, uchar * pos) override;
+ int rnd_pos_by_record(uchar *record) override;
+ void position(const uchar * record) override;
/*
-------------------------------------------------------------------------
@@ -796,11 +798,11 @@ public:
index_init initializes an index before using it and index_end does
any end processing needed.
*/
- virtual int index_read_map(uchar * buf, const uchar * key,
- key_part_map keypart_map,
- enum ha_rkey_function find_flag);
- virtual int index_init(uint idx, bool sorted);
- virtual int index_end();
+ int index_read_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag) override;
+ int index_init(uint idx, bool sorted) override;
+ int index_end() override;
/**
@breif
@@ -808,36 +810,36 @@ public:
row if available. If the key value is null, begin at first key of the
index.
*/
- virtual int index_read_idx_map(uchar *buf, uint index, const uchar *key,
- key_part_map keypart_map,
- enum ha_rkey_function find_flag);
+ int index_read_idx_map(uchar *buf, uint index, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag) override;
/*
These methods are used to jump to next or previous entry in the index
scan. There are also methods to jump to first and last entry.
*/
- virtual int index_next(uchar * buf);
- virtual int index_prev(uchar * buf);
- virtual int index_first(uchar * buf);
- virtual int index_last(uchar * buf);
- virtual int index_next_same(uchar * buf, const uchar * key, uint keylen);
+ int index_next(uchar * buf) override;
+ int index_prev(uchar * buf) override;
+ int index_first(uchar * buf) override;
+ int index_last(uchar * buf) override;
+ int index_next_same(uchar * buf, const uchar * key, uint keylen) override;
int index_read_last_map(uchar *buf,
const uchar *key,
- key_part_map keypart_map);
+ key_part_map keypart_map) override;
/*
read_first_row is virtual method but is only implemented by
handler.cc, no storage engine has implemented it so neither
will the partition handler.
- virtual int read_first_row(uchar *buf, uint primary_key);
+ int read_first_row(uchar *buf, uint primary_key) override;
*/
- virtual int read_range_first(const key_range * start_key,
- const key_range * end_key,
- bool eq_range, bool sorted);
- virtual int read_range_next();
+ int read_range_first(const key_range * start_key,
+ const key_range * end_key,
+ bool eq_range, bool sorted) override;
+ int read_range_next() override;
HANDLER_BUFFER *m_mrr_buffer;
@@ -899,20 +901,20 @@ public:
RANGE_SEQ_IF *seq,
range_seq_t seq_it
);
- virtual ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
- void *seq_init_param,
- uint n_ranges, uint *bufsz,
- uint *mrr_mode,
- Cost_estimate *cost);
- virtual ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
- uint key_parts, uint *bufsz,
- uint *mrr_mode, Cost_estimate *cost);
- virtual int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
- uint n_ranges, uint mrr_mode,
- HANDLER_BUFFER *buf);
- virtual int multi_range_read_next(range_id_t *range_info);
- virtual int multi_range_read_explain_info(uint mrr_mode, char *str,
- size_t size);
+ ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *mrr_mode,
+ Cost_estimate *cost) override;
+ ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint key_parts, uint *bufsz,
+ uint *mrr_mode, Cost_estimate *cost) override;
+ int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mrr_mode,
+ HANDLER_BUFFER *buf) override;
+ int multi_range_read_next(range_id_t *range_info) override;
+ int multi_range_read_explain_info(uint mrr_mode, char *str, size_t size)
+ override;
uint last_part() { return m_last_part; }
private:
@@ -940,21 +942,20 @@ public:
purposes.
-------------------------------------------------------------------------
*/
- virtual int info(uint);
- void get_dynamic_partition_info(PARTITION_STATS *stat_info,
- uint part_id);
- void set_partitions_to_open(List<String> *partition_names);
- int change_partitions_to_open(List<String> *partition_names);
+ int info(uint) override;
+ void get_dynamic_partition_info(PARTITION_STATS *stat_info, uint part_id)
+ override;
+ void set_partitions_to_open(List<String> *partition_names) override;
+ int change_partitions_to_open(List<String> *partition_names) override;
int open_read_partitions(char *name_buff, size_t name_buff_size);
- virtual int extra(enum ha_extra_function operation);
- virtual int extra_opt(enum ha_extra_function operation, ulong arg);
- virtual int reset(void);
- virtual uint count_query_cache_dependant_tables(uint8 *tables_type);
- virtual my_bool
- register_query_cache_dependant_tables(THD *thd,
- Query_cache *cache,
- Query_cache_block_table **block,
- uint *n);
+ int extra(enum ha_extra_function operation) override;
+ int extra_opt(enum ha_extra_function operation, ulong arg) override;
+ int reset() override;
+ uint count_query_cache_dependant_tables(uint8 *tables_type) override;
+ my_bool register_query_cache_dependant_tables(THD *thd,
+ Query_cache *cache,
+ Query_cache_block_table **block,
+ uint *n) override;
private:
typedef int handler_callback(handler *, void *);
@@ -1009,36 +1010,40 @@ public:
index-only scanning when performing an ORDER BY query.
Only called from one place in sql_select.cc
*/
- virtual const key_map *keys_to_use_for_scanning();
+ const key_map *keys_to_use_for_scanning() override;
/*
Called in test_quick_select to determine if indexes should be used.
*/
- virtual double scan_time();
+ double scan_time() override;
+
+ double key_scan_time(uint inx) override;
+
+ double keyread_time(uint inx, uint ranges, ha_rows rows) override;
/*
The next method will never be called if you do not implement indexes.
*/
- virtual double read_time(uint index, uint ranges, ha_rows rows);
+ double read_time(uint index, uint ranges, ha_rows rows) override;
/*
For the given range how many records are estimated to be in this range.
Used by optimiser to calculate cost of using a particular index.
*/
- virtual ha_rows records_in_range(uint inx, key_range * min_key,
- key_range * max_key);
+ ha_rows records_in_range(uint inx, key_range * min_key, key_range * max_key)
+ override;
/*
Upper bound of number records returned in scan is sum of all
underlying handlers.
*/
- virtual ha_rows estimate_rows_upper_bound();
+ ha_rows estimate_rows_upper_bound() override;
/*
table_cache_type is implemented by the underlying handler but all
underlying handlers must have the same implementation for it to work.
*/
- virtual uint8 table_cache_type();
- virtual ha_rows records();
+ uint8 table_cache_type() override;
+ ha_rows records() override;
/* Calculate hash value for PARTITION BY KEY tables. */
static uint32 calculate_key_hash_value(Field **field_array);
@@ -1056,19 +1061,19 @@ public:
Here we must ensure that all handlers use the same index type
for each index created.
*/
- virtual const char *index_type(uint inx);
+ const char *index_type(uint inx) override;
/* The name of the table type that will be used for display purposes */
- virtual const char *table_type() const;
+ const char *table_type() const;
/* The name of the row type used for the underlying tables. */
- virtual enum row_type get_row_type() const;
+ enum row_type get_row_type() const override;
/*
Handler specific error messages
*/
- virtual void print_error(int error, myf errflag);
- virtual bool get_error_message(int error, String * buf);
+ void print_error(int error, myf errflag) override;
+ bool get_error_message(int error, String * buf) override;
/*
-------------------------------------------------------------------------
MODULE handler characteristics
@@ -1144,10 +1149,6 @@ public:
with hidden primary key)
(No handler has this limitation currently)
- HA_WANTS_PRIMARY_KEY:
- Can't define a table without primary key except sequences
- (Only InnoDB has this when using innodb_force_primary_key == ON)
-
HA_STATS_RECORDS_IS_EXACT:
Does the counter of records after the info call specify an exact
value or not. If it does this flag is set.
@@ -1226,7 +1227,7 @@ public:
HA_CAN_INSERT_DELAYED, HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is disabled
until further investigated.
*/
- virtual Table_flags table_flags() const;
+ Table_flags table_flags() const override;
/*
This is a bitmap of flags that says how the storage engine
@@ -1284,7 +1285,7 @@ public:
must be updated in the row.
(InnoDB, MyISAM)
*/
- virtual ulong index_flags(uint inx, uint part, bool all_parts) const
+ ulong index_flags(uint inx, uint part, bool all_parts) const override
{
/*
The following code is not safe if you are using different
@@ -1297,7 +1298,8 @@ public:
wrapper function for handlerton alter_table_flags, since
the ha_partition_hton cannot know all its capabilities
*/
- virtual alter_table_operations alter_table_flags(alter_table_operations flags);
+ alter_table_operations alter_table_flags(alter_table_operations flags)
+ override;
/*
unireg.cc will call the following to make sure that the storage engine
can handle the data it is about to send.
@@ -1305,19 +1307,18 @@ public:
The maximum supported values is the minimum of all handlers in the table
*/
uint min_of_the_max_uint(uint (handler::*operator_func)(void) const) const;
- virtual uint max_supported_record_length() const;
- virtual uint max_supported_keys() const;
- virtual uint max_supported_key_parts() const;
- virtual uint max_supported_key_length() const;
- virtual uint max_supported_key_part_length() const;
- virtual uint min_record_length(uint options) const;
+ uint max_supported_record_length() const override;
+ uint max_supported_keys() const override;
+ uint max_supported_key_parts() const override;
+ uint max_supported_key_length() const override;
+ uint max_supported_key_part_length() const override;
+ uint min_record_length(uint options) const override;
/*
Primary key is clustered can only be true if all underlying handlers have
this feature.
*/
- virtual bool primary_key_is_clustered()
- { return m_pkey_is_clustered; }
+ bool primary_key_is_clustered() override { return m_pkey_is_clustered; }
/*
-------------------------------------------------------------------------
@@ -1335,7 +1336,7 @@ public:
to check whether the rest of the reference part is also the same.
-------------------------------------------------------------------------
*/
- virtual int cmp_ref(const uchar * ref1, const uchar * ref2);
+ int cmp_ref(const uchar * ref1, const uchar * ref2) override;
/*
-------------------------------------------------------------------------
MODULE auto increment
@@ -1349,15 +1350,15 @@ public:
auto_increment_column_changed
-------------------------------------------------------------------------
*/
- virtual bool need_info_for_auto_inc();
- virtual bool can_use_for_auto_inc_init();
- virtual void get_auto_increment(ulonglong offset, ulonglong increment,
- ulonglong nb_desired_values,
- ulonglong *first_value,
- ulonglong *nb_reserved_values);
- virtual void release_auto_increment();
+ bool need_info_for_auto_inc() override;
+ bool can_use_for_auto_inc_init() override;
+ void get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values) override;
+ void release_auto_increment() override;
private:
- virtual int reset_auto_increment(ulonglong value);
+ int reset_auto_increment(ulonglong value) override;
void update_next_auto_inc_val();
virtual void lock_auto_increment()
{
@@ -1419,7 +1420,7 @@ public:
This method is a special InnoDB method called before a HANDLER query.
-------------------------------------------------------------------------
*/
- virtual void init_table_handle_for_HANDLER();
+ void init_table_handle_for_HANDLER() override;
/*
The remainder of this file defines the handler methods not implemented
@@ -1447,20 +1448,20 @@ public:
List<FOREIGN_KEY_INFO> *f_key_list)
virtual uint referenced_by_foreign_key()
*/
- virtual bool can_switch_engines();
+ bool can_switch_engines() override;
/*
-------------------------------------------------------------------------
MODULE fulltext index
-------------------------------------------------------------------------
*/
void ft_close_search(FT_INFO *handler);
- virtual int ft_init();
- virtual int pre_ft_init();
- virtual void ft_end();
- virtual int pre_ft_end();
- virtual FT_INFO *ft_init_ext(uint flags, uint inx, String *key);
- virtual int ft_read(uchar *buf);
- virtual int pre_ft_read(bool use_parallel);
+ int ft_init() override;
+ int pre_ft_init() override;
+ void ft_end() override;
+ int pre_ft_end() override;
+ FT_INFO *ft_init_ext(uint flags, uint inx, String *key) override;
+ int ft_read(uchar *buf) override;
+ int pre_ft_read(bool use_parallel) override;
/*
-------------------------------------------------------------------------
@@ -1468,7 +1469,7 @@ public:
-------------------------------------------------------------------------
The following method is only used by MyISAM when used as
temporary tables in a join.
- virtual int restart_rnd_next(uchar *buf, uchar *pos);
+ int restart_rnd_next(uchar *buf, uchar *pos) override;
*/
/*
@@ -1479,17 +1480,19 @@ public:
They are used for in-place alter table:
-------------------------------------------------------------------------
*/
- virtual enum_alter_inplace_result
+ enum_alter_inplace_result
check_if_supported_inplace_alter(TABLE *altered_table,
- Alter_inplace_info *ha_alter_info);
- virtual bool prepare_inplace_alter_table(TABLE *altered_table,
- Alter_inplace_info *ha_alter_info);
- virtual bool inplace_alter_table(TABLE *altered_table,
- Alter_inplace_info *ha_alter_info);
- virtual bool commit_inplace_alter_table(TABLE *altered_table,
- Alter_inplace_info *ha_alter_info,
- bool commit);
- virtual void notify_table_changed();
+ Alter_inplace_info *ha_alter_info)
+ override;
+ bool prepare_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+ override;
+ bool inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info) override;
+ bool commit_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit) override;
+ void notify_table_changed() override;
/*
-------------------------------------------------------------------------
@@ -1513,24 +1516,24 @@ public:
all partitions.
-------------------------------------------------------------------------
*/
- virtual int optimize(THD* thd, HA_CHECK_OPT *check_opt);
- virtual int analyze(THD* thd, HA_CHECK_OPT *check_opt);
- virtual int check(THD* thd, HA_CHECK_OPT *check_opt);
- virtual int repair(THD* thd, HA_CHECK_OPT *check_opt);
- virtual bool check_and_repair(THD *thd);
- virtual bool auto_repair(int error) const;
- virtual bool is_crashed() const;
- virtual int check_for_upgrade(HA_CHECK_OPT *check_opt);
+ int optimize(THD* thd, HA_CHECK_OPT *check_opt) override;
+ int analyze(THD* thd, HA_CHECK_OPT *check_opt) override;
+ int check(THD* thd, HA_CHECK_OPT *check_opt) override;
+ int repair(THD* thd, HA_CHECK_OPT *check_opt) override;
+ bool check_and_repair(THD *thd) override;
+ bool auto_repair(int error) const override;
+ bool is_crashed() const override;
+ int check_for_upgrade(HA_CHECK_OPT *check_opt) override;
/*
-------------------------------------------------------------------------
MODULE condition pushdown
-------------------------------------------------------------------------
*/
- virtual const COND *cond_push(const COND *cond);
- virtual void cond_pop();
- virtual void clear_top_table_fields();
- virtual int info_push(uint info_type, void *info);
+ const COND *cond_push(const COND *cond) override;
+ void cond_pop() override;
+ void clear_top_table_fields() override;
+ int info_push(uint info_type, void *info) override;
private:
int handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, uint flags);
@@ -1544,10 +1547,13 @@ public:
void append_row_to_str(String &str);
public:
+ int pre_calculate_checksum() override;
+ int calculate_checksum() override;
+
/* Enabled keycache for performance reasons, WL#4571 */
- virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt);
- virtual int preload_keys(THD* thd, HA_CHECK_OPT* check_opt);
- virtual TABLE_LIST *get_next_global_for_child();
+ int assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt) override;
+ int preload_keys(THD* thd, HA_CHECK_OPT* check_opt) override;
+ TABLE_LIST *get_next_global_for_child() override;
/*
-------------------------------------------------------------------------
@@ -1556,9 +1562,9 @@ public:
Enable/Disable Indexes are only supported by HEAP and MyISAM.
-------------------------------------------------------------------------
*/
- virtual int disable_indexes(uint mode);
- virtual int enable_indexes(uint mode);
- virtual int indexes_are_disabled(void);
+ int disable_indexes(uint mode) override;
+ int enable_indexes(uint mode) override;
+ int indexes_are_disabled() override;
/*
-------------------------------------------------------------------------
@@ -1586,7 +1592,7 @@ public:
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 *partition_ht() const override
{
handlerton *h= m_file[0]->ht;
for (uint i=1; i < m_tot_parts; i++)
@@ -1614,5 +1620,16 @@ public:
friend int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2);
friend int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2);
+ bool can_convert_string(
+ const Field_string* field,
+ const Column_definition& new_field) const override;
+
+ bool can_convert_varstring(
+ const Field_varstring* field,
+ const Column_definition& new_field) const override;
+
+ bool can_convert_blob(
+ const Field_blob* field,
+ const Column_definition& new_field) const override;
};
#endif /* HA_PARTITION_INCLUDED */
diff --git a/sql/ha_sequence.cc b/sql/ha_sequence.cc
index a0959f692cf..260f2c202fa 100644
--- a/sql/ha_sequence.cc
+++ b/sql/ha_sequence.cc
@@ -194,7 +194,7 @@ int ha_sequence::create(const char *name, TABLE *form,
the sequence with 'buf' as the sequence object is already up to date.
*/
-int ha_sequence::write_row(uchar *buf)
+int ha_sequence::write_row(const uchar *buf)
{
int error;
sequence_definition tmp_seq;
diff --git a/sql/ha_sequence.h b/sql/ha_sequence.h
index fd9da05b591..4360e9faa3d 100644
--- a/sql/ha_sequence.h
+++ b/sql/ha_sequence.h
@@ -71,7 +71,7 @@ public:
int create(const char *name, TABLE *form,
HA_CREATE_INFO *create_info);
handler *clone(const char *name, MEM_ROOT *mem_root);
- int write_row(uchar *buf);
+ int write_row(const uchar *buf);
Table_flags table_flags() const;
/* One can't update or delete from sequence engine */
int update_row(const uchar *old_data, const uchar *new_data)
diff --git a/sql/handle_connections_win.cc b/sql/handle_connections_win.cc
new file mode 100644
index 00000000000..50fd9090e0a
--- /dev/null
+++ b/sql/handle_connections_win.cc
@@ -0,0 +1,613 @@
+/* Copyright (c) 2018 MariaDB Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+/* Accepting connections on Windows */
+
+#include <my_global.h>
+#include <sql_class.h>
+#include <sql_connect.h>
+#include <mysqld.h>
+#include <mswsock.h>
+#include <mysql/psi/mysql_socket.h>
+#include <sddl.h>
+
+#include <handle_connections_win.h>
+
+/* From mysqld.cc */
+extern HANDLE hEventShutdown;
+extern MYSQL_SOCKET base_ip_sock, extra_ip_sock;
+#ifdef HAVE_POOL_OF_THREADS
+extern PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ();
+extern void tp_win_callback_prolog();
+#else
+#define get_threadpool_win_callback_environ() 0
+#define tp_win_callback_prolog() do{}while(0)
+#endif
+static SECURITY_ATTRIBUTES pipe_security;
+
+/**
+ Abstract base class for accepting new connection,
+ asynchronously (i.e the accept() operation can be posted,
+ and result is retrieved later) , and creating a new connection.
+*/
+
+struct Listener
+{
+ /** Windows handle of the Listener.
+ Subclasses would use SOCKET or named pipe handle
+ */
+ HANDLE m_handle;
+ /** Required for all async IO*/
+ OVERLAPPED m_overlapped;
+
+ /** Create new listener
+ @param handle - @see m_handle
+ @param wait_handle - usually, event handle or INVALID_HANDLE_VALUE
+ @see wait_handle
+ */
+ Listener(HANDLE handle, HANDLE wait_handle):
+ m_handle(handle), m_overlapped()
+ {
+ m_overlapped.hEvent= wait_handle;
+ }
+
+ /**
+ if not NULL, this handle can be be used in WaitForSingle/MultipleObject(s).
+ This handle will be closed when object is destroyed.
+
+ If NULL, the completion notification happens in threadpool.
+ */
+ HANDLE wait_handle()
+ {
+ return m_overlapped.hEvent;
+ }
+
+ /* Start waiting for new client connection. */
+ virtual void begin_accept()= 0;
+
+ /**
+ Completion callback,called whenever IO posted by begin_accept is finisjed
+ Listener needs to create a new THD then (or, call scheduler so it creates one)
+
+ @param success - whether IO completed successfull
+ */
+ virtual void completion_callback(bool success)= 0;
+
+ /**
+ Completion callback for Listener, that uses events for waiting
+ to IO. Not suitable for threadpool etc. Retrieves the status of
+ completed IO from the OVERLAPPED structure
+ */
+ void completion_callback()
+ {
+ DBUG_ASSERT(wait_handle() && (wait_handle() != INVALID_HANDLE_VALUE));
+ DWORD bytes;
+ return completion_callback(
+ GetOverlappedResult(wait_handle(), &m_overlapped, &bytes, FALSE));
+ }
+
+ /** Cancel an in-progress IO. Useful for threadpool-bound IO */
+ void cancel()
+ {
+ CancelIoEx(m_handle, &m_overlapped);
+ }
+
+ /* Destructor. Closes wait handle, if it was passed in constructor */
+ virtual ~Listener()
+ {
+ if (m_overlapped.hEvent)
+ CloseHandle(m_overlapped.hEvent);
+ };
+};
+
+/* Winsock extension finctions. */
+static LPFN_ACCEPTEX my_AcceptEx;
+static LPFN_GETACCEPTEXSOCKADDRS my_GetAcceptExSockaddrs;
+
+/**
+ Listener that handles socket connections.
+ Can be threadpool-bound (i.e the completion is executed in threadpool thread),
+ or use events for waits.
+
+ Threadpool-bound listener should be used with theradpool scheduler, for better
+ performance.
+*/
+struct Socket_Listener: public Listener
+{
+ /** Client socket passed to AcceptEx() call.*/
+ SOCKET m_client_socket;
+
+ /** Buffer for sockaddrs passed to AcceptEx()/GetAcceptExSockaddrs() */
+ char m_buffer[2 * sizeof(sockaddr_storage) + 32];
+
+ /* Threadpool IO struct.*/
+ PTP_IO m_tp_io;
+
+ /**
+ Callback for Windows threadpool's StartThreadpoolIo() function.
+ */
+ static void CALLBACK tp_accept_completion_callback(
+ PTP_CALLBACK_INSTANCE, PVOID context, PVOID , ULONG io_result,
+ ULONG_PTR, PTP_IO io)
+ {
+ tp_win_callback_prolog();
+ Listener *listener= (Listener *)context;
+
+ if (io_result == ERROR_OPERATION_ABORTED)
+ {
+ /* ERROR_OPERATION_ABORTED caused by CancelIoEx()*/
+ CloseThreadpoolIo(io);
+ delete listener;
+ return;
+ }
+ listener->completion_callback(io_result == 0);
+ }
+
+ /**
+ Constructor
+ @param listen_socket - listening socket
+ @PTP_CALLBACK_ENVIRON callback_environ - threadpool environment, or NULL
+ if threadpool is not used for completion callbacks.
+ */
+ Socket_Listener(MYSQL_SOCKET listen_socket, PTP_CALLBACK_ENVIRON callback_environ) :
+ Listener((HANDLE)listen_socket.fd,0),
+ m_client_socket(INVALID_SOCKET)
+ {
+ if (callback_environ)
+ {
+ /* Accept executed in threadpool. */
+ m_tp_io= CreateThreadpoolIo(m_handle,
+ tp_accept_completion_callback, this, callback_environ);
+ }
+ else
+ {
+ /* Completion signaled via event. */
+ m_tp_io= 0;
+ m_overlapped.hEvent= CreateEvent(0, FALSE , FALSE, 0);
+ }
+ }
+
+ /*
+ Use AcceptEx to asynchronously wait for new connection;
+ */
+ void begin_accept()
+ {
+retry :
+ m_client_socket= socket(server_socket_ai_family, SOCK_STREAM, IPPROTO_TCP);
+ if (m_client_socket == INVALID_SOCKET)
+ {
+ sql_perror("socket() call failed.");
+ unireg_abort(1);
+ }
+
+ DWORD bytes_received;
+ if (m_tp_io)
+ StartThreadpoolIo(m_tp_io);
+
+ BOOL ret= my_AcceptEx(
+ (SOCKET)m_handle,
+ m_client_socket,
+ m_buffer,
+ 0,
+ sizeof(sockaddr_storage) + 16,
+ sizeof(sockaddr_storage) + 16,
+ &bytes_received,
+ &m_overlapped);
+
+ DWORD last_error= ret? 0: WSAGetLastError();
+ if (last_error == WSAECONNRESET || last_error == ERROR_NETNAME_DELETED)
+ {
+ if (m_tp_io)
+ CancelThreadpoolIo(m_tp_io);
+ closesocket(m_client_socket);
+ goto retry;
+ }
+
+ if (ret || last_error == ERROR_IO_PENDING || abort_loop)
+ return;
+
+ sql_print_error("my_AcceptEx failed, last error %u", last_error);
+ abort();
+ }
+
+ /* Create new socket connection.*/
+ void completion_callback(bool success)
+ {
+ if (!success)
+ {
+ /* my_AcceptEx() returned error */
+ closesocket(m_client_socket);
+ begin_accept();
+ return;
+ }
+
+ MYSQL_SOCKET s_client{m_client_socket};
+ MYSQL_SOCKET s_listen{(SOCKET)m_handle};
+
+#ifdef HAVE_PSI_SOCKET_INTERFACE
+ /* Parse socket addresses buffer filled by AcceptEx(),
+ only needed for PSI instrumentation. */
+ sockaddr *local_addr, *remote_addr;
+ int local_addr_len, remote_addr_len;
+
+ my_GetAcceptExSockaddrs(m_buffer,
+ 0, sizeof(sockaddr_storage) + 16, sizeof(sockaddr_storage) + 16,
+ &local_addr, &local_addr_len, &remote_addr, &remote_addr_len);
+
+ s_client.m_psi= PSI_SOCKET_CALL(init_socket)
+ (key_socket_client_connection, (const my_socket*)&s_listen.fd, remote_addr, remote_addr_len);
+#endif
+
+ /* Start accepting new connection. After this point, do not use
+ any member data, they could be used by a different (threadpool) thread. */
+ begin_accept();
+
+ /* Some chores post-AcceptEx() that we need to create a normal socket.*/
+ if (setsockopt(s_client.fd, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
+ (char *)&s_listen.fd, sizeof(s_listen.fd)))
+ {
+ if (!abort_loop)
+ {
+ sql_perror("setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
+ abort();
+ }
+ }
+
+ /* Create a new connection.*/
+ handle_accepted_socket(s_client, s_listen);
+ }
+
+ ~Socket_Listener()
+ {
+ if (m_client_socket != INVALID_SOCKET)
+ closesocket(m_client_socket);
+ }
+
+ /*
+ Retrieve the pointer to the Winsock extension functions
+ AcceptEx and GetAcceptExSockaddrs.
+ */
+ static void init_winsock_extensions()
+ {
+ SOCKET s= mysql_socket_getfd(base_ip_sock);
+ if (s == INVALID_SOCKET)
+ s= mysql_socket_getfd(extra_ip_sock);
+ if (s == INVALID_SOCKET)
+ {
+ /* --skip-networking was used*/
+ return;
+ }
+ GUID guid_AcceptEx= WSAID_ACCEPTEX;
+ GUID guid_GetAcceptExSockaddrs= WSAID_GETACCEPTEXSOCKADDRS;
+
+ GUID *guids[]= { &guid_AcceptEx, &guid_GetAcceptExSockaddrs };
+ void *funcs[]= { &my_AcceptEx, &my_GetAcceptExSockaddrs };
+ DWORD bytes;
+ for (int i= 0; i < array_elements(guids); i++)
+ {
+ if (WSAIoctl(s,
+ SIO_GET_EXTENSION_FUNCTION_POINTER,
+ guids[i], sizeof(GUID),
+ funcs[i], sizeof(void *),
+ &bytes, 0, 0) == -1)
+ {
+ sql_print_error("WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER) failed");
+ unireg_abort(1);
+ }
+ }
+ }
+};
+
+/*
+ Create a security descriptor for pipe.
+ - Use low integrity level, so that it is possible to connect
+ from any process.
+ - Give current user read/write access to pipe.
+ - Give Everyone read/write access to pipe minus FILE_CREATE_PIPE_INSTANCE
+*/
+static void init_pipe_security_descriptor()
+{
+#define SDDL_FMT "S:(ML;; NW;;; LW) D:(A;; 0x%08x;;; WD)(A;; FRFW;;; %s)"
+#define EVERYONE_PIPE_ACCESS_MASK \
+ (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES | READ_CONTROL | \
+ SYNCHRONIZE | FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
+
+#ifndef SECURITY_MAX_SID_STRING_CHARACTERS
+/* Old SDK does not have this constant */
+#define SECURITY_MAX_SID_STRING_CHARACTERS 187
+#endif
+
+ /*
+ Figure out SID of the user that runs the server, then create SDDL string
+ for pipe permissions, and convert it to the security descriptor.
+ */
+ char sddl_string[sizeof(SDDL_FMT) + 8 + SECURITY_MAX_SID_STRING_CHARACTERS];
+ struct
+ {
+ TOKEN_USER token_user;
+ BYTE buffer[SECURITY_MAX_SID_SIZE];
+ } token_buffer;
+ HANDLE token;
+ DWORD tmp;
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
+ goto fail;
+
+ if (!GetTokenInformation(token, TokenUser, &token_buffer,
+ (DWORD) sizeof(token_buffer), &tmp))
+ goto fail;
+
+ CloseHandle(token);
+
+ char *current_user_string_sid;
+ if (!ConvertSidToStringSid(token_buffer.token_user.User.Sid,
+ &current_user_string_sid))
+ goto fail;
+
+ snprintf(sddl_string, sizeof(sddl_string), SDDL_FMT,
+ EVERYONE_PIPE_ACCESS_MASK, current_user_string_sid);
+ LocalFree(current_user_string_sid);
+
+ if (ConvertStringSecurityDescriptorToSecurityDescriptor(sddl_string,
+ SDDL_REVISION_1, &pipe_security.lpSecurityDescriptor, 0))
+ return;
+
+fail:
+ sql_perror("Can't start server : Initialize security descriptor");
+ unireg_abort(1);
+}
+
+/**
+ Pipe Listener.
+ Only event notification mode is implemented, no threadpool
+*/
+struct Pipe_Listener : public Listener
+{
+ PTP_CALLBACK_ENVIRON m_tp_env;
+ Pipe_Listener():
+ Listener(INVALID_HANDLE_VALUE, CreateEvent(0, FALSE, FALSE, 0)),
+ m_tp_env(get_threadpool_win_callback_environ())
+ {
+ }
+
+ /*
+ Creates local named pipe instance \\.\pipe\$socket for named pipe connection.
+ */
+ static HANDLE create_named_pipe()
+ {
+ static bool first_instance= true;
+ static char pipe_name[512];
+ DWORD open_mode= PIPE_ACCESS_DUPLEX |
+ FILE_FLAG_OVERLAPPED;
+
+ if (first_instance)
+ {
+ snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\pipe\\%s", mysqld_unix_port);
+ open_mode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
+ init_pipe_security_descriptor();
+ pipe_security.nLength= sizeof(SECURITY_ATTRIBUTES);
+ pipe_security.bInheritHandle= FALSE;
+ }
+ HANDLE pipe_handle= CreateNamedPipe(pipe_name,
+ open_mode,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+ PIPE_UNLIMITED_INSTANCES,
+ (int)global_system_variables.net_buffer_length,
+ (int)global_system_variables.net_buffer_length,
+ NMPWAIT_USE_DEFAULT_WAIT,
+ &pipe_security);
+ if (pipe_handle == INVALID_HANDLE_VALUE)
+ {
+ sql_perror("Create named pipe failed");
+ sql_print_error("Aborting");
+ exit(1);
+ }
+ first_instance= false;
+ return pipe_handle;
+ }
+
+ static void create_pipe_connection(HANDLE pipe)
+ {
+ CONNECT *connect;
+ if (!(connect= new CONNECT) || !(connect->vio= vio_new_win32pipe(pipe)))
+ {
+ CloseHandle(pipe);
+ delete connect;
+ statistic_increment(aborted_connects, &LOCK_status);
+ statistic_increment(connection_errors_internal, &LOCK_status);
+ return;
+ }
+ connect->host= my_localhost;
+ create_new_thread(connect);
+ }
+
+ /* Threadpool callback.*/
+ static void CALLBACK tp_create_pipe_connection(
+ PTP_CALLBACK_INSTANCE,void *Context)
+ {
+ tp_win_callback_prolog();
+ create_pipe_connection(Context);
+ }
+
+ void begin_accept()
+ {
+ m_handle= create_named_pipe();
+ BOOL connected= ConnectNamedPipe(m_handle, &m_overlapped);
+ if (connected)
+ {
+ /* Overlapped ConnectNamedPipe should return zero. */
+ sql_perror("Overlapped ConnectNamedPipe() already connected.");
+ abort();
+ }
+ DWORD last_error= GetLastError();
+ switch (last_error)
+ {
+ case ERROR_PIPE_CONNECTED:
+ /* Client is already connected, so signal an event.*/
+ {
+ /*
+ Cleanup overlapped (so that subsequent GetOverlappedResult()
+ does not show results of previous IO
+ */
+ HANDLE e= m_overlapped.hEvent;
+ memset(&m_overlapped, 0, sizeof(m_overlapped));
+ m_overlapped.hEvent = e;
+ }
+ if (!SetEvent(m_overlapped.hEvent))
+ {
+ sql_perror("SetEvent() failed for connected pipe.");
+ abort();
+ }
+ break;
+ case ERROR_IO_PENDING:
+ break;
+ default:
+ sql_perror("ConnectNamedPipe() failed.");
+ abort();
+ break;
+ }
+ }
+
+ void completion_callback(bool success)
+ {
+ if (!success)
+ {
+#ifdef DBUG_OFF
+ sql_print_warning("ConnectNamedPipe completed with %u", GetLastError());
+#endif
+ CloseHandle(m_handle);
+ m_handle= INVALID_HANDLE_VALUE;
+ begin_accept();
+ return;
+ }
+ HANDLE pipe= m_handle;
+ begin_accept();
+ // If threadpool is on, create connection in threadpool thread
+ if (!m_tp_env || !TrySubmitThreadpoolCallback(tp_create_pipe_connection, pipe, m_tp_env))
+ create_pipe_connection(pipe);
+ }
+
+ ~Pipe_Listener()
+ {
+ if (m_handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(m_handle);
+ }
+ }
+
+ static void cleanup()
+ {
+ LocalFree(pipe_security.lpSecurityDescriptor);
+ }
+};
+
+/**
+ Accept new client connections on Windows.
+
+ Since we deal with pipe and sockets, they cannot be put into a select/loop.
+ But we can use asynchronous IO, and WaitForMultipleObject() loop.
+
+ In addition, for slightly better performance, if we're using threadpool,
+ socket connections are accepted directly in the threadpool.
+
+ The mode of operation is therefore
+
+ 1. There is WaitForMultipleObject() loop that waits for shutdown notification
+ (hEventShutdown),and possibly pipes and sockets(e.g if threadpool is not used)
+ This loop ends when shutdown notification is detected.
+
+ 2. If threadpool is used, new socket connections are accepted there.
+*/
+
+
+#define MAX_WAIT_HANDLES 32
+#define NUM_PIPE_LISTENERS 24
+#define SHUTDOWN_IDX 0
+#define LISTENER_START_IDX 1
+
+void handle_connections_win()
+{
+ Listener* all_listeners[MAX_WAIT_HANDLES]= {};
+ HANDLE wait_events[MAX_WAIT_HANDLES]= {};
+ int n_listeners= 0;
+ int n_waits= 0;
+
+ Socket_Listener::init_winsock_extensions();
+
+ /* Listen for TCP connections on "extra-port" (no threadpool).*/
+ if (extra_ip_sock.fd != INVALID_SOCKET)
+ all_listeners[n_listeners++]= new Socket_Listener(extra_ip_sock, 0);
+
+ /* Listen for named pipe connections */
+ if (mysqld_unix_port[0] && !opt_bootstrap && opt_enable_named_pipe)
+ {
+ /*
+ Use several listeners for pipe, to reduce ERROR_PIPE_BUSY on client side.
+ */
+ for (int i= 0; i < NUM_PIPE_LISTENERS; i++)
+ all_listeners[n_listeners++]= new Pipe_Listener();
+ }
+
+ if (base_ip_sock.fd != INVALID_SOCKET)
+ {
+ /* Wait for TCP connections.*/
+ SetFileCompletionNotificationModes((HANDLE)base_ip_sock.fd, FILE_SKIP_SET_EVENT_ON_HANDLE);
+ all_listeners[n_listeners++]= new Socket_Listener(base_ip_sock, get_threadpool_win_callback_environ());
+ }
+
+ if (!n_listeners && !opt_bootstrap)
+ {
+ sql_print_error("Either TCP connections or named pipe connections must be enabled.");
+ unireg_abort(1);
+ }
+
+ wait_events[SHUTDOWN_IDX]= hEventShutdown;
+ n_waits = 1;
+
+ for (int i= 0; i < n_listeners; i++)
+ {
+ HANDLE wait_handle= all_listeners[i]->wait_handle();
+ if(wait_handle)
+ {
+ DBUG_ASSERT((i == 0) || (all_listeners[i-1]->wait_handle() != 0));
+ wait_events[n_waits++]= wait_handle;
+ }
+ all_listeners[i]->begin_accept();
+ }
+
+ for (;;)
+ {
+ DWORD idx = WaitForMultipleObjects(n_waits ,wait_events, FALSE, INFINITE);
+ DBUG_ASSERT((int)idx >= 0 && (int)idx < n_waits);
+
+ if (idx == SHUTDOWN_IDX)
+ break;
+
+ all_listeners[idx - LISTENER_START_IDX]->completion_callback();
+ }
+
+ /* Cleanup */
+ for (int i= 0; i < n_listeners; i++)
+ {
+ Listener *listener= all_listeners[i];
+ if (listener->wait_handle())
+ delete listener;
+ else
+ // Threadpool-bound listener will be deleted in threadpool
+ // Do not call destructor, because callback maybe running.
+ listener->cancel();
+ }
+ Pipe_Listener::cleanup();
+}
diff --git a/sql/handle_connections_win.h b/sql/handle_connections_win.h
new file mode 100644
index 00000000000..a81f4346fb2
--- /dev/null
+++ b/sql/handle_connections_win.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2018 MariaDB Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+/**
+ Handles incoming socket and pipe connections, on Windows.
+ Creates new (THD) connections..
+*/
+extern void handle_connections_win();
diff --git a/sql/handler.cc b/sql/handler.cc
index c0a810a72bc..ac5b43249db 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates.
- Copyright (c) 2009, 2019, MariaDB Corporation.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -43,6 +43,7 @@
#include "debug_sync.h" // DEBUG_SYNC
#include "sql_audit.h"
#include "ha_sequence.h"
+#include "rowid_filter.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -54,8 +55,12 @@
#include "semisync_master.h"
#include "wsrep_mysqld.h"
-#include "wsrep.h"
+#ifdef WITH_WSREP
+#include "wsrep_binlog.h"
#include "wsrep_xid.h"
+#include "wsrep_thd.h"
+#include "wsrep_trans_observer.h" /* wsrep transaction hooks */
+#endif /* WITH_WSREP */
/*
While we have legacy_db_type, we have this array to
@@ -128,6 +133,23 @@ static plugin_ref ha_default_tmp_plugin(THD *thd)
return ha_default_plugin(thd);
}
+#if defined(WITH_ARIA_STORAGE_ENGINE) && MYSQL_VERSION_ID < 100500
+void ha_maria_implicit_commit(THD *thd, bool new_trn)
+{
+ if (ha_maria::has_active_transaction(thd))
+ {
+ int error;
+ MDL_request mdl_request;
+ mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT, MDL_EXPLICIT);
+ error= thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout);
+ ha_maria::implicit_commit(thd, new_trn);
+ if (!error)
+ thd->mdl_context.release_lock(mdl_request.ticket);
+ }
+}
+#endif
+
/** @brief
Return the default storage engine handlerton for thread
@@ -277,6 +299,9 @@ handlerton *ha_checktype(THD *thd, handlerton *hton, bool no_substitute)
if (no_substitute)
return NULL;
+#ifdef WITH_WSREP
+ (void)wsrep_after_rollback(thd, false);
+#endif /* WITH_WSREP */
return ha_default_handlerton(thd);
} /* ha_checktype */
@@ -322,7 +347,7 @@ handler *get_ha_partition(partition_info *part_info)
}
else
{
- my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR),
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATAL),
static_cast<int>(sizeof(ha_partition)));
}
DBUG_RETURN(((handler*) partition));
@@ -432,9 +457,9 @@ static int ha_finish_errors(void)
return 0;
}
-static volatile int32 need_full_discover_for_existence= 0;
-static volatile int32 engines_with_discover_file_names= 0;
-static volatile int32 engines_with_discover= 0;
+static Atomic_counter<int32> need_full_discover_for_existence(0);
+static Atomic_counter<int32> engines_with_discover_file_names(0);
+static Atomic_counter<int32> engines_with_discover(0);
static int full_discover_for_existence(handlerton *, const char *, const char *)
{ return 0; }
@@ -456,13 +481,13 @@ static int hton_ext_based_table_discovery(handlerton *hton, LEX_CSTRING *db,
static void update_discovery_counters(handlerton *hton, int val)
{
if (hton->discover_table_existence == full_discover_for_existence)
- my_atomic_add32(&need_full_discover_for_existence, val);
+ need_full_discover_for_existence+= val;
if (hton->discover_table_names && hton->tablefile_extensions[0])
- my_atomic_add32(&engines_with_discover_file_names, val);
+ engines_with_discover_file_names+= val;
if (hton->discover_table)
- my_atomic_add32(&engines_with_discover, val);
+ engines_with_discover+= val;
}
int ha_finalize_handlerton(st_plugin_int *plugin)
@@ -709,7 +734,7 @@ int ha_init()
binary log (which is considered a transaction-capable storage engine in
counting total_ha)
*/
- opt_using_transactions= total_ha>(ulong)opt_bin_log;
+ opt_using_transactions= total_ha > (ulong) opt_bin_log;
savepoint_alloc_size+= sizeof(SAVEPOINT);
DBUG_RETURN(error);
}
@@ -719,7 +744,6 @@ int ha_end()
int error= 0;
DBUG_ENTER("ha_end");
-
/*
This should be eventually based on the graceful shutdown flag.
So if flag is equal to HA_PANIC_CLOSE, the deallocate
@@ -835,11 +859,10 @@ static my_bool kill_handlerton(THD *thd, plugin_ref plugin,
{
handlerton *hton= plugin_hton(plugin);
- mysql_mutex_lock(&thd->LOCK_thd_data);
+ mysql_mutex_assert_owner(&thd->LOCK_thd_data);
if (hton->state == SHOW_OPTION_YES && hton->kill_query &&
thd_get_ha_data(thd, hton))
hton->kill_query(hton, thd, *(enum thd_kill_levels *) level);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
return FALSE;
}
@@ -851,6 +874,43 @@ void ha_kill_query(THD* thd, enum thd_kill_levels level)
}
+/*****************************************************************************
+ Backup functions
+******************************************************************************/
+
+static my_bool plugin_prepare_for_backup(THD *unused1, plugin_ref plugin,
+ void *not_used)
+{
+ handlerton *hton= plugin_hton(plugin);
+ if (hton->state == SHOW_OPTION_YES && hton->prepare_for_backup)
+ hton->prepare_for_backup();
+ return FALSE;
+}
+
+void ha_prepare_for_backup()
+{
+ plugin_foreach_with_mask(0, plugin_prepare_for_backup,
+ MYSQL_STORAGE_ENGINE_PLUGIN,
+ PLUGIN_IS_DELETED|PLUGIN_IS_READY, 0);
+}
+
+static my_bool plugin_end_backup(THD *unused1, plugin_ref plugin,
+ void *not_used)
+{
+ handlerton *hton= plugin_hton(plugin);
+ if (hton->state == SHOW_OPTION_YES && hton->end_backup)
+ hton->end_backup();
+ return FALSE;
+}
+
+void ha_end_backup()
+{
+ plugin_foreach_with_mask(0, plugin_end_backup,
+ MYSQL_STORAGE_ENGINE_PLUGIN,
+ PLUGIN_IS_DELETED|PLUGIN_IS_READY, 0);
+}
+
+
/* ========================================================================
======================= TRANSACTIONS ===================================*/
@@ -1183,25 +1243,40 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg)
ha_info->register_ha(trans, ht_arg);
trans->no_2pc|=(ht_arg->prepare==0);
- if (thd->transaction.xid_state.xid.is_null())
- thd->transaction.xid_state.xid.set(thd->query_id);
+
+ /* Set implicit xid even if there's explicit XA, it will be ignored anyway. */
+ if (thd->transaction.implicit_xid.is_null())
+ thd->transaction.implicit_xid.set(thd->query_id);
+
DBUG_VOID_RETURN;
}
static int prepare_or_error(handlerton *ht, THD *thd, bool all)
{
+#ifdef WITH_WSREP
+ const bool run_wsrep_hooks= wsrep_run_commit_hook(thd, all);
+ if (run_wsrep_hooks && ht->flags & HTON_WSREP_REPLICATION &&
+ wsrep_before_prepare(thd, all))
+ {
+ return(1);
+ }
+#endif /* WITH_WSREP */
+
int err= ht->prepare(ht, thd, all);
status_var_increment(thd->status_var.ha_prepare_count);
if (err)
{
- /* avoid sending error, if we're going to replay the transaction */
-#ifdef WITH_WSREP
- if (ht != wsrep_hton ||
- err == EMSGSIZE || thd->wsrep_conflict_state != MUST_REPLAY)
-#endif
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
}
+#ifdef WITH_WSREP
+ if (run_wsrep_hooks && !err && ht->flags & HTON_WSREP_REPLICATION &&
+ wsrep_after_prepare(thd, all))
+ {
+ err= 1;
+ }
+#endif /* WITH_WSREP */
+
return err;
}
@@ -1342,6 +1417,9 @@ int ha_commit_trans(THD *thd, bool all)
Ha_trx_info *ha_info= trans->ha_list;
bool need_prepare_ordered, need_commit_ordered;
my_xid xid;
+#ifdef WITH_WSREP
+ const bool run_wsrep_hooks= wsrep_run_commit_hook(thd, all);
+#endif /* WITH_WSREP */
DBUG_ENTER("ha_commit_trans");
DBUG_PRINT("info",("thd: %p option_bits: %lu all: %d",
thd, (ulong) thd->variables.option_bits, all));
@@ -1385,10 +1463,6 @@ int ha_commit_trans(THD *thd, bool all)
DBUG_RETURN(2);
}
-#ifdef WITH_ARIA_STORAGE_ENGINE
- ha_maria::implicit_commit(thd, TRUE);
-#endif
-
if (!ha_info)
{
/*
@@ -1396,7 +1470,15 @@ int ha_commit_trans(THD *thd, bool all)
*/
if (is_real_trans)
thd->transaction.cleanup();
- DBUG_RETURN(0);
+#ifdef WITH_WSREP
+ if (wsrep_is_active(thd) && is_real_trans && !error)
+ {
+ wsrep_commit_empty(thd, all);
+ }
+#endif /* WITH_WSREP */
+
+ ha_maria_implicit_commit(thd, TRUE);
+ DBUG_RETURN(error);
}
DBUG_EXECUTE_IF("crash_commit_before", DBUG_SUICIDE(););
@@ -1409,7 +1491,7 @@ int ha_commit_trans(THD *thd, bool all)
/* rw_trans is TRUE when we in a transaction changing data */
bool rw_trans= is_real_trans &&
(rw_ha_count > (thd->is_current_stmt_binlog_disabled()?0U:1U));
- MDL_request mdl_request;
+ MDL_request mdl_backup;
DBUG_PRINT("info", ("is_real_trans: %d rw_trans: %d rw_ha_count: %d",
is_real_trans, rw_trans, rw_ha_count));
@@ -1423,19 +1505,25 @@ int ha_commit_trans(THD *thd, bool all)
We allow the owner of FTWRL to COMMIT; we assume that it knows
what it does.
*/
- mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE,
- MDL_EXPLICIT);
+ mdl_backup.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT, MDL_EXPLICIT);
- if (!WSREP(thd) &&
- thd->mdl_context.acquire_lock(&mdl_request,
- thd->variables.lock_wait_timeout))
+ if (!WSREP(thd))
{
- ha_rollback_trans(thd, all);
- DBUG_RETURN(1);
+ if (thd->mdl_context.acquire_lock(&mdl_backup,
+ thd->variables.lock_wait_timeout))
+ {
+ ha_rollback_trans(thd, all);
+ DBUG_RETURN(1);
+ }
+ thd->backup_commit_lock= &mdl_backup;
}
-
DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock");
+
+ /* Use shortcut as we already have the MDL_BACKUP_COMMIT lock */
+ ha_maria::implicit_commit(thd, TRUE);
}
+ else
+ ha_maria_implicit_commit(thd, TRUE);
if (rw_trans &&
opt_readonly &&
@@ -1483,13 +1571,33 @@ int ha_commit_trans(THD *thd, bool all)
if (trans->no_2pc || (rw_ha_count <= 1))
{
+#ifdef WITH_WSREP
+ /*
+ This commit will not go through log_and_order() where wsrep commit
+ ordering is normally done. Commit ordering must be done here.
+ */
+ if (run_wsrep_hooks)
+ error= wsrep_before_commit(thd, all);
+ if (error)
+ {
+ ha_rollback_trans(thd, FALSE);
+ goto wsrep_err;
+ }
+#endif /* WITH_WSREP */
error= ha_commit_one_phase(thd, all);
+#ifdef WITH_WSREP
+ if (run_wsrep_hooks)
+ error= error || wsrep_after_commit(thd, all);
+#endif /* WITH_WSREP */
goto done;
}
need_prepare_ordered= FALSE;
need_commit_ordered= FALSE;
- xid= thd->transaction.xid_state.xid.get_my_xid();
+ DBUG_ASSERT(thd->transaction.implicit_xid.get_my_xid() ==
+ thd->transaction.implicit_xid.quick_get_my_xid());
+ xid= thd->transaction.xid_state.is_explicit_XA() ? 0 :
+ thd->transaction.implicit_xid.quick_get_my_xid();
for (Ha_trx_info *hi= ha_info; hi; hi= hi->next())
{
@@ -1515,10 +1623,14 @@ int ha_commit_trans(THD *thd, bool all)
DBUG_EXECUTE_IF("crash_commit_after_prepare", DBUG_SUICIDE(););
#ifdef WITH_WSREP
- if (!error && WSREP_ON && wsrep_is_wsrep_xid(&thd->transaction.xid_state.xid))
+ if (run_wsrep_hooks && !error)
{
- // xid was rewritten by wsrep
- xid= wsrep_xid_seqno(thd->transaction.xid_state.xid);
+ wsrep::seqno const s= wsrep_xid_seqno(thd->wsrep_xid);
+ if (!s.is_undefined())
+ {
+ // xid was rewritten by wsrep
+ xid= s.get();
+ }
}
#endif /* WITH_WSREP */
@@ -1527,18 +1639,35 @@ int ha_commit_trans(THD *thd, bool all)
error= commit_one_phase_2(thd, all, trans, is_real_trans);
goto done;
}
-
+#ifdef WITH_WSREP
+ if (run_wsrep_hooks && (error = wsrep_before_commit(thd, all)))
+ goto wsrep_err;
+#endif /* WITH_WSREP */
DEBUG_SYNC(thd, "ha_commit_trans_before_log_and_order");
cookie= tc_log->log_and_order(thd, xid, all, need_prepare_ordered,
need_commit_ordered);
if (!cookie)
+ {
+ WSREP_DEBUG("log_and_order has failed %llu %d", thd->thread_id, cookie);
goto err;
-
+ }
DEBUG_SYNC(thd, "ha_commit_trans_after_log_and_order");
DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE(););
error= commit_one_phase_2(thd, all, trans, is_real_trans) ? 2 : 0;
-
+#ifdef WITH_WSREP
+ if (run_wsrep_hooks && (error || (error = wsrep_after_commit(thd, all))))
+ {
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ if (wsrep_must_abort(thd))
+ {
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ (void)tc_log->unlog(cookie, xid);
+ goto wsrep_err;
+ }
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ }
+#endif /* WITH_WSREP */
DBUG_EXECUTE_IF("crash_commit_before_unlog", DBUG_SUICIDE(););
if (tc_log->unlog(cookie, xid))
{
@@ -1560,6 +1689,19 @@ done:
goto end;
/* Come here if error and we need to rollback. */
+#ifdef WITH_WSREP
+wsrep_err:
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ if (run_wsrep_hooks && wsrep_must_abort(thd))
+ {
+ WSREP_DEBUG("BF abort has happened after prepare & certify");
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ ha_rollback_trans(thd, TRUE);
+ }
+ else
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+
+#endif /* WITH_WSREP */
err:
error= 1; /* Transaction was rolled back */
/*
@@ -1569,9 +1711,13 @@ err:
*/
if (!(thd->rgi_slave && thd->rgi_slave->is_parallel_exec))
ha_rollback_trans(thd, all);
-
+ else
+ {
+ WSREP_DEBUG("rollback skipped %p %d",thd->rgi_slave,
+ thd->rgi_slave->is_parallel_exec);
+ }
end:
- if (rw_trans && mdl_request.ticket)
+ if (mdl_backup.ticket)
{
/*
We do not always immediately release transactional locks
@@ -1579,14 +1725,25 @@ end:
thus we release the commit blocker lock as soon as it's
not needed.
*/
- thd->mdl_context.release_lock(mdl_request.ticket);
+ thd->mdl_context.release_lock(mdl_backup.ticket);
}
+ thd->backup_commit_lock= 0;
+#ifdef WITH_WSREP
+ if (wsrep_is_active(thd) && is_real_trans && !error &&
+ (rw_ha_count == 0 || all) &&
+ wsrep_not_committed(thd))
+ {
+ wsrep_commit_empty(thd, all);
+ }
+#endif /* WITH_WSREP */
+
DBUG_RETURN(error);
}
/**
@note
- This function does not care about global read lock. A caller should.
+ This function does not care about global read lock or backup locks,
+ the caller should.
@param[in] all Is set in case of explicit commit
(COMMIT statement), or implicit commit
@@ -1635,6 +1792,7 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
DBUG_ENTER("commit_one_phase_2");
if (is_real_trans)
DEBUG_SYNC(thd, "commit_one_phase_2");
+
if (ha_info)
{
for (; ha_info; ha_info= ha_info_next)
@@ -1663,6 +1821,7 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
#endif
}
}
+
/* Free resources and perform other cleanup even for 'empty' transactions. */
if (is_real_trans)
{
@@ -1738,6 +1897,9 @@ int ha_rollback_trans(THD *thd, bool all)
DBUG_RETURN(1);
}
+#ifdef WITH_WSREP
+ (void) wsrep_before_rollback(thd, all);
+#endif /* WITH_WSREP */
if (ha_info)
{
/* Close all cursors that can not survive ROLLBACK */
@@ -1753,9 +1915,9 @@ int ha_rollback_trans(THD *thd, bool all)
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error=1;
#ifdef WITH_WSREP
- WSREP_WARN("handlerton rollback failed, thd %llu %lld conf %d SQL %s",
- thd->thread_id, thd->query_id, thd->wsrep_conflict_state,
- thd->query());
+ WSREP_WARN("handlerton rollback failed, thd %lld %lld conf %d SQL %s",
+ thd->thread_id, thd->query_id, thd->wsrep_trx().state(),
+ thd->query());
#endif /* WITH_WSREP */
}
status_var_increment(thd->status_var.ha_rollback_count);
@@ -1766,17 +1928,25 @@ int ha_rollback_trans(THD *thd, bool all)
trans->no_2pc=0;
}
- /*
- Thanks to possibility of MDL deadlock rollback request can come even if
- transaction hasn't been started in any transactional storage engine.
- */
- if (is_real_trans && thd->transaction_rollback_request &&
- thd->transaction.xid_state.xa_state != XA_NOTR)
- thd->transaction.xid_state.rm_error= thd->get_stmt_da()->sql_errno();
-
+#ifdef WITH_WSREP
+ if (thd->is_error())
+ {
+ WSREP_DEBUG("ha_rollback_trans(%lld, %s) rolled back: %s: %s; is_real %d",
+ thd->thread_id, all?"TRUE":"FALSE", WSREP_QUERY(thd),
+ thd->get_stmt_da()->message(), is_real_trans);
+ }
+ (void) wsrep_after_rollback(thd, all);
+#endif /* WITH_WSREP */
/* Always cleanup. Even if nht==0. There may be savepoints. */
if (is_real_trans)
{
+ /*
+ Thanks to possibility of MDL deadlock rollback request can come even if
+ transaction hasn't been started in any transactional storage engine.
+ */
+ if (thd->transaction_rollback_request)
+ thd->transaction.xid_state.set_error(thd->get_stmt_da()->sql_errno());
+
thd->has_waiter= false;
thd->transaction.cleanup();
}
@@ -1911,22 +2081,16 @@ char *xid_to_str(char *buf, const XID &xid)
}
#endif
-#ifdef WITH_WSREP
static my_xid wsrep_order_and_check_continuity(XID *list, int len)
{
+#ifdef WITH_WSREP
wsrep_sort_xid_array(list, len);
- wsrep_uuid_t uuid;
- wsrep_seqno_t seqno;
- if (wsrep_get_SE_checkpoint(uuid, seqno))
- {
- WSREP_ERROR("Could not read wsrep SE checkpoint for recovery");
- return 0;
- }
- long long cur_seqno= seqno;
+ wsrep::gtid cur_position= wsrep_get_SE_checkpoint();
+ long long cur_seqno= cur_position.seqno().get();
for (int i= 0; i < len; ++i)
{
if (!wsrep_is_wsrep_xid(list + i) ||
- wsrep_xid_seqno(*(list + i)) != cur_seqno + 1)
+ wsrep_xid_seqno(list + i) != cur_seqno + 1)
{
WSREP_WARN("Discovered discontinuity in recovered wsrep "
"transaction XIDs. Truncating the recovery list to "
@@ -1937,9 +2101,10 @@ static my_xid wsrep_order_and_check_continuity(XID *list, int len)
}
WSREP_INFO("Last wsrep seqno to be recovered %lld", cur_seqno);
return (cur_seqno < 0 ? 0 : cur_seqno);
-}
+#else
+ return 0;
#endif /* WITH_WSREP */
-
+}
/**
recover() step of xa.
@@ -1977,33 +2142,43 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
{
sql_print_information("Found %d prepared transaction(s) in %s",
got, hton_name(hton)->str);
-#ifdef WITH_WSREP
/* If wsrep_on=ON, XIDs are first ordered and then the range of
recovered XIDs is checked for continuity. All the XIDs which
are in continuous range can be safely committed if binlog
is off since they have already ordered and certified in the
- cluster. */
- my_xid wsrep_limit= 0;
+ cluster.
+
+ The discontinuity of wsrep XIDs may happen because the GTID
+ is assigned for transaction in wsrep_before_prepare(), but the
+ commit order is entered in wsrep_before_commit(). This means that
+ transactions may run prepare step out of order and may
+ result in gap in wsrep XIDs. This can be the case for example
+ if we have T1 with seqno 1 and T2 with seqno 2 and the server
+ crashes after T2 finishes prepare step but before T1 starts
+ the prepare.
+ */
+ my_xid wsrep_limit __attribute__((unused))= 0;
+
+ /* Note that we could call this for binlog also that
+ will not have WSREP(thd) but global wsrep on might
+ be true.
+ */
if (WSREP_ON)
- {
wsrep_limit= wsrep_order_and_check_continuity(info->list, got);
- }
-#endif /* WITH_WSREP */
for (int i=0; i < got; i ++)
{
- my_xid x= IF_WSREP(WSREP_ON && wsrep_is_wsrep_xid(&info->list[i]) ?
- wsrep_xid_seqno(info->list[i]) :
+ my_xid x= IF_WSREP(wsrep_is_wsrep_xid(&info->list[i]) ?
+ wsrep_xid_seqno(&info->list[i]) :
info->list[i].get_my_xid(),
info->list[i].get_my_xid());
if (!x) // not "mine" - that is generated by external TM
{
-#ifndef DBUG_OFF
- char buf[XIDDATASIZE*4+6]; // see xid_to_str
- DBUG_PRINT("info",
- ("ignore xid %s", xid_to_str(buf, info->list[i])));
-#endif
- xid_cache_insert(info->list+i, XA_PREPARED);
+ DBUG_EXECUTE("info",{
+ char buf[XIDDATASIZE*4+6];
+ _db_doprnt_("ignore xid %s", xid_to_str(buf, info->list[i]));
+ });
+ xid_cache_insert(info->list + i);
info->found_foreign_xids++;
continue;
}
@@ -2023,33 +2198,25 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
my_hash_search(info->commit_list, (uchar *)&x, sizeof(x)) != 0 :
tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT))
{
-#ifndef DBUG_OFF
- int rc=
-#endif
- hton->commit_by_xid(hton, info->list+i);
-#ifndef DBUG_OFF
+ int rc= hton->commit_by_xid(hton, info->list+i);
if (rc == 0)
{
- char buf[XIDDATASIZE*4+6]; // see xid_to_str
- DBUG_PRINT("info",
- ("commit xid %s", xid_to_str(buf, info->list[i])));
+ DBUG_EXECUTE("info",{
+ char buf[XIDDATASIZE*4+6];
+ _db_doprnt_("commit xid %s", xid_to_str(buf, info->list[i]));
+ });
}
-#endif
}
else
{
-#ifndef DBUG_OFF
- int rc=
-#endif
- hton->rollback_by_xid(hton, info->list+i);
-#ifndef DBUG_OFF
+ int rc= hton->rollback_by_xid(hton, info->list+i);
if (rc == 0)
{
- char buf[XIDDATASIZE*4+6]; // see xid_to_str
- DBUG_PRINT("info",
- ("rollback xid %s", xid_to_str(buf, info->list[i])));
+ DBUG_EXECUTE("info",{
+ char buf[XIDDATASIZE*4+6];
+ _db_doprnt_("rollback xid %s", xid_to_str(buf, info->list[i]));
+ });
}
-#endif
}
}
if (got < info->len)
@@ -2116,186 +2283,6 @@ int ha_recover(HASH *commit_list)
DBUG_RETURN(0);
}
-/**
- return the XID as it appears in the SQL function's arguments.
- So this string can be passed to XA START, XA PREPARE etc...
-
- @note
- the 'buf' has to have space for at least SQL_XIDSIZE bytes.
-*/
-
-
-/*
- 'a'..'z' 'A'..'Z', '0'..'9'
- and '-' '_' ' ' symbols don't have to be
- converted.
-*/
-
-static const char xid_needs_conv[128]=
-{
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
- 0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,
- 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,
- 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1
-};
-
-uint get_sql_xid(XID *xid, char *buf)
-{
- int tot_len= xid->gtrid_length + xid->bqual_length;
- int i;
- const char *orig_buf= buf;
-
- for (i=0; i<tot_len; i++)
- {
- uchar c= ((uchar *) xid->data)[i];
- if (c >= 128 || xid_needs_conv[c])
- break;
- }
-
- if (i >= tot_len)
- {
- /* No need to convert characters to hexadecimals. */
- *buf++= '\'';
- memcpy(buf, xid->data, xid->gtrid_length);
- buf+= xid->gtrid_length;
- *buf++= '\'';
- if (xid->bqual_length > 0 || xid->formatID != 1)
- {
- *buf++= ',';
- *buf++= '\'';
- memcpy(buf, xid->data+xid->gtrid_length, xid->bqual_length);
- buf+= xid->bqual_length;
- *buf++= '\'';
- }
- }
- else
- {
- *buf++= 'X';
- *buf++= '\'';
- for (i= 0; i < xid->gtrid_length; i++)
- {
- *buf++=_dig_vec_lower[((uchar*) xid->data)[i] >> 4];
- *buf++=_dig_vec_lower[((uchar*) xid->data)[i] & 0x0f];
- }
- *buf++= '\'';
- if (xid->bqual_length > 0 || xid->formatID != 1)
- {
- *buf++= ',';
- *buf++= 'X';
- *buf++= '\'';
- for (; i < tot_len; i++)
- {
- *buf++=_dig_vec_lower[((uchar*) xid->data)[i] >> 4];
- *buf++=_dig_vec_lower[((uchar*) xid->data)[i] & 0x0f];
- }
- *buf++= '\'';
- }
- }
-
- if (xid->formatID != 1)
- {
- *buf++= ',';
- buf+= my_longlong10_to_str_8bit(&my_charset_bin, buf,
- MY_INT64_NUM_DECIMAL_DIGITS, -10, xid->formatID);
- }
-
- return (uint)(buf - orig_buf);
-}
-
-
-/**
- return the list of XID's to a client, the same way SHOW commands do.
-
- @note
- I didn't find in XA specs that an RM cannot return the same XID twice,
- so mysql_xa_recover does not filter XID's to ensure uniqueness.
- It can be easily fixed later, if necessary.
-*/
-
-static my_bool xa_recover_callback(XID_STATE *xs, Protocol *protocol,
- char *data, uint data_len, CHARSET_INFO *data_cs)
-{
- if (xs->xa_state == XA_PREPARED)
- {
- protocol->prepare_for_resend();
- protocol->store_longlong((longlong) xs->xid.formatID, FALSE);
- protocol->store_longlong((longlong) xs->xid.gtrid_length, FALSE);
- protocol->store_longlong((longlong) xs->xid.bqual_length, FALSE);
- protocol->store(data, data_len, data_cs);
- if (protocol->write())
- return TRUE;
- }
- return FALSE;
-}
-
-
-static my_bool xa_recover_callback_short(XID_STATE *xs, Protocol *protocol)
-{
- return xa_recover_callback(xs, protocol, xs->xid.data,
- xs->xid.gtrid_length + xs->xid.bqual_length, &my_charset_bin);
-}
-
-
-static my_bool xa_recover_callback_verbose(XID_STATE *xs, Protocol *protocol)
-{
- char buf[SQL_XIDSIZE];
- uint len= get_sql_xid(&xs->xid, buf);
- return xa_recover_callback(xs, protocol, buf, len,
- &my_charset_utf8_general_ci);
-}
-
-
-bool mysql_xa_recover(THD *thd)
-{
- List<Item> field_list;
- Protocol *protocol= thd->protocol;
- MEM_ROOT *mem_root= thd->mem_root;
- my_hash_walk_action action;
- DBUG_ENTER("mysql_xa_recover");
-
- field_list.push_back(new (mem_root)
- Item_int(thd, "formatID", 0,
- MY_INT32_NUM_DECIMAL_DIGITS), mem_root);
- field_list.push_back(new (mem_root)
- Item_int(thd, "gtrid_length", 0,
- MY_INT32_NUM_DECIMAL_DIGITS), mem_root);
- field_list.push_back(new (mem_root)
- Item_int(thd, "bqual_length", 0,
- MY_INT32_NUM_DECIMAL_DIGITS), mem_root);
- {
- uint len;
- CHARSET_INFO *cs;
-
- if (thd->lex->verbose)
- {
- len= SQL_XIDSIZE;
- cs= &my_charset_utf8_general_ci;
- action= (my_hash_walk_action) xa_recover_callback_verbose;
- }
- else
- {
- len= XIDDATASIZE;
- cs= &my_charset_bin;
- action= (my_hash_walk_action) xa_recover_callback_short;
- }
-
- field_list.push_back(new (mem_root)
- Item_empty_string(thd, "data", len, cs), mem_root);
- }
-
- if (protocol->send_result_set_metadata(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(1);
-
- if (xid_cache_iterate(thd, action, protocol))
- DBUG_RETURN(1);
- my_eof(thd);
- DBUG_RETURN(0);
-}
/*
Called by engine to notify TC that a new commit checkpoint has been reached.
@@ -2381,11 +2368,26 @@ int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
{
int err;
handlerton *ht= ha_info->ht();
+#ifdef WITH_WSREP
+ if (WSREP(thd) && ht->flags & HTON_WSREP_REPLICATION)
+ {
+ WSREP_DEBUG("ha_rollback_to_savepoint: run before_rollbackha_rollback_trans hook");
+ (void) wsrep_before_rollback(thd, !thd->in_sub_stmt);
+
+ }
+#endif // WITH_WSREP
if ((err= ht->rollback(ht, thd, !thd->in_sub_stmt)))
{ // cannot happen
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error=1;
}
+#ifdef WITH_WSREP
+ if (WSREP(thd) && ht->flags & HTON_WSREP_REPLICATION)
+ {
+ WSREP_DEBUG("ha_rollback_to_savepoint: run after_rollback hook");
+ (void) wsrep_after_rollback(thd, !thd->in_sub_stmt);
+ }
+#endif // WITH_WSREP
status_var_increment(thd->status_var.ha_rollback_count);
ha_info_next= ha_info->next();
ha_info->reset(); /* keep it conveniently zero-filled */
@@ -2402,6 +2404,16 @@ int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
*/
int ha_savepoint(THD *thd, SAVEPOINT *sv)
{
+#ifdef WITH_WSREP
+ /*
+ Register binlog hton for savepoint processing if wsrep binlog
+ emulation is on.
+ */
+ if (WSREP_EMULATE_BINLOG(thd) && wsrep_thd_is_local(thd))
+ {
+ wsrep_register_binlog_handler(thd, thd->in_multi_stmt_transaction_mode());
+ }
+#endif /* WITH_WSREP */
int error=0;
THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
&thd->transaction.all);
@@ -2628,7 +2640,7 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
dummy_share.table_name= *alias;
dummy_table.alias.set(alias->str, alias->length, table_alias_charset);
file->change_table_ptr(&dummy_table, &dummy_share);
- file->print_error(error, MYF(intercept ? ME_JUST_WARNING : 0));
+ file->print_error(error, MYF(intercept ? ME_WARNING : 0));
}
if (intercept)
error= 0;
@@ -2686,23 +2698,28 @@ LEX_CSTRING *handler::engine_name()
}
+/*
+ It is assumed that the value of the parameter 'ranges' can be only 0 or 1.
+ If ranges == 1 then the function returns the cost of index only scan
+ by index 'keyno' of one range containing 'rows' key entries.
+ If ranges == 0 then the function returns only the cost of copying
+ those key entries into the engine buffers.
+*/
+
double handler::keyread_time(uint index, uint ranges, ha_rows rows)
{
- /*
- It is assumed that we will read trough the whole key range and that all
- key blocks are half full (normally things are much better). It is also
- assumed that each time we read the next key from the index, the handler
- performs a random seek, thus the cost is proportional to the number of
- blocks read. This model does not take into account clustered indexes -
- engines that support that (e.g. InnoDB) may want to overwrite this method.
- The model counts in the time to read index entries from cache.
- */
+ DBUG_ASSERT(ranges == 0 || ranges == 1);
size_t len= table->key_info[index].key_length + ref_length;
if (index == table->s->primary_key && table->file->primary_key_is_clustered())
len= table->s->stored_rec_length;
- double keys_per_block= (stats.block_size/2.0/len+1);
- return (rows + keys_per_block-1)/ keys_per_block +
- len*rows/(stats.block_size+1)/TIME_FOR_COMPARE ;
+ double cost= (double)rows*len/(stats.block_size+1)*IDX_BLOCK_COPY_COST;
+ if (ranges)
+ {
+ uint keys_per_block= (uint) (stats.block_size/2.0/len+1);
+ ulonglong blocks= !rows ? 0 : (rows-1) / keys_per_block + 1;
+ cost+= blocks;
+ }
+ return cost;
}
void **handler::ha_data(THD *thd) const
@@ -3689,6 +3706,8 @@ void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag)
}
else
{
+ if (key->algorithm == HA_KEY_ALG_LONG_HASH)
+ setup_keyinfo_hash(key);
/* Table is opened and defined at this point */
key_unpack(&str,table, key);
uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(msg);
@@ -3699,6 +3718,8 @@ void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag)
}
my_printf_error(ER_DUP_ENTRY, msg, errflag, str.c_ptr_safe(),
key->name.str);
+ if (key->algorithm == HA_KEY_ALG_LONG_HASH)
+ re_setup_keyinfo_hash(key);
}
}
@@ -3716,7 +3737,6 @@ void print_keydup_error(TABLE *table, KEY *key, myf errflag)
errflag);
}
-
/**
Print error that we got from handler function.
@@ -3738,7 +3758,7 @@ void handler::print_error(int error, myf errflag)
if (ha_thd()->transaction_rollback_request)
{
/* Ensure this becomes a true error */
- errflag&= ~(ME_JUST_WARNING | ME_JUST_INFO);
+ errflag&= ~(ME_WARNING | ME_NOTE);
}
int textno= -1; // impossible value
@@ -3873,14 +3893,14 @@ void handler::print_error(int error, myf errflag)
{
textno=ER_RECORD_FILE_FULL;
/* Write the error message to error log */
- errflag|= ME_NOREFRESH;
+ errflag|= ME_ERROR_LOG;
break;
}
case HA_ERR_INDEX_FILE_FULL:
{
textno=ER_INDEX_FILE_FULL;
/* Write the error message to error log */
- errflag|= ME_NOREFRESH;
+ errflag|= ME_ERROR_LOG;
break;
}
case HA_ERR_LOCK_WAIT_TIMEOUT:
@@ -4007,14 +4027,14 @@ void handler::print_error(int error, myf errflag)
if (unlikely(fatal_error))
{
/* Ensure this becomes a true error */
- errflag&= ~(ME_JUST_WARNING | ME_JUST_INFO);
+ errflag&= ~(ME_WARNING | ME_NOTE);
if ((debug_assert_if_crashed_table ||
global_system_variables.log_warnings > 1))
{
/*
Log error to log before we crash or if extended warnings are requested
*/
- errflag|= ME_NOREFRESH;
+ errflag|= ME_ERROR_LOG;
}
}
@@ -4186,7 +4206,8 @@ static bool update_frm_version(TABLE *table)
int4store(version, MYSQL_VERSION_ID);
- if ((result= (int)mysql_file_pwrite(file, (uchar*) version, 4, 51L, MYF_RW)))
+ if ((result= (int)mysql_file_pwrite(file, (uchar*) version, 4, 51L,
+ MYF(MY_WME+MY_NABP))))
goto err;
table->s->mysql_version= MYSQL_VERSION_ID;
@@ -4205,9 +4226,10 @@ err:
*/
uint handler::get_dup_key(int error)
{
- DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
- m_lock_type != F_UNLCK);
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || m_lock_type != F_UNLCK);
DBUG_ENTER("handler::get_dup_key");
+ if (table->s->long_unique_table && table->file->errkey < table->s->keys)
+ DBUG_RETURN(table->file->errkey);
table->file->errkey = (uint) -1;
if (error == HA_ERR_FOUND_DUPP_KEY ||
error == HA_ERR_FOREIGN_DUPLICATE_KEY ||
@@ -4624,7 +4646,7 @@ handler::check_if_supported_inplace_alter(TABLE *altered_table,
DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
alter_table_operations inplace_offline_operations=
- ALTER_COLUMN_EQUAL_PACK_LENGTH |
+ ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE |
ALTER_COLUMN_NAME |
ALTER_RENAME_COLUMN |
ALTER_CHANGE_COLUMN_DEFAULT |
@@ -4660,7 +4682,7 @@ handler::check_if_supported_inplace_alter(TABLE *altered_table,
DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
uint table_changes= (ha_alter_info->handler_flags &
- ALTER_COLUMN_EQUAL_PACK_LENGTH) ?
+ ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE) ?
IS_EQUAL_PACK_LENGTH : IS_EQUAL_YES;
if (table->file->check_if_incompatible_data(create_info, table_changes)
== COMPATIBLE_DATA_YES)
@@ -4669,6 +4691,30 @@ handler::check_if_supported_inplace_alter(TABLE *altered_table,
DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
}
+Alter_inplace_info::Alter_inplace_info(HA_CREATE_INFO *create_info_arg,
+ Alter_info *alter_info_arg,
+ KEY *key_info_arg, uint key_count_arg,
+ partition_info *modified_part_info_arg,
+ bool ignore_arg, bool error_non_empty)
+ : create_info(create_info_arg),
+ alter_info(alter_info_arg),
+ key_info_buffer(key_info_arg),
+ key_count(key_count_arg),
+ index_drop_count(0),
+ index_drop_buffer(nullptr),
+ index_add_count(0),
+ index_add_buffer(nullptr),
+ rename_keys(current_thd->mem_root),
+ handler_ctx(nullptr),
+ group_commit_ctx(nullptr),
+ handler_flags(0),
+ modified_part_info(modified_part_info_arg),
+ ignore(ignore_arg),
+ online(false),
+ unsupported_reason(nullptr),
+ error_if_not_empty(error_non_empty)
+ {}
+
void Alter_inplace_info::report_unsupported_error(const char *not_supported,
const char *try_instead) const
{
@@ -4933,6 +4979,7 @@ void handler::get_dynamic_partition_info(PARTITION_STATS *stat_info,
stat_info->update_time= stats.update_time;
stat_info->check_time= stats.check_time;
stat_info->check_sum= stats.checksum;
+ stat_info->check_sum_null= stats.checksum_null;
}
@@ -5075,7 +5122,7 @@ int handler::calculate_checksum()
uchar null_mask= table->s->last_null_bit_pos
? 256 - (1 << table->s->last_null_bit_pos) : 0;
- table->use_all_columns();
+ table->use_all_stored_columns();
stats.checksum= 0;
if ((error= ha_rnd_init(1)))
@@ -5087,7 +5134,7 @@ int handler::calculate_checksum()
return HA_ERR_ABORTED_BY_USER;
ha_checksum row_crc= 0;
- error= table->file->ha_rnd_next(table->record[0]);
+ error= ha_rnd_next(table->record[0]);
if (error)
break;
@@ -5141,7 +5188,7 @@ int handler::calculate_checksum()
stats.checksum+= row_crc;
}
- table->file->ha_rnd_end();
+ ha_rnd_end();
return error == HA_ERR_END_OF_FILE ? 0 : error;
}
@@ -5211,7 +5258,7 @@ int ha_create_table(THD *thd, const char *path,
{
if (!thd->is_error())
my_error(ER_CANT_CREATE_TABLE, MYF(0), db, table_name, error);
- table.file->print_error(error, MYF(ME_JUST_WARNING));
+ table.file->print_error(error, MYF(ME_WARNING));
PSI_CALL_drop_table_share(temp_table, share.db.str, (uint)share.db.length,
share.table_name.str, (uint)share.table_name.length);
}
@@ -5972,27 +6019,75 @@ int handler::compare_key2(key_range *range) const
/**
ICP callback - to be called by an engine to check the pushed condition
*/
-extern "C" enum icp_result handler_index_cond_check(void* h_arg)
+extern "C" check_result_t handler_index_cond_check(void* h_arg)
{
handler *h= (handler*)h_arg;
THD *thd= h->table->in_use;
- enum icp_result res;
+ check_result_t res;
DEBUG_SYNC(thd, "handler_index_cond_check");
enum thd_kill_levels abort_at= h->has_transactions() ?
THD_ABORT_SOFTLY : THD_ABORT_ASAP;
if (thd_kill_level(thd) > abort_at)
- return ICP_ABORTED_BY_USER;
+ return CHECK_ABORTED_BY_USER;
if (h->end_range && h->compare_key2(h->end_range) > 0)
- return ICP_OUT_OF_RANGE;
+ return CHECK_OUT_OF_RANGE;
h->increment_statistics(&SSV::ha_icp_attempts);
- if ((res= h->pushed_idx_cond->val_int()? ICP_MATCH : ICP_NO_MATCH) ==
- ICP_MATCH)
+ if ((res= h->pushed_idx_cond->val_int()? CHECK_POS : CHECK_NEG) ==
+ CHECK_POS)
h->increment_statistics(&SSV::ha_icp_match);
return res;
}
+
+/**
+ Rowid filter callback - to be called by an engine to check rowid / primary
+ keys of the rows whose data is to be fetched against the used rowid filter
+*/
+
+extern "C"
+check_result_t handler_rowid_filter_check(void *h_arg)
+{
+ handler *h= (handler*) h_arg;
+ TABLE *tab= h->get_table();
+
+ /*
+ Check for out-of-range and killed conditions only if we haven't done it
+ already in the pushed index condition check
+ */
+ if (!h->pushed_idx_cond)
+ {
+ THD *thd= h->table->in_use;
+ DEBUG_SYNC(thd, "handler_rowid_filter_check");
+ enum thd_kill_levels abort_at= h->has_transactions() ?
+ THD_ABORT_SOFTLY : THD_ABORT_ASAP;
+ if (thd_kill_level(thd) > abort_at)
+ return CHECK_ABORTED_BY_USER;
+
+ if (h->end_range && h->compare_key2(h->end_range) > 0)
+ return CHECK_OUT_OF_RANGE;
+ }
+
+ h->position(tab->record[0]);
+ return h->pushed_rowid_filter->check((char*)h->ref)? CHECK_POS: CHECK_NEG;
+}
+
+
+/**
+ Callback function for an engine to check whether the used rowid filter
+ has been already built
+*/
+
+extern "C" int handler_rowid_filter_is_active(void *h_arg)
+{
+ if (!h_arg)
+ return false;
+ handler *h= (handler*) h_arg;
+ return h->rowid_filter_is_active;
+}
+
+
int handler::index_read_idx_map(uchar * buf, uint index, const uchar * key,
key_part_map keypart_map,
enum ha_rkey_function find_flag)
@@ -6173,6 +6268,12 @@ bool handler::check_table_binlog_row_based(bool binlog_row)
return false;
if (unlikely((table->in_use->variables.sql_log_bin_off)))
return 0; /* Called by partitioning engine */
+#ifdef WITH_WSREP
+ if (!table->in_use->variables.sql_log_bin &&
+ wsrep_thd_is_applying(table->in_use))
+ return 0; /* wsrep patch sets sql_log_bin to silence binlogging
+ from high priority threads */
+#endif /* WITH_WSREP */
if (unlikely((!check_table_binlog_row_based_done)))
{
check_table_binlog_row_based_done= 1;
@@ -6203,12 +6304,12 @@ bool handler::check_table_binlog_row_based_internal(bool binlog_row)
Otherwise, return 'true' if binary logging is on.
*/
IF_WSREP(((WSREP_EMULATE_BINLOG(thd) &&
- (thd->wsrep_exec_mode != REPL_RECV)) ||
+ wsrep_thd_is_local(thd)) ||
((WSREP(thd) ||
(thd->variables.option_bits & OPTION_BIN_LOG)) &&
mysql_bin_log.is_open())),
- (thd->variables.option_bits & OPTION_BIN_LOG) &&
- mysql_bin_log.is_open()));
+ (thd->variables.option_bits & OPTION_BIN_LOG) &&
+ mysql_bin_log.is_open()));
}
@@ -6245,7 +6346,9 @@ static int write_locked_table_maps(THD *thd)
MYSQL_LOCK *locks[2];
locks[0]= thd->extra_lock;
locks[1]= thd->lock;
- my_bool with_annotate= thd->variables.binlog_annotate_row_events &&
+ my_bool with_annotate= IF_WSREP(!wsrep_fragments_certified_for_stmt(thd),
+ true) &&
+ thd->variables.binlog_annotate_row_events &&
thd->query() && thd->query_length();
for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i )
@@ -6333,23 +6436,9 @@ int binlog_log_row(TABLE* table, const uchar *before_record,
/* only InnoDB tables will be replicated through binlog emulation */
if ((WSREP_EMULATE_BINLOG(thd) &&
- table->file->partition_ht()->db_type != DB_TYPE_INNODB) ||
- (thd->wsrep_ignore_table == true))
+ !(table->file->partition_ht()->flags & HTON_WSREP_REPLICATION)) ||
+ thd->wsrep_ignore_table == true)
return 0;
-
- /* enforce wsrep_max_ws_rows */
- if (WSREP(thd) && table->s->tmp_table == NO_TMP_TABLE)
- {
- thd->wsrep_affected_rows++;
- if (wsrep_max_ws_rows &&
- thd->wsrep_exec_mode != REPL_RECV &&
- thd->wsrep_affected_rows > wsrep_max_ws_rows)
- {
- trans_rollback_stmt(thd) || trans_rollback(thd);
- my_message(ER_ERROR_DURING_COMMIT, "wsrep_max_ws_rows exceeded", MYF(0));
- return ER_ERROR_DURING_COMMIT;
- }
- }
#endif
if (!table->file->check_table_binlog_row_based(1))
@@ -6457,12 +6546,194 @@ int handler::ha_reset()
/* Reset information about pushed engine conditions */
cancel_pushed_idx_cond();
/* Reset information about pushed index conditions */
+ cancel_pushed_rowid_filter();
clear_top_table_fields();
DBUG_RETURN(reset());
}
+#ifdef WITH_WSREP
+static int wsrep_after_row(THD *thd)
+{
+ DBUG_ENTER("wsrep_after_row");
+ /* enforce wsrep_max_ws_rows */
+ thd->wsrep_affected_rows++;
+ if (wsrep_max_ws_rows &&
+ wsrep_thd_is_local(thd) &&
+ thd->wsrep_affected_rows > wsrep_max_ws_rows)
+ {
+ trans_rollback_stmt(thd) || trans_rollback(thd);
+ my_message(ER_ERROR_DURING_COMMIT, "wsrep_max_ws_rows exceeded", MYF(0));
+ DBUG_RETURN(ER_ERROR_DURING_COMMIT);
+ }
+ else if (wsrep_after_row(thd, false))
+ {
+ DBUG_RETURN(ER_LOCK_DEADLOCK);
+ }
+ DBUG_RETURN(0);
+}
+#endif /* WITH_WSREP */
+
+static int check_duplicate_long_entry_key(TABLE *table, handler *h,
+ const uchar *new_rec, uint key_no)
+{
+ Field *hash_field;
+ int result, error= 0;
+ KEY *key_info= table->key_info + key_no;
+ hash_field= key_info->key_part->field;
+ uchar ptr[HA_HASH_KEY_LENGTH_WITH_NULL];
+
+ DBUG_ASSERT((key_info->flags & HA_NULL_PART_KEY &&
+ key_info->key_length == HA_HASH_KEY_LENGTH_WITH_NULL)
+ || key_info->key_length == HA_HASH_KEY_LENGTH_WITHOUT_NULL);
+
+ if (hash_field->is_real_null())
+ return 0;
+
+ key_copy(ptr, new_rec, key_info, key_info->key_length, false);
+
+ if (!table->check_unique_buf)
+ table->check_unique_buf= (uchar *)alloc_root(&table->mem_root,
+ table->s->reclength);
+
+ result= h->ha_index_init(key_no, 0);
+ if (result)
+ return result;
+ store_record(table, check_unique_buf);
+ result= h->ha_index_read_map(table->record[0],
+ ptr, HA_WHOLE_KEY, HA_READ_KEY_EXACT);
+ if (!result)
+ {
+ bool is_same;
+ Field * t_field;
+ Item_func_hash * temp= (Item_func_hash *)hash_field->vcol_info->expr;
+ Item ** arguments= temp->arguments();
+ uint arg_count= temp->argument_count();
+ do
+ {
+ my_ptrdiff_t diff= table->check_unique_buf - new_rec;
+ is_same= true;
+ for (uint j=0; is_same && j < arg_count; j++)
+ {
+ DBUG_ASSERT(arguments[j]->type() == Item::FIELD_ITEM ||
+ // this one for left(fld_name,length)
+ arguments[j]->type() == Item::FUNC_ITEM);
+ if (arguments[j]->type() == Item::FIELD_ITEM)
+ {
+ t_field= static_cast<Item_field *>(arguments[j])->field;
+ if (t_field->cmp_offset(diff))
+ is_same= false;
+ }
+ else
+ {
+ Item_func_left *fnc= static_cast<Item_func_left *>(arguments[j]);
+ DBUG_ASSERT(!my_strcasecmp(system_charset_info, "left", fnc->func_name()));
+ DBUG_ASSERT(fnc->arguments()[0]->type() == Item::FIELD_ITEM);
+ t_field= static_cast<Item_field *>(fnc->arguments()[0])->field;
+ uint length= (uint)fnc->arguments()[1]->val_int();
+ if (t_field->cmp_prefix(t_field->ptr, t_field->ptr + diff, length))
+ is_same= false;
+ }
+ }
+ }
+ while (!is_same && !(result= h->ha_index_next_same(table->record[0],
+ ptr, key_info->key_length)));
+ if (is_same)
+ error= HA_ERR_FOUND_DUPP_KEY;
+ goto exit;
+ }
+ if (result != HA_ERR_KEY_NOT_FOUND)
+ error= result;
+exit:
+ if (error == HA_ERR_FOUND_DUPP_KEY)
+ {
+ table->file->errkey= key_no;
+ if (h->ha_table_flags() & HA_DUPLICATE_POS)
+ {
+ h->position(table->record[0]);
+ memcpy(table->file->dup_ref, h->ref, h->ref_length);
+ }
+ }
+ restore_record(table, check_unique_buf);
+ h->ha_index_end();
+ return error;
+}
+
+/** @brief
+ check whether inserted records breaks the
+ unique constraint on long columns.
+ @returns 0 if no duplicate else returns error
+ */
+static int check_duplicate_long_entries(TABLE *table, handler *h,
+ const uchar *new_rec)
+{
+ table->file->errkey= -1;
+ int result;
+ for (uint i= 0; i < table->s->keys; i++)
+ {
+ if (table->key_info[i].algorithm == HA_KEY_ALG_LONG_HASH &&
+ (result= check_duplicate_long_entry_key(table, h, new_rec, i)))
+ return result;
+ }
+ return 0;
+}
+
+/** @brief
+ check whether updated records breaks the
+ unique constraint on long columns.
+ In the case of update we just need to check the specic key
+ reason for that is consider case
+ create table t1(a blob , b blob , x blob , y blob ,unique(a,b)
+ ,unique(x,y))
+ and update statement like this
+ update t1 set a=23+a; in this case if we try to scan for
+ whole keys in table then index scan on x_y will return 0
+ because data is same so in the case of update we take
+ key as a parameter in normal insert key should be -1
+ @returns 0 if no duplicate else returns error
+ */
+static int check_duplicate_long_entries_update(TABLE *table, handler *h, uchar *new_rec)
+{
+ Field *field;
+ uint key_parts;
+ int error= 0;
+ KEY *keyinfo;
+ KEY_PART_INFO *keypart;
+ /*
+ Here we are comparing whether new record and old record are same
+ with respect to fields in hash_str
+ */
+ uint reclength= (uint) (table->record[1] - table->record[0]);
+ table->clone_handler_for_update();
+ for (uint i= 0; i < table->s->keys; i++)
+ {
+ keyinfo= table->key_info + i;
+ if (keyinfo->algorithm == HA_KEY_ALG_LONG_HASH)
+ {
+ key_parts= fields_in_hash_keyinfo(keyinfo);
+ keypart= keyinfo->key_part - key_parts;
+ for (uint j= 0; j < key_parts; j++, keypart++)
+ {
+ field= keypart->field;
+ /* Compare fields if they are different then check for duplicates*/
+ if(field->cmp_binary_offset(reclength))
+ {
+ if((error= check_duplicate_long_entry_key(table, table->update_handler,
+ new_rec, i)))
+ goto exit;
+ /*
+ break because check_duplicate_long_entries_key will
+ take care of remaining fields
+ */
+ break;
+ }
+ }
+ }
+ }
+ exit:
+ return error;
+}
-int handler::ha_write_row(uchar *buf)
+int handler::ha_write_row(const uchar *buf)
{
int error;
Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;
@@ -6475,6 +6746,14 @@ int handler::ha_write_row(uchar *buf)
mark_trx_read_write();
increment_statistics(&SSV::ha_write_count);
+ if (table->s->long_unique_table)
+ {
+ if (this->inited == RND)
+ table->clone_handler_for_update();
+ handler *h= table->update_handler ? table->update_handler : table->file;
+ if ((error= check_duplicate_long_entries(table, h, buf)))
+ DBUG_RETURN(error);
+ }
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
{ error= write_row(buf); })
@@ -6483,7 +6762,15 @@ int handler::ha_write_row(uchar *buf)
{
rows_changed++;
error= binlog_log_row(table, 0, buf, log_func);
+#ifdef WITH_WSREP
+ if (table_share->tmp_table == NO_TMP_TABLE &&
+ WSREP(ha_thd()) && (error= wsrep_after_row(ha_thd())))
+ {
+ DBUG_RETURN(error);
+ }
+#endif /* WITH_WSREP */
}
+
DEBUG_SYNC_C("ha_write_row_end");
DBUG_RETURN(error);
}
@@ -6506,6 +6793,11 @@ int handler::ha_update_row(const uchar *old_data, const uchar *new_data)
MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
increment_statistics(&SSV::ha_update_count);
+ if (table->s->long_unique_table &&
+ (error= check_duplicate_long_entries_update(table, table->file, (uchar *)new_data)))
+ {
+ return error;
+ }
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
{ error= update_row(old_data, new_data);})
@@ -6515,6 +6807,13 @@ int handler::ha_update_row(const uchar *old_data, const uchar *new_data)
{
rows_changed++;
error= binlog_log_row(table, old_data, new_data, log_func);
+#ifdef WITH_WSREP
+ if (table_share->tmp_table == NO_TMP_TABLE &&
+ WSREP(ha_thd()) && (error= wsrep_after_row(ha_thd())))
+ {
+ return error;
+ }
+#endif /* WITH_WSREP */
}
return error;
}
@@ -6523,7 +6822,7 @@ int handler::ha_update_row(const uchar *old_data, const uchar *new_data)
Update first row. Only used by sequence tables
*/
-int handler::update_first_row(uchar *new_data)
+int handler::update_first_row(const uchar *new_data)
{
int error;
if (likely(!(error= ha_rnd_init(1))))
@@ -6570,6 +6869,13 @@ int handler::ha_delete_row(const uchar *buf)
{
rows_changed++;
error= binlog_log_row(table, buf, 0, log_func);
+#ifdef WITH_WSREP
+ if (table_share->tmp_table == NO_TMP_TABLE &&
+ WSREP(ha_thd()) && (error= wsrep_after_row(ha_thd())))
+ {
+ return error;
+ }
+#endif /* WITH_WSREP */
}
return error;
}
@@ -6589,14 +6895,14 @@ int handler::ha_delete_row(const uchar *buf)
@retval != 0 Failure.
*/
-int handler::ha_direct_update_rows(ha_rows *update_rows)
+int handler::ha_direct_update_rows(ha_rows *update_rows, ha_rows *found_rows)
{
int error;
MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
- error = direct_update_rows(update_rows);
+ error = direct_update_rows(update_rows, found_rows);
MYSQL_UPDATE_ROW_DONE(error);
return error;
}
@@ -6729,6 +7035,20 @@ void handler::set_lock_type(enum thr_lock_type lock)
table->reginfo.lock_type= lock;
}
+Compare_keys handler::compare_key_parts(const Field &old_field,
+ const Column_definition &new_field,
+ const KEY_PART_INFO &old_part,
+ const KEY_PART_INFO &new_part) const
+{
+ if (!old_field.is_equal(new_field))
+ return Compare_keys::NotEqual;
+
+ if (old_part.length != new_part.length)
+ return Compare_keys::NotEqual;
+
+ return Compare_keys::Equal;
+}
+
#ifdef WITH_WSREP
/**
@details
@@ -6759,7 +7079,7 @@ int ha_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal)
DBUG_ENTER("ha_abort_transaction");
if (!WSREP(bf_thd) &&
!(bf_thd->variables.wsrep_OSU_method == WSREP_OSU_RSU &&
- bf_thd->wsrep_exec_mode == TOTAL_ORDER)) {
+ wsrep_thd_is_toi(bf_thd))) {
DBUG_RETURN(0);
}
@@ -6775,54 +7095,6 @@ int ha_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal)
DBUG_RETURN(0);
}
-
-void ha_fake_trx_id(THD *thd)
-{
- DBUG_ENTER("ha_fake_trx_id");
-
- bool no_fake_trx_id= true;
-
- if (!WSREP(thd))
- {
- DBUG_VOID_RETURN;
- }
-
- if (thd->wsrep_ws_handle.trx_id != WSREP_UNDEFINED_TRX_ID)
- {
- WSREP_DEBUG("fake trx id skipped: %" PRIu64, thd->wsrep_ws_handle.trx_id);
- DBUG_VOID_RETURN;
- }
-
- /* Try statement transaction if standard one is not set. */
- THD_TRANS *trans= (thd->transaction.all.ha_list) ? &thd->transaction.all :
- &thd->transaction.stmt;
-
- Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
-
- for (; ha_info; ha_info= ha_info_next)
- {
- handlerton *hton= ha_info->ht();
- if (hton->fake_trx_id)
- {
- hton->fake_trx_id(hton, thd);
-
- /* Got a fake trx id. */
- no_fake_trx_id= false;
-
- /*
- We need transaction ID from just one storage engine providing
- fake_trx_id (which will most likely be the case).
- */
- break;
- }
- ha_info_next= ha_info->next();
- }
-
- if (unlikely(no_fake_trx_id))
- WSREP_WARN("Cannot get fake transaction ID from storage engine.");
-
- DBUG_VOID_RETURN;
-}
#endif /* WITH_WSREP */
@@ -7110,6 +7382,10 @@ int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info)
DBUG_RETURN(res);
}
+/*****************************************************************************
+ VERSIONING functions
+******************************************************************************/
+
bool Vers_parse_info::is_start(const char *name) const
{
DBUG_ASSERT(name);
@@ -7183,8 +7459,8 @@ bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info)
alter_info->flags|= ALTER_PARSER_ADD_COLUMN;
- system_time= start_end_t(default_start, default_end);
- as_row= system_time;
+ period= start_end_t(default_start, default_end);
+ as_row= period;
if (vers_create_sys_field(thd, default_start, alter_info, VERS_SYS_START_FLAG) ||
vers_create_sys_field(thd, default_end, alter_info, VERS_SYS_END_FLAG))
@@ -7303,7 +7579,7 @@ bool Vers_parse_info::fix_alter_info(THD *thd, Alter_info *alter_info,
if (DBUG_EVALUATE_IF("sysvers_force", 0, share->tmp_table))
{
- my_error(ER_VERS_TEMPORARY, MYF(0));
+ my_error(ER_VERS_NOT_SUPPORTED, MYF(0), "CREATE TEMPORARY TABLE");
return true;
}
@@ -7378,7 +7654,7 @@ bool Vers_parse_info::fix_alter_info(THD *thd, Alter_info *alter_info,
DBUG_ASSERT(end.str);
as_row= start_end_t(start, end);
- system_time= as_row;
+ period= as_row;
if (alter_info->create_list.elements)
{
@@ -7473,7 +7749,7 @@ Vers_parse_info::fix_create_like(Alter_info &alter_info, HA_CREATE_INFO &create_
}
as_row= start_end_t(f_start->field_name, f_end->field_name);
- system_time= as_row;
+ period= as_row;
create_info.options|= HA_VERSIONED_TABLE;
return false;
@@ -7498,14 +7774,14 @@ bool Vers_parse_info::check_conditions(const Lex_table_name &table_name,
return true;
}
- if (!system_time.start || !system_time.end)
+ if (!period.start || !period.end)
{
my_error(ER_MISSING, MYF(0), table_name.str, "PERIOD FOR SYSTEM_TIME");
return true;
}
- if (!as_row.start.streq(system_time.start) ||
- !as_row.end.streq(system_time.end))
+ if (!as_row.start.streq(period.start) ||
+ !as_row.end.streq(period.end))
{
my_error(ER_VERS_PERIOD_COLUMNS, MYF(0), as_row.start.str, as_row.end.str);
return true;
@@ -7599,3 +7875,109 @@ bool Vers_parse_info::check_sys_fields(const Lex_table_name &table_name,
return false;
}
+
+bool Table_period_info::check_field(const Create_field* f,
+ const Lex_ident& f_name) const
+{
+ bool res= false;
+ if (!f)
+ {
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), f_name.str, name.str);
+ res= true;
+ }
+ else if (f->type_handler()->mysql_timestamp_type() != MYSQL_TIMESTAMP_DATE &&
+ f->type_handler()->mysql_timestamp_type() != MYSQL_TIMESTAMP_DATETIME)
+ {
+ my_error(ER_WRONG_FIELD_SPEC, MYF(0), f->field_name.str);
+ res= true;
+ }
+ else if (f->vcol_info || f->flags & VERS_SYSTEM_FIELD)
+ {
+ my_error(ER_PERIOD_FIELD_WRONG_ATTRIBUTES, MYF(0),
+ f->field_name.str, "GENERATED ALWAYS AS");
+ res= true;
+ }
+
+ return res;
+}
+
+bool Table_scope_and_contents_source_st::check_fields(
+ THD *thd, Alter_info *alter_info,
+ const Lex_table_name &table_name, const Lex_table_name &db, int select_count)
+{
+ return vers_check_system_fields(thd, alter_info,
+ table_name, db, select_count) ||
+ check_period_fields(thd, alter_info);
+}
+
+bool Table_scope_and_contents_source_st::check_period_fields(
+ THD *thd, Alter_info *alter_info)
+{
+ if (!period_info.name)
+ return false;
+
+ if (tmp_table())
+ {
+ my_error(ER_PERIOD_TEMPORARY_NOT_ALLOWED, MYF(0));
+ return true;
+ }
+
+ Table_period_info::start_end_t &period= period_info.period;
+ const Create_field *row_start= NULL;
+ const Create_field *row_end= NULL;
+ List_iterator<Create_field> it(alter_info->create_list);
+ while (const Create_field *f= it++)
+ {
+ if (period.start.streq(f->field_name)) row_start= f;
+ else if (period.end.streq(f->field_name)) row_end= f;
+
+ if (period_info.name.streq(f->field_name))
+ {
+ my_error(ER_DUP_FIELDNAME, MYF(0), f->field_name.str);
+ return true;
+ }
+ }
+
+ bool res= period_info.check_field(row_start, period.start.str)
+ || period_info.check_field(row_end, period.end.str);
+ if (res)
+ return true;
+
+ if (row_start->type_handler() != row_end->type_handler()
+ || row_start->length != row_end->length)
+ {
+ my_error(ER_PERIOD_TYPES_MISMATCH, MYF(0), period_info.name.str);
+ res= true;
+ }
+
+ return res;
+}
+
+bool
+Table_scope_and_contents_source_st::fix_create_fields(THD *thd,
+ Alter_info *alter_info,
+ const TABLE_LIST &create_table)
+{
+ return vers_fix_system_fields(thd, alter_info, create_table)
+ || fix_period_fields(thd, alter_info);
+}
+
+bool
+Table_scope_and_contents_source_st::fix_period_fields(THD *thd,
+ Alter_info *alter_info)
+{
+ if (!period_info.name)
+ return false;
+
+ Table_period_info::start_end_t &period= period_info.period;
+ List_iterator<Create_field> it(alter_info->create_list);
+ while (Create_field *f= it++)
+ {
+ if (period.start.streq(f->field_name) || period.end.streq(f->field_name))
+ {
+ f->period= &period_info;
+ f->flags|= NOT_NULL_FLAG;
+ }
+ }
+ return false;
+}
diff --git a/sql/handler.h b/sql/handler.h
index 55c2f0bcd57..891187db171 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -43,10 +43,17 @@
#include <keycache.h>
#include <mysql/psi/mysql_table.h>
#include "sql_sequence.h"
+#include "mem_root_array.h"
class Alter_info;
class Virtual_column_info;
class sequence_definition;
+class Rowid_filter;
+class Field_string;
+class Field_varstring;
+class Field_blob;
+class Field_geom;
+class Column_definition;
// the following is for checking tables
@@ -115,7 +122,13 @@ enum enum_alter_inplace_result {
#define HA_NO_BLOBS (1ULL << 9) /* Doesn't support blobs */
#define HA_CAN_INDEX_BLOBS (1ULL << 10)
#define HA_AUTO_PART_KEY (1ULL << 11) /* auto-increment in multi-part key */
-#define HA_REQUIRE_PRIMARY_KEY (1ULL << 12) /* .. and can't create a hidden one */
+/*
+ The engine requires every table to have a user-specified PRIMARY KEY.
+ Do not set the flag if the engine can generate a hidden primary key internally.
+ This flag is ignored if a SEQUENCE is created (which, in turn, needs
+ HA_CAN_TABLES_WITHOUT_ROLLBACK flag)
+*/
+#define HA_REQUIRE_PRIMARY_KEY (1ULL << 12)
#define HA_STATS_RECORDS_IS_EXACT (1ULL << 13) /* stats.records is exact */
/*
INSERT_DELAYED only works with handlers that uses MySQL internal table
@@ -300,10 +313,23 @@ enum enum_alter_inplace_result {
#define HA_CAN_MULTISTEP_MERGE (1LL << 53)
/* calling cmp_ref() on the engine is expensive */
-#define HA_CMP_REF_IS_EXPENSIVE (1ULL << 54)
+#define HA_SLOW_CMP_REF (1ULL << 54)
+#define HA_CMP_REF_IS_EXPENSIVE HA_SLOW_CMP_REF
+
+/**
+ Some engines are unable to provide an efficient implementation for rnd_pos().
+ Server will try to avoid it, if possible
+
+ TODO better to do it with cost estimates, not with an explicit flag
+*/
+#define HA_SLOW_RND_POS (1ULL << 55)
+
+/* Safe for online backup */
+#define HA_CAN_ONLINE_BACKUPS (1ULL << 56)
-/* Engine wants primary keys for everything except sequences */
-#define HA_WANTS_PRIMARY_KEY (1ULL << 55)
+/* Support native hash index */
+#define HA_CAN_HASH_KEYS (1ULL << 58)
+#define HA_LAST_TABLE_FLAG HA_CAN_HASH_KEYS
/* bits in index_flags(index_number) for what you can do with index */
#define HA_READ_NEXT 1 /* TODO really use this flag */
@@ -325,6 +351,8 @@ enum enum_alter_inplace_result {
*/
#define HA_CLUSTERED_INDEX 512
+#define HA_DO_RANGE_FILTER_PUSHDOWN 1024
+
/*
bits in alter_table_flags:
*/
@@ -608,10 +636,15 @@ typedef ulonglong alter_table_operations;
#define ALTER_KEYS_ONOFF (1ULL << 9)
// Set for FORCE, ENGINE(same engine), by mysql_recreate_table()
#define ALTER_RECREATE (1ULL << 10)
+// Set for CONVERT TO
+#define ALTER_CONVERT_TO (1ULL << 11)
+// Set for DROP ... ADD some_index
+#define ALTER_RENAME_INDEX (1ULL << 12)
// Set for ADD FOREIGN KEY
#define ALTER_ADD_FOREIGN_KEY (1ULL << 21)
// Set for DROP FOREIGN KEY
#define ALTER_DROP_FOREIGN_KEY (1ULL << 22)
+#define ALTER_CHANGE_INDEX_COMMENT (1ULL << 23)
// Set for ADD [COLUMN] FIRST | AFTER
#define ALTER_COLUMN_ORDER (1ULL << 25)
#define ALTER_ADD_CHECK_CONSTRAINT (1ULL << 27)
@@ -676,13 +709,9 @@ typedef ulonglong alter_table_operations;
#define ALTER_VIRTUAL_COLUMN_TYPE (1ULL << 47)
#define ALTER_STORED_COLUMN_TYPE (1ULL << 48)
-/**
- Change column datatype in such way that new type has compatible
- packed representation with old type, so it is theoretically
- possible to perform change by only updating data dictionary
- without changing table rows.
-*/
-#define ALTER_COLUMN_EQUAL_PACK_LENGTH (1ULL << 49)
+
+// Engine can handle type change by itself in ALGORITHM=INPLACE
+#define ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE (1ULL << 49)
// Reorder column
#define ALTER_STORED_COLUMN_ORDER (1ULL << 50)
@@ -864,15 +893,6 @@ struct xid_t {
};
typedef struct xid_t XID;
-/*
- The size of XID string representation in the form
- 'gtrid', 'bqual', formatID
- see xid_t::get_sql_string() for details.
-*/
-#define SQL_XIDSIZE (XIDDATASIZE * 2 + 8 + MY_INT64_NUM_DECIMAL_DIGITS)
-/* The 'buf' has to have space for at least SQL_XIDSIZE bytes. */
-uint get_sql_xid(XID *xid, char *buf);
-
/* for recover() handlerton call */
#define MIN_XID_LIST_SIZE 128
#define MAX_XID_LIST_SIZE (1024*128)
@@ -910,6 +930,16 @@ enum tablespace_access_mode
TS_NOT_ACCESSIBLE = 2
};
+/* Statistics about batch operations like bulk_insert */
+struct ha_copy_info
+{
+ ha_rows records; /* Used to check if rest of variables can be used */
+ ha_rows touched;
+ ha_rows copied;
+ ha_rows deleted;
+ ha_rows updated;
+};
+
struct handlerton;
class st_alter_tablespace : public Sql_alloc
{
@@ -981,6 +1011,7 @@ enum enum_schema_tables
SCH_KEY_CACHES,
SCH_KEY_COLUMN_USAGE,
SCH_OPEN_TABLES,
+ SCH_OPT_TRACE,
SCH_PARAMETERS,
SCH_PARTITIONS,
SCH_PLUGINS,
@@ -1199,6 +1230,8 @@ struct handler_iterator {
class handler;
class group_by_handler;
+class derived_handler;
+class select_handler;
struct Query;
typedef class st_select_lex SELECT_LEX;
typedef struct st_order ORDER;
@@ -1478,7 +1511,6 @@ struct handlerton
THD *victim_thd, my_bool signal);
int (*set_checkpoint)(handlerton *hton, const XID* xid);
int (*get_checkpoint)(handlerton *hton, XID* xid);
- void (*fake_trx_id)(handlerton *hton, THD *thd);
/*
Optional clauses in the CREATE/ALTER TABLE
*/
@@ -1518,6 +1550,21 @@ struct handlerton
*/
group_by_handler *(*create_group_by)(THD *thd, Query *query);
+ /*
+ Create and return a derived_handler if the storage engine can execute
+ the derived table 'derived', otherwise return NULL.
+ In a general case 'derived' may contain tables not from the engine.
+ If the engine cannot handle or does not want to handle such pushed derived
+ the function create_group_by has to return NULL.
+ */
+ derived_handler *(*create_derived)(THD *thd, TABLE_LIST *derived);
+
+ /*
+ Create and return a select_handler if the storage engine can execute
+ the select statement 'select, otherwise return NULL
+ */
+ select_handler *(*create_select) (THD *thd, SELECT_LEX *select);
+
/*********************************************************************
Table discovery API.
It allows the server to "discover" tables that exist in the storage
@@ -1615,6 +1662,10 @@ struct handlerton
@return transaction commit ID
@retval 0 if no system-versioned data was affected by the transaction */
ulonglong (*prepare_commit_versioned)(THD *thd, ulonglong *trx_id);
+
+ /* backup */
+ void (*prepare_for_backup)(void);
+ void (*end_backup)(void);
};
@@ -1671,6 +1722,9 @@ handlerton *ha_default_tmp_handlerton(THD *thd);
// Engine needs to access the main connect string in partitions
#define HTON_CAN_READ_CONNECT_STRING_IN_PARTITION (1 <<12)
+/* can be replicated by wsrep replication provider plugin */
+#define HTON_WSREP_REPLICATION (1 << 13)
+
/*
Table requires and close and reopen after truncate
If the handler has HTON_CAN_RECREATE, this flag is not used
@@ -1885,6 +1939,7 @@ typedef struct {
time_t check_time;
time_t update_time;
ulonglong check_sum;
+ bool check_sum_null;
} PARTITION_STATS;
#define UNDEF_NODEGROUP 65535
@@ -1928,54 +1983,66 @@ enum vers_sys_type_t
VERS_TRX_ID
};
-extern const LEX_CSTRING null_clex_str;
-
-struct Vers_parse_info
+struct Table_period_info: Sql_alloc
{
- Vers_parse_info() :
- versioned_fields(false),
- unversioned_fields(false)
- {}
+ Table_period_info() :
+ create_if_not_exists(false),
+ constr(NULL) {}
+ Table_period_info(const char *name_arg, size_t size) :
+ name(name_arg, size),
+ create_if_not_exists(false),
+ constr(NULL) {}
- void init() // Deep initialization
- {
- system_time= start_end_t(null_clex_str, null_clex_str);
- as_row= start_end_t(null_clex_str, null_clex_str);
- versioned_fields= false;
- unversioned_fields= false;
- }
+ Lex_ident name;
struct start_end_t
{
- start_end_t()
- {}
- start_end_t(LEX_CSTRING _start, LEX_CSTRING _end) :
+ start_end_t() {};
+ start_end_t(const LEX_CSTRING& _start, const LEX_CSTRING& _end) :
start(_start),
end(_end) {}
Lex_ident start;
Lex_ident end;
};
+ start_end_t period;
+ bool create_if_not_exists;
+ Virtual_column_info *constr;
- start_end_t system_time;
- start_end_t as_row;
+ bool is_set() const
+ {
+ DBUG_ASSERT(bool(period.start) == bool(period.end));
+ return period.start;
+ }
- void set_system_time(Lex_ident start, Lex_ident end)
+ void set_period(const Lex_ident& start, const Lex_ident& end)
{
- system_time.start= start;
- system_time.end= end;
+ period.start= start;
+ period.end= end;
}
+ bool check_field(const Create_field* f, const Lex_ident& f_name) const;
+};
+
+struct Vers_parse_info: public Table_period_info
+{
+ Vers_parse_info() :
+ Table_period_info(STRING_WITH_LEN("SYSTEM_TIME")),
+ versioned_fields(false),
+ unversioned_fields(false)
+ {}
+
+ Table_period_info::start_end_t as_row;
protected:
friend struct Table_scope_and_contents_source_st;
void set_start(const LEX_CSTRING field_name)
{
as_row.start= field_name;
- system_time.start= field_name;
+ period.start= field_name;
}
void set_end(const LEX_CSTRING field_name)
{
as_row.end= field_name;
- system_time.end= field_name;
+ period.end= field_name;
}
bool is_start(const char *name) const;
bool is_end(const char *name) const;
@@ -1984,7 +2051,7 @@ protected:
bool fix_implicit(THD *thd, Alter_info *alter_info);
operator bool() const
{
- return as_row.start || as_row.end || system_time.start || system_time.end;
+ return as_row.start || as_row.end || period.start || period.end;
}
bool need_check(const Alter_info *alter_info) const;
bool check_conditions(const Lex_table_name &table_name,
@@ -2025,7 +2092,7 @@ public:
struct Table_scope_and_contents_source_pod_st // For trivial members
{
- CHARSET_INFO *table_charset;
+ CHARSET_INFO *alter_table_convert_to_charset;
LEX_CUSTRING tabledef_version;
LEX_CSTRING connect_string;
LEX_CSTRING comment;
@@ -2105,15 +2172,26 @@ struct Table_scope_and_contents_source_st:
public Table_scope_and_contents_source_pod_st
{
Vers_parse_info vers_info;
+ Table_period_info period_info;
void init()
{
Table_scope_and_contents_source_pod_st::init();
- vers_info.init();
+ vers_info= {};
+ period_info= {};
}
- bool vers_fix_system_fields(THD *thd, Alter_info *alter_info,
+ bool fix_create_fields(THD *thd, Alter_info *alter_info,
const TABLE_LIST &create_table);
+ bool fix_period_fields(THD *thd, Alter_info *alter_info);
+ bool check_fields(THD *thd, Alter_info *alter_info,
+ const Lex_table_name &table_name,
+ const Lex_table_name &db,
+ int select_count= 0);
+ bool check_period_fields(THD *thd, Alter_info *alter_info);
+
+ bool vers_fix_system_fields(THD *thd, Alter_info *alter_info,
+ const TABLE_LIST &create_table);
bool vers_check_system_fields(THD *thd, Alter_info *alter_info,
const Lex_table_name &table_name,
@@ -2159,7 +2237,7 @@ struct HA_CREATE_INFO: public Table_scope_and_contents_source_st,
DBUG_ASSERT(cs);
if (check_conflicting_charset_declarations(cs))
return true;
- table_charset= default_table_charset= cs;
+ alter_table_convert_to_charset= default_table_charset= cs;
used_fields|= (HA_CREATE_USED_CHARSET | HA_CREATE_USED_DEFAULT_CHARSET);
return false;
}
@@ -2305,6 +2383,29 @@ public:
uint *index_add_buffer;
/**
+ Old and new index names. Used for index rename.
+ */
+ struct Rename_key_pair
+ {
+ Rename_key_pair(const KEY *old_key, const KEY *new_key)
+ : old_key(old_key), new_key(new_key)
+ {
+ }
+ const KEY *old_key;
+ const KEY *new_key;
+ };
+ /**
+ Vector of key pairs from DROP/ADD index which can be renamed.
+ */
+ typedef Mem_root_array<Rename_key_pair, true> Rename_keys_vector;
+
+ /**
+ A list of indexes which should be renamed.
+ Index definitions stays the same.
+ */
+ Rename_keys_vector rename_keys;
+
+ /**
Context information to allow handlers to keep context between in-place
alter API calls.
@@ -2371,24 +2472,7 @@ public:
Alter_info *alter_info_arg,
KEY *key_info_arg, uint key_count_arg,
partition_info *modified_part_info_arg,
- bool ignore_arg, bool error_non_empty)
- : create_info(create_info_arg),
- alter_info(alter_info_arg),
- key_info_buffer(key_info_arg),
- key_count(key_count_arg),
- index_drop_count(0),
- index_drop_buffer(NULL),
- index_add_count(0),
- index_add_buffer(NULL),
- handler_ctx(NULL),
- group_commit_ctx(NULL),
- handler_flags(0),
- modified_part_info(modified_part_info_arg),
- ignore(ignore_arg),
- online(false),
- unsupported_reason(NULL),
- error_if_not_empty(error_non_empty)
- {}
+ bool ignore_arg, bool error_non_empty);
~Alter_inplace_info()
{
@@ -2583,11 +2667,14 @@ typedef bool (*SKIP_INDEX_TUPLE_FUNC) (range_seq_t seq, range_id_t range_info);
class Cost_estimate
{
public:
- double io_count; /* number of I/O */
- double avg_io_cost; /* cost of an average I/O oper. */
- double cpu_cost; /* cost of operations in CPU */
- double import_cost; /* cost of remote operations */
- double mem_cost; /* cost of used memory */
+ double io_count; /* number of I/O to fetch records */
+ double avg_io_cost; /* cost of an average I/O oper. to fetch records */
+ double idx_io_count; /* number of I/O to read keys */
+ double idx_avg_io_cost; /* cost of an average I/O oper. to fetch records */
+ double cpu_cost; /* total cost of operations in CPU */
+ double idx_cpu_cost; /* cost of operations in CPU for index */
+ double import_cost; /* cost of remote operations */
+ double mem_cost; /* cost of used memory */
enum { IO_COEFF=1 };
enum { CPU_COEFF=1 };
@@ -2601,10 +2688,18 @@ public:
double total_cost()
{
- return IO_COEFF*io_count*avg_io_cost + CPU_COEFF * cpu_cost +
+ return IO_COEFF*io_count*avg_io_cost +
+ IO_COEFF*idx_io_count*idx_avg_io_cost +
+ CPU_COEFF*cpu_cost +
MEM_COEFF*mem_cost + IMPORT_COEFF*import_cost;
}
+ double index_only_cost()
+ {
+ return IO_COEFF*idx_io_count*idx_avg_io_cost +
+ CPU_COEFF*idx_cpu_cost;
+ }
+
/**
Whether or not all costs in the object are zero
@@ -2612,30 +2707,48 @@ public:
*/
bool is_zero() const
{
- return io_count == 0.0 && cpu_cost == 0.0 &&
+ return io_count == 0.0 && idx_io_count && cpu_cost == 0.0 &&
import_cost == 0.0 && mem_cost == 0.0;
}
void reset()
{
avg_io_cost= 1.0;
- io_count= cpu_cost= mem_cost= import_cost= 0.0;
+ idx_avg_io_cost= 1.0;
+ io_count= idx_io_count= cpu_cost= idx_cpu_cost= mem_cost= import_cost= 0.0;
}
void multiply(double m)
{
io_count *= m;
cpu_cost *= m;
+ idx_io_count *= m;
+ idx_cpu_cost *= m;
import_cost *= m;
/* Don't multiply mem_cost */
}
void add(const Cost_estimate* cost)
{
- double io_count_sum= io_count + cost->io_count;
- add_io(cost->io_count, cost->avg_io_cost);
- io_count= io_count_sum;
+ if (cost->io_count)
+ {
+ double io_count_sum= io_count + cost->io_count;
+ avg_io_cost= (io_count * avg_io_cost +
+ cost->io_count * cost->avg_io_cost)
+ /io_count_sum;
+ io_count= io_count_sum;
+ }
+ if (cost->idx_io_count)
+ {
+ double idx_io_count_sum= idx_io_count + cost->idx_io_count;
+ idx_avg_io_cost= (idx_io_count * idx_avg_io_cost +
+ cost->idx_io_count * cost->idx_avg_io_cost)
+ /idx_io_count_sum;
+ idx_io_count= idx_io_count_sum;
+ }
cpu_cost += cost->cpu_cost;
+ idx_cpu_cost += cost->idx_cpu_cost;
+ import_cost += cost->import_cost;
}
void add_io(double add_io_cnt, double add_avg_cost)
@@ -2796,6 +2909,7 @@ public:
time_t update_time;
uint block_size; /* index block size */
ha_checksum checksum;
+ bool checksum_null;
/*
number of buffer bytes that native mrr implementation needs,
@@ -2806,12 +2920,15 @@ public:
data_file_length(0), max_data_file_length(0),
index_file_length(0), max_index_file_length(0), delete_length(0),
auto_increment_value(0), records(0), deleted(0), mean_rec_length(0),
- create_time(0), check_time(0), update_time(0), block_size(0),
- mrr_length_per_rec(0)
+ create_time(0), check_time(0), update_time(0), block_size(8192),
+ checksum(0), checksum_null(FALSE), mrr_length_per_rec(0)
{}
};
-extern "C" enum icp_result handler_index_cond_check(void* h_arg);
+extern "C" check_result_t handler_index_cond_check(void* h_arg);
+
+extern "C" check_result_t handler_rowid_filter_check(void* h_arg);
+extern "C" int handler_rowid_filter_is_active(void* h_arg);
uint calculate_key_len(TABLE *, uint, const uchar *, key_part_map);
/*
@@ -2834,6 +2951,13 @@ public:
virtual ~Handler_share() {}
};
+enum class Compare_keys : uint32_t
+{
+ Equal,
+ EqualButKeyPartLength,
+ EqualButComment,
+ NotEqual
+};
/**
The handler class is the interface for dynamically loadable
@@ -2969,16 +3093,23 @@ public:
ulonglong rows_changed;
/* One bigger than needed to avoid to test if key == MAX_KEY */
ulonglong index_rows_read[MAX_KEY+1];
+ ha_copy_info copy_info;
private:
/* ANALYZE time tracker, if present */
Exec_time_tracker *tracker;
public:
void set_time_tracker(Exec_time_tracker *tracker_arg) { tracker=tracker_arg;}
+ Exec_time_tracker *get_time_tracker() { return tracker; }
Item *pushed_idx_cond;
uint pushed_idx_cond_keyno; /* The index which the above condition is for */
+ /* Rowid filter pushed into the engine */
+ Rowid_filter *pushed_rowid_filter;
+ /* true when the pushed rowid filter has been already filled */
+ bool rowid_filter_is_active;
+
Discrete_interval auto_inc_interval_for_cur_row;
/**
Number of reserved auto-increment intervals. Serves as a heuristic
@@ -3034,7 +3165,7 @@ public:
check_table_binlog_row_based_done(0),
check_table_binlog_row_based_result(0),
row_already_logged(0),
- in_range_check_pushed_down(FALSE),
+ in_range_check_pushed_down(FALSE), errkey(-1),
key_used_on_scan(MAX_KEY),
active_index(MAX_KEY), keyread(MAX_KEY),
ref_length(sizeof(my_off_t)),
@@ -3043,6 +3174,8 @@ public:
tracker(NULL),
pushed_idx_cond(NULL),
pushed_idx_cond_keyno(MAX_KEY),
+ pushed_rowid_filter(NULL),
+ rowid_filter_is_active(0),
auto_inc_intervals_count(0),
m_psi(NULL), set_top_table_fields(FALSE), top_table(0),
top_table_field(0), top_table_fields(0),
@@ -3064,7 +3197,7 @@ public:
{
cached_table_flags= table_flags();
}
- /* ha_ methods: pubilc wrappers for private virtual API */
+ /* ha_ methods: public wrappers for private virtual API */
int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked,
MEM_ROOT *mem_root= 0, List<String> *partitions_to_open=NULL);
@@ -3131,7 +3264,11 @@ public:
/**
The cached_table_flags is set at ha_open and ha_external_lock
*/
- Table_flags ha_table_flags() const { return cached_table_flags; }
+ Table_flags ha_table_flags() const
+ {
+ DBUG_ASSERT(cached_table_flags < (HA_LAST_TABLE_FLAG << 1));
+ return cached_table_flags;
+ }
/**
These functions represent the public interface to *users* of the
handler class, hence they are *not* virtual. For the inheritance
@@ -3139,7 +3276,7 @@ public:
and delete_row() below.
*/
int ha_external_lock(THD *thd, int lock_type);
- int ha_write_row(uchar * buf);
+ int ha_write_row(const uchar * buf);
int ha_update_row(const uchar * old_data, const uchar * new_data);
int ha_delete_row(const uchar * buf);
void ha_release_auto_increment();
@@ -3168,6 +3305,7 @@ public:
{
DBUG_ENTER("handler::ha_start_bulk_insert");
estimation_rows_to_insert= rows;
+ bzero(&copy_info,sizeof(copy_info));
start_bulk_insert(rows, flags);
DBUG_VOID_RETURN;
}
@@ -3233,6 +3371,13 @@ public:
{
rows_read= rows_changed= rows_tmp_read= 0;
bzero(index_rows_read, sizeof(index_rows_read));
+ bzero(&copy_info, sizeof(copy_info));
+ }
+ virtual void reset_copy_info() {}
+ void ha_reset_copy_info()
+ {
+ bzero(&copy_info, sizeof(copy_info));
+ reset_copy_info();
}
virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
{
@@ -3243,6 +3388,11 @@ public:
virtual double scan_time()
{ return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
+ virtual double key_scan_time(uint index)
+ {
+ return keyread_time(index, 1, records());
+ }
+
/**
The cost of reading a set of ranges from the table using an index
to access it.
@@ -3270,7 +3420,7 @@ public:
/*
True if changes to the table is persistent (no rollback)
- This is manly used to decide how to log changes to the table in
+ This is mainly used to decide how to log changes to the table in
the binary log.
*/
bool has_transactions()
@@ -3814,6 +3964,7 @@ public:
virtual uint max_supported_key_part_length() const { return 255; }
virtual uint min_record_length(uint options) const { return 1; }
+ virtual int pre_calculate_checksum() { return 0; }
virtual int calculate_checksum();
virtual bool is_crashed() const { return 0; }
virtual bool auto_repair(int error) const { return 0; }
@@ -4065,6 +4216,14 @@ public:
in_range_check_pushed_down= false;
}
+ virtual void cancel_pushed_rowid_filter()
+ {
+ pushed_rowid_filter= NULL;
+ rowid_filter_is_active= false;
+ }
+
+ virtual bool rowid_filter_push(Rowid_filter *rowid_filter) { return true; }
+
/* Needed for partition / spider */
virtual TABLE_LIST *get_next_global_for_child() { return NULL; }
@@ -4429,7 +4588,7 @@ private:
*/
virtual int rnd_init(bool scan)= 0;
virtual int rnd_end() { return 0; }
- virtual int write_row(uchar *buf __attribute__((unused)))
+ virtual int write_row(const uchar *buf __attribute__((unused)))
{
return HA_ERR_WRONG_COMMAND;
}
@@ -4452,7 +4611,7 @@ private:
Optimized function for updating the first row. Only used by sequence
tables
*/
- virtual int update_first_row(uchar *new_data);
+ virtual int update_first_row(const uchar *new_data);
virtual int delete_row(const uchar *buf __attribute__((unused)))
{
@@ -4461,7 +4620,7 @@ private:
/* Perform initialization for a direct update request */
public:
- int ha_direct_update_rows(ha_rows *update_rows);
+ int ha_direct_update_rows(ha_rows *update_rows, ha_rows *found_rows);
virtual int direct_update_rows_init(List<Item> *update_fields)
{
return HA_ERR_WRONG_COMMAND;
@@ -4471,7 +4630,8 @@ private:
{
return HA_ERR_WRONG_COMMAND;
}
- virtual int direct_update_rows(ha_rows *update_rows __attribute__((unused)))
+ virtual int direct_update_rows(ha_rows *update_rows __attribute__((unused)),
+ ha_rows *found_rows __attribute__((unused)))
{
return HA_ERR_WRONG_COMMAND;
}
@@ -4669,7 +4829,8 @@ public:
virtual void set_lock_type(enum thr_lock_type lock);
- friend enum icp_result handler_index_cond_check(void* h_arg);
+ friend check_result_t handler_index_cond_check(void* h_arg);
+ friend check_result_t handler_rowid_filter_check(void *h_arg);
/**
Find unique record by index or unique constrain
@@ -4690,6 +4851,42 @@ public:
{ DBUG_ASSERT(ht); return partition_ht()->flags & HTON_NATIVE_SYS_VERSIONING; }
virtual void update_partition(uint part_id)
{}
+
+ virtual bool is_clustering_key(uint index) { return false; }
+
+ /**
+ Some engines can perform column type conversion with ALGORITHM=INPLACE.
+ These functions check for such possibility.
+ Implementation could be based on Field_xxx::is_equal()
+ */
+ virtual bool can_convert_string(const Field_string *field,
+ const Column_definition &new_type) const
+ {
+ return false;
+ }
+ virtual bool can_convert_varstring(const Field_varstring *field,
+ const Column_definition &new_type) const
+ {
+ return false;
+ }
+ virtual bool can_convert_blob(const Field_blob *field,
+ const Column_definition &new_type) const
+ {
+ return false;
+ }
+ virtual bool can_convert_geom(const Field_geom *field,
+ const Column_definition &new_type) const
+ {
+ return false;
+ }
+
+ /* Used for ALTER TABLE.
+ Some engines can handle some differences in indexes by themself. */
+ virtual Compare_keys compare_key_parts(const Field &old_field,
+ const Column_definition &new_field,
+ const KEY_PART_INFO &old_part,
+ const KEY_PART_INFO &new_part) const;
+
protected:
Handler_share *get_ha_share_ptr();
void set_ha_share_ptr(Handler_share *arg_ha_share);
@@ -4768,6 +4965,8 @@ int ha_create_table(THD *thd, const char *path,
HA_CREATE_INFO *create_info, LEX_CUSTRING *frm);
int ha_delete_table(THD *thd, handlerton *db_type, const char *path,
const LEX_CSTRING *db, const LEX_CSTRING *alias, bool generate_warning);
+void ha_prepare_for_backup();
+void ha_end_backup();
/* statistics and info */
bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat);
@@ -4835,9 +5034,6 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv);
int ha_release_savepoint(THD *thd, SAVEPOINT *sv);
#ifdef WITH_WSREP
int ha_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal);
-void ha_fake_trx_id(THD *thd);
-#else
-inline void ha_fake_trx_id(THD *thd) { }
#endif
/* these are called by storage engines */
@@ -4853,7 +5049,6 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht);
const char *get_canonical_filename(handler *file, const char *path,
char *tmp_path);
-bool mysql_xa_recover(THD *thd);
void commit_checkpoint_notify_ha(handlerton *hton, void *cookie);
inline const LEX_CSTRING *table_case_name(HA_CREATE_INFO *info, const LEX_CSTRING *name)
@@ -4895,4 +5090,10 @@ int del_global_table_stat(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *t
@note This does not need to be multi-byte safe or anything */
char *xid_to_str(char *buf, const XID &xid);
#endif // !DBUG_OFF
+
+#if defined(WITH_ARIA_STORAGE_ENGINE) && MYSQL_VERSION_ID < 100500
+extern void ha_maria_implicit_commit(THD *thd, bool new_trans);
+#else
+#define ha_maria_implicit_commit(A, B) while(0)
+#endif
#endif /* HANDLER_INCLUDED */
diff --git a/sql/init.cc b/sql/init.cc
index 84465151fb1..4735178707f 100644
--- a/sql/init.cc
+++ b/sql/init.cc
@@ -24,7 +24,7 @@
#include "mariadb.h"
#include "sql_priv.h"
#include "init.h"
-#include "mysqld.h" // abort_loop, ...
+#include "mysqld.h"
#include "my_time.h" // my_init_time
#include "unireg.h" // SPECIAL_SAME_DB_NAME
#include <m_ctype.h>
@@ -34,8 +34,6 @@ void unireg_init(ulong options)
DBUG_ENTER("unireg_init");
error_handler_hook = my_message_stderr;
- abort_loop=0;
-
my_disable_async_io=1; /* aioread is only in shared library */
wild_many='%'; wild_one='_'; wild_prefix='\\'; /* Change to sql syntax */
diff --git a/sql/init.h b/sql/init.h
index c7c158082b9..b9d5053fc4d 100644
--- a/sql/init.h
+++ b/sql/init.h
@@ -17,6 +17,5 @@
#define INIT_INCLUDED
void unireg_init(ulong options);
-ATTRIBUTE_NORETURN void unireg_end(void);
#endif /* INIT_INCLUDED */
diff --git a/sql/innodb_priv.h b/sql/innodb_priv.h
index aaae75689e3..bec63a839c8 100644
--- a/sql/innodb_priv.h
+++ b/sql/innodb_priv.h
@@ -19,15 +19,13 @@
/** @file Declaring server-internal functions that are used by InnoDB. */
#include <sql_priv.h>
+#include <strfunc.h> /* strconvert */
class THD;
int get_quote_char_for_identifier(THD *thd, const char *name, size_t length);
bool schema_table_store_record(THD *thd, TABLE *table);
void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
-uint strconvert(CHARSET_INFO *from_cs, const char *from, size_t from_length,
- CHARSET_INFO *to_cs, char *to, size_t to_length,
- uint *errors);
void sql_print_error(const char *format, ...);
diff --git a/sql/item.cc b/sql/item.cc
index b8a20580067..6c52ade0d9f 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -124,60 +124,31 @@ void Item::push_note_converted_to_positive_complement(THD *thd)
}
-longlong Item::val_datetime_packed_result()
+longlong Item::val_datetime_packed_result(THD *thd)
{
MYSQL_TIME ltime, tmp;
- if (get_date_result(&ltime, TIME_FUZZY_DATES | TIME_INVALID_DATES))
+ if (get_date_result(thd, &ltime, Datetime::Options_cmp(thd)))
return 0;
if (ltime.time_type != MYSQL_TIMESTAMP_TIME)
return pack_time(&ltime);
- if ((null_value= time_to_datetime_with_warn(current_thd, &ltime, &tmp, 0)))
+ if ((null_value= time_to_datetime_with_warn(thd, &ltime, &tmp,
+ TIME_CONV_NONE)))
return 0;
return pack_time(&tmp);
}
-/**
- Get date/time/datetime.
- If DATETIME or DATE result is returned, it's converted to TIME.
-*/
-bool Item::get_time_with_conversion(THD *thd, MYSQL_TIME *ltime,
- ulonglong fuzzydate)
+longlong Item::val_time_packed_result(THD *thd)
{
- if (get_date(ltime, fuzzydate))
- return true;
- if (ltime->time_type != MYSQL_TIMESTAMP_TIME)
- {
- MYSQL_TIME ltime2;
- if ((thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) &&
- (ltime->year || ltime->day || ltime->month))
- {
- /*
- Old mode conversion from DATETIME with non-zero YYYYMMDD part
- to TIME works very inconsistently. Possible variants:
- - truncate the YYYYMMDD part
- - add (MM*33+DD)*24 to hours
- - add (MM*31+DD)*24 to hours
- Let's return TRUE here, to disallow equal field propagation.
- Note, If we start to use this method in more pieces of the code other
- than equal field propagation, we should probably return
- TRUE only if some flag in fuzzydate is set.
- */
- return true;
- }
- if (datetime_to_time_with_warn(thd, ltime, &ltime2, TIME_SECOND_PART_DIGITS))
- {
- /*
- If the time difference between CURRENT_DATE and ltime
- did not fit into the supported TIME range, then we set the
- difference to the maximum possible value in the supported TIME range
- */
- DBUG_ASSERT(0);
- return (null_value= true);
- }
- *ltime= ltime2;
- }
- return false;
+ MYSQL_TIME ltime;
+ if (get_date_result(thd, &ltime, Time::Options_cmp(thd)))
+ return 0;
+ if (ltime.time_type == MYSQL_TIMESTAMP_TIME)
+ return pack_time(&ltime);
+ int warn= 0;
+ Time tmp(&warn, &ltime, 0);
+ DBUG_ASSERT(tmp.is_valid_time());
+ return tmp.to_packed();
}
@@ -262,36 +233,6 @@ String *Item::val_string_from_int(String *str)
}
-String *Item::val_string_from_decimal(String *str)
-{
- my_decimal dec_buf, *dec= val_decimal(&dec_buf);
- if (null_value)
- return 0;
- my_decimal_round(E_DEC_FATAL_ERROR, dec, decimals, FALSE, &dec_buf);
- my_decimal2string(E_DEC_FATAL_ERROR, &dec_buf, 0, 0, 0, str);
- return str;
-}
-
-
-/*
- All val_xxx_from_date() must call this method, to expose consistent behaviour
- regarding SQL_MODE when converting DATE/DATETIME to other data types.
-*/
-bool Item::get_temporal_with_sql_mode(MYSQL_TIME *ltime)
-{
- return get_date(ltime, field_type() == MYSQL_TYPE_TIME
- ? TIME_TIME_ONLY
- : sql_mode_for_dates(current_thd));
-}
-
-
-bool Item::is_null_from_temporal()
-{
- MYSQL_TIME ltime;
- return get_temporal_with_sql_mode(&ltime);
-}
-
-
longlong Item::val_int_from_str(int *error)
{
char buff[MAX_FIELD_WIDTH];
@@ -333,12 +274,37 @@ longlong Item::val_int_unsigned_typecast_from_str()
}
-longlong Item::val_int_unsigned_typecast_from_int()
+longlong Item::val_int_signed_typecast_from_real()
{
- longlong value= val_int();
- if (!null_value && unsigned_flag == 0 && value < 0)
- push_note_converted_to_positive_complement(current_thd);
- return value;
+ double nr= val_real();
+ if (null_value)
+ return 0;
+ Converter_double_to_longlong conv(nr, false);
+ if (conv.error())
+ {
+ THD *thd= current_thd;
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_DATA_OVERFLOW, ER_THD(thd, ER_DATA_OVERFLOW),
+ ErrConvDouble(nr).ptr(), "SIGNED BIGINT");
+ }
+ return conv.result();
+}
+
+
+longlong Item::val_int_unsigned_typecast_from_real()
+{
+ double nr= val_real();
+ if (null_value)
+ return 0;
+ Converter_double_to_longlong conv(nr, true);
+ if (conv.error())
+ {
+ THD *thd= current_thd;
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_DATA_OVERFLOW, ER_THD(thd, ER_DATA_OVERFLOW),
+ ErrConvDouble(nr).ptr(), "UNSIGNED BIGINT");
+ }
+ return conv.result();
}
@@ -351,18 +317,12 @@ longlong Item::val_int_signed_typecast_from_int()
}
-String *Item::val_string_from_date(String *str)
+longlong Item::val_int_unsigned_typecast_from_int()
{
- MYSQL_TIME ltime;
- if (get_temporal_with_sql_mode(&ltime) ||
- str->alloc(MAX_DATE_STRING_REP_LENGTH))
- {
- null_value= 1;
- return (String *) 0;
- }
- str->length(my_TIME_to_str(&ltime, const_cast<char*>(str->ptr()), decimals));
- str->set_charset(&my_charset_numeric);
- return str;
+ longlong value= val_int();
+ if (!null_value && unsigned_flag == 0 && value < 0)
+ push_note_converted_to_positive_complement(current_thd);
+ return value;
}
@@ -397,93 +357,10 @@ my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value)
}
-my_decimal *Item::val_decimal_from_date(my_decimal *decimal_value)
-{
- DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (get_temporal_with_sql_mode(&ltime))
- {
- my_decimal_set_zero(decimal_value);
- null_value= 1; // set NULL, stop processing
- return 0;
- }
- return date2my_decimal(&ltime, decimal_value);
-}
-
-
-my_decimal *Item::val_decimal_from_time(my_decimal *decimal_value)
-{
- DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (get_time(&ltime))
- {
- my_decimal_set_zero(decimal_value);
- return 0;
- }
- return date2my_decimal(&ltime, decimal_value);
-}
-
-
-longlong Item::val_int_from_date()
-{
- DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (get_temporal_with_sql_mode(&ltime))
- return 0;
- longlong v= TIME_to_ulonglong(&ltime);
- return ltime.neg ? -v : v;
-}
-
-
-double Item::val_real_from_date()
-{
- DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (get_temporal_with_sql_mode(&ltime))
- return 0;
- return TIME_to_double(&ltime);
-}
-
-
-double Item::val_real_from_decimal()
-{
- /* Note that fix_fields may not be called for Item_avg_field items */
- double result;
- my_decimal value_buff, *dec_val= val_decimal(&value_buff);
- if (null_value)
- return 0.0;
- my_decimal2double(E_DEC_FATAL_ERROR, dec_val, &result);
- return result;
-}
-
-
-longlong Item::val_int_from_decimal()
-{
- /* Note that fix_fields may not be called for Item_avg_field items */
- longlong result;
- my_decimal value, *dec_val= val_decimal(&value);
- if (null_value)
- return 0;
- my_decimal2int(E_DEC_FATAL_ERROR, dec_val, unsigned_flag, &result);
- return result;
-}
-
-
-longlong Item::val_int_unsigned_typecast_from_decimal()
-{
- longlong result;
- my_decimal tmp, *dec= val_decimal(&tmp);
- if (null_value)
- return 0;
- my_decimal2int(E_DEC_FATAL_ERROR, dec, 1, &result);
- return result;
-}
-
-
int Item::save_time_in_field(Field *field, bool no_conversions)
{
MYSQL_TIME ltime;
- if (get_time(&ltime))
+ if (get_time(field->table->in_use, &ltime))
return set_field_to_null_with_conversions(field, no_conversions);
field->set_notnull();
return field->store_time_dec(&ltime, decimals);
@@ -493,7 +370,8 @@ int Item::save_time_in_field(Field *field, bool no_conversions)
int Item::save_date_in_field(Field *field, bool no_conversions)
{
MYSQL_TIME ltime;
- if (get_date(&ltime, sql_mode_for_dates(field->table->in_use)))
+ THD *thd= field->table->in_use;
+ if (get_date(thd, &ltime, Datetime::Options(thd)))
return set_field_to_null_with_conversions(field, no_conversions);
field->set_notnull();
return field->store_time_dec(&ltime, decimals);
@@ -533,11 +411,11 @@ int Item::save_str_value_in_field(Field *field, String *result)
Item::Item(THD *thd):
is_expensive_cache(-1), rsize(0), name(null_clex_str), orig_name(0),
- fixed(0), is_autogenerated_name(TRUE)
+ is_autogenerated_name(TRUE)
{
DBUG_ASSERT(thd);
marker= 0;
- maybe_null=null_value=with_sum_func=with_window_func=with_field=0;
+ maybe_null= null_value= with_window_func= with_field= false;
in_rollup= 0;
with_param= 0;
@@ -590,11 +468,9 @@ Item::Item(THD *thd, Item *item):
maybe_null(item->maybe_null),
in_rollup(item->in_rollup),
null_value(item->null_value),
- with_sum_func(item->with_sum_func),
with_param(item->with_param),
with_window_func(item->with_window_func),
with_field(item->with_field),
- fixed(item->fixed),
is_autogenerated_name(item->is_autogenerated_name)
{
next= thd->free_list; // Put in free list
@@ -664,7 +540,6 @@ void Item::cleanup()
{
DBUG_ENTER("Item::cleanup");
DBUG_PRINT("enter", ("this: %p", this));
- fixed= 0;
marker= 0;
join_tab_idx= MAX_TABLES;
if (orig_name)
@@ -684,7 +559,7 @@ void Item::cleanup()
bool Item::cleanup_processor(void *arg)
{
- if (fixed)
+ if (is_fixed())
cleanup();
return FALSE;
}
@@ -776,7 +651,8 @@ Item_ident::Item_ident(THD *thd, TABLE_LIST *view_arg,
:Item_result_field(thd), orig_db_name(NullS),
orig_table_name(view_arg->table_name.str),
orig_field_name(*field_name_arg),
- context(&view_arg->view->select_lex.context),
+ /* TODO: suspicious use of first_select_lex */
+ context(&view_arg->view->first_select_lex()->context),
db_name(NullS), table_name(view_arg->alias.str),
field_name(*field_name_arg),
alias_name_used(FALSE), cached_field_index(NO_CACHED_FIELD_INDEX),
@@ -970,12 +846,15 @@ bool Item_field::register_field_in_read_map(void *arg)
{
TABLE *table= (TABLE *) arg;
int res= 0;
+ if (table && table != field->table)
+ return res;
+
if (field->vcol_info &&
- !bitmap_fast_test_and_set(field->table->vcol_set, field->field_index))
+ !bitmap_fast_test_and_set(field->table->read_set, field->field_index))
{
res= field->vcol_info->expr->walk(&Item::register_field_in_read_map,1,arg);
}
- if (field->table == table || !table)
+ else
bitmap_set_bit(field->table->read_set, field->field_index);
return res;
}
@@ -1203,7 +1082,7 @@ bool Item::check_type_scalar(const char *opname) const
This hack in Item_outer_ref should probably be refactored eventually.
Discuss with Sanja.
*/
- DBUG_ASSERT(fixed || type() == REF_ITEM);
+ DBUG_ASSERT(is_fixed() || type() == REF_ITEM);
const Type_handler *handler= type_handler();
if (handler->is_scalar_type())
return false;
@@ -1352,7 +1231,6 @@ Item *Item_cache::safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
unlikely(!(cache= new (thd->mem_root) Item_cache_str(thd, conv))))
return NULL; // Safe conversion is not possible, or OEM
cache->setup(thd, conv);
- cache->fixed= false; // Make Item::fix_fields() happy
return cache;
}
@@ -1403,7 +1281,7 @@ Item *Item::const_charset_converter(THD *thd, CHARSET_INFO *tocs,
const char *func_name)
{
DBUG_ASSERT(const_item());
- DBUG_ASSERT(fixed);
+ DBUG_ASSERT(is_fixed());
StringBuffer<64>tmp;
String *s= val_str(&tmp);
MEM_ROOT *mem_root= thd->mem_root;
@@ -1471,117 +1349,38 @@ Item *Item_param::safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
As a extra convenience the time structure is reset on error or NULL values!
*/
-bool Item::get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item::get_date_from_int(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- longlong value= val_int();
- bool neg= !unsigned_flag && value < 0;
- if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value,
- ltime, fuzzydate,
- field_table_or_null(),
- field_name_or_null()))
- return null_value|= make_zero_date(ltime, fuzzydate);
- return null_value= false;
-}
-
-
-bool Item::get_date_from_year(MYSQL_TIME *ltime, ulonglong fuzzydate)
-{
- longlong value= val_int();
- DBUG_ASSERT(unsigned_flag || value >= 0);
- if (max_length == 2)
- {
- if (value < 70)
- value+= 2000;
- else if (value <= 1900)
- value+= 1900;
- }
- value*= 10000; /* make it YYYYMMHH */
- if (null_value || int_to_datetime_with_warn(false, value,
- ltime, fuzzydate,
- field_table_or_null(),
- field_name_or_null()))
- return null_value|= make_zero_date(ltime, fuzzydate);
- return null_value= false;
-}
-
-
-bool Item::get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate)
-{
- double value= val_real();
- if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate,
+ Longlong_hybrid value(val_int(), unsigned_flag);
+ return null_value || int_to_datetime_with_warn(thd, value,
+ ltime, fuzzydate,
field_table_or_null(),
- field_name_or_null()))
- return null_value|= make_zero_date(ltime, fuzzydate);
- return null_value= false;
+ field_name_or_null());
}
-bool Item::get_date_from_decimal(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item::get_date_from_real(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- my_decimal value, *res;
- if (!(res= val_decimal(&value)) ||
- decimal_to_datetime_with_warn(res, ltime, fuzzydate,
- field_table_or_null(),
- field_name_or_null()))
- return null_value|= make_zero_date(ltime, fuzzydate);
- return null_value= false;
+ double value= val_real();
+ return null_value || double_to_datetime_with_warn(thd, value,
+ ltime, fuzzydate,
+ field_table_or_null(),
+ field_name_or_null());
}
-bool Item::get_date_from_string(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item::get_date_from_string(THD *thd, MYSQL_TIME *to, date_mode_t mode)
{
- char buff[40];
- String tmp(buff,sizeof(buff), &my_charset_bin),*res;
- if (!(res=val_str(&tmp)) ||
- str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
- ltime, fuzzydate))
- return null_value|= make_zero_date(ltime, fuzzydate);
- return null_value= false;
+ StringBuffer<40> tmp;
+ const TABLE_SHARE *s = field_table_or_null();
+ Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
+ s ? s->table_name.str : nullptr,
+ field_name_or_null(), to, mode);
+ Temporal_hybrid *t= new(to) Temporal_hybrid(thd, &warn, val_str(&tmp), mode);
+ return !t->is_valid_temporal();
}
-bool Item::make_zero_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
-{
- /*
- if the item was not null and conversion failed, we return a zero date
- if allowed, otherwise - null.
- */
- bzero((char*) ltime,sizeof(*ltime));
- if (fuzzydate & TIME_TIME_ONLY)
- {
- /*
- In the following scenario:
- - The caller expected to get a TIME value
- - Item returned a not NULL string or numeric value
- - But then conversion from string or number to TIME failed
- we need to change the default time_type from MYSQL_TIMESTAMP_DATE
- (which was set in bzero) to MYSQL_TIMESTAMP_TIME and therefore
- return TIME'00:00:00' rather than DATE'0000-00-00'.
- If we don't do this, methods like Item::get_time_with_conversion()
- will erroneously subtract CURRENT_DATE from '0000-00-00 00:00:00'
- and return TIME'-838:59:59' instead of TIME'00:00:00' as a result.
- */
- ltime->time_type= MYSQL_TIMESTAMP_TIME;
- }
- return !(fuzzydate & TIME_FUZZY_DATES);
-}
-
-bool Item::get_seconds(ulonglong *sec, ulong *sec_part)
-{
- if (decimals == 0)
- { // optimize for an important special case
- longlong val= val_int();
- bool neg= val < 0 && !unsigned_flag;
- *sec= neg ? -val : val;
- *sec_part= 0;
- return neg;
- }
- my_decimal tmp, *dec= val_decimal(&tmp);
- if (!dec)
- return 0;
- return my_decimal2seconds(dec, sec, sec_part);
-}
-
const MY_LOCALE *Item::locale_from_val_str()
{
StringBuffer<MAX_FIELD_WIDTH> tmp;
@@ -1719,7 +1518,7 @@ Query_fragment::Query_fragment(THD *thd, sp_head *sphead,
*****************************************************************************/
Item_sp_variable::Item_sp_variable(THD *thd, const LEX_CSTRING *sp_var_name)
- :Item(thd), m_thd(0), m_name(*sp_var_name)
+ :Item_fixed_hybrid(thd), m_thd(0), m_name(*sp_var_name)
#ifndef DBUG_OFF
, m_sp(0)
#endif
@@ -1731,7 +1530,7 @@ bool Item_sp_variable::fix_fields_from_item(THD *thd, Item **, const Item *it)
{
m_thd= thd; /* NOTE: this must be set before any this_xxx() */
- DBUG_ASSERT(it->fixed);
+ DBUG_ASSERT(it->is_fixed());
max_length= it->max_length;
decimals= it->decimals;
@@ -1802,6 +1601,12 @@ String *Item_sp_variable::val_str(String *sp)
}
+bool Item_sp_variable::val_native(THD *thd, Native *to)
+{
+ return val_native_from_item(thd, this_item(), to);
+}
+
+
my_decimal *Item_sp_variable::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed);
@@ -1812,11 +1617,11 @@ my_decimal *Item_sp_variable::val_decimal(my_decimal *decimal_value)
}
-bool Item_sp_variable::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_sp_variable::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed);
Item *it= this_item();
- bool val= it->get_date(ltime, fuzzydate);
+ bool val= it->get_date(thd, ltime, fuzzydate);
null_value= it->null_value;
return val;
}
@@ -1852,10 +1657,10 @@ Item_splocal::Item_splocal(THD *thd,
Rewritable_query_parameter(pos_in_q, len_in_q),
Type_handler_hybrid_field_type(handler),
m_rcontext_handler(rh),
- m_var_idx(sp_var_idx)
+ m_var_idx(sp_var_idx),
+ m_type(handler == &type_handler_row ? ROW_ITEM : CONST_ITEM)
{
maybe_null= TRUE;
- m_type= sp_map_item_type(handler);
}
@@ -2193,14 +1998,19 @@ my_decimal *Item_name_const::val_decimal(my_decimal *decimal_value)
return val;
}
-bool Item_name_const::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_name_const::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed);
- bool rc= value_item->get_date(ltime, fuzzydate);
+ bool rc= value_item->get_date(thd, ltime, fuzzydate);
null_value= value_item->null_value;
return rc;
}
+bool Item_name_const::val_native(THD *thd, Native *to)
+{
+ return val_native_from_item(thd, value_item, to);
+}
+
bool Item_name_const::is_null()
{
return value_item->is_null();
@@ -2208,58 +2018,30 @@ bool Item_name_const::is_null()
Item_name_const::Item_name_const(THD *thd, Item *name_arg, Item *val):
- Item(thd), value_item(val), name_item(name_arg)
+ Item_fixed_hybrid(thd), value_item(val), name_item(name_arg)
{
StringBuffer<128> name_buffer;
String *name_str;
Item::maybe_null= TRUE;
- valid_args= true;
- if (!name_item->basic_const_item() ||
- !(name_str= name_item->val_str(&name_buffer))) // Can't have a NULL name
- goto err;
- set_name(thd, name_str->ptr(), name_str->length(), name_str->charset());
-
- if (value_item->basic_const_item())
- return; // ok
-
- if (value_item->type() == FUNC_ITEM)
- {
- Item_func *value_func= (Item_func *) value_item;
- if (value_func->functype() != Item_func::COLLATE_FUNC &&
- value_func->functype() != Item_func::NEG_FUNC)
- goto err;
-
- if (value_func->key_item()->basic_const_item())
- return; // ok
- }
-
-err:
- valid_args= false;
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST");
+ if (name_item->basic_const_item() &&
+ (name_str= name_item->val_str(&name_buffer))) // Can't have a NULL name
+ set_name(thd, name_str->ptr(), name_str->length(), name_str->charset());
}
Item::Type Item_name_const::type() const
{
/*
- As
- 1. one can try to create the Item_name_const passing non-constant
- arguments, although it's incorrect and
- 2. the type() method can be called before the fix_fields() to get
- type information for a further type cast, e.g.
- if (item->type() == FIELD_ITEM)
- ((Item_field *) item)->...
- we return NULL_ITEM in the case to avoid wrong casting.
-
- valid_args guarantees value_item->basic_const_item(); if type is
- FUNC_ITEM, then we have a fudged item_func_neg() on our hands
- and return the underlying type.
+
+ We are guarenteed that value_item->basic_const_item(), if not
+ an error is thrown that WRONG ARGUMENTS are supplied to
+ NAME_CONST function.
+ If type is FUNC_ITEM, then we have a fudged item_func_neg()
+ on our hands and return the underlying type.
For Item_func_set_collation()
e.g. NAME_CONST('name', 'value' COLLATE collation) we return its
'value' argument type.
*/
- if (!valid_args)
- return NULL_ITEM;
Item::Type value_type= value_item->type();
if (value_type == FUNC_ITEM)
{
@@ -2279,10 +2061,8 @@ Item::Type Item_name_const::type() const
bool Item_name_const::fix_fields(THD *thd, Item **ref)
{
- if ((!value_item->fixed &&
- value_item->fix_fields(thd, &value_item)) ||
- (!name_item->fixed &&
- name_item->fix_fields(thd, &name_item)) ||
+ if (value_item->fix_fields_if_needed(thd, &value_item) ||
+ name_item->fix_fields_if_needed(thd, &name_item) ||
!value_item->const_item() ||
!name_item->const_item())
{
@@ -2393,7 +2173,7 @@ void Item::split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array,
else
{
/* Not a SUM() function */
- if (unlikely((!with_sum_func && !(split_flags & SPLIT_SUM_SELECT))))
+ if (unlikely((!with_sum_func() && !(split_flags & SPLIT_SUM_SELECT))))
{
/*
This is not a SUM function and there are no SUM functions inside.
@@ -2401,7 +2181,7 @@ void Item::split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array,
*/
return;
}
- if (likely(with_sum_func ||
+ if (likely(with_sum_func() ||
(type() == FUNC_ITEM &&
(((Item_func *) this)->functype() ==
Item_func::ISNOTNULLTEST_FUNC ||
@@ -2754,7 +2534,7 @@ bool Type_std_attributes::agg_item_set_converter(const DTCollation &coll,
thd->change_item_tree(arg, conv);
- if (conv->fix_fields(thd, arg))
+ if (conv->fix_fields_if_needed(thd, arg))
{
res= TRUE;
break; // we cannot return here, we need to restore "arena".
@@ -2819,9 +2599,7 @@ Item_sp::Item_sp(THD *thd, Name_resolution_context *context_arg,
dummy_table= (TABLE*) thd->calloc(sizeof(TABLE) + sizeof(TABLE_SHARE) +
sizeof(Query_arena));
dummy_table->s= (TABLE_SHARE*) (dummy_table + 1);
- /* TODO(cvicentiu) Move this sp_query_arena in the class as a direct member.
- Currently it can not be done due to header include dependencies. */
- sp_query_arena= (Query_arena *) (dummy_table->s + 1);
+ sp_query_arena= new(dummy_table->s + 1) Query_arena();
memset(&sp_mem_root, 0, sizeof(sp_mem_root));
}
@@ -2832,7 +2610,7 @@ Item_sp::Item_sp(THD *thd, Item_sp *item):
dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE) +
sizeof(Query_arena));
dummy_table->s= (TABLE_SHARE*) (dummy_table+1);
- sp_query_arena= (Query_arena *) (dummy_table->s + 1);
+ sp_query_arena= new(dummy_table->s + 1) Query_arena();
memset(&sp_mem_root, 0, sizeof(sp_mem_root));
}
@@ -2905,7 +2683,7 @@ bool Item_sp::execute(THD *thd, bool *null_value, Item **args, uint arg_count)
if (unlikely(execute_impl(thd, args, arg_count)))
{
*null_value= 1;
- context->process_error(thd);
+ process_error(thd);
if (thd->killed)
thd->send_kill_message();
return true;
@@ -2938,7 +2716,7 @@ Item_sp::execute_impl(THD *thd, Item **args, uint arg_count)
DBUG_ENTER("Item_sp::execute_impl");
- if (context->security_ctx)
+ if (context && context->security_ctx)
{
/* Set view definer security context */
thd->security_ctx= context->security_ctx;
@@ -3097,18 +2875,6 @@ Item* Item_ref::build_clone(THD *thd)
}
-void Item_ident_for_show::make_send_field(THD *thd, Send_field *tmp_field)
-{
- tmp_field->table_name= tmp_field->org_table_name= table_name;
- tmp_field->db_name= db_name;
- tmp_field->col_name= tmp_field->org_col_name= field->field_name;
- tmp_field->length=field->field_length;
- tmp_field->type=field->type();
- tmp_field->flags= field->table->maybe_null ?
- (field->flags & ~NOT_NULL_FLAG) : field->flags;
- tmp_field->decimals= field->decimals();
-}
-
/**********************************************/
Item_field::Item_field(THD *thd, Field *f)
@@ -3117,7 +2883,10 @@ Item_field::Item_field(THD *thd, Field *f)
have_privileges(0), any_privileges(0)
{
set_field(f);
-
+ /*
+ field_name and table_name should not point to garbage
+ if this item is to be reused
+ */
orig_table_name= table_name;
orig_field_name= field_name;
with_field= 1;
@@ -3434,7 +3203,7 @@ String *Item_field::str_result(String *str)
return result_field->val_str(str,&str_value);
}
-bool Item_field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+bool Item_field::get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate)
{
if ((null_value=field->is_null()) || field->get_date(ltime,fuzzydate))
{
@@ -3444,7 +3213,7 @@ bool Item_field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
return 0;
}
-bool Item_field::get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_field::get_date_result(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
if ((null_value= result_field->is_null()) ||
result_field->get_date(ltime, fuzzydate))
@@ -3456,6 +3225,36 @@ bool Item_field::get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate)
}
+bool Item_field::val_native(THD *thd, Native *to)
+{
+ return val_native_from_field(field, to);
+}
+
+
+bool Item_field::val_native_result(THD *thd, Native *to)
+{
+ return val_native_from_field(result_field, to);
+}
+
+
+longlong Item_field::val_datetime_packed(THD *thd)
+{
+ DBUG_ASSERT(fixed == 1);
+ if ((null_value= field->is_null()))
+ return 0;
+ return field->val_datetime_packed(thd);
+}
+
+
+longlong Item_field::val_time_packed(THD *thd)
+{
+ DBUG_ASSERT(fixed == 1);
+ if ((null_value= field->is_null()))
+ return 0;
+ return field->val_time_packed(thd);
+}
+
+
void Item_field::save_result(Field *to)
{
save_field_in_field(result_field, &null_value, to, TRUE);
@@ -3654,6 +3453,60 @@ longlong Item_field::val_int_endpoint(bool left_endp, bool *incl_endp)
return null_value? LONGLONG_MIN : res;
}
+
+bool Item_basic_value::eq(const Item *item, bool binary_cmp) const
+{
+ const Item_const *c0, *c1;
+ const Type_handler *h0, *h1;
+ /*
+ - Test get_item_const() for NULL filters out Item_param
+ bound in a way that needs a data type conversion
+ (e.g. non-integer value in a LIMIT clause).
+ Item_param::get_item_const() return NULL in such cases.
+ - Test for type_handler_for_comparison() equality makes sure
+ that values of different data type groups do not get detected
+ as equal (e.g. numbers vs strings, time vs datetime).
+ - Test for cast_to_int_type_handler() equality distinguishes
+ values with dual properties. For example, VARCHAR 'abc' and hex
+ hybrid 0x616263 are equal in string context, but they are not equal
+ if the hybrid appears in integer context (it behaves as integer then).
+ Here we have no full information about the context, so treat them
+ as not equal.
+ QQ: We could pass Value_source::Context here instead of
+ "bool binary_cmp", to make substitution more delicate.
+ See Field::get_equal_const_item().
+ */
+ bool res= (c0= get_item_const()) &&
+ (c1= item->get_item_const()) &&
+ (h0= type_handler())->type_handler_for_comparison() ==
+ (h1= item->type_handler())->type_handler_for_comparison() &&
+ h0->cast_to_int_type_handler()->type_handler_for_comparison() ==
+ h1->cast_to_int_type_handler()->type_handler_for_comparison();
+ if (res)
+ {
+ switch (c0->const_is_null() + c1->const_is_null()) {
+ case 2: // Two NULLs
+ res= true;
+ break;
+ case 1: // NULL and non-NULL
+ res= false;
+ break;
+ case 0: // Two non-NULLs
+ res= h0->Item_const_eq(c0, c1, binary_cmp);
+ }
+ }
+ DBUG_EXECUTE_IF("Item_basic_value",
+ push_warning_printf(current_thd,
+ Sql_condition::WARN_LEVEL_NOTE,
+ ER_UNKNOWN_ERROR, "%seq=%d a=%s b=%s",
+ binary_cmp ? "bin_" : "", (int) res,
+ DbugStringItemTypeValue(current_thd, this).c_ptr(),
+ DbugStringItemTypeValue(current_thd, item).c_ptr()
+ ););
+ return res;
+}
+
+
/**
Create an item from a string we KNOW points to a valid longlong
end \\0 terminated number string.
@@ -3673,7 +3526,6 @@ Item_int::Item_int(THD *thd, const char *str_arg, size_t length):
the field name.
*/
name.length= !str_arg[max_length] ? max_length : strlen(str_arg);
- fixed= 1;
}
@@ -3685,8 +3537,6 @@ my_decimal *Item_int::val_decimal(my_decimal *decimal_value)
String *Item_int::val_str(String *str)
{
- // following assert is redundant, because fixed=1 assigned in constructor
- DBUG_ASSERT(fixed == 1);
str->set_int(value, unsigned_flag, collation.collation);
return str;
}
@@ -3723,8 +3573,6 @@ Item_uint::Item_uint(THD *thd, const char *str_arg, longlong i, uint length):
String *Item_uint::val_str(String *str)
{
- // following assert is redundant, because fixed=1 assigned in constructor
- DBUG_ASSERT(fixed == 1);
str->set((ulonglong) value, collation.collation);
return str;
}
@@ -3746,7 +3594,6 @@ Item_decimal::Item_decimal(THD *thd, const char *str_arg, size_t length,
name.str= str_arg;
name.length= safe_strlen(str_arg);
decimals= (uint8) decimal_value.frac;
- fixed= 1;
max_length= my_decimal_precision_to_length_no_truncation(decimal_value.intg +
decimals,
decimals,
@@ -3758,7 +3605,6 @@ Item_decimal::Item_decimal(THD *thd, longlong val, bool unsig):
{
int2my_decimal(E_DEC_FATAL_ERROR, val, unsig, &decimal_value);
decimals= (uint8) decimal_value.frac;
- fixed= 1;
max_length= my_decimal_precision_to_length_no_truncation(decimal_value.intg +
decimals,
decimals,
@@ -3771,7 +3617,6 @@ Item_decimal::Item_decimal(THD *thd, double val, int precision, int scale):
{
double2my_decimal(E_DEC_FATAL_ERROR, val, &decimal_value);
decimals= (uint8) decimal_value.frac;
- fixed= 1;
max_length= my_decimal_precision_to_length_no_truncation(decimal_value.intg +
decimals,
decimals,
@@ -3788,16 +3633,14 @@ Item_decimal::Item_decimal(THD *thd, const char *str, const my_decimal *val_arg,
name.length= safe_strlen(str);
decimals= (uint8) decimal_par;
max_length= length;
- fixed= 1;
}
-Item_decimal::Item_decimal(THD *thd, my_decimal *value_par):
+Item_decimal::Item_decimal(THD *thd, const my_decimal *value_par):
Item_num(thd)
{
my_decimal2decimal(value_par, &decimal_value);
decimals= (uint8) decimal_value.frac;
- fixed= 1;
max_length= my_decimal_precision_to_length_no_truncation(decimal_value.intg +
decimals,
decimals,
@@ -3806,63 +3649,15 @@ Item_decimal::Item_decimal(THD *thd, my_decimal *value_par):
Item_decimal::Item_decimal(THD *thd, const uchar *bin, int precision, int scale):
- Item_num(thd)
+ Item_num(thd),
+ decimal_value(bin, precision, scale)
{
- binary2my_decimal(E_DEC_FATAL_ERROR, bin,
- &decimal_value, precision, scale);
decimals= (uint8) decimal_value.frac;
- fixed= 1;
max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
unsigned_flag);
}
-longlong Item_decimal::val_int()
-{
- longlong result;
- my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &result);
- return result;
-}
-
-double Item_decimal::val_real()
-{
- double result;
- my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &result);
- return result;
-}
-
-String *Item_decimal::val_str(String *result)
-{
- result->set_charset(&my_charset_numeric);
- my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, result);
- return result;
-}
-
-void Item_decimal::print(String *str, enum_query_type query_type)
-{
- my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, &str_value);
- str->append(str_value);
-}
-
-
-bool Item_decimal::eq(const Item *item, bool binary_cmp) const
-{
- if (type() == item->type() && item->basic_const_item())
- {
- /*
- We need to cast off const to call val_decimal(). This should
- be OK for a basic constant. Additionally, we can pass 0 as
- a true decimal constant will return its internal decimal
- storage and ignore the argument.
- */
- Item *arg= (Item*) item;
- my_decimal *value= arg->val_decimal(0);
- return !my_decimal_cmp(&decimal_value, value);
- }
- return 0;
-}
-
-
void Item_decimal::set_decimal_value(my_decimal *value_par)
{
my_decimal2decimal(value_par, &decimal_value);
@@ -3884,8 +3679,6 @@ Item *Item_decimal::clone_item(THD *thd)
String *Item_float::val_str(String *str)
{
- // following assert is redundant, because fixed=1 assigned in constructor
- DBUG_ASSERT(fixed == 1);
str->set_real(value, decimals, &my_charset_numeric);
return str;
}
@@ -3893,8 +3686,6 @@ String *Item_float::val_str(String *str)
my_decimal *Item_float::val_decimal(my_decimal *decimal_value)
{
- // following assert is redundant, because fixed=1 assigned in constructor
- DBUG_ASSERT(fixed == 1);
double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_value);
return (decimal_value);
}
@@ -3955,7 +3746,6 @@ void Item_string::print(String *str, enum_query_type query_type)
double Item_string::val_real()
{
- DBUG_ASSERT(fixed == 1);
return double_from_string_with_check(&str_value);
}
@@ -3966,7 +3756,6 @@ double Item_string::val_real()
*/
longlong Item_string::val_int()
{
- DBUG_ASSERT(fixed == 1);
return longlong_from_string_with_check(&str_value);
}
@@ -3979,23 +3768,17 @@ my_decimal *Item_string::val_decimal(my_decimal *decimal_value)
double Item_null::val_real()
{
- // following assert is redundant, because fixed=1 assigned in constructor
- DBUG_ASSERT(fixed == 1);
null_value=1;
return 0.0;
}
longlong Item_null::val_int()
{
- // following assert is redundant, because fixed=1 assigned in constructor
- DBUG_ASSERT(fixed == 1);
null_value=1;
return 0;
}
/* ARGSUSED */
String *Item_null::val_str(String *str)
{
- // following assert is redundant, because fixed=1 assigned in constructor
- DBUG_ASSERT(fixed == 1);
null_value=1;
return 0;
}
@@ -4006,25 +3789,23 @@ my_decimal *Item_null::val_decimal(my_decimal *decimal_value)
}
-longlong Item_null::val_datetime_packed()
+longlong Item_null::val_datetime_packed(THD *)
{
null_value= true;
return 0;
}
-longlong Item_null::val_time_packed()
+longlong Item_null::val_time_packed(THD *)
{
null_value= true;
return 0;
}
-bool Item_null::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_null::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- // following assert is redundant, because fixed=1 assigned in constructor
- DBUG_ASSERT(fixed == 1);
- make_zero_date(ltime, fuzzydate);
+ set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
return (null_value= true);
}
@@ -4073,8 +3854,6 @@ Item_param::Item_param(THD *thd, const LEX_CSTRING *name_arg,
*/
Type_handler_hybrid_field_type(&type_handler_null),
state(NO_VALUE),
- /* Don't pretend to be a literal unless value for this item is set. */
- item_type(PARAM_ITEM),
m_empty_string_is_null(false),
indicator(STMT_INDICATOR_NONE),
m_out_param_info(NULL),
@@ -4133,7 +3912,6 @@ void Item_param::sync_clones()
c->Type_geometry_attributes::operator=(*this);
c->state= state;
- c->item_type= item_type;
c->m_empty_string_is_null= m_empty_string_is_null;
c->value.PValue_simple::operator=(value);
@@ -4168,7 +3946,6 @@ void Item_param::set_null()
max_length= 0;
decimals= 0;
state= NULL_VALUE;
- fix_type(Item::NULL_ITEM);
DBUG_VOID_RETURN;
}
@@ -4183,7 +3960,6 @@ void Item_param::set_int(longlong i, uint32 max_length_arg)
decimals= 0;
maybe_null= 0;
null_value= 0;
- fix_type(Item::INT_ITEM);
DBUG_VOID_RETURN;
}
@@ -4198,7 +3974,6 @@ void Item_param::set_double(double d)
decimals= NOT_FIXED_DEC;
maybe_null= 0;
null_value= 0;
- fix_type(Item::REAL_ITEM);
DBUG_VOID_RETURN;
}
@@ -4231,7 +4006,6 @@ void Item_param::set_decimal(const char *str, ulong length)
decimals, unsigned_flag);
maybe_null= 0;
null_value= 0;
- fix_type(Item::DECIMAL_ITEM);
DBUG_VOID_RETURN;
}
@@ -4249,7 +4023,6 @@ void Item_param::set_decimal(const my_decimal *dv, bool unsigned_arg)
decimals, unsigned_flag);
maybe_null= 0;
null_value= 0;
- fix_type(Item::DECIMAL_ITEM);
}
@@ -4261,7 +4034,6 @@ void Item_param::fix_temporal(uint32 max_length_arg, uint decimals_arg)
decimals= decimals_arg;
maybe_null= 0;
null_value= 0;
- fix_type(Item::DATE_ITEM);
}
@@ -4346,7 +4118,6 @@ bool Item_param::set_str(const char *str, ulong length,
null_value= 0;
/* max_length and decimals are set after charset conversion */
/* sic: str may be not null-terminated, don't add DBUG_PRINT here */
- fix_type(Item::STRING_ITEM);
DBUG_RETURN(FALSE);
}
@@ -4380,7 +4151,6 @@ bool Item_param::set_longdata(const char *str, ulong length)
state= LONG_DATA_VALUE;
maybe_null= 0;
null_value= 0;
- fix_type(Item::STRING_ITEM);
DBUG_RETURN(FALSE);
}
@@ -4444,7 +4214,7 @@ bool Item_param::set_from_item(THD *thd, Item *item)
}
}
struct st_value tmp;
- if (!item->save_in_value(&tmp))
+ if (!item->save_in_value(thd, &tmp))
{
const Type_handler *h= item->type_handler();
set_handler(h);
@@ -4482,16 +4252,6 @@ void Item_param::reset()
state= NO_VALUE;
maybe_null= 1;
null_value= 0;
- fixed= false;
- /*
- Don't reset item_type to PARAM_ITEM: it's only needed to guard
- us from item optimizations at prepare stage, when item doesn't yet
- contain a literal of some kind.
- In all other cases when this object is accessed its value is
- set (this assumption is guarded by 'state' and
- DBUG_ASSERTS(state != NO_VALUE) in all Item_param::get_*
- methods).
- */
DBUG_VOID_RETURN;
}
@@ -4574,7 +4334,7 @@ void Item_param::invalid_default_param() const
}
-bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate)
+bool Item_param::get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate)
{
/*
LIMIT clause parameter should not call get_date()
@@ -4588,7 +4348,7 @@ bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate)
*res= value.time;
return 0;
}
- return type_handler()->Item_get_date(this, res, fuzzydate);
+ return type_handler()->Item_get_date_with_warn(thd, this, res, fuzzydate);
}
@@ -4600,11 +4360,7 @@ double Item_param::PValue::val_real() const
case INT_RESULT:
return (double) integer;
case DECIMAL_RESULT:
- {
- double result;
- my_decimal2double(E_DEC_FATAL_ERROR, &m_decimal, &result);
- return result;
- }
+ return m_decimal.to_double();
case STRING_RESULT:
return double_from_string_with_check(&m_string);
case TIME_RESULT:
@@ -4629,11 +4385,7 @@ longlong Item_param::PValue::val_int(const Type_std_attributes *attr) const
case INT_RESULT:
return integer;
case DECIMAL_RESULT:
- {
- longlong i;
- my_decimal2int(E_DEC_FATAL_ERROR, &m_decimal, attr->unsigned_flag, &i);
- return i;
- }
+ return m_decimal.to_longlong(attr->unsigned_flag);
case STRING_RESULT:
return longlong_from_string_with_check(&m_string);
case TIME_RESULT:
@@ -4683,7 +4435,7 @@ String *Item_param::PValue::val_str(String *str,
str->set(integer, &my_charset_bin);
return str;
case DECIMAL_RESULT:
- if (my_decimal2string(E_DEC_FATAL_ERROR, &m_decimal, 0, 0, 0, str) <= 1)
+ if (m_decimal.to_string_native(str, 0, 0, 0) <= 1)
return str;
return NULL;
case TIME_RESULT:
@@ -4724,8 +4476,7 @@ const String *Item_param::value_query_val_str(THD *thd, String *str) const
str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin);
return str;
case DECIMAL_RESULT:
- if (my_decimal2string(E_DEC_FATAL_ERROR, &value.m_decimal,
- 0, 0, 0, str) > 1)
+ if (value.m_decimal.to_string_native(str, 0, 0, 0) > 1)
return &my_null_string;
return str;
case TIME_RESULT:
@@ -4830,11 +4581,20 @@ bool Item_param::convert_str_value(THD *thd)
bool Item_param::basic_const_item() const
{
- DBUG_ASSERT(fixed || state == NO_VALUE);
- if (state == NO_VALUE ||
- (state == SHORT_DATA_VALUE && type_handler()->cmp_type() == TIME_RESULT))
- return FALSE;
- return TRUE;
+ switch (state) {
+ case LONG_DATA_VALUE:
+ case NULL_VALUE:
+ return true;
+ case SHORT_DATA_VALUE:
+ return type_handler()->cmp_type() != TIME_RESULT;
+ case DEFAULT_VALUE:
+ case IGNORE_VALUE:
+ invalid_default_param();
+ return false;
+ case NO_VALUE:
+ break;
+ }
+ return false;
}
@@ -4895,48 +4655,6 @@ Item_param::clone_item(THD *thd)
}
-bool Item_param::value_eq(const Item *item, bool binary_cmp) const
-{
- switch (value.type_handler()->cmp_type()) {
- case INT_RESULT:
- return int_eq(value.integer, item);
- case REAL_RESULT:
- return real_eq(value.real, item);
- case STRING_RESULT:
- return str_eq(&value.m_string, item, binary_cmp);
- case DECIMAL_RESULT:
- case TIME_RESULT:
- case ROW_RESULT:
- break;
- }
- return false;
-}
-
-
-bool
-Item_param::eq(const Item *item, bool binary_cmp) const
-{
- if (!basic_const_item())
- return FALSE;
-
- // There's no "default". See comments in Item_param::save_in_field().
- switch (state) {
- case IGNORE_VALUE:
- case DEFAULT_VALUE:
- invalid_default_param();
- return false;
- case NULL_VALUE:
- return null_eq(item);
- case SHORT_DATA_VALUE:
- case LONG_DATA_VALUE:
- return value_eq(item, binary_cmp);
- case NO_VALUE:
- return false;
- }
- DBUG_ASSERT(0); // Garbage
- return FALSE;
-}
-
/* End of Item_param related */
void Item_param::print(String *str, enum_query_type query_type)
@@ -4990,12 +4708,10 @@ Item_param::set_param_type_and_swap_value(Item_param *src)
{
Type_std_attributes::set(src);
set_handler(src->type_handler());
- item_type= src->item_type;
maybe_null= src->maybe_null;
null_value= src->null_value;
state= src->state;
- fixed= src->fixed;
value.swap(src->value);
}
@@ -5005,7 +4721,6 @@ void Item_param::set_default()
{
m_is_settable_routine_parameter= false;
state= DEFAULT_VALUE;
- fixed= true;
/*
When Item_param is set to DEFAULT_VALUE:
- its val_str() and val_decimal() return NULL
@@ -5021,7 +4736,6 @@ void Item_param::set_ignore()
{
m_is_settable_routine_parameter= false;
state= IGNORE_VALUE;
- fixed= true;
null_value= true;
}
@@ -5050,7 +4764,7 @@ Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it)
correctly fetches the value from the client-server protocol,
using set_param_func().
*/
- if (arg->save_in_value(&tmp) ||
+ if (arg->save_in_value(thd, &tmp) ||
set_value(thd, arg, &tmp, arg->type_handler()))
{
set_null();
@@ -5075,7 +4789,7 @@ void
Item_param::set_out_param_info(Send_field *info)
{
m_out_param_info= info;
- set_handler_by_field_type(m_out_param_info->type);
+ set_handler(m_out_param_info->type_handler());
}
@@ -5116,16 +4830,7 @@ void Item_param::make_send_field(THD *thd, Send_field *field)
OUT-parameter info to fill out the names.
*/
- field->db_name= m_out_param_info->db_name;
- field->table_name= m_out_param_info->table_name;
- field->org_table_name= m_out_param_info->org_table_name;
- field->col_name= m_out_param_info->col_name;
- field->org_col_name= m_out_param_info->org_col_name;
-
- field->length= m_out_param_info->length;
- field->flags= m_out_param_info->flags;
- field->decimals= m_out_param_info->decimals;
- field->type= m_out_param_info->type;
+ *field= *m_out_param_info;
}
bool Item_param::append_for_log(THD *thd, String *str)
@@ -5196,17 +4901,6 @@ my_decimal *Item_copy_string::val_decimal(my_decimal *decimal_value)
Functions to convert item to field (for send_result_set_metadata)
*/
-/* ARGSUSED */
-bool Item::fix_fields(THD *thd, Item **ref)
-{
-
- // We do not check fields which are fixed during construction
- DBUG_ASSERT(fixed == 0 || basic_const_item());
- fixed= 1;
- return FALSE;
-}
-
-
void Item_ref_null_helper::save_val(Field *to)
{
DBUG_ASSERT(fixed == 1);
@@ -5260,9 +4954,15 @@ String* Item_ref_null_helper::val_str(String* s)
}
-bool Item_ref_null_helper::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_ref_null_helper::val_native(THD *thd, Native *to)
+{
+ return (owner->was_null|= val_native_from_item(thd, *ref, to));
+}
+
+
+bool Item_ref_null_helper::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return (owner->was_null|= null_value= (*ref)->get_date_result(ltime, fuzzydate));
+ return (owner->was_null|= null_value= (*ref)->get_date_result(thd, ltime, fuzzydate));
}
@@ -5564,7 +5264,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
ref->alias_name_used= TRUE;
/* If this is a non-aggregated field inside HAVING, search in GROUP BY. */
- if (select->having_fix_field && !ref->with_sum_func && group_list)
+ if (select->having_fix_field && !ref->with_sum_func() && group_list)
{
group_by_ref= find_field_in_group_list(ref, group_list);
@@ -5606,7 +5306,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
ref->name.str, "forward reference in item list");
return NULL;
}
- DBUG_ASSERT((*select_ref)->fixed);
+ DBUG_ASSERT((*select_ref)->is_fixed());
return &select->ref_pointer_array[counter];
}
if (group_by_ref)
@@ -5729,8 +5429,9 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
Name_resolution_context *outer_context= 0;
SELECT_LEX *select= 0;
/* Currently derived tables cannot be correlated */
- if (current_sel->master_unit()->first_select()->linkage !=
- DERIVED_TABLE_TYPE)
+ if ((current_sel->master_unit()->first_select()->get_linkage() !=
+ DERIVED_TABLE_TYPE) &&
+ current_sel->master_unit()->outer_select())
outer_context= context->outer_context;
/*
@@ -5883,7 +5584,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
return -1; /* Some error occurred (e.g. ambiguous names). */
if (ref != not_found_item)
{
- DBUG_ASSERT(*ref && (*ref)->fixed);
+ DBUG_ASSERT(*ref && (*ref)->is_fixed());
prev_subselect_item->used_tables_and_const_cache_join(*ref);
break;
}
@@ -5925,7 +5626,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
Item_ref *rf;
/* Should have been checked in resolve_ref_in_select_and_group(). */
- DBUG_ASSERT(*ref && (*ref)->fixed);
+ DBUG_ASSERT(*ref && (*ref)->is_fixed());
/*
Here, a subset of actions performed by Item_ref::set_properties
is not enough. So we pass ptr to NULL into Item_[direct]_ref
@@ -6501,23 +6202,25 @@ Item *Item_field::replace_equal_field(THD *thd, uchar *arg)
comparison context, and it's safe to replace it to the constant from
item_equal.
*/
- DBUG_ASSERT(type_handler()->type_handler_for_comparison()->cmp_type() ==
+ DBUG_ASSERT(type_handler_for_comparison()->cmp_type() ==
item_equal->compare_type_handler()->cmp_type());
return const_item2;
}
- Item_field *subst=
- (Item_field *)(item_equal->get_first(param->context_tab, this));
+ Item_ident *subst=
+ (Item_ident *) (item_equal->get_first(param->context_tab, this));
if (subst)
- subst= (Item_field *) (subst->real_item());
- if (subst && !field->eq(subst->field))
- return subst;
+ {
+ Item_field *subst2= (Item_field *) (subst->real_item());
+ if (subst2 && !field->eq(subst2->field))
+ return subst2;
+ }
}
return this;
}
void Item::init_make_send_field(Send_field *tmp_field,
- enum enum_field_types field_type_arg)
+ const Type_handler *h)
{
tmp_field->db_name= "";
tmp_field->org_table_name= "";
@@ -6527,7 +6230,7 @@ void Item::init_make_send_field(Send_field *tmp_field,
tmp_field->flags= (maybe_null ? 0 : NOT_NULL_FLAG) |
(my_binary_compare(charset_for_protocol()) ?
BINARY_FLAG : 0);
- tmp_field->type= field_type_arg;
+ tmp_field->set_handler(h);
tmp_field->length=max_length;
tmp_field->decimals=decimals;
if (unsigned_flag)
@@ -6536,13 +6239,13 @@ void Item::init_make_send_field(Send_field *tmp_field,
void Item::make_send_field(THD *thd, Send_field *tmp_field)
{
- init_make_send_field(tmp_field, field_type());
+ init_make_send_field(tmp_field, type_handler());
}
void Item_empty_string::make_send_field(THD *thd, Send_field *tmp_field)
{
- init_make_send_field(tmp_field, string_type_handler()->field_type());
+ init_make_send_field(tmp_field, string_type_handler());
}
@@ -6873,12 +6576,11 @@ int Item::save_real_in_field(Field *field, bool no_conversions)
int Item::save_decimal_in_field(Field *field, bool no_conversions)
{
- my_decimal decimal_value;
- my_decimal *value= val_decimal(&decimal_value);
- if (null_value)
+ VDec value(this);
+ if (value.is_null())
return set_field_to_null_with_conversions(field, no_conversions);
field->set_notnull();
- return field->store_decimal(value);
+ return field->store_decimal(value.ptr());
}
@@ -6945,14 +6647,18 @@ Item_string::make_string_literal_concat(THD *thd, const LEX_CSTRING *str)
*/
Item *Item_string::make_odbc_literal(THD *thd, const LEX_CSTRING *typestr)
{
- enum_field_types type= odbc_temporal_literal_type(typestr);
- Item *res= type == MYSQL_TYPE_STRING ? this :
- create_temporal_literal(thd, val_str(NULL), type, false);
+ Item_literal *res;
+ const Type_handler *h;
+ if (collation.repertoire == MY_REPERTOIRE_ASCII &&
+ str_value.length() < MAX_DATE_STRING_REP_LENGTH * 4 &&
+ (h= Type_handler::odbc_literal_type_handler(typestr)) &&
+ (res= h->create_literal_item(thd, val_str(NULL), false)))
+ return res;
/*
- create_temporal_literal() returns NULL if failed to parse the string,
+ h->create_literal_item() returns NULL if failed to parse the string,
or the string format did not match the type, e.g.: {d'2001-01-01 10:10:10'}
*/
- return res ? res : this;
+ return this;
}
@@ -7155,7 +6861,6 @@ Item_float::Item_float(THD *thd, const char *str_arg, size_t length):
name.length= strlen(str_arg);
decimals=(uint8) nr_of_decimals(str_arg, str_arg+length);
max_length=(uint32)length;
- fixed= 1;
}
@@ -7211,7 +6916,6 @@ void Item_hex_constant::hex_string_init(THD *thd, const char *str, size_t str_le
}
*ptr=0; // Keep purify happy
collation.set(&my_charset_bin, DERIVATION_COERCIBLE);
- fixed= 1;
unsigned_flag= 1;
}
@@ -7290,24 +6994,14 @@ Item_bin_string::Item_bin_string(THD *thd, const char *str, size_t str_length):
ptr[0]= 0;
collation.set(&my_charset_bin, DERIVATION_COERCIBLE);
- fixed= 1;
}
-bool Item_temporal_literal::eq(const Item *item, bool binary_cmp) const
-{
- return
- item->basic_const_item() && type() == item->type() &&
- field_type() == ((Item_temporal_literal *) item)->field_type() &&
- !my_time_compare(&cached_time,
- &((Item_temporal_literal *) item)->cached_time);
-}
-
void Item_date_literal::print(String *str, enum_query_type query_type)
{
str->append("DATE'");
char buf[MAX_DATE_STRING_REP_LENGTH];
- my_date_to_str(&cached_time, buf);
+ my_date_to_str(cached_time.get_mysql_time(), buf);
str->append(buf);
str->append('\'');
}
@@ -7319,12 +7013,11 @@ Item *Item_date_literal::clone_item(THD *thd)
}
-bool Item_date_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_date_literal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- DBUG_ASSERT(fixed);
- fuzzy_date |= sql_mode_for_dates(current_thd);
- *ltime= cached_time;
- return (null_value= check_date_with_warn(ltime, fuzzy_date,
+ fuzzydate |= sql_mode_for_dates(thd);
+ cached_time.copy_to_mysql_time(ltime);
+ return (null_value= check_date_with_warn(thd, ltime, fuzzydate,
MYSQL_TIMESTAMP_ERROR));
}
@@ -7333,7 +7026,7 @@ void Item_datetime_literal::print(String *str, enum_query_type query_type)
{
str->append("TIMESTAMP'");
char buf[MAX_DATE_STRING_REP_LENGTH];
- my_datetime_to_str(&cached_time, buf, decimals);
+ my_datetime_to_str(cached_time.get_mysql_time(), buf, decimals);
str->append(buf);
str->append('\'');
}
@@ -7345,12 +7038,11 @@ Item *Item_datetime_literal::clone_item(THD *thd)
}
-bool Item_datetime_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_datetime_literal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- DBUG_ASSERT(fixed);
- fuzzy_date |= sql_mode_for_dates(current_thd);
- *ltime= cached_time;
- return (null_value= check_date_with_warn(ltime, fuzzy_date,
+ fuzzydate |= sql_mode_for_dates(thd);
+ cached_time.copy_to_mysql_time(ltime);
+ return (null_value= check_date_with_warn(thd, ltime, fuzzydate,
MYSQL_TIMESTAMP_ERROR));
}
@@ -7359,7 +7051,7 @@ void Item_time_literal::print(String *str, enum_query_type query_type)
{
str->append("TIME'");
char buf[MAX_DATE_STRING_REP_LENGTH];
- my_time_to_str(&cached_time, buf, decimals);
+ my_time_to_str(cached_time.get_mysql_time(), buf, decimals);
str->append(buf);
str->append('\'');
}
@@ -7371,13 +7063,12 @@ Item *Item_time_literal::clone_item(THD *thd)
}
-bool Item_time_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_time_literal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- DBUG_ASSERT(fixed);
- *ltime= cached_time;
- if (fuzzy_date & TIME_TIME_ONLY)
+ cached_time.copy_to_mysql_time(ltime);
+ if (fuzzydate & TIME_TIME_ONLY)
return (null_value= false);
- return (null_value= check_date_with_warn(ltime, fuzzy_date,
+ return (null_value= check_date_with_warn(thd, ltime, fuzzydate,
MYSQL_TIMESTAMP_ERROR));
}
@@ -7493,7 +7184,7 @@ void Item_field::update_null_value()
no_errors= thd->no_errors;
thd->no_errors= 1;
- Item::update_null_value();
+ type_handler()->Item_update_null_value(this);
thd->no_errors= no_errors;
}
@@ -7544,6 +7235,198 @@ Item *Item_field::update_value_transformer(THD *thd, uchar *select_arg)
}
+/**
+ @brief
+ Prepare AND/OR formula for extraction of a pushable condition
+
+ @param checker the checker callback function to be applied to the nodes
+ of the tree of the object
+ @param arg parameter to be passed to the checker
+
+ @details
+ This method recursively traverses this AND/OR condition and for each
+ subformula of the condition it checks whether it can be usable for the
+ extraction of a pushable condition. The criteria of pushability of
+ a subformula is checked by the callback function 'checker' with one
+ parameter arg. The subformulas that are not usable are marked with
+ the flag NO_EXTRACTION_FL.
+ @note
+ This method is called before any call of build_pushable_cond.
+ The flag NO_EXTRACTION_FL set in a subformula allows to avoid building
+ clones for the subformulas that are not used in the pushable condition.
+ @note
+ This method is called for pushdown conditions into materialized
+ derived tables/views optimization.
+ Item::pushable_cond_checker_for_derived() is passed as the actual callback
+ function.
+ Also it is called for pushdown conditions in materialized IN subqueries.
+ Item::pushable_cond_checker_for_subquery is passed as the actual
+ callback function.
+*/
+
+void Item::check_pushable_cond(Pushdown_checker checker, uchar *arg)
+{
+ clear_extraction_flag();
+ if (type() == Item::COND_ITEM)
+ {
+ bool and_cond= ((Item_cond*) this)->functype() == Item_func::COND_AND_FUNC;
+ List_iterator<Item> li(*((Item_cond*) this)->argument_list());
+ uint count= 0;
+ Item *item;
+ while ((item=li++))
+ {
+ item->check_pushable_cond(checker, arg);
+ if (item->get_extraction_flag() != NO_EXTRACTION_FL)
+ count++;
+ else if (!and_cond)
+ break;
+ }
+ if ((and_cond && count == 0) || item)
+ {
+ set_extraction_flag(NO_EXTRACTION_FL);
+ if (and_cond)
+ li.rewind();
+ while ((item= li++))
+ item->clear_extraction_flag();
+ }
+ }
+ else if (!((this->*checker) (arg)))
+ set_extraction_flag(NO_EXTRACTION_FL);
+}
+
+
+/**
+ @brief
+ Build condition extractable from this condition for pushdown
+
+ @param thd the thread handle
+ @param checker the checker callback function to be applied to the nodes
+ of the tree of the object to check if multiple equality
+ elements can be used to create equalities
+ @param arg parameter to be passed to the checker
+
+ @details
+ This method finds out what condition that can be pushed down can be
+ extracted from this condition. If such condition C exists the
+ method builds the item for it. The method uses the flag NO_EXTRACTION_FL
+ set by the preliminary call of the method check_pushable_cond() to figure
+ out whether a subformula is pushable or not.
+ In the case when this item is a multiple equality a checker method is
+ called to find the equal fields to build a new equality that can be
+ pushed down.
+ @note
+ The built condition C is always implied by the condition cond
+ (cond => C). The method tries to build the most restrictive such
+ condition (i.e. for any other condition C' such that cond => C'
+ we have C => C').
+ @note
+ The build item is not ready for usage: substitution for the field items
+ has to be done and it has to be re-fixed.
+ @note
+ This method is called for pushdown conditions into materialized
+ derived tables/views optimization.
+ Item::pushable_equality_checker_for_derived() is passed as the actual
+ callback function.
+ Also it is called for pushdown conditions into materialized IN subqueries.
+ Item::pushable_equality_checker_for_subquery() is passed as the actual
+ callback function.
+
+ @retval
+ the built condition pushable into if such a condition exists
+ NULL if there is no such a condition
+*/
+
+Item *Item::build_pushable_cond(THD *thd,
+ Pushdown_checker checker,
+ uchar *arg)
+{
+ bool is_multiple_equality= type() == Item::FUNC_ITEM &&
+ ((Item_func*) this)->functype() == Item_func::MULT_EQUAL_FUNC;
+
+ if (get_extraction_flag() == NO_EXTRACTION_FL)
+ return 0;
+
+ if (type() == Item::COND_ITEM)
+ {
+ bool cond_and= false;
+ Item_cond *new_cond;
+ if (((Item_cond*) this)->functype() == Item_func::COND_AND_FUNC)
+ {
+ cond_and= true;
+ new_cond= new (thd->mem_root) Item_cond_and(thd);
+ }
+ else
+ new_cond= new (thd->mem_root) Item_cond_or(thd);
+ if (!new_cond)
+ return 0;
+ List_iterator<Item> li(*((Item_cond*) this)->argument_list());
+ Item *item;
+ bool is_fix_needed= false;
+
+ while ((item=li++))
+ {
+ if (item->get_extraction_flag() == NO_EXTRACTION_FL)
+ {
+ if (!cond_and)
+ return 0;
+ continue;
+ }
+ Item *fix= item->build_pushable_cond(thd, checker, arg);
+ if (!fix && !cond_and)
+ return 0;
+ if (!fix)
+ continue;
+
+ if (fix->type() == Item::COND_ITEM &&
+ ((Item_cond*) fix)->functype() == Item_func::COND_AND_FUNC)
+ is_fix_needed= true;
+
+ if (new_cond->argument_list()->push_back(fix, thd->mem_root))
+ return 0;
+ }
+ if (is_fix_needed && new_cond->fix_fields(thd, 0))
+ return 0;
+
+ switch (new_cond->argument_list()->elements)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return new_cond->argument_list()->head();
+ default:
+ return new_cond;
+ }
+ }
+ else if (is_multiple_equality)
+ {
+ List<Item> equalities;
+ Item *new_cond= NULL;
+ if (((Item_equal *)this)->create_pushable_equalities(thd, &equalities,
+ checker, arg, true) ||
+ (equalities.elements == 0))
+ return 0;
+
+ switch (equalities.elements)
+ {
+ case 0:
+ return 0;
+ case 1:
+ new_cond= equalities.head();
+ break;
+ default:
+ new_cond= new (thd->mem_root) Item_cond_and(thd, equalities);
+ break;
+ }
+ if (new_cond && new_cond->fix_fields(thd, &new_cond))
+ return 0;
+ return new_cond;
+ }
+ else if (get_extraction_flag() != NO_EXTRACTION_FL)
+ return build_clone(thd);
+ return 0;
+}
+
+
static
Item *get_field_item_for_having(THD *thd, Item *item, st_select_lex *sel)
{
@@ -7677,50 +7560,15 @@ Item *Item_direct_view_ref::derived_field_transformer_for_where(THD *thd,
return (*ref);
}
-static
-Grouping_tmp_field *find_matching_grouping_field(Item *item,
- st_select_lex *sel)
-{
- DBUG_ASSERT(item->type() == Item::FIELD_ITEM ||
- (item->type() == Item::REF_ITEM &&
- ((Item_ref *) item)->ref_type() == Item_ref::VIEW_REF));
- List_iterator<Grouping_tmp_field> li(sel->grouping_tmp_fields);
- Grouping_tmp_field *gr_field;
- Item_field *field_item= (Item_field *) (item->real_item());
- while ((gr_field= li++))
- {
- if (field_item->field == gr_field->tmp_field)
- return gr_field;
- }
- Item_equal *item_equal= item->get_item_equal();
- if (item_equal)
- {
- Item_equal_fields_iterator it(*item_equal);
- Item *equal_item;
- while ((equal_item= it++))
- {
- field_item= (Item_field *) (equal_item->real_item());
- li.rewind();
- while ((gr_field= li++))
- {
- if (field_item->field == gr_field->tmp_field)
- return gr_field;
- }
- }
- }
- return NULL;
-}
-
-Item *Item_field::derived_grouping_field_transformer_for_where(THD *thd,
- uchar *arg)
+Item *Item_field::grouping_field_transformer_for_where(THD *thd, uchar *arg)
{
st_select_lex *sel= (st_select_lex *)arg;
- Grouping_tmp_field *gr_field= find_matching_grouping_field(this, sel);
+ Field_pair *gr_field= find_matching_field_pair(this, sel->grouping_tmp_fields);
if (gr_field)
{
Item *producing_clone=
- gr_field->producing_item->build_clone(thd);
+ gr_field->corresponding_item->build_clone(thd);
if (producing_clone)
producing_clone->marker|= SUBSTITUTION_FL;
return producing_clone;
@@ -7730,8 +7578,8 @@ Item *Item_field::derived_grouping_field_transformer_for_where(THD *thd,
Item *
-Item_direct_view_ref::derived_grouping_field_transformer_for_where(THD *thd,
- uchar *arg)
+Item_direct_view_ref::grouping_field_transformer_for_where(THD *thd,
+ uchar *arg)
{
if ((*ref)->marker & SUBSTITUTION_FL)
{
@@ -7741,8 +7589,9 @@ Item_direct_view_ref::derived_grouping_field_transformer_for_where(THD *thd,
if (!item_equal)
return this;
st_select_lex *sel= (st_select_lex *)arg;
- Grouping_tmp_field *gr_field= find_matching_grouping_field(this, sel);
- return gr_field->producing_item->build_clone(thd);
+ Field_pair *gr_field= find_matching_field_pair(this,
+ sel->grouping_tmp_fields);
+ return gr_field->corresponding_item->build_clone(thd);
}
void Item_field::print(String *str, enum_query_type query_type)
@@ -7778,7 +7627,7 @@ Item_ref::Item_ref(THD *thd, Name_resolution_context *context_arg,
/*
This constructor used to create some internals references over fixed items
*/
- if ((set_properties_only= (ref && *ref && (*ref)->fixed)))
+ if ((set_properties_only= (ref && *ref && (*ref)->is_fixed())))
set_properties();
}
@@ -7827,7 +7676,7 @@ Item_ref::Item_ref(THD *thd, TABLE_LIST *view_arg, Item **item,
/*
This constructor is used to create some internal references over fixed items
*/
- if ((set_properties_only= (ref && *ref && (*ref)->fixed)))
+ if ((set_properties_only= (ref && *ref && (*ref)->is_fixed())))
set_properties();
}
@@ -7953,7 +7802,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
goto error; /* Some error occurred (e.g. ambiguous names). */
if (ref != not_found_item)
{
- DBUG_ASSERT(*ref && (*ref)->fixed);
+ DBUG_ASSERT(*ref && (*ref)->is_fixed());
prev_subselect_item->used_tables_and_const_cache_join(*ref);
break;
}
@@ -8076,7 +7925,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
goto error;
}
/* Should be checked in resolve_ref_in_select_and_group(). */
- DBUG_ASSERT(*ref && (*ref)->fixed);
+ DBUG_ASSERT(*ref && (*ref)->is_fixed());
mark_as_dependent(thd, last_checked_context->select_lex,
context->select_lex, this, this, false);
/*
@@ -8101,13 +7950,13 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
*/
if (!((*ref)->type() == REF_ITEM &&
((Item_ref *)(*ref))->ref_type() == OUTER_REF) &&
- (((*ref)->with_sum_func && name.str &&
- !(current_sel->linkage != GLOBAL_OPTIONS_TYPE &&
+ (((*ref)->with_sum_func() && name.str &&
+ !(current_sel->get_linkage() != GLOBAL_OPTIONS_TYPE &&
current_sel->having_fix_field)) ||
- !(*ref)->fixed))
+ !(*ref)->is_fixed()))
{
my_error(ER_ILLEGAL_REFERENCE, MYF(0),
- name.str, ((*ref)->with_sum_func?
+ name.str, ((*ref)->with_sum_func() ?
"reference to group function":
"forward reference in item list"));
goto error;
@@ -8133,7 +7982,7 @@ void Item_ref::set_properties()
We have to remember if we refer to a sum function, to ensure that
split_sum_func() doesn't try to change the reference.
*/
- with_sum_func= (*ref)->with_sum_func;
+ copy_with_sum_func(*ref);
with_param= (*ref)->with_param;
with_window_func= (*ref)->with_window_func;
with_field= (*ref)->with_field;
@@ -8319,6 +8168,14 @@ String *Item_ref::str_result(String* str)
}
+bool Item_ref::val_native_result(THD *thd, Native *to)
+{
+ return result_field ?
+ val_native_from_field(result_field, to) :
+ val_native(thd, to);
+}
+
+
my_decimal *Item_ref::val_decimal_result(my_decimal *decimal_value)
{
if (result_field)
@@ -8407,25 +8264,31 @@ bool Item_ref::is_null()
}
-bool Item_ref::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+bool Item_ref::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+{
+ return (null_value=(*ref)->get_date_result(thd, ltime, fuzzydate));
+}
+
+
+bool Item_ref::val_native(THD *thd, Native *to)
{
- return (null_value=(*ref)->get_date_result(ltime,fuzzydate));
+ return val_native_from_item(thd, *ref, to);
}
-longlong Item_ref::val_datetime_packed()
+longlong Item_ref::val_datetime_packed(THD *thd)
{
DBUG_ASSERT(fixed);
- longlong tmp= (*ref)->val_datetime_packed_result();
+ longlong tmp= (*ref)->val_datetime_packed_result(thd);
null_value= (*ref)->null_value;
return tmp;
}
-longlong Item_ref::val_time_packed()
+longlong Item_ref::val_time_packed(THD *thd)
{
DBUG_ASSERT(fixed);
- longlong tmp= (*ref)->val_time_packed_result();
+ longlong tmp= (*ref)->val_time_packed_result(thd);
null_value= (*ref)->null_value;
return tmp;
}
@@ -8562,23 +8425,29 @@ bool Item_direct_ref::is_null()
}
-bool Item_direct_ref::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+bool Item_direct_ref::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+{
+ return (null_value=(*ref)->get_date(thd, ltime, fuzzydate));
+}
+
+
+bool Item_direct_ref::val_native(THD *thd, Native *to)
{
- return (null_value=(*ref)->get_date(ltime,fuzzydate));
+ return val_native_from_item(thd, *ref, to);
}
-longlong Item_direct_ref::val_time_packed()
+longlong Item_direct_ref::val_time_packed(THD *thd)
{
- longlong tmp = (*ref)->val_time_packed();
+ longlong tmp = (*ref)->val_time_packed(thd);
null_value= (*ref)->null_value;
return tmp;
}
-longlong Item_direct_ref::val_datetime_packed()
+longlong Item_direct_ref::val_datetime_packed(THD *thd)
{
- longlong tmp = (*ref)->val_datetime_packed();
+ longlong tmp = (*ref)->val_datetime_packed(thd);
null_value= (*ref)->null_value;
return tmp;
}
@@ -8592,10 +8461,10 @@ Item_cache_wrapper::~Item_cache_wrapper()
Item_cache_wrapper::Item_cache_wrapper(THD *thd, Item *item_arg):
Item_result_field(thd), orig_item(item_arg), expr_cache(NULL), expr_value(NULL)
{
- DBUG_ASSERT(orig_item->fixed);
+ DBUG_ASSERT(orig_item->is_fixed());
Type_std_attributes::set(orig_item);
maybe_null= orig_item->maybe_null;
- with_sum_func= orig_item->with_sum_func;
+ copy_with_sum_func(orig_item);
with_param= orig_item->with_param;
with_field= orig_item->with_field;
name= item_arg->name;
@@ -8654,7 +8523,7 @@ void Item_cache_wrapper::print(String *str, enum_query_type query_type)
bool Item_cache_wrapper::fix_fields(THD *thd __attribute__((unused)),
Item **it __attribute__((unused)))
{
- DBUG_ASSERT(orig_item->fixed);
+ DBUG_ASSERT(orig_item->is_fixed());
DBUG_ASSERT(fixed);
return FALSE;
}
@@ -8873,6 +8742,28 @@ String *Item_cache_wrapper::val_str(String* str)
/**
+ Get the native value of the possibly cached item
+*/
+
+bool Item_cache_wrapper::val_native(THD *thd, Native* to)
+{
+ Item *cached_value;
+ DBUG_ENTER("Item_cache_wrapper::val_native");
+ if (!expr_cache)
+ DBUG_RETURN(val_native_from_item(thd, orig_item, to));
+
+ if ((cached_value= check_cache()))
+ DBUG_RETURN(val_native_from_item(thd, cached_value, to));
+
+ cache();
+ if ((null_value= expr_value->null_value))
+ DBUG_RETURN(true);
+ DBUG_RETURN(expr_value->val_native(thd, to));
+}
+
+
+
+/**
Get the decimal value of the possibly cached item
*/
@@ -8957,18 +8848,18 @@ bool Item_cache_wrapper::is_null()
Get the date value of the possibly cached item
*/
-bool Item_cache_wrapper::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_cache_wrapper::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
Item *cached_value;
DBUG_ENTER("Item_cache_wrapper::get_date");
if (!expr_cache)
- DBUG_RETURN((null_value= orig_item->get_date(ltime, fuzzydate)));
+ DBUG_RETURN((null_value= orig_item->get_date(thd, ltime, fuzzydate)));
if ((cached_value= check_cache()))
- DBUG_RETURN((null_value= cached_value->get_date(ltime, fuzzydate)));
+ DBUG_RETURN((null_value= cached_value->get_date(thd, ltime, fuzzydate)));
cache();
- DBUG_RETURN((null_value= expr_value->get_date(ltime, fuzzydate)));
+ DBUG_RETURN((null_value= expr_value->get_date(thd, ltime, fuzzydate)));
}
@@ -8984,7 +8875,7 @@ int Item_cache_wrapper::save_in_field(Field *to, bool no_conversions)
Item* Item_cache_wrapper::get_tmp_table_item(THD *thd)
{
- if (!orig_item->with_sum_func && !orig_item->const_item())
+ if (!orig_item->with_sum_func() && !orig_item->const_item())
return new (thd->mem_root) Item_temptable_field(thd, result_field);
return copy_or_same(thd);
}
@@ -9014,7 +8905,7 @@ bool Item_direct_view_ref::fix_fields(THD *thd, Item **reference)
/* view fild reference must be defined */
DBUG_ASSERT(*ref);
/* (*ref)->check_cols() will be made in Item_direct_ref::fix_fields */
- if ((*ref)->fixed)
+ if ((*ref)->is_fixed())
{
Item *ref_item= (*ref)->real_item();
if (ref_item->type() == Item::FIELD_ITEM)
@@ -9210,6 +9101,19 @@ Item *Item_direct_view_ref::propagate_equal_fields(THD *thd,
}
+Item *Item_ref::propagate_equal_fields(THD *thd, const Context &ctx,
+ COND_EQUAL *cond)
+{
+ Item *field_item= real_item();
+ if (field_item->type() != FIELD_ITEM)
+ return this;
+ Item *item= field_item->propagate_equal_fields(thd, ctx, cond);
+ if (item != field_item)
+ return item;
+ return this;
+}
+
+
/**
Replace an Item_direct_view_ref for an equal Item_field evaluated earlier
(if any).
@@ -9252,6 +9156,20 @@ Item *Item_direct_view_ref::replace_equal_field(THD *thd, uchar *arg)
}
+bool Item_field::excl_dep_on_table(table_map tab_map)
+{
+ return used_tables() == tab_map ||
+ (item_equal && (item_equal->used_tables() & tab_map));
+}
+
+
+bool
+Item_field::excl_dep_on_grouping_fields(st_select_lex *sel)
+{
+ return find_matching_field_pair(this, sel->grouping_tmp_fields) != NULL;
+}
+
+
bool Item_direct_view_ref::excl_dep_on_table(table_map tab_map)
{
table_map used= used_tables();
@@ -9267,17 +9185,34 @@ bool Item_direct_view_ref::excl_dep_on_table(table_map tab_map)
return (*ref)->excl_dep_on_table(tab_map);
}
+
bool Item_direct_view_ref::excl_dep_on_grouping_fields(st_select_lex *sel)
{
if (item_equal)
{
DBUG_ASSERT(real_item()->type() == Item::FIELD_ITEM);
- return find_matching_grouping_field(this, sel) != NULL;
+ return (find_matching_field_pair(this, sel->grouping_tmp_fields) != NULL);
}
return (*ref)->excl_dep_on_grouping_fields(sel);
}
+bool Item_args::excl_dep_on_grouping_fields(st_select_lex *sel)
+{
+ for (uint i= 0; i < arg_count; i++)
+ {
+ if (args[i]->type() == Item::FUNC_ITEM &&
+ ((Item_func *)args[i])->functype() == Item_func::UDF_FUNC)
+ return false;
+ if (args[i]->const_item())
+ continue;
+ if (!args[i]->excl_dep_on_grouping_fields(sel))
+ return false;
+ }
+ return true;
+}
+
+
double Item_direct_view_ref::val_result()
{
double tmp=(*ref)->val_result();
@@ -9427,6 +9362,12 @@ void Item_default_value::calculate()
DEBUG_SYNC(field->table->in_use, "after_Item_default_value_calculate");
}
+bool Item_default_value::val_native(THD *thd, Native *to)
+{
+ calculate();
+ return Item_field::val_native(thd, to);
+}
+
String *Item_default_value::val_str(String *str)
{
calculate();
@@ -9451,10 +9392,10 @@ my_decimal *Item_default_value::val_decimal(my_decimal *decimal_value)
return Item_field::val_decimal(decimal_value);
}
-bool Item_default_value::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+bool Item_default_value::get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate)
{
calculate();
- return Item_field::get_date(ltime, fuzzydate);
+ return Item_field::get_date(thd, ltime, fuzzydate);
}
bool Item_default_value::send(Protocol *protocol, st_value *buffer)
@@ -9505,10 +9446,17 @@ my_decimal *Item_default_value::val_decimal_result(my_decimal *decimal_value)
return Item_field::val_decimal_result(decimal_value);
}
-bool Item_default_value::get_date_result(MYSQL_TIME *ltime,ulonglong fuzzydate)
+bool Item_default_value::get_date_result(THD *thd, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate)
+{
+ calculate();
+ return Item_field::get_date_result(thd, ltime, fuzzydate);
+}
+
+bool Item_default_value::val_native_result(THD *thd, Native *to)
{
calculate();
- return Item_field::get_date_result(ltime, fuzzydate);
+ return Item_field::val_native_result(thd, to);
}
table_map Item_default_value::used_tables() const
@@ -9558,7 +9506,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items)
{
DBUG_ASSERT(fixed == 0);
/* We should only check that arg is in first table */
- if (!arg->fixed)
+ if (!arg->is_fixed())
{
bool res;
TABLE_LIST *orig_next_table= context->last_name_resolution_table;
@@ -9770,7 +9718,7 @@ void Item_trigger_field::cleanup()
Since special nature of Item_trigger_field we should not do most of
things from Item_field::cleanup() or Item_ident::cleanup() here.
*/
- Item::cleanup();
+ Item_fixed_hybrid::cleanup();
}
@@ -9829,73 +9777,14 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
{
- Item_result res_type=item_cmp_type(field->result_type(),
- item->result_type());
- /*
- We have to check field->cmp_type() instead of res_type,
- as result_type() - and thus res_type - can never be TIME_RESULT (yet).
- */
- if (field->cmp_type() == TIME_RESULT)
- {
- MYSQL_TIME field_time, item_time, item_time2, *item_time_cmp= &item_time;
- if (field->type() == MYSQL_TYPE_TIME)
- {
- field->get_time(&field_time);
- item->get_time(&item_time);
- }
- else
- {
- field->get_date(&field_time, TIME_INVALID_DATES);
- item->get_date(&item_time, TIME_INVALID_DATES);
- if (item_time.time_type == MYSQL_TIMESTAMP_TIME)
- if (time_to_datetime(thd, &item_time, item_time_cmp= &item_time2))
- return 1;
- }
- return my_time_compare(&field_time, item_time_cmp);
- }
- if (res_type == STRING_RESULT)
+ Type_handler_hybrid_field_type cmp(field->type_handler_for_comparison());
+ if (cmp.aggregate_for_comparison(item->type_handler_for_comparison()))
{
- char item_buff[MAX_FIELD_WIDTH];
- char field_buff[MAX_FIELD_WIDTH];
-
- String item_tmp(item_buff,sizeof(item_buff),&my_charset_bin);
- String field_tmp(field_buff,sizeof(field_buff),&my_charset_bin);
- String *item_result= item->val_str(&item_tmp);
- /*
- Some implementations of Item::val_str(String*) actually modify
- the field Item::null_value, hence we can't check it earlier.
- */
- if (item->null_value)
- return 0;
- String *field_result= field->val_str(&field_tmp);
- return sortcmp(field_result, item_result, field->charset());
- }
- if (res_type == INT_RESULT)
- return 0; // Both are of type int
- if (res_type == DECIMAL_RESULT)
- {
- my_decimal item_buf, *item_val,
- field_buf, *field_val;
- item_val= item->val_decimal(&item_buf);
- if (item->null_value)
- return 0;
- field_val= field->val_decimal(&field_buf);
- return my_decimal_cmp(field_val, item_val);
- }
- /*
- The patch for Bug#13463415 started using this function for comparing
- BIGINTs. That uncovered a bug in Visual Studio 32bit optimized mode.
- Prefixing the auto variables with volatile fixes the problem....
- */
- volatile double result= item->val_real();
- if (item->null_value)
+ // At fix_fields() time we checked that "field" and "item" are comparable
+ DBUG_ASSERT(0);
return 0;
- volatile double field_result= field->val_real();
- if (field_result < result)
- return -1;
- else if (field_result > result)
- return 1;
- return 0;
+ }
+ return cmp.type_handler()->stored_field_cmp_to_item(thd, field, item);
}
@@ -9958,7 +9847,6 @@ bool Item_cache_int::cache_value()
String *Item_cache_int::val_str(String *str)
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return NULL;
str->set_int(value, unsigned_flag, default_charset());
@@ -9968,7 +9856,6 @@ String *Item_cache_int::val_str(String *str)
my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val)
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return NULL;
int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_val);
@@ -9977,7 +9864,6 @@ my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val)
double Item_cache_int::val_real()
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return 0.0;
return (double) value;
@@ -9985,7 +9871,6 @@ double Item_cache_int::val_real()
longlong Item_cache_int::val_int()
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return 0;
return value;
@@ -10025,88 +9910,12 @@ Item_cache_temporal::Item_cache_temporal(THD *thd, const Type_handler *handler)
}
-longlong Item_cache_temporal::val_datetime_packed()
-{
- DBUG_ASSERT(fixed == 1);
- if (Item_cache_temporal::field_type() == MYSQL_TYPE_TIME)
- return Item::val_datetime_packed(); // TIME-to-DATETIME conversion needed
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
- return 0;
- }
- return value;
-}
-
-
-longlong Item_cache_temporal::val_time_packed()
-{
- DBUG_ASSERT(fixed == 1);
- if (Item_cache_temporal::field_type() != MYSQL_TYPE_TIME)
- return Item::val_time_packed(); // DATETIME-to-TIME conversion needed
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
- return 0;
- }
- return value;
-}
-
-
-String *Item_cache_temporal::val_str(String *str)
-{
- DBUG_ASSERT(fixed == 1);
- if (!has_value())
- {
- null_value= true;
- return NULL;
- }
- return val_string_from_date(str);
-}
-
-
-my_decimal *Item_cache_temporal::val_decimal(my_decimal *decimal_value)
-{
- DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= true;
- return NULL;
- }
- return val_decimal_from_date(decimal_value);
-}
-
-
-longlong Item_cache_temporal::val_int()
-{
- DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= true;
- return 0;
- }
- return val_int_from_date();
-}
-
-
-double Item_cache_temporal::val_real()
-{
- DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= true;
- return 0;
- }
- return val_real_from_date();
-}
-
-
bool Item_cache_temporal::cache_value()
{
if (!example)
return false;
value_cached= true;
- value= example->val_datetime_packed_result();
+ value= example->val_datetime_packed_result(current_thd);
null_value_inside= null_value= example->null_value;
return true;
}
@@ -10117,16 +9926,14 @@ bool Item_cache_time::cache_value()
if (!example)
return false;
value_cached= true;
- value= example->val_time_packed_result();
+ value= example->val_time_packed_result(current_thd);
null_value_inside= null_value= example->null_value;
return true;
}
-bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_cache_temporal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- ErrConvInteger str(value);
-
if (!has_value())
{
bzero((char*) ltime,sizeof(*ltime));
@@ -10141,7 +9948,8 @@ bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
int Item_cache_temporal::save_in_field(Field *field, bool no_conversions)
{
MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
+ // This is a temporal type. No nanoseconds, so round mode is not important.
+ if (get_date(field->get_thd(), &ltime, TIME_CONV_NONE | TIME_FRAC_NONE))
return set_field_to_null_with_conversions(field, no_conversions);
field->set_notnull();
int error= field->store_time_dec(&ltime, decimals);
@@ -10183,25 +9991,78 @@ Item *Item_cache_temporal::convert_to_basic_const_item(THD *thd)
Item *Item_cache_datetime::make_literal(THD *thd)
{
- MYSQL_TIME ltime;
- unpack_time(val_datetime_packed(), &ltime, MYSQL_TIMESTAMP_DATETIME);
- return new (thd->mem_root) Item_datetime_literal(thd, &ltime, decimals);
+ Datetime dt(thd, this, TIME_CONV_NONE | TIME_FRAC_NONE);
+ return new (thd->mem_root) Item_datetime_literal(thd, &dt, decimals);
}
Item *Item_cache_date::make_literal(THD *thd)
{
- MYSQL_TIME ltime;
- unpack_time(val_datetime_packed(), &ltime, MYSQL_TIMESTAMP_DATE);
- return new (thd->mem_root) Item_date_literal(thd, &ltime);
+ Date d(thd, this, TIME_CONV_NONE | TIME_FRAC_NONE);
+ return new (thd->mem_root) Item_date_literal(thd, &d);
}
Item *Item_cache_time::make_literal(THD *thd)
{
- MYSQL_TIME ltime;
- unpack_time(val_time_packed(), &ltime, MYSQL_TIMESTAMP_TIME);
- return new (thd->mem_root) Item_time_literal(thd, &ltime, decimals);
+ Time t(thd, this);
+ return new (thd->mem_root) Item_time_literal(thd, &t, decimals);
+}
+
+
+int Item_cache_timestamp::save_in_field(Field *field, bool no_conversions)
+{
+ if (!has_value())
+ return set_field_to_null_with_conversions(field, no_conversions);
+ return m_native.save_in_field(field, decimals);
+}
+
+
+bool Item_cache_timestamp::val_native(THD *thd, Native *to)
+{
+ if (!has_value())
+ {
+ null_value= true;
+ return true;
+ }
+ return null_value= to->copy(m_native);
+}
+
+
+Datetime Item_cache_timestamp::to_datetime(THD *thd)
+{
+ DBUG_ASSERT(is_fixed() == 1);
+ if (!has_value())
+ {
+ null_value= true;
+ return Datetime();
+ }
+ return m_native.to_datetime(thd);
}
+
+bool Item_cache_timestamp::get_date(THD *thd, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate)
+{
+ if (!has_value())
+ {
+ set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME);
+ return true;
+ }
+ Timestamp_or_zero_datetime tm(m_native);
+ return (null_value= tm.to_TIME(thd, ltime, fuzzydate));
+}
+
+
+bool Item_cache_timestamp::cache_value()
+{
+ if (!example)
+ return false;
+ value_cached= true;
+ null_value= example->val_native_with_conversion_result(current_thd, &m_native,
+ type_handler());
+ return true;
+}
+
+
bool Item_cache_real::cache_value()
{
if (!example)
@@ -10215,7 +10076,6 @@ bool Item_cache_real::cache_value()
double Item_cache_real::val_real()
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return 0.0;
return value;
@@ -10223,7 +10083,6 @@ double Item_cache_real::val_real()
longlong Item_cache_real::val_int()
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return 0;
return Converter_double_to_longlong(value, unsigned_flag).result();
@@ -10232,7 +10091,6 @@ longlong Item_cache_real::val_int()
String* Item_cache_double::val_str(String *str)
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return NULL;
str->set_real(value, decimals, default_charset());
@@ -10242,7 +10100,6 @@ String* Item_cache_double::val_str(String *str)
String* Item_cache_float::val_str(String *str)
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return NULL;
Float(value).to_string(str, decimals);
@@ -10252,7 +10109,6 @@ String* Item_cache_float::val_str(String *str)
my_decimal *Item_cache_real::val_decimal(my_decimal *decimal_val)
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return NULL;
double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val);
@@ -10288,38 +10144,22 @@ bool Item_cache_decimal::cache_value()
double Item_cache_decimal::val_real()
{
- DBUG_ASSERT(fixed);
- double res;
- if (!has_value())
- return 0.0;
- my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &res);
- return res;
+ return !has_value() ? 0.0 : decimal_value.to_double();
}
longlong Item_cache_decimal::val_int()
{
- DBUG_ASSERT(fixed);
- longlong res;
- if (!has_value())
- return 0;
- my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &res);
- return res;
+ return !has_value() ? 0 : decimal_value.to_longlong(unsigned_flag);
}
String* Item_cache_decimal::val_str(String *str)
{
- DBUG_ASSERT(fixed);
- if (!has_value())
- return NULL;
- my_decimal_round(E_DEC_FATAL_ERROR, &decimal_value, decimals, FALSE,
- &decimal_value);
- my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, str);
- return str;
+ return !has_value() ? NULL :
+ decimal_value.to_string_round(str, decimals, &decimal_value);
}
my_decimal *Item_cache_decimal::val_decimal(my_decimal *val)
{
- DBUG_ASSERT(fixed);
if (!has_value())
return NULL;
return &decimal_value;
@@ -10336,9 +10176,8 @@ Item *Item_cache_decimal::convert_to_basic_const_item(THD *thd)
new_item= (Item*) new (thd->mem_root) Item_null(thd);
else
{
- my_decimal decimal_value;
- my_decimal *result= val_decimal(&decimal_value);
- new_item= (Item*) new (thd->mem_root) Item_decimal(thd, result);
+ VDec tmp(this);
+ new_item= (Item*) new (thd->mem_root) Item_decimal(thd, tmp.ptr());
}
return new_item;
}
@@ -10376,7 +10215,6 @@ bool Item_cache_str::cache_value()
double Item_cache_str::val_real()
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return 0.0;
return value ? double_from_string_with_check(value) : 0.0;
@@ -10385,7 +10223,6 @@ double Item_cache_str::val_real()
longlong Item_cache_str::val_int()
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return 0;
return value ? longlong_from_string_with_check(value) : 0;
@@ -10394,7 +10231,6 @@ longlong Item_cache_str::val_int()
String* Item_cache_str::val_str(String *str)
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return 0;
return value;
@@ -10403,7 +10239,6 @@ String* Item_cache_str::val_str(String *str)
my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val)
{
- DBUG_ASSERT(fixed == 1);
if (!has_value())
return NULL;
return value ? decimal_from_string_with_check(decimal_val, value) : 0;
@@ -10596,7 +10431,7 @@ String *Item_type_holder::val_str(String*)
return 0;
}
-bool Item_type_holder::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_type_holder::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(0); // should never be called
return true;
@@ -10605,7 +10440,7 @@ bool Item_type_holder::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
void Item_result_field::cleanup()
{
DBUG_ENTER("Item_result_field::cleanup()");
- Item::cleanup();
+ Item_fixed_hybrid::cleanup();
result_field= 0;
DBUG_VOID_RETURN;
}
@@ -10781,17 +10616,6 @@ const char *dbug_print(SELECT_LEX_UNIT *x) { return dbug_print_unit(x); }
#endif /*DBUG_OFF*/
-bool Item_field::excl_dep_on_table(table_map tab_map)
-{
- return used_tables() == tab_map ||
- (item_equal && (item_equal->used_tables() & tab_map));
-}
-
-bool
-Item_field::excl_dep_on_grouping_fields(st_select_lex *sel)
-{
- return find_matching_grouping_field(this, sel) != NULL;
-}
void Item::register_in(THD *thd)
@@ -10799,3 +10623,15 @@ void Item::register_in(THD *thd)
next= thd->free_list;
thd->free_list= this;
}
+
+
+bool Item::cleanup_excluding_immutables_processor (void *arg)
+{
+ if (!(get_extraction_flag() == IMMUTABLE_FL))
+ return cleanup_processor(arg);
+ else
+ {
+ clear_extraction_flag();
+ return false;
+ }
+}
diff --git a/sql/item.h b/sql/item.h
index a9598ca29c6..dbd2cb0b1df 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2,7 +2,7 @@
#define SQL_ITEM_INCLUDED
/* Copyright (c) 2000, 2017, Oracle and/or its affiliates.
- Copyright (c) 2009, 2020, MariaDB Corporation.
+ Copyright (c) 2009, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -100,7 +100,10 @@ class sp_head;
class Protocol;
struct TABLE_LIST;
void item_init(void); /* Init item functions */
+class Item_basic_value;
+class Item_result_field;
class Item_field;
+class Item_ref;
class Item_param;
class user_var_entry;
class JOIN;
@@ -108,6 +111,7 @@ struct KEY_FIELD;
struct SARGABLE_PARAM;
class RANGE_OPT_PARAM;
class SEL_TREE;
+class With_sum_func_cache;
enum precedence {
LOWEST_PRECEDENCE,
@@ -146,8 +150,11 @@ bool mark_unsupported_function(const char *w1, const char *w2,
#define NO_EXTRACTION_FL (1 << 6)
#define FULL_EXTRACTION_FL (1 << 7)
-#define SUBSTITUTION_FL (1 << 8)
-#define EXTRACTION_MASK (NO_EXTRACTION_FL | FULL_EXTRACTION_FL)
+#define DELETION_FL (1 << 8)
+#define IMMUTABLE_FL (1 << 9)
+#define SUBSTITUTION_FL (1 << 10)
+#define EXTRACTION_MASK \
+ (NO_EXTRACTION_FL | FULL_EXTRACTION_FL | DELETION_FL | IMMUTABLE_FL)
extern const char *item_empty_name;
@@ -596,6 +603,7 @@ typedef bool (Item::*Item_processor) (void *arg);
typedef bool (Item::*Item_analyzer) (uchar **argp);
typedef Item* (Item::*Item_transformer) (THD *thd, uchar *arg);
typedef void (*Cond_traverser) (const Item *item, void *arg);
+typedef bool (Item::*Pushdown_checker) (uchar *arg);
struct st_cond_statistic;
@@ -628,6 +636,87 @@ public:
String_copier_for_item(THD *thd): m_thd(thd) { }
};
+
+/**
+ A helper class describing what kind of Item created a temporary field.
+ - If m_field is set, then the temporary field was created from Field
+ (e.g. when the Item was Item_field, or Item_ref pointing to Item_field)
+ - If m_default_field is set, then there is a usable DEFAULT value.
+ (e.g. when the Item is Item_field)
+ - If m_item_result_field is set, then the temporary field was created
+ from certain sub-types of Item_result_field (e.g. Item_func)
+ See create_tmp_field() in sql_select.cc for details.
+*/
+
+class Tmp_field_src
+{
+ Field *m_field;
+ Field *m_default_field;
+ Item_result_field *m_item_result_field;
+public:
+ Tmp_field_src()
+ :m_field(0),
+ m_default_field(0),
+ m_item_result_field(0)
+ { }
+ Field *field() const { return m_field; }
+ Field *default_field() const { return m_default_field; }
+ Item_result_field *item_result_field() const { return m_item_result_field; }
+ void set_field(Field *field) { m_field= field; }
+ void set_default_field(Field *field) { m_default_field= field; }
+ void set_item_result_field(Item_result_field *item)
+ { m_item_result_field= item; }
+};
+
+
+/**
+ Parameters for create_tmp_field_ex().
+ See create_tmp_field() in sql_select.cc for details.
+*/
+
+class Tmp_field_param
+{
+ bool m_group;
+ bool m_modify_item;
+ bool m_table_cant_handle_bit_fields;
+ bool m_make_copy_field;
+public:
+ Tmp_field_param(bool group,
+ bool modify_item,
+ bool table_cant_handle_bit_fields,
+ bool make_copy_field)
+ :m_group(group),
+ m_modify_item(modify_item),
+ m_table_cant_handle_bit_fields(table_cant_handle_bit_fields),
+ m_make_copy_field(make_copy_field)
+ { }
+ bool group() const { return m_group; }
+ bool modify_item() const { return m_modify_item; }
+ bool table_cant_handle_bit_fields() const
+ { return m_table_cant_handle_bit_fields; }
+ bool make_copy_field() const { return m_make_copy_field; }
+ void set_modify_item(bool to) { m_modify_item= to; }
+};
+
+
+class Item_const
+{
+public:
+ virtual ~Item_const() {}
+ virtual const Type_all_attributes *get_type_all_attributes_from_const() const= 0;
+ virtual bool const_is_null() const { return false; }
+ virtual const longlong *const_ptr_longlong() const { return NULL; }
+ virtual const double *const_ptr_double() const { return NULL; }
+ virtual const my_decimal *const_ptr_my_decimal() const { return NULL; }
+ virtual const MYSQL_TIME *const_ptr_mysql_time() const { return NULL; }
+ virtual const String *const_ptr_string() const { return NULL; }
+};
+
+
+/****************************************************************************/
+
+#define STOP_PTR ((void *) 1)
+
class Item: public Value_source,
public Type_all_attributes
{
@@ -651,17 +740,27 @@ public:
static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
enum Type {FIELD_ITEM= 0, FUNC_ITEM, SUM_FUNC_ITEM,
- WINDOW_FUNC_ITEM, STRING_ITEM,
- INT_ITEM, REAL_ITEM, NULL_ITEM, VARBIN_ITEM,
- COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_VALUE_ITEM,
- CONTEXTUALLY_TYPED_VALUE_ITEM,
- PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM,
- FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM,
+ WINDOW_FUNC_ITEM,
+ /*
+ NOT NULL literal-alike constants, which do not change their
+ value during an SQL statement execution, but can optionally
+ change their value between statements:
+ - Item_literal - real NOT NULL constants
+ - Item_param - can change between statements
+ - Item_splocal - can change between statements
+ - Item_user_var_as_out_param - hack
+ Note, Item_user_var_as_out_param actually abuses the type code.
+ It should be moved out of the Item tree eventually.
+ */
+ CONST_ITEM,
+ NULL_ITEM, // Item_null or Item_param bound to NULL
+ COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_VALUE_ITEM,
+ CONTEXTUALLY_TYPED_VALUE_ITEM,
+ PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM,
+ FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM,
SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER,
- PARAM_ITEM, TRIGGER_FIELD_ITEM, DECIMAL_ITEM,
- XPATH_NODESET, XPATH_NODESET_CMP,
- VIEW_FIXER_ITEM, EXPR_CACHE_ITEM,
- DATE_ITEM};
+ PARAM_ITEM, TRIGGER_FIELD_ITEM,
+ EXPR_CACHE_ITEM};
enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE };
@@ -703,11 +802,34 @@ protected:
*/
Field *tmp_table_field_from_field_type(TABLE *table)
{
+ DBUG_ASSERT(is_fixed());
const Type_handler *h= type_handler()->type_handler_for_tmp_table(this);
return h->make_and_init_table_field(&name, Record_addr(maybe_null),
*this, table);
}
+ /**
+ Create a temporary field for a simple Item, which does not
+ need any special action after the field creation:
+ - is not an Item_field descendant (and not a reference to Item_field)
+ - is not an Item_result_field descendant
+ - does not need to copy any DEFAULT value to the result Field
+ - does not need to set Field::is_created_from_null_item for the result
+ See create_tmp_field_ex() for details on parameters and return values.
+ */
+ Field *create_tmp_field_ex_simple(TABLE *table,
+ Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ DBUG_ASSERT(!param->make_copy_field());
+ DBUG_ASSERT(!is_result_field());
+ DBUG_ASSERT(type() != NULL_ITEM);
+ return tmp_table_field_from_field_type(table);
+ }
Field *create_tmp_field_int(TABLE *table, uint convert_int_length);
+ Field *tmp_table_field_from_field_type_maybe_null(TABLE *table,
+ Tmp_field_src *src,
+ const Tmp_field_param *param,
+ bool is_explicit_null);
void raise_error_not_evaluable();
void push_note_converted_to_negative_complement(THD *thd);
@@ -716,21 +838,21 @@ protected:
/* Helper methods, to get an Item value from another Item */
double val_real_from_item(Item *item)
{
- DBUG_ASSERT(fixed == 1);
+ DBUG_ASSERT(is_fixed());
double value= item->val_real();
null_value= item->null_value;
return value;
}
longlong val_int_from_item(Item *item)
{
- DBUG_ASSERT(fixed == 1);
+ DBUG_ASSERT(is_fixed());
longlong value= item->val_int();
null_value= item->null_value;
return value;
}
String *val_str_from_item(Item *item, String *str)
{
- DBUG_ASSERT(fixed == 1);
+ DBUG_ASSERT(is_fixed());
String *res= item->val_str(str);
if (res)
res->set_charset(collation.collation);
@@ -738,33 +860,48 @@ protected:
res= NULL;
return res;
}
+ bool val_native_from_item(THD *thd, Item *item, Native *to)
+ {
+ DBUG_ASSERT(is_fixed());
+ null_value= item->val_native(thd, to);
+ DBUG_ASSERT(null_value == item->null_value);
+ return null_value;
+ }
+ bool val_native_from_field(Field *field, Native *to)
+ {
+ if ((null_value= field->is_null()))
+ return true;
+ return (null_value= field->val_native(to));
+ }
+ bool val_native_with_conversion_from_item(THD *thd, Item *item, Native *to,
+ const Type_handler *handler)
+ {
+ DBUG_ASSERT(is_fixed());
+ return null_value= item->val_native_with_conversion(thd, to, handler);
+ }
my_decimal *val_decimal_from_item(Item *item, my_decimal *decimal_value)
{
- DBUG_ASSERT(fixed == 1);
+ DBUG_ASSERT(is_fixed());
my_decimal *value= item->val_decimal(decimal_value);
if ((null_value= item->null_value))
value= NULL;
return value;
}
- bool get_date_from_item(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date_from_item(THD *thd, Item *item,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- bool rc= item->get_date(ltime, fuzzydate);
+ bool rc= item->get_date(thd, ltime, fuzzydate);
null_value= MY_TEST(rc || item->null_value);
return rc;
}
- /*
- This method is used if the item was not null but conversion to
- TIME/DATE/DATETIME failed. We return a zero date if allowed,
- otherwise - null.
- */
- bool make_zero_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
-
public:
+
/*
Cache val_str() into the own buffer, e.g. to evaluate constant
expressions with subqueries in the ORDER/GROUP clauses.
*/
String *val_str() { return val_str(&str_value); }
+ virtual Item_func *get_item_func() { return NULL; }
const MY_LOCALE *locale_from_val_str();
@@ -784,14 +921,12 @@ public:
bool in_rollup; /* If used in GROUP BY list
of a query with ROLLUP */
bool null_value; /* if item is null */
- bool with_sum_func; /* True if item contains a sum func */
bool with_param; /* True if contains an SP parameter */
bool with_window_func; /* True if item contains a window func */
/**
True if any item except Item_sum contains a field. Set during parsing.
*/
bool with_field;
- bool fixed; /* If item fixed with fix_fields */
bool is_autogenerated_name; /* indicate was name of this Item
autogenerated or set by user */
// alloc & destruct is done as start of select on THD::mem_root
@@ -815,7 +950,7 @@ public:
void set_name(THD *thd, const char *str, size_t length, CHARSET_INFO *cs);
void set_name_no_truncate(THD *thd, const char *str, uint length,
CHARSET_INFO *cs);
- void init_make_send_field(Send_field *tmp_field,enum enum_field_types type);
+ void init_make_send_field(Send_field *tmp_field, const Type_handler *h);
void share_name_with(const Item *item)
{
name= item->name;
@@ -826,7 +961,7 @@ public:
bool fix_fields_if_needed(THD *thd, Item **ref)
{
- return fixed ? false : fix_fields(thd, ref);
+ return is_fixed() ? false : fix_fields(thd, ref);
}
bool fix_fields_if_needed_for_scalar(THD *thd, Item **ref)
{
@@ -840,7 +975,27 @@ public:
{
return fix_fields_if_needed_for_scalar(thd, ref);
}
- virtual bool fix_fields(THD *, Item **);
+ /*
+ By default we assume that an Item is fixed by the contstructor.
+ */
+ virtual bool fix_fields(THD *, Item **)
+ {
+ /*
+ This should not normally be called, because usually before
+ fix_fields() we check is_fixed() to be false.
+ But historically we allow fix_fields() to be called for Items
+ who return basic_const_item()==true.
+ */
+ DBUG_ASSERT(is_fixed());
+ DBUG_ASSERT(basic_const_item());
+ return false;
+ }
+ virtual bool is_fixed() const { return true; }
+ virtual void unfix_fields()
+ {
+ DBUG_ASSERT(0);
+ }
+
/*
Fix after some tables has been pulled out. Basically re-calculate all
attributes that are dependent on the tables.
@@ -860,11 +1015,14 @@ public:
but rather uses intermediate type conversion items. Then the method is
supposed to be applied recursively.
*/
- virtual inline void quick_fix_field() { fixed= 1; }
+ virtual void quick_fix_field()
+ {
+ DBUG_ASSERT(0);
+ }
- bool save_in_value(struct st_value *value)
+ bool save_in_value(THD *thd, struct st_value *value)
{
- return type_handler()->Item_save_in_value(this, value);
+ return type_handler()->Item_save_in_value(thd, this, value);
}
/* Function returns 1 on overflow and -1 on fatal errors */
@@ -889,6 +1047,21 @@ public:
return type_handler()->field_type();
}
virtual const Type_handler *type_handler() const= 0;
+ /**
+ Detects if an Item has a fixed data type which is known
+ even before fix_fields().
+ Currently it's important only to find Items with a fixed boolean
+ data type. More item types can be marked in the future as having
+ a fixed data type (e.g. all literals, all fixed type functions, etc).
+
+ @retval NULL if the Item type is not known before fix_fields()
+ @retval the pointer to the data type handler, if the data type
+ is known before fix_fields().
+ */
+ virtual const Type_handler *fixed_type_handler() const
+ {
+ return NULL;
+ }
const Type_handler *type_handler_for_comparison() const
{
return type_handler()->type_handler_for_comparison();
@@ -897,13 +1070,9 @@ public:
{
return type_handler();
}
- virtual const Type_handler *cast_to_int_type_handler() const
- {
- return type_handler();
- }
- virtual const Type_handler *type_handler_for_system_time() const
+ const Type_handler *cast_to_int_type_handler() const
{
- return real_type_handler();
+ return real_type_handler()->cast_to_int_type_handler();
}
/* result_type() of an item specifies how the value should be returned */
Item_result result_type() const
@@ -959,6 +1128,10 @@ public:
return type_handler()->Item_get_cache(thd, this);
}
virtual enum Type type() const =0;
+ bool is_of_type(Type t, Item_result cmp) const
+ {
+ return type() == t && cmp_type() == cmp;
+ }
/*
real_type() is the type of base item. This is same as type() for
most items, except Item_ref() and Item_cache_wrapper() where it
@@ -1023,6 +1196,12 @@ public:
If value is not null null_value flag will be reset to FALSE.
*/
virtual double val_real()=0;
+ Double_null to_double_null()
+ {
+ // val_real() must be caleed on a separate line. See to_longlong_null()
+ double nr= val_real();
+ return Double_null(nr, null_value);
+ }
/*
Return integer representation of item.
@@ -1038,6 +1217,20 @@ public:
{
return Longlong_hybrid(val_int(), unsigned_flag);
}
+ Longlong_null to_longlong_null()
+ {
+ longlong nr= val_int();
+ /*
+ C++ does not guarantee the order of parameter evaluation,
+ so to make sure "null_value" is passed to the constructor
+ after the val_int() call, val_int() is caled on a separate line.
+ */
+ return Longlong_null(nr, null_value);
+ }
+ Longlong_hybrid_null to_longlong_hybrid_null()
+ {
+ return Longlong_hybrid_null(to_longlong_null(), unsigned_flag);
+ }
/**
Get a value for CAST(x AS SIGNED).
Too large positive unsigned integer values are converted
@@ -1059,15 +1252,16 @@ public:
{
return cast_to_int_type_handler()->Item_val_int_unsigned_typecast(this);
}
- longlong val_int_unsigned_typecast_from_decimal();
longlong val_int_unsigned_typecast_from_int();
longlong val_int_unsigned_typecast_from_str();
+ longlong val_int_unsigned_typecast_from_real();
/**
Get a value for CAST(x AS UNSIGNED).
Huge positive unsigned values are converted to negative complements.
*/
longlong val_int_signed_typecast_from_int();
+ longlong val_int_signed_typecast_from_real();
/*
This is just a shortcut to avoid the cast. You should still use
@@ -1106,6 +1300,58 @@ public:
*/
virtual String *val_str(String *str)=0;
+
+ bool val_native_with_conversion(THD *thd, Native *to, const Type_handler *th)
+ {
+ return th->Item_val_native_with_conversion(thd, this, to);
+ }
+ bool val_native_with_conversion_result(THD *thd, Native *to,
+ const Type_handler *th)
+ {
+ return th->Item_val_native_with_conversion_result(thd, this, to);
+ }
+
+ virtual bool val_native(THD *thd, Native *to)
+ {
+ /*
+ The default implementation for the Items that do not need native format:
+ - Item_basic_value (default implementation)
+ - Item_copy
+ - Item_exists_subselect
+ - Item_sum_field
+ - Item_sum_or_func (default implementation)
+ - Item_proc
+ - Item_type_holder (as val_xxx() are never called for it);
+
+ These hybrid Item types override val_native():
+ - Item_field
+ - Item_param
+ - Item_sp_variable
+ - Item_ref
+ - Item_cache_wrapper
+ - Item_direct_ref
+ - Item_direct_view_ref
+ - Item_ref_null_helper
+ - Item_name_const
+ - Item_time_literal
+ - Item_sum_or_func
+ Note, these hybrid type Item_sum_or_func descendants
+ override the default implementation:
+ * Item_sum_hybrid
+ * Item_func_hybrid_field_type
+ * Item_func_min_max
+ * Item_func_sp
+ * Item_func_last_value
+ * Item_func_rollup_const
+ */
+ DBUG_ASSERT(0);
+ return null_value= true;
+ }
+ virtual bool val_native_result(THD *thd, Native *to)
+ {
+ return val_native(thd, to);
+ }
+
/*
Returns string representation of this item in ASCII format.
@@ -1218,7 +1464,7 @@ public:
{
return type_handler()->Item_val_bool(this);
}
- virtual String *val_nodeset(String*) { return 0; }
+ virtual String *val_raw(String*) { return 0; }
bool eval_const_cond()
{
@@ -1240,23 +1486,15 @@ public:
/* Helper functions, see item_sum.cc */
String *val_string_from_real(String *str);
String *val_string_from_int(String *str);
- String *val_string_from_decimal(String *str);
- String *val_string_from_date(String *str);
my_decimal *val_decimal_from_real(my_decimal *decimal_value);
my_decimal *val_decimal_from_int(my_decimal *decimal_value);
my_decimal *val_decimal_from_string(my_decimal *decimal_value);
- my_decimal *val_decimal_from_date(my_decimal *decimal_value);
- my_decimal *val_decimal_from_time(my_decimal *decimal_value);
- longlong val_int_from_decimal();
- longlong val_int_from_date();
longlong val_int_from_real()
{
- DBUG_ASSERT(fixed == 1);
+ DBUG_ASSERT(is_fixed());
return Converter_double_to_longlong_with_warn(val_real(), false).result();
}
longlong val_int_from_str(int *error);
- double val_real_from_decimal();
- double val_real_from_date();
/*
Returns true if this item can be calculated during
@@ -1284,11 +1522,6 @@ public:
return Sql_mode_dependency();
}
- // Get TIME, DATE or DATETIME using proper sql_mode flags for the field type
- bool get_temporal_with_sql_mode(MYSQL_TIME *ltime);
- // Check NULL value for a TIME, DATE or DATETIME expression
- bool is_null_from_temporal();
-
int save_time_in_field(Field *field, bool no_conversions);
int save_date_in_field(Field *field, bool no_conversions);
int save_str_in_field(Field *field, bool no_conversions);
@@ -1348,13 +1581,21 @@ public:
a constant expression. Used in the optimizer to propagate basic constants.
*/
virtual bool basic_const_item() const { return 0; }
- /*
+ /**
Determines if the expression is allowed as
a virtual column assignment source:
INSERT INTO t1 (vcol) VALUES (10) -> error
INSERT INTO t1 (vcol) VALUES (NULL) -> ok
*/
virtual bool vcol_assignment_allowed_value() const { return false; }
+ /**
+ Test if "this" is an ORDER position (rather than an expression).
+ Notes:
+ - can be called before fix_fields().
+ - local SP variables (even of integer types) are always expressions, not
+ positions. (And they can't be used before fix_fields is called for them).
+ */
+ virtual bool is_order_clause_position() const { return false; }
/*
Determines if the Item is an evaluable expression, that is
it can return a value, so we can call methods val_xxx(), get_date(), etc.
@@ -1375,6 +1616,7 @@ public:
}
/* cloning of constant items (0 if it is not const) */
virtual Item *clone_item(THD *thd) { return 0; }
+ /* deep copy item */
virtual Item* build_clone(THD *thd) { return get_copy(thd); }
virtual cond_result eq_cmp_result() const { return COND_OK; }
inline uint float_length(uint decimals_par) const
@@ -1420,14 +1662,14 @@ public:
/**
TIME or DATETIME precision of the item: 0..6
*/
- uint time_precision()
+ uint time_precision(THD *thd)
{
- return const_item() ? type_handler()->Item_time_precision(this) :
+ return const_item() ? type_handler()->Item_time_precision(thd, this) :
MY_MIN(decimals, TIME_SECOND_PART_DIGITS);
}
- uint datetime_precision()
+ uint datetime_precision(THD *thd)
{
- return const_item() ? type_handler()->Item_datetime_precision(this) :
+ return const_item() ? type_handler()->Item_datetime_precision(thd, this) :
MY_MIN(decimals, TIME_SECOND_PART_DIGITS);
}
virtual longlong val_int_min() const
@@ -1537,78 +1779,28 @@ public:
void split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array,
List<Item> &fields,
Item **ref, uint flags);
- virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)= 0;
- bool get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool get_date_from_year(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool get_date_from_decimal(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool get_date_from_string(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool get_time(MYSQL_TIME *ltime)
- { return get_date(ltime, Time::flags_for_get_date()); }
- /*
- Get time with automatic DATE/DATETIME to TIME conversion,
- by subtracting CURRENT_DATE.
-
- Performce a reverse operation to CAST(time AS DATETIME)
- Suppose:
- - we have a set of items (typically with the native MYSQL_TYPE_TIME type)
- whose item->get_date() return TIME1 value, and
- - CAST(AS DATETIME) for the same Items return DATETIME1,
- after applying time-to-datetime conversion to TIME1.
-
- then all items (typically of the native MYSQL_TYPE_{DATE|DATETIME} types)
- whose get_date() return DATETIME1 must also return TIME1 from
- get_time_with_conversion()
-
- @param thd - the thread, its variables.old_mode is checked
- to decide if use simple YYYYMMDD truncation (old mode),
- or perform full DATETIME-to-TIME conversion with
- CURRENT_DATE subtraction.
- @param[out] ltime - store the result here
- @param fuzzydate - flags to be used for the get_date() call.
- Normally, should include TIME_TIME_ONLY, to let
- the called low-level routines, e.g. str_to_date(),
- know that we prefer TIME rather that DATE/DATETIME
- and do less conversion outside of the low-level
- routines.
-
- @returns true - on error, e.g. get_date() returned NULL value,
- or get_date() returned DATETIME/DATE with non-zero
- YYYYMMDD part.
- @returns false - on success
- */
- bool get_time_with_conversion(THD *thd, MYSQL_TIME *ltime,
- ulonglong fuzzydate);
+ virtual bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)= 0;
+ bool get_date_from_int(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool get_date_from_real(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool get_date_from_string(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool get_time(THD *thd, MYSQL_TIME *ltime)
+ { return get_date(thd, ltime, Time::Options(thd)); }
// Get a DATE or DATETIME value in numeric packed format for comparison
- virtual longlong val_datetime_packed()
+ virtual longlong val_datetime_packed(THD *thd)
{
- ulonglong fuzzydate= TIME_FUZZY_DATES | TIME_INVALID_DATES;
- Datetime dt(current_thd, this, fuzzydate);
- return dt.is_valid_datetime() ? pack_time(dt.get_mysql_time()) : 0;
+ return Datetime(thd, this, Datetime::Options_cmp(thd)).to_packed();
}
// Get a TIME value in numeric packed format for comparison
- virtual longlong val_time_packed()
- {
- Time tm(this, Time::comparison_flags_for_get_date());
- return tm.is_valid_time() ? pack_time(tm.get_mysql_time()) : 0;
- }
- longlong val_datetime_packed_result();
- longlong val_time_packed_result()
+ virtual longlong val_time_packed(THD *thd)
{
- MYSQL_TIME ltime;
- ulonglong fuzzydate= Time::comparison_flags_for_get_date();
- return get_date_result(&ltime, fuzzydate) ? 0 : pack_time(&ltime);
+ return Time(thd, this, Time::Options_cmp(thd)).to_packed();
}
+ longlong val_datetime_packed_result(THD *thd);
+ longlong val_time_packed_result(THD *thd);
+
+ virtual bool get_date_result(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ { return get_date(thd, ltime,fuzzydate); }
- // Get a temporal value in packed DATE/DATETIME or TIME format
- longlong val_temporal_packed(enum_field_types f_type)
- {
- return f_type == MYSQL_TYPE_TIME ? val_time_packed() :
- val_datetime_packed();
- }
- bool get_seconds(ulonglong *sec, ulong *sec_part);
- virtual bool get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date(ltime,fuzzydate); }
/*
The method allows to determine nullness of a complex expression
without fully evaluating it, instead of calling val/result*() then
@@ -1623,35 +1815,7 @@ public:
*/
virtual void update_null_value ()
{
- switch (cmp_type()) {
- case INT_RESULT:
- (void) val_int();
- break;
- case REAL_RESULT:
- (void) val_real();
- break;
- case DECIMAL_RESULT:
- {
- my_decimal tmp;
- (void) val_decimal(&tmp);
- }
- break;
- case TIME_RESULT:
- {
- MYSQL_TIME ltime;
- (void) get_temporal_with_sql_mode(&ltime);
- }
- break;
- case STRING_RESULT:
- {
- StringBuffer<MAX_FIELD_WIDTH> tmp;
- (void) val_str(&tmp);
- }
- break;
- case ROW_RESULT:
- DBUG_ASSERT(0);
- null_value= true;
- }
+ return type_handler()->Item_update_null_value(this);
}
/*
@@ -1669,10 +1833,9 @@ public:
set field of temporary table for Item which can be switched on temporary
table during query processing (grouping and so on)
*/
- virtual void set_result_field(Field *field) {}
virtual bool is_result_field() { return 0; }
- virtual bool is_bool_type() { return false; }
virtual bool is_json_type() { return false; }
+ virtual bool is_bool_literal() const { return false; }
/* This is to handle printing of default values */
virtual bool need_parentheses_in_default() { return false; }
virtual void save_in_result_field(bool no_conversions) {}
@@ -1737,8 +1900,11 @@ public:
/*========= Item processors, to be used with Item::walk() ========*/
virtual bool remove_dependence_processor(void *arg) { return 0; }
virtual bool cleanup_processor(void *arg);
- virtual bool cleanup_excluding_fields_processor(void *arg) { return cleanup_processor(arg); }
- virtual bool cleanup_excluding_const_fields_processor(void *arg) { return cleanup_processor(arg); }
+ virtual bool cleanup_excluding_fields_processor (void *arg)
+ { return cleanup_processor(arg); }
+ bool cleanup_excluding_immutables_processor (void *arg);
+ virtual bool cleanup_excluding_const_fields_processor (void *arg)
+ { return cleanup_processor(arg); }
virtual bool collect_item_field_processor(void *arg) { return 0; }
virtual bool collect_outer_ref_processor(void *arg) {return 0; }
virtual bool check_inner_refs_processor(void *arg) { return 0; }
@@ -1787,12 +1953,22 @@ public:
Not to be used for AND/OR formulas.
*/
virtual bool excl_dep_on_table(table_map tab_map) { return false; }
- /*
+ /*
TRUE if the expression depends only on grouping fields of sel
- or can be converted to such an exression using equalities.
+ or can be converted to such an expression using equalities.
+ It also checks if the expression doesn't contain stored procedures,
+ subqueries or randomly generated elements.
Not to be used for AND/OR formulas.
*/
- virtual bool excl_dep_on_grouping_fields(st_select_lex *sel) { return false; }
+ virtual bool excl_dep_on_grouping_fields(st_select_lex *sel)
+ { return false; }
+ /*
+ TRUE if the expression depends only on fields from the left part of
+ IN subquery or can be converted to such an expression using equalities.
+ Not to be used for AND/OR formulas.
+ */
+ virtual bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred)
+ { return false; }
virtual bool switch_to_nullable_fields_processor(void *arg) { return 0; }
virtual bool find_function_processor (void *arg) { return 0; }
@@ -1850,6 +2026,12 @@ public:
virtual bool check_partition_func_processor(void *arg) { return 1;}
virtual bool post_fix_fields_part_expr_processor(void *arg) { return 0; }
virtual bool rename_fields_processor(void *arg) { return 0; }
+ /*
+ TRUE if the function is knowingly TRUE or FALSE.
+ Not to be used for AND/OR formulas.
+ */
+ virtual bool is_simplified_cond_processor(void *arg) { return false; }
+
/** Processor used to check acceptability of an item in the defining
expression for a virtual column
@@ -1883,8 +2065,18 @@ public:
virtual bool check_valid_arguments_processor(void *arg) { return 0; }
virtual bool update_vcol_processor(void *arg) { return 0; }
virtual bool set_fields_as_dependent_processor(void *arg) { return 0; }
+ /*
+ Find if some of the key parts of table keys (the reference on table is
+ passed as an argument) participate in the expression.
+ If there is some, sets a bit for this key in the proper key map.
+ */
+ virtual bool check_index_dependence(void *arg) { return 0; }
/*============== End of Item processor list ======================*/
+ /*
+ Does not guarantee deep copy (depends on copy ctor).
+ See build_clone() for deep copy.
+ */
virtual Item *get_copy(THD *thd)=0;
bool cache_const_expr_analyzer(uchar **arg);
@@ -1961,11 +2153,17 @@ public:
return Type_handler::type_handler_long_or_longlong(max_char_length());
}
- virtual Field *create_tmp_field(bool group, TABLE *table)
- {
- return tmp_table_field_from_field_type(table);
- }
-
+ /**
+ Create field for temporary table.
+ @param table Temporary table
+ @param [OUT] src Who created the fields
+ @param param Create parameters
+ @retval NULL (on error)
+ @retval a pointer to a newly create Field (on success)
+ */
+ virtual Field *create_tmp_field_ex(TABLE *table,
+ Tmp_field_src *src,
+ const Tmp_field_param *param)= 0;
virtual Item_field *field_for_view_update() { return 0; }
virtual Item *neg_transformer(THD *thd) { return NULL; }
@@ -1977,11 +2175,19 @@ public:
{ return this; }
virtual Item *derived_field_transformer_for_where(THD *thd, uchar *arg)
{ return this; }
- virtual Item *derived_grouping_field_transformer_for_where(THD *thd,
- uchar *arg)
+ virtual Item *grouping_field_transformer_for_where(THD *thd, uchar *arg)
+ { return this; }
+ /* Now is not used. */
+ virtual Item *in_subq_field_transformer_for_where(THD *thd, uchar *arg)
+ { return this; }
+ virtual Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg)
{ return this; }
virtual Item *in_predicate_to_in_subs_transformer(THD *thd, uchar *arg)
{ return this; }
+ virtual Item *field_transformer_for_having_pushdown(THD *thd, uchar *arg)
+ { return this; }
+ virtual Item *multiple_equality_transformer(THD *thd, uchar *arg)
+ { return this; }
virtual bool expr_cache_is_needed(THD *) { return FALSE; }
virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs) const
@@ -2032,6 +2238,7 @@ public:
delete this;
}
+ virtual const Item_const *get_item_const() const { return NULL; }
virtual Item_splocal *get_item_splocal() { return 0; }
virtual Rewritable_query_parameter *get_rewritable_query_parameter()
{ return 0; }
@@ -2105,14 +2312,17 @@ public:
/*
Return TRUE if the item points to a column of an outer-joined table.
*/
- virtual bool is_outer_field() const { DBUG_ASSERT(fixed); return FALSE; }
+ virtual bool is_outer_field() const { DBUG_ASSERT(is_fixed()); return FALSE; }
/**
Checks if this item or any of its descendents contains a subquery.
This is a replacement of the former Item::has_subquery() and
Item::with_subselect.
*/
- virtual bool with_subquery() const { DBUG_ASSERT(fixed); return false; }
+ virtual bool with_subquery() const { DBUG_ASSERT(is_fixed()); return false; }
+
+ virtual bool with_sum_func() const { return false; }
+ virtual With_sum_func_cache* get_with_sum_func_cache() { return NULL; }
Item* set_expr_cache(THD *thd);
@@ -2159,6 +2369,11 @@ public:
*/
virtual void under_not(Item_func_not * upper
__attribute__((unused))) {};
+ /*
+ If Item_field is wrapped in Item_direct_wrep remove this Item_direct_ref
+ wrapper.
+ */
+ virtual Item *remove_item_direct_ref() { return this; }
void register_in(THD *thd);
@@ -2176,6 +2391,29 @@ public:
{
marker &= ~EXTRACTION_MASK;
}
+ void check_pushable_cond(Pushdown_checker excl_dep_func, uchar *arg);
+ bool pushable_cond_checker_for_derived(uchar *arg)
+ {
+ return excl_dep_on_table(*((table_map *)arg));
+ }
+ bool pushable_cond_checker_for_subquery(uchar *arg)
+ {
+ return excl_dep_on_in_subq_left_part((Item_in_subselect *)arg);
+ }
+ Item *build_pushable_cond(THD *thd,
+ Pushdown_checker checker,
+ uchar *arg);
+ /*
+ Checks if this item depends only on the arg table
+ */
+ bool pushable_equality_checker_for_derived(uchar *arg)
+ {
+ return (used_tables() == *((table_map *)arg));
+ }
+ /*
+ Checks if this item consists in the left part of arg IN subquery predicate
+ */
+ bool pushable_equality_checker_for_subquery(uchar *arg);
};
MEM_ROOT *get_thd_memroot(THD *thd);
@@ -2190,6 +2428,69 @@ inline Item* get_item_copy (THD *thd, T* item)
}
+#ifndef DBUG_OFF
+/**
+ A helper class to print the data type and the value for an Item
+ in debug builds.
+*/
+class DbugStringItemTypeValue: public StringBuffer<128>
+{
+public:
+ DbugStringItemTypeValue(THD *thd, const Item *item)
+ {
+ append('(');
+ append(item->type_handler()->name().ptr());
+ append(')');
+ const_cast<Item*>(item)->print(this, QT_EXPLAIN);
+ /* Append end \0 to allow usage of c_ptr() */
+ append('\0');
+ str_length--;
+ }
+};
+#endif
+
+class With_sum_func_cache
+{
+protected:
+ bool m_with_sum_func; // True if the owner item contains a sum func
+public:
+ With_sum_func_cache()
+ :m_with_sum_func(false)
+ { }
+ With_sum_func_cache(const Item *a)
+ :m_with_sum_func(a->with_sum_func())
+ { }
+ With_sum_func_cache(const Item *a, const Item *b)
+ :m_with_sum_func(a->with_sum_func() || b->with_sum_func())
+ { }
+ With_sum_func_cache(const Item *a, const Item *b, const Item *c)
+ :m_with_sum_func(a->with_sum_func() || b->with_sum_func() ||
+ c->with_sum_func())
+ { }
+ With_sum_func_cache(const Item *a, const Item *b, const Item *c,
+ const Item *d)
+ :m_with_sum_func(a->with_sum_func() || b->with_sum_func() ||
+ c->with_sum_func() || d->with_sum_func())
+ { }
+ With_sum_func_cache(const Item *a, const Item *b, const Item *c,
+ const Item *d, const Item *e)
+ :m_with_sum_func(a->with_sum_func() || b->with_sum_func() ||
+ c->with_sum_func() || d->with_sum_func() ||
+ e->with_sum_func())
+ { }
+ void set_with_sum_func() { m_with_sum_func= true; }
+ void reset_with_sum_func() { m_with_sum_func= false; }
+ void copy_with_sum_func(const Item *item)
+ {
+ m_with_sum_func= item->with_sum_func();
+ }
+ void join_with_sum_func(const Item *item)
+ {
+ m_with_sum_func|= item->with_sum_func();
+ }
+};
+
+
/*
This class is a replacement for the former member Item::with_subselect.
Determines if the descendant Item is a subselect or some of
@@ -2293,22 +2594,23 @@ protected:
}
return true;
}
- bool excl_dep_on_grouping_fields(st_select_lex *sel)
+ bool excl_dep_on_grouping_fields(st_select_lex *sel);
+ bool eq(const Item_args *other, bool binary_cmp) const
{
- for (uint i= 0; i < arg_count; i++)
+ for (uint i= 0; i < arg_count ; i++)
{
- if (args[i]->const_item())
- continue;
- if (!args[i]->excl_dep_on_grouping_fields(sel))
+ if (!args[i]->eq(other->args[i], binary_cmp))
return false;
}
return true;
}
- bool eq(const Item_args *other, bool binary_cmp) const
+ bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred)
{
- for (uint i= 0; i < arg_count ; i++)
+ for (uint i= 0; i < arg_count; i++)
{
- if (!args[i]->eq(other->args[i], binary_cmp))
+ if (args[i]->const_item())
+ continue;
+ if (!args[i]->excl_dep_on_in_subq_left_part(subq_pred))
return false;
}
return true;
@@ -2364,6 +2666,30 @@ public:
{
args[arg_count++]= item;
}
+ /**
+ Extract row elements from the given position.
+ For example, for this input: (1,2),(3,4),(5,6)
+ pos=0 will extract (1,3,5)
+ pos=1 will extract (2,4,6)
+ @param thd - current thread, to allocate memory on its mem_root
+ @param rows - an array of compatible ROW-type items
+ @param pos - the element position to extract
+ */
+ bool alloc_and_extract_row_elements(THD *thd, const Item_args *rows, uint pos)
+ {
+ DBUG_ASSERT(rows->argument_count() > 0);
+ DBUG_ASSERT(rows->arguments()[0]->cols() > pos);
+ if (alloc_arguments(thd, rows->argument_count()))
+ return true;
+ for (uint i= 0; i < rows->argument_count(); i++)
+ {
+ DBUG_ASSERT(rows->arguments()[0]->cols() == rows->arguments()[i]->cols());
+ Item *arg= rows->arguments()[i]->element_index(pos);
+ add_argument(arg);
+ }
+ DBUG_ASSERT(argument_count() == rows->argument_count());
+ return false;
+ }
inline Item **arguments() const { return args; }
inline uint argument_count() const { return arg_count; }
inline void remove_arguments() { arg_count=0; }
@@ -2399,27 +2725,39 @@ public:
class Item_string;
-/**
- A common class for Item_basic_constant and Item_param
-*/
-class Item_basic_value :public Item
+class Item_fixed_hybrid: public Item
{
- bool is_basic_value(const Item *item, Type type_arg) const
- {
- return item->basic_const_item() && item->type() == type_arg;
- }
- bool is_basic_value(Type type_arg) const
+public:
+ bool fixed; // If item was fixed with fix_fields
+public:
+ Item_fixed_hybrid(THD *thd): Item(thd), fixed(false)
+ { }
+ Item_fixed_hybrid(THD *thd, Item_fixed_hybrid *item)
+ :Item(thd, item), fixed(item->fixed)
+ { }
+ bool fix_fields(THD *thd, Item **ref)
{
- return basic_const_item() && type() == type_arg;
+ DBUG_ASSERT(!fixed);
+ fixed= true;
+ return false;
}
- bool str_eq(const String *value,
- const String *other, CHARSET_INFO *cs, bool binary_cmp) const
+ void cleanup()
{
- return binary_cmp ?
- value->bin_eq(other) :
- collation.collation == cs && value->eq(other, collation.collation);
+ Item::cleanup();
+ fixed= false;
}
+ void quick_fix_field() { fixed= true; }
+ void unfix_fields() { fixed= false; }
+ bool is_fixed() const { return fixed; }
+};
+
+/**
+ A common class for Item_basic_constant and Item_param
+*/
+class Item_basic_value :public Item,
+ public Item_const
+{
protected:
// Value metadata, e.g. to make string processing easier
class Metadata: private MY_STRING_METADATA
@@ -2456,66 +2794,40 @@ protected:
fix_charset_and_length(str.charset(), dv, Metadata(&str));
}
Item_basic_value(THD *thd): Item(thd) {}
- /*
- In the xxx_eq() methods below we need to cast off "const" to
- call val_xxx(). This is OK for Item_basic_constant and Item_param.
- */
- bool null_eq(const Item *item) const
- {
- DBUG_ASSERT(is_basic_value(NULL_ITEM));
- return item->type() == NULL_ITEM;
- }
- bool str_eq(const String *value, const Item *item, bool binary_cmp) const
- {
- DBUG_ASSERT(is_basic_value(STRING_ITEM));
- return is_basic_value(item, STRING_ITEM) &&
- str_eq(value, ((Item_basic_value*)item)->val_str(NULL),
- item->collation.collation, binary_cmp);
- }
- bool real_eq(double value, const Item *item) const
- {
- DBUG_ASSERT(is_basic_value(REAL_ITEM));
- return is_basic_value(item, REAL_ITEM) &&
- value == ((Item_basic_value*)item)->val_real();
- }
- bool int_eq(longlong value, const Item *item) const
+public:
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
{
- DBUG_ASSERT(is_basic_value(INT_ITEM));
- return is_basic_value(item, INT_ITEM) &&
- value == ((Item_basic_value*)item)->val_int() &&
- (value >= 0 || item->unsigned_flag == unsigned_flag);
+
+ /*
+ create_tmp_field_ex() for this type of Items is called for:
+ - CREATE TABLE ... SELECT
+ - In ORDER BY: SELECT max(a) FROM t1 GROUP BY a ORDER BY 'const';
+ - In CURSORS:
+ DECLARE c CURSOR FOR SELECT 'test';
+ OPEN c;
+ */
+ return tmp_table_field_from_field_type_maybe_null(table, src, param,
+ type() == Item::NULL_ITEM);
}
+ bool eq(const Item *item, bool binary_cmp) const;
+ const Type_all_attributes *get_type_all_attributes_from_const() const
+ { return this; }
};
class Item_basic_constant :public Item_basic_value
{
- table_map used_table_map;
public:
- Item_basic_constant(THD *thd): Item_basic_value(thd), used_table_map(0) {};
- void set_used_tables(table_map map) { used_table_map= map; }
- table_map used_tables() const { return used_table_map; }
- bool check_vcol_func_processor(void *arg) { return FALSE;}
+ Item_basic_constant(THD *thd): Item_basic_value(thd) {};
+ bool check_vcol_func_processor(void *arg) { return false; }
+ const Item_const *get_item_const() const { return this; }
virtual Item_basic_constant *make_string_literal_concat(THD *thd,
const LEX_CSTRING *)
{
DBUG_ASSERT(0);
return this;
}
- /* to prevent drop fixed flag (no need parent cleanup call) */
- void cleanup()
- {
- /*
- Restore the original field name as it might not have been allocated
- in the statement memory. If the name is auto generated, it must be
- done again between subsequent executions of a prepared statement.
- */
- if (orig_name)
- {
- name.str= orig_name;
- name.length= strlen(orig_name);
- }
- }
};
@@ -2526,7 +2838,7 @@ public:
- CASE expression (Item_case_expr);
*****************************************************************************/
-class Item_sp_variable :public Item
+class Item_sp_variable :public Item_fixed_hybrid
{
protected:
/*
@@ -2558,7 +2870,8 @@ public:
longlong val_int();
String *val_str(String *sp);
my_decimal *val_decimal(my_decimal *decimal_value);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool val_native(THD *thd, Native *to);
bool is_null();
public:
@@ -2566,6 +2879,11 @@ public:
inline bool const_item() const;
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ return create_tmp_field_ex_simple(table, src, param);
+ }
inline int save_in_field(Field *field, bool no_conversions);
inline bool send(Protocol *protocol, st_value *buffer);
bool check_vcol_func_processor(void *arg)
@@ -2669,6 +2987,20 @@ public:
*/
Field *create_field_for_create_select(TABLE *table)
{ return create_table_field_from_handler(table); }
+
+ bool is_valid_limit_clause_variable_with_error() const
+ {
+ /*
+ In case if the variable has an anchored data type, e.g.:
+ DECLARE a TYPE OF t1.a;
+ type_handler() is set to &type_handler_null and this
+ function detects such variable as not valid in LIMIT.
+ */
+ if (type_handler()->is_limit_clause_valid_type())
+ return true;
+ my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0));
+ return false;
+ }
};
@@ -2815,11 +3147,10 @@ inline enum Item::Type Item_case_expr::type() const
extract a common base with class Item_ref, too.
*/
-class Item_name_const : public Item
+class Item_name_const : public Item_fixed_hybrid
{
Item *value_item;
Item *name_item;
- bool valid_args;
public:
Item_name_const(THD *thd, Item *name_arg, Item *val);
@@ -2830,7 +3161,8 @@ public:
longlong val_int();
String *val_str(String *sp);
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool val_native(THD *thd, Native *to);
bool is_null();
virtual void print(String *str, enum_query_type query_type);
@@ -2844,6 +3176,17 @@ public:
return TRUE;
}
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ /*
+ We can get to here when using a CURSOR for a query with NAME_CONST():
+ DECLARE c CURSOR FOR SELECT NAME_CONST('x','y') FROM t1;
+ OPEN c;
+ */
+ return tmp_table_field_from_field_type_maybe_null(table, src, param,
+ type() == Item::NULL_ITEM);
+ }
int save_in_field(Field *field, bool no_conversions)
{
return value_item->save_in_field(field, no_conversions);
@@ -2861,15 +3204,27 @@ public:
{ return get_item_copy<Item_name_const>(thd, this); }
};
-class Item_num: public Item_basic_constant
+
+class Item_literal: public Item_basic_constant
+{
+public:
+ Item_literal(THD *thd): Item_basic_constant(thd)
+ { }
+ enum Type type() const { return CONST_ITEM; }
+ bool check_partition_func_processor(void *int_arg) { return false;}
+ bool const_item() const { return true; }
+ bool basic_const_item() const { return true; }
+};
+
+
+class Item_num: public Item_literal
{
public:
- Item_num(THD *thd): Item_basic_constant(thd) { collation.set_numeric(); }
+ Item_num(THD *thd): Item_literal(thd) { collation.set_numeric(); }
Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
- bool check_partition_func_processor(void *int_arg) { return FALSE;}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate);
}
};
@@ -2878,24 +3233,26 @@ public:
class st_select_lex;
-class Item_result_field :public Item /* Item with result field */
+class Item_result_field :public Item_fixed_hybrid /* Item with result field */
{
public:
Field *result_field; /* Save result here */
- Item_result_field(THD *thd): Item(thd), result_field(0) {}
+ Item_result_field(THD *thd): Item_fixed_hybrid(thd), result_field(0) {}
// Constructor used for Item_sum/Item_cond_and/or (see Item comment)
Item_result_field(THD *thd, Item_result_field *item):
- Item(thd, item), result_field(item->result_field)
+ Item_fixed_hybrid(thd, item), result_field(item->result_field)
{}
~Item_result_field() {} /* Required with gcc 2.95 */
Field *get_tmp_table_field() { return result_field; }
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param);
+ void get_tmp_field_src(Tmp_field_src *src, const Tmp_field_param *param);
/*
This implementation of used_tables() used by Item_avg_field and
Item_variance_field which work when only temporary table left, so theu
return table map of the temporary table.
*/
table_map used_tables() const { return 1; }
- void set_result_field(Field *field) { result_field= field; }
bool is_result_field() { return true; }
void save_in_result_field(bool no_conversions)
{
@@ -2974,39 +3331,6 @@ public:
};
-class Item_ident_for_show :public Item
-{
-public:
- Field *field;
- const char *db_name;
- const char *table_name;
-
- Item_ident_for_show(THD *thd, Field *par_field, const char *db_arg,
- const char *table_name_arg):
- Item(thd), field(par_field), db_name(db_arg), table_name(table_name_arg)
- {
- Type_std_attributes::set(par_field->type_std_attributes());
- }
- enum Type type() const { return FIELD_ITEM; }
- double val_real() { return field->val_real(); }
- longlong val_int() { return field->val_int(); }
- String *val_str(String *str) { return field->val_str(str); }
- my_decimal *val_decimal(my_decimal *dec) { return field->val_decimal(dec); }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- {
- return field->get_date(ltime, fuzzydate);
- }
- void make_send_field(THD *thd, Send_field *tmp_field);
- const Type_handler *type_handler() const
- {
- const Type_handler *handler= field->type_handler();
- return handler->type_handler_for_item_field();
- }
- Item* get_copy(THD *thd)
- { return get_item_copy<Item_ident_for_show>(thd, this); }
-};
-
-
class Item_field :public Item_ident,
public Load_data_outvar
{
@@ -3050,6 +3374,8 @@ public:
void save_result(Field *to);
double val_result();
longlong val_int_result();
+ bool val_native(THD *thd, Native *to);
+ bool val_native_result(THD *thd, Native *to);
String *str_result(String* tmp);
my_decimal *val_decimal_result(my_decimal *);
bool val_bool_result();
@@ -3093,16 +3419,17 @@ public:
const Type_handler *handler= field->type_handler();
return handler->type_handler_for_item_field();
}
- const Type_handler *cast_to_int_type_handler() const
- {
- return field->type_handler()->cast_to_int_type_handler();
- }
const Type_handler *real_type_handler() const
{
if (field->is_created_from_null_item)
return &type_handler_null;
return field->type_handler();
}
+ Field *create_tmp_field_from_item_field(TABLE *new_table,
+ Item_ref *orig_item,
+ const Tmp_field_param *param);
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param);
TYPELIB *get_typelib() const { return field->get_typelib(); }
enum_monotonicity_info get_monotonicity_info() const
{
@@ -3113,8 +3440,10 @@ public:
return Sql_mode_dependency(0, field->value_depends_on_sql_mode());
}
longlong val_int_endpoint(bool left_endp, bool *incl_endp);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool get_date_result(MYSQL_TIME *ltime,ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool get_date_result(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate);
+ longlong val_datetime_packed(THD *thd);
+ longlong val_time_packed(THD *thd);
bool is_null() { return field->is_null(); }
void update_null_value();
void update_table_bitmaps()
@@ -3124,13 +3453,7 @@ public:
TABLE *tab= field->table;
tab->covering_keys.intersect(field->part_of_key);
if (tab->read_set)
- bitmap_fast_test_and_set(tab->read_set, field->field_index);
- /*
- Do not mark a self-referecing virtual column.
- Such virtual columns are reported as invalid.
- */
- if (field->vcol_info && tab->vcol_set)
- tab->mark_virtual_col(field);
+ tab->mark_column_with_deps(field);
}
}
void update_used_tables()
@@ -3225,10 +3548,13 @@ public:
virtual Item *update_value_transformer(THD *thd, uchar *select_arg);
Item *derived_field_transformer_for_having(THD *thd, uchar *arg);
Item *derived_field_transformer_for_where(THD *thd, uchar *arg);
- Item *derived_grouping_field_transformer_for_where(THD *thd, uchar *arg);
+ Item *grouping_field_transformer_for_where(THD *thd, uchar *arg);
+ Item *in_subq_field_transformer_for_where(THD *thd, uchar *arg);
+ Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg);
virtual void print(String *str, enum_query_type query_type);
bool excl_dep_on_table(table_map tab_map);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
+ bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred);
bool cleanup_excluding_fields_processor(void *arg)
{ return field ? 0 : cleanup_processor(arg); }
bool cleanup_excluding_const_fields_processor(void *arg)
@@ -3246,6 +3572,7 @@ public:
DBUG_ASSERT(field_type() == MYSQL_TYPE_GEOMETRY);
return field->get_geometry_type();
}
+ bool check_index_dependence(void *arg);
friend class Item_default_value;
friend class Item_insert_value;
friend class st_select_lex_unit;
@@ -3333,25 +3660,24 @@ public:
max_length= 0;
name.str= name_par ? name_par : "NULL";
name.length= strlen(name.str);
- fixed= 1;
collation.set(cs, DERIVATION_IGNORABLE, MY_REPERTOIRE_ASCII);
}
enum Type type() const { return NULL_ITEM; }
bool vcol_assignment_allowed_value() const { return true; }
- bool eq(const Item *item, bool binary_cmp) const { return null_eq(item); }
double val_real();
longlong val_int();
String *val_str(String *str);
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
- longlong val_datetime_packed();
- longlong val_time_packed();
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ longlong val_datetime_packed(THD *);
+ longlong val_time_packed(THD *);
int save_in_field(Field *field, bool no_conversions);
int save_safe_in_field(Field *field);
bool send(Protocol *protocol, st_value *buffer);
const Type_handler *type_handler() const { return &type_handler_null; }
bool basic_const_item() const { return 1; }
Item *clone_item(THD *thd);
+ bool const_is_null() const { return true; }
bool is_null() { return 1; }
virtual inline void print(String *str, enum_query_type query_type)
@@ -3373,9 +3699,17 @@ public:
Field *result_field;
Item_null_result(THD *thd): Item_null(thd), result_field(0) {}
bool is_result_field() { return result_field != 0; }
- enum_field_types field_type() const
+ const Type_handler *type_handler() const
+ {
+ if (result_field)
+ return result_field->type_handler();
+ return &type_handler_null;
+ }
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
{
- return result_field->type();
+ DBUG_ASSERT(0);
+ return NULL;
}
void save_in_result_field(bool no_conversions)
{
@@ -3444,8 +3778,8 @@ class Item_param :public Item_basic_value,
All Item_param::set_xxx() make sure to do so.
In the state with an assigned value:
- Item_param::basic_const_item() returns true
- - Item::type() returns NULL_ITEM, INT_ITEM, REAL_ITEM, DECIMAL_ITEM,
- DATE_ITEM, STRING_ITEM, depending on the value assigned.
+ - Item::type() returns NULL_ITEM or CONST_ITEM,
+ depending on the value assigned.
So in this state Item_param behaves in many cases like a literal.
When Item_param::cleanup() is called:
@@ -3468,14 +3802,6 @@ class Item_param :public Item_basic_value,
DEFAULT_VALUE, IGNORE_VALUE
} state;
- enum Type item_type;
-
- void fix_type(Type type)
- {
- item_type= type;
- fixed= true;
- }
-
void fix_temporal(uint32 max_length_arg, uint decimals_arg);
struct CONVERSION_INFO
@@ -3574,7 +3900,6 @@ class Item_param :public Item_basic_value,
PValue value;
const String *value_query_val_str(THD *thd, String* str) const;
- bool value_eq(const Item *item, bool binary_cmp) const;
Item *value_clone_item(THD *thd);
bool is_evaluable_expression() const;
bool can_return_value() const;
@@ -3614,9 +3939,58 @@ public:
enum Type type() const
{
- return item_type;
+ // Don't pretend to be a constant unless value for this item is set.
+ switch (state) {
+ case NO_VALUE: return PARAM_ITEM;
+ case NULL_VALUE: return NULL_ITEM;
+ case SHORT_DATA_VALUE: return CONST_ITEM;
+ case LONG_DATA_VALUE: return CONST_ITEM;
+ case DEFAULT_VALUE: return PARAM_ITEM;
+ case IGNORE_VALUE: return PARAM_ITEM;
+ }
+ DBUG_ASSERT(0);
+ return PARAM_ITEM;
}
+ bool is_order_clause_position() const
+ {
+ return state == SHORT_DATA_VALUE &&
+ type_handler()->is_order_clause_position_type();
+ }
+
+ const Item_const *get_item_const() const
+ {
+ switch (state) {
+ case SHORT_DATA_VALUE:
+ case LONG_DATA_VALUE:
+ case NULL_VALUE:
+ return this;
+ case IGNORE_VALUE:
+ case DEFAULT_VALUE:
+ case NO_VALUE:
+ break;
+ }
+ return NULL;
+ }
+
+ bool const_is_null() const { return state == NULL_VALUE; }
+ bool can_return_const_value(Item_result type) const
+ {
+ return can_return_value() &&
+ value.type_handler()->cmp_type() == type &&
+ type_handler()->cmp_type() == type;
+ }
+ const longlong *const_ptr_longlong() const
+ { return can_return_const_value(INT_RESULT) ? &value.integer : NULL; }
+ const double *const_ptr_double() const
+ { return can_return_const_value(REAL_RESULT) ? &value.real : NULL; }
+ const my_decimal *const_ptr_my_decimal() const
+ { return can_return_const_value(DECIMAL_RESULT) ? &value.m_decimal : NULL; }
+ const MYSQL_TIME *const_ptr_mysql_time() const
+ { return can_return_const_value(TIME_RESULT) ? &value.time : NULL; }
+ const String *const_ptr_string() const
+ { return can_return_const_value(STRING_RESULT) ? &value.m_string : NULL; }
+
double val_real()
{
return can_return_value() ? value.val_real() : 0e0;
@@ -3633,7 +4007,12 @@ public:
{
return can_return_value() ? value.val_str(str, this) : NULL;
}
- bool get_date(MYSQL_TIME *tm, ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *tm, date_mode_t fuzzydate);
+ bool val_native(THD *thd, Native *to)
+ {
+ return Item_param::type_handler()->Item_param_val_native(thd, this, to);
+ }
+
int save_in_field(Field *field, bool no_conversions);
void set_default();
@@ -3710,8 +4089,14 @@ public:
so no one will use parameters value in fix_fields still
parameter is constant during execution.
*/
+ bool const_item() const
+ {
+ return state != NO_VALUE;
+ }
virtual table_map used_tables() const
- { return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; }
+ {
+ return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT;
+ }
virtual void print(String *str, enum_query_type query_type);
bool is_null()
{ DBUG_ASSERT(state != NO_VALUE); return state == NULL_VALUE; }
@@ -3741,12 +4126,6 @@ public:
*/
Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
Item *clone_item(THD *thd);
- /*
- Implement by-value equality evaluation if parameter value
- is set and is a basic constant (integer, real or string).
- Otherwise return FALSE.
- */
- bool eq(const Item *item, bool binary_cmp) const;
void set_param_type_and_swap_value(Item_param *from);
Rewritable_query_parameter *get_rewritable_query_parameter()
@@ -3794,50 +4173,44 @@ public:
longlong value;
Item_int(THD *thd, int32 i,size_t length= MY_INT32_NUM_DECIMAL_DIGITS):
Item_num(thd), value((longlong) i)
- { max_length=(uint32)length; fixed= 1; }
+ { max_length=(uint32)length; }
Item_int(THD *thd, longlong i,size_t length= MY_INT64_NUM_DECIMAL_DIGITS):
Item_num(thd), value(i)
- { max_length=(uint32)length; fixed= 1; }
+ { max_length=(uint32)length; }
Item_int(THD *thd, ulonglong i, size_t length= MY_INT64_NUM_DECIMAL_DIGITS):
Item_num(thd), value((longlong)i)
- { max_length=(uint32)length; fixed= 1; unsigned_flag= 1; }
+ { max_length=(uint32)length; unsigned_flag= 1; }
Item_int(THD *thd, const char *str_arg,longlong i,size_t length):
Item_num(thd), value(i)
{
max_length=(uint32)length;
name.str= str_arg; name.length= safe_strlen(name.str);
- fixed= 1;
}
Item_int(THD *thd, const char *str_arg,longlong i,size_t length, bool flag):
Item_num(thd), value(i)
{
max_length=(uint32)length;
name.str= str_arg; name.length= safe_strlen(name.str);
- fixed= 1;
unsigned_flag= flag;
}
Item_int(THD *thd, const char *str_arg, size_t length=64);
- enum Type type() const { return INT_ITEM; }
const Type_handler *type_handler() const
{ return type_handler_long_or_longlong(); }
- Field *create_tmp_field(bool group, TABLE *table)
- { return tmp_table_field_from_field_type(table); }
Field *create_field_for_create_select(TABLE *table)
{ return tmp_table_field_from_field_type(table); }
- longlong val_int() { DBUG_ASSERT(fixed == 1); return value; }
- longlong val_int_min() const { DBUG_ASSERT(fixed == 1); return value; }
- double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; }
+ const longlong *const_ptr_longlong() const { return &value; }
+ longlong val_int() { return value; }
+ longlong val_int_min() const { return value; }
+ double val_real() { return (double) value; }
my_decimal *val_decimal(my_decimal *);
String *val_str(String*);
int save_in_field(Field *field, bool no_conversions);
- bool basic_const_item() const { return 1; }
+ bool is_order_clause_position() const { return true; }
Item *clone_item(THD *thd);
virtual void print(String *str, enum_query_type query_type);
Item *neg(THD *thd);
uint decimal_precision() const
{ return (uint) (max_length - MY_TEST(value < 0)); }
- bool eq(const Item *item, bool binary_cmp) const
- { return int_eq(value, item); }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_int>(thd, this); }
};
@@ -3853,8 +4226,20 @@ class Item_bool :public Item_int
public:
Item_bool(THD *thd, const char *str_arg, longlong i):
Item_int(thd, str_arg, i, 1) {}
- bool is_bool_type() { return true; }
+ Item_bool(THD *thd, bool i) :Item_int(thd, (longlong) i, 1) { }
+ bool is_bool_literal() const { return true; }
Item *neg_transformer(THD *thd);
+ const Type_handler *type_handler() const
+ { return &type_handler_bool; }
+ const Type_handler *fixed_type_handler() const
+ { return &type_handler_bool; }
+ void quick_fix_field()
+ {
+ /*
+ We can get here when Item_bool is created instead of a constant
+ predicate at various condition optimization stages in sql_select.
+ */
+ }
};
@@ -3864,8 +4249,7 @@ public:
Item_uint(THD *thd, const char *str_arg, size_t length);
Item_uint(THD *thd, ulonglong i): Item_int(thd, i, 10) {}
Item_uint(THD *thd, const char *str_arg, longlong i, uint length);
- double val_real()
- { DBUG_ASSERT(fixed == 1); return ulonglong2double((ulonglong)value); }
+ double val_real() { return ulonglong2double((ulonglong)value); }
String *val_str(String*);
Item *clone_item(THD *thd);
virtual void print(String *str, enum_query_type query_type);
@@ -3886,7 +4270,7 @@ public:
longlong val_int();
double val_real() { return (double)val_int(); }
void set(longlong packed, enum_mysql_timestamp_type ts_type);
- bool get_date(MYSQL_TIME *to, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate)
{
*to= ltime;
return false;
@@ -3904,24 +4288,26 @@ public:
CHARSET_INFO *charset);
Item_decimal(THD *thd, const char *str, const my_decimal *val_arg,
uint decimal_par, uint length);
- Item_decimal(THD *thd, my_decimal *value_par);
+ Item_decimal(THD *thd, const my_decimal *value_par);
Item_decimal(THD *thd, longlong val, bool unsig);
Item_decimal(THD *thd, double val, int precision, int scale);
Item_decimal(THD *thd, const uchar *bin, int precision, int scale);
- enum Type type() const { return DECIMAL_ITEM; }
const Type_handler *type_handler() const { return &type_handler_newdecimal; }
- longlong val_int();
- double val_real();
- String *val_str(String*);
+ longlong val_int() { return decimal_value.to_longlong(unsigned_flag); }
+ double val_real() { return decimal_value.to_double(); }
+ String *val_str(String *to) { return decimal_value.to_string(to); }
my_decimal *val_decimal(my_decimal *val) { return &decimal_value; }
+ const my_decimal *const_ptr_my_decimal() const { return &decimal_value; }
int save_in_field(Field *field, bool no_conversions);
- bool basic_const_item() const { return 1; }
Item *clone_item(THD *thd);
- virtual void print(String *str, enum_query_type query_type);
+ virtual void print(String *str, enum_query_type query_type)
+ {
+ decimal_value.to_string(&str_value);
+ str->append(str_value);
+ }
Item *neg(THD *thd);
uint decimal_precision() const { return decimal_value.precision(); }
- bool eq(const Item *, bool binary_cmp) const;
void set_decimal_value(my_decimal *value_par);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_decimal>(thd, this); }
@@ -3941,21 +4327,18 @@ public:
name.length= safe_strlen(str);
decimals=(uint8) decimal_par;
max_length= length;
- fixed= 1;
}
Item_float(THD *thd, double value_par, uint decimal_par):
Item_num(thd), presentation(0), value(value_par)
{
decimals= (uint8) decimal_par;
- fixed= 1;
}
int save_in_field(Field *field, bool no_conversions);
- enum Type type() const { return REAL_ITEM; }
const Type_handler *type_handler() const { return &type_handler_double; }
- double val_real() { DBUG_ASSERT(fixed == 1); return value; }
+ const double *const_ptr_double() const { return &value; }
+ double val_real() { return value; }
longlong val_int()
{
- DBUG_ASSERT(fixed == 1);
if (value <= (double) LONGLONG_MIN)
{
return LONGLONG_MIN;
@@ -3968,12 +4351,9 @@ public:
}
String *val_str(String*);
my_decimal *val_decimal(my_decimal *);
- bool basic_const_item() const { return 1; }
Item *clone_item(THD *thd);
Item *neg(THD *thd);
virtual void print(String *str, enum_query_type query_type);
- bool eq(const Item *item, bool binary_cmp) const
- { return real_eq(value, item); }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_float>(thd, this); }
};
@@ -4000,14 +4380,12 @@ public:
};
-class Item_string :public Item_basic_constant
+class Item_string :public Item_literal
{
protected:
void fix_from_value(Derivation dv, const Metadata metadata)
{
fix_charset_and_length(str_value.charset(), dv, metadata);
- // it is constant => can be used without fix_fields (and frequently used)
- fixed= 1;
}
void fix_and_set_name_from_value(THD *thd, Derivation dv,
const Metadata metadata)
@@ -4018,41 +4396,41 @@ protected:
protected:
/* Just create an item and do not fill string representation */
Item_string(THD *thd, CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE):
- Item_basic_constant(thd)
+ Item_literal(thd)
{
collation.set(cs, dv);
max_length= 0;
set_name(thd, NULL, 0, system_charset_info);
decimals= NOT_FIXED_DEC;
- fixed= 1;
}
public:
- Item_string(THD *thd, CHARSET_INFO *csi, const char *str_arg, uint length_arg):
- Item_basic_constant(thd)
+ Item_string(THD *thd, CHARSET_INFO *csi, const char *str_arg, uint length_arg)
+ :Item_literal(thd)
{
collation.set(csi, DERIVATION_COERCIBLE);
set_name(thd, NULL, 0, system_charset_info);
decimals= NOT_FIXED_DEC;
- fixed= 1;
str_value.copy(str_arg, length_arg, csi);
max_length= str_value.numchars() * csi->mbmaxlen;
}
// Constructors with the item name set from its value
Item_string(THD *thd, const char *str, uint length, CHARSET_INFO *cs,
- Derivation dv, uint repertoire): Item_basic_constant(thd)
+ Derivation dv, uint repertoire)
+ :Item_literal(thd)
{
str_value.set_or_copy_aligned(str, length, cs);
fix_and_set_name_from_value(thd, dv, Metadata(&str_value, repertoire));
}
Item_string(THD *thd, const char *str, size_t length,
- CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE):
- Item_basic_constant(thd)
+ CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE)
+ :Item_literal(thd)
{
str_value.set_or_copy_aligned(str, length, cs);
fix_and_set_name_from_value(thd, dv, Metadata(&str_value));
}
Item_string(THD *thd, const String *str, CHARSET_INFO *tocs, uint *conv_errors,
- Derivation dv, uint repertoire): Item_basic_constant(thd)
+ Derivation dv, uint repertoire)
+ :Item_literal(thd)
{
if (str_value.copy(str, tocs, conv_errors))
str_value.set("", 0, tocs); // EOM ?
@@ -4061,16 +4439,16 @@ public:
}
// Constructors with an externally provided item name
Item_string(THD *thd, const char *name_par, const char *str, size_t length,
- CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE):
- Item_basic_constant(thd)
+ CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE)
+ :Item_literal(thd)
{
str_value.set_or_copy_aligned(str, length, cs);
fix_from_value(dv, Metadata(&str_value));
set_name(thd, name_par,safe_strlen(name_par), system_charset_info);
}
Item_string(THD *thd, const char *name_par, const char *str, size_t length,
- CHARSET_INFO *cs, Derivation dv, uint repertoire):
- Item_basic_constant(thd)
+ CHARSET_INFO *cs, Derivation dv, uint repertoire)
+ :Item_literal(thd)
{
str_value.set_or_copy_aligned(str, length, cs);
fix_from_value(dv, Metadata(&str_value, repertoire));
@@ -4080,26 +4458,23 @@ public:
{
str_value.print(to);
}
- enum Type type() const { return STRING_ITEM; }
double val_real();
longlong val_int();
+ const String *const_ptr_string() const
+ {
+ return &str_value;
+ }
String *val_str(String*)
{
- DBUG_ASSERT(fixed == 1);
return (String*) &str_value;
}
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return get_date_from_string(ltime, fuzzydate);
+ return get_date_from_string(thd, ltime, fuzzydate);
}
int save_in_field(Field *field, bool no_conversions);
const Type_handler *type_handler() const { return &type_handler_varchar; }
- bool basic_const_item() const { return 1; }
- bool eq(const Item *item, bool binary_cmp) const
- {
- return str_eq(&str_value, item, binary_cmp);
- }
Item *clone_item(THD *thd);
Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
{
@@ -4111,7 +4486,6 @@ public:
max_length= str_value.numchars() * collation.collation->mbmaxlen;
}
virtual void print(String *str, enum_query_type query_type);
- bool check_partition_func_processor(void *int_arg) {return FALSE;}
/**
Return TRUE if character-set-introducer was explicitly specified in the
@@ -4140,34 +4514,6 @@ public:
String *check_well_formed_result(bool send_error)
{ return Item::check_well_formed_result(&str_value, send_error); }
- enum_field_types odbc_temporal_literal_type(const LEX_CSTRING *type_str) const
- {
- /*
- If string is a reasonably short pure ASCII string literal,
- try to parse known ODBC style date, time or timestamp literals,
- e.g:
- SELECT {d'2001-01-01'};
- SELECT {t'10:20:30'};
- SELECT {ts'2001-01-01 10:20:30'};
- */
- if (collation.repertoire == MY_REPERTOIRE_ASCII &&
- str_value.length() < MAX_DATE_STRING_REP_LENGTH * 4)
- {
- if (type_str->length == 1)
- {
- if (type_str->str[0] == 'd') /* {d'2001-01-01'} */
- return MYSQL_TYPE_DATE;
- else if (type_str->str[0] == 't') /* {t'10:20:30'} */
- return MYSQL_TYPE_TIME;
- }
- else if (type_str->length == 2) /* {ts'2001-01-01 10:20:30'} */
- {
- if (type_str->str[0] == 't' && type_str->str[1] == 's')
- return MYSQL_TYPE_DATETIME;
- }
- }
- return MYSQL_TYPE_STRING; // Not a temporal literal
- }
Item_basic_constant *make_string_literal_concat(THD *thd,
const LEX_CSTRING *);
Item *make_odbc_literal(THD *thd, const LEX_CSTRING *typestr);
@@ -4354,38 +4700,30 @@ public:
/**
Item_hex_constant -- a common class for hex literals: X'HHHH' and 0xHHHH
*/
-class Item_hex_constant: public Item_basic_constant
+class Item_hex_constant: public Item_literal
{
private:
void hex_string_init(THD *thd, const char *str, size_t str_length);
public:
- Item_hex_constant(THD *thd): Item_basic_constant(thd)
+ Item_hex_constant(THD *thd): Item_literal(thd)
{
hex_string_init(thd, "", 0);
}
Item_hex_constant(THD *thd, const char *str, size_t str_length):
- Item_basic_constant(thd)
+ Item_literal(thd)
{
hex_string_init(thd, str, str_length);
}
- enum Type type() const { return VARBIN_ITEM; }
const Type_handler *type_handler() const { return &type_handler_varchar; }
virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
{
return const_charset_converter(thd, tocs, true);
}
- bool check_partition_func_processor(void *int_arg) {return FALSE;}
- bool basic_const_item() const { return 1; }
- bool eq(const Item *item, bool binary_cmp) const
+ const String *const_ptr_string() const { return &str_value; }
+ String *val_str(String*) { return &str_value; }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return item->basic_const_item() && item->type() == type() &&
- item->cast_to_int_type_handler() == cast_to_int_type_handler() &&
- str_value.bin_eq(&((Item_hex_constant*)item)->str_value);
- }
- String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- {
- return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate);
}
};
@@ -4401,22 +4739,18 @@ public:
Item_hex_hybrid(THD *thd): Item_hex_constant(thd) {}
Item_hex_hybrid(THD *thd, const char *str, size_t str_length):
Item_hex_constant(thd, str, str_length) {}
+ const Type_handler *type_handler() const { return &type_handler_hex_hybrid; }
uint decimal_precision() const;
double val_real()
{
- DBUG_ASSERT(fixed == 1);
return (double) (ulonglong) Item_hex_hybrid::val_int();
}
longlong val_int()
{
- // following assert is redundant, because fixed=1 assigned in constructor
- DBUG_ASSERT(fixed == 1);
return longlong_from_hex_hybrid(str_value.ptr(), str_value.length());
}
my_decimal *val_decimal(my_decimal *decimal_value)
{
- // following assert is redundant, because fixed=1 assigned in constructor
- DBUG_ASSERT(fixed == 1);
longlong value= Item_hex_hybrid::val_int();
int2my_decimal(E_DEC_FATAL_ERROR, value, TRUE, decimal_value);
return decimal_value;
@@ -4426,14 +4760,6 @@ public:
field->set_notnull();
return field->store_hex_hybrid(str_value.ptr(), str_value.length());
}
- const Type_handler *cast_to_int_type_handler() const
- {
- return &type_handler_longlong;
- }
- const Type_handler *type_handler_for_system_time() const
- {
- return &type_handler_longlong;
- }
void print(String *str, enum_query_type query_type);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_hex_hybrid>(thd, this); }
@@ -4457,12 +4783,10 @@ public:
Item_hex_constant(thd, str, str_length) {}
longlong val_int()
{
- DBUG_ASSERT(fixed == 1);
return longlong_from_string_with_check(&str_value);
}
double val_real()
{
- DBUG_ASSERT(fixed == 1);
return double_from_string_with_check(&str_value);
}
my_decimal *val_decimal(my_decimal *decimal_value)
@@ -4488,47 +4812,70 @@ public:
};
-class Item_temporal_literal :public Item_basic_constant
+class Item_timestamp_literal: public Item_literal
{
-protected:
- MYSQL_TIME cached_time;
+ Timestamp_or_zero_datetime m_value;
public:
- /**
- Constructor for Item_date_literal.
- @param ltime DATE value.
- */
- Item_temporal_literal(THD *thd, const MYSQL_TIME *ltime)
- :Item_basic_constant(thd)
+ Item_timestamp_literal(THD *thd)
+ :Item_literal(thd)
+ { }
+ const Type_handler *type_handler() const { return &type_handler_timestamp2; }
+ int save_in_field(Field *field, bool no_conversions)
+ {
+ Timestamp_or_zero_datetime_native native(m_value, decimals);
+ return native.save_in_field(field, decimals);
+ }
+ longlong val_int()
+ {
+ return m_value.to_datetime(current_thd).to_longlong();
+ }
+ double val_real()
+ {
+ return m_value.to_datetime(current_thd).to_double();
+ }
+ String *val_str(String *to)
+ {
+ return m_value.to_datetime(current_thd).to_string(to, decimals);
+ }
+ my_decimal *val_decimal(my_decimal *to)
+ {
+ return m_value.to_datetime(current_thd).to_decimal(to);
+ }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ {
+ bool res= m_value.to_TIME(thd, ltime, fuzzydate);
+ DBUG_ASSERT(!res);
+ return res;
+ }
+ bool val_native(THD *thd, Native *to)
+ {
+ return m_value.to_native(to, decimals);
+ }
+ void set_value(const Timestamp_or_zero_datetime &value)
+ {
+ m_value= value;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_timestamp_literal>(thd, this); }
+};
+
+
+class Item_temporal_literal :public Item_literal
+{
+public:
+ Item_temporal_literal(THD *thd)
+ :Item_literal(thd)
{
collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII);
decimals= 0;
- cached_time= *ltime;
}
- Item_temporal_literal(THD *thd, const MYSQL_TIME *ltime, uint dec_arg):
- Item_basic_constant(thd)
+ Item_temporal_literal(THD *thd, uint dec_arg):
+ Item_literal(thd)
{
collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII);
decimals= dec_arg;
- cached_time= *ltime;
}
- bool basic_const_item() const { return true; }
- bool const_item() const { return true; }
- enum Type type() const { return DATE_ITEM; }
- bool eq(const Item *item, bool binary_cmp) const;
- bool check_partition_func_processor(void *int_arg) {return FALSE;}
-
- bool is_null()
- { return is_null_from_temporal(); }
- bool get_date_with_sql_mode(MYSQL_TIME *to);
- String *val_str(String *str)
- { return val_string_from_date(str); }
- longlong val_int()
- { return val_int_from_date(); }
- double val_real()
- { return val_real_from_date(); }
- my_decimal *val_decimal(my_decimal *decimal_value)
- { return val_decimal_from_date(decimal_value); }
int save_in_field(Field *field, bool no_conversions)
{ return save_date_in_field(field, no_conversions); }
};
@@ -4539,25 +4886,63 @@ public:
*/
class Item_date_literal: public Item_temporal_literal
{
+protected:
+ Date cached_time;
+ bool update_null()
+ {
+ return maybe_null &&
+ (null_value= cached_time.check_date_with_warn(current_thd));
+ }
public:
- Item_date_literal(THD *thd, const MYSQL_TIME *ltime)
- :Item_temporal_literal(thd, ltime)
+ Item_date_literal(THD *thd, const Date *ltime)
+ :Item_temporal_literal(thd),
+ cached_time(*ltime)
{
+ DBUG_ASSERT(cached_time.is_valid_date());
max_length= MAX_DATE_WIDTH;
- fixed= 1;
/*
If date has zero month or day, it can return NULL in case of
NO_ZERO_DATE or NO_ZERO_IN_DATE.
- We can't just check the current sql_mode here in constructor,
+ If date is `February 30`, it can return NULL in case if
+ no ALLOW_INVALID_DATES is set.
+ We can't set null_value using the current sql_mode here in constructor,
because sql_mode can change in case of prepared statements
between PREPARE and EXECUTE.
+ Here we only set maybe_null to true if the value has such anomalies.
+ Later (during execution time), if maybe_null is true, then the value
+ will be checked per row, according to the execution time sql_mode.
+ The check_date() below call should cover all cases mentioned.
*/
- maybe_null= !ltime->month || !ltime->day;
+ maybe_null= cached_time.check_date(TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
}
const Type_handler *type_handler() const { return &type_handler_newdate; }
void print(String *str, enum_query_type query_type);
+ const MYSQL_TIME *const_ptr_mysql_time() const
+ {
+ return cached_time.get_mysql_time();
+ }
Item *clone_item(THD *thd);
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ longlong val_int()
+ {
+ return update_null() ? 0 : cached_time.to_longlong();
+ }
+ double val_real()
+ {
+ return update_null() ? 0 : cached_time.to_double();
+ }
+ String *val_str(String *to)
+ {
+ return update_null() ? 0 : cached_time.to_string(to);
+ }
+ my_decimal *val_decimal(my_decimal *to)
+ {
+ return update_null() ? 0 : cached_time.to_decimal(to);
+ }
+ longlong val_datetime_packed(THD *thd)
+ {
+ return update_null() ? 0 : cached_time.valid_date_to_packed();
+ }
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_date_literal>(thd, this); }
};
@@ -4568,17 +4953,36 @@ public:
*/
class Item_time_literal: public Item_temporal_literal
{
+protected:
+ Time cached_time;
public:
- Item_time_literal(THD *thd, const MYSQL_TIME *ltime, uint dec_arg):
- Item_temporal_literal(thd, ltime, dec_arg)
+ Item_time_literal(THD *thd, const Time *ltime, uint dec_arg):
+ Item_temporal_literal(thd, dec_arg),
+ cached_time(*ltime)
{
+ DBUG_ASSERT(cached_time.is_valid_time());
max_length= MIN_TIME_WIDTH + (decimals ? decimals + 1 : 0);
- fixed= 1;
}
const Type_handler *type_handler() const { return &type_handler_time2; }
void print(String *str, enum_query_type query_type);
+ const MYSQL_TIME *const_ptr_mysql_time() const
+ {
+ return cached_time.get_mysql_time();
+ }
Item *clone_item(THD *thd);
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ longlong val_int() { return cached_time.to_longlong(); }
+ double val_real() { return cached_time.to_double(); }
+ String *val_str(String *to) { return cached_time.to_string(to, decimals); }
+ my_decimal *val_decimal(my_decimal *to) { return cached_time.to_decimal(to); }
+ longlong val_time_packed(THD *thd)
+ {
+ return cached_time.valid_time_to_packed();
+ }
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
+ bool val_native(THD *thd, Native *to)
+ {
+ return Time(thd, this).to_native(to, decimals);
+ }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_time_literal>(thd, this); }
};
@@ -4589,19 +4993,51 @@ public:
*/
class Item_datetime_literal: public Item_temporal_literal
{
+protected:
+ Datetime cached_time;
+ bool update_null()
+ {
+ return maybe_null &&
+ (null_value= cached_time.check_date_with_warn(current_thd));
+ }
public:
- Item_datetime_literal(THD *thd, const MYSQL_TIME *ltime, uint dec_arg):
- Item_temporal_literal(thd, ltime, dec_arg)
+ Item_datetime_literal(THD *thd, const Datetime *ltime, uint dec_arg):
+ Item_temporal_literal(thd, dec_arg),
+ cached_time(*ltime)
{
+ DBUG_ASSERT(cached_time.is_valid_datetime());
max_length= MAX_DATETIME_WIDTH + (decimals ? decimals + 1 : 0);
- fixed= 1;
// See the comment on maybe_null in Item_date_literal
- maybe_null= !ltime->month || !ltime->day;
+ maybe_null= cached_time.check_date(TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
}
const Type_handler *type_handler() const { return &type_handler_datetime2; }
void print(String *str, enum_query_type query_type);
+ const MYSQL_TIME *const_ptr_mysql_time() const
+ {
+ return cached_time.get_mysql_time();
+ }
Item *clone_item(THD *thd);
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ longlong val_int()
+ {
+ return update_null() ? 0 : cached_time.to_longlong();
+ }
+ double val_real()
+ {
+ return update_null() ? 0 : cached_time.to_double();
+ }
+ String *val_str(String *to)
+ {
+ return update_null() ? NULL : cached_time.to_string(to, decimals);
+ }
+ my_decimal *val_decimal(my_decimal *to)
+ {
+ return update_null() ? NULL : cached_time.to_decimal(to);
+ }
+ longlong val_datetime_packed(THD *thd)
+ {
+ return update_null() ? 0 : cached_time.valid_datetime_to_packed();
+ }
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_datetime_literal>(thd, this); }
};
@@ -4636,11 +5072,14 @@ class Item_date_literal_for_invalid_dates: public Item_date_literal
in sql_mode=TRADITIONAL.
*/
public:
- Item_date_literal_for_invalid_dates(THD *thd, const MYSQL_TIME *ltime)
- :Item_date_literal(thd, ltime) { }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+ Item_date_literal_for_invalid_dates(THD *thd, const Date *ltime)
+ :Item_date_literal(thd, ltime)
+ {
+ maybe_null= false;
+ }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- *ltime= cached_time;
+ cached_time.copy_to_mysql_time(ltime);
return (null_value= false);
}
};
@@ -4654,11 +5093,14 @@ class Item_datetime_literal_for_invalid_dates: public Item_datetime_literal
{
public:
Item_datetime_literal_for_invalid_dates(THD *thd,
- const MYSQL_TIME *ltime, uint dec_arg)
- :Item_datetime_literal(thd, ltime, dec_arg) { }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+ const Datetime *ltime, uint dec_arg)
+ :Item_datetime_literal(thd, ltime, dec_arg)
{
- *ltime= cached_time;
+ maybe_null= false;
+ }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ {
+ cached_time.copy_to_mysql_time(ltime);
return (null_value= false);
}
};
@@ -4855,8 +5297,10 @@ struct st_sp_security_context;
class Item_sp
{
-public:
+protected:
+ // Can be NULL in some non-SELECT queries
Name_resolution_context *context;
+public:
sp_name *m_name;
sp_head *m_sp;
TABLE *dummy_table;
@@ -4878,9 +5322,15 @@ public:
bool execute_impl(THD *thd, Item **args, uint arg_count);
bool init_result_field(THD *thd, uint max_length, uint maybe_null,
bool *null_value, LEX_CSTRING *name);
+ void process_error(THD *thd)
+ {
+ if (context)
+ context->process_error(thd);
+ }
};
-class Item_ref :public Item_ident
+class Item_ref :public Item_ident,
+ protected With_sum_func_cache
{
protected:
void set_properties();
@@ -4916,7 +5366,8 @@ public:
/* Constructor need to process subselect with temporary tables (see Item) */
Item_ref(THD *thd, Item_ref *item)
- :Item_ident(thd, item), set_properties_only(0), ref(item->ref) {}
+ :Item_ident(thd, item), With_sum_func_cache(*item),
+ set_properties_only(0), ref(item->ref) {}
enum Type type() const { return REF_ITEM; }
enum Type real_type() const { return ref ? (*ref)->type() :
REF_ITEM; }
@@ -4932,13 +5383,15 @@ public:
my_decimal *val_decimal(my_decimal *);
bool val_bool();
String *val_str(String* tmp);
+ bool val_native(THD *thd, Native *to);
bool is_null();
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
- longlong val_datetime_packed();
- longlong val_time_packed();
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ longlong val_datetime_packed(THD *);
+ longlong val_time_packed(THD *);
double val_result();
longlong val_int_result();
String *str_result(String* tmp);
+ bool val_native_result(THD *thd, Native *to);
my_decimal *val_decimal_result(my_decimal *);
bool val_bool_result();
bool is_null_result();
@@ -4956,6 +5409,9 @@ public:
Field *get_tmp_table_field()
{ return result_field ? result_field : (*ref)->get_tmp_table_field(); }
Item *get_tmp_table_item(THD *thd);
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param);
+ Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *);
table_map used_tables() const;
void update_used_tables();
COND *build_equal_items(THD *thd, COND_EQUAL *inherited,
@@ -5089,6 +5545,8 @@ public:
}
bool excl_dep_on_grouping_fields(st_select_lex *sel)
{ return (*ref)->excl_dep_on_grouping_fields(sel); }
+ bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred)
+ { return (*ref)->excl_dep_on_in_subq_left_part(subq_pred); }
bool cleanup_excluding_fields_processor(void *arg)
{
Item *item= real_item();
@@ -5105,6 +5563,15 @@ public:
return 0;
return cleanup_processor(arg);
}
+ bool with_sum_func() const { return m_with_sum_func; }
+ With_sum_func_cache* get_with_sum_func_cache() { return this; }
+ Item *field_transformer_for_having_pushdown(THD *thd, uchar *arg)
+ { return (*ref)->field_transformer_for_having_pushdown(thd, arg); }
+ Item *remove_item_direct_ref()
+ {
+ *ref= (*ref)->remove_item_direct_ref();
+ return this;
+ }
};
@@ -5144,13 +5611,16 @@ public:
my_decimal *val_decimal(my_decimal *);
bool val_bool();
String *val_str(String* tmp);
+ bool val_native(THD *thd, Native *to);
bool is_null();
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
- longlong val_datetime_packed();
- longlong val_time_packed();
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ longlong val_datetime_packed(THD *);
+ longlong val_time_packed(THD *);
virtual Ref_Type ref_type() { return DIRECT_REF; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_direct_ref>(thd, this); }
+ Item *remove_item_direct_ref()
+ { return (*ref)->remove_item_direct_ref(); }
};
@@ -5195,7 +5665,8 @@ class Expression_cache_tracker;
*/
class Item_cache_wrapper :public Item_result_field,
- public With_subquery_cache
+ public With_subquery_cache,
+ protected With_sum_func_cache
{
private:
/* Pointer on the cached expression */
@@ -5223,6 +5694,8 @@ public:
enum Type type() const { return EXPR_CACHE_ITEM; }
enum Type real_type() const { return orig_item->type(); }
bool with_subquery() const { DBUG_ASSERT(fixed); return m_with_subquery; }
+ bool with_sum_func() const { return m_with_sum_func; }
+ With_sum_func_cache* get_with_sum_func_cache() { return this; }
bool set_cache(THD *thd);
Expression_cache_tracker* init_tracker(MEM_ROOT *mem_root);
@@ -5237,10 +5710,11 @@ public:
double val_real();
longlong val_int();
String *val_str(String* tmp);
+ bool val_native(THD *thd, Native *to);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool is_null();
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool send(Protocol *protocol, st_value *buffer);
void save_org_in_field(Field *field,
fast_field_copier data __attribute__ ((__unused__)))
@@ -5399,10 +5873,12 @@ public:
}
bool excl_dep_on_table(table_map tab_map);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
+ bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred);
Item *derived_field_transformer_for_having(THD *thd, uchar *arg);
Item *derived_field_transformer_for_where(THD *thd, uchar *arg);
- Item *derived_grouping_field_transformer_for_where(THD *thd,
- uchar *arg);
+ Item *grouping_field_transformer_for_where(THD *thd, uchar *arg);
+ Item *in_subq_field_transformer_for_where(THD *thd, uchar *arg);
+ Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg);
void save_val(Field *to)
{
@@ -5432,6 +5908,12 @@ public:
else
return Item_direct_ref::val_str(tmp);
}
+ bool val_native(THD *thd, Native *to)
+ {
+ if (check_null_ref())
+ return true;
+ return Item_direct_ref::val_native(thd, to);
+ }
my_decimal *val_decimal(my_decimal *tmp)
{
if (check_null_ref())
@@ -5453,28 +5935,28 @@ public:
else
return Item_direct_ref::is_null();
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
if (check_null_ref())
{
bzero((char*) ltime,sizeof(*ltime));
return 1;
}
- return Item_direct_ref::get_date(ltime, fuzzydate);
+ return Item_direct_ref::get_date(thd, ltime, fuzzydate);
}
- longlong val_time_packed()
+ longlong val_time_packed(THD *thd)
{
if (check_null_ref())
return 0;
else
- return Item_direct_ref::val_time_packed();
+ return Item_direct_ref::val_time_packed(thd);
}
- longlong val_datetime_packed()
+ longlong val_datetime_packed(THD *thd)
{
if (check_null_ref())
return 0;
else
- return Item_direct_ref::val_datetime_packed();
+ return Item_direct_ref::val_datetime_packed(thd);
}
bool send(Protocol *protocol, st_value *buffer);
void save_org_in_field(Field *field,
@@ -5510,6 +5992,9 @@ public:
Item *get_copy(THD *thd)
{ return get_item_copy<Item_direct_view_ref>(thd, this); }
+ Item *field_transformer_for_having_pushdown(THD *thd, uchar *arg)
+ { return this; }
+ Item *remove_item_direct_ref() { return this; }
};
@@ -5599,7 +6084,8 @@ public:
String* val_str(String* s);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool val_native(THD *thd, Native *to);
virtual void print(String *str, enum_query_type query_type);
table_map used_tables() const;
Item *get_copy(THD *thd)
@@ -5691,12 +6177,12 @@ protected:
*/
Item_copy(THD *thd, Item *i): Item(thd)
{
+ DBUG_ASSERT(i->is_fixed());
item= i;
null_value=maybe_null=item->maybe_null;
Type_std_attributes::set(item);
name= item->name;
set_handler(item->type_handler());
- fixed= item->fixed;
}
public:
@@ -5717,6 +6203,12 @@ public:
const Type_handler *type_handler() const
{ return Type_handler_hybrid_field_type::type_handler(); }
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
void make_send_field(THD *thd, Send_field *field)
{ item->make_send_field(thd, field); }
table_map used_tables() const { return (table_map) 1L; }
@@ -5758,8 +6250,8 @@ public:
my_decimal *val_decimal(my_decimal *);
double val_real();
longlong val_int();
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date_from_string(ltime, fuzzydate); }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ { return get_date_from_string(thd, ltime, fuzzydate); }
void copy();
int save_in_field(Field *field, bool no_conversions);
Item *get_copy(THD *thd)
@@ -5767,6 +6259,76 @@ public:
};
+/**
+ We need a separate class Item_copy_timestamp because
+ TIMESTAMP->string->TIMESTAMP conversion is not round trip safe
+ near the DST change, e.g. '2010-10-31 02:25:26' can mean:
+ - my_time_t(1288477526) - summer time in Moscow
+ - my_time_t(1288481126) - winter time in Moscow, one hour later
+*/
+class Item_copy_timestamp: public Item_copy
+{
+ Timestamp_or_zero_datetime m_value;
+ bool sane() const { return !null_value || m_value.is_zero_datetime(); }
+public:
+ Item_copy_timestamp(THD *thd, Item *arg): Item_copy(thd, arg) { }
+ const Type_handler *type_handler() const { return &type_handler_timestamp2; }
+ void copy()
+ {
+ Timestamp_or_zero_datetime_native_null tmp(current_thd, item, false);
+ null_value= tmp.is_null();
+ m_value= tmp.is_null() ? Timestamp_or_zero_datetime() :
+ Timestamp_or_zero_datetime(tmp);
+ }
+ int save_in_field(Field *field, bool no_conversions)
+ {
+ DBUG_ASSERT(sane());
+ if (null_value)
+ return set_field_to_null(field);
+ Timestamp_or_zero_datetime_native native(m_value, decimals);
+ return native.save_in_field(field, decimals);
+ }
+ longlong val_int()
+ {
+ DBUG_ASSERT(sane());
+ return null_value ? 0 :
+ m_value.to_datetime(current_thd).to_longlong();
+ }
+ double val_real()
+ {
+ DBUG_ASSERT(sane());
+ return null_value ? 0e0 :
+ m_value.to_datetime(current_thd).to_double();
+ }
+ String *val_str(String *to)
+ {
+ DBUG_ASSERT(sane());
+ return null_value ? NULL :
+ m_value.to_datetime(current_thd).to_string(to, decimals);
+ }
+ my_decimal *val_decimal(my_decimal *to)
+ {
+ DBUG_ASSERT(sane());
+ return null_value ? NULL :
+ m_value.to_datetime(current_thd).to_decimal(to);
+ }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ {
+ DBUG_ASSERT(sane());
+ bool res= m_value.to_TIME(thd, ltime, fuzzydate);
+ DBUG_ASSERT(!res);
+ return null_value || res;
+ }
+ bool val_native(THD *thd, Native *to)
+ {
+ DBUG_ASSERT(sane());
+ return null_value || m_value.to_native(to, decimals);
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_copy_timestamp>(thd, this); }
+};
+
+
/*
Cached_item_XXX objects are not exactly caches. They do the following:
@@ -5894,7 +6456,9 @@ public:
double val_real();
longlong val_int();
my_decimal *val_decimal(my_decimal *decimal_value);
- bool get_date(MYSQL_TIME *ltime,ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate);
+ bool val_native(THD *thd, Native *to);
+ bool val_native_result(THD *thd, Native *to);
/* Result variants */
double val_result();
@@ -5903,7 +6467,7 @@ public:
my_decimal *val_decimal_result(my_decimal *val);
bool val_bool_result();
bool is_null_result();
- bool get_date_result(MYSQL_TIME *ltime,ulonglong fuzzydate);
+ bool get_date_result(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool send(Protocol *protocol, st_value *buffer);
int save_in_field(Field *field_arg, bool no_conversions);
@@ -5933,6 +6497,8 @@ public:
}
Item *transform(THD *thd, Item_transformer transformer, uchar *args);
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param);
};
@@ -5948,10 +6514,11 @@ public:
return false;
}
bool is_evaluable_expression() const { return false; }
- bool fix_fields(THD *thd, Item **items)
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
{
- fixed= true;
- return false;
+ DBUG_ASSERT(0);
+ return NULL;
}
String *val_str(String *str)
{
@@ -5977,7 +6544,7 @@ public:
null_value= true;
return 0;
}
- bool get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(0); // never should be called
return null_value= true;
@@ -6051,6 +6618,7 @@ public:
param->set_ignore();
return false;
}
+
Item *get_copy(THD *thd)
{ return get_item_copy<Item_ignore_specification>(thd, this); }
};
@@ -6197,7 +6765,7 @@ public:
for any value.
*/
-class Item_cache: public Item_basic_constant,
+class Item_cache: public Item,
public Type_handler_hybrid_field_type
{
protected:
@@ -6216,6 +6784,8 @@ protected:
cache_value() will set this flag to TRUE.
*/
bool value_cached;
+
+ table_map used_table_map;
public:
/*
This is set if at least one of the values of a sub query is NULL
@@ -6226,24 +6796,24 @@ public:
bool null_value_inside;
Item_cache(THD *thd):
- Item_basic_constant(thd),
+ Item(thd),
Type_handler_hybrid_field_type(&type_handler_string),
example(0), cached_field(0),
- value_cached(0)
+ value_cached(0),
+ used_table_map(0)
{
- fixed= 1;
maybe_null= 1;
null_value= 1;
null_value_inside= true;
}
protected:
Item_cache(THD *thd, const Type_handler *handler):
- Item_basic_constant(thd),
+ Item(thd),
Type_handler_hybrid_field_type(handler),
example(0), cached_field(0),
- value_cached(0)
+ value_cached(0),
+ used_table_map(0)
{
- fixed= 1;
maybe_null= 1;
null_value= 1;
null_value_inside= true;
@@ -6259,10 +6829,18 @@ public:
cached_field= ((Item_field *)item)->field;
return 0;
};
+
+ void set_used_tables(table_map map) { used_table_map= map; }
+ table_map used_tables() const { return used_table_map; }
enum Type type() const { return CACHE_ITEM; }
const Type_handler *type_handler() const
{ return Type_handler_hybrid_field_type::type_handler(); }
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ return create_tmp_field_ex_simple(table, src, param);
+ }
virtual void keep_array() {}
virtual void print(String *str, enum_query_type query_type);
@@ -6293,7 +6871,7 @@ public:
void cleanup()
{
clear();
- Item_basic_constant::cleanup();
+ Item::cleanup();
}
/**
Check if saved item has a non-NULL value.
@@ -6328,6 +6906,8 @@ public:
virtual void set_null();
bool walk(Item_processor processor, bool walk_subquery, void *arg)
{
+ if (arg == STOP_PTR)
+ return FALSE;
if (example && example->walk(processor, walk_subquery, arg))
return TRUE;
return (this->*processor)(arg);
@@ -6345,7 +6925,11 @@ public:
{ return convert_to_basic_const_item(thd); }
Item *derived_field_transformer_for_where(THD *thd, uchar *arg)
{ return convert_to_basic_const_item(thd); }
- Item *derived_grouping_field_transformer_for_where(THD *thd, uchar *arg)
+ Item *grouping_field_transformer_for_where(THD *thd, uchar *arg)
+ { return convert_to_basic_const_item(thd); }
+ Item *in_subq_field_transformer_for_where(THD *thd, uchar *arg)
+ { return convert_to_basic_const_item(thd); }
+ Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg)
{ return convert_to_basic_const_item(thd); }
};
@@ -6364,8 +6948,8 @@ public:
longlong val_int();
String* val_str(String *str);
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date_from_int(ltime, fuzzydate); }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ { return get_date_from_int(thd, ltime, fuzzydate); }
bool cache_value();
int save_in_field(Field *field, bool no_conversions);
Item *convert_to_basic_const_item(THD *thd);
@@ -6377,9 +6961,12 @@ public:
class Item_cache_year: public Item_cache_int
{
public:
- Item_cache_year(THD *thd): Item_cache_int(thd, &type_handler_year) { }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date_from_year(ltime, fuzzydate); }
+ Item_cache_year(THD *thd, const Type_handler *handler)
+ :Item_cache_int(thd, handler) { }
+ bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode)
+ {
+ return type_handler_year.Item_get_date_with_warn(thd, this, to, mode);
+ }
};
@@ -6388,14 +6975,8 @@ class Item_cache_temporal: public Item_cache_int
protected:
Item_cache_temporal(THD *thd, const Type_handler *handler);
public:
- String* val_str(String *str);
- my_decimal *val_decimal(my_decimal *);
- longlong val_int();
- longlong val_datetime_packed();
- longlong val_time_packed();
- double val_real();
bool cache_value();
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
int save_in_field(Field *field, bool no_conversions);
bool setup(THD *thd, Item *item)
{
@@ -6425,6 +7006,35 @@ public:
Item *get_copy(THD *thd)
{ return get_item_copy<Item_cache_time>(thd, this); }
Item *make_literal(THD *);
+ longlong val_datetime_packed(THD *thd)
+ {
+ Datetime::Options_cmp opt(thd);
+ return has_value() ? Datetime(thd, this, opt).to_packed() : 0;
+ }
+ longlong val_time_packed(THD *thd)
+ {
+ return has_value() ? value : 0;
+ }
+ longlong val_int()
+ {
+ return has_value() ? Time(this).to_longlong() : 0;
+ }
+ double val_real()
+ {
+ return has_value() ? Time(this).to_double() : 0;
+ }
+ String *val_str(String *to)
+ {
+ return has_value() ? Time(this).to_string(to, decimals) : NULL;
+ }
+ my_decimal *val_decimal(my_decimal *to)
+ {
+ return has_value() ? Time(this).to_decimal(to) : NULL;
+ }
+ bool val_native(THD *thd, Native *to)
+ {
+ return has_value() ? Time(thd, this).to_native(to, decimals) : true;
+ }
};
@@ -6436,6 +7046,30 @@ public:
Item *get_copy(THD *thd)
{ return get_item_copy<Item_cache_datetime>(thd, this); }
Item *make_literal(THD *);
+ longlong val_datetime_packed(THD *thd)
+ {
+ return has_value() ? value : 0;
+ }
+ longlong val_time_packed(THD *thd)
+ {
+ return Time(thd, this, Time::Options_cmp(thd)).to_packed();
+ }
+ longlong val_int()
+ {
+ return has_value() ? Datetime(this).to_longlong() : 0;
+ }
+ double val_real()
+ {
+ return has_value() ? Datetime(this).to_double() : 0;
+ }
+ String *val_str(String *to)
+ {
+ return has_value() ? Datetime(this).to_string(to, decimals) : NULL;
+ }
+ my_decimal *val_decimal(my_decimal *to)
+ {
+ return has_value() ? Datetime(this).to_decimal(to) : NULL;
+ }
};
@@ -6447,6 +7081,65 @@ public:
Item *get_copy(THD *thd)
{ return get_item_copy<Item_cache_date>(thd, this); }
Item *make_literal(THD *);
+ longlong val_datetime_packed(THD *thd)
+ {
+ return has_value() ? value : 0;
+ }
+ longlong val_time_packed(THD *thd)
+ {
+ return Time(thd, this, Time::Options_cmp(thd)).to_packed();
+ }
+ longlong val_int() { return has_value() ? Date(this).to_longlong() : 0; }
+ double val_real() { return has_value() ? Date(this).to_double() : 0; }
+ String *val_str(String *to)
+ {
+ return has_value() ? Date(this).to_string(to) : NULL;
+ }
+ my_decimal *val_decimal(my_decimal *to)
+ {
+ return has_value() ? Date(this).to_decimal(to) : NULL;
+ }
+};
+
+
+class Item_cache_timestamp: public Item_cache
+{
+ Timestamp_or_zero_datetime_native m_native;
+ Datetime to_datetime(THD *thd);
+public:
+ Item_cache_timestamp(THD *thd)
+ :Item_cache(thd, &type_handler_timestamp2) { }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_timestamp>(thd, this); }
+ bool cache_value();
+ String* val_str(String *to)
+ {
+ return to_datetime(current_thd).to_string(to, decimals);
+ }
+ my_decimal *val_decimal(my_decimal *to)
+ {
+ return to_datetime(current_thd).to_decimal(to);
+ }
+ longlong val_int()
+ {
+ return to_datetime(current_thd).to_longlong();
+ }
+ double val_real()
+ {
+ return to_datetime(current_thd).to_double();
+ }
+ longlong val_datetime_packed(THD *thd)
+ {
+ return to_datetime(current_thd).to_packed();
+ }
+ longlong val_time_packed(THD *thd)
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ int save_in_field(Field *field, bool no_conversions);
+ bool val_native(THD *thd, Native *to);
};
@@ -6462,8 +7155,8 @@ public:
double val_real();
longlong val_int();
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date_from_real(ltime, fuzzydate); }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ { return get_date_from_real(thd, ltime, fuzzydate); }
bool cache_value();
Item *convert_to_basic_const_item(THD *thd);
};
@@ -6504,8 +7197,11 @@ public:
longlong val_int();
String* val_str(String *str);
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date_from_decimal(ltime, fuzzydate); }
+ bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode)
+ {
+ return decimal_to_datetime_with_warn(thd, VDec(this).ptr(), to, mode,
+ NULL, NULL);
+ }
bool cache_value();
Item *convert_to_basic_const_item(THD *thd);
Item *get_copy(THD *thd)
@@ -6532,8 +7228,8 @@ public:
longlong val_int();
String* val_str(String *);
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date_from_string(ltime, fuzzydate); }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ { return get_date_from_string(thd, ltime, fuzzydate); }
CHARSET_INFO *charset() const { return value->charset(); };
int save_in_field(Field *field, bool no_conversions);
bool cache_value();
@@ -6614,7 +7310,7 @@ public:
illegal_method_call((const char*)"val_decimal");
return 0;
};
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
illegal_method_call((const char*)"val_decimal");
return true;
@@ -6663,7 +7359,7 @@ public:
Type_handler_hybrid_field_type(item->real_type_handler()),
enum_set_typelib(0)
{
- DBUG_ASSERT(item->fixed);
+ DBUG_ASSERT(item->is_fixed());
maybe_null= item->maybe_null;
}
Item_type_holder(THD *thd,
@@ -6714,8 +7410,9 @@ public:
longlong val_int();
my_decimal *val_decimal(my_decimal *);
String *val_str(String*);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
- Field *create_tmp_field(bool group, TABLE *table)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
{
return Item_type_holder::real_type_handler()->
make_and_init_table_field(&name, Record_addr(maybe_null),
@@ -6846,7 +7543,7 @@ bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str,
inline bool Virtual_column_info::is_equal(const Virtual_column_info* vcol) const
{
- return field_type == vcol->get_real_type()
+ return type_handler() == vcol->type_handler()
&& stored_in_db == vcol->is_stored()
&& expr->eq(vcol->expr, true);
}
@@ -6856,4 +7553,39 @@ inline void Virtual_column_info::print(String* str)
expr->print_for_table_def(str);
}
+inline bool TABLE::mark_column_with_deps(Field *field)
+{
+ bool res;
+ if (!(res= bitmap_fast_test_and_set(read_set, field->field_index)))
+ {
+ if (field->vcol_info)
+ mark_virtual_column_deps(field);
+ }
+ return res;
+}
+
+inline bool TABLE::mark_virtual_column_with_deps(Field *field)
+{
+ bool res;
+ DBUG_ASSERT(field->vcol_info);
+ if (!(res= bitmap_fast_test_and_set(read_set, field->field_index)))
+ mark_virtual_column_deps(field);
+ return res;
+}
+
+inline void TABLE::mark_virtual_column_deps(Field *field)
+{
+ DBUG_ASSERT(field->vcol_info);
+ DBUG_ASSERT(field->vcol_info->expr);
+ field->vcol_info->expr->walk(&Item::register_field_in_read_map, 1, 0);
+}
+
+inline void TABLE::use_all_stored_columns()
+{
+ bitmap_set_all(read_set);
+ if (Field **vf= vfield)
+ for (; *vf; vf++)
+ bitmap_clear_bit(read_set, (*vf)->field_index);
+}
+
#endif /* SQL_ITEM_INCLUDED */
diff --git a/sql/item_buff.cc b/sql/item_buff.cc
index a64e092a434..05cef6871be 100644
--- a/sql/item_buff.cc
+++ b/sql/item_buff.cc
@@ -47,9 +47,9 @@ Cached_item *new_Cached_item(THD *thd, Item *item, bool pass_through_ref)
}
switch (item->result_type()) {
case STRING_RESULT:
- return new Cached_item_str(thd, (Item_field *) item);
+ return new Cached_item_str(thd, item);
case INT_RESULT:
- return new Cached_item_int((Item_field *) item);
+ return new Cached_item_int(item);
case REAL_RESULT:
return new Cached_item_real(item);
case DECIMAL_RESULT:
@@ -225,16 +225,15 @@ Cached_item_decimal::Cached_item_decimal(Item *it)
bool Cached_item_decimal::cmp()
{
- my_decimal tmp;
- my_decimal *ptmp= item->val_decimal(&tmp);
- if (null_value != item->null_value ||
- (!item->null_value && my_decimal_cmp(&value, ptmp)))
+ VDec tmp(item);
+ if (null_value != tmp.is_null() ||
+ (!tmp.is_null() && tmp.cmp(&value)))
{
- null_value= item->null_value;
+ null_value= tmp.is_null();
/* Save only not null values */
if (!null_value)
{
- my_decimal2decimal(ptmp, &value);
+ my_decimal2decimal(tmp.ptr(), &value);
return TRUE;
}
return FALSE;
@@ -245,17 +244,9 @@ bool Cached_item_decimal::cmp()
int Cached_item_decimal::cmp_read_only()
{
- my_decimal tmp;
- my_decimal *ptmp= item->val_decimal(&tmp);
+ VDec tmp(item);
if (null_value)
- {
- if (item->null_value)
- return 0;
- else
- return -1;
- }
- if (item->null_value)
- return 1;
- return my_decimal_cmp(&value, ptmp);
+ return tmp.is_null() ? 0 : -1;
+ return tmp.is_null() ? 1 : value.cmp(tmp.ptr());
}
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 413c422eace..687aa7e192b 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -31,32 +31,8 @@
#include <m_ctype.h>
#include "sql_select.h"
#include "sql_parse.h" // check_stack_overrun
-#include "sql_time.h" // make_truncated_value_warning
#include "sql_base.h" // dynamic_column_error_message
-/**
- find an temporal type (item) that others will be converted to
- for the purpose of comparison.
-
- this is the type that will be used in warnings like
- "Incorrect <<TYPE>> value".
-*/
-static Item *find_date_time_item(Item **args, uint nargs, uint col)
-{
- Item *date_arg= 0, **arg, **arg_end;
- for (arg= args, arg_end= args + nargs; arg != arg_end ; arg++)
- {
- Item *item= arg[0]->element_index(col);
- if (item->cmp_type() != TIME_RESULT)
- continue;
- if (item->field_type() == MYSQL_TYPE_DATETIME)
- return item;
- if (!date_arg)
- date_arg= item;
- }
- return date_arg;
-}
-
/*
Compare row signature of two expressions
@@ -603,6 +579,18 @@ bool Arg_comparator::set_cmp_func_datetime()
}
+bool Arg_comparator::set_cmp_func_native()
+{
+ THD *thd= current_thd;
+ m_compare_collation= &my_charset_numeric;
+ func= is_owner_equal_func() ? &Arg_comparator::compare_e_native :
+ &Arg_comparator::compare_native;
+ a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
+ b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
+ return false;
+}
+
+
bool Arg_comparator::set_cmp_func_int()
{
THD *thd= current_thd;
@@ -719,10 +707,11 @@ Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value,
int Arg_comparator::compare_time()
{
- longlong val1= (*a)->val_time_packed();
+ THD *thd= current_thd;
+ longlong val1= (*a)->val_time_packed(thd);
if (!(*a)->null_value)
{
- longlong val2= (*b)->val_time_packed();
+ longlong val2= (*b)->val_time_packed(thd);
if (!(*b)->null_value)
return compare_not_null_values(val1, val2);
}
@@ -734,8 +723,9 @@ int Arg_comparator::compare_time()
int Arg_comparator::compare_e_time()
{
- longlong val1= (*a)->val_time_packed();
- longlong val2= (*b)->val_time_packed();
+ THD *thd= current_thd;
+ longlong val1= (*a)->val_time_packed(thd);
+ longlong val2= (*b)->val_time_packed(thd);
if ((*a)->null_value || (*b)->null_value)
return MY_TEST((*a)->null_value && (*b)->null_value);
return MY_TEST(val1 == val2);
@@ -745,10 +735,11 @@ int Arg_comparator::compare_e_time()
int Arg_comparator::compare_datetime()
{
- longlong val1= (*a)->val_datetime_packed();
+ THD *thd= current_thd;
+ longlong val1= (*a)->val_datetime_packed(thd);
if (!(*a)->null_value)
{
- longlong val2= (*b)->val_datetime_packed();
+ longlong val2= (*b)->val_datetime_packed(thd);
if (!(*b)->null_value)
return compare_not_null_values(val1, val2);
}
@@ -760,8 +751,9 @@ int Arg_comparator::compare_datetime()
int Arg_comparator::compare_e_datetime()
{
- longlong val1= (*a)->val_datetime_packed();
- longlong val2= (*b)->val_datetime_packed();
+ THD *thd= current_thd;
+ longlong val1= (*a)->val_datetime_packed(thd);
+ longlong val2= (*b)->val_datetime_packed(thd);
if ((*a)->null_value || (*b)->null_value)
return MY_TEST((*a)->null_value && (*b)->null_value);
return MY_TEST(val1 == val2);
@@ -802,6 +794,39 @@ int Arg_comparator::compare_e_string()
}
+int Arg_comparator::compare_native()
+{
+ THD *thd= current_thd;
+ if (!(*a)->val_native_with_conversion(thd, &m_native1,
+ compare_type_handler()))
+ {
+ if (!(*b)->val_native_with_conversion(thd, &m_native2,
+ compare_type_handler()))
+ {
+ if (set_null)
+ owner->null_value= 0;
+ return compare_type_handler()->cmp_native(m_native1, m_native2);
+ }
+ }
+ if (set_null)
+ owner->null_value= 1;
+ return -1;
+}
+
+
+int Arg_comparator::compare_e_native()
+{
+ THD *thd= current_thd;
+ bool res1= (*a)->val_native_with_conversion(thd, &m_native1,
+ compare_type_handler());
+ bool res2= (*b)->val_native_with_conversion(thd, &m_native2,
+ compare_type_handler());
+ if (res1 || res2)
+ return MY_TEST(res1 == res2);
+ return MY_TEST(compare_type_handler()->cmp_native(m_native1, m_native2) == 0);
+}
+
+
int Arg_comparator::compare_real()
{
/*
@@ -830,19 +855,17 @@ int Arg_comparator::compare_real()
int Arg_comparator::compare_decimal()
{
- my_decimal decimal1;
- my_decimal *val1= (*a)->val_decimal(&decimal1);
- if (!(*a)->null_value)
+ VDec val1(*a);
+ if (!val1.is_null())
{
- my_decimal decimal2;
- my_decimal *val2= (*b)->val_decimal(&decimal2);
- if (!(*b)->null_value)
+ VDec val2(*b);
+ if (!val2.is_null())
{
if (set_null)
owner->null_value= 0;
- my_decimal_round_if_needed(E_DEC_FATAL_ERROR, val1, (*a)->decimals, 0);
- my_decimal_round_if_needed(E_DEC_FATAL_ERROR, val2, (*b)->decimals, 0);
- return my_decimal_cmp(val1, val2);
+ val1.round_self_if_needed((*a)->decimals, HALF_UP);
+ val2.round_self_if_needed((*b)->decimals, HALF_UP);
+ return val1.cmp(val2);
}
}
if (set_null)
@@ -861,14 +884,12 @@ int Arg_comparator::compare_e_real()
int Arg_comparator::compare_e_decimal()
{
- my_decimal decimal1, decimal2;
- my_decimal *val1= (*a)->val_decimal(&decimal1);
- my_decimal *val2= (*b)->val_decimal(&decimal2);
- if ((*a)->null_value || (*b)->null_value)
- return MY_TEST((*a)->null_value && (*b)->null_value);
- my_decimal_round_if_needed(E_DEC_FATAL_ERROR, val1, (*a)->decimals, 0);
- my_decimal_round_if_needed(E_DEC_FATAL_ERROR, val2, (*b)->decimals, 0);
- return my_decimal_cmp(val1, val2) == 0;
+ VDec val1(*a), val2(*b);
+ if (val1.is_null() || val2.is_null())
+ return MY_TEST(val1.is_null() && val2.is_null());
+ val1.round_self_if_needed((*a)->decimals, HALF_UP);
+ val2.round_self_if_needed((*b)->decimals, HALF_UP);
+ return MY_TEST(val1.cmp(val2) == 0);
}
@@ -1212,8 +1233,13 @@ bool Item_in_optimizer::eval_not_null_tables(void *opt_arg)
void Item_in_optimizer::print(String *str, enum_query_type query_type)
{
- restore_first_argument();
- Item_func::print(str, query_type);
+ if (query_type & QT_PARSABLE)
+ args[1]->print(str, query_type);
+ else
+ {
+ restore_first_argument();
+ Item_func::print(str, query_type);
+ }
}
@@ -1229,8 +1255,7 @@ void Item_in_optimizer::print(String *str, enum_query_type query_type)
void Item_in_optimizer::restore_first_argument()
{
- if (args[1]->type() == Item::SUBSELECT_ITEM &&
- ((Item_subselect *)args[1])->is_in_predicate())
+ if (!invisible_mode())
{
args[0]= ((Item_in_subselect *)args[1])->left_expr;
}
@@ -1247,8 +1272,7 @@ bool Item_in_optimizer::fix_left(THD *thd)
it is args[0].
*/
Item **ref0= args;
- if (args[1]->type() == Item::SUBSELECT_ITEM &&
- ((Item_subselect *)args[1])->is_in_predicate())
+ if (!invisible_mode())
{
/*
left_expr->fix_fields() may cause left_expr to be substituted for
@@ -1310,7 +1334,7 @@ bool Item_in_optimizer::fix_left(THD *thd)
used_tables_cache= args[0]->used_tables();
}
eval_not_null_tables(NULL);
- with_sum_func= args[0]->with_sum_func;
+ copy_with_sum_func(args[0]);
with_param= args[0]->with_param || args[1]->with_param;
with_field= args[0]->with_field;
if ((const_item_cache= args[0]->const_item()))
@@ -1318,11 +1342,11 @@ bool Item_in_optimizer::fix_left(THD *thd)
cache->store(args[0]);
cache->cache_value();
}
- if (args[1]->fixed)
+ if (args[1]->is_fixed())
{
/* to avoid overriding is called to update left expression */
used_tables_and_const_cache_join(args[1]);
- with_sum_func= with_sum_func || args[1]->with_sum_func;
+ join_with_sum_func(args[1]);
}
DBUG_RETURN(0);
}
@@ -1358,7 +1382,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
if (args[1]->maybe_null)
maybe_null=1;
m_with_subquery= true;
- with_sum_func= with_sum_func || args[1]->with_sum_func;
+ join_with_sum_func(args[1]);
with_field= with_field || args[1]->with_field;
with_param= args[0]->with_param || args[1]->with_param;
used_tables_and_const_cache_join(args[1]);
@@ -1910,7 +1934,7 @@ bool Item_func_interval::fix_length_and_dec()
max_length= 2;
used_tables_and_const_cache_join(row);
not_null_tables_cache= row->not_null_tables();
- with_sum_func= with_sum_func || row->with_sum_func;
+ join_with_sum_func(row);
with_param= with_param || row->with_param;
with_field= with_field || row->with_field;
return FALSE;
@@ -1989,11 +2013,11 @@ longlong Item_func_interval::val_int()
((el->result_type() == DECIMAL_RESULT) ||
(el->result_type() == INT_RESULT)))
{
- my_decimal e_dec_buf, *e_dec= el->val_decimal(&e_dec_buf);
+ VDec e_dec(el);
/* Skip NULL ranges. */
- if (el->null_value)
+ if (e_dec.is_null())
continue;
- if (my_decimal_cmp(e_dec, dec) > 0)
+ if (e_dec.cmp(dec) > 0)
return i - 1;
}
else
@@ -2139,22 +2163,49 @@ bool Item_func_between::fix_length_and_dec_temporal(THD *thd)
}
-longlong Item_func_between::val_int_cmp_temporal()
+longlong Item_func_between::val_int_cmp_datetime()
{
- enum_field_types f_type= m_comparator.type_handler()->field_type();
- longlong value= args[0]->val_temporal_packed(f_type), a, b;
+ THD *thd= current_thd;
+ longlong value= args[0]->val_datetime_packed(thd), a, b;
if ((null_value= args[0]->null_value))
return 0;
- a= args[1]->val_temporal_packed(f_type);
- b= args[2]->val_temporal_packed(f_type);
- if (!args[1]->null_value && !args[2]->null_value)
- return (longlong) ((value >= a && value <= b) != negated);
- if (args[1]->null_value && args[2]->null_value)
+ a= args[1]->val_datetime_packed(thd);
+ b= args[2]->val_datetime_packed(thd);
+ return val_int_cmp_int_finalize(value, a, b);
+}
+
+
+longlong Item_func_between::val_int_cmp_time()
+{
+ THD *thd= current_thd;
+ longlong value= args[0]->val_time_packed(thd), a, b;
+ if ((null_value= args[0]->null_value))
+ return 0;
+ a= args[1]->val_time_packed(thd);
+ b= args[2]->val_time_packed(thd);
+ return val_int_cmp_int_finalize(value, a, b);
+}
+
+
+longlong Item_func_between::val_int_cmp_native()
+{
+ THD *thd= current_thd;
+ const Type_handler *h= m_comparator.type_handler();
+ NativeBuffer<STRING_BUFFER_USUAL_SIZE> value, a, b;
+ if (val_native_with_conversion_from_item(thd, args[0], &value, h))
+ return 0;
+ bool ra= args[1]->val_native_with_conversion(thd, &a, h);
+ bool rb= args[2]->val_native_with_conversion(thd, &b, h);
+ if (!ra && !rb)
+ return (longlong)
+ ((h->cmp_native(value, a) >= 0 &&
+ h->cmp_native(value, b) <= 0) != negated);
+ if (ra && rb)
null_value= true;
- else if (args[1]->null_value)
- null_value= value <= b; // not null if false range.
+ else if (ra)
+ null_value= h->cmp_native(value, b) <= 0;
else
- null_value= value >= a;
+ null_value= h->cmp_native(value, a) >= 0;
return (longlong) (!null_value && negated);
}
@@ -2206,23 +2257,37 @@ longlong Item_func_between::val_int_cmp_int()
}
-longlong Item_func_between::val_int_cmp_decimal()
+bool Item_func_between::val_int_cmp_int_finalize(longlong value,
+ longlong a,
+ longlong b)
{
- my_decimal dec_buf, *dec= args[0]->val_decimal(&dec_buf),
- a_buf, *a_dec, b_buf, *b_dec;
- if ((null_value=args[0]->null_value))
- return 0; /* purecov: inspected */
- a_dec= args[1]->val_decimal(&a_buf);
- b_dec= args[2]->val_decimal(&b_buf);
if (!args[1]->null_value && !args[2]->null_value)
- return (longlong) ((my_decimal_cmp(dec, a_dec) >= 0 &&
- my_decimal_cmp(dec, b_dec) <= 0) != negated);
+ return (longlong) ((value >= a && value <= b) != negated);
if (args[1]->null_value && args[2]->null_value)
null_value= true;
else if (args[1]->null_value)
- null_value= (my_decimal_cmp(dec, b_dec) <= 0);
+ null_value= value <= b; // not null if false range.
+ else
+ null_value= value >= a;
+ return (longlong) (!null_value && negated);
+}
+
+
+longlong Item_func_between::val_int_cmp_decimal()
+{
+ VDec dec(args[0]);
+ if ((null_value= dec.is_null()))
+ return 0; /* purecov: inspected */
+ VDec a_dec(args[1]), b_dec(args[2]);
+ if (!a_dec.is_null() && !b_dec.is_null())
+ return (longlong) ((dec.cmp(a_dec) >= 0 &&
+ dec.cmp(b_dec) <= 0) != negated);
+ if (a_dec.is_null() && b_dec.is_null())
+ null_value= true;
+ else if (a_dec.is_null())
+ null_value= (dec.cmp(b_dec) <= 0);
else
- null_value= (my_decimal_cmp(dec, a_dec) >= 0);
+ null_value= (dec.cmp(a_dec) >= 0);
return (longlong) (!null_value && negated);
}
@@ -2330,12 +2395,22 @@ Item_func_ifnull::str_op(String *str)
}
-bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_func_ifnull::native_op(THD *thd, Native *to)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (!val_native_with_conversion_from_item(thd, args[0], to, type_handler()))
+ return false;
+ return val_native_with_conversion_from_item(thd, args[1], to, type_handler());
+}
+
+
+bool Item_func_ifnull::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed == 1);
for (uint i= 0; i < 2; i++)
{
- Datetime dt(current_thd, args[i], fuzzydate & ~TIME_FUZZY_DATES);
+ Datetime_truncation_not_needed dt(thd, args[i],
+ fuzzydate & ~TIME_FUZZY_DATES);
if (!(dt.copy_to_mysql_time(ltime, mysql_timestamp_type())))
return (null_value= false);
}
@@ -2343,12 +2418,12 @@ bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
}
-bool Item_func_ifnull::time_op(MYSQL_TIME *ltime)
+bool Item_func_ifnull::time_op(THD *thd, MYSQL_TIME *ltime)
{
DBUG_ASSERT(fixed == 1);
for (uint i= 0; i < 2; i++)
{
- if (!Time(args[i]).copy_to_mysql_time(ltime))
+ if (!Time(thd, args[i]).copy_to_mysql_time(ltime))
return (null_value= false);
}
return (null_value= true);
@@ -2830,28 +2905,38 @@ Item_func_nullif::decimal_op(my_decimal * decimal_value)
bool
-Item_func_nullif::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
+Item_func_nullif::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed == 1);
if (!compare())
return (null_value= true);
- Datetime dt(current_thd, args[2], fuzzydate);
+ Datetime_truncation_not_needed dt(thd, args[2], fuzzydate);
return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type()));
}
bool
-Item_func_nullif::time_op(MYSQL_TIME *ltime)
+Item_func_nullif::time_op(THD *thd, MYSQL_TIME *ltime)
{
DBUG_ASSERT(fixed == 1);
if (!compare())
return (null_value= true);
- return (null_value= Time(args[2]).copy_to_mysql_time(ltime));
+ return (null_value= Time(thd, args[2]).copy_to_mysql_time(ltime));
}
bool
+Item_func_nullif::native_op(THD *thd, Native *to)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (!compare())
+ return (null_value= true);
+ return val_native_with_conversion_from_item(thd, args[2], to, type_handler());
+}
+
+
+bool
Item_func_nullif::is_null()
{
return (null_value= (!compare() ? 1 : args[2]->is_null()));
@@ -2928,7 +3013,7 @@ Item *Item_func_case_simple::find_item()
Item *Item_func_decode_oracle::find_item()
{
uint idx;
- if (!Predicant_to_list_comparator::cmp_nulls_equal(this, &idx))
+ if (!Predicant_to_list_comparator::cmp_nulls_equal(current_thd, this, &idx))
return args[idx + when_count()];
Item **pos= Item_func_decode_oracle::else_expr_addr();
return pos ? pos[0] : 0;
@@ -3004,24 +3089,34 @@ my_decimal *Item_func_case::decimal_op(my_decimal *decimal_value)
}
-bool Item_func_case::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_func_case::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed == 1);
Item *item= find_item();
if (!item)
return (null_value= true);
- Datetime dt(current_thd, item, fuzzydate);
+ Datetime_truncation_not_needed dt(thd, item, fuzzydate);
return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type()));
}
-bool Item_func_case::time_op(MYSQL_TIME *ltime)
+bool Item_func_case::time_op(THD *thd, MYSQL_TIME *ltime)
{
DBUG_ASSERT(fixed == 1);
Item *item= find_item();
if (!item)
return (null_value= true);
- return (null_value= Time(item).copy_to_mysql_time(ltime));
+ return (null_value= Time(thd, item).copy_to_mysql_time(ltime));
+}
+
+
+bool Item_func_case::native_op(THD *thd, Native *to)
+{
+ DBUG_ASSERT(fixed == 1);
+ Item *item= find_item();
+ if (!item)
+ return (null_value= true);
+ return val_native_with_conversion_from_item(thd, item, to, type_handler());
}
@@ -3358,12 +3453,13 @@ double Item_func_coalesce::real_op()
}
-bool Item_func_coalesce::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_func_coalesce::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed == 1);
for (uint i= 0; i < arg_count; i++)
{
- Datetime dt(current_thd, args[i], fuzzydate & ~TIME_FUZZY_DATES);
+ Datetime_truncation_not_needed dt(thd, args[i],
+ fuzzydate & ~TIME_FUZZY_DATES);
if (!dt.copy_to_mysql_time(ltime, mysql_timestamp_type()))
return (null_value= false);
}
@@ -3371,18 +3467,30 @@ bool Item_func_coalesce::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
}
-bool Item_func_coalesce::time_op(MYSQL_TIME *ltime)
+bool Item_func_coalesce::time_op(THD *thd, MYSQL_TIME *ltime)
{
DBUG_ASSERT(fixed == 1);
for (uint i= 0; i < arg_count; i++)
{
- if (!Time(args[i]).copy_to_mysql_time(ltime))
+ if (!Time(thd, args[i]).copy_to_mysql_time(ltime))
return (null_value= false);
}
return (null_value= true);
}
+bool Item_func_coalesce::native_op(THD *thd, Native *to)
+{
+ DBUG_ASSERT(fixed == 1);
+ for (uint i= 0; i < arg_count; i++)
+ {
+ if (!val_native_with_conversion_from_item(thd, args[i], to, type_handler()))
+ return false;
+ }
+ return (null_value= true);
+}
+
+
my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
@@ -3660,11 +3768,58 @@ Item *in_longlong::create_item(THD *thd)
}
+static int cmp_timestamp(void *cmp_arg,
+ Timestamp_or_zero_datetime *a,
+ Timestamp_or_zero_datetime *b)
+{
+ return a->cmp(*b);
+}
+
+
+in_timestamp::in_timestamp(THD *thd, uint elements)
+ :in_vector(thd, elements, sizeof(Value), (qsort2_cmp) cmp_timestamp, 0)
+{}
+
+
+void in_timestamp::set(uint pos, Item *item)
+{
+ Timestamp_or_zero_datetime *buff= &((Timestamp_or_zero_datetime *) base)[pos];
+ Timestamp_or_zero_datetime_native_null native(current_thd, item, true);
+ if (native.is_null())
+ *buff= Timestamp_or_zero_datetime();
+ else
+ *buff= Timestamp_or_zero_datetime(native);
+}
+
+
+uchar *in_timestamp::get_value(Item *item)
+{
+ Timestamp_or_zero_datetime_native_null native(current_thd, item, true);
+ if (native.is_null())
+ return 0;
+ tmp= Timestamp_or_zero_datetime(native);
+ return (uchar*) &tmp;
+}
+
+
+Item *in_timestamp::create_item(THD *thd)
+{
+ return new (thd->mem_root) Item_timestamp_literal(thd);
+}
+
+
+void in_timestamp::value_to_item(uint pos, Item *item)
+{
+ const Timestamp_or_zero_datetime &buff= (((Timestamp_or_zero_datetime*) base)[pos]);
+ static_cast<Item_timestamp_literal*>(item)->set_value(buff);
+}
+
+
void in_datetime::set(uint pos,Item *item)
{
struct packed_longlong *buff= &((packed_longlong*) base)[pos];
- buff->val= item->val_datetime_packed();
+ buff->val= item->val_datetime_packed(current_thd);
buff->unsigned_flag= 1L;
}
@@ -3672,13 +3827,22 @@ void in_time::set(uint pos,Item *item)
{
struct packed_longlong *buff= &((packed_longlong*) base)[pos];
- buff->val= item->val_time_packed();
+ buff->val= item->val_time_packed(current_thd);
buff->unsigned_flag= 1L;
}
-uchar *in_temporal::get_value_internal(Item *item, enum_field_types f_type)
+uchar *in_datetime::get_value(Item *item)
{
- tmp.val= item->val_temporal_packed(f_type);
+ tmp.val= item->val_datetime_packed(current_thd);
+ if (item->null_value)
+ return 0;
+ tmp.unsigned_flag= 1L;
+ return (uchar*) &tmp;
+}
+
+uchar *in_time::get_value(Item *item)
+{
+ tmp.val= item->val_time_packed(current_thd);
if (item->null_value)
return 0;
tmp.unsigned_flag= 1L;
@@ -3890,39 +4054,15 @@ bool cmp_item_row::alloc_comparators(THD *thd, uint cols)
void cmp_item_row::store_value(Item *item)
{
DBUG_ENTER("cmp_item_row::store_value");
- THD *thd= current_thd;
- if (!alloc_comparators(thd, item->cols()))
+ DBUG_ASSERT(comparators);
+ DBUG_ASSERT(n == item->cols());
+ item->bring_value();
+ item->null_value= 0;
+ for (uint i=0; i < n; i++)
{
- item->bring_value();
- item->null_value= 0;
- for (uint i=0; i < n; i++)
- {
- if (!comparators[i])
- {
- /**
- Comparators for the row elements that have temporal data types
- are installed at initialization time by prepare_comparators().
- Here we install comparators for the other data types.
- There is a bug in the below code. See MDEV-11511.
- When performing:
- (predicant0,predicant1) IN ((value00,value01),(value10,value11))
- It uses only the data type and the collation of the predicant
- elements only. It should be fixed to aggregate the data type and
- the collation for all elements at the N-th positions of the
- predicate and all values:
- - predicate0, value00, value01
- - predicate1, value10, value11
- */
- Item *elem= item->element_index(i);
- const Type_handler *handler= elem->type_handler();
- DBUG_ASSERT(elem->cmp_type() != TIME_RESULT);
- if (!(comparators[i]=
- handler->make_cmp_item(thd, elem->collation.collation)))
- break; // new failed
- }
- comparators[i]->store_value(item->element_index(i));
- item->null_value|= item->element_index(i)->null_value;
- }
+ DBUG_ASSERT(comparators[i]);
+ comparators[i]->store_value(item->element_index(i));
+ item->null_value|= item->element_index(i)->null_value;
}
DBUG_VOID_RETURN;
}
@@ -4015,9 +4155,8 @@ int cmp_item_decimal::cmp_not_null(const Value *val)
int cmp_item_decimal::cmp(Item *arg)
{
- my_decimal tmp_buf, *tmp= arg->val_decimal(&tmp_buf);
- return (m_null_value || arg->null_value) ?
- UNKNOWN : (my_decimal_cmp(&value, tmp) != 0);
+ VDec tmp(arg);
+ return m_null_value || tmp.is_null() ? UNKNOWN : (tmp.cmp(&value) != 0);
}
@@ -4034,14 +4173,6 @@ cmp_item* cmp_item_decimal::make_same()
}
-void cmp_item_temporal::store_value_internal(Item *item,
- enum_field_types f_type)
-{
- value= item->val_temporal_packed(f_type);
- m_null_value= item->null_value;
-}
-
-
int cmp_item_datetime::cmp_not_null(const Value *val)
{
DBUG_ASSERT(!val->is_null());
@@ -4052,7 +4183,7 @@ int cmp_item_datetime::cmp_not_null(const Value *val)
int cmp_item_datetime::cmp(Item *arg)
{
- const bool rc= value != arg->val_datetime_packed();
+ const bool rc= value != arg->val_datetime_packed(current_thd);
return (m_null_value || arg->null_value) ? UNKNOWN : rc;
}
@@ -4067,7 +4198,7 @@ int cmp_item_time::cmp_not_null(const Value *val)
int cmp_item_time::cmp(Item *arg)
{
- const bool rc= value != arg->val_time_packed();
+ const bool rc= value != arg->val_time_packed(current_thd);
return (m_null_value || arg->null_value) ? UNKNOWN : rc;
}
@@ -4091,6 +4222,49 @@ cmp_item *cmp_item_time::make_same()
}
+void cmp_item_timestamp::store_value(Item *item)
+{
+ item->val_native_with_conversion(current_thd, &m_native,
+ &type_handler_timestamp2);
+ m_null_value= item->null_value;
+}
+
+
+int cmp_item_timestamp::cmp_not_null(const Value *val)
+{
+ /*
+ This method will be implemented when we add this syntax:
+ SELECT TIMESTAMP WITH LOCAL TIME ZONE '2001-01-01 10:20:30'
+ For now TIMESTAMP is compared to non-TIMESTAMP using DATETIME.
+ */
+ DBUG_ASSERT(0);
+ return 0;
+}
+
+
+int cmp_item_timestamp::cmp(Item *arg)
+{
+ THD *thd= current_thd;
+ Timestamp_or_zero_datetime_native_null tmp(thd, arg, true);
+ return m_null_value || tmp.is_null() ? UNKNOWN :
+ type_handler_timestamp2.cmp_native(m_native, tmp) != 0;
+}
+
+
+int cmp_item_timestamp::compare(cmp_item *arg)
+{
+ cmp_item_timestamp *tmp= static_cast<cmp_item_timestamp*>(arg);
+ return type_handler_timestamp2.cmp_native(m_native, tmp->m_native);
+}
+
+
+cmp_item* cmp_item_timestamp::make_same()
+{
+ return new cmp_item_timestamp();
+}
+
+
+
bool Item_func_in::count_sargable_conds(void *arg)
{
((SELECT_LEX*) arg)->cond_count++;
@@ -4311,25 +4485,84 @@ bool Item_func_in::value_list_convert_const_to_int(THD *thd)
}
-/**
- Historically this code installs comparators at initialization time
- for temporal ROW elements only. All other comparators are installed later,
- during the first store_value(). This causes the bug MDEV-11511.
- See also comments in cmp_item_row::store_value().
-*/
-bool cmp_item_row::prepare_comparators(THD *thd, Item **args, uint arg_count)
+bool cmp_item_row::
+ aggregate_row_elements_for_comparison(THD *thd,
+ Type_handler_hybrid_field_type *cmp,
+ Item_args *tmp,
+ const char *funcname,
+ uint col,
+ uint level)
+{
+ DBUG_EXECUTE_IF("cmp_item",
+ {
+ for (uint i= 0 ; i < tmp->argument_count(); i++)
+ {
+ Item *arg= tmp->arguments()[i];
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_UNKNOWN_ERROR, "DBUG: %s[%d,%d] handler=%s",
+ String_space(level).c_ptr(), col, i,
+ arg->type_handler()->name().ptr());
+ }
+ }
+ );
+ bool err= cmp->aggregate_for_comparison(funcname, tmp->arguments(),
+ tmp->argument_count(), true);
+ DBUG_EXECUTE_IF("cmp_item",
+ {
+ if (!err)
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_UNKNOWN_ERROR, "DBUG: %s=> handler=%s",
+ String_space(level).c_ptr(),
+ cmp->type_handler()->name().ptr());
+ }
+ );
+ return err;
+}
+
+
+bool cmp_item_row::prepare_comparators(THD *thd, const char *funcname,
+ const Item_args *args, uint level)
{
+ DBUG_EXECUTE_IF("cmp_item",
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_UNKNOWN_ERROR, "DBUG: %sROW(%d args) level=%d",
+ String_space(level).c_ptr(),
+ args->argument_count(), level););
+ DBUG_ASSERT(args->argument_count() > 0);
+ if (alloc_comparators(thd, args->arguments()[0]->cols()))
+ return true;
+ DBUG_ASSERT(n == args->arguments()[0]->cols());
for (uint col= 0; col < n; col++)
{
- Item *date_arg= find_date_time_item(args, arg_count, col);
- if (date_arg)
+ Item_args tmp;
+ Type_handler_hybrid_field_type cmp;
+
+ if (tmp.alloc_and_extract_row_elements(thd, args, col) ||
+ aggregate_row_elements_for_comparison(thd, &cmp, &tmp,
+ funcname, col, level + 1))
+ return true;
+
+ /*
+ There is a legacy bug (MDEV-11511) in the code below,
+ which should be fixed eventually.
+ When performing:
+ (predicant0,predicant1) IN ((value00,value01),(value10,value11))
+ It uses only the data type and the collation of the predicant
+ elements only. It should be fixed to take into account the data type and
+ the collation for all elements at the N-th positions of the
+ predicate and all values:
+ - predicate0, value00, value01
+ - predicate1, value10, value11
+ */
+ Item *item0= args->arguments()[0]->element_index(col);
+ CHARSET_INFO *collation= item0->collation.collation;
+ if (!(comparators[col]= cmp.type_handler()->make_cmp_item(thd, collation)))
+ return true;
+ if (cmp.type_handler() == &type_handler_row)
{
- // TODO: do like the scalar comparators do
- const Type_handler *h= date_arg->type_handler();
- comparators[col]= h->field_type() == MYSQL_TYPE_TIME ?
- (cmp_item *) new (thd->mem_root) cmp_item_time() :
- (cmp_item *) new (thd->mem_root) cmp_item_datetime();
- if (!comparators[col])
+ // Prepare comparators for ROW elements recursively
+ cmp_item_row *row= static_cast<cmp_item_row*>(comparators[col]);
+ if (row->prepare_comparators(thd, funcname, &tmp, level + 1))
return true;
}
}
@@ -4339,19 +4572,10 @@ bool cmp_item_row::prepare_comparators(THD *thd, Item **args, uint arg_count)
bool Item_func_in::fix_for_row_comparison_using_bisection(THD *thd)
{
- uint cols= args[0]->cols();
if (unlikely(!(array= new (thd->mem_root) in_row(thd, arg_count-1, 0))))
return true;
cmp_item_row *cmp= &((in_row*)array)->tmp;
- if (cmp->alloc_comparators(thd, cols) ||
- cmp->prepare_comparators(thd, args, arg_count))
- return true;
- /*
- Only DATETIME items comparators were initialized.
- Call store_value() to setup others.
- */
- cmp->store_value(args[0]);
- if (unlikely(thd->is_fatal_error)) // OOM
+ if (cmp->prepare_comparators(thd, func_name(), this, 0))
return true;
fix_in_vector();
return false;
@@ -4390,8 +4614,7 @@ bool Item_func_in::fix_for_row_comparison_using_cmp_items(THD *thd)
DBUG_ASSERT(get_comparator_type_handler(0) == &type_handler_row);
DBUG_ASSERT(get_comparator_cmp_item(0));
cmp_item_row *cmp_row= (cmp_item_row*) get_comparator_cmp_item(0);
- return cmp_row->alloc_comparators(thd, args[0]->cols()) ||
- cmp_row->prepare_comparators(thd, args, arg_count);
+ return cmp_row->prepare_comparators(thd, func_name(), this, 0);
}
@@ -4662,7 +4885,7 @@ Item_cond::fix_fields(THD *thd, Item **ref)
const_item_cache= FALSE;
}
- with_sum_func|= item->with_sum_func;
+ join_with_sum_func(item);
with_param|= item->with_param;
with_field|= item->with_field;
m_with_subquery|= item->with_subquery();
@@ -5022,6 +5245,8 @@ bool Item_cond::excl_dep_on_table(table_map tab_map)
bool Item_cond::excl_dep_on_grouping_fields(st_select_lex *sel)
{
+ if (has_rand_bit())
+ return false;
List_iterator_fast<Item> li(list);
Item *item;
while ((item= li++))
@@ -5432,13 +5657,17 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
if (!res2)
return FALSE; // Null argument
- const size_t len = res2->length();
- const char* first = res2->ptr();
- const char* last = first + len - 1;
+ const size_t len= res2->length();
+
/*
len must be > 2 ('%pattern%')
heuristic: only do TurboBM for pattern_len > 2
*/
+ if (len <= 2)
+ return FALSE;
+
+ const char* first= res2->ptr();
+ const char* last= first + len - 1;
if (len > MIN_TURBOBM_PATTERN_LEN + 2 &&
*first == wild_many &&
@@ -6373,76 +6602,53 @@ void Item_equal::add_const(THD *thd, Item *c)
equal_items.push_front(c, thd->mem_root);
return;
}
- Item *const_item= get_const();
- switch (Item_equal::compare_type_handler()->cmp_type()) {
- case TIME_RESULT:
- {
- enum_field_types f_type= context_field->field_type();
- longlong value0= c->val_temporal_packed(f_type);
- longlong value1= const_item->val_temporal_packed(f_type);
- cond_false= c->null_value || const_item->null_value || value0 != value1;
- break;
- }
- case STRING_RESULT:
- {
- String *str1, *str2;
- /*
- Suppose we have an expression (with a string type field) like this:
- WHERE field=const1 AND field=const2 ...
-
- For all pairs field=constXXX we know that:
-
- - Item_func_eq::fix_length_and_dec() performed collation and character
- set aggregation and added character set converters when needed.
- Note, the case like:
- WHERE field=const1 COLLATE latin1_bin AND field=const2
- is not handled here, because the field would be replaced to
- Item_func_set_collation, which cannot get into Item_equal.
- So all constXXX that are handled by Item_equal
- already have compatible character sets with "field".
-
- - Also, Field_str::test_if_equality_guarantees_uniqueness() guarantees
- that the comparison collation of all equalities handled by Item_equal
- match the the collation of the field.
-
- Therefore, at Item_equal::add_const() time all constants constXXX
- should be directly comparable to each other without an additional
- character set conversion.
- It's safe to do val_str() for "const_item" and "c" and compare
- them according to the collation of the *field*.
-
- So in a script like this:
- CREATE TABLE t1 (a VARCHAR(10) COLLATE xxx);
- INSERT INTO t1 VALUES ('a'),('A');
- SELECT * FROM t1 WHERE a='a' AND a='A';
- Item_equal::add_const() effectively rewrites the condition to:
- SELECT * FROM t1 WHERE a='a' AND 'a' COLLATE xxx='A';
- and then to:
- SELECT * FROM t1 WHERE a='a'; // if the two constants were equal
- // e.g. in case of latin1_swedish_ci
- or to:
- SELECT * FROM t1 WHERE FALSE; // if the two constants were not equal
- // e.g. in case of latin1_bin
-
- Note, both "const_item" and "c" can return NULL, e.g.:
- SELECT * FROM t1 WHERE a=NULL AND a='const';
- SELECT * FROM t1 WHERE a='const' AND a=NULL;
- SELECT * FROM t1 WHERE a='const' AND a=(SELECT MAX(a) FROM t2)
- */
- cond_false= !(str1= const_item->val_str(&cmp_value1)) ||
- !(str2= c->val_str(&cmp_value2)) ||
- !str1->eq(str2, compare_collation());
- break;
- }
- default:
- {
- Item_func_eq *func= new (thd->mem_root) Item_func_eq(thd, c, const_item);
- if (func->set_cmp_func())
- return;
- func->quick_fix_field();
- cond_false= !func->val_int();
- }
- }
+
+ /*
+ Suppose we have an expression (with a string type field) like this:
+ WHERE field=const1 AND field=const2 ...
+
+ For all pairs field=constXXX we know that:
+
+ - Item_func_eq::fix_length_and_dec() performed collation and character
+ set aggregation and added character set converters when needed.
+ Note, the case like:
+ WHERE field=const1 COLLATE latin1_bin AND field=const2
+ is not handled here, because the field would be replaced to
+ Item_func_set_collation, which cannot get into Item_equal.
+ So all constXXX that are handled by Item_equal
+ already have compatible character sets with "field".
+
+ - Also, Field_str::test_if_equality_guarantees_uniqueness() guarantees
+ that the comparison collation of all equalities handled by Item_equal
+ match the the collation of the field.
+
+ Therefore, at Item_equal::add_const() time all constants constXXX
+ should be directly comparable to each other without an additional
+ character set conversion.
+ It's safe to do val_str() for "const_item" and "c" and compare
+ them according to the collation of the *field*.
+
+ So in a script like this:
+ CREATE TABLE t1 (a VARCHAR(10) COLLATE xxx);
+ INSERT INTO t1 VALUES ('a'),('A');
+ SELECT * FROM t1 WHERE a='a' AND a='A';
+ Item_equal::add_const() effectively rewrites the condition to:
+ SELECT * FROM t1 WHERE a='a' AND 'a' COLLATE xxx='A';
+ and then to:
+ SELECT * FROM t1 WHERE a='a'; // if the two constants were equal
+ // e.g. in case of latin1_swedish_ci
+ or to:
+ SELECT * FROM t1 WHERE FALSE; // if the two constants were not equal
+ // e.g. in case of latin1_bin
+
+ Note, both "const_item" and "c" can return NULL, e.g.:
+ SELECT * FROM t1 WHERE a=NULL AND a='const';
+ SELECT * FROM t1 WHERE a='const' AND a=NULL;
+ SELECT * FROM t1 WHERE a='const' AND a=(SELECT MAX(a) FROM t2)
+ */
+
+ cond_false= !Item_equal::compare_type_handler()->Item_eq_value(thd, this, c,
+ get_const());
if (with_const && equal_items.elements == 1)
cond_true= TRUE;
if (cond_false || cond_true)
@@ -6747,7 +6953,7 @@ bool Item_equal::fix_fields(THD *thd, Item **ref)
used_tables_cache|= item->used_tables();
tmp_table_map= item->not_null_tables();
not_null_tables_cache|= tmp_table_map;
- DBUG_ASSERT(!item->with_sum_func && !item->with_subquery());
+ DBUG_ASSERT(!item->with_sum_func() && !item->with_subquery());
if (item->maybe_null)
maybe_null= 1;
if (!item->get_item_equal())
@@ -7197,3 +7403,186 @@ Item_bool_rowready_func2* Le_creator::create_swap(THD *thd, Item *a, Item *b) co
{
return new(thd->mem_root) Item_func_ge(thd, b, a);
}
+
+
+bool
+Item_equal::excl_dep_on_grouping_fields(st_select_lex *sel)
+{
+ Item_equal_fields_iterator it(*this);
+ Item *item;
+
+ while ((item=it++))
+ {
+ if (item->excl_dep_on_grouping_fields(sel))
+ {
+ set_extraction_flag(FULL_EXTRACTION_FL);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
+ @brief
+ Transform multiple equality into list of equalities
+
+ @param thd the thread handle
+ @param equalities the list where created equalities are stored
+ @param checker the checker callback function to be applied to the nodes
+ of the tree of the object to check if multiple equality
+ elements can be used to create equalities
+ @param arg parameter to be passed to the checker
+ @param clone_const true <=> clone the constant member if there is any
+
+ @details
+ How the method works on examples:
+
+ Example 1:
+ It takes MULT_EQ(x,a,b) and tries to create from its elements a set of
+ equalities {(x=a),(x=b)}.
+
+ Example 2:
+ It takes MULT_EQ(1,a,b) and tries to create from its elements a set of
+ equalities {(a=1),(a=b)}.
+
+ How it is done:
+
+ 1. If there is a constant member c the first non-constant member x for
+ which the function checker returns true is taken and an item for
+ the equality x=c is created. When constructing the equality item
+ the left part of the equality is always taken as a clone of x while
+ the right part is taken as a clone of c only if clone_const == true.
+
+ 2. After this all equalities of the form x=a (where x designates the first
+ non-constant member for which checker returns true and a is some other
+ such member of the multiplle equality) are created. When constructing
+ an equality item both its parts are taken as clones of x and a.
+
+ Suppose in the examples above that for 'x', 'a', and 'b' the function
+ checker returns true.
+
+ Example 1:
+ the equality (x=a) is built
+ the equality (x=b) is built
+
+ Example 2:
+ the equality (a=1) is built
+ the equality (a=b) is built
+
+ 3. As a result we get a set of equalities built with the elements of
+ this multiple equality. They are saved in the equality list.
+
+ Example 1:
+ {(x=a),(x=b)}
+
+ Example 2:
+ {(a=1),(a=b)}
+
+ @note
+ This method is called for condition pushdown into materialized
+ derived table/view, and IN subquery, and pushdown from HAVING into WHERE.
+ When it is called for pushdown from HAVING the empty checker is passed.
+ This is because in this case the elements of the multiple equality don't
+ need to be checked if they can be used to build equalities: either all
+ equalities can be pushed or none of them can be pushed.
+ When the function is called for pushdown from HAVING the value of the
+ parameter clone_const is always false. In other cases it's always true.
+
+ @retval true if an error occurs
+ @retval false otherwise
+*/
+
+bool Item_equal::create_pushable_equalities(THD *thd,
+ List<Item> *equalities,
+ Pushdown_checker checker,
+ uchar *arg,
+ bool clone_const)
+{
+ Item *item;
+ Item *left_item= NULL;
+ Item *right_item = get_const();
+ Item_equal_fields_iterator it(*this);
+
+ while ((item=it++))
+ {
+ left_item= item;
+ if (checker && !((item->*checker) (arg)))
+ continue;
+ break;
+ }
+
+ if (!left_item)
+ return false;
+
+ if (right_item)
+ {
+ Item_func_eq *eq= 0;
+ Item *left_item_clone= left_item->build_clone(thd);
+ Item *right_item_clone= !clone_const ?
+ right_item : right_item->build_clone(thd);
+ if (!left_item_clone || !right_item_clone)
+ return true;
+ eq= new (thd->mem_root) Item_func_eq(thd,
+ left_item_clone,
+ right_item_clone);
+ if (!eq || equalities->push_back(eq, thd->mem_root))
+ return true;
+ if (!clone_const)
+ right_item->set_extraction_flag(IMMUTABLE_FL);
+ }
+
+ while ((item=it++))
+ {
+ if (checker && !((item->*checker) (arg)))
+ continue;
+ Item_func_eq *eq= 0;
+ Item *left_item_clone= left_item->build_clone(thd);
+ Item *right_item_clone= item->build_clone(thd);
+ if (!(left_item_clone && right_item_clone))
+ return true;
+ left_item_clone->set_item_equal(NULL);
+ right_item_clone->set_item_equal(NULL);
+ eq= new (thd->mem_root) Item_func_eq(thd,
+ right_item_clone,
+ left_item_clone);
+ if (!eq || equalities->push_back(eq, thd->mem_root))
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ Transform multiple equality into the AND condition of equalities.
+
+ Example:
+ MULT_EQ(x,a,b)
+ =>
+ (x=a) AND (x=b)
+
+ Equalities are built in Item_equal::create_pushable_equalities() method
+ using elements of this multiple equality. The result of this method is
+ saved in an equality list.
+ This method returns the condition where the elements of the equality list
+ are anded.
+*/
+
+Item *Item_equal::multiple_equality_transformer(THD *thd, uchar *arg)
+{
+ List<Item> equalities;
+ if (create_pushable_equalities(thd, &equalities, 0, 0, false))
+ return 0;
+
+ switch (equalities.elements)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return equalities.head();
+ break;
+ default:
+ return new (thd->mem_root) Item_cond_and(thd, equalities);
+ break;
+ }
+}
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 4c88f5b274f..d77da3fa48c 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -68,6 +68,7 @@ class Arg_comparator: public Sql_alloc
if (val1 == val2) return 0;
return 1;
}
+ NativeBuffer<STRING_BUFFER_USUAL_SIZE> m_native1, m_native2;
public:
/* Allow owner function to use string buffers. */
String value1, value2;
@@ -89,6 +90,7 @@ public:
bool set_cmp_func_string();
bool set_cmp_func_time();
bool set_cmp_func_datetime();
+ bool set_cmp_func_native();
bool set_cmp_func_int();
bool set_cmp_func_real();
bool set_cmp_func_decimal();
@@ -121,6 +123,8 @@ public:
int compare_e_datetime();
int compare_time();
int compare_e_time();
+ int compare_native();
+ int compare_e_native();
int compare_json_str_basic(Item *j, Item *s);
int compare_json_str();
int compare_str_json();
@@ -128,6 +132,9 @@ public:
int compare_e_json_str();
int compare_e_str_json();
+ void min_max_update_field_native(THD *thd, Field *field, Item *item,
+ int cmp_sign);
+
Item** cache_converted_constant(THD *thd, Item **value, Item **cache,
const Type_handler *type);
inline bool is_owner_equal_func()
@@ -152,7 +159,8 @@ public:
class SEL_ARG;
struct KEY_PART;
-class Item_bool_func :public Item_int_func
+class Item_bool_func :public Item_int_func,
+ public Type_cmp_attributes
{
protected:
/*
@@ -215,9 +223,9 @@ public:
Item_bool_func(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c) {}
Item_bool_func(THD *thd, List<Item> &list): Item_int_func(thd, list) { }
Item_bool_func(THD *thd, Item_bool_func *item) :Item_int_func(thd, item) {}
- const Type_handler *type_handler() const { return &type_handler_long; }
- bool is_bool_type() { return true; }
- virtual CHARSET_INFO *compare_collation() const { return NULL; }
+ const Type_handler *type_handler() const { return &type_handler_bool; }
+ const Type_handler *fixed_type_handler() const { return &type_handler_bool; }
+ CHARSET_INFO *compare_collation() const { return NULL; }
bool fix_length_and_dec() { decimals=0; max_length=1; return FALSE; }
uint decimal_precision() const { return 1; }
bool need_parentheses_in_default() { return true; }
@@ -824,11 +832,7 @@ class Item_func_ne :public Item_bool_rowready_func2
{
protected:
SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param,
- Field *field, Item *value)
- {
- DBUG_ENTER("Item_func_ne::get_func_mm_tree");
- DBUG_RETURN(get_ne_mm_tree(param, field, value, value));
- }
+ Field *field, Item *value);
public:
Item_func_ne(THD *thd, Item *a, Item *b):
Item_bool_rowready_func2(thd, a, b) {}
@@ -894,6 +898,7 @@ class Item_func_between :public Item_func_opt_neg
protected:
SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param,
Field *field, Item *value);
+ bool val_int_cmp_int_finalize(longlong value, longlong a, longlong b);
public:
String value0,value1,value2;
Item_func_between(THD *thd, Item *a, Item *b, Item *c):
@@ -934,7 +939,9 @@ public:
{ return get_item_copy<Item_func_between>(thd, this); }
longlong val_int_cmp_string();
- longlong val_int_cmp_temporal();
+ longlong val_int_cmp_datetime();
+ longlong val_int_cmp_time();
+ longlong val_int_cmp_native();
longlong val_int_cmp_int();
longlong val_int_cmp_real();
longlong val_int_cmp_decimal();
@@ -957,7 +964,7 @@ public:
{
if (agg_arg_charsets_for_comparison(cmp_collation, args, 2))
return TRUE;
- fix_char_length(2);
+ fix_char_length(2); // returns "1" or "0" or "-1"
return FALSE;
}
Item *get_copy(THD *thd)
@@ -1011,8 +1018,9 @@ public:
longlong int_op();
String *str_op(String *);
my_decimal *decimal_op(my_decimal *);
- bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool time_op(MYSQL_TIME *ltime);
+ bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool time_op(THD *thd, MYSQL_TIME *ltime);
+ bool native_op(THD *thd, Native *to);
bool fix_length_and_dec()
{
if (aggregate_for_result(func_name(), args, arg_count, true))
@@ -1090,8 +1098,9 @@ public:
longlong int_op();
String *str_op(String *str);
my_decimal *decimal_op(my_decimal *);
- bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool time_op(MYSQL_TIME *ltime);
+ bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool time_op(THD *thd, MYSQL_TIME *ltime);
+ bool native_op(THD *thd, Native *to);
bool fix_length_and_dec()
{
if (Item_func_case_abbreviation2::fix_length_and_dec2(args))
@@ -1125,12 +1134,12 @@ public:
:Item_func_case_abbreviation2(thd, a, b, c)
{ }
- bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- Datetime dt(current_thd, find_item(), fuzzydate);
+ Datetime_truncation_not_needed dt(thd, find_item(), fuzzydate);
return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type()));
}
- bool time_op(MYSQL_TIME *ltime)
+ bool time_op(THD *thd, MYSQL_TIME *ltime)
{
return (null_value= Time(find_item()).copy_to_mysql_time(ltime));
}
@@ -1150,6 +1159,11 @@ public:
{
return val_str_from_item(find_item(), str);
}
+ bool native_op(THD *thd, Native *to)
+ {
+ return val_native_with_conversion_from_item(thd, find_item(), to,
+ type_handler());
+ }
};
@@ -1243,12 +1257,13 @@ public:
Item_func_hybrid_field_type::cleanup();
arg_count= 2; // See the comment to the constructor
}
- bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool time_op(MYSQL_TIME *ltime);
+ bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool time_op(THD *thd, MYSQL_TIME *ltime);
double real_op();
longlong int_op();
String *str_op(String *str);
my_decimal *decimal_op(my_decimal *);
+ bool native_op(THD *thd, Native *to);
bool fix_length_and_dec();
bool walk(Item_processor processor, bool walk_subquery, void *arg);
const char *func_name() const { return "nullif"; }
@@ -1285,7 +1300,11 @@ public:
{ reset_first_arg_if_needed(); return this; }
Item *derived_field_transformer_for_where(THD *thd, uchar *arg)
{ reset_first_arg_if_needed(); return this; }
- Item *derived_grouping_field_transformer_for_where(THD *thd, uchar *arg)
+ Item *grouping_field_transformer_for_where(THD *thd, uchar *arg)
+ { reset_first_arg_if_needed(); return this; }
+ Item *in_subq_field_transformer_for_where(THD *thd, uchar *arg)
+ { reset_first_arg_if_needed(); return this; }
+ Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg)
{ reset_first_arg_if_needed(); return this; }
};
@@ -1408,13 +1427,24 @@ public:
};
+class in_timestamp :public in_vector
+{
+ Timestamp_or_zero_datetime tmp;
+public:
+ in_timestamp(THD *thd, uint elements);
+ void set(uint pos,Item *item);
+ uchar *get_value(Item *item);
+ Item* create_item(THD *thd);
+ void value_to_item(uint pos, Item *item);
+ const Type_handler *type_handler() const { return &type_handler_timestamp2; }
+};
+
+
/*
Class to represent a vector of constant DATE/DATETIME values.
*/
class in_temporal :public in_longlong
{
-protected:
- uchar *get_value_internal(Item *item, enum_field_types f_type);
public:
/* Cache for the left item. */
@@ -1427,8 +1457,6 @@ public:
Item_datetime *dt= static_cast<Item_datetime*>(item);
dt->set(val->val, type_handler()->mysql_timestamp_type());
}
- uchar *get_value(Item *item)
- { return get_value_internal(item, type_handler()->field_type()); }
friend int cmp_longlong(void *cmp_arg, packed_longlong *a,packed_longlong *b);
};
@@ -1440,6 +1468,7 @@ public:
:in_temporal(thd, elements)
{}
void set(uint pos,Item *item);
+ uchar *get_value(Item *item);
const Type_handler *type_handler() const { return &type_handler_datetime2; }
};
@@ -1451,6 +1480,7 @@ public:
:in_temporal(thd, elements)
{}
void set(uint pos,Item *item);
+ uchar *get_value(Item *item);
const Type_handler *type_handler() const { return &type_handler_time2; }
};
@@ -1625,7 +1655,6 @@ class cmp_item_temporal: public cmp_item_scalar
{
protected:
longlong value;
- void store_value_internal(Item *item, enum_field_types type);
public:
cmp_item_temporal() {}
int compare(cmp_item *ci);
@@ -1640,7 +1669,8 @@ public:
{ }
void store_value(Item *item)
{
- store_value_internal(item, MYSQL_TYPE_DATETIME);
+ value= item->val_datetime_packed(current_thd);
+ m_null_value= item->null_value;
}
int cmp_not_null(const Value *val);
int cmp(Item *arg);
@@ -1656,13 +1686,28 @@ public:
{ }
void store_value(Item *item)
{
- store_value_internal(item, MYSQL_TYPE_TIME);
+ value= item->val_time_packed(current_thd);
+ m_null_value= item->null_value;
}
int cmp_not_null(const Value *val);
int cmp(Item *arg);
cmp_item *make_same();
};
+
+class cmp_item_timestamp: public cmp_item_scalar
+{
+ Timestamp_or_zero_datetime_native m_native;
+public:
+ cmp_item_timestamp() :cmp_item_scalar() { }
+ void store_value(Item *item);
+ int cmp_not_null(const Value *val);
+ int cmp(Item *arg);
+ int compare(cmp_item *ci);
+ cmp_item *make_same();
+};
+
+
class cmp_item_real : public cmp_item_scalar
{
double value;
@@ -1894,7 +1939,7 @@ class Predicant_to_list_comparator
return UNKNOWN;
return in_item->cmp(args->arguments()[m_comparators[i].m_arg_index]);
}
- int cmp_args_nulls_equal(Item_args *args, uint i)
+ int cmp_args_nulls_equal(THD *thd, Item_args *args, uint i)
{
Predicant_to_value_comparator *cmp=
&m_comparators[m_comparators[i].m_handler_index];
@@ -1905,7 +1950,7 @@ class Predicant_to_list_comparator
ValueBuffer<MAX_FIELD_WIDTH> val;
if (m_comparators[i].m_handler_index == i)
in_item->store_value(predicant);
- m_comparators[i].m_handler->Item_save_in_value(arg, &val);
+ m_comparators[i].m_handler->Item_save_in_value(thd, arg, &val);
if (predicant->null_value && val.is_null())
return FALSE; // Two nulls are equal
if (predicant->null_value || val.is_null())
@@ -2086,12 +2131,12 @@ public:
/*
Same as above, but treats two NULLs as equal, e.g. as in DECODE_ORACLE().
*/
- bool cmp_nulls_equal(Item_args *args, uint *idx)
+ bool cmp_nulls_equal(THD *thd, Item_args *args, uint *idx)
{
for (uint i= 0 ; i < m_comparator_count ; i++)
{
DBUG_ASSERT(m_comparators[i].m_handler != NULL);
- if (cmp_args_nulls_equal(args, i) == FALSE)
+ if (cmp_args_nulls_equal(thd, args, i) == FALSE)
{
*idx= m_comparators[i].m_arg_index;
return false; // Found a matching value
@@ -2129,8 +2174,9 @@ public:
longlong int_op();
String *str_op(String *);
my_decimal *decimal_op(my_decimal *);
- bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool time_op(MYSQL_TIME *ltime);
+ bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool time_op(THD *thd, MYSQL_TIME *ltime);
+ bool native_op(THD *thd, Native *to);
bool fix_fields(THD *thd, Item **ref);
table_map not_null_tables() const { return 0; }
const char *func_name() const { return "case"; }
@@ -2426,12 +2472,19 @@ class cmp_item_row :public cmp_item
{
cmp_item **comparators;
uint n;
+ bool alloc_comparators(THD *thd, uint n);
+ bool aggregate_row_elements_for_comparison(THD *thd,
+ Type_handler_hybrid_field_type *cmp,
+ Item_args *tmp,
+ const char *funcname,
+ uint col,
+ uint level);
public:
cmp_item_row(): comparators(0), n(0) {}
~cmp_item_row();
void store_value(Item *item);
- bool alloc_comparators(THD *thd, uint n);
- bool prepare_comparators(THD *, Item **args, uint arg_count);
+ bool prepare_comparators(THD *, const char *funcname,
+ const Item_args *args, uint level);
int cmp(Item *arg);
int cmp_not_null(const Value *val)
{
@@ -2512,9 +2565,8 @@ public:
{
Field *field=((Item_field*) args[0]->real_item())->field;
- if (((field->type() == MYSQL_TYPE_DATE) ||
- (field->type() == MYSQL_TYPE_DATETIME)) &&
- (field->flags & NOT_NULL_FLAG))
+ if ((field->flags & NOT_NULL_FLAG) &&
+ field->type_handler()->cond_notnull_field_isnull_to_field_eq_zero())
return true;
}
return false;
@@ -2635,6 +2687,9 @@ public:
Item_bool_func2(thd, a, b), canDoTurboBM(FALSE), pattern(0), pattern_len(0),
bmGs(0), bmBc(0), escape_item(escape_arg),
escape_used_in_parsing(escape_used), use_sampling(0), negated(0) {}
+
+ bool get_negated() const { return negated; } // Used by ColumnStore
+
Sql_mode_dependency value_depends_on_sql_mode() const;
longlong val_int();
enum Functype functype() const { return LIKE_FUNC; }
@@ -3098,7 +3153,6 @@ class Item_equal: public Item_bool_func
const Type_handler *m_compare_handler;
CHARSET_INFO *m_compare_collation;
- String cmp_value1, cmp_value2;
public:
COND_EQUAL *upper_levels; /* multiple equalities of upper and levels */
@@ -3156,8 +3210,16 @@ public:
{
return used_tables() & tab_map;
}
+ bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred);
+ bool excl_dep_on_grouping_fields(st_select_lex *sel);
+ bool create_pushable_equalities(THD *thd, List<Item> *equalities,
+ Pushdown_checker checker, uchar *arg,
+ bool clone_const);
+ /* Return the number of elements in this multiple equality */
+ uint elements_count() { return equal_items.elements; }
friend class Item_equal_fields_iterator;
bool count_sargable_conds(void *arg);
+ Item *multiple_equality_transformer(THD *thd, uchar *arg);
friend class Item_equal_iterator<List_iterator_fast,Item>;
friend class Item_equal_iterator<List_iterator,Item>;
friend Item *eliminate_item_equal(THD *thd, COND *cond,
@@ -3193,6 +3255,10 @@ public:
else
current_level= cond_equal.current_level;
}
+ bool is_empty()
+ {
+ return (current_level.elements == 0);
+ }
};
@@ -3307,11 +3373,8 @@ public:
inline bool is_cond_and(Item *item)
{
- if (item->type() != Item::COND_ITEM)
- return FALSE;
-
- Item_cond *cond_item= (Item_cond*) item;
- return (cond_item->functype() == Item_func::COND_AND_FUNC);
+ Item_func *func_item= item->get_item_func();
+ return func_item && func_item->functype() == Item_func::COND_AND_FUNC;
}
class Item_cond_or :public Item_cond
@@ -3412,11 +3475,8 @@ public:
inline bool is_cond_or(Item *item)
{
- if (item->type() != Item::COND_ITEM)
- return FALSE;
-
- Item_cond *cond_item= (Item_cond*) item;
- return (cond_item->functype() == Item_func::COND_OR_FUNC);
+ Item_func *func_item= item->get_item_func();
+ return func_item && func_item->functype() == Item_func::COND_OR_FUNC;
}
Item *and_expressions(Item *a, Item *b, Item **org_item);
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 70ac2abe959..ffc90e16b8f 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -3217,6 +3217,45 @@ protected:
};
#endif
+#ifdef WITH_WSREP
+class Create_func_wsrep_last_written_gtid : public Create_func_arg0
+{
+public:
+ virtual Item *create_builder(THD *thd);
+
+ static Create_func_wsrep_last_written_gtid s_singleton;
+
+protected:
+ Create_func_wsrep_last_written_gtid() {}
+ virtual ~Create_func_wsrep_last_written_gtid() {}
+};
+
+
+class Create_func_wsrep_last_seen_gtid : public Create_func_arg0
+{
+public:
+ virtual Item *create_builder(THD *thd);
+
+ static Create_func_wsrep_last_seen_gtid s_singleton;
+
+protected:
+ Create_func_wsrep_last_seen_gtid() {}
+ virtual ~Create_func_wsrep_last_seen_gtid() {}
+};
+
+
+class Create_func_wsrep_sync_wait_upto : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
+
+ static Create_func_wsrep_sync_wait_upto s_singleton;
+
+protected:
+ Create_func_wsrep_sync_wait_upto() {}
+ virtual ~Create_func_wsrep_sync_wait_upto() {}
+};
+#endif /* WITH_WSREP */
#ifdef HAVE_SPATIAL
class Create_func_x : public Create_func_arg1
@@ -3511,12 +3550,11 @@ Create_sp_func::create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name,
sph->add_used_routine(lex, thd, qname);
if (pkgname.m_name.length)
sp_handler_package_body.add_used_routine(lex, thd, &pkgname);
+ Name_resolution_context *ctx= lex->current_context();
if (arg_count > 0)
- func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(),
- qname, sph, *item_list);
+ func= new (thd->mem_root) Item_func_sp(thd, ctx, qname, sph, *item_list);
else
- func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(),
- qname, sph);
+ func= new (thd->mem_root) Item_func_sp(thd, ctx, qname, sph);
lex->safe_to_cache_query= 0;
return func;
@@ -3661,7 +3699,7 @@ Create_func_addtime Create_func_addtime::s_singleton;
Item*
Create_func_addtime::create_2_arg(THD *thd, Item *arg1, Item *arg2)
{
- return new (thd->mem_root) Item_func_add_time(thd, arg1, arg2, 0, 0);
+ return new (thd->mem_root) Item_func_add_time(thd, arg1, arg2, false);
}
@@ -6172,7 +6210,26 @@ Create_func_name_const Create_func_name_const::s_singleton;
Item*
Create_func_name_const::create_2_arg(THD *thd, Item *arg1, Item *arg2)
{
- return new (thd->mem_root) Item_name_const(thd, arg1, arg2);
+ if (!arg1->basic_const_item())
+ goto err;
+
+ if (arg2->basic_const_item())
+ return new (thd->mem_root) Item_name_const(thd, arg1, arg2);
+
+ if (arg2->type() == Item::FUNC_ITEM)
+ {
+ Item_func *value_func= (Item_func *) arg2;
+ if (value_func->functype() != Item_func::COLLATE_FUNC &&
+ value_func->functype() != Item_func::NEG_FUNC)
+ goto err;
+
+ if (!value_func->key_item()->basic_const_item())
+ goto err;
+ return new (thd->mem_root) Item_name_const(thd, arg1, arg2);
+ }
+err:
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST");
+ return NULL;
}
@@ -6726,7 +6783,7 @@ Create_func_subtime Create_func_subtime::s_singleton;
Item*
Create_func_subtime::create_2_arg(THD *thd, Item *arg1, Item *arg2)
{
- return new (thd->mem_root) Item_func_add_time(thd, arg1, arg2, 0, 1);
+ return new (thd->mem_root) Item_func_add_time(thd, arg1, arg2, true);
}
@@ -6955,6 +7012,63 @@ Create_func_within::create_2_arg(THD *thd, Item *arg1, Item *arg2)
}
#endif
+#ifdef WITH_WSREP
+Create_func_wsrep_last_written_gtid
+Create_func_wsrep_last_written_gtid::s_singleton;
+
+Item*
+Create_func_wsrep_last_written_gtid::create_builder(THD *thd)
+{
+ thd->lex->safe_to_cache_query= 0;
+ return new (thd->mem_root) Item_func_wsrep_last_written_gtid(thd);
+}
+
+
+Create_func_wsrep_last_seen_gtid
+Create_func_wsrep_last_seen_gtid::s_singleton;
+
+Item*
+Create_func_wsrep_last_seen_gtid::create_builder(THD *thd)
+{
+ thd->lex->safe_to_cache_query= 0;
+ return new (thd->mem_root) Item_func_wsrep_last_seen_gtid(thd);
+}
+
+
+Create_func_wsrep_sync_wait_upto
+Create_func_wsrep_sync_wait_upto::s_singleton;
+
+Item*
+Create_func_wsrep_sync_wait_upto::create_native(THD *thd,
+ LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+ Item *param_1, *param_2;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count)
+ {
+ case 1:
+ param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_wsrep_sync_wait_upto(thd, param_1);
+ break;
+ case 2:
+ param_1= item_list->pop();
+ param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_wsrep_sync_wait_upto(thd, param_1, param_2);
+ break;
+ default:
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ break;
+ }
+ thd->lex->safe_to_cache_query= 0;
+ return func;
+}
+#endif /* WITH_WSREP */
#ifdef HAVE_SPATIAL
Create_func_x Create_func_x::s_singleton;
@@ -7400,6 +7514,11 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("WEEKDAY") }, BUILDER(Create_func_weekday)},
{ { STRING_WITH_LEN("WEEKOFYEAR") }, BUILDER(Create_func_weekofyear)},
{ { STRING_WITH_LEN("WITHIN") }, GEOM_BUILDER(Create_func_within)},
+#ifdef WITH_WSREP
+ { { STRING_WITH_LEN("WSREP_LAST_WRITTEN_GTID") }, BUILDER(Create_func_wsrep_last_written_gtid)},
+ { { STRING_WITH_LEN("WSREP_LAST_SEEN_GTID") }, BUILDER(Create_func_wsrep_last_seen_gtid)},
+ { { STRING_WITH_LEN("WSREP_SYNC_WAIT_UPTO_GTID") }, BUILDER(Create_func_wsrep_sync_wait_upto)},
+#endif /* WITH_WSREP */
{ { STRING_WITH_LEN("X") }, GEOM_BUILDER(Create_func_x)},
{ { STRING_WITH_LEN("Y") }, GEOM_BUILDER(Create_func_y)},
{ { STRING_WITH_LEN("YEARWEEK") }, BUILDER(Create_func_year_week)},
@@ -7519,84 +7638,6 @@ find_qualified_function_builder(THD *thd)
}
-static bool
-have_important_literal_warnings(const MYSQL_TIME_STATUS *status)
-{
- return (status->warnings & ~MYSQL_TIME_NOTE_TRUNCATED) != 0;
-}
-
-
-/**
- Builder for datetime literals:
- TIME'00:00:00', DATE'2001-01-01', TIMESTAMP'2001-01-01 00:00:00'.
- @param thd The current thread
- @param str Character literal
- @param length Length of str
- @param type Type of literal (TIME, DATE or DATETIME)
- @param send_error Whether to generate an error on failure
-*/
-
-Item *create_temporal_literal(THD *thd,
- const char *str, size_t length,
- CHARSET_INFO *cs,
- enum_field_types type,
- bool send_error)
-{
- MYSQL_TIME_STATUS status;
- MYSQL_TIME ltime;
- Item *item= NULL;
- sql_mode_t flags= sql_mode_for_dates(thd);
-
- switch(type)
- {
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_NEWDATE:
- if (!str_to_datetime(cs, str, length, &ltime, flags, &status) &&
- ltime.time_type == MYSQL_TIMESTAMP_DATE && !status.warnings)
- item= new (thd->mem_root) Item_date_literal(thd, &ltime);
- break;
- case MYSQL_TYPE_DATETIME:
- if (!str_to_datetime(cs, str, length, &ltime, flags, &status) &&
- ltime.time_type == MYSQL_TIMESTAMP_DATETIME &&
- !have_important_literal_warnings(&status))
- item= new (thd->mem_root) Item_datetime_literal(thd, &ltime,
- status.precision);
- break;
- case MYSQL_TYPE_TIME:
- if (!str_to_time(cs, str, length, &ltime, 0, &status) &&
- ltime.time_type == MYSQL_TIMESTAMP_TIME &&
- !have_important_literal_warnings(&status))
- item= new (thd->mem_root) Item_time_literal(thd, &ltime,
- status.precision);
- break;
- default:
- DBUG_ASSERT(0);
- }
-
- if (likely(item))
- {
- if (status.warnings) // e.g. a note on nanosecond truncation
- {
- ErrConvString err(str, length, cs);
- make_truncated_value_warning(thd,
- Sql_condition::time_warn_level(status.warnings),
- &err, ltime.time_type, NULL, NULL, NULL);
- }
- return item;
- }
-
- if (send_error)
- {
- const char *typestr=
- (type == MYSQL_TYPE_DATE) ? "DATE" :
- (type == MYSQL_TYPE_TIME) ? "TIME" : "DATETIME";
- ErrConvString err(str, length, thd->variables.character_set_client);
- my_error(ER_WRONG_VALUE, MYF(0), typestr, err.ptr());
- }
- return NULL;
-}
-
-
static List<Item> *create_func_dyncol_prepare(THD *thd,
DYNCALL_CREATE_DEF **dfs,
List<DYNCALL_CREATE_DEF> &list)
diff --git a/sql/item_create.h b/sql/item_create.h
index 7e92016ab96..bb129a14d40 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -191,21 +191,6 @@ protected:
#endif
-Item *create_temporal_literal(THD *thd,
- const char *str, size_t length,
- CHARSET_INFO *cs,
- enum_field_types type,
- bool send_error);
-inline
-Item *create_temporal_literal(THD *thd, const String *str,
- enum_field_types type,
- bool send_error)
-{
- return create_temporal_literal(thd,
- str->ptr(), str->length(), str->charset(),
- type, send_error);
-}
-
struct Native_func_registry
{
LEX_CSTRING name;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index a6d9ecc1d2f..d50fb22a154 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -134,7 +134,7 @@ void Item_func::sync_with_sum_func_and_with_field(List<Item> &list)
Item *item;
while ((item= li++))
{
- with_sum_func|= item->with_sum_func;
+ join_with_sum_func(item);
with_window_func|= item->with_window_func;
with_field|= item->with_field;
with_param|= item->with_param;
@@ -356,7 +356,7 @@ Item_func::fix_fields(THD *thd, Item **ref)
if (item->maybe_null)
maybe_null=1;
- with_sum_func= with_sum_func || item->with_sum_func;
+ join_with_sum_func(item);
with_param= with_param || item->with_param;
with_window_func= with_window_func || item->with_window_func;
with_field= with_field || item->with_field;
@@ -381,7 +381,7 @@ Item_func::quick_fix_field()
{
for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
{
- if (!(*arg)->fixed)
+ if (!(*arg)->is_fixed())
(*arg)->quick_fix_field();
}
}
@@ -732,7 +732,7 @@ void Item_func::signal_divide_by_null()
Item *Item_func::get_tmp_table_item(THD *thd)
{
- if (!with_sum_func && !const_item())
+ if (!Item_func::with_sum_func() && !const_item())
return new (thd->mem_root) Item_temptable_field(thd, result_field);
return copy_or_same(thd);
}
@@ -804,51 +804,6 @@ bool Item_func_plus::fix_length_and_dec(void)
}
-String *Item_func_hybrid_field_type::val_str_from_decimal_op(String *str)
-{
- my_decimal decimal_value, *val;
- if (!(val= decimal_op_with_null_check(&decimal_value)))
- return 0; // null is set
- DBUG_ASSERT(!null_value);
- my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val);
- str->set_charset(collation.collation);
- my_decimal2string(E_DEC_FATAL_ERROR, val, 0, 0, 0, str);
- return str;
-}
-
-double Item_func_hybrid_field_type::val_real_from_decimal_op()
-{
- my_decimal decimal_value, *val;
- if (!(val= decimal_op_with_null_check(&decimal_value)))
- return 0.0; // null is set
- double result;
- my_decimal2double(E_DEC_FATAL_ERROR, val, &result);
- return result;
-}
-
-longlong Item_func_hybrid_field_type::val_int_from_decimal_op()
-{
- my_decimal decimal_value, *val;
- if (!(val= decimal_op_with_null_check(&decimal_value)))
- return 0; // null is set
- longlong result;
- my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result);
- return result;
-}
-
-bool Item_func_hybrid_field_type::get_date_from_decimal_op(MYSQL_TIME *ltime,
- ulonglong fuzzydate)
-{
- my_decimal value, *res;
- if (!(res= decimal_op_with_null_check(&value)) ||
- decimal_to_datetime_with_warn(res, ltime, fuzzydate,
- field_table_or_null(),
- field_name_or_null()))
- return make_zero_mysql_time(ltime, fuzzydate);
- return (null_value= 0);
-}
-
-
String *Item_func_hybrid_field_type::val_str_from_int_op(String *str)
{
longlong nr= int_op();
@@ -874,19 +829,6 @@ Item_func_hybrid_field_type::val_decimal_from_int_op(my_decimal *dec)
return dec;
}
-bool Item_func_hybrid_field_type::get_date_from_int_op(MYSQL_TIME *ltime,
- ulonglong fuzzydate)
-{
- longlong value= int_op();
- bool neg= !unsigned_flag && value < 0;
- if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value,
- ltime, fuzzydate,
- field_table_or_null(),
- field_name_or_null()))
- return make_zero_mysql_time(ltime, fuzzydate);
- return (null_value= 0);
-}
-
String *Item_func_hybrid_field_type::val_str_from_real_op(String *str)
{
@@ -912,22 +854,11 @@ Item_func_hybrid_field_type::val_decimal_from_real_op(my_decimal *dec)
return dec;
}
-bool Item_func_hybrid_field_type::get_date_from_real_op(MYSQL_TIME *ltime,
- ulonglong fuzzydate)
-{
- double value= real_op();
- if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate,
- field_table_or_null(),
- field_name_or_null()))
- return make_zero_mysql_time(ltime, fuzzydate);
- return (null_value= 0);
-}
-
String *Item_func_hybrid_field_type::val_str_from_date_op(String *str)
{
MYSQL_TIME ltime;
- if (date_op_with_null_check(&ltime) ||
+ if (date_op_with_null_check(current_thd, &ltime) ||
(null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH)))
return (String *) 0;
str->length(my_TIME_to_str(&ltime, const_cast<char*>(str->ptr()), decimals));
@@ -939,7 +870,7 @@ String *Item_func_hybrid_field_type::val_str_from_date_op(String *str)
double Item_func_hybrid_field_type::val_real_from_date_op()
{
MYSQL_TIME ltime;
- if (date_op_with_null_check(&ltime))
+ if (date_op_with_null_check(current_thd, &ltime))
return 0;
return TIME_to_double(&ltime);
}
@@ -947,7 +878,7 @@ double Item_func_hybrid_field_type::val_real_from_date_op()
longlong Item_func_hybrid_field_type::val_int_from_date_op()
{
MYSQL_TIME ltime;
- if (date_op_with_null_check(&ltime))
+ if (date_op_with_null_check(current_thd, &ltime))
return 0;
return TIME_to_ulonglong(&ltime);
}
@@ -956,7 +887,7 @@ my_decimal *
Item_func_hybrid_field_type::val_decimal_from_date_op(my_decimal *dec)
{
MYSQL_TIME ltime;
- if (date_op_with_null_check(&ltime))
+ if (date_op_with_null_check(current_thd, &ltime))
{
my_decimal_set_zero(dec);
return 0;
@@ -968,7 +899,7 @@ Item_func_hybrid_field_type::val_decimal_from_date_op(my_decimal *dec)
String *Item_func_hybrid_field_type::val_str_from_time_op(String *str)
{
MYSQL_TIME ltime;
- if (time_op_with_null_check(&ltime) ||
+ if (time_op_with_null_check(current_thd, &ltime) ||
(null_value= my_TIME_to_str(&ltime, str, decimals)))
return NULL;
return str;
@@ -977,20 +908,22 @@ String *Item_func_hybrid_field_type::val_str_from_time_op(String *str)
double Item_func_hybrid_field_type::val_real_from_time_op()
{
MYSQL_TIME ltime;
- return time_op_with_null_check(&ltime) ? 0 : TIME_to_double(&ltime);
+ return time_op_with_null_check(current_thd, &ltime) ? 0 :
+ TIME_to_double(&ltime);
}
longlong Item_func_hybrid_field_type::val_int_from_time_op()
{
MYSQL_TIME ltime;
- return time_op_with_null_check(&ltime) ? 0 : TIME_to_ulonglong(&ltime);
+ return time_op_with_null_check(current_thd, &ltime) ? 0 :
+ TIME_to_ulonglong(&ltime);
}
my_decimal *
Item_func_hybrid_field_type::val_decimal_from_time_op(my_decimal *dec)
{
MYSQL_TIME ltime;
- if (time_op_with_null_check(&ltime))
+ if (time_op_with_null_check(current_thd, &ltime))
{
my_decimal_set_zero(dec);
return 0;
@@ -1018,18 +951,6 @@ Item_func_hybrid_field_type::val_decimal_from_str_op(my_decimal *decimal_value)
return res ? decimal_from_string_with_check(decimal_value, res) : 0;
}
-bool Item_func_hybrid_field_type::get_date_from_str_op(MYSQL_TIME *ltime,
- ulonglong fuzzydate)
-{
- StringBuffer<40> tmp;
- String *res;
- if (!(res= str_op_with_null_check(&tmp)) ||
- str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
- ltime, fuzzydate))
- return make_zero_mysql_time(ltime, fuzzydate);
- return (null_value= 0);
-}
-
void Item_func_signed::print(String *str, enum_query_type query_type)
{
@@ -1049,47 +970,15 @@ void Item_func_unsigned::print(String *str, enum_query_type query_type)
}
-String *Item_decimal_typecast::val_str(String *str)
-{
- my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf);
- if (null_value)
- return NULL;
- my_decimal2string(E_DEC_FATAL_ERROR, tmp, 0, 0, 0, str);
- return str;
-}
-
-
-double Item_decimal_typecast::val_real()
-{
- my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf);
- double res;
- if (null_value)
- return 0.0;
- my_decimal2double(E_DEC_FATAL_ERROR, tmp, &res);
- return res;
-}
-
-
-longlong Item_decimal_typecast::val_int()
-{
- my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf);
- longlong res;
- if (null_value)
- return 0;
- my_decimal2int(E_DEC_FATAL_ERROR, tmp, unsigned_flag, &res);
- return res;
-}
-
-
my_decimal *Item_decimal_typecast::val_decimal(my_decimal *dec)
{
- my_decimal tmp_buf, *tmp= args[0]->val_decimal(&tmp_buf);
+ VDec tmp(args[0]);
bool sign;
uint precision;
- if ((null_value= args[0]->null_value))
+ if ((null_value= tmp.is_null()))
return NULL;
- my_decimal_round(E_DEC_FATAL_ERROR, tmp, decimals, FALSE, dec);
+ tmp.round_to(dec, decimals, HALF_UP);
sign= dec->sign();
if (unsigned_flag)
{
@@ -1206,17 +1095,20 @@ double Item_func_plus::real_op()
return check_float_overflow(value);
}
+#if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002
+#pragma GCC push_options
+#pragma GCC optimize ("no-expensive-optimizations")
+#endif
longlong Item_func_plus::int_op()
{
longlong val0= args[0]->val_int();
longlong val1= args[1]->val_int();
- longlong res= val0 + val1;
bool res_unsigned= FALSE;
+ longlong res;
if ((null_value= args[0]->null_value || args[1]->null_value))
return 0;
-
/*
First check whether the result can be represented as a
(bool unsigned_flag, longlong value) pair, then check if it is compatible
@@ -1257,16 +1149,29 @@ longlong Item_func_plus::int_op()
{
if (val0 >=0 && val1 >= 0)
res_unsigned= TRUE;
- else if (val0 < 0 && val1 < 0 && res >= 0)
+ else if (val0 < 0 && val1 < 0 && val0 < (LONGLONG_MIN - val1))
goto err;
}
}
+
+#ifndef WITH_UBSAN
+ res= val0 + val1;
+#else
+ if (res_unsigned)
+ res= (longlong) ((ulonglong) val0 + (ulonglong) val1);
+ else
+ res= val0+val1;
+#endif /* WITH_UBSAN */
+
return check_integer_overflow(res, res_unsigned);
err:
return raise_integer_overflow();
}
+#if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002
+#pragma GCC pop_options
+#endif
/**
Calculate plus of two decimals.
@@ -1281,17 +1186,13 @@ err:
my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value)
{
- my_decimal value1, *val1;
- my_decimal value2, *val2;
- val1= args[0]->val_decimal(&value1);
- if ((null_value= args[0]->null_value))
- return 0;
- val2= args[1]->val_decimal(&value2);
- if (!(null_value= (args[1]->null_value ||
+ VDec2_lazy val(args[0], args[1]);
+ if (!(null_value= (val.has_null() ||
check_decimal_overflow(my_decimal_add(E_DEC_FATAL_ERROR &
~E_DEC_OVERFLOW,
decimal_value,
- val1, val2)) > 3)))
+ val.m_a.ptr(),
+ val.m_b.ptr())) > 3)))
return decimal_value;
return 0;
}
@@ -1363,12 +1264,17 @@ double Item_func_minus::real_op()
}
+#if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002
+#pragma GCC push_options
+#pragma GCC optimize ("no-expensive-optimizations")
+#endif
+
longlong Item_func_minus::int_op()
{
longlong val0= args[0]->val_int();
longlong val1= args[1]->val_int();
- longlong res= val0 - val1;
bool res_unsigned= FALSE;
+ longlong res;
if ((null_value= args[0]->null_value || args[1]->null_value))
return 0;
@@ -1383,12 +1289,8 @@ longlong Item_func_minus::int_op()
if (args[1]->unsigned_flag)
{
if ((ulonglong) val0 < (ulonglong) val1)
- {
- if (res >= 0)
- goto err;
- }
- else
- res_unsigned= TRUE;
+ goto err;
+ res_unsigned= TRUE;
}
else
{
@@ -1409,23 +1311,35 @@ longlong Item_func_minus::int_op()
{
if (args[1]->unsigned_flag)
{
- if ((ulonglong) (val0 - LONGLONG_MIN) < (ulonglong) val1)
+ if (((ulonglong) val0 - (ulonglong) LONGLONG_MIN) < (ulonglong) val1)
goto err;
}
else
{
if (val0 > 0 && val1 < 0)
res_unsigned= TRUE;
- else if (val0 < 0 && val1 > 0 && res >= 0)
+ else if (val0 < 0 && val1 > 0 && val0 < (LONGLONG_MIN + val1))
goto err;
}
}
+#ifndef WITH_UBSAN
+ res= val0 - val1;
+#else
+ if (res_unsigned)
+ res= (longlong) ((ulonglong) val0 - (ulonglong) val1);
+ else
+ res= val0 - val1;
+#endif /* WITH_UBSAN */
+
return check_integer_overflow(res, res_unsigned);
err:
return raise_integer_overflow();
}
+#if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002
+#pragma GCC pop_options
+#endif
/**
See Item_func_plus::decimal_op for comments.
@@ -1433,18 +1347,13 @@ err:
my_decimal *Item_func_minus::decimal_op(my_decimal *decimal_value)
{
- my_decimal value1, *val1;
- my_decimal value2, *val2=
-
- val1= args[0]->val_decimal(&value1);
- if ((null_value= args[0]->null_value))
- return 0;
- val2= args[1]->val_decimal(&value2);
- if (!(null_value= (args[1]->null_value ||
- (check_decimal_overflow(my_decimal_sub(E_DEC_FATAL_ERROR &
- ~E_DEC_OVERFLOW,
- decimal_value, val1,
- val2)) > 3))))
+ VDec2_lazy val(args[0], args[1]);
+ if (!(null_value= (val.has_null() ||
+ check_decimal_overflow(my_decimal_sub(E_DEC_FATAL_ERROR &
+ ~E_DEC_OVERFLOW,
+ decimal_value,
+ val.m_a.ptr(),
+ val.m_b.ptr())) > 3)))
return decimal_value;
return 0;
}
@@ -1543,17 +1452,13 @@ err:
my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value)
{
- my_decimal value1, *val1;
- my_decimal value2, *val2;
- val1= args[0]->val_decimal(&value1);
- if ((null_value= args[0]->null_value))
- return 0;
- val2= args[1]->val_decimal(&value2);
- if (!(null_value= (args[1]->null_value ||
- (check_decimal_overflow(my_decimal_mul(E_DEC_FATAL_ERROR &
- ~E_DEC_OVERFLOW,
- decimal_value, val1,
- val2)) > 3))))
+ VDec2_lazy val(args[0], args[1]);
+ if (!(null_value= (val.has_null() ||
+ check_decimal_overflow(my_decimal_mul(E_DEC_FATAL_ERROR &
+ ~E_DEC_OVERFLOW,
+ decimal_value,
+ val.m_a.ptr(),
+ val.m_b.ptr())) > 3)))
return decimal_value;
return 0;
}
@@ -1604,21 +1509,15 @@ double Item_func_div::real_op()
my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value)
{
- my_decimal value1, *val1;
- my_decimal value2, *val2;
int err;
-
- val1= args[0]->val_decimal(&value1);
- if ((null_value= args[0]->null_value))
- return 0;
- val2= args[1]->val_decimal(&value2);
- if ((null_value= args[1]->null_value))
+ VDec2_lazy val(args[0], args[1]);
+ if ((null_value= val.has_null()))
return 0;
if ((err= check_decimal_overflow(my_decimal_div(E_DEC_FATAL_ERROR &
~E_DEC_OVERFLOW &
~E_DEC_DIV_ZERO,
decimal_value,
- val1, val2,
+ val.m_a.ptr(), val.m_b.ptr(),
prec_increment))) > 3)
{
if (err == E_DEC_DIV_ZERO)
@@ -1708,20 +1607,14 @@ longlong Item_func_int_div::val_int()
if (args[0]->result_type() != INT_RESULT ||
args[1]->result_type() != INT_RESULT)
{
- my_decimal tmp;
- my_decimal *val0p= args[0]->val_decimal(&tmp);
- if ((null_value= args[0]->null_value))
+ VDec2_lazy val(args[0], args[1]);
+ if ((null_value= val.has_null()))
return 0;
- my_decimal val0= *val0p;
-
- my_decimal *val1p= args[1]->val_decimal(&tmp);
- if ((null_value= args[1]->null_value))
- return 0;
- my_decimal val1= *val1p;
int err;
+ my_decimal tmp;
if ((err= my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, &tmp,
- &val0, &val1, 0)) > 3)
+ val.m_a.ptr(), val.m_b.ptr(), 0)) > 3)
{
if (err == E_DEC_DIV_ZERO)
signal_divide_by_null();
@@ -1729,8 +1622,7 @@ longlong Item_func_int_div::val_int()
}
my_decimal truncated;
- const bool do_truncate= true;
- if (my_decimal_round(E_DEC_FATAL_ERROR, &tmp, 0, do_truncate, &truncated))
+ if (tmp.round_to(&truncated, 0, TRUNCATE))
DBUG_ASSERT(false);
longlong res;
@@ -1815,17 +1707,11 @@ double Item_func_mod::real_op()
my_decimal *Item_func_mod::decimal_op(my_decimal *decimal_value)
{
- my_decimal value1, *val1;
- my_decimal value2, *val2;
-
- val1= args[0]->val_decimal(&value1);
- if ((null_value= args[0]->null_value))
- return 0;
- val2= args[1]->val_decimal(&value2);
- if ((null_value= args[1]->null_value))
+ VDec2_lazy val(args[0], args[1]);
+ if ((null_value= val.has_null()))
return 0;
switch (my_decimal_mod(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, decimal_value,
- val1, val2)) {
+ val.m_a.ptr(), val.m_b.ptr())) {
case E_DEC_TRUNCATED:
case E_DEC_OK:
return decimal_value;
@@ -1865,6 +1751,46 @@ bool Item_func_mod::fix_length_and_dec()
DBUG_RETURN(FALSE);
}
+static void calc_hash_for_unique(ulong &nr1, ulong &nr2, String *str)
+{
+ CHARSET_INFO *cs;
+ uchar l[4];
+ int4store(l, str->length());
+ cs= str->charset();
+ cs->coll->hash_sort(cs, l, sizeof(l), &nr1, &nr2);
+ cs= str->charset();
+ cs->coll->hash_sort(cs, (uchar *)str->ptr(), str->length(), &nr1, &nr2);
+}
+
+longlong Item_func_hash::val_int()
+{
+ DBUG_EXECUTE_IF("same_long_unique_hash", return 9;);
+ unsigned_flag= true;
+ ulong nr1= 1,nr2= 4;
+ String * str;
+ for(uint i= 0;i<arg_count;i++)
+ {
+ str = args[i]->val_str();
+ if(args[i]->null_value)
+ {
+ null_value= 1;
+ return 0;
+ }
+ calc_hash_for_unique(nr1, nr2, str);
+ }
+ null_value= 0;
+ return (longlong)nr1;
+}
+
+
+bool Item_func_hash::fix_length_and_dec()
+{
+ decimals= 0;
+ max_length= 8;
+ return false;
+}
+
+
double Item_func_neg::real_op()
{
@@ -1898,10 +1824,10 @@ longlong Item_func_neg::int_op()
my_decimal *Item_func_neg::decimal_op(my_decimal *decimal_value)
{
- my_decimal val, *value= args[0]->val_decimal(&val);
- if (!(null_value= args[0]->null_value))
+ VDec value(args[0]);
+ if (!(null_value= value.is_null()))
{
- my_decimal2decimal(value, decimal_value);
+ my_decimal2decimal(value.ptr(), decimal_value);
my_decimal_neg(decimal_value);
return decimal_value;
}
@@ -1925,7 +1851,7 @@ void Item_func_neg::fix_length_and_dec_int()
longlong val= args[0]->val_int();
if ((ulonglong) val >= (ulonglong) LONGLONG_MIN &&
((ulonglong) val != (ulonglong) LONGLONG_MIN ||
- args[0]->type() != INT_ITEM))
+ !args[0]->is_of_type(CONST_ITEM, INT_RESULT)))
{
/*
Ensure that result is converted to DECIMAL, as longlong can't hold
@@ -1996,10 +1922,10 @@ longlong Item_func_abs::int_op()
my_decimal *Item_func_abs::decimal_op(my_decimal *decimal_value)
{
- my_decimal val, *value= args[0]->val_decimal(&val);
- if (!(null_value= args[0]->null_value))
+ VDec value(args[0]);
+ if (!(null_value= value.is_null()))
{
- my_decimal2decimal(value, decimal_value);
+ my_decimal2decimal(value.ptr(), decimal_value);
if (decimal_value->sign())
my_decimal_neg(decimal_value);
return decimal_value;
@@ -2233,31 +2159,29 @@ double Item_func_cot::val_real()
longlong Item_func_shift_left::val_int()
{
DBUG_ASSERT(fixed == 1);
- uint shift;
- ulonglong res= ((ulonglong) args[0]->val_int() <<
- (shift=(uint) args[1]->val_int()));
+ uint shift= (uint) args[1]->val_int();
+ ulonglong value= args[0]->val_int();
if (args[0]->null_value || args[1]->null_value)
{
null_value=1;
return 0;
}
null_value=0;
- return (shift < sizeof(longlong)*8 ? (longlong) res : 0);
+ return (shift < sizeof(longlong)*8 ? (value << shift) : 0);
}
longlong Item_func_shift_right::val_int()
{
DBUG_ASSERT(fixed == 1);
- uint shift;
- ulonglong res= (ulonglong) args[0]->val_int() >>
- (shift=(uint) args[1]->val_int());
+ uint shift= (uint) args[1]->val_int();
+ ulonglong value= args[0]->val_int();
if (args[0]->null_value || args[1]->null_value)
{
null_value=1;
return 0;
}
null_value=0;
- return (shift < sizeof(longlong)*8 ? (longlong) res : 0);
+ return (shift < sizeof(longlong)*8 ? (value >> shift) : 0);
}
@@ -2275,35 +2199,64 @@ longlong Item_func_bit_neg::val_int()
void Item_func_int_val::fix_length_and_dec_int_or_decimal()
{
+ DBUG_ASSERT(args[0]->cmp_type() == DECIMAL_RESULT);
+ DBUG_ASSERT(args[0]->max_length <= DECIMAL_MAX_STR_LENGTH);
/*
- The INT branch of this code should be revised.
- It creates too large data types, e.g.
- CREATE OR REPLACE TABLE t2 AS SELECT FLOOR(9999999.999) AS fa;
- results in a BININT(10) column, while INT(7) should probably be enough.
+ FLOOR() for negative numbers can increase length: floor(-9.9) -> -10
+ CEILING() for positive numbers can increase length: ceil(9.9) -> 10
*/
- ulonglong tmp_max_length= (ulonglong ) args[0]->max_length -
- (args[0]->decimals ? args[0]->decimals + 1 : 0) + 2;
- max_length= tmp_max_length > (ulonglong) UINT_MAX32 ?
- (uint32) UINT_MAX32 : (uint32) tmp_max_length;
- uint tmp= float_length(decimals);
- set_if_smaller(max_length,tmp);
- decimals= 0;
+ decimal_round_mode mode= round_mode();
+ uint length_increase= args[0]->decimals > 0 &&
+ (mode == CEILING ||
+ (mode == FLOOR && !args[0]->unsigned_flag)) ? 1 : 0;
+ uint precision= args[0]->decimal_int_part() + length_increase;
+ set_if_bigger(precision, 1);
/*
- -2 because in most high position can't be used any digit for longlong
- and one position for increasing value during operation
+ The BIGINT data type can store:
+ UNSIGNED BIGINT: 0..18446744073709551615 - up to 19 digits
+ SIGNED BIGINT: -9223372036854775808..9223372036854775807 - up to 18 digits
+
+ The INT data type can store:
+ UNSIGNED INT: 0..4294967295 - up to 9 digits
+ SIGNED INT: -2147483648..2147483647 - up to 9 digits
*/
- if (args[0]->max_length - args[0]->decimals >= DECIMAL_LONGLONG_DIGITS - 2)
+ if (precision > 18)
{
+ unsigned_flag= args[0]->unsigned_flag;
fix_char_length(
- my_decimal_precision_to_length_no_truncation(
- args[0]->decimal_int_part(), 0, false));
+ my_decimal_precision_to_length_no_truncation(precision, 0,
+ unsigned_flag));
set_handler(&type_handler_newdecimal);
}
else
{
- unsigned_flag= args[0]->unsigned_flag;
- set_handler(type_handler_long_or_longlong());
+ uint sign_length= (unsigned_flag= args[0]->unsigned_flag) ? 0 : 1;
+ fix_char_length(precision + sign_length);
+ if (precision > 9)
+ {
+#if MYSQL_VERSION_ID > 100500
+#error Remove the '#else' branch and the conditional compilation
+ if (unsigned_flag)
+ set_handler(&type_handler_ulonglong);
+ else
+ set_handler(&type_handler_slonglong);
+#else
+ set_handler(&type_handler_longlong);
+#endif
+ }
+ else
+ {
+#if MYSQL_VERSION_ID > 100500
+#error Remove the '#else' branch and the conditional compilation
+ if (unsigned_flag)
+ set_handler(&type_handler_ulong);
+ else
+ set_handler(&type_handler_slong);
+#else
+ set_handler(&type_handler_long);
+#endif
+ }
}
}
@@ -2320,35 +2273,29 @@ bool Item_func_int_val::fix_length_and_dec()
{
DBUG_ENTER("Item_func_int_val::fix_length_and_dec");
DBUG_PRINT("info", ("name %s", func_name()));
- if (args[0]->cast_to_int_type_handler()->
- Item_func_int_val_fix_length_and_dec(this))
+ /*
+ We don't want to translate ENUM/SET to CHAR here.
+ So let's call real_type_handler(), not type_handler().
+ */
+ if (args[0]->real_type_handler()->Item_func_int_val_fix_length_and_dec(this))
DBUG_RETURN(TRUE);
- DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
+ DBUG_PRINT("info", ("Type: %s", real_type_handler()->name().ptr()));
DBUG_RETURN(FALSE);
}
longlong Item_func_ceiling::int_op()
{
- longlong result;
switch (args[0]->result_type()) {
+ case STRING_RESULT: // hex hybrid
case INT_RESULT:
- result= args[0]->val_int();
- null_value= args[0]->null_value;
- break;
+ return val_int_from_item(args[0]);
case DECIMAL_RESULT:
- {
- my_decimal dec_buf, *dec;
- if ((dec= Item_func_ceiling::decimal_op(&dec_buf)))
- my_decimal2int(E_DEC_FATAL_ERROR, dec, unsigned_flag, &result);
- else
- result= 0;
+ return VDec_op(this).to_longlong(unsigned_flag);
+ default:
break;
}
- default:
- result= (longlong)Item_func_ceiling::real_op();
- };
- return result;
+ return (longlong) Item_func_ceiling::real_op();
}
@@ -2366,36 +2313,52 @@ double Item_func_ceiling::real_op()
my_decimal *Item_func_ceiling::decimal_op(my_decimal *decimal_value)
{
- my_decimal val, *value= args[0]->val_decimal(&val);
- if (!(null_value= (args[0]->null_value ||
- my_decimal_ceiling(E_DEC_FATAL_ERROR, value,
- decimal_value) > 1)))
+ VDec value(args[0]);
+ if (!(null_value= (value.is_null() ||
+ value.round_to(decimal_value, 0, CEILING) > 1)))
return decimal_value;
return 0;
}
+bool Item_func_ceiling::date_op(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate)
+{
+ Datetime::Options opt(thd, TIME_FRAC_TRUNCATE);
+ Datetime *tm= new (to) Datetime(thd, args[0], opt);
+ tm->ceiling(thd);
+ null_value= !tm->is_valid_datetime();
+ DBUG_ASSERT(maybe_null || !null_value);
+ return null_value;
+}
+
+
+bool Item_func_ceiling::time_op(THD *thd, MYSQL_TIME *to)
+{
+ static const Time::Options_for_round opt;
+ Time *tm= new (to) Time(thd, args[0], opt);
+ tm->ceiling();
+ null_value= !tm->is_valid_time();
+ DBUG_ASSERT(maybe_null || !null_value);
+ return null_value;
+}
+
+
longlong Item_func_floor::int_op()
{
- longlong result;
switch (args[0]->result_type()) {
+ case STRING_RESULT: // hex hybrid
case INT_RESULT:
- result= args[0]->val_int();
- null_value= args[0]->null_value;
- break;
+ return val_int_from_item(args[0]);
case DECIMAL_RESULT:
{
my_decimal dec_buf, *dec;
- if ((dec= Item_func_floor::decimal_op(&dec_buf)))
- my_decimal2int(E_DEC_FATAL_ERROR, dec, unsigned_flag, &result);
- else
- result= 0;
- break;
+ return (!(dec= Item_func_floor::decimal_op(&dec_buf))) ? 0 :
+ dec->to_longlong(unsigned_flag);
}
default:
- result= (longlong)Item_func_floor::real_op();
- };
- return result;
+ break;
+ }
+ return (longlong) Item_func_floor::real_op();
}
@@ -2413,15 +2376,36 @@ double Item_func_floor::real_op()
my_decimal *Item_func_floor::decimal_op(my_decimal *decimal_value)
{
- my_decimal val, *value= args[0]->val_decimal(&val);
- if (!(null_value= (args[0]->null_value ||
- my_decimal_floor(E_DEC_FATAL_ERROR, value,
- decimal_value) > 1)))
+ VDec value(args[0]);
+ if (!(null_value= (value.is_null() ||
+ value.round_to(decimal_value, 0, FLOOR) > 1)))
return decimal_value;
return 0;
}
+bool Item_func_floor::date_op(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate)
+{
+ // DATETIME is not negative, so FLOOR means just truncation
+ Datetime::Options opt(thd, TIME_FRAC_TRUNCATE);
+ Datetime *tm= new (to) Datetime(thd, args[0], opt, 0);
+ null_value= !tm->is_valid_datetime();
+ DBUG_ASSERT(maybe_null || !null_value);
+ return null_value;
+}
+
+
+bool Item_func_floor::time_op(THD *thd, MYSQL_TIME *to)
+{
+ static const Time::Options_for_round opt;
+ Time *tm= new (to) Time(thd, args[0], opt);
+ tm->floor();
+ null_value= !tm->is_valid_time();
+ DBUG_ASSERT(maybe_null || !null_value);
+ return null_value;
+}
+
+
void Item_func_round::fix_length_and_dec_decimal(uint decimals_to_set)
{
int decimals_delta= args[0]->decimals - decimals_to_set;
@@ -2481,29 +2465,109 @@ void Item_func_round::fix_arg_double()
}
-void Item_func_round::fix_arg_int()
+void Item_func_round::fix_arg_temporal(const Type_handler *h,
+ uint int_part_length)
{
- if (args[1]->const_item())
+ set_handler(h);
+ if (args[1]->const_item() && !args[1]->is_expensive())
{
+ Longlong_hybrid_null dec= args[1]->to_longlong_hybrid_null();
+ fix_attributes_temporal(int_part_length,
+ dec.is_null() ? args[0]->decimals :
+ dec.to_uint(TIME_SECOND_PART_DIGITS));
+ }
+ else
+ fix_attributes_temporal(int_part_length, args[0]->decimals);
+}
+
+
+void Item_func_round::fix_arg_time()
+{
+ fix_arg_temporal(&type_handler_time2, MIN_TIME_WIDTH);
+}
+
+
+void Item_func_round::fix_arg_datetime()
+{
+ /*
+ Day increment operations are not supported for '0000-00-00',
+ see get_date_from_daynr() for details. Therefore, expressions like
+ ROUND('0000-00-00 23:59:59.999999')
+ return NULL.
+ */
+ if (!truncate)
+ maybe_null= true;
+ fix_arg_temporal(&type_handler_datetime2, MAX_DATETIME_WIDTH);
+}
+
+
+bool Item_func_round::test_if_length_can_increase()
+{
+ if (truncate)
+ return false;
+ if (args[1]->const_item() && !args[1]->is_expensive())
+ {
+ // Length can increase in some cases: e.g. ROUND(9,-1) -> 10.
Longlong_hybrid val1= args[1]->to_longlong_hybrid();
- if (args[1]->null_value)
- fix_length_and_dec_double(NOT_FIXED_DEC);
- else if ((!val1.to_uint(DECIMAL_MAX_SCALE) && truncate) ||
- args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS)
- {
- // Length can increase in some cases: ROUND(9,-1) -> 10
- int length_can_increase= MY_TEST(!truncate && val1.neg());
- max_length= args[0]->max_length + length_can_increase;
- // Here we can keep INT_RESULT
- unsigned_flag= args[0]->unsigned_flag;
- decimals= 0;
- set_handler(type_handler_long_or_longlong());
- }
+ return !args[1]->null_value && val1.neg();
+ }
+ return true; // ROUND(x,n), where n is not a constant.
+}
+
+
+/**
+ Calculate data type and attributes for INT-alike input.
+
+ @param [IN] preferred - The preferred data type handler for simple cases
+ such as ROUND(x) and TRUNCATE(x,0), when the input
+ is short enough to fit into an integer type
+ (without extending to DECIMAL).
+ - If `preferred` is not NULL, then the code tries
+ to preserve the given data type handler and
+ the data type attributes `preferred_attrs`.
+ - If `preferred` is NULL, then the code fully
+ calculates attributes using
+ args[0]->decimal_precision() and chooses between
+ INT and BIGINT, depending on attributes.
+ @param [IN] preferred_attrs - The preferred data type attributes for
+ simple cases.
+*/
+void Item_func_round::fix_arg_int(const Type_handler *preferred,
+ const Type_std_attributes *preferred_attrs,
+ bool use_decimal_on_length_increase)
+{
+ DBUG_ASSERT(args[0]->decimals == 0);
+
+ Type_std_attributes::set(preferred_attrs);
+ if (!test_if_length_can_increase())
+ {
+ // Preserve the exact data type and attributes
+ set_handler(preferred);
+ }
+ else
+ {
+ max_length++;
+ if (use_decimal_on_length_increase)
+ set_handler(&type_handler_newdecimal);
else
- fix_length_and_dec_decimal(val1.to_uint(DECIMAL_MAX_SCALE));
+ set_handler(type_handler_long_or_longlong());
}
+}
+
+
+void Item_func_round::fix_arg_hex_hybrid()
+{
+ DBUG_ASSERT(args[0]->decimals == 0);
+ DBUG_ASSERT(args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS);
+ DBUG_ASSERT(args[0]->unsigned_flag); // no needs to add sign length
+ bool length_can_increase= test_if_length_can_increase();
+ max_length= args[0]->decimal_precision() + MY_TEST(length_can_increase);
+ unsigned_flag= true;
+ decimals= 0;
+ if (length_can_increase && args[0]->max_length >= 8)
+ set_handler(&type_handler_newdecimal);
else
- fix_length_and_dec_double(args[0]->decimals);
+ set_handler(type_handler_long_or_longlong());
}
@@ -2605,21 +2669,49 @@ longlong Item_func_round::int_op()
my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value)
{
- my_decimal val, *value= args[0]->val_decimal(&val);
+ VDec value(args[0]);
longlong dec= args[1]->val_int();
if (dec >= 0 || args[1]->unsigned_flag)
dec= MY_MIN((ulonglong) dec, decimals);
else if (dec < INT_MIN)
dec= INT_MIN;
- if (!(null_value= (args[0]->null_value || args[1]->null_value ||
- my_decimal_round(E_DEC_FATAL_ERROR, value, (int) dec,
- truncate, decimal_value) > 1)))
+ if (!(null_value= (value.is_null() || args[1]->null_value ||
+ value.round_to(decimal_value, (uint) dec,
+ truncate ? TRUNCATE : HALF_UP) > 1)))
return decimal_value;
return 0;
}
+bool Item_func_round::time_op(THD *thd, MYSQL_TIME *to)
+{
+ DBUG_ASSERT(args[0]->type_handler()->mysql_timestamp_type() ==
+ MYSQL_TIMESTAMP_TIME);
+ Time::Options_for_round opt(truncate ? TIME_FRAC_TRUNCATE : TIME_FRAC_ROUND);
+ Longlong_hybrid_null dec= args[1]->to_longlong_hybrid_null();
+ Time *tm= new (to) Time(thd, args[0], opt,
+ dec.to_uint(TIME_SECOND_PART_DIGITS));
+ null_value= !tm->is_valid_time() || dec.is_null();
+ DBUG_ASSERT(maybe_null || !null_value);
+ return null_value;
+}
+
+
+bool Item_func_round::date_op(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate)
+{
+ DBUG_ASSERT(args[0]->type_handler()->mysql_timestamp_type() ==
+ MYSQL_TIMESTAMP_DATETIME);
+ Datetime::Options opt(thd, truncate ? TIME_FRAC_TRUNCATE : TIME_FRAC_ROUND);
+ Longlong_hybrid_null dec= args[1]->to_longlong_hybrid_null();
+ Datetime *tm= new (to) Datetime(thd, args[0], opt,
+ dec.to_uint(TIME_SECOND_PART_DIGITS));
+ null_value= !tm->is_valid_datetime() || dec.is_null();
+ DBUG_ASSERT(maybe_null || !null_value);
+ return null_value;
+}
+
+
void Item_func_rand::seed_random(Item *arg)
{
/*
@@ -2633,7 +2725,7 @@ void Item_func_rand::seed_random(Item *arg)
THD *thd= current_thd;
if (WSREP(thd))
{
- if (thd->wsrep_exec_mode==REPL_RECV)
+ if (wsrep_thd_is_applying(thd))
tmp= thd->wsrep_rand;
else
thd->wsrep_rand= tmp;
@@ -2753,14 +2845,15 @@ bool Item_func_min_max::fix_attributes(Item **items, uint nitems)
0 Otherwise
*/
-bool Item_func_min_max::get_date_native(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_func_min_max::get_date_native(THD *thd, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate)
{
longlong UNINIT_VAR(min_max);
DBUG_ASSERT(fixed == 1);
for (uint i=0; i < arg_count ; i++)
{
- longlong res= args[i]->val_datetime_packed();
+ longlong res= args[i]->val_datetime_packed(thd);
/* Check if we need to stop (because of error or KILL) and stop the loop */
if (unlikely(args[i]->null_value))
@@ -2771,8 +2864,8 @@ bool Item_func_min_max::get_date_native(MYSQL_TIME *ltime, ulonglong fuzzy_date)
}
unpack_time(min_max, ltime, mysql_timestamp_type());
- if (!(fuzzy_date & TIME_TIME_ONLY) &&
- unlikely((null_value= check_date_with_warn(ltime, fuzzy_date,
+ if (!(fuzzydate & TIME_TIME_ONLY) &&
+ unlikely((null_value= check_date_with_warn(thd, ltime, fuzzydate,
MYSQL_TIMESTAMP_ERROR))))
return true;
@@ -2780,17 +2873,17 @@ bool Item_func_min_max::get_date_native(MYSQL_TIME *ltime, ulonglong fuzzy_date)
}
-bool Item_func_min_max::get_time_native(MYSQL_TIME *ltime)
+bool Item_func_min_max::get_time_native(THD *thd, MYSQL_TIME *ltime)
{
DBUG_ASSERT(fixed == 1);
- Time value(args[0]);
+ Time value(thd, args[0], Time::Options(thd), decimals);
if (!value.is_valid_time())
return (null_value= true);
for (uint i= 1; i < arg_count ; i++)
{
- Time tmp(args[i]);
+ Time tmp(thd, args[i], Time::Options(thd), decimals);
if (!tmp.is_valid_time())
return (null_value= true);
@@ -2904,6 +2997,28 @@ my_decimal *Item_func_min_max::val_decimal_native(my_decimal *dec)
}
+bool Item_func_min_max::val_native(THD *thd, Native *native)
+{
+ DBUG_ASSERT(fixed == 1);
+ const Type_handler *handler= Item_hybrid_func::type_handler();
+ NativeBuffer<STRING_BUFFER_USUAL_SIZE> cur;
+ for (uint i= 0; i < arg_count; i++)
+ {
+ if (val_native_with_conversion_from_item(thd, args[i],
+ i == 0 ? native : &cur,
+ handler))
+ return true;
+ if (i > 0)
+ {
+ int cmp= handler->cmp_native(*native, cur);
+ if ((cmp_sign < 0 ? cmp : -cmp) < 0 && native->copy(cur))
+ return null_value= true;
+ }
+ }
+ return null_value= false;
+}
+
+
longlong Item_func_bit_length::val_int()
{
DBUG_ASSERT(fixed == 1);
@@ -2966,10 +3081,11 @@ longlong Item_func_locate::val_int()
if (arg_count == 3)
{
- start0= start= args[2]->val_int() - 1;
+ start0= start= args[2]->val_int();
- if ((start < 0) || (start > a->length()))
+ if ((start <= 0) || (start > a->length()))
return 0;
+ start0--; start--;
/* start is now sufficiently valid to pass to charpos function */
start= a->charpos((int) start);
@@ -3035,14 +3151,14 @@ longlong Item_func_field::val_int()
}
else if (cmp_type == DECIMAL_RESULT)
{
- my_decimal dec_arg_buf, *dec_arg,
- dec_buf, *dec= args[0]->val_decimal(&dec_buf);
- if (args[0]->null_value)
+ VDec dec(args[0]);
+ if (dec.is_null())
return 0;
+ my_decimal dec_arg_buf;
for (uint i=1; i < arg_count; i++)
{
- dec_arg= args[i]->val_decimal(&dec_arg_buf);
- if (!args[i]->null_value && !my_decimal_cmp(dec_arg, dec))
+ my_decimal *dec_arg= args[i]->val_decimal(&dec_arg_buf);
+ if (!args[i]->null_value && !dec.cmp(dec_arg))
return (longlong) (i);
}
}
@@ -3134,7 +3250,7 @@ bool Item_func_find_in_set::fix_length_and_dec()
find->length(), 0);
enum_bit=0;
if (enum_value)
- enum_bit=1LL << (enum_value-1);
+ enum_bit= 1ULL << (enum_value-1);
}
}
}
@@ -3295,6 +3411,7 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
}
uint i;
Item **arg,**arg_end;
+ With_sum_func_cache *with_sum_func_cache= func->get_with_sum_func_cache();
for (i=0, arg=arguments, arg_end=arguments+arg_count;
arg != arg_end ;
arg++,i++)
@@ -3318,7 +3435,8 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
func->collation.set(&my_charset_bin);
if (item->maybe_null)
func->maybe_null=1;
- func->with_sum_func= func->with_sum_func || item->with_sum_func;
+ if (with_sum_func_cache)
+ with_sum_func_cache->join_with_sum_func(item);
func->with_window_func= func->with_window_func ||
item->with_window_func;
func->with_field= func->with_field || item->with_field;
@@ -3624,32 +3742,6 @@ String *Item_func_udf_int::val_str(String *str)
}
-longlong Item_func_udf_decimal::val_int()
-{
- 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);
- return result;
-}
-
-
-double Item_func_udf_decimal::val_real()
-{
- 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);
- return result;
-}
-
-
my_decimal *Item_func_udf_decimal::val_decimal(my_decimal *dec_buf)
{
my_decimal *res;
@@ -3665,21 +3757,6 @@ my_decimal *Item_func_udf_decimal::val_decimal(my_decimal *dec_buf)
}
-String *Item_func_udf_decimal::val_str(String *str)
-{
- 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)
- str->length(DECIMAL_MAX_STR_LENGTH);
- my_decimal_round(E_DEC_FATAL_ERROR, dec, decimals, FALSE, &dec_buf);
- my_decimal2string(E_DEC_FATAL_ERROR, &dec_buf, 0, 0, '0', str);
- return str;
-}
-
-
/* Default max_length is max argument length */
bool Item_func_udf_str::fix_length_and_dec()
@@ -3746,7 +3823,7 @@ longlong Item_master_pos_wait::val_int()
connection_name.length= con->length();
if (check_master_connection_name(&connection_name))
{
- my_error(ER_WRONG_ARGUMENTS, MYF(ME_JUST_WARNING),
+ my_error(ER_WRONG_ARGUMENTS, MYF(ME_WARNING),
"MASTER_CONNECTION_NAME");
goto err;
}
@@ -4459,7 +4536,7 @@ user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name,
if (!my_hash_inited(hash))
return 0;
if (!(entry = (user_var_entry*) my_malloc(size,
- MYF(MY_WME | ME_FATALERROR |
+ MYF(MY_WME | ME_FATAL |
MY_THREAD_SPECIFIC))))
return 0;
entry->name.str=(char*) entry+ ALIGN_SIZE(sizeof(user_var_entry))+
@@ -4714,7 +4791,7 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length,
entry->value=0;
entry->value= (char*) my_realloc(entry->value, length,
MYF(MY_ALLOW_ZERO_PTR | MY_WME |
- ME_FATALERROR |
+ ME_FATAL |
MY_THREAD_SPECIFIC));
if (!entry->value)
return 1;
@@ -4780,11 +4857,7 @@ double user_var_entry::val_real(bool *null_value)
case INT_RESULT:
return (double) *(longlong*) value;
case DECIMAL_RESULT:
- {
- double result;
- my_decimal2double(E_DEC_FATAL_ERROR, (my_decimal *)value, &result);
- return result;
- }
+ return ((my_decimal *)value)->to_double();
case STRING_RESULT:
return my_atof(value); // This is null terminated
case ROW_RESULT:
@@ -4809,11 +4882,7 @@ longlong user_var_entry::val_int(bool *null_value) const
case INT_RESULT:
return *(longlong*) value;
case DECIMAL_RESULT:
- {
- longlong result;
- my_decimal2int(E_DEC_FATAL_ERROR, (my_decimal *)value, 0, &result);
- return result;
- }
+ return ((my_decimal *)value)->to_longlong(false);
case STRING_RESULT:
{
int error;
@@ -5550,10 +5619,9 @@ bool Item_func_get_user_var::set_value(THD *thd,
bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref)
{
- DBUG_ASSERT(fixed == 0);
+ DBUG_ASSERT(!is_fixed());
DBUG_ASSERT(thd->lex->exchange);
- if (Item::fix_fields(thd, ref) ||
- !(entry= get_variable(&thd->user_vars, &org_name, 1)))
+ if (!(entry= get_variable(&thd->user_vars, &org_name, 1)))
return TRUE;
entry->type= STRING_RESULT;
/*
@@ -5611,7 +5679,8 @@ my_decimal* Item_user_var_as_out_param::val_decimal(my_decimal *decimal_buffer)
}
-bool Item_user_var_as_out_param::get_date(MYSQL_TIME *ltime, ulonglong fuzzy)
+bool Item_user_var_as_out_param::get_date(THD *thd, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate)
{
DBUG_ASSERT(0);
return true;
@@ -5650,7 +5719,7 @@ void Item_func_get_system_var::update_null_value()
THD *thd= current_thd;
int save_no_errors= thd->no_errors;
thd->no_errors= TRUE;
- Item::update_null_value();
+ type_handler()->Item_update_null_value(this);
thd->no_errors= save_no_errors;
}
@@ -6490,7 +6559,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
(thd->lex->sql_command == SQLCOM_CREATE_VIEW))
{
Security_context *save_security_ctx= thd->security_ctx;
- if (context->security_ctx)
+ if (context && context->security_ctx)
thd->security_ctx= context->security_ctx;
/*
@@ -6505,7 +6574,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
if (res)
{
- context->process_error(thd);
+ process_error(thd);
DBUG_RETURN(res);
}
}
@@ -6522,7 +6591,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
if (!(m_sp= sp))
{
my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr());
- context->process_error(thd);
+ process_error(thd);
DBUG_RETURN(TRUE);
}
@@ -6678,6 +6747,14 @@ String *Item_func_last_value::val_str(String *str)
return tmp;
}
+
+bool Item_func_last_value::val_native(THD *thd, Native *to)
+{
+ evaluate_sideeffects();
+ return val_native_from_item(thd, last_value, to);
+}
+
+
longlong Item_func_last_value::val_int()
{
longlong tmp;
@@ -6706,10 +6783,10 @@ my_decimal *Item_func_last_value::val_decimal(my_decimal *decimal_value)
}
-bool Item_func_last_value::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_func_last_value::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
evaluate_sideeffects();
- bool tmp= last_value->get_date(ltime, fuzzydate);
+ bool tmp= last_value->get_date(thd, ltime, fuzzydate);
null_value= last_value->null_value;
return tmp;
}
diff --git a/sql/item_func.h b/sql/item_func.h
index 754b1cd1eb2..b368e5872fe 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -35,12 +35,11 @@ extern "C" /* Bug in BSDI include file */
#include <cmath>
-class Item_func :public Item_func_or_sum
+class Item_func :public Item_func_or_sum,
+ protected With_sum_func_cache
{
void sync_with_sum_func_and_with_field(List<Item> &list);
protected:
- String *val_str_from_val_str_ascii(String *str, String *str2);
-
virtual bool check_arguments() const
{
return check_argument_types_scalar(0, arg_count);
@@ -56,6 +55,7 @@ protected:
bool check_argument_types_can_return_text(uint start, uint end) const;
bool check_argument_types_can_return_date(uint start, uint end) const;
bool check_argument_types_can_return_time(uint start, uint end) const;
+ void print_cast_temporal(String *str, enum_query_type query_type);
public:
table_map not_null_tables_cache;
@@ -80,49 +80,56 @@ public:
CASE_SEARCHED_FUNC, // Used by ColumnStore/Spider
CASE_SIMPLE_FUNC // Used by ColumnStore/spider
};
+ static scalar_comparison_op functype_to_scalar_comparison_op(Functype type)
+ {
+ switch (type) {
+ case EQ_FUNC: return SCALAR_CMP_EQ;
+ case EQUAL_FUNC: return SCALAR_CMP_EQUAL;
+ case LT_FUNC: return SCALAR_CMP_LT;
+ case LE_FUNC: return SCALAR_CMP_LE;
+ case GE_FUNC: return SCALAR_CMP_GE;
+ case GT_FUNC: return SCALAR_CMP_GT;
+ default: break;
+ }
+ DBUG_ASSERT(0);
+ return SCALAR_CMP_EQ;
+ }
enum Type type() const { return FUNC_ITEM; }
virtual enum Functype functype() const { return UNKNOWN_FUNC; }
Item_func(THD *thd): Item_func_or_sum(thd)
{
- with_sum_func= 0;
with_field= 0;
with_param= 0;
}
- Item_func(THD *thd, Item *a): Item_func_or_sum(thd, a)
+ Item_func(THD *thd, Item *a)
+ :Item_func_or_sum(thd, a), With_sum_func_cache(a)
{
- with_sum_func= a->with_sum_func;
with_param= a->with_param;
with_field= a->with_field;
}
- Item_func(THD *thd, Item *a, Item *b):
- Item_func_or_sum(thd, a, b)
+ Item_func(THD *thd, Item *a, Item *b)
+ :Item_func_or_sum(thd, a, b), With_sum_func_cache(a, b)
{
- with_sum_func= a->with_sum_func || b->with_sum_func;
with_param= a->with_param || b->with_param;
with_field= a->with_field || b->with_field;
}
- Item_func(THD *thd, Item *a, Item *b, Item *c):
- Item_func_or_sum(thd, a, b, c)
+ Item_func(THD *thd, Item *a, Item *b, Item *c)
+ :Item_func_or_sum(thd, a, b, c), With_sum_func_cache(a, b, c)
{
- with_sum_func= a->with_sum_func || b->with_sum_func || c->with_sum_func;
with_field= a->with_field || b->with_field || c->with_field;
with_param= a->with_param || b->with_param || c->with_param;
}
- Item_func(THD *thd, Item *a, Item *b, Item *c, Item *d):
- Item_func_or_sum(thd, a, b, c, d)
+ Item_func(THD *thd, Item *a, Item *b, Item *c, Item *d)
+ :Item_func_or_sum(thd, a, b, c, d), With_sum_func_cache(a, b, c, d)
{
- with_sum_func= a->with_sum_func || b->with_sum_func ||
- c->with_sum_func || d->with_sum_func;
with_field= a->with_field || b->with_field ||
c->with_field || d->with_field;
with_param= a->with_param || b->with_param ||
c->with_param || d->with_param;
}
- Item_func(THD *thd, Item *a, Item *b, Item *c, Item *d, Item* e):
- Item_func_or_sum(thd, a, b, c, d, e)
+ Item_func(THD *thd, Item *a, Item *b, Item *c, Item *d, Item* e)
+ :Item_func_or_sum(thd, a, b, c, d, e), With_sum_func_cache(a, b, c, d, e)
{
- with_sum_func= a->with_sum_func || b->with_sum_func ||
- c->with_sum_func || d->with_sum_func || e->with_sum_func;
with_field= a->with_field || b->with_field ||
c->with_field || d->with_field || e->with_field;
with_param= a->with_param || b->with_param ||
@@ -134,11 +141,10 @@ public:
set_arguments(thd, list);
}
// Constructor used for Item_cond_and/or (see Item comment)
- Item_func(THD *thd, Item_func *item):
- Item_func_or_sum(thd, item),
+ Item_func(THD *thd, Item_func *item)
+ :Item_func_or_sum(thd, item), With_sum_func_cache(item),
not_null_tables_cache(item->not_null_tables_cache)
- {
- }
+ { }
bool fix_fields(THD *, Item **ref);
void cleanup()
{
@@ -174,16 +180,12 @@ public:
virtual void print(String *str, enum_query_type query_type);
void print_op(String *str, enum_query_type query_type);
void print_args(String *str, uint from, enum_query_type query_type);
- inline bool get_arg0_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
- {
- DBUG_ASSERT(!(fuzzy_date & TIME_TIME_ONLY));
- Datetime dt(current_thd, args[0], fuzzy_date);
- return (null_value= dt.copy_to_mysql_time(ltime));
- }
bool is_null() {
update_null_value();
return null_value;
}
+ String *val_str_from_val_str_ascii(String *str, String *str2);
+
void signal_divide_by_null();
friend class udf_handler;
Field *create_field_for_create_select(TABLE *table)
@@ -325,6 +327,11 @@ public:
return this;
}
+ bool has_rand_bit()
+ {
+ return used_tables() & RAND_TABLE_BIT;
+ }
+
bool excl_dep_on_table(table_map tab_map)
{
if (used_tables() & OUTER_REF_TABLE_BIT)
@@ -335,9 +342,16 @@ public:
bool excl_dep_on_grouping_fields(st_select_lex *sel)
{
+ if (has_rand_bit() || with_subquery())
+ return false;
return Item_args::excl_dep_on_grouping_fields(sel);
}
+ bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred)
+ {
+ return Item_args::excl_dep_on_in_subq_left_part(subq_pred);
+ }
+
/*
We assume the result of any function that has a TIMESTAMP argument to be
timezone-dependent, since a TIMESTAMP value in both numeric and string
@@ -380,6 +394,12 @@ public:
- or replaced to an Item_int_with_ref
*/
bool setup_args_and_comparator(THD *thd, Arg_comparator *cmp);
+
+ bool with_sum_func() const { return m_with_sum_func; }
+ With_sum_func_cache* get_with_sum_func_cache() { return this; }
+ Item_func *get_item_func() { return this; }
+ bool is_simplified_cond_processor(void *arg)
+ { return const_item() && !val_int(); }
};
@@ -400,8 +420,8 @@ public:
DBUG_ASSERT(fixed == 1);
return Converter_double_to_longlong(val_real(), unsigned_flag).result();
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date_from_real(ltime, fuzzydate); }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ { return get_date_from_real(thd, ltime, fuzzydate); }
const Type_handler *type_handler() const { return &type_handler_double; }
bool fix_length_and_dec()
{
@@ -438,6 +458,230 @@ public:
{
Type_geometry_attributes::set_geometry_type(type);
}
+ void fix_length_and_dec_long_or_longlong(uint char_length, bool unsigned_arg)
+ {
+ collation.set_numeric();
+ unsigned_flag= unsigned_arg;
+ max_length= char_length;
+#if MARIADB_VERSION_ID < 100500
+ set_handler(Type_handler::type_handler_long_or_longlong(char_length));
+#else
+ set_handler(Type_handler::type_handler_long_or_longlong(char_length,
+ unsigned_arg));
+#endif
+ }
+ void fix_length_and_dec_ulong_or_ulonglong_by_nbits(uint nbits)
+ {
+ uint digits= Type_handler_bit::Bit_decimal_notation_int_digits_by_nbits(nbits);
+ collation.set_numeric();
+ unsigned_flag= true;
+ max_length= digits;
+ if (nbits > 32)
+ set_handler(&type_handler_longlong);
+ else
+ set_handler(&type_handler_long);
+ }
+};
+
+
+class Item_handled_func: public Item_func
+{
+public:
+ class Handler
+ {
+ public:
+ virtual ~Handler() { }
+ virtual String *val_str(Item_handled_func *, String *) const= 0;
+ virtual String *val_str_ascii(Item_handled_func *, String *) const= 0;
+ virtual double val_real(Item_handled_func *) const= 0;
+ virtual longlong val_int(Item_handled_func *) const= 0;
+ virtual my_decimal *val_decimal(Item_handled_func *, my_decimal *) const= 0;
+ virtual bool get_date(THD *thd, Item_handled_func *, MYSQL_TIME *, date_mode_t fuzzydate) const= 0;
+ virtual bool val_native(THD *thd, Item_handled_func *, Native *to) const
+ {
+ DBUG_ASSERT(0);
+ to->length(0);
+ return true;
+ }
+ virtual const Type_handler *return_type_handler() const= 0;
+ virtual bool fix_length_and_dec(Item_handled_func *) const= 0;
+ };
+
+ /**
+ Abstract class for functions returning TIME, DATE, DATETIME or string values,
+ whose data type depends on parameters and is set at fix_fields time.
+ */
+ class Handler_temporal: public Handler
+ {
+ public:
+ String *val_str(Item_handled_func *item, String *to) const
+ {
+ StringBuffer<MAX_FIELD_WIDTH> ascii_buf;
+ return item->val_str_from_val_str_ascii(to, &ascii_buf);
+ }
+ };
+
+ /**
+ Abstract class for functions returning strings,
+ which are generated from get_date() results,
+ when get_date() can return different MYSQL_TIMESTAMP_XXX per row.
+ */
+ class Handler_temporal_string: public Handler_temporal
+ {
+ public:
+ const Type_handler *return_type_handler() const
+ {
+ return &type_handler_string;
+ }
+ double val_real(Item_handled_func *item) const
+ {
+ return Temporal_hybrid(item).to_double();
+ }
+ longlong val_int(Item_handled_func *item) const
+ {
+ return Temporal_hybrid(item).to_longlong();
+ }
+ my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const
+ {
+ return Temporal_hybrid(item).to_decimal(to);
+ }
+ String *val_str_ascii(Item_handled_func *item, String *to) const
+ {
+ return Temporal_hybrid(item).to_string(to, item->decimals);
+ }
+ };
+
+
+ class Handler_date: public Handler_temporal
+ {
+ public:
+ const Type_handler *return_type_handler() const
+ {
+ return &type_handler_newdate;
+ }
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ item->fix_attributes_date();
+ return false;
+ }
+ double val_real(Item_handled_func *item) const
+ {
+ return Date(item).to_double();
+ }
+ longlong val_int(Item_handled_func *item) const
+ {
+ return Date(item).to_longlong();
+ }
+ my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const
+ {
+ return Date(item).to_decimal(to);
+ }
+ String *val_str_ascii(Item_handled_func *item, String *to) const
+ {
+ return Date(item).to_string(to);
+ }
+ };
+
+
+ class Handler_time: public Handler_temporal
+ {
+ public:
+ const Type_handler *return_type_handler() const
+ {
+ return &type_handler_time2;
+ }
+ double val_real(Item_handled_func *item) const
+ {
+ return Time(item).to_double();
+ }
+ longlong val_int(Item_handled_func *item) const
+ {
+ return Time(item).to_longlong();
+ }
+ my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const
+ {
+ return Time(item).to_decimal(to);
+ }
+ String *val_str_ascii(Item_handled_func *item, String *to) const
+ {
+ return Time(item).to_string(to, item->decimals);
+ }
+ bool val_native(THD *thd, Item_handled_func *item, Native *to) const
+ {
+ return Time(thd, item).to_native(to, item->decimals);
+ }
+ };
+
+
+ class Handler_datetime: public Handler_temporal
+ {
+ public:
+ const Type_handler *return_type_handler() const
+ {
+ return &type_handler_datetime2;
+ }
+ double val_real(Item_handled_func *item) const
+ {
+ return Datetime(item).to_double();
+ }
+ longlong val_int(Item_handled_func *item) const
+ {
+ return Datetime(item).to_longlong();
+ }
+ my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const
+ {
+ return Datetime(item).to_decimal(to);
+ }
+ String *val_str_ascii(Item_handled_func *item, String *to) const
+ {
+ return Datetime(item).to_string(to, item->decimals);
+ }
+ };
+
+
+protected:
+ const Handler *m_func_handler;
+public:
+ Item_handled_func(THD *thd, Item *a)
+ :Item_func(thd, a), m_func_handler(NULL) { }
+ Item_handled_func(THD *thd, Item *a, Item *b)
+ :Item_func(thd, a, b), m_func_handler(NULL) { }
+ void set_func_handler(const Handler *handler)
+ {
+ m_func_handler= handler;
+ }
+ const Type_handler *type_handler() const
+ {
+ return m_func_handler->return_type_handler();
+ }
+ String *val_str(String *to)
+ {
+ return m_func_handler->val_str(this, to);
+ }
+ String *val_str_ascii(String *to)
+ {
+ return m_func_handler->val_str_ascii(this, to);
+ }
+ double val_real()
+ {
+ return m_func_handler->val_real(this);
+ }
+ longlong val_int()
+ {
+ return m_func_handler->val_int(this);
+ }
+ my_decimal *val_decimal(my_decimal *to)
+ {
+ return m_func_handler->val_decimal(this, to);
+ }
+ bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate)
+ {
+ return m_func_handler->get_date(thd, this, to, fuzzydate);
+ }
+ bool val_native(THD *thd, Native *to)
+ {
+ return m_func_handler->val_native(thd, this, to);
+ }
};
@@ -463,15 +707,15 @@ class Item_func_hybrid_field_type: public Item_hybrid_func
Helper methods to make sure that the result of
decimal_op(), str_op() and date_op() is properly synched with null_value.
*/
- bool date_op_with_null_check(MYSQL_TIME *ltime)
+ bool date_op_with_null_check(THD *thd, MYSQL_TIME *ltime)
{
- bool rc= date_op(ltime, 0);
+ bool rc= date_op(thd, ltime, date_mode_t(0));
DBUG_ASSERT(!rc ^ null_value);
return rc;
}
- bool time_op_with_null_check(MYSQL_TIME *ltime)
+ bool time_op_with_null_check(THD *thd, MYSQL_TIME *ltime)
{
- bool rc= time_op(ltime);
+ bool rc= time_op(thd, ltime);
DBUG_ASSERT(!rc ^ null_value);
DBUG_ASSERT(rc || ltime->time_type == MYSQL_TIMESTAMP_TIME);
return rc;
@@ -482,17 +726,6 @@ class Item_func_hybrid_field_type: public Item_hybrid_func
DBUG_ASSERT((res != NULL) ^ null_value);
return res;
}
- my_decimal *decimal_op_with_null_check(my_decimal *decimal_buffer)
- {
- my_decimal *res= decimal_op(decimal_buffer);
- DBUG_ASSERT((res != NULL) ^ null_value);
- return res;
- }
- bool make_zero_mysql_time(MYSQL_TIME *ltime, ulonglong fuzzydate)
- {
- bzero(ltime, sizeof(*ltime));
- return null_value|= !(fuzzydate & TIME_FUZZY_DATES);
- }
public:
// Value methods that involve no conversion
@@ -500,10 +733,6 @@ public:
{
return str_op_with_null_check(&str_value);
}
- my_decimal *val_decimal_from_decimal_op(my_decimal *dec)
- {
- return decimal_op_with_null_check(dec);
- }
longlong val_int_from_int_op()
{
return int_op();
@@ -514,7 +743,6 @@ public:
}
// Value methods that involve conversion
- String *val_str_from_decimal_op(String *str);
String *val_str_from_real_op(String *str);
String *val_str_from_int_op(String *str);
String *val_str_from_date_op(String *str);
@@ -528,21 +756,14 @@ public:
longlong val_int_from_str_op();
longlong val_int_from_real_op();
- longlong val_int_from_decimal_op();
longlong val_int_from_date_op();
longlong val_int_from_time_op();
double val_real_from_str_op();
- double val_real_from_decimal_op();
double val_real_from_date_op();
double val_real_from_time_op();
double val_real_from_int_op();
- bool get_date_from_str_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool get_date_from_real_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool get_date_from_decimal_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool get_date_from_int_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
-
public:
Item_func_hybrid_field_type(THD *thd):
Item_hybrid_func(thd)
@@ -586,11 +807,17 @@ public:
DBUG_ASSERT(null_value == (res == NULL));
return res;
}
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date)
+ bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode)
{
DBUG_ASSERT(fixed);
return Item_func_hybrid_field_type::type_handler()->
- Item_func_hybrid_field_type_get_date(this, res, fuzzy_date);
+ Item_func_hybrid_field_type_get_date_with_warn(thd, this, to, mode);
+ }
+
+ bool val_native(THD *thd, Native *to)
+ {
+ DBUG_ASSERT(fixed);
+ return native_op(thd, to);
}
/**
@@ -600,6 +827,20 @@ public:
@return The result of the operation.
*/
virtual longlong int_op()= 0;
+ Longlong_null to_longlong_null_op()
+ {
+ longlong nr= int_op();
+ /*
+ C++ does not guarantee the order of parameter evaluation,
+ so to make sure "null_value" is passed to the constructor
+ after the int_op() call, int_op() is caled on a separate line.
+ */
+ return Longlong_null(nr, null_value);
+ }
+ Longlong_hybrid_null to_longlong_hybrid_null_op()
+ {
+ return Longlong_hybrid_null(to_longlong_null_op(), unsigned_flag);
+ }
/**
@brief Performs the operation that this functions implements when the
@@ -608,6 +849,12 @@ public:
@return The result of the operation.
*/
virtual double real_op()= 0;
+ Double_null to_double_null_op()
+ {
+ // val_real() must be caleed on a separate line. See to_longlong_null()
+ double nr= real_op();
+ return Double_null(nr, null_value);
+ }
/**
@brief Performs the operation that this functions implements when the
@@ -634,15 +881,16 @@ public:
field type is DATETIME or DATE.
@return The result of the operation.
*/
- virtual bool date_op(MYSQL_TIME *res, ulonglong fuzzy_date)= 0;
+ virtual bool date_op(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate)= 0;
/**
@brief Performs the operation that this functions implements when
field type is TIME.
@return The result of the operation.
*/
- virtual bool time_op(MYSQL_TIME *res)= 0;
+ virtual bool time_op(THD *thd, MYSQL_TIME *res)= 0;
+ virtual bool native_op(THD *thd, Native *native)= 0;
};
@@ -700,12 +948,17 @@ public:
Item_func_hybrid_field_type(thd, list)
{ }
String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
- bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(0);
return true;
}
- bool time_op(MYSQL_TIME *ltime)
+ bool time_op(THD *thd, MYSQL_TIME *ltime)
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ bool native_op(THD *thd, Native *to)
{
DBUG_ASSERT(0);
return true;
@@ -791,8 +1044,8 @@ public:
{ collation.set_numeric(); }
double val_real();
String *val_str(String*str);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date_from_int(ltime, fuzzydate); }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ { return get_date_from_int(thd, ltime, fuzzydate); }
const Type_handler *type_handler() const= 0;
bool fix_length_and_dec() { return FALSE; }
};
@@ -812,6 +1065,19 @@ public:
};
+class Item_func_hash: public Item_int_func
+{
+public:
+ Item_func_hash(THD *thd, List<Item> &item): Item_int_func(thd, item)
+ {}
+ longlong val_int();
+ bool fix_length_and_dec();
+ const Type_handler *type_handler() const { return &type_handler_long; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_hash>(thd, this); }
+ const char *func_name() const { return "<hash>"; }
+};
+
class Item_longlong_func: public Item_int_func
{
public:
@@ -982,12 +1248,15 @@ public:
fix_char_length(my_decimal_precision_to_length_no_truncation(len, dec,
unsigned_flag));
}
- String *val_str(String *str);
- double val_real();
- longlong val_int();
+ String *val_str(String *str) { return VDec(this).to_string(str); }
+ double val_real() { return VDec(this).to_double(); }
+ longlong val_int() { return VDec(this).to_longlong(unsigned_flag); }
my_decimal *val_decimal(my_decimal*);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date_from_decimal(ltime, fuzzydate); }
+ bool get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode)
+ {
+ return decimal_to_datetime_with_warn(thd, VDec(this).ptr(), to, mode,
+ NULL, NULL);
+ }
const Type_handler *type_handler() const { return &type_handler_newdecimal; }
void fix_length_and_dec_generic() {}
bool fix_length_and_dec()
@@ -1434,13 +1703,33 @@ public:
};
-class Item_func_int_val :public Item_func_num1
+class Item_func_int_val :public Item_func_hybrid_field_type
{
public:
- Item_func_int_val(THD *thd, Item *a): Item_func_num1(thd, a) {}
+ Item_func_int_val(THD *thd, Item *a): Item_func_hybrid_field_type(thd, a) {}
+ bool check_partition_func_processor(void *int_arg) { return FALSE; }
+ bool check_vcol_func_processor(void *arg) { return FALSE; }
+ virtual decimal_round_mode round_mode() const= 0;
void fix_length_and_dec_double();
void fix_length_and_dec_int_or_decimal();
+ void fix_length_and_dec_time()
+ {
+ fix_attributes_time(0);
+ set_handler(&type_handler_time2);
+ }
+ void fix_length_and_dec_datetime()
+ {
+ fix_attributes_datetime(0);
+ set_handler(&type_handler_datetime2);
+ maybe_null= true; // E.g. CEILING(TIMESTAMP'0000-01-01 23:59:59.9')
+ }
bool fix_length_and_dec();
+ String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
+ bool native_op(THD *thd, Native *to)
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
};
@@ -1449,9 +1738,12 @@ class Item_func_ceiling :public Item_func_int_val
public:
Item_func_ceiling(THD *thd, Item *a): Item_func_int_val(thd, a) {}
const char *func_name() const { return "ceiling"; }
+ decimal_round_mode round_mode() const { return CEILING; }
longlong int_op();
double real_op();
my_decimal *decimal_op(my_decimal *);
+ bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool time_op(THD *thd, MYSQL_TIME *ltime);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_ceiling>(thd, this); }
};
@@ -1462,33 +1754,59 @@ class Item_func_floor :public Item_func_int_val
public:
Item_func_floor(THD *thd, Item *a): Item_func_int_val(thd, a) {}
const char *func_name() const { return "floor"; }
+ decimal_round_mode round_mode() const { return FLOOR; }
longlong int_op();
double real_op();
my_decimal *decimal_op(my_decimal *);
+ bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool time_op(THD *thd, MYSQL_TIME *ltime);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_floor>(thd, this); }
};
/* This handles round and truncate */
-class Item_func_round :public Item_func_numhybrid
+class Item_func_round :public Item_func_hybrid_field_type
{
bool truncate;
void fix_length_and_dec_decimal(uint decimals_to_set);
void fix_length_and_dec_double(uint decimals_to_set);
+ bool test_if_length_can_increase();
public:
Item_func_round(THD *thd, Item *a, Item *b, bool trunc_arg)
- :Item_func_numhybrid(thd, a, b), truncate(trunc_arg) {}
+ :Item_func_hybrid_field_type(thd, a, b), truncate(trunc_arg) {}
const char *func_name() const { return truncate ? "truncate" : "round"; }
double real_op();
longlong int_op();
my_decimal *decimal_op(my_decimal *);
+ bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool time_op(THD *thd, MYSQL_TIME *ltime);
+ bool native_op(THD *thd, Native *to)
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ String *str_op(String *str)
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
void fix_arg_decimal();
- void fix_arg_int();
+ void fix_arg_int(const Type_handler *preferred,
+ const Type_std_attributes *preferred_attributes,
+ bool use_decimal_on_length_increase);
+ void fix_arg_hex_hybrid();
void fix_arg_double();
+ void fix_arg_time();
+ void fix_arg_datetime();
+ void fix_arg_temporal(const Type_handler *h, uint int_part_length);
bool fix_length_and_dec()
{
- return args[0]->type_handler()->Item_func_round_fix_length_and_dec(this);
+ /*
+ We don't want to translate ENUM/SET to CHAR here.
+ So let's real_type_handler(), not type_handler().
+ */
+ return args[0]->real_type_handler()->Item_func_round_fix_length_and_dec(this);
}
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_round>(thd, this); }
@@ -1585,8 +1903,8 @@ public:
double val_real_native();
longlong val_int_native();
my_decimal *val_decimal_native(my_decimal *);
- bool get_date_native(MYSQL_TIME *res, ulonglong fuzzydate);
- bool get_time_native(MYSQL_TIME *res);
+ bool get_date_native(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
+ bool get_time_native(THD *thd, MYSQL_TIME *res);
double val_real()
{
@@ -1612,12 +1930,13 @@ public:
return Item_func_min_max::type_handler()->
Item_func_min_max_val_decimal(this, dec);
}
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date)
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed);
return Item_func_min_max::type_handler()->
- Item_func_min_max_get_date(this, res, fuzzy_date);
+ Item_func_min_max_get_date(thd, this, res, fuzzydate);
}
+ bool val_native(THD *thd, Native *to);
void aggregate_attributes_real(Item **items, uint nitems)
{
/*
@@ -1681,10 +2000,12 @@ public:
double val_real() { return val_real_from_item(args[0]); }
longlong val_int() { return val_int_from_item(args[0]); }
String *val_str(String *str) { return val_str_from_item(args[0], str); }
+ bool val_native(THD *thd, Native *to)
+ { return val_native_from_item(thd, args[0], to); }
my_decimal *val_decimal(my_decimal *dec)
{ return val_decimal_from_item(args[0], dec); }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date_from_item(args[0], ltime, fuzzydate); }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ { return get_date_from_item(thd, args[0], ltime, fuzzydate); }
const char *func_name() const { return "rollup_const"; }
bool const_item() const { return 0; }
const Type_handler *type_handler() const { return args[0]->type_handler(); }
@@ -2051,6 +2372,18 @@ protected:
udf_handler udf;
bool is_expensive_processor(void *arg) { return TRUE; }
+ class VDec_udf: public Dec_ptr_and_buffer
+ {
+ public:
+ VDec_udf(Item_udf_func *func, udf_handler *udf)
+ {
+ my_bool tmp_null_value;
+ m_ptr= udf->val_decimal(&tmp_null_value, &m_buffer);
+ DBUG_ASSERT(is_null() == (tmp_null_value != 0));
+ func->null_value= is_null();
+ }
+ };
+
public:
Item_udf_func(THD *thd, udf_func *udf_arg):
Item_func(thd), udf(udf_arg) {}
@@ -2128,10 +2461,12 @@ public:
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC);
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate);
}
+ bool excl_dep_on_grouping_fields(st_select_lex *sel)
+ { return false; }
};
@@ -2191,10 +2526,19 @@ public:
Item_udf_func(thd, udf_arg) {}
Item_func_udf_decimal(THD *thd, udf_func *udf_arg, List<Item> &list):
Item_udf_func(thd, udf_arg, list) {}
- longlong val_int();
- double val_real();
+ longlong val_int()
+ {
+ return VDec_udf(this, &udf).to_longlong(unsigned_flag);
+ }
+ double val_real()
+ {
+ return VDec_udf(this, &udf).to_double();
+ }
my_decimal *val_decimal(my_decimal *);
- String *val_str(String *str);
+ String *val_str(String *str)
+ {
+ return VDec_udf(this, &udf).to_string_round(str, decimals);
+ }
const Type_handler *type_handler() const { return &type_handler_newdecimal; }
bool fix_length_and_dec() { fix_num_length_and_dec(); return FALSE; }
Item *get_copy(THD *thd)
@@ -2425,13 +2769,15 @@ public:
Item_func_user_var(THD *thd, Item_func_user_var *item)
:Item_hybrid_func(thd, item),
m_var_entry(item->m_var_entry), name(item->name) { }
- Field *create_tmp_field(bool group, TABLE *table)
- { return create_table_field_from_handler(table); }
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param);
Field *create_field_for_create_select(TABLE *table)
{ return create_table_field_from_handler(table); }
bool check_vcol_func_processor(void *arg);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return type_handler()->Item_get_date(this, ltime, fuzzydate); }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ {
+ return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate);
+ }
};
@@ -2559,14 +2905,14 @@ public:
in List<Item> and desire to place this code somewhere near other functions
working with user variables.
*/
-class Item_user_var_as_out_param :public Item,
+class Item_user_var_as_out_param :public Item_fixed_hybrid,
public Load_data_outvar
{
LEX_CSTRING org_name;
user_var_entry *entry;
public:
Item_user_var_as_out_param(THD *thd, const LEX_CSTRING *a)
- :Item(thd)
+ :Item_fixed_hybrid(thd)
{
DBUG_ASSERT(a->length < UINT_MAX32);
org_name= *a;
@@ -2601,12 +2947,18 @@ public:
{
return 0;
}
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
/* We should return something different from FIELD_ITEM here */
- enum Type type() const { return STRING_ITEM;}
+ enum Type type() const { return CONST_ITEM;}
double val_real();
longlong val_int();
String *val_str(String *str);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
my_decimal *val_decimal(my_decimal *decimal_buffer);
/* fix_fields() binds variable name with its entry structure */
bool fix_fields(THD *thd, Item **ref);
@@ -2653,9 +3005,9 @@ public:
String* val_str(String*);
my_decimal *val_decimal(my_decimal *dec_buf)
{ return val_decimal_from_real(dec_buf); }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate);
}
/* TODO: fix to support views */
const char *func_name() const { return "get_system_var"; }
@@ -2914,6 +3266,8 @@ public:
const Type_handler *type_handler() const;
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param);
Field *create_field_for_create_select(TABLE *table)
{
return result_type() != STRING_RESULT ?
@@ -2943,7 +3297,7 @@ public:
return sp_result_field->val_decimal(dec_buf);
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
if (execute())
return true;
@@ -2969,6 +3323,13 @@ public:
return str;
}
+ bool val_native(THD *thd, Native *to)
+ {
+ if (execute())
+ return true;
+ return null_value= sp_result_field->val_native(to);
+ }
+
void update_null_value()
{
execute();
@@ -3003,6 +3364,8 @@ public:
not_null_tables_cache= 0;
return 0;
}
+ bool excl_dep_on_grouping_fields(st_select_lex *sel)
+ { return false; }
};
@@ -3097,7 +3460,8 @@ public:
longlong val_int();
String *val_str(String *);
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ bool val_native(THD *thd, Native *);
bool fix_length_and_dec();
const char *func_name() const { return "last_value"; }
const Type_handler *type_handler() const { return last_value->type_handler(); }
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index f2cc2e61b96..ef7ccf1dad6 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -918,7 +918,7 @@ String *Item_func_point::val_str(String *str)
if ((null_value= (args[0]->null_value ||
args[1]->null_value ||
- str->realloc(4/*SRID*/ + 1 + 4 + SIZEOF_STORED_DOUBLE * 2))))
+ str->alloc(4/*SRID*/ + 1 + 4 + SIZEOF_STORED_DOUBLE * 2))))
return 0;
str->set_charset(&my_charset_bin);
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index 75545b8e4f3..245a8353d35 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -512,7 +512,7 @@ public:
return TRUE;
for (unsigned int i= 0; i < arg_count; ++i)
{
- if (args[i]->fixed && args[i]->field_type() != MYSQL_TYPE_GEOMETRY)
+ if (args[i]->is_fixed() && args[i]->field_type() != MYSQL_TYPE_GEOMETRY)
{
String str;
args[i]->print(&str, QT_NO_DATA_EXPANSION);
diff --git a/sql/item_inetfunc.cc b/sql/item_inetfunc.cc
index 52c3e0635af..a2abbce6619 100644
--- a/sql/item_inetfunc.cc
+++ b/sql/item_inetfunc.cc
@@ -21,9 +21,18 @@
///////////////////////////////////////////////////////////////////////////
-static const int IN_ADDR_SIZE= sizeof (in_addr);
-static const int IN6_ADDR_SIZE= sizeof (in6_addr);
-static const int IN6_ADDR_NUM_WORDS= IN6_ADDR_SIZE / 2;
+static const size_t IN_ADDR_SIZE= 4;
+static const size_t IN_ADDR_MAX_CHAR_LENGTH= 15;
+
+static const size_t IN6_ADDR_SIZE= 16;
+static const size_t IN6_ADDR_NUM_WORDS= IN6_ADDR_SIZE / 2;
+
+/**
+ Non-abbreviated syntax is 8 groups, up to 4 digits each,
+ plus 7 delimiters between the groups.
+ Abbreviated syntax is even shorter.
+*/
+static const uint IN6_ADDR_MAX_CHAR_LENGTH= 8 * 4 + 7;
static const char HEX_DIGITS[]= "0123456789abcdef";
@@ -89,7 +98,6 @@ err:
return 0;
}
-///////////////////////////////////////////////////////////////////////////
String* Item_func_inet_ntoa::val_str(String* str)
{
@@ -139,109 +147,264 @@ String* Item_func_inet_ntoa::val_str(String* str)
///////////////////////////////////////////////////////////////////////////
-/**
- Check the function argument, handle errors properly.
-
- @return The function value.
-*/
-longlong Item_func_inet_bool_base::val_int()
+class Inet4
{
- DBUG_ASSERT(fixed);
-
- // String argument expected
- if (unlikely(args[0]->result_type() != STRING_RESULT))
- return 0;
-
- String buffer;
- String *arg_str= args[0]->val_str(&buffer);
-
- if (unlikely(!arg_str)) // Out-of memory happened. error has been reported.
- return 0; // Or: the underlying field is NULL
+ char m_buffer[IN_ADDR_SIZE];
+protected:
+ bool ascii_to_ipv4(const char *str, size_t length);
+ bool character_string_to_ipv4(const char *str, size_t str_length,
+ CHARSET_INFO *cs)
+ {
+ if (cs->state & MY_CS_NONASCII)
+ {
+ char tmp[IN_ADDR_MAX_CHAR_LENGTH];
+ String_copier copier;
+ uint length= copier.well_formed_copy(&my_charset_latin1, tmp, sizeof(tmp),
+ cs, str, str_length);
+ return ascii_to_ipv4(tmp, length);
+ }
+ return ascii_to_ipv4(str, str_length);
+ }
+ bool binary_to_ipv4(const char *str, size_t length)
+ {
+ if (length != sizeof(m_buffer))
+ return true;
+ memcpy(m_buffer, str, length);
+ return false;
+ }
+ // Non-initializing constructor
+ Inet4() { }
+public:
+ void to_binary(char *dst, size_t dstsize) const
+ {
+ DBUG_ASSERT(dstsize >= sizeof(m_buffer));
+ memcpy(dst, m_buffer, sizeof(m_buffer));
+ }
+ bool to_binary(String *to) const
+ {
+ return to->copy(m_buffer, sizeof(m_buffer), &my_charset_bin);
+ }
+ size_t to_string(char *dst, size_t dstsize) const;
+ bool to_string(String *to) const
+ {
+ to->set_charset(&my_charset_latin1);
+ if (to->alloc(INET_ADDRSTRLEN))
+ return true;
+ to->length((uint32) to_string((char*) to->ptr(), INET_ADDRSTRLEN));
+ return false;
+ }
+};
- return calc_value(arg_str) ? 1 : 0;
-}
-///////////////////////////////////////////////////////////////////////////
+class Inet4_null: public Inet4, public Null_flag
+{
+public:
+ // Initialize from a text representation
+ Inet4_null(const char *str, size_t length, CHARSET_INFO *cs)
+ :Null_flag(character_string_to_ipv4(str, length, cs))
+ { }
+ Inet4_null(const String &str)
+ :Inet4_null(str.ptr(), str.length(), str.charset())
+ { }
+ // Initialize from a binary representation
+ Inet4_null(const char *str, size_t length)
+ :Null_flag(binary_to_ipv4(str, length))
+ { }
+ Inet4_null(const Binary_string &str)
+ :Inet4_null(str.ptr(), str.length())
+ { }
+public:
+ const Inet4& to_inet4() const
+ {
+ DBUG_ASSERT(!is_null());
+ return *this;
+ }
+ void to_binary(char *dst, size_t dstsize) const
+ {
+ to_inet4().to_binary(dst, dstsize);
+ }
+ bool to_binary(String *to) const
+ {
+ return to_inet4().to_binary(to);
+ }
+ size_t to_string(char *dst, size_t dstsize) const
+ {
+ return to_inet4().to_string(dst, dstsize);
+ }
+ bool to_string(String *to) const
+ {
+ return to_inet4().to_string(to);
+ }
+};
-/**
- Check the function argument, handle errors properly.
- @param [out] buffer Buffer for string operations.
+class Inet6
+{
+ char m_buffer[IN6_ADDR_SIZE];
+protected:
+ bool make_from_item(Item *item);
+ bool ascii_to_ipv6(const char *str, size_t str_length);
+ bool character_string_to_ipv6(const char *str, size_t str_length,
+ CHARSET_INFO *cs)
+ {
+ if (cs->state & MY_CS_NONASCII)
+ {
+ char tmp[IN6_ADDR_MAX_CHAR_LENGTH];
+ String_copier copier;
+ uint length= copier.well_formed_copy(&my_charset_latin1, tmp, sizeof(tmp),
+ cs, str, str_length);
+ return ascii_to_ipv6(tmp, length);
+ }
+ return ascii_to_ipv6(str, str_length);
+ }
+ bool binary_to_ipv6(const char *str, size_t length)
+ {
+ if (length != sizeof(m_buffer))
+ return true;
+ memcpy(m_buffer, str, length);
+ return false;
+ }
+ // Non-initializing constructor
+ Inet6() { }
+public:
+ bool to_binary(String *to) const
+ {
+ return to->copy(m_buffer, sizeof(m_buffer), &my_charset_bin);
+ }
+ size_t to_string(char *dst, size_t dstsize) const;
+ bool to_string(String *to) const
+ {
+ to->set_charset(&my_charset_latin1);
+ if (to->alloc(INET6_ADDRSTRLEN))
+ return true;
+ to->length((uint32) to_string((char*) to->ptr(), INET6_ADDRSTRLEN));
+ return false;
+ }
+ bool is_v4compat() const
+ {
+ static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size");
+ return IN6_IS_ADDR_V4COMPAT((struct in6_addr *) m_buffer);
+ }
+ bool is_v4mapped() const
+ {
+ static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size");
+ return IN6_IS_ADDR_V4MAPPED((struct in6_addr *) m_buffer);
+ }
+};
- @return The function value.
-*/
-String *Item_func_inet_str_base::val_str_ascii(String *buffer)
+class Inet6_null: public Inet6, public Null_flag
{
- DBUG_ASSERT(fixed);
-
- // String argument expected
- if (unlikely(args[0]->result_type() != STRING_RESULT))
+public:
+ // Initialize from a text representation
+ Inet6_null(const char *str, size_t length, CHARSET_INFO *cs)
+ :Null_flag(character_string_to_ipv6(str, length, cs))
+ { }
+ Inet6_null(const String &str)
+ :Inet6_null(str.ptr(), str.length(), str.charset())
+ { }
+ // Initialize from a binary representation
+ Inet6_null(const char *str, size_t length)
+ :Null_flag(binary_to_ipv6(str, length))
+ { }
+ Inet6_null(const Binary_string &str)
+ :Inet6_null(str.ptr(), str.length())
+ { }
+ // Initialize from an Item
+ Inet6_null(Item *item)
+ :Null_flag(make_from_item(item))
+ { }
+public:
+ const Inet6& to_inet6() const
{
- null_value= true;
- return NULL;
+ DBUG_ASSERT(!is_null());
+ return *this;
}
-
- StringBuffer<STRING_BUFFER_USUAL_SIZE> tmp;
- String *arg_str= args[0]->val_str(&tmp);
- if (unlikely(!arg_str))
+ bool to_binary(String *to) const
{
- // Out-of memory happened. error has been reported.
- // Or: the underlying field is NULL
- null_value= true;
- return NULL;
+ DBUG_ASSERT(!is_null());
+ return to_inet6().to_binary(to);
+ }
+ size_t to_string(char *dst, size_t dstsize) const
+ {
+ return to_inet6().to_string(dst, dstsize);
}
+ bool to_string(String *to) const
+ {
+ return to_inet6().to_string(to);
+ }
+ bool is_v4compat() const
+ {
+ return to_inet6().is_v4compat();
+ }
+ bool is_v4mapped() const
+ {
+ return to_inet6().is_v4mapped();
+ }
+};
- null_value= !calc_value(arg_str, buffer);
- return unlikely(null_value) ? NULL : buffer;
-}
+bool Inet6::make_from_item(Item *item)
+{
+ String tmp(m_buffer, sizeof(m_buffer), &my_charset_bin);
+ String *str= item->val_str(&tmp);
+ /*
+ Charset could be tested in item->collation.collation before the val_str()
+ call, but traditionally Inet6 functions still call item->val_str()
+ for non-binary arguments and therefore execute side effects.
+ */
+ if (!str || str->length() != sizeof(m_buffer) ||
+ str->charset() != &my_charset_bin)
+ return true;
+ if (str->ptr() != m_buffer)
+ memcpy(m_buffer, str->ptr(), sizeof(m_buffer));
+ return false;
+};
-///////////////////////////////////////////////////////////////////////////
/**
Tries to convert given string to binary IPv4-address representation.
This is a portable alternative to inet_pton(AF_INET).
@param str String to convert.
- @param str_len String length.
- @param[out] ipv4_address Buffer to store IPv4-address.
+ @param str_length String length.
@return Completion status.
- @retval false Given string does not represent an IPv4-address.
- @retval true The string has been converted successfully.
+ @retval true - error, the given string does not represent an IPv4-address.
+ @retval false - ok, the string has been converted successfully.
@note The problem with inet_pton() is that it treats leading zeros in
IPv4-part differently on different platforms.
*/
-static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_address)
+bool Inet4::ascii_to_ipv4(const char *str, size_t str_length)
{
if (str_length < 7)
{
- DBUG_PRINT("error", ("str_to_ipv4(%.*s): "
+ DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): "
"invalid IPv4 address: too short.",
(int) str_length, str));
- return false;
+ return true;
}
- if (str_length > 15)
+ if (str_length > IN_ADDR_MAX_CHAR_LENGTH)
{
- DBUG_PRINT("error", ("str_to_ipv4(%.*s): "
+ DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): "
"invalid IPv4 address: too long.",
(int) str_length, str));
- return false;
+ return true;
}
- unsigned char *ipv4_bytes= (unsigned char *) ipv4_address;
+ unsigned char *ipv4_bytes= (unsigned char *) &m_buffer;
+ const char *str_end= str + str_length;
const char *p= str;
int byte_value= 0;
int chars_in_group= 0;
int dot_count= 0;
char c= 0;
- while (((p - str) < (int)str_length) && *p)
+ while (p < str_end && *p)
{
c= *p++;
@@ -251,30 +414,30 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
if (chars_in_group > 3)
{
- DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
"too many characters in a group.",
(int) str_length, str));
- return false;
+ return true;
}
byte_value= byte_value * 10 + (c - '0');
if (byte_value > 255)
{
- DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
"invalid byte value.",
(int) str_length, str));
- return false;
+ return true;
}
}
else if (c == '.')
{
if (chars_in_group == 0)
{
- DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
"too few characters in a group.",
(int) str_length, str));
- return false;
+ return true;
}
ipv4_bytes[dot_count]= (unsigned char) byte_value;
@@ -285,79 +448,77 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
if (dot_count > 3)
{
- DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
"too many dots.", (int) str_length, str));
- return false;
+ return true;
}
}
else
{
- DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
"invalid character at pos %d.",
(int) str_length, str, (int) (p - str)));
- return false;
+ return true;
}
}
if (c == '.')
{
- DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
"ending at '.'.", (int) str_length, str));
- return false;
+ return true;
}
if (dot_count != 3)
{
- DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
"too few groups.",
(int) str_length, str));
- return false;
+ return true;
}
ipv4_bytes[3]= (unsigned char) byte_value;
- DBUG_PRINT("info", ("str_to_ipv4(%.*s): valid IPv4 address: %d.%d.%d.%d",
+ DBUG_PRINT("info", ("ascii_to_ipv4(%.*s): valid IPv4 address: %d.%d.%d.%d",
(int) str_length, str,
ipv4_bytes[0], ipv4_bytes[1],
ipv4_bytes[2], ipv4_bytes[3]));
- return true;
+ return false;
}
-///////////////////////////////////////////////////////////////////////////
/**
Tries to convert given string to binary IPv6-address representation.
This is a portable alternative to inet_pton(AF_INET6).
@param str String to convert.
- @param str_len String length.
- @param[out] ipv6_address Buffer to store IPv6-address.
+ @param str_length String length.
@return Completion status.
- @retval false Given string does not represent an IPv6-address.
- @retval true The string has been converted successfully.
+ @retval true - error, the given string does not represent an IPv6-address.
+ @retval false - ok, the string has been converted successfully.
@note The problem with inet_pton() is that it treats leading zeros in
IPv4-part differently on different platforms.
*/
-static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
+bool Inet6::ascii_to_ipv6(const char *str, size_t str_length)
{
if (str_length < 2)
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: too short.",
- str_length, str));
- return false;
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: too short.",
+ (int) str_length, str));
+ return true;
}
- if (str_length > 8 * 4 + 7)
+ if (str_length > IN6_ADDR_MAX_CHAR_LENGTH)
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: too long.",
- str_length, str));
- return false;
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: too long.",
+ (int) str_length, str));
+ return true;
}
- memset(ipv6_address, 0, IN6_ADDR_SIZE);
+ memset(m_buffer, 0, sizeof(m_buffer));
const char *p= str;
@@ -367,21 +528,21 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
if (*p != ':')
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
- "can not start with ':x'.", str_length, str));
- return false;
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
+ "can not start with ':x'.", (int) str_length, str));
+ return true;
}
}
- char *ipv6_bytes= (char *) ipv6_address;
- char *ipv6_bytes_end= ipv6_bytes + IN6_ADDR_SIZE;
- char *dst= ipv6_bytes;
+ const char *str_end= str + str_length;
+ char *ipv6_bytes_end= m_buffer + sizeof(m_buffer);
+ char *dst= m_buffer;
char *gap_ptr= NULL;
const char *group_start_ptr= p;
int chars_in_group= 0;
int group_value= 0;
- while (((p - str) < str_length) && *p)
+ while (p < str_end && *p)
{
char c= *p++;
@@ -393,27 +554,27 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
{
if (gap_ptr)
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
- "too many gaps(::).", str_length, str));
- return false;
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
+ "too many gaps(::).", (int) str_length, str));
+ return true;
}
gap_ptr= dst;
continue;
}
- if (!*p || ((p - str) >= str_length))
+ if (!*p || p >= str_end)
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
- "ending at ':'.", str_length, str));
- return false;
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
+ "ending at ':'.", (int) str_length, str));
+ return true;
}
if (dst + 2 > ipv6_bytes_end)
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
- "too many groups (1).", str_length, str));
- return false;
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
+ "too many groups (1).", (int) str_length, str));
+ return true;
}
dst[0]= (unsigned char) (group_value >> 8) & 0xff;
@@ -427,20 +588,21 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
{
if (dst + IN_ADDR_SIZE > ipv6_bytes_end)
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
- "unexpected IPv4-part.", str_length, str));
- return false;
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
+ "unexpected IPv4-part.", (int) str_length, str));
+ return true;
}
- if (!str_to_ipv4(group_start_ptr,
- str + str_length - group_start_ptr,
- (in_addr *) dst))
+ Inet4_null tmp(group_start_ptr, (size_t) (str_end - group_start_ptr),
+ &my_charset_latin1);
+ if (tmp.is_null())
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
- "invalid IPv4-part.", str_length, str));
- return false;
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
+ "invalid IPv4-part.", (int) str_length, str));
+ return true;
}
+ tmp.to_binary(dst, IN_ADDR_SIZE);
dst += IN_ADDR_SIZE;
chars_in_group= 0;
@@ -452,18 +614,18 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
if (!hdp)
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
"invalid character at pos %d.",
- str_length, str, (int) (p - str)));
- return false;
+ (int) str_length, str, (int) (p - str)));
+ return true;
}
if (chars_in_group >= 4)
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
"too many digits in group.",
- str_length, str));
- return false;
+ (int) str_length, str));
+ return true;
}
group_value <<= 4;
@@ -479,9 +641,9 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
{
if (dst + 2 > ipv6_bytes_end)
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
- "too many groups (2).", str_length, str));
- return false;
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
+ "too many groups (2).", (int) str_length, str));
+ return true;
}
dst[0]= (unsigned char) (group_value >> 8) & 0xff;
@@ -493,9 +655,9 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
{
if (dst == ipv6_bytes_end)
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
- "no room for a gap (::).", str_length, str));
- return false;
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
+ "no room for a gap (::).", (int) str_length, str));
+ return true;
}
int bytes_to_move= (int)(dst - gap_ptr);
@@ -511,50 +673,49 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
if (dst < ipv6_bytes_end)
{
- DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
- "too few groups.", str_length, str));
- return false;
+ DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
+ "too few groups.", (int) str_length, str));
+ return true;
}
- return true;
+ return false;
}
-///////////////////////////////////////////////////////////////////////////
/**
Converts IPv4-binary-address to a string. This function is a portable
alternative to inet_ntop(AF_INET).
@param[in] ipv4 IPv4-address data (byte array)
- @param[out] str A buffer to store string representation of IPv4-address.
- It must be at least of INET_ADDRSTRLEN.
+ @param[out] dst A buffer to store string representation of IPv4-address.
+ @param[in] dstsize Number of bytes avaiable in "dst"
@note The problem with inet_ntop() is that it is available starting from
Windows Vista, but the minimum supported version is Windows 2000.
*/
-static void ipv4_to_str(const in_addr *ipv4, char *str)
+size_t Inet4::to_string(char *dst, size_t dstsize) const
{
- const unsigned char *ipv4_bytes= (const unsigned char *) ipv4;
-
- sprintf(str, "%d.%d.%d.%d",
- ipv4_bytes[0], ipv4_bytes[1], ipv4_bytes[2], ipv4_bytes[3]);
+ return (size_t) my_snprintf(dst, dstsize, "%d.%d.%d.%d",
+ (uchar) m_buffer[0], (uchar) m_buffer[1],
+ (uchar) m_buffer[2], (uchar) m_buffer[3]);
}
-///////////////////////////////////////////////////////////////////////////
+
/**
Converts IPv6-binary-address to a string. This function is a portable
alternative to inet_ntop(AF_INET6).
@param[in] ipv6 IPv6-address data (byte array)
- @param[out] str A buffer to store string representation of IPv6-address.
+ @param[out] dst A buffer to store string representation of IPv6-address.
It must be at least of INET6_ADDRSTRLEN.
+ @param[in] dstsize Number of bytes available dst.
@note The problem with inet_ntop() is that it is available starting from
Windows Vista, but out the minimum supported version is Windows 2000.
*/
-static void ipv6_to_str(const in6_addr *ipv6, char *str)
+size_t Inet6::to_string(char *dst, size_t dstsize) const
{
struct Region
{
@@ -562,6 +723,8 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
int length;
};
+ const char *ipv6= m_buffer;
+ char *dstend= dst + dstsize;
const unsigned char *ipv6_bytes= (const unsigned char *) ipv6;
// 1. Translate IPv6-address bytes to words.
@@ -570,7 +733,8 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
uint16 ipv6_words[IN6_ADDR_NUM_WORDS];
- for (int i= 0; i < IN6_ADDR_NUM_WORDS; ++i)
+ DBUG_ASSERT(dstsize > 0); // Need a space at least for the trailing '\0'
+ for (size_t i= 0; i < IN6_ADDR_NUM_WORDS; ++i)
ipv6_words[i]= (ipv6_bytes[2 * i] << 8) + ipv6_bytes[2 * i + 1];
// 2. Find "the gap" -- longest sequence of zeros in IPv6-address.
@@ -580,7 +744,7 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
{
Region rg= { -1, -1 };
- for (int i = 0; i < IN6_ADDR_NUM_WORDS; ++i)
+ for (size_t i= 0; i < IN6_ADDR_NUM_WORDS; ++i)
{
if (ipv6_words[i] != 0)
{
@@ -601,7 +765,7 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
}
else
{
- rg.pos= i;
+ rg.pos= (int) i;
rg.length= 1;
}
}
@@ -616,10 +780,14 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
// 3. Convert binary data to string.
- char *p= str;
+ char *p= dst;
- for (int i = 0; i < IN6_ADDR_NUM_WORDS; ++i)
+ for (int i= 0; i < (int) IN6_ADDR_NUM_WORDS; ++i)
{
+ DBUG_ASSERT(dstend >= p);
+ size_t dstsize_available= dstend - p;
+ if (dstsize_available < 5)
+ break;
if (i == gap.pos)
{
// We're at the gap position. We should put trailing ':' and jump to
@@ -646,10 +814,11 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
{
// The data represents either IPv4-compatible or IPv4-mapped address.
// The IPv6-part (zeros or zeros + ffff) has been already put into
- // the string (str). Now it's time to dump IPv4-part.
+ // the string (dst). Now it's time to dump IPv4-part.
- ipv4_to_str((const in_addr *) (ipv6_bytes + 12), p);
- return;
+ return (size_t) (p - dst) +
+ Inet4_null((const char *) (ipv6_bytes + 12), 4).
+ to_string(p, dstsize_available);
}
else
{
@@ -660,7 +829,7 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
p += sprintf(p, "%x", ipv6_words[i]);
- if (i != IN6_ADDR_NUM_WORDS - 1)
+ if (i + 1 != IN6_ADDR_NUM_WORDS)
{
*p= ':';
++p;
@@ -669,6 +838,7 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
}
*p= 0;
+ return (size_t) (p - dst);
}
///////////////////////////////////////////////////////////////////////////
@@ -676,161 +846,122 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
/**
Converts IP-address-string to IP-address-data.
- @param arg IP-address-string.
- @param [out] buffer Buffer to store IP-address-data.
+ ipv4-string -> varbinary(4)
+ ipv6-string -> varbinary(16)
@return Completion status.
- @retval false Given string does not represent an IP-address.
- @retval true The string has been converted successfully.
+ @retval NULL Given string does not represent an IP-address.
+ @retval !NULL The string has been converted successfully.
*/
-bool Item_func_inet6_aton::calc_value(const String *arg, String *buffer)
+String *Item_func_inet6_aton::val_str(String *buffer)
{
- // ipv4-string -> varbinary(4)
- // ipv6-string -> varbinary(16)
+ DBUG_ASSERT(fixed);
- in_addr ipv4_address;
- in6_addr ipv6_address;
+ Ascii_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
+ if ((null_value= tmp.is_null()))
+ return NULL;
- if (str_to_ipv4(arg->ptr(), arg->length(), &ipv4_address))
+ Inet4_null ipv4(*tmp.string());
+ if (!ipv4.is_null())
{
- buffer->length(0);
- buffer->append((char *) &ipv4_address, sizeof (in_addr), &my_charset_bin);
-
- return true;
+ ipv4.to_binary(buffer);
+ return buffer;
}
- if (str_to_ipv6(arg->ptr(), arg->length(), &ipv6_address))
+ Inet6_null ipv6(*tmp.string());
+ if (!ipv6.is_null())
{
- buffer->length(0);
- buffer->append((char *) &ipv6_address, sizeof (in6_addr), &my_charset_bin);
-
- return true;
+ ipv6.to_binary(buffer);
+ return buffer;
}
- return false;
+ null_value= true;
+ return NULL;
}
-///////////////////////////////////////////////////////////////////////////
/**
Converts IP-address-data to IP-address-string.
-
- @param arg IP-address-data.
- @param [out] buffer Buffer to store IP-address-string.
-
- @return Completion status.
- @retval false The argument does not correspond to IP-address.
- @retval true The string has been converted successfully.
*/
-bool Item_func_inet6_ntoa::calc_value(const String *arg, String *buffer)
+String *Item_func_inet6_ntoa::val_str_ascii(String *buffer)
{
- if (arg->charset() != &my_charset_bin)
- return false;
+ DBUG_ASSERT(fixed);
- if ((int) arg->length() == IN_ADDR_SIZE)
+ // Binary string argument expected
+ if (unlikely(args[0]->result_type() != STRING_RESULT ||
+ args[0]->collation.collation != &my_charset_bin))
{
- char str[INET_ADDRSTRLEN];
-
- ipv4_to_str((const in_addr *) arg->ptr(), str);
-
- buffer->length(0);
- buffer->append(str, (uint32) strlen(str), &my_charset_latin1);
-
- return true;
+ null_value= true;
+ return NULL;
}
- else if ((int) arg->length() == IN6_ADDR_SIZE)
- {
- char str[INET6_ADDRSTRLEN];
- ipv6_to_str((const in6_addr *) arg->ptr(), str);
+ String_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
+ if ((null_value= tmp.is_null()))
+ return NULL;
- buffer->length(0);
- buffer->append(str, (uint32) strlen(str), &my_charset_latin1);
+ Inet4_null ipv4(static_cast<const Binary_string&>(*tmp.string()));
+ if (!ipv4.is_null())
+ {
+ ipv4.to_string(buffer);
+ return buffer;
+ }
- return true;
+ Inet6_null ipv6(static_cast<const Binary_string&>(*tmp.string()));
+ if (!ipv6.is_null())
+ {
+ ipv6.to_string(buffer);
+ return buffer;
}
- DBUG_PRINT("info",
- ("INET6_NTOA(): varbinary(4) or varbinary(16) expected."));
- return false;
+ DBUG_PRINT("info", ("INET6_NTOA(): varbinary(4) or varbinary(16) expected."));
+ null_value= true;
+ return NULL;
}
-///////////////////////////////////////////////////////////////////////////
/**
Checks if the passed string represents an IPv4-address.
-
- @param arg The string to check.
-
- @return Check status.
- @retval false The passed string does not represent an IPv4-address.
- @retval true The passed string represents an IPv4-address.
*/
-bool Item_func_is_ipv4::calc_value(const String *arg)
+longlong Item_func_is_ipv4::val_int()
{
- in_addr ipv4_address;
-
- return str_to_ipv4(arg->ptr(), arg->length(), &ipv4_address);
+ DBUG_ASSERT(fixed);
+ String_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
+ return !tmp.is_null() && !Inet4_null(*tmp.string()).is_null();
}
-///////////////////////////////////////////////////////////////////////////
/**
Checks if the passed string represents an IPv6-address.
-
- @param arg The string to check.
-
- @return Check status.
- @retval false The passed string does not represent an IPv6-address.
- @retval true The passed string represents an IPv6-address.
*/
-bool Item_func_is_ipv6::calc_value(const String *arg)
+longlong Item_func_is_ipv6::val_int()
{
- in6_addr ipv6_address;
-
- return str_to_ipv6(arg->ptr(), arg->length(), &ipv6_address);
+ DBUG_ASSERT(fixed);
+ String_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
+ return !tmp.is_null() && !Inet6_null(*tmp.string()).is_null();
}
-///////////////////////////////////////////////////////////////////////////
/**
Checks if the passed IPv6-address is an IPv4-compat IPv6-address.
-
- @param arg The IPv6-address to check.
-
- @return Check status.
- @retval false The passed IPv6-address is not an IPv4-compatible IPv6-address.
- @retval true The passed IPv6-address is an IPv4-compatible IPv6-address.
*/
-bool Item_func_is_ipv4_compat::calc_value(const String *arg)
+longlong Item_func_is_ipv4_compat::val_int()
{
- if ((int) arg->length() != IN6_ADDR_SIZE || arg->charset() != &my_charset_bin)
- return false;
-
- return IN6_IS_ADDR_V4COMPAT((struct in6_addr *) arg->ptr());
+ Inet6_null ip6(args[0]);
+ return !ip6.is_null() && ip6.is_v4compat();
}
-///////////////////////////////////////////////////////////////////////////
/**
Checks if the passed IPv6-address is an IPv4-mapped IPv6-address.
-
- @param arg The IPv6-address to check.
-
- @return Check status.
- @retval false The passed IPv6-address is not an IPv4-mapped IPv6-address.
- @retval true The passed IPv6-address is an IPv4-mapped IPv6-address.
*/
-bool Item_func_is_ipv4_mapped::calc_value(const String *arg)
+longlong Item_func_is_ipv4_mapped::val_int()
{
- if ((int) arg->length() != IN6_ADDR_SIZE || arg->charset() != &my_charset_bin)
- return false;
-
- return IN6_IS_ADDR_V4MAPPED((struct in6_addr *) arg->ptr());
+ Inet6_null ip6(args[0]);
+ return !ip6.is_null() && ip6.is_v4mapped();
}
diff --git a/sql/item_inetfunc.h b/sql/item_inetfunc.h
index 9ab46e88053..8cfb4cd278c 100644
--- a/sql/item_inetfunc.h
+++ b/sql/item_inetfunc.h
@@ -81,33 +81,7 @@ public:
{
null_value= false;
}
-
-public:
- virtual longlong val_int();
bool need_parentheses_in_default() { return false; }
-
-protected:
- virtual bool calc_value(const String *arg) = 0;
-};
-
-
-/*************************************************************************
- Item_func_inet_str_base implements common code for INET6/IP-related
- functions returning string value.
-*************************************************************************/
-
-class Item_func_inet_str_base : public Item_str_ascii_func
-{
-public:
- inline Item_func_inet_str_base(THD *thd, Item *arg):
- Item_str_ascii_func(thd, arg)
- { }
-
-public:
- virtual String *val_str_ascii(String *buffer);
-
-protected:
- virtual bool calc_value(const String *arg, String *buffer) = 0;
};
@@ -115,11 +89,11 @@ protected:
Item_func_inet6_aton implements INET6_ATON() SQL-function.
*************************************************************************/
-class Item_func_inet6_aton : public Item_func_inet_str_base
+class Item_func_inet6_aton : public Item_str_func
{
public:
inline Item_func_inet6_aton(THD *thd, Item *ip_addr):
- Item_func_inet_str_base(thd, ip_addr)
+ Item_str_func(thd, ip_addr)
{ }
public:
@@ -136,8 +110,7 @@ public:
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_inet6_aton>(thd, this); }
-protected:
- virtual bool calc_value(const String *arg, String *buffer);
+ String *val_str(String *to);
};
@@ -145,11 +118,11 @@ protected:
Item_func_inet6_ntoa implements INET6_NTOA() SQL-function.
*************************************************************************/
-class Item_func_inet6_ntoa : public Item_func_inet_str_base
+class Item_func_inet6_ntoa : public Item_str_ascii_func
{
public:
inline Item_func_inet6_ntoa(THD *thd, Item *ip_addr):
- Item_func_inet_str_base(thd, ip_addr)
+ Item_str_ascii_func(thd, ip_addr)
{ }
public:
@@ -168,11 +141,9 @@ public:
maybe_null= 1;
return FALSE;
}
+ String *val_str_ascii(String *to);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_inet6_ntoa>(thd, this); }
-
-protected:
- virtual bool calc_value(const String *arg, String *buffer);
};
@@ -193,8 +164,7 @@ public:
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_is_ipv4>(thd, this); }
-protected:
- virtual bool calc_value(const String *arg);
+ longlong val_int();
};
@@ -209,14 +179,12 @@ public:
Item_func_inet_bool_base(thd, ip_addr)
{ }
-public:
virtual const char *func_name() const
{ return "is_ipv6"; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_is_ipv6>(thd, this); }
-protected:
- virtual bool calc_value(const String *arg);
+ longlong val_int();
};
@@ -230,15 +198,11 @@ public:
inline Item_func_is_ipv4_compat(THD *thd, Item *ip_addr):
Item_func_inet_bool_base(thd, ip_addr)
{ }
-
-public:
virtual const char *func_name() const
{ return "is_ipv4_compat"; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_is_ipv4_compat>(thd, this); }
-
-protected:
- virtual bool calc_value(const String *arg);
+ longlong val_int();
};
@@ -252,15 +216,11 @@ public:
inline Item_func_is_ipv4_mapped(THD *thd, Item *ip_addr):
Item_func_inet_bool_base(thd, ip_addr)
{ }
-
-public:
virtual const char *func_name() const
{ return "is_ipv4_mapped"; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_is_ipv4_mapped>(thd, this); }
-
-protected:
- virtual bool calc_value(const String *arg);
+ longlong val_int();
};
#endif // ITEM_INETFUNC_INCLUDED
diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc
index 082d7935360..4b4a94c814e 100644
--- a/sql/item_jsonfunc.cc
+++ b/sql/item_jsonfunc.cc
@@ -374,17 +374,11 @@ static int path_setup_nwc(json_path_t *p, CHARSET_INFO *i_cs,
longlong Item_func_json_valid::val_int()
{
String *js= args[0]->val_json(&tmp_value);
- json_engine_t je;
if ((null_value= args[0]->null_value))
return 0;
- json_scan_start(&je, js->charset(), (const uchar *) js->ptr(),
- (const uchar *) js->ptr()+js->length());
-
- while (json_scan_next(&je) == 0) {}
-
- return je.s.error == 0;
+ return json_valid(js->ptr(), js->length(), js->charset());
}
@@ -624,8 +618,6 @@ String *Item_func_json_unquote::read_json(json_engine_t *je)
json_scan_start(je, js->charset(),(const uchar *) js->ptr(),
(const uchar *) js->ptr() + js->length());
- je->value_type= (enum json_value_types) -1; /* To report errors right. */
-
if (json_read_value(je))
goto error;
@@ -988,7 +980,8 @@ my_decimal *Item_func_json_extract::val_decimal(my_decimal *to)
case JSON_VALUE_ARRAY:
case JSON_VALUE_FALSE:
case JSON_VALUE_NULL:
- break;
+ case JSON_VALUE_UNINITALIZED:
+ break;
};
}
int2my_decimal(E_DEC_FATAL_ERROR, 0, false/*unsigned_flag*/, to);
@@ -1457,7 +1450,7 @@ null_return:
static int append_json_value(String *str, Item *item, String *tmp_val)
{
- if (item->is_bool_type())
+ if (item->type_handler()->is_bool_type())
{
longlong v_int= item->val_int();
const char *t_f;
@@ -2264,7 +2257,7 @@ static int do_merge_patch(String *str, json_engine_t *je1, json_engine_t *je2,
json_string_t key_name;
size_t sav_len;
bool mrg_empty;
-
+
*empty_result= FALSE;
json_string_set_cs(&key_name, je1->s.cs);
diff --git a/sql/item_row.cc b/sql/item_row.cc
index 04868842f11..a363f9b5576 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -61,7 +61,7 @@ bool Item_row::fix_fields(THD *thd, Item **ref)
}
}
maybe_null|= item->maybe_null;
- with_sum_func= with_sum_func || item->with_sum_func;
+ join_with_sum_func(item);
with_window_func = with_window_func || item->with_window_func;
with_field= with_field || item->with_field;
m_with_subquery|= item->with_subquery();
@@ -92,7 +92,7 @@ void Item_row::cleanup()
{
DBUG_ENTER("Item_row::cleanup");
- Item::cleanup();
+ Item_fixed_hybrid::cleanup();
/* Reset to the original values */
used_tables_and_const_cache_init();
with_null= 0;
diff --git a/sql/item_row.h b/sql/item_row.h
index 0d6a6db2e0e..ea5a0f21d8b 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -33,10 +33,11 @@
Item which stores (x,y,...) and ROW(x,y,...).
Note that this can be recursive: ((x,y),(z,t)) is a ROW of ROWs.
*/
-class Item_row: public Item,
+class Item_row: public Item_fixed_hybrid,
private Item_args,
private Used_tables_and_const_cache,
- private With_subquery_cache
+ private With_subquery_cache,
+ private With_sum_func_cache
{
table_map not_null_tables_cache;
/**
@@ -45,17 +46,25 @@ class Item_row: public Item,
*/
bool with_null;
public:
- Item_row(THD *thd, List<Item> &list):
- Item(thd), Item_args(thd, list), not_null_tables_cache(0), with_null(0)
+ Item_row(THD *thd, List<Item> &list)
+ :Item_fixed_hybrid(thd), Item_args(thd, list),
+ not_null_tables_cache(0), with_null(0)
{ }
- Item_row(THD *thd, Item_row *row):
- Item(thd), Item_args(thd, static_cast<Item_args*>(row)), Used_tables_and_const_cache(),
+ Item_row(THD *thd, Item_row *row)
+ :Item_fixed_hybrid(thd), Item_args(thd, static_cast<Item_args*>(row)),
+ Used_tables_and_const_cache(),
+ With_sum_func_cache(*row),
not_null_tables_cache(0), with_null(0)
{ }
bool with_subquery() const { DBUG_ASSERT(fixed); return m_with_subquery; }
enum Type type() const { return ROW_ITEM; };
const Type_handler *type_handler() const { return &type_handler_row; }
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ return NULL; // Check with Vicentiu why it's called for Item_row
+ }
void illegal_method_call(const char *);
bool is_null() { return null_value; }
void make_send_field(THD *thd, Send_field *)
@@ -82,7 +91,7 @@ public:
illegal_method_call((const char*)"val_decimal");
return 0;
};
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
illegal_method_call((const char*)"get_date");
return true;
@@ -92,6 +101,8 @@ public:
void cleanup();
void split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
List<Item> &fields, uint flags);
+ bool with_sum_func() const { return m_with_sum_func; }
+ With_sum_func_cache* get_with_sum_func_cache() { return this; }
table_map used_tables() const { return used_tables_cache; };
bool const_item() const { return const_item_cache; };
void update_used_tables()
@@ -134,6 +145,11 @@ public:
return Item_args::excl_dep_on_grouping_fields(sel);
}
+ bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred)
+ {
+ return Item_args::excl_dep_on_in_subq_left_part(subq_pred);
+ }
+
bool check_vcol_func_processor(void *arg) {return FALSE; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_row>(thd, this); }
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 5bdd3e10069..759b18c4657 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -276,7 +276,7 @@ String *Item_func_sha2::val_str_ascii(String *str)
Since we're subverting the usual String methods, we must make sure that
the destination has space for the bytes we're about to write.
*/
- str->realloc((uint) digest_length*2 + 1); /* Each byte as two nybbles */
+ str->alloc((uint) digest_length*2 + 1); /* Each byte as two nybbles */
/* Convert the large number to a string-hex representation. */
array_to_hex((char *) str->ptr(), digest_buf, (uint)digest_length);
@@ -783,7 +783,7 @@ String *Item_func_des_encrypt::val_str(String *str)
tail= 8 - (res_length % 8); // 1..8 marking extra length
res_length+=tail;
- if (tmp_arg.realloc(res_length))
+ if (tmp_arg.alloc(res_length))
goto error;
tmp_arg.length(0);
tmp_arg.append(res->ptr(), res->length());
@@ -791,7 +791,6 @@ String *Item_func_des_encrypt::val_str(String *str)
if (tmp_arg.append(append_str, tail) || str->alloc(res_length+1))
goto error;
tmp_arg[res_length-1]=tail; // save extra length
- str->realloc(res_length+1);
str->length(res_length+1);
str->set_charset(&my_charset_bin);
(*str)[0]=(char) (128 | key_number);
@@ -1038,7 +1037,7 @@ String *Item_func_concat_ws::val_str(String *str)
{
uint new_len = MY_MAX(tmp_value.alloced_length() * 2, concat_len);
- if (tmp_value.realloc(new_len))
+ if (tmp_value.alloc(new_len))
goto null;
}
}
@@ -1093,8 +1092,7 @@ String *Item_func_reverse::val_str(String *str)
/* An empty string is a special case as the string pointer may be null */
if (!res->length())
return make_empty_result();
- if (str->alloced_length() < res->length() &&
- str->realloc(res->length()))
+ if (str->alloc(res->length()))
{
null_value= 1;
return 0;
@@ -1516,17 +1514,18 @@ String *Item_func_insert::val_str(String *str)
null_value=0;
res=args[0]->val_str(str);
res2=args[3]->val_str(&tmp_value);
- start= args[1]->val_int() - 1;
+ start= args[1]->val_int();
length= args[2]->val_int();
if (args[0]->null_value || args[1]->null_value || args[2]->null_value ||
args[3]->null_value)
goto null; /* purecov: inspected */
- if ((start < 0) || (start > res->length()))
+ if ((start <= 0) || (start > res->length()))
return res; // Wrong param; skip insert
if ((length < 0) || (length > res->length()))
length= res->length();
+ start--;
/*
There is one exception not handled (intentionaly) by the character set
@@ -2663,15 +2662,45 @@ String *Item_func_soundex::val_str(String *str)
This should be 'internationalized' sometimes.
*/
-const int FORMAT_MAX_DECIMALS= 30;
+/*
+ The maximum supported decimal scale:
+ 38 - starting from 10.2.1
+ 30 - before 10.2.1
+*/
+const int FORMAT_MAX_DECIMALS= 38;
bool Item_func_format::fix_length_and_dec()
{
- uint32 char_length= args[0]->max_char_length();
- uint32 max_sep_count= (char_length / 3) + (decimals ? 1 : 0) + /*sign*/1;
+ uint32 char_length= args[0]->type_handler()->Item_decimal_notation_int_digits(args[0]);
+ uint dec= FORMAT_MAX_DECIMALS;
+ /*
+ Format can require one more integer digit if rounding happens:
+ FORMAT(9.9,0) -> '10'
+ Set need_extra_digit_for_rounding to true by default
+ if args[0] has some decimals: if args[1] is not
+ a constant, then format can potentially reduce
+ the number of decimals and round to the next integer.
+ */
+ bool need_extra_digit_for_rounding= args[0]->decimals > 0;
+ if (args[1]->const_item() && !args[1]->is_expensive())
+ {
+ Longlong_hybrid tmp= args[1]->to_longlong_hybrid();
+ if (!args[1]->null_value)
+ {
+ dec= tmp.to_uint(FORMAT_MAX_DECIMALS);
+ need_extra_digit_for_rounding= (dec < args[0]->decimals);
+ }
+ }
+ /*
+ In case of a data type with zero integer digits, e.g. DECIMAL(4,4),
+ we'll print at least one integer digit.
+ */
+ if (need_extra_digit_for_rounding || !char_length)
+ char_length++;
+ uint32 max_sep_count= (char_length / 3) + (dec ? 1 : 0) + /*sign*/1;
collation.set(default_charset());
- fix_char_length(char_length + max_sep_count + decimals);
+ fix_char_length(char_length + max_sep_count + dec);
if (arg_count == 3)
locale= args[2]->basic_const_item() ? args[2]->locale_from_val_str() : NULL;
else
@@ -2712,12 +2741,10 @@ String *Item_func_format::val_str_ascii(String *str)
if (args[0]->result_type() == DECIMAL_RESULT ||
args[0]->result_type() == INT_RESULT)
{
- my_decimal dec_val, rnd_dec, *res;
- res= args[0]->val_decimal(&dec_val);
- if ((null_value=args[0]->null_value))
+ VDec res(args[0]);
+ if ((null_value= res.is_null()))
return 0; /* purecov: inspected */
- my_decimal_round(E_DEC_FATAL_ERROR, res, dec, false, &rnd_dec);
- my_decimal2string(E_DEC_FATAL_ERROR, &rnd_dec, 0, 0, 0, str);
+ res.to_string_round(str, dec);
str_length= str->length();
}
else
@@ -2726,7 +2753,7 @@ String *Item_func_format::val_str_ascii(String *str)
if ((null_value=args[0]->null_value))
return 0; /* purecov: inspected */
nr= my_double_round(nr, (longlong) dec, FALSE, FALSE);
- str->set_real(nr, dec, &my_charset_numeric);
+ str->set_fcvt(nr, dec);
if (!std::isfinite(nr))
return str;
str_length=str->length();
@@ -3769,13 +3796,12 @@ String *Item_func_unhex::val_str(String *str)
}
for (end=res->ptr()+res->length(); from < end ; from+=2, to++)
{
- int hex_char;
- *to= (hex_char= hexchar_to_int(from[0])) << 4;
- if ((null_value= (hex_char == -1)))
- return 0;
- *to|= hex_char= hexchar_to_int(from[1]);
- if ((null_value= (hex_char == -1)))
+ int hex_char1, hex_char2;
+ hex_char1= hexchar_to_int(from[0]);
+ hex_char2= hexchar_to_int(from[1]);
+ if ((null_value= (hex_char1 == -1 || hex_char2 == -1)))
return 0;
+ *to= (char) ((hex_char1 << 4) | hex_char2);
}
return str;
}
@@ -4236,7 +4262,7 @@ String *Item_func_compress::val_str(String *str)
// Check new_size overflow: new_size <= res->length()
if (((uint32) (new_size+5) <= res->length()) ||
- str->realloc((uint32) new_size + 4 + 1))
+ str->alloc((uint32) new_size + 4 + 1))
{
null_value= 1;
return 0;
@@ -4308,7 +4334,7 @@ String *Item_func_uncompress::val_str(String *str)
max_allowed_packet));
goto err;
}
- if (str->realloc((uint32)new_size))
+ if (str->alloc((uint32)new_size))
goto err;
if ((err= uncompress((Byte*)str->ptr(), &new_size,
@@ -4337,7 +4363,7 @@ String *Item_func_uuid::val_str(String *str)
DBUG_ASSERT(fixed == 1);
uchar guid[MY_UUID_SIZE];
- str->realloc(MY_UUID_STRING_LENGTH+1);
+ str->alloc(MY_UUID_STRING_LENGTH+1);
str->length(MY_UUID_STRING_LENGTH);
str->set_charset(system_charset_info);
my_uuid(guid);
@@ -4603,11 +4629,11 @@ bool Item_func_dyncol_create::prepare_arguments(THD *thd, bool force_names_arg)
break;
case DYN_COL_DATETIME:
case DYN_COL_DATE:
- args[valpos]->get_date(&vals[i].x.time_value,
- sql_mode_for_dates(thd));
+ args[valpos]->get_date(thd, &vals[i].x.time_value,
+ Datetime::Options(thd));
break;
case DYN_COL_TIME:
- args[valpos]->get_time(&vals[i].x.time_value);
+ args[valpos]->get_time(thd, &vals[i].x.time_value);
break;
default:
DBUG_ASSERT(0);
@@ -5173,7 +5199,7 @@ null:
}
-bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_dyncol_get::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DYNAMIC_COLUMN_VALUE val;
char buff[STRING_BUFFER_USUAL_SIZE];
@@ -5194,10 +5220,8 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
if (signed_value || val.x.ulong_value <= LONGLONG_MAX)
{
longlong llval = (longlong)val.x.ulong_value;
- bool neg = llval < 0;
- if (int_to_datetime_with_warn(neg, (ulonglong)(neg ? -llval :
- llval),
- ltime, fuzzy_date, 0, 0 /* TODO */))
+ if (int_to_datetime_with_warn(thd, Longlong_hybrid(llval, !signed_value),
+ ltime, fuzzydate, 0, 0 /* TODO */))
goto null;
return 0;
}
@@ -5205,20 +5229,20 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
val.x.double_value= static_cast<double>(ULONGLONG_MAX);
/* fall through */
case DYN_COL_DOUBLE:
- if (double_to_datetime_with_warn(val.x.double_value, ltime, fuzzy_date,
+ if (double_to_datetime_with_warn(thd, val.x.double_value, ltime, fuzzydate,
0, 0 /* TODO */))
goto null;
return 0;
case DYN_COL_DECIMAL:
- if (decimal_to_datetime_with_warn((my_decimal*)&val.x.decimal.value, ltime,
- fuzzy_date, 0, 0 /* TODO */))
+ if (decimal_to_datetime_with_warn(thd, (my_decimal*)&val.x.decimal.value,
+ ltime, fuzzydate, 0, 0 /* TODO */))
goto null;
return 0;
case DYN_COL_STRING:
- if (str_to_datetime_with_warn(&my_charset_numeric,
+ if (str_to_datetime_with_warn(thd, &my_charset_numeric,
val.x.string.value.str,
val.x.string.value.length,
- ltime, fuzzy_date))
+ ltime, fuzzydate))
goto null;
return 0;
case DYN_COL_DATETIME:
@@ -5326,3 +5350,106 @@ String *Item_temptable_rowid::val_str(String *str)
str_value.set((char*)(table->file->ref), max_length, &my_charset_bin);
return &str_value;
}
+#ifdef WITH_WSREP
+
+#include "wsrep_mysqld.h"
+
+String *Item_func_wsrep_last_written_gtid::val_str_ascii(String *str)
+{
+ wsrep::gtid gtid= current_thd->wsrep_cs().last_written_gtid();
+ if (gtid_str.alloc(wsrep::gtid_c_str_len()))
+ {
+ my_error(ER_OUTOFMEMORY, wsrep::gtid_c_str_len());
+ null_value= true;
+ return NULL;
+ }
+
+ ssize_t gtid_len= gtid_print_to_c_str(gtid, (char*) gtid_str.ptr(),
+ wsrep::gtid_c_str_len());
+ if (gtid_len < 0)
+ {
+ my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), func_name(),
+ "wsrep_gtid_print failed");
+ null_value= true;
+ return NULL;
+ }
+ gtid_str.length(gtid_len);
+ return &gtid_str;
+}
+
+String *Item_func_wsrep_last_seen_gtid::val_str_ascii(String *str)
+{
+ wsrep::gtid gtid= wsrep::gtid::undefined();
+ if (Wsrep_server_state::instance().is_provider_loaded())
+ {
+ /* TODO: Should call Wsrep_server_state.instance().last_committed_gtid()
+ instead. */
+ gtid= Wsrep_server_state::instance().provider().last_committed_gtid();
+ }
+ if (gtid_str.alloc(wsrep::gtid_c_str_len()))
+ {
+ my_error(ER_OUTOFMEMORY, wsrep::gtid_c_str_len());
+ null_value= true;
+ return NULL;
+ }
+ ssize_t gtid_len= wsrep::gtid_print_to_c_str(gtid, (char*) gtid_str.ptr(),
+ wsrep::gtid_c_str_len());
+ if (gtid_len < 0)
+ {
+ my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), func_name(),
+ "wsrep_gtid_print failed");
+ null_value= true;
+ return NULL;
+ }
+ gtid_str.length(gtid_len);
+ return &gtid_str;
+}
+
+longlong Item_func_wsrep_sync_wait_upto::val_int()
+{
+ int timeout= -1;
+ String* gtid_str= args[0]->val_str(&value);
+ if (gtid_str == NULL)
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
+ return 0LL;
+ }
+
+ if (arg_count == 2)
+ {
+ timeout= args[1]->val_int();
+ }
+
+ wsrep_gtid_t gtid;
+ int gtid_len= wsrep_gtid_scan(gtid_str->ptr(), gtid_str->length(), &gtid);
+ if (gtid_len < 0)
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
+ return 0LL;
+ }
+
+ if (gtid.seqno == WSREP_SEQNO_UNDEFINED &&
+ wsrep_uuid_compare(&gtid.uuid, &WSREP_UUID_UNDEFINED) == 0)
+ {
+ return 1LL;
+ }
+
+ enum wsrep::provider::status status=
+ wsrep_sync_wait_upto(current_thd, &gtid, timeout);
+
+ if (status)
+ {
+ int err;
+ switch (status) {
+ case wsrep::provider::error_transaction_missing:
+ err= ER_WRONG_ARGUMENTS;
+ break;
+ default:
+ err= ER_LOCK_WAIT_TIMEOUT;
+ }
+ my_error(err, MYF(0), func_name());
+ return 0LL;
+ }
+ return 1LL;
+}
+#endif /* WITH_WSREP */
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index c0505773ba8..826a978805e 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -42,8 +42,13 @@ protected:
we don't want to free and potentially have to reallocate the buffer
for each call.
*/
- str_value.length(0);
- str_value.set_charset(collation.collation);
+ if (!str_value.is_alloced())
+ str_value.set("", 0, collation.collation); /* Avoid null ptrs */
+ else
+ {
+ str_value.length(0); /* Reuse allocated area */
+ str_value.set_charset(collation.collation);
+ }
return &str_value;
}
public:
@@ -62,16 +67,11 @@ public:
longlong val_int();
double val_real();
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date_from_string(ltime, fuzzydate); }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ { return get_date_from_string(thd, ltime, fuzzydate); }
const Type_handler *type_handler() const { return string_type_handler(); }
void left_right_max_length();
bool fix_fields(THD *thd, Item **ref);
- void update_null_value()
- {
- StringBuffer<MAX_FIELD_WIDTH> tmp;
- (void) val_str(&tmp);
- }
};
@@ -1210,7 +1210,7 @@ public:
bool fix_length_and_dec()
{
collation.set(default_charset());
- max_length=64;
+ fix_char_length(64);
maybe_null= 1;
return FALSE;
}
@@ -1485,11 +1485,11 @@ public:
return NULL;
return res;
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
if (args[0]->result_type() == STRING_RESULT)
- return Item_str_func::get_date(ltime, fuzzydate);
- bool res= args[0]->get_date(ltime, fuzzydate);
+ return Item_str_func::get_date(thd, ltime, fuzzydate);
+ bool res= args[0]->get_date(thd, ltime, fuzzydate);
if ((null_value= args[0]->null_value))
return 1;
return res;
@@ -1784,7 +1784,7 @@ public:
double val_real();
my_decimal *val_decimal(my_decimal *);
bool get_dyn_value(THD *thd, DYNAMIC_COLUMN_VALUE *val, String *tmp);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
void print(String *str, enum_query_type query_type);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_dyncol_get>(thd, this); }
@@ -1824,5 +1824,56 @@ public:
Item *get_copy(THD *thd)
{ return get_item_copy<Item_temptable_rowid>(thd, this); }
};
+#ifdef WITH_WSREP
+
+#include "wsrep_api.h"
+
+class Item_func_wsrep_last_written_gtid: public Item_str_ascii_func
+{
+ String gtid_str;
+public:
+ Item_func_wsrep_last_written_gtid(THD *thd): Item_str_ascii_func(thd) {}
+ const char *func_name() const { return "wsrep_last_written_gtid"; }
+ String *val_str_ascii(String *);
+ bool fix_length_and_dec()
+ {
+ max_length= WSREP_GTID_STR_LEN;
+ maybe_null= true;
+ return FALSE;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_wsrep_last_written_gtid>(thd, this); }
+};
+
+class Item_func_wsrep_last_seen_gtid: public Item_str_ascii_func
+{
+ String gtid_str;
+public:
+ Item_func_wsrep_last_seen_gtid(THD *thd): Item_str_ascii_func(thd) {}
+ const char *func_name() const { return "wsrep_last_seen_gtid"; }
+ String *val_str_ascii(String *);
+ bool fix_length_and_dec()
+ {
+ max_length= WSREP_GTID_STR_LEN;
+ maybe_null= true;
+ return FALSE;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_wsrep_last_seen_gtid>(thd, this); }
+};
+
+class Item_func_wsrep_sync_wait_upto: public Item_int_func
+{
+ String value;
+public:
+ Item_func_wsrep_sync_wait_upto(THD *thd, Item *a): Item_int_func(thd, a) {}
+ Item_func_wsrep_sync_wait_upto(THD *thd, Item *a, Item* b): Item_int_func(thd, a, b) {}
+ const Type_handler *type_handler() const { return &type_handler_string; }
+ const char *func_name() const { return "wsrep_sync_wait_upto_gtid"; }
+ longlong val_int();
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_wsrep_sync_wait_upto>(thd, this); }
+};
+#endif /* WITH_WSREP */
#endif /* ITEM_STRFUNC_INCLUDED */
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index bedf85e3699..53a6847c52f 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -54,7 +54,8 @@ Item_subselect::Item_subselect(THD *thd_arg):
value_assigned(0), own_engine(0), thd(0), old_engine(0),
have_to_be_excluded(0),
inside_first_fix_fields(0), done_first_fix_fields(FALSE),
- expr_cache(0), forced_const(FALSE), substitution(0), engine(0), eliminated(FALSE),
+ expr_cache(0), forced_const(FALSE), expensive_fl(FALSE),
+ substitution(0), engine(0), eliminated(FALSE),
changed(0), is_correlated(FALSE), with_recursive_reference(0)
{
DBUG_ENTER("Item_subselect::Item_subselect");
@@ -85,6 +86,9 @@ void Item_subselect::init(st_select_lex *select_lex,
DBUG_ENTER("Item_subselect::init");
DBUG_PRINT("enter", ("select_lex: %p this: %p",
select_lex, this));
+
+ select_lex->parent_lex->relink_hack(select_lex);
+
unit= select_lex->master_unit();
if (unit->item)
@@ -118,18 +122,12 @@ void Item_subselect::init(st_select_lex *select_lex,
parsing_place= (outer_select->in_sum_expr ?
NO_MATTER :
outer_select->parsing_place);
- if (unit->is_unit_op() && unit->first_select()->next_select())
+ if (unit->is_unit_op() &&
+ (unit->first_select()->next_select() || unit->fake_select_lex))
engine= new subselect_union_engine(unit, result, this);
else
engine= new subselect_single_select_engine(select_lex, result, this);
}
- {
- SELECT_LEX *upper= unit->outer_select();
- if (upper->parsing_place == IN_HAVING)
- upper->subquery_in_having= 1;
- /* The subquery is an expression cache candidate */
- upper->expr_cache_may_be_used[upper->parsing_place]= TRUE;
- }
DBUG_PRINT("info", ("engine: %p", engine));
DBUG_VOID_RETURN;
}
@@ -220,7 +218,8 @@ Item_subselect::~Item_subselect()
if (own_engine)
delete engine;
else
- engine->cleanup();
+ if (engine) // can be empty in case of EOM
+ engine->cleanup();
engine= NULL;
DBUG_VOID_RETURN;
}
@@ -244,6 +243,14 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
DBUG_ASSERT(unit->thd == thd);
+ {
+ SELECT_LEX *upper= unit->outer_select();
+ if (upper->parsing_place == IN_HAVING)
+ upper->subquery_in_having= 1;
+ /* The subquery is an expression cache candidate */
+ upper->expr_cache_may_be_used[upper->parsing_place]= TRUE;
+ }
+
status_var_increment(thd_param->status_var.feature_subquery);
DBUG_ASSERT(fixed == 0);
@@ -604,6 +611,9 @@ bool Item_subselect::is_expensive()
double examined_rows= 0;
bool all_are_simple= true;
+ if (!expensive_fl && is_evaluated())
+ return false;
+
/* check extremely simple select */
if (!unit->first_select()->next_select()) // no union
{
@@ -614,7 +624,7 @@ bool Item_subselect::is_expensive()
SELECT_LEX *sl= unit->first_select();
JOIN *join = sl->join;
if (join && !join->tables_list && !sl->first_inner_unit())
- return false;
+ return (expensive_fl= false);
}
@@ -624,14 +634,14 @@ bool Item_subselect::is_expensive()
/* not optimized subquery */
if (!cur_join)
- return true;
+ return (expensive_fl= true);
/*
If the subquery is not optimised or in the process of optimization
it supposed to be expensive
*/
if (cur_join->optimization_state != JOIN::OPTIMIZATION_DONE)
- return true;
+ return (expensive_fl= true);
if (!cur_join->tables_list && !sl->first_inner_unit())
continue;
@@ -653,7 +663,7 @@ bool Item_subselect::is_expensive()
considered optimized if it has a join plan.
*/
if (!cur_join->join_tab)
- return true;
+ return (expensive_fl= true);
if (sl->first_inner_unit())
{
@@ -661,15 +671,15 @@ bool Item_subselect::is_expensive()
Subqueries that contain subqueries are considered expensive.
@todo: accumulate the cost of subqueries.
*/
- return true;
+ return (expensive_fl= true);
}
examined_rows+= cur_join->get_examined_rows();
}
// here we are sure that subquery is optimized so thd is set
- return !all_are_simple &&
- (examined_rows > thd->variables.expensive_subquery_limit);
+ return (expensive_fl= !all_are_simple &&
+ (examined_rows > thd->variables.expensive_subquery_limit));
}
@@ -1000,7 +1010,7 @@ bool Item_subselect::const_item() const
Item *Item_subselect::get_tmp_table_item(THD *thd_arg)
{
- if (!with_sum_func && !const_item())
+ if (!Item_subselect::with_sum_func() && !const_item())
return new (thd->mem_root) Item_temptable_field(thd_arg, result_field);
return copy_or_same(thd_arg);
}
@@ -1140,7 +1150,7 @@ void Item_maxmin_subselect::no_rows_in_result()
*/
if (parsing_place != SELECT_LIST || const_item())
return;
- value= (new (thd->mem_root) Item_null(thd))->get_cache(thd);
+ value= get_cache(thd);
null_value= 0;
was_values= 0;
make_const();
@@ -1158,7 +1168,7 @@ void Item_singlerow_subselect::no_rows_in_result()
*/
if (parsing_place != SELECT_LIST || const_item())
return;
- value= (new (thd->mem_root) Item_null(thd))->get_cache(thd);
+ value= get_cache(thd);
reset();
make_const();
}
@@ -1203,7 +1213,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
if (!select_lex->master_unit()->is_unit_op() &&
!select_lex->table_list.elements &&
select_lex->item_list.elements == 1 &&
- !select_lex->item_list.head()->with_sum_func &&
+ !select_lex->item_list.head()->with_sum_func() &&
/*
We can't change name of Item_field or Item_ref, because it will
prevent its correct resolving, but we should save name of
@@ -1428,6 +1438,24 @@ String *Item_singlerow_subselect::val_str(String *str)
}
+bool Item_singlerow_subselect::val_native(THD *thd, Native *to)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (forced_const)
+ return value->val_native(thd, to);
+ if (!exec() && !value->null_value)
+ {
+ null_value= false;
+ return value->val_native(thd, to);
+ }
+ else
+ {
+ reset();
+ return true;
+ }
+}
+
+
my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
@@ -1474,19 +1502,19 @@ bool Item_singlerow_subselect::val_bool()
}
-bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
+bool Item_singlerow_subselect::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed == 1);
if (forced_const)
{
- bool val= value->get_date(ltime, fuzzydate);
+ bool val= value->get_date(thd, ltime, fuzzydate);
null_value= value->null_value;
return val;
}
if (!exec() && !value->null_value)
{
null_value= FALSE;
- return value->get_date(ltime, fuzzydate);
+ return value->get_date(thd, ltime, fuzzydate);
}
else
{
@@ -1503,6 +1531,8 @@ Item_exists_subselect::Item_exists_subselect(THD *thd,
emb_on_expr_nest(NULL), optimizer(0), exists_transformed(0)
{
DBUG_ENTER("Item_exists_subselect::Item_exists_subselect");
+
+
init(select_lex, new (thd->mem_root) select_exists_subselect(thd, this));
max_columns= UINT_MAX;
null_value= FALSE; //can't be NULL
@@ -1546,6 +1576,7 @@ Item_in_subselect::Item_in_subselect(THD *thd, Item * left_exp,
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
DBUG_PRINT("info", ("in_strategy: %u", (uint)in_strategy));
+
left_expr_orig= left_expr= left_exp;
/* prepare to possible disassembling the item in convert_subq_to_sj() */
if (left_exp->type() == Item::ROW_ITEM)
@@ -2142,7 +2173,7 @@ bool Item_in_subselect::fix_having(Item *having, SELECT_LEX *select_lex)
{
bool fix_res= 0;
DBUG_ASSERT(thd);
- if (!having->fixed)
+ if (!having->is_fixed())
{
select_lex->having_fix_field= 1;
fix_res= having->fix_fields(thd, 0);
@@ -2482,9 +2513,9 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item *item_having_part2= 0;
for (uint i= 0; i < cols_num; i++)
{
- DBUG_ASSERT((left_expr->fixed &&
+ DBUG_ASSERT((left_expr->is_fixed() &&
- select_lex->ref_pointer_array[i]->fixed) ||
+ select_lex->ref_pointer_array[i]->is_fixed()) ||
(select_lex->ref_pointer_array[i]->type() == REF_ITEM &&
((Item_ref*)(select_lex->ref_pointer_array[i]))->ref_type() ==
Item_ref::OUTER_REF));
@@ -2553,8 +2584,8 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
for (uint i= 0; i < cols_num; i++)
{
Item *item, *item_isnull;
- DBUG_ASSERT((left_expr->fixed &&
- select_lex->ref_pointer_array[i]->fixed) ||
+ DBUG_ASSERT((left_expr->is_fixed() &&
+ select_lex->ref_pointer_array[i]->is_fixed()) ||
(select_lex->ref_pointer_array[i]->type() == REF_ITEM &&
((Item_ref*)(select_lex->ref_pointer_array[i]))->ref_type() ==
Item_ref::OUTER_REF));
@@ -2993,7 +3024,6 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg)
!upper_not->is_top_level_item())) ||
first_select->is_part_of_union() ||
first_select->group_list.elements ||
- first_select->order_list.elements ||
join->having ||
first_select->with_sum_func ||
!first_select->leaf_tables.elements||
@@ -3001,8 +3031,31 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg)
with_recursive_reference)
DBUG_RETURN(FALSE);
- DBUG_ASSERT(first_select->order_list.elements == 0 &&
- first_select->group_list.elements == 0 &&
+ /*
+ EXISTS-to-IN coversion and ORDER BY ... LIMIT clause:
+
+ - "[ORDER BY ...] LIMIT n" clause with a non-zero n does not affect
+ the result of the EXISTS(...) predicate, and so we can discard
+ it during the conversion.
+ - "[ORDER BY ...] LIMIT m, n" can turn a non-empty resultset into empty
+ one, so it affects tthe EXISTS(...) result and cannot be discarded.
+
+ Disallow exists-to-in conversion if
+ (1). three is a LIMIT which is not a basic constant
+ (1a) or is a "LIMIT 0" (see MDEV-19429)
+ (2). there is an OFFSET clause
+ */
+ if ((first_select->select_limit && // (1)
+ (!first_select->select_limit->basic_const_item() || // (1)
+ first_select->select_limit->val_uint() == 0)) || // (1a)
+ first_select->offset_limit) // (2)
+ {
+ DBUG_RETURN(FALSE);
+ }
+
+ /* Disallow the conversion if offset + limit exists */
+
+ DBUG_ASSERT(first_select->group_list.elements == 0 &&
first_select->having == NULL);
if (find_inner_outer_equalities(&join->conds, eqs))
@@ -3033,9 +3086,6 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg)
if ((uint)eqs.elements() > (first_select->item_list.elements +
first_select->select_n_reserved))
goto out;
- /* It is simple query */
- DBUG_ASSERT(first_select->join->all_fields.elements ==
- first_select->item_list.elements);
arena= thd->activate_stmt_arena_if_needed(&backup);
@@ -3291,21 +3341,6 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
DBUG_ENTER("Item_in_subselect::select_in_like_transformer");
DBUG_ASSERT(thd == join->thd);
-
- /*
- IN/SOME/ALL/ANY subqueries aren't support LIMIT clause. Without it
- ORDER BY clause becomes meaningless thus we drop it here.
- */
- for (SELECT_LEX *sl= current->master_unit()->first_select();
- sl; sl= sl->next_select())
- {
- if (sl->join)
- {
- sl->join->order= 0;
- sl->join->skip_sort_order= 1;
- }
- }
-
thd->where= "IN/ALL/ANY subquery";
/*
@@ -3369,7 +3404,8 @@ out:
void Item_in_subselect::print(String *str, enum_query_type query_type)
{
- if (test_strategy(SUBS_IN_TO_EXISTS))
+ if (test_strategy(SUBS_IN_TO_EXISTS) &&
+ !(query_type & QT_PARSABLE))
str->append(STRING_WITH_LEN("<exists>"));
else
{
@@ -3596,7 +3632,8 @@ Item_allany_subselect::select_transformer(JOIN *join)
void Item_allany_subselect::print(String *str, enum_query_type query_type)
{
- if (test_strategy(SUBS_IN_TO_EXISTS))
+ if (test_strategy(SUBS_IN_TO_EXISTS) &&
+ !(query_type & QT_PARSABLE))
str->append(STRING_WITH_LEN("<exists>"));
else
{
@@ -5908,7 +5945,7 @@ int
Ordered_key::cmp_keys_by_row_data(ha_rows a, ha_rows b)
{
uchar *rowid_a, *rowid_b;
- int __attribute__((unused)) error;
+ int error;
int cmp_res;
/* The length in bytes of the rowids (positions) of tmp_table. */
uint rowid_length= tbl->file->ref_length;
@@ -5922,14 +5959,14 @@ Ordered_key::cmp_keys_by_row_data(ha_rows a, ha_rows b)
if (unlikely((error= tbl->file->ha_rnd_pos(tbl->record[0], rowid_a))))
{
/* purecov: begin inspected */
- tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error
+ tbl->file->print_error(error, MYF(ME_FATAL)); // Sets fatal_error
return 0;
/* purecov: end */
}
if (unlikely((error= tbl->file->ha_rnd_pos(tbl->record[1], rowid_b))))
{
/* purecov: begin inspected */
- tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error
+ tbl->file->print_error(error, MYF(ME_FATAL)); // Sets fatal_error
return 0;
/* purecov: end */
}
@@ -6009,13 +6046,13 @@ int Ordered_key::cmp_key_with_search_key(rownum_t row_num)
/* The length in bytes of the rowids (positions) of tmp_table. */
uint rowid_length= tbl->file->ref_length;
uchar *cur_rowid= row_num_to_rowid + row_num * rowid_length;
- int __attribute__((unused)) error;
+ int error;
int cmp_res;
if (unlikely((error= tbl->file->ha_rnd_pos(tbl->record[0], cur_rowid))))
{
/* purecov: begin inspected */
- tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error
+ tbl->file->print_error(error, MYF(ME_FATAL)); // Sets fatal_error
return 0;
/* purecov: end */
}
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 16c683b27e0..4816785fa13 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -33,6 +33,7 @@ class subselect_hash_sj_engine;
class Item_bool_func2;
class Comp_creator;
class With_element;
+class Field_pair;
typedef class st_select_lex SELECT_LEX;
@@ -46,7 +47,8 @@ class Cached_item;
/* base class for subselects */
class Item_subselect :public Item_result_field,
- protected Used_tables_and_const_cache
+ protected Used_tables_and_const_cache,
+ protected With_sum_func_cache
{
/*
Set to TRUE if the value is assigned for the subselect
@@ -75,6 +77,8 @@ protected:
to substitute 'this' with a constant item.
*/
bool forced_const;
+ /* Set to the result of the last call of is_expensive() */
+ bool expensive_fl;
#ifndef DBUG_OFF
/* Count the number of times this subquery predicate has been executed. */
uint exec_counter;
@@ -191,6 +195,8 @@ public:
}
bool fix_fields(THD *thd, Item **ref);
bool with_subquery() const { DBUG_ASSERT(fixed); return true; }
+ bool with_sum_func() const { return m_with_sum_func; }
+ With_sum_func_cache* get_with_sum_func_cache() { return this; }
bool mark_as_dependent(THD *thd, st_select_lex *select, Item *item);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
void recalc_used_tables(st_select_lex *new_parent, bool after_pullout);
@@ -312,9 +318,10 @@ public:
double val_real();
longlong val_int ();
String *val_str (String *);
+ bool val_native(THD *thd, Native *);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
const Type_handler *type_handler() const;
bool fix_length_and_dec();
@@ -405,14 +412,14 @@ public:
}
void no_rows_in_result();
- const Type_handler *type_handler() const { return &type_handler_longlong; }
+ const Type_handler *type_handler() const { return &type_handler_bool; }
longlong val_int();
double val_real();
String *val_str(String*);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
- { return get_date_from_int(ltime, fuzzydate); }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ { return get_date_from_int(thd, ltime, fuzzydate); }
bool fix_fields(THD *thd, Item **ref);
bool fix_length_and_dec();
void print(String *str, enum_query_type query_type);
@@ -580,6 +587,8 @@ public:
*/
bool is_registered_semijoin;
+ List<Field_pair> corresponding_fields;
+
/*
Used to determine how this subselect item is represented in the item tree,
in case there is a need to locate it there and replace with something else.
@@ -637,7 +646,6 @@ public:
double val_real();
String *val_str(String*);
my_decimal *val_decimal(my_decimal *);
- void update_null_value () { (void) val_bool(); }
bool val_bool();
bool test_limit(st_select_lex_unit *unit);
void print(String *str, enum_query_type query_type);
@@ -757,6 +765,8 @@ public:
return 0;
};
+ bool pushdown_cond_for_in_subquery(THD *thd, Item *cond);
+
friend class Item_ref_null_helper;
friend class Item_is_not_null_test;
friend class Item_in_optimizer;
@@ -868,7 +878,6 @@ protected:
bool set_row(List<Item> &item_list, Item_cache **row);
};
-
class subselect_single_select_engine: public subselect_engine
{
bool prepared; /* simple subselect is prepared */
@@ -902,9 +911,10 @@ public:
friend class subselect_hash_sj_engine;
friend class Item_in_subselect;
- friend bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
- Item **join_where);
-
+ friend bool execute_degenerate_jtbm_semi_join(THD *thd,
+ TABLE_LIST *tbl,
+ Item_in_subselect *subq_pred,
+ List<Item> &eq_list);
};
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 034c4d2a515..581c94bd191 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -406,7 +406,7 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref)
for (sl= thd->lex->current_select;
sl && sl != aggr_sel && sl->master_unit()->item;
sl= sl->master_unit()->outer_select() )
- sl->master_unit()->item->with_sum_func= 1;
+ sl->master_unit()->item->get_with_sum_func_cache()->set_with_sum_func();
}
thd->lex->current_select->mark_as_dependent(thd, aggr_sel, NULL);
@@ -487,7 +487,6 @@ void Item_sum::mark_as_sum_func()
cur_select->n_sum_items++;
cur_select->with_sum_func= 1;
const_item_cache= false;
- with_sum_func= 1;
with_field= 0;
window_func_sum_expr_flag= false;
}
@@ -893,7 +892,7 @@ bool Aggregator_distinct::setup(THD *thd)
item_sum->null_value= item_sum->maybe_null= 1;
item_sum->quick_group= 0;
- DBUG_ASSERT(item_sum->get_arg(0)->fixed);
+ DBUG_ASSERT(item_sum->get_arg(0)->is_fixed());
arg= item_sum->get_arg(0);
if (arg->const_item())
@@ -1290,9 +1289,11 @@ Field *Item_sum_min_max::create_tmp_field(bool group, TABLE *table)
if (args[0]->type() == Item::FIELD_ITEM)
{
Field *field= ((Item_field*) args[0])->field;
- if ((field= create_tmp_field_from_field(table->in_use, field, &name,
- table, NULL)))
- field->flags&= ~NOT_NULL_FLAG;
+ if ((field= field->create_tmp_field(table->in_use->mem_root, table, true)))
+ {
+ DBUG_ASSERT((field->flags & NOT_NULL_FLAG) == 0);
+ field->field_name= name;
+ }
DBUG_RETURN(field);
}
DBUG_RETURN(tmp_table_field_from_field_type(table));
@@ -1340,7 +1341,7 @@ Item_sum_sp::fix_fields(THD *thd, Item **ref)
if (!m_sp)
{
my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr());
- context->process_error(thd);
+ process_error(thd);
return TRUE;
}
@@ -1695,12 +1696,7 @@ longlong Item_sum_sum::val_int()
if (aggr)
aggr->endup();
if (result_type() == DECIMAL_RESULT)
- {
- longlong result;
- my_decimal2int(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, unsigned_flag,
- &result);
- return result;
- }
+ return dec_buffs[curr_dec_buff].to_longlong(unsigned_flag);
return val_int_from_real();
}
@@ -1711,7 +1707,7 @@ double Item_sum_sum::val_real()
if (aggr)
aggr->endup();
if (result_type() == DECIMAL_RESULT)
- my_decimal2double(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, &sum);
+ sum= dec_buffs[curr_dec_buff].to_double();
return sum;
}
@@ -1721,7 +1717,7 @@ String *Item_sum_sum::val_str(String *str)
if (aggr)
aggr->endup();
if (result_type() == DECIMAL_RESULT)
- return val_string_from_decimal(str);
+ return VDec(this).to_string_round(str, decimals);
return val_string_from_real(str);
}
@@ -2087,7 +2083,7 @@ String *Item_sum_avg::val_str(String *str)
if (aggr)
aggr->endup();
if (result_type() == DECIMAL_RESULT)
- return val_string_from_decimal(str);
+ return VDec(this).to_string_round(str, decimals);
return val_string_from_real(str);
}
@@ -2147,46 +2143,42 @@ Item *Item_sum_std::result_item(THD *thd, Field *field)
variance. The difference between the two classes is that the first is used
for a mundane SELECT, while the latter is used in a GROUPing SELECT.
*/
-static void variance_fp_recurrence_next(double *m, double *s, ulonglong *count, double nr)
+void Stddev::recurrence_next(double nr)
{
- *count += 1;
-
- if (*count == 1)
+ if (!m_count++)
{
- *m= nr;
- *s= 0;
+ DBUG_ASSERT(m_m == 0);
+ DBUG_ASSERT(m_s == 0);
+ m_m= nr;
}
else
{
- double m_kminusone= *m;
+ double m_kminusone= m_m;
volatile double diff= nr - m_kminusone;
- *m= m_kminusone + diff / (double) *count;
- *s= *s + diff * (nr - *m);
+ m_m= m_kminusone + diff / (double) m_count;
+ m_s= m_s + diff * (nr - m_m);
}
}
-static double variance_fp_recurrence_result(double s, ulonglong count, bool is_sample_variance)
+double Stddev::result(bool is_sample_variance)
{
- if (count == 1)
+ if (m_count == 1)
return 0.0;
if (is_sample_variance)
- return s / (count - 1);
+ return m_s / (m_count - 1);
/* else, is a population variance */
- return s / count;
+ return m_s / m_count;
}
Item_sum_variance::Item_sum_variance(THD *thd, Item_sum_variance *item):
Item_sum_double(thd, item),
- count(item->count), sample(item->sample),
+ m_stddev(item->m_stddev), sample(item->sample),
prec_increment(item->prec_increment)
-{
- recurrence_m= item->recurrence_m;
- recurrence_s= item->recurrence_s;
-}
+{ }
void Item_sum_variance::fix_length_and_dec_double()
@@ -2249,8 +2241,7 @@ Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table)
The easiest way is to do this is to store both value in a string
and unpack on access.
*/
- field= new Field_string(sizeof(double)*2 + sizeof(longlong), 0,
- &name, &my_charset_bin);
+ field= new Field_string(Stddev::binary_size(), 0, &name, &my_charset_bin);
}
else
field= new Field_double(max_length, maybe_null, &name, decimals,
@@ -2265,7 +2256,7 @@ Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table)
void Item_sum_variance::clear()
{
- count= 0;
+ m_stddev= Stddev();
}
bool Item_sum_variance::add()
@@ -2277,7 +2268,7 @@ bool Item_sum_variance::add()
double nr= args[0]->val_real();
if (!args[0]->null_value)
- variance_fp_recurrence_next(&recurrence_m, &recurrence_s, &count, nr);
+ m_stddev.recurrence_next(nr);
return 0;
}
@@ -2295,14 +2286,14 @@ double Item_sum_variance::val_real()
below which a 'count' number of items is called NULL.
*/
DBUG_ASSERT((sample == 0) || (sample == 1));
- if (count <= sample)
+ if (m_stddev.count() <= sample)
{
null_value=1;
return 0.0;
}
null_value=0;
- return variance_fp_recurrence_result(recurrence_s, count, sample);
+ return m_stddev.result(sample);
}
@@ -2314,24 +2305,32 @@ void Item_sum_variance::reset_field()
nr= args[0]->val_real(); /* sets null_value as side-effect */
if (args[0]->null_value)
- bzero(res,sizeof(double)*2+sizeof(longlong));
+ bzero(res,Stddev::binary_size());
else
- {
- /* Serialize format is (double)m, (double)s, (longlong)count */
- ulonglong tmp_count;
- double tmp_s;
- float8store(res, nr); /* recurrence variable m */
- tmp_s= 0.0;
- float8store(res + sizeof(double), tmp_s);
- tmp_count= 1;
- int8store(res + sizeof(double)*2, tmp_count);
- }
+ Stddev(nr).to_binary(res);
+}
+
+
+Stddev::Stddev(const uchar *ptr)
+{
+ float8get(m_m, ptr);
+ float8get(m_s, ptr + sizeof(double));
+ m_count= sint8korr(ptr + sizeof(double) * 2);
+}
+
+
+void Stddev::to_binary(uchar *ptr) const
+{
+ /* Serialize format is (double)m, (double)s, (longlong)count */
+ float8store(ptr, m_m);
+ float8store(ptr + sizeof(double), m_s);
+ ptr+= sizeof(double)*2;
+ int8store(ptr, m_count);
}
void Item_sum_variance::update_field()
{
- ulonglong field_count;
uchar *res=result_field->ptr;
double nr= args[0]->val_real(); /* sets null_value as side-effect */
@@ -2340,17 +2339,9 @@ void Item_sum_variance::update_field()
return;
/* Serialize format is (double)m, (double)s, (longlong)count */
- double field_recurrence_m, field_recurrence_s;
- float8get(field_recurrence_m, res);
- float8get(field_recurrence_s, res + sizeof(double));
- field_count=sint8korr(res+sizeof(double)*2);
-
- variance_fp_recurrence_next(&field_recurrence_m, &field_recurrence_s, &field_count, nr);
-
- float8store(res, field_recurrence_m);
- float8store(res + sizeof(double), field_recurrence_s);
- res+= sizeof(double)*2;
- int8store(res,field_count);
+ Stddev field_stddev(res);
+ field_stddev.recurrence_next(nr);
+ field_stddev.to_binary(res);
}
@@ -2371,12 +2362,12 @@ void Item_sum_min_max::clear()
bool
-Item_sum_min_max::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+Item_sum_min_max::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed == 1);
if (null_value)
return true;
- bool retval= value->get_date(ltime, fuzzydate);
+ bool retval= value->get_date(thd, ltime, fuzzydate);
if ((null_value= value->null_value))
DBUG_ASSERT(retval == true);
return retval;
@@ -2445,6 +2436,15 @@ Item_sum_min_max::val_str(String *str)
}
+bool Item_sum_min_max::val_native(THD *thd, Native *to)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (null_value)
+ return true;
+ return val_native_from_item(thd, value, to);
+}
+
+
void Item_sum_min_max::cleanup()
{
DBUG_ENTER("Item_sum_min_max::cleanup");
@@ -2635,9 +2635,9 @@ bool Item_sum_bit::add_as_window(ulonglong value)
void Item_sum_or::set_bits_from_counters()
{
ulonglong value= 0;
- for (int i= 0; i < NUM_BIT_COUNTERS; i++)
+ for (uint i= 0; i < NUM_BIT_COUNTERS; i++)
{
- value|= bit_counters[i] > 0 ? (1 << i) : 0;
+ value|= bit_counters[i] > 0 ? (1ULL << i) : 0ULL;
}
bits= value | reset_bits;
}
@@ -2721,25 +2721,6 @@ bool Item_sum_and::add()
** reset result of a Item_sum with is saved in a tmp_table
*************************************************************************/
-void Item_sum_num::reset_field()
-{
- double nr= args[0]->val_real();
- uchar *res=result_field->ptr;
-
- if (maybe_null)
- {
- if (args[0]->null_value)
- {
- nr=0.0;
- result_field->set_null();
- }
- else
- result_field->set_notnull();
- }
- float8store(res,nr);
-}
-
-
void Item_sum_min_max::reset_field()
{
Item *UNINIT_VAR(tmp_item), *arg0;
@@ -2810,11 +2791,11 @@ void Item_sum_min_max::reset_field()
}
case DECIMAL_RESULT:
{
- my_decimal value_buff, *arg_dec= arg0->val_decimal(&value_buff);
+ VDec arg_dec(arg0);
if (maybe_null)
{
- if (arg0->null_value)
+ if (arg_dec.is_null())
result_field->set_null();
else
result_field->set_notnull();
@@ -2823,9 +2804,7 @@ void Item_sum_min_max::reset_field()
We must store zero in the field as we will use the field value in
add()
*/
- if (!arg_dec) // Null
- arg_dec= &decimal_zero;
- result_field->store_decimal(arg_dec);
+ result_field->store_decimal(arg_dec.ptr_or(&decimal_zero));
break;
}
case ROW_RESULT:
@@ -2848,15 +2827,10 @@ void Item_sum_sum::reset_field()
DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
if (result_type() == DECIMAL_RESULT)
{
- my_decimal value, *arg_val;
if (unlikely(direct_added))
- arg_val= &direct_sum_decimal;
+ result_field->store_decimal(&direct_sum_decimal);
else
- {
- if (!(arg_val= args[0]->val_decimal(&value)))
- arg_val= &decimal_zero; // Null
- }
- result_field->store_decimal(arg_val);
+ result_field->store_decimal(VDec(args[0]).ptr_or(&decimal_zero));
}
else
{
@@ -2909,15 +2883,9 @@ void Item_sum_avg::reset_field()
if (result_type() == DECIMAL_RESULT)
{
longlong tmp;
- my_decimal value, *arg_dec= args[0]->val_decimal(&value);
- if (args[0]->null_value)
- {
- arg_dec= &decimal_zero;
- tmp= 0;
- }
- else
- tmp= 1;
- my_decimal2binary(E_DEC_FATAL_ERROR, arg_dec, res, f_precision, f_scale);
+ VDec value(args[0]);
+ tmp= value.is_null() ? 0 : 1;
+ value.to_binary(res, f_precision, f_scale);
res+= dec_bin_size;
int8store(res, tmp);
}
@@ -2984,9 +2952,8 @@ void Item_sum_sum::update_field()
{
if (!result_field->is_null())
{
- my_decimal field_value;
- my_decimal *field_val= result_field->val_decimal(&field_value);
- my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, field_val);
+ my_decimal field_value(result_field);
+ my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, &field_value);
result_field->store_decimal(dec_buffs);
}
else
@@ -3053,15 +3020,14 @@ void Item_sum_avg::update_field()
if (result_type() == DECIMAL_RESULT)
{
- my_decimal value, *arg_val= args[0]->val_decimal(&value);
- if (!args[0]->null_value)
+ VDec tmp(args[0]);
+ if (!tmp.is_null())
{
binary2my_decimal(E_DEC_FATAL_ERROR, res,
dec_buffs + 1, f_precision, f_scale);
field_count= sint8korr(res + dec_bin_size);
- my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, dec_buffs + 1);
- my_decimal2binary(E_DEC_FATAL_ERROR, dec_buffs,
- res, f_precision, f_scale);
+ my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, tmp.ptr(), dec_buffs + 1);
+ dec_buffs->to_binary(res, f_precision, f_scale);
res+= dec_bin_size;
field_count++;
int8store(res, field_count);
@@ -3105,18 +3071,32 @@ void Item_sum_min_max::update_field()
tmp_item= args[0];
args[0]= direct_item;
}
- switch (result_type()) {
- case STRING_RESULT:
- min_max_update_str_field();
- break;
- case INT_RESULT:
- min_max_update_int_field();
- break;
- case DECIMAL_RESULT:
- min_max_update_decimal_field();
- break;
- default:
- min_max_update_real_field();
+ if (Item_sum_min_max::type_handler()->is_val_native_ready())
+ {
+ /*
+ TODO-10.5: change Item_sum_min_max to use val_native() for all data types
+ - make all type handlers val_native() ready
+ - use min_max_update_native_field() for all data types
+ - remove Item_sum_min_max::min_max_update_{str|real|int|decimal}_field()
+ */
+ min_max_update_native_field();
+ }
+ else
+ {
+ switch (Item_sum_min_max::type_handler()->cmp_type()) {
+ case STRING_RESULT:
+ case TIME_RESULT:
+ min_max_update_str_field();
+ break;
+ case INT_RESULT:
+ min_max_update_int_field();
+ break;
+ case DECIMAL_RESULT:
+ min_max_update_decimal_field();
+ break;
+ default:
+ min_max_update_real_field();
+ }
}
if (unlikely(direct_added))
{
@@ -3127,6 +3107,40 @@ void Item_sum_min_max::update_field()
}
+void Arg_comparator::min_max_update_field_native(THD *thd,
+ Field *field,
+ Item *item,
+ int cmp_sign)
+{
+ DBUG_ENTER("Arg_comparator::min_max_update_field_native");
+ if (!item->val_native(current_thd, &m_native2))
+ {
+ if (field->is_null())
+ field->store_native(m_native2); // The first non-null value
+ else
+ {
+ field->val_native(&m_native1);
+ if ((cmp_sign * m_compare_handler->cmp_native(m_native2, m_native1)) < 0)
+ field->store_native(m_native2);
+ }
+ field->set_notnull();
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+void
+Item_sum_min_max::min_max_update_native_field()
+{
+ DBUG_ENTER("Item_sum_min_max::min_max_update_native_field");
+ DBUG_ASSERT(cmp);
+ DBUG_ASSERT(type_handler_for_comparison() == cmp->compare_type_handler());
+ THD *thd= current_thd;
+ cmp->min_max_update_field_native(thd, result_field, args[0], cmp_sign);
+ DBUG_VOID_RETURN;
+}
+
+
void
Item_sum_min_max::min_max_update_str_field()
{
@@ -3259,9 +3273,7 @@ my_decimal *Item_avg_field_decimal::val_decimal(my_decimal *dec_buf)
if ((null_value= !count))
return 0;
- my_decimal dec_count, dec_field;
- binary2my_decimal(E_DEC_FATAL_ERROR,
- field->ptr, &dec_field, f_precision, f_scale);
+ my_decimal dec_count, dec_field(field->ptr, f_precision, f_scale);
int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &dec_count);
my_decimal_div(E_DEC_FATAL_ERROR, dec_buf,
&dec_field, &dec_count, prec_increment);
@@ -3282,15 +3294,11 @@ double Item_std_field::val_real()
double Item_variance_field::val_real()
{
// fix_fields() never calls for this Item
- double recurrence_s;
- ulonglong count;
- float8get(recurrence_s, (field->ptr + sizeof(double)));
- count=sint8korr(field->ptr+sizeof(double)*2);
-
- if ((null_value= (count <= sample)))
+ Stddev tmp(field->ptr);
+ if ((null_value= (tmp.count() <= sample)))
return 0.0;
- return variance_fp_recurrence_result(recurrence_s, count, sample);
+ return tmp.result(sample);
}
@@ -3319,6 +3327,25 @@ bool Item_udf_sum::add()
DBUG_RETURN(0);
}
+
+bool Item_udf_sum::supports_removal() const
+{
+ DBUG_ENTER("Item_udf_sum::supports_remove");
+ DBUG_PRINT("info", ("support: %d", udf.supports_removal()));
+ DBUG_RETURN(udf.supports_removal());
+}
+
+
+void Item_udf_sum::remove()
+{
+ my_bool tmp_null_value;
+ DBUG_ENTER("Item_udf_sum::remove");
+ udf.remove(&tmp_null_value);
+ null_value= tmp_null_value;
+ DBUG_VOID_RETURN;
+}
+
+
void Item_udf_sum::cleanup()
{
/*
@@ -3375,24 +3402,6 @@ my_decimal *Item_sum_udf_float::val_decimal(my_decimal *dec)
}
-String *Item_sum_udf_decimal::val_str(String *str)
-{
- return val_string_from_decimal(str);
-}
-
-
-double Item_sum_udf_decimal::val_real()
-{
- return val_real_from_decimal();
-}
-
-
-longlong Item_sum_udf_decimal::val_int()
-{
- return val_int_from_decimal();
-}
-
-
my_decimal *Item_sum_udf_decimal::val_decimal(my_decimal *dec_buf)
{
my_decimal *res;
@@ -3729,7 +3738,7 @@ Item_func_group_concat(THD *thd, Name_resolution_context *context_arg,
arg_count_field(select_list->elements),
row_count(0),
distinct(distinct_arg),
- warning_for_row(FALSE),
+ warning_for_row(FALSE), always_null(FALSE),
force_copy_fields(0), row_limit(NULL),
offset_limit(NULL), limit_clause(limit_clause),
copy_offset_limit(0), copy_row_limit(0), original(0)
@@ -4154,6 +4163,7 @@ bool Item_func_group_concat::setup(THD *thd)
if (!ref_pointer_array)
DBUG_RETURN(TRUE);
memcpy(ref_pointer_array, args, arg_count * sizeof(Item*));
+ DBUG_ASSERT(context);
if (setup_order(thd, Ref_ptr_array(ref_pointer_array, n_elems),
context->table_list, list, all_fields, *order))
DBUG_RETURN(TRUE);
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 48565ece5a8..f715c80ffaf 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -384,7 +384,9 @@ protected:
Item **orig_args, *tmp_orig_args[2];
static size_t ram_limitation(THD *thd);
-
+public:
+ // Methods used by ColumnStore
+ Item **get_orig_args() const { return orig_args; }
public:
void mark_as_sum_func();
@@ -511,7 +513,12 @@ public:
}
virtual void make_unique() { force_copy_fields= TRUE; }
Item *get_tmp_table_item(THD *thd);
- Field *create_tmp_field(bool group, TABLE *table);
+ virtual Field *create_tmp_field(bool group, TABLE *table);
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ return create_tmp_field(param->group(), table);
+ }
virtual bool collect_outer_ref_processor(void *param);
bool init_sum_func_check(THD *thd);
bool check_sum_func(THD *thd, Item **ref);
@@ -578,6 +585,8 @@ public:
void mark_as_window_func_sum_expr() { window_func_sum_expr_flag= true; }
bool is_window_func_sum_expr() { return window_func_sum_expr_flag; }
virtual void setup_caches(THD *thd) {};
+
+ bool with_sum_func() const { return true; }
virtual void set_partition_row_count(ulonglong count) { DBUG_ASSERT(0); }
};
@@ -725,7 +734,6 @@ public:
Item_sum_num(THD *thd, Item_sum_num *item):
Item_sum(thd, item) {}
bool fix_fields(THD *, Item **);
- void reset_field();
};
@@ -748,9 +756,9 @@ public:
{
return val_decimal_from_real(to);
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return get_date_from_real(ltime, fuzzydate);
+ return get_date_from_real(thd, ltime, fuzzydate);
}
const Type_handler *type_handler() const { return &type_handler_double; }
};
@@ -766,9 +774,9 @@ public:
double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); }
String *val_str(String*str);
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return get_date_from_int(ltime, fuzzydate);
+ return get_date_from_int(thd, ltime, fuzzydate);
}
const Type_handler *type_handler() const { return &type_handler_longlong; }
bool fix_length_and_dec()
@@ -811,9 +819,9 @@ public:
longlong val_int();
String *val_str(String*str);
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate);
}
const Type_handler *type_handler() const
{ return Type_handler_hybrid_field_type::type_handler(); }
@@ -985,18 +993,38 @@ But, this falls prey to catastrophic cancellation. Instead, use the recurrence
*/
+class Stddev
+{
+ double m_m;
+ double m_s;
+ ulonglong m_count;
+public:
+ Stddev() :m_m(0), m_s(0), m_count(0) { }
+ Stddev(double nr) :m_m(nr), m_s(0.0), m_count(1) { }
+ Stddev(const uchar *);
+ void to_binary(uchar *) const;
+ void recurrence_next(double nr);
+ double result(bool is_simple_variance);
+ ulonglong count() const { return m_count; }
+ static uint32 binary_size()
+ {
+ return (uint32) (sizeof(double) * 2 + sizeof(ulonglong));
+ };
+};
+
+
+
class Item_sum_variance : public Item_sum_double
{
+ Stddev m_stddev;
bool fix_length_and_dec();
public:
- double recurrence_m, recurrence_s; /* Used in recurrence relation. */
- ulonglong count;
uint sample;
uint prec_increment;
Item_sum_variance(THD *thd, Item *item_par, uint sample_arg):
- Item_sum_double(thd, item_par), count(0),
+ Item_sum_double(thd, item_par),
sample(sample_arg)
{}
Item_sum_variance(THD *thd, Item_sum_variance *item);
@@ -1016,7 +1044,7 @@ public:
Field *create_tmp_field(bool group, TABLE *table);
void cleanup()
{
- count= 0;
+ m_stddev= Stddev();
Item_sum_double::cleanup();
}
Item *get_copy(THD *thd)
@@ -1102,9 +1130,10 @@ public:
double val_real();
longlong val_int();
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
void reset_field();
String *val_str(String *);
+ bool val_native(THD *thd, Native *);
const Type_handler *real_type_handler() const
{
return get_arg(0)->real_type_handler();
@@ -1115,6 +1144,7 @@ public:
void min_max_update_real_field();
void min_max_update_int_field();
void min_max_update_decimal_field();
+ void min_max_update_native_field();
void cleanup();
bool any_value() { return was_values; }
void no_rows_in_result();
@@ -1396,7 +1426,7 @@ public:
void update_field(){DBUG_ASSERT(0);}
void clear();
void cleanup();
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
return execute() || sp_result_field->get_date(ltime, fuzzydate);
}
@@ -1424,17 +1454,21 @@ public:
decimals= item->decimals;
max_length= item->max_length;
unsigned_flag= item->unsigned_flag;
- fixed= true;
}
table_map used_tables() const { return (table_map) 1L; }
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ return create_tmp_field_ex_simple(table, src, param);
+ }
void save_in_result_field(bool no_conversions) { DBUG_ASSERT(0); }
bool check_vcol_func_processor(void *arg)
{
return mark_unsupported_function(name.str, arg, VCOL_IMPOSSIBLE);
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate);
}
};
@@ -1479,9 +1513,18 @@ public:
dec_bin_size(item->dec_bin_size)
{ }
const Type_handler *type_handler() const { return &type_handler_newdecimal; }
- double val_real() { return val_real_from_decimal(); }
- longlong val_int() { return val_int_from_decimal(); }
- String *val_str(String *str) { return val_string_from_decimal(str); }
+ double val_real()
+ {
+ return VDec(this).to_double();
+ }
+ longlong val_int()
+ {
+ return VDec(this).to_longlong(unsigned_flag);
+ }
+ String *val_str(String *str)
+ {
+ return VDec(this).to_string_round(str, decimals);
+ }
my_decimal *val_decimal(my_decimal *);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_avg_field_decimal>(thd, this); }
@@ -1581,13 +1624,15 @@ public:
void clear();
bool add();
+ bool supports_removal() const;
+ void remove();
void reset_field() {};
void update_field() {};
void cleanup();
virtual void print(String *str, enum_query_type query_type);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate);
}
};
@@ -1685,9 +1730,18 @@ public:
Item_udf_sum(thd, udf_arg, list) {}
Item_sum_udf_decimal(THD *thd, Item_sum_udf_decimal *item)
:Item_udf_sum(thd, item) {}
- String *val_str(String *);
- double val_real();
- longlong val_int();
+ String *val_str(String *str)
+ {
+ return VDec(this).to_string_round(str, decimals);
+ }
+ double val_real()
+ {
+ return VDec(this).to_double();
+ }
+ longlong val_int()
+ {
+ return VDec(this).to_longlong(unsigned_flag);
+ }
my_decimal *val_decimal(my_decimal *);
const Type_handler *type_handler() const { return &type_handler_newdecimal; }
bool fix_length_and_dec() { fix_num_length_and_dec(); return FALSE; }
@@ -1711,6 +1765,7 @@ class Item_sum_udf_float :public Item_sum_double
double val_real() { DBUG_ASSERT(fixed == 1); return 0.0; }
void clear() {}
bool add() { return 0; }
+ void reset_field() { DBUG_ASSERT(0); };
void update_field() {}
};
@@ -1729,6 +1784,7 @@ public:
double val_real() { DBUG_ASSERT(fixed == 1); return 0; }
void clear() {}
bool add() { return 0; }
+ void reset_field() { DBUG_ASSERT(0); };
void update_field() {}
};
@@ -1747,6 +1803,7 @@ class Item_sum_udf_decimal :public Item_sum_double
my_decimal *val_decimal(my_decimal *) { DBUG_ASSERT(fixed == 1); return 0; }
void clear() {}
bool add() { return 0; }
+ void reset_field() { DBUG_ASSERT(0); };
void update_field() {}
};
@@ -1768,6 +1825,7 @@ public:
enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; }
void clear() {}
bool add() { return 0; }
+ void reset_field() { DBUG_ASSERT(0); };
void update_field() {}
};
@@ -1842,6 +1900,14 @@ class Item_func_group_concat : public Item_sum
bool repack_tree(THD *thd);
public:
+ // Methods used by ColumnStore
+ bool get_distinct() const { return distinct; }
+ uint get_count_field() const { return arg_count_field; }
+ uint get_order_field() const { return arg_count_order; }
+ const String* get_separator() const { return separator; }
+ ORDER** get_order() const { return order; }
+
+public:
Item_func_group_concat(THD *thd, Name_resolution_context *context_arg,
bool is_distinct, List<Item> *is_select,
const SQL_I_List<ORDER> &is_order, String *is_separator,
@@ -1890,9 +1956,9 @@ public:
{
return val_decimal_from_string(decimal_value);
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return get_date_from_string(ltime, fuzzydate);
+ return get_date_from_string(thd, ltime, fuzzydate);
}
String* val_str(String* str);
Item *copy_or_same(THD* thd);
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index cb5754c3b05..7dbc81e8825 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2009, 2016, MariaDB
+ Copyright (c) 2009, 2020, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -59,6 +59,29 @@
/** Day number for Dec 31st, 9999. */
#define MAX_DAY_NUMBER 3652424L
+
+Func_handler_date_add_interval_datetime_arg0_time
+ func_handler_date_add_interval_datetime_arg0_time;
+
+Func_handler_date_add_interval_datetime func_handler_date_add_interval_datetime;
+Func_handler_date_add_interval_date func_handler_date_add_interval_date;
+Func_handler_date_add_interval_time func_handler_date_add_interval_time;
+Func_handler_date_add_interval_string func_handler_date_add_interval_string;
+
+Func_handler_add_time_datetime func_handler_add_time_datetime_add(1);
+Func_handler_add_time_datetime func_handler_add_time_datetime_sub(-1);
+Func_handler_add_time_time func_handler_add_time_time_add(1);
+Func_handler_add_time_time func_handler_add_time_time_sub(-1);
+Func_handler_add_time_string func_handler_add_time_string_add(1);
+Func_handler_add_time_string func_handler_add_time_string_sub(-1);
+
+Func_handler_str_to_date_datetime_sec func_handler_str_to_date_datetime_sec;
+Func_handler_str_to_date_datetime_usec func_handler_str_to_date_datetime_usec;
+Func_handler_str_to_date_date func_handler_str_to_date_date;
+Func_handler_str_to_date_time_sec func_handler_str_to_date_time_sec;
+Func_handler_str_to_date_time_usec func_handler_str_to_date_time_usec;
+
+
/*
Date formats corresponding to compound %r and %T conversion specifiers
@@ -103,12 +126,12 @@ static DATE_TIME_FORMAT time_24hrs_format= {{0}, '\0', 0,
1 error
*/
-static bool extract_date_time(DATE_TIME_FORMAT *format,
+static bool extract_date_time(THD *thd, DATE_TIME_FORMAT *format,
const char *val, uint length, MYSQL_TIME *l_time,
timestamp_type cached_timestamp_type,
const char **sub_pattern_end,
const char *date_time_type,
- ulonglong fuzzy_date)
+ date_conv_mode_t fuzzydate)
{
int weekday= 0, yearday= 0, daypart= 0;
int week_number= -1;
@@ -303,17 +326,17 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
We can't just set error here, as we don't want to generate two
warnings in case of errors
*/
- if (extract_date_time(&time_ampm_format, val,
+ if (extract_date_time(thd, &time_ampm_format, val,
(uint)(val_end - val), l_time,
- cached_timestamp_type, &val, "time", fuzzy_date))
+ cached_timestamp_type, &val, "time", fuzzydate))
DBUG_RETURN(1);
break;
/* Time in 24-hour notation */
case 'T':
- if (extract_date_time(&time_24hrs_format, val,
+ if (extract_date_time(thd, &time_24hrs_format, val,
(uint)(val_end - val), l_time,
- cached_timestamp_type, &val, "time", fuzzy_date))
+ cached_timestamp_type, &val, "time", fuzzydate))
DBUG_RETURN(1);
break;
@@ -419,7 +442,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
goto err;
int was_cut;
- if (check_date(l_time, fuzzy_date | TIME_INVALID_DATES, &was_cut))
+ if (check_date(l_time, fuzzydate | TIME_INVALID_DATES, &was_cut))
goto err;
if (val != val_end)
@@ -428,10 +451,10 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
{
if (!my_isspace(&my_charset_latin1,*val))
{
- make_truncated_value_warning(current_thd,
- Sql_condition::WARN_LEVEL_WARN,
- val_begin, length,
- cached_timestamp_type, NULL, NULL, NULL);
+ ErrConvString err(val_begin, length, &my_charset_bin);
+ make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ &err, cached_timestamp_type,
+ nullptr, nullptr, nullptr);
break;
}
} while (++val != val_end);
@@ -440,7 +463,6 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
err:
{
- THD *thd= current_thd;
char buff[128];
strmake(buff, val_begin, MY_MIN(length, sizeof(buff)-1));
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
@@ -711,7 +733,10 @@ static bool make_date_time(const LEX_CSTRING &format, MYSQL_TIME *l_time,
For example, '1.1' -> '1.100000'
*/
-static bool get_interval_info(const char *str, size_t length,CHARSET_INFO *cs, size_t count, ulonglong *values,
+#define MAX_DIGITS_IN_TIME_SPEC 20
+
+static bool get_interval_info(const char *str, size_t length,CHARSET_INFO *cs,
+ size_t count, ulonglong *values,
bool transform_msec)
{
const char *end=str+length;
@@ -723,11 +748,21 @@ static bool get_interval_info(const char *str, size_t length,CHARSET_INFO *cs, s
for (i=0 ; i < count ; i++)
{
- longlong value;
+ ulonglong value;
const char *start= str;
- for (value= 0; str != end && my_isdigit(cs, *str); str++)
+ const char *local_end= end;
+
+ /*
+ We limit things to 19 digits to not get an overflow. This is ok as
+ this function is meant to read up to microseconds
+ */
+ if ((local_end-str) > MAX_DIGITS_IN_TIME_SPEC)
+ local_end= str+ MAX_DIGITS_IN_TIME_SPEC;
+
+ for (value= 0; str != local_end && my_isdigit(cs, *str) ; str++)
value= value*10 + *str - '0';
- if ((field_length= (size_t)(str - start)) >= 20)
+
+ if ((field_length= (size_t)(str - start)) >= MAX_DIGITS_IN_TIME_SPEC)
return true;
values[i]= value;
while (str != end && !my_isdigit(cs,*str))
@@ -787,10 +822,9 @@ longlong Item_func_period_diff::val_int()
longlong Item_func_to_days::val_int()
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
- return 0;
- return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day);
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 : d.daynr();
}
@@ -798,42 +832,31 @@ longlong Item_func_to_seconds::val_int_endpoint(bool left_endp,
bool *incl_endp)
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- longlong seconds;
- longlong days;
- int dummy; /* unused */
- if (get_arg0_date(&ltime, TIME_FUZZY_DATES))
+ // val_int_endpoint() is called only if args[0] is a temporal Item_field
+ Datetime_from_temporal dt(current_thd, args[0], TIME_FUZZY_DATES);
+ if ((null_value= !dt.is_valid_datetime()))
{
/* got NULL, leave the incl_endp intact */
return LONGLONG_MIN;
}
- seconds= ltime.hour * 3600L + ltime.minute * 60 + ltime.second;
- seconds= ltime.neg ? -seconds : seconds;
- days= (longlong) calc_daynr(ltime.year, ltime.month, ltime.day);
- seconds+= days * 24L * 3600L;
/* Set to NULL if invalid date, but keep the value */
- null_value= check_date(&ltime,
- (ltime.year || ltime.month || ltime.day),
- (TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE),
- &dummy);
+ null_value= dt.check_date(TIME_NO_ZEROS);
/*
Even if the evaluation return NULL, seconds is useful for pruning
*/
- return seconds;
+ return dt.to_seconds();
}
longlong Item_func_to_seconds::val_int()
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- longlong seconds;
- longlong days;
- if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
- return 0;
- seconds= ltime.hour * 3600L + ltime.minute * 60 + ltime.second;
- seconds=ltime.neg ? -seconds : seconds;
- days= (longlong) calc_daynr(ltime.year, ltime.month, ltime.day);
- return seconds + days * 24L * 3600L;
+ THD *thd= current_thd;
+ /*
+ Unlike val_int_endpoint(), we cannot use Datetime_from_temporal here.
+ The argument can be of a non-temporal data type.
+ */
+ Datetime dt(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
+ return (null_value= !dt.is_valid_datetime()) ? 0 : dt.to_seconds();
}
/*
@@ -877,19 +900,17 @@ enum_monotonicity_info Item_func_to_seconds::get_monotonicity_info() const
longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp)
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
+ // val_int_endpoint() is only called if args[0] is a temporal Item_field
+ Datetime_from_temporal dt(current_thd, args[0], TIME_CONV_NONE);
longlong res;
- int dummy; /* unused */
- if (get_arg0_date(&ltime, 0))
+ if ((null_value= !dt.is_valid_datetime()))
{
/* got NULL, leave the incl_endp intact */
return LONGLONG_MIN;
}
- res=(longlong) calc_daynr(ltime.year,ltime.month,ltime.day);
+ res= (longlong) dt.daynr();
/* Set to NULL if invalid date, but keep the value */
- null_value= check_date(&ltime,
- (TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE),
- &dummy);
+ null_value= dt.check_date(TIME_NO_ZEROS);
if (null_value)
{
/*
@@ -918,8 +939,8 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp)
col < '2007-09-15 12:34:56' -> TO_DAYS(col) <= TO_DAYS('2007-09-15')
*/
- if ((!left_endp && !(ltime.hour || ltime.minute || ltime.second ||
- ltime.second_part)) ||
+ const MYSQL_TIME &ltime= dt.get_mysql_time()[0];
+ if ((!left_endp && dt.hhmmssff_is_zero()) ||
(left_endp && ltime.hour == 23 && ltime.minute == 59 &&
ltime.second == 59))
/* do nothing */
@@ -933,25 +954,25 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp)
longlong Item_func_dayofyear::val_int()
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (get_arg0_date(&ltime, TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE))
- return 0;
- return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day) -
- calc_daynr(ltime.year,1,1) + 1;
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 : d.dayofyear();
}
longlong Item_func_dayofmonth::val_int()
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- return get_arg0_date(&ltime, 0) ? 0 : (longlong) ltime.day;
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 : d.get_mysql_time()->day;
}
longlong Item_func_month::val_int()
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- return get_arg0_date(&ltime, 0) ? 0 : (longlong) ltime.month;
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 : d.get_mysql_time()->month;
}
@@ -973,12 +994,12 @@ String* Item_func_monthname::val_str(String* str)
DBUG_ASSERT(fixed == 1);
const char *month_name;
uint err;
- MYSQL_TIME ltime;
-
- if ((null_value= (get_arg0_date(&ltime, 0) || !ltime.month)))
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd));
+ if ((null_value= (!d.is_valid_datetime() || !d.get_mysql_time()->month)))
return (String *) 0;
- month_name= locale->month_names->type_names[ltime.month - 1];
+ month_name= locale->month_names->type_names[d.get_mysql_time()->month - 1];
str->copy(month_name, (uint) strlen(month_name), &my_charset_utf8_bin,
collation.collation, &err);
return str;
@@ -992,23 +1013,24 @@ String* Item_func_monthname::val_str(String* str)
longlong Item_func_quarter::val_int()
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (get_arg0_date(&ltime, 0))
- return 0;
- return (longlong) ((ltime.month+2)/3);
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 : d.quarter();
}
longlong Item_func_hour::val_int()
{
DBUG_ASSERT(fixed == 1);
- Time tm(args[0], Time::Options_for_cast());
+ THD *thd= current_thd;
+ Time tm(thd, args[0], Time::Options_for_cast(thd));
return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->hour;
}
longlong Item_func_minute::val_int()
{
DBUG_ASSERT(fixed == 1);
- Time tm(args[0], Time::Options_for_cast());
+ THD *thd= current_thd;
+ Time tm(thd, args[0], Time::Options_for_cast(thd));
return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->minute;
}
@@ -1018,7 +1040,8 @@ longlong Item_func_minute::val_int()
longlong Item_func_second::val_int()
{
DBUG_ASSERT(fixed == 1);
- Time tm(args[0], Time::Options_for_cast());
+ THD *thd= current_thd;
+ Time tm(thd, args[0], Time::Options_for_cast(thd));
return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->second;
}
@@ -1065,36 +1088,34 @@ uint week_mode(uint mode)
longlong Item_func_week::val_int()
{
DBUG_ASSERT(fixed == 1);
- uint year, week_format;
- MYSQL_TIME ltime;
- if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
+ uint week_format;
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
+ if ((null_value= !d.is_valid_datetime()))
return 0;
if (arg_count > 1)
week_format= (uint)args[1]->val_int();
else
- week_format= current_thd->variables.default_week_format;
- return (longlong) calc_week(&ltime, week_mode(week_format), &year);
+ week_format= thd->variables.default_week_format;
+ return d.week(week_mode(week_format));
}
longlong Item_func_yearweek::val_int()
{
DBUG_ASSERT(fixed == 1);
- uint year,week;
- MYSQL_TIME ltime;
- if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
- return 0;
- week= calc_week(&ltime,
- (week_mode((uint) args[1]->val_int()) | WEEK_YEAR),
- &year);
- return week+year*100;
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 :
+ d.yearweek((week_mode((uint) args[1]->val_int()) | WEEK_YEAR));
}
longlong Item_func_weekday::val_int()
{
DBUG_ASSERT(fixed == 1);
- Datetime dt(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
+ THD *thd= current_thd;
+ Datetime dt(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
if ((null_value= !dt.is_valid_datetime()))
return 0;
return dt.weekday(odbc_type) + MY_TEST(odbc_type);
@@ -1118,11 +1139,12 @@ String* Item_func_dayname::val_str(String* str)
DBUG_ASSERT(fixed == 1);
const char *day_name;
uint err;
- Datetime dt(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
+ THD *thd= current_thd;
+ Datetime dt(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
if ((null_value= !dt.is_valid_datetime()))
return (String*) 0;
-
+
day_name= locale->day_names->type_names[dt.weekday(false)];
str->copy(day_name, (uint) strlen(day_name), &my_charset_utf8_bin,
collation.collation, &err);
@@ -1133,8 +1155,9 @@ String* Item_func_dayname::val_str(String* str)
longlong Item_func_year::val_int()
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- return get_arg0_date(&ltime, 0) ? 0 : (longlong) ltime.year;
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 : d.get_mysql_time()->year;
}
@@ -1165,8 +1188,9 @@ enum_monotonicity_info Item_func_year::get_monotonicity_info() const
longlong Item_func_year::val_int_endpoint(bool left_endp, bool *incl_endp)
{
DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (get_arg0_date(&ltime, 0))
+ // val_int_endpoint() is cally only if args[0] is a temporal Item_field
+ Datetime_from_temporal dt(current_thd, args[0], TIME_CONV_NONE);
+ if ((null_value= !dt.is_valid_datetime()))
{
/* got NULL, leave the incl_endp intact */
return LONGLONG_MIN;
@@ -1183,8 +1207,9 @@ longlong Item_func_year::val_int_endpoint(bool left_endp, bool *incl_endp)
col < '2007-09-15 23:00:00' -> YEAR(col) <= 2007
*/
+ const MYSQL_TIME &ltime= dt.get_mysql_time()[0];
if (!left_endp && ltime.day == 1 && ltime.month == 1 &&
- !(ltime.hour || ltime.minute || ltime.second || ltime.second_part))
+ dt.hhmmssff_is_zero())
; /* do nothing */
else
*incl_endp= TRUE;
@@ -1208,14 +1233,13 @@ bool Item_func_unix_timestamp::get_timestamp_value(my_time_t *seconds,
}
}
- MYSQL_TIME ltime;
- if (get_arg0_date(&ltime, TIME_NO_ZERO_IN_DATE))
- return 1;
-
- uint error_code;
- *seconds= TIME_to_timestamp(current_thd, &ltime, &error_code);
- *second_part= ltime.second_part;
- return (null_value= (error_code == ER_WARN_DATA_OUT_OF_RANGE));
+ Timestamp_or_zero_datetime_native_null native(current_thd, args[0], true);
+ if ((null_value= native.is_null() || native.is_zero_datetime()))
+ return true;
+ Timestamp tm(native);
+ *seconds= tm.tv().tv_sec;
+ *second_part= tm.tv().tv_usec;
+ return false;
}
@@ -1272,7 +1296,8 @@ longlong Item_func_unix_timestamp::val_int_endpoint(bool left_endp, bool *incl_e
longlong Item_func_time_to_sec::int_op()
{
DBUG_ASSERT(fixed == 1);
- Time tm(args[0], Time::Options_for_cast());
+ THD *thd= current_thd;
+ Time tm(thd, args[0], Time::Options_for_cast(thd));
return ((null_value= !tm.is_valid_time())) ? 0 : tm.to_seconds();
}
@@ -1280,7 +1305,8 @@ longlong Item_func_time_to_sec::int_op()
my_decimal *Item_func_time_to_sec::decimal_op(my_decimal* buf)
{
DBUG_ASSERT(fixed == 1);
- Time tm(args[0], Time::Options_for_cast());
+ THD *thd= current_thd;
+ Time tm(thd, args[0], Time::Options_for_cast(thd));
if ((null_value= !tm.is_valid_time()))
return 0;
const MYSQL_TIME *ltime= tm.get_mysql_time();
@@ -1295,7 +1321,8 @@ my_decimal *Item_func_time_to_sec::decimal_op(my_decimal* buf)
To make code easy, allow interval objects without separators.
*/
-bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval)
+bool get_interval_value(THD *thd, Item *args,
+ interval_type int_type, INTERVAL *interval)
{
ulonglong array[5];
longlong UNINIT_VAR(value);
@@ -1308,25 +1335,19 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval)
bzero((char*) interval,sizeof(*interval));
if (int_type == INTERVAL_SECOND && args->decimals)
{
- my_decimal decimal_value, *val;
- ulonglong second;
- ulong second_part;
- if (!(val= args->val_decimal(&decimal_value)))
+ VDec val(args);
+ if (val.is_null())
return true;
- interval->neg= my_decimal2seconds(val, &second, &second_part);
- if (second == LONGLONG_MAX)
+ Sec6 d(val.ptr());
+ interval->neg= d.neg();
+ if (d.sec() >= LONGLONG_MAX)
{
- THD *thd= current_thd;
- ErrConvDecimal err(val);
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE,
- ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), "DECIMAL",
- err.ptr());
+ ErrConvDecimal err(val.ptr());
+ thd->push_warning_truncated_wrong_value("seconds", err.ptr());
return true;
}
-
- interval->second= second;
- interval->second_part= second_part;
+ interval->second= d.sec();
+ interval->second_part= d.usec();
return false;
}
else if ((int) int_type <= INTERVAL_MICROSECOND)
@@ -1472,90 +1493,11 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval)
}
-String *Item_temporal_func::val_str(String *str)
-{
- DBUG_ASSERT(fixed == 1);
- return val_string_from_date(str);
-}
-
-
-bool Item_temporal_hybrid_func::fix_temporal_type(MYSQL_TIME *ltime)
-{
- if (ltime->time_type < 0) /* MYSQL_TIMESTAMP_NONE, MYSQL_TIMESTAMP_ERROR */
- return false;
-
- if (ltime->time_type != MYSQL_TIMESTAMP_TIME)
- goto date_or_datetime_value;
-
- /* Convert TIME to DATE or DATETIME */
- switch (field_type())
- {
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_TIMESTAMP:
- {
- MYSQL_TIME tmp;
- if (time_to_datetime_with_warn(current_thd, ltime, &tmp, 0))
- return (null_value= true);
- *ltime= tmp;
- if (field_type() == MYSQL_TYPE_DATE)
- datetime_to_date(ltime);
- return false;
- }
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */
- return false;
- default:
- DBUG_ASSERT(0);
- return (null_value= true);
- }
-
-date_or_datetime_value:
- /* Convert DATE or DATETIME to TIME, DATE, or DATETIME */
- switch (field_type())
- {
- case MYSQL_TYPE_TIME:
- datetime_to_time(ltime);
- return false;
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_TIMESTAMP:
- date_to_datetime(ltime);
- return false;
- case MYSQL_TYPE_DATE:
- datetime_to_date(ltime);
- return false;
- case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */
- return false;
- default:
- DBUG_ASSERT(0);
- return (null_value= true);
- }
- return false;
-}
-
-
-String *Item_temporal_hybrid_func::val_str_ascii(String *str)
-{
- DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
-
- if (get_date(&ltime, 0) || fix_temporal_type(&ltime) ||
- (null_value= my_TIME_to_str(&ltime, str, decimals)))
- return (String *) 0;
-
- /* Check that the returned timestamp type matches to the function type */
- DBUG_ASSERT(field_type() == MYSQL_TYPE_STRING ||
- ltime.time_type == MYSQL_TIMESTAMP_NONE ||
- ltime.time_type == mysql_timestamp_type());
- return str;
-}
-
-
-bool Item_func_from_days::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_func_from_days::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
longlong value=args[0]->val_int();
if ((null_value= (args[0]->null_value ||
- ((fuzzy_date & TIME_NO_ZERO_DATE) && value == 0))))
+ ((fuzzydate & TIME_NO_ZERO_DATE) && value == 0))))
return true;
bzero(ltime, sizeof(MYSQL_TIME));
if (get_date_from_daynr((long) value, &ltime->year, &ltime->month,
@@ -1592,10 +1534,9 @@ void Item_func_curdate_utc::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)
}
-bool Item_func_curdate::get_date(MYSQL_TIME *res,
- ulonglong fuzzy_date __attribute__((unused)))
+bool Item_func_curdate::get_date(THD *thd, MYSQL_TIME *res,
+ date_mode_t fuzzydate __attribute__((unused)))
{
- THD *thd= current_thd;
query_id_t query_id= thd->query_id;
/* Cache value for this query */
if (last_query_id != query_id)
@@ -1622,10 +1563,9 @@ bool Item_func_curtime::fix_fields(THD *thd, Item **items)
return Item_timefunc::fix_fields(thd, items);
}
-bool Item_func_curtime::get_date(MYSQL_TIME *res,
- ulonglong fuzzy_date __attribute__((unused)))
+bool Item_func_curtime::get_date(THD *thd, MYSQL_TIME *res,
+ date_mode_t fuzzydate __attribute__((unused)))
{
- THD *thd= current_thd;
query_id_t query_id= thd->query_id;
/* Cache value for this query */
if (last_query_id != query_id)
@@ -1654,7 +1594,7 @@ static void set_sec_part(ulong sec_part, MYSQL_TIME *ltime, Item *item)
{
ltime->second_part= sec_part;
if (item->decimals < TIME_SECOND_PART_DIGITS)
- my_time_trunc(ltime, item->decimals);
+ my_datetime_trunc(ltime, item->decimals);
}
}
@@ -1696,7 +1636,7 @@ bool Item_func_now::fix_fields(THD *thd, Item **items)
func_name(), TIME_SECOND_PART_DIGITS);
return 1;
}
- return Item_temporal_func::fix_fields(thd, items);
+ return Item_datetimefunc::fix_fields(thd, items);
}
void Item_func_now::print(String *str, enum_query_type query_type)
@@ -1715,15 +1655,14 @@ int Item_func_now_local::save_in_field(Field *field, bool no_conversions)
{
THD *thd= field->get_thd();
my_time_t ts= thd->query_start();
- uint dec= MY_MIN(decimals, field->decimals());
- ulong sec_part= dec ? thd->query_start_sec_part() : 0;
- sec_part-= my_time_fraction_remainder(sec_part, dec);
+ ulong sec_part= decimals ? thd->query_start_sec_part() : 0;
+ sec_part-= my_time_fraction_remainder(sec_part, decimals);
field->set_notnull();
((Field_timestamp*)field)->store_TIME(ts, sec_part);
return 0;
}
else
- return Item_temporal_func::save_in_field(field, no_conversions);
+ return Item_datetimefunc::save_in_field(field, no_conversions);
}
@@ -1754,10 +1693,9 @@ void Item_func_now_utc::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)
}
-bool Item_func_now::get_date(MYSQL_TIME *res,
- ulonglong fuzzy_date __attribute__((unused)))
+bool Item_func_now::get_date(THD *thd, MYSQL_TIME *res,
+ date_mode_t fuzzydate __attribute__((unused)))
{
- THD *thd= current_thd;
query_id_t query_id= thd->query_id;
/* Cache value for this query */
if (last_query_id != query_id)
@@ -1783,62 +1721,23 @@ void Item_func_sysdate_local::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)
}
-bool Item_func_sysdate_local::get_date(MYSQL_TIME *res,
- ulonglong fuzzy_date __attribute__((unused)))
+bool Item_func_sysdate_local::get_date(THD *thd, MYSQL_TIME *res,
+ date_mode_t fuzzydate __attribute__((unused)))
{
- store_now_in_TIME(current_thd, res);
+ store_now_in_TIME(thd, res);
return 0;
}
-bool Item_func_sec_to_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_func_sec_to_time::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed == 1);
- bool sign;
- ulonglong sec;
- ulong sec_part;
-
- bzero((char *)ltime, sizeof(*ltime));
- ltime->time_type= MYSQL_TIMESTAMP_TIME;
-
- sign= args[0]->get_seconds(&sec, &sec_part);
-
- if ((null_value= args[0]->null_value))
- return 1;
-
- ltime->neg= sign;
- if (sec > TIME_MAX_VALUE_SECONDS)
- goto overflow;
-
- DBUG_ASSERT(sec_part <= TIME_MAX_SECOND_PART);
-
- ltime->hour= (uint) (sec/3600);
- ltime->minute= (uint) (sec % 3600) /60;
- ltime->second= (uint) sec % 60;
- ltime->second_part= sec_part;
-
- return 0;
-
-overflow:
- /* use check_time_range() to set ltime to the max value depending on dec */
- int unused;
- char buf[100];
- String tmp(buf, sizeof(buf), &my_charset_bin), *err= args[0]->val_str(&tmp);
-
- ltime->hour= TIME_MAX_HOUR+1;
- check_time_range(ltime, decimals, &unused);
- if (!err)
- {
- ErrConvInteger err2(sec, unsigned_flag);
- make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
- &err2, MYSQL_TIMESTAMP_TIME, NULL, NULL, NULL);
- }
- else
- {
- ErrConvString err2(err);
- make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
- &err2, MYSQL_TIMESTAMP_TIME, NULL, NULL, NULL);
- }
- return 0;
+ VSec9 sec(thd, args[0], "seconds", LONGLONG_MAX);
+ if ((null_value= sec.is_null()))
+ return true;
+ sec.round(decimals, thd->temporal_round_mode());
+ if (sec.sec_to_time(ltime, decimals) && !sec.truncated())
+ sec.make_truncated_warning(thd, "seconds");
+ return false;
}
bool Item_func_date_format::fix_length_and_dec()
@@ -1994,8 +1893,10 @@ String *Item_func_date_format::val_str(String *str)
uint size;
const MY_LOCALE *lc= 0;
DBUG_ASSERT(fixed == 1);
-
- if ((null_value= args[0]->get_date(&l_time, is_time_format ? TIME_TIME_ONLY : 0)))
+ date_conv_mode_t mode= is_time_format ? TIME_TIME_ONLY : TIME_CONV_NONE;
+ THD *thd= current_thd;
+ if ((null_value= args[0]->get_date(thd, &l_time,
+ Temporal::Options(mode, thd))))
return 0;
if (!(format= args[1]->val_str(&format_buffer)) || !format->length())
@@ -2041,35 +1942,34 @@ bool Item_func_from_unixtime::fix_length_and_dec()
}
-bool Item_func_from_unixtime::get_date(MYSQL_TIME *ltime,
- ulonglong fuzzy_date __attribute__((unused)))
+bool Item_func_from_unixtime::get_date(THD *thd, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate __attribute__((unused)))
{
- bool sign;
- ulonglong sec;
- ulong sec_part;
-
bzero((char *)ltime, sizeof(*ltime));
ltime->time_type= MYSQL_TIMESTAMP_TIME;
- sign= args[0]->get_seconds(&sec, &sec_part);
+ VSec9 sec(thd, args[0], "unixtime", TIMESTAMP_MAX_VALUE);
+ DBUG_ASSERT(sec.is_null() || sec.sec() <= TIMESTAMP_MAX_VALUE);
- if (args[0]->null_value || sign || sec > TIMESTAMP_MAX_VALUE)
+ if (sec.is_null() || sec.truncated() || sec.neg())
return (null_value= 1);
- tz->gmt_sec_to_TIME(ltime, (my_time_t)sec);
+ sec.round(MY_MIN(decimals, TIME_SECOND_PART_DIGITS), thd->temporal_round_mode());
+ if (sec.sec() > TIMESTAMP_MAX_VALUE)
+ return (null_value= true); // Went out of range after rounding
- ltime->second_part= sec_part;
+ tz->gmt_sec_to_TIME(ltime, (my_time_t) sec.sec());
+ ltime->second_part= sec.usec();
return (null_value= 0);
}
-bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime,
- ulonglong fuzzy_date __attribute__((unused)))
+bool Item_func_convert_tz::get_date(THD *thd, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate __attribute__((unused)))
{
my_time_t my_time_tmp;
String str;
- THD *thd= current_thd;
if (!from_tz_cached)
{
@@ -2083,9 +1983,13 @@ bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime,
to_tz_cached= args[2]->const_item();
}
- if (from_tz==0 || to_tz==0 ||
- get_arg0_date(ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
- return (null_value= 1);
+ if ((null_value= (from_tz == 0 || to_tz == 0)))
+ return true;
+
+ Datetime::Options opt(TIME_NO_ZEROS, thd);
+ Datetime *dt= new(ltime) Datetime(thd, args[0], opt);
+ if ((null_value= !dt->is_valid_datetime()))
+ return true;
{
uint not_used;
@@ -2105,7 +2009,7 @@ bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime,
void Item_func_convert_tz::cleanup()
{
from_tz_cached= to_tz_cached= 0;
- Item_temporal_func::cleanup();
+ Item_datetimefunc::cleanup();
}
@@ -2136,89 +2040,52 @@ bool Item_date_add_interval::fix_length_and_dec()
MYSQL_TIME or DATETIME argument)
*/
arg0_field_type= args[0]->field_type();
- uint interval_dec= 0;
- if (int_type == INTERVAL_MICROSECOND ||
- (int_type >= INTERVAL_DAY_MICROSECOND &&
- int_type <= INTERVAL_SECOND_MICROSECOND))
- interval_dec= TIME_SECOND_PART_DIGITS;
- else if (int_type == INTERVAL_SECOND && args[1]->decimals > 0)
- interval_dec= MY_MIN(args[1]->decimals, TIME_SECOND_PART_DIGITS);
if (arg0_field_type == MYSQL_TYPE_DATETIME ||
arg0_field_type == MYSQL_TYPE_TIMESTAMP)
{
- uint dec= MY_MAX(args[0]->datetime_precision(), interval_dec);
- set_handler(&type_handler_datetime);
- fix_attributes_datetime(dec);
+ set_func_handler(&func_handler_date_add_interval_datetime);
}
else if (arg0_field_type == MYSQL_TYPE_DATE)
{
if (int_type <= INTERVAL_DAY || int_type == INTERVAL_YEAR_MONTH)
- {
- set_handler(&type_handler_newdate);
- fix_attributes_date();
- }
+ set_func_handler(&func_handler_date_add_interval_date);
else
- {
- set_handler(&type_handler_datetime2);
- fix_attributes_datetime(interval_dec);
- }
+ set_func_handler(&func_handler_date_add_interval_datetime);
}
else if (arg0_field_type == MYSQL_TYPE_TIME)
{
- uint dec= MY_MAX(args[0]->time_precision(), interval_dec);
if (int_type >= INTERVAL_DAY && int_type != INTERVAL_YEAR_MONTH)
- {
- set_handler(&type_handler_time2);
- fix_attributes_time(dec);
- }
+ set_func_handler(&func_handler_date_add_interval_time);
else
- {
- set_handler(&type_handler_datetime2);
- fix_attributes_datetime(dec);
- }
+ set_func_handler(&func_handler_date_add_interval_datetime_arg0_time);
}
else
{
- uint dec= MY_MAX(args[0]->datetime_precision(), interval_dec);
- set_handler(&type_handler_string);
- collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
- fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec);
+ set_func_handler(&func_handler_date_add_interval_string);
}
maybe_null= true;
- return FALSE;
+ return m_func_handler->fix_length_and_dec(this);
}
-bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Func_handler_date_add_interval_datetime_arg0_time::
+ get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const
{
- INTERVAL interval;
-
- if (args[0]->get_date(ltime,
- field_type() == MYSQL_TYPE_TIME ?
- TIME_TIME_ONLY : 0) ||
- get_interval_value(args[1], int_type, &interval))
- return (null_value=1);
-
- if (ltime->time_type != MYSQL_TIMESTAMP_TIME &&
- check_date_with_warn(ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE,
- MYSQL_TIMESTAMP_ERROR))
- return (null_value=1);
-
- if (date_sub_interval)
- interval.neg = !interval.neg;
-
- if (date_add_interval(ltime, int_type, interval))
- return (null_value=1);
- return (null_value= 0);
+ // time_expr + INTERVAL {YEAR|QUARTER|MONTH|WEEK|YEAR_MONTH}
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_DATETIME_FUNCTION_OVERFLOW,
+ ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW), "time");
+ return (item->null_value= true);
}
bool Item_date_add_interval::eq(const Item *item, bool binary_cmp) const
{
- Item_date_add_interval *other= (Item_date_add_interval*) item;
if (!Item_func::eq(item, binary_cmp))
return 0;
+ Item_date_add_interval *other= (Item_date_add_interval*) item;
return ((int_type == other->int_type) &&
(date_sub_interval == other->date_sub_interval));
}
@@ -2260,16 +2127,18 @@ void Item_extract::print(String *str, enum_query_type query_type)
bool Item_extract::fix_length_and_dec()
{
maybe_null=1; // If wrong date
+ uint32 daylen= args[0]->cmp_type() == TIME_RESULT ? 2 :
+ TIME_MAX_INTERVAL_DAY_CHAR_LENGTH;
switch (int_type) {
case INTERVAL_YEAR: set_date_length(4); break; // YYYY
case INTERVAL_YEAR_MONTH: set_date_length(6); break; // YYYYMM
case INTERVAL_QUARTER: set_date_length(2); break; // 1..4
case INTERVAL_MONTH: set_date_length(2); break; // MM
case INTERVAL_WEEK: set_date_length(2); break; // 0..52
- case INTERVAL_DAY: set_date_length(2); break; // DD
- case INTERVAL_DAY_HOUR: set_time_length(4); break; // DDhh
- case INTERVAL_DAY_MINUTE: set_time_length(6); break; // DDhhmm
- case INTERVAL_DAY_SECOND: set_time_length(8); break; // DDhhmmss
+ case INTERVAL_DAY: set_day_length(daylen); break; // DD
+ case INTERVAL_DAY_HOUR: set_day_length(daylen+2); break; // DDhh
+ case INTERVAL_DAY_MINUTE: set_day_length(daylen+4); break; // DDhhmm
+ case INTERVAL_DAY_SECOND: set_day_length(daylen+6); break; // DDhhmmss
case INTERVAL_HOUR: set_time_length(2); break; // hh
case INTERVAL_HOUR_MINUTE: set_time_length(4); break; // hhmm
case INTERVAL_HOUR_SECOND: set_time_length(6); break; // hhmmss
@@ -2277,7 +2146,7 @@ bool Item_extract::fix_length_and_dec()
case INTERVAL_MINUTE_SECOND: set_time_length(4); break; // mmss
case INTERVAL_SECOND: set_time_length(2); break; // ss
case INTERVAL_MICROSECOND: set_time_length(6); break; // ffffff
- case INTERVAL_DAY_MICROSECOND: set_time_length(14); break; // DDhhmmssffffff
+ case INTERVAL_DAY_MICROSECOND: set_time_length(daylen+12); break; // DDhhmmssffffff
case INTERVAL_HOUR_MICROSECOND: set_time_length(12); break; // hhmmssffffff
case INTERVAL_MINUTE_MICROSECOND: set_time_length(10); break; // mmssffffff
case INTERVAL_SECOND_MICROSECOND: set_time_length(8); break; // ssffffff
@@ -2287,69 +2156,46 @@ bool Item_extract::fix_length_and_dec()
}
-longlong Item_extract::val_int()
+uint Extract_source::week(THD *thd) const
{
- DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
+ DBUG_ASSERT(is_valid_extract_source());
uint year;
- ulong week_format;
- long neg;
- int is_time_flag = date_value ? 0 : TIME_TIME_ONLY;
-
- // Not using get_arg0_date to avoid automatic TIME to DATETIME conversion
- if ((null_value= args[0]->get_date(&ltime, is_time_flag)))
- return 0;
-
- neg= ltime.neg ? -1 : 1;
+ ulong week_format= current_thd->variables.default_week_format;
+ return calc_week(this, week_mode(week_format), &year);
+}
- DBUG_ASSERT(ltime.time_type != MYSQL_TIMESTAMP_TIME || ltime.day == 0);
- if (ltime.time_type == MYSQL_TIMESTAMP_TIME)
- time_to_daytime_interval(&ltime);
+longlong Item_extract::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ THD *thd= current_thd;
+ Extract_source dt(thd, args[0], m_date_mode);
+ if ((null_value= !dt.is_valid_extract_source()))
+ return 0;
switch (int_type) {
- case INTERVAL_YEAR: return ltime.year;
- case INTERVAL_YEAR_MONTH: return ltime.year*100L+ltime.month;
- case INTERVAL_QUARTER: return (ltime.month+2)/3;
- case INTERVAL_MONTH: return ltime.month;
- case INTERVAL_WEEK:
- {
- week_format= current_thd->variables.default_week_format;
- return calc_week(&ltime, week_mode(week_format), &year);
- }
- case INTERVAL_DAY: return ltime.day;
- case INTERVAL_DAY_HOUR: return (long) (ltime.day*100L+ltime.hour)*neg;
- case INTERVAL_DAY_MINUTE: return (long) (ltime.day*10000L+
- ltime.hour*100L+
- ltime.minute)*neg;
- case INTERVAL_DAY_SECOND: return ((longlong) ltime.day*1000000L+
- (longlong) (ltime.hour*10000L+
- ltime.minute*100+
- ltime.second))*neg;
- case INTERVAL_HOUR: return (long) ltime.hour*neg;
- case INTERVAL_HOUR_MINUTE: return (long) (ltime.hour*100+ltime.minute)*neg;
- case INTERVAL_HOUR_SECOND: return (long) (ltime.hour*10000+ltime.minute*100+
- ltime.second)*neg;
- case INTERVAL_MINUTE: return (long) ltime.minute*neg;
- case INTERVAL_MINUTE_SECOND: return (long) (ltime.minute*100+ltime.second)*neg;
- case INTERVAL_SECOND: return (long) ltime.second*neg;
- case INTERVAL_MICROSECOND: return (long) ltime.second_part*neg;
- case INTERVAL_DAY_MICROSECOND: return (((longlong)ltime.day*1000000L +
- (longlong)ltime.hour*10000L +
- ltime.minute*100 +
- ltime.second)*1000000L +
- ltime.second_part)*neg;
- case INTERVAL_HOUR_MICROSECOND: return (((longlong)ltime.hour*10000L +
- ltime.minute*100 +
- ltime.second)*1000000L +
- ltime.second_part)*neg;
- case INTERVAL_MINUTE_MICROSECOND: return (((longlong)(ltime.minute*100+
- ltime.second))*1000000L+
- ltime.second_part)*neg;
- case INTERVAL_SECOND_MICROSECOND: return ((longlong)ltime.second*1000000L+
- ltime.second_part)*neg;
+ case INTERVAL_YEAR: return dt.year();
+ case INTERVAL_YEAR_MONTH: return dt.year_month();
+ case INTERVAL_QUARTER: return dt.quarter();
+ case INTERVAL_MONTH: return dt.month();
+ case INTERVAL_WEEK: return dt.week(thd);
+ case INTERVAL_DAY: return dt.day();
+ case INTERVAL_DAY_HOUR: return dt.day_hour();
+ case INTERVAL_DAY_MINUTE: return dt.day_minute();
+ case INTERVAL_DAY_SECOND: return dt.day_second();
+ case INTERVAL_HOUR: return dt.hour();
+ case INTERVAL_HOUR_MINUTE: return dt.hour_minute();
+ case INTERVAL_HOUR_SECOND: return dt.hour_second();
+ case INTERVAL_MINUTE: return dt.minute();
+ case INTERVAL_MINUTE_SECOND: return dt.minute_second();
+ case INTERVAL_SECOND: return dt.second();
+ case INTERVAL_MICROSECOND: return dt.microsecond();
+ case INTERVAL_DAY_MICROSECOND: return dt.day_microsecond();
+ case INTERVAL_HOUR_MICROSECOND: return dt.hour_microsecond();
+ case INTERVAL_MINUTE_MICROSECOND: return dt.minute_microsecond();
+ case INTERVAL_SECOND_MICROSECOND: return dt.second_microsecond();
case INTERVAL_LAST: DBUG_ASSERT(0); break; /* purecov: deadcode */
}
- return 0; // Impossible
+ return 0; // Impossible
}
bool Item_extract::eq(const Item *item, bool binary_cmp) const
@@ -2388,13 +2234,14 @@ bool Item_char_typecast::eq(const Item *item, bool binary_cmp) const
return 1;
}
-void Item_temporal_typecast::print(String *str, enum_query_type query_type)
+void Item_func::print_cast_temporal(String *str, enum_query_type query_type)
{
char buf[32];
str->append(STRING_WITH_LEN("cast("));
args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as "));
- str->append(cast_type());
+ const Name name= type_handler()->name();
+ str->append(name.ptr(), name.length());
if (decimals && decimals != NOT_FIXED_DEC)
{
str->append('(');
@@ -2607,45 +2454,47 @@ void Item_char_typecast::fix_length_and_dec_internal(CHARSET_INFO *from_cs)
}
-bool Item_time_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_time_typecast::get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode)
{
- Time tm(args[0], Time::Options_for_cast());
- if ((null_value= !tm.is_valid_time()))
- return true;
- tm.copy_to_mysql_time(ltime);
- if (decimals < TIME_SECOND_PART_DIGITS)
- my_time_trunc(ltime, decimals);
- return (fuzzy_date & TIME_TIME_ONLY) ? 0 :
- (null_value= check_date_with_warn(ltime, fuzzy_date,
- MYSQL_TIMESTAMP_ERROR));
+ Time *tm= new(to) Time(thd, args[0], Time::Options_for_cast(mode, thd),
+ MY_MIN(decimals, TIME_SECOND_PART_DIGITS));
+ return (null_value= !tm->is_valid_time());
}
-bool Item_date_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+Sql_mode_dependency Item_time_typecast::value_depends_on_sql_mode() const
{
- fuzzy_date |= sql_mode_for_dates(current_thd);
- if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY))
- return 1;
+ return Item_timefunc::value_depends_on_sql_mode() |
+ Sql_mode_dependency(decimals < args[0]->decimals ?
+ MODE_TIME_ROUND_FRACTIONAL : 0, 0);
+}
- if (make_date_with_warn(ltime, fuzzy_date, MYSQL_TIMESTAMP_DATE))
- return (null_value= 1);
- return 0;
+bool Item_date_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+{
+ date_mode_t tmp= (fuzzydate | sql_mode_for_dates(thd)) & ~TIME_TIME_ONLY;
+ // Force truncation
+ Date *d= new(ltime) Date(thd, args[0], Date::Options(date_conv_mode_t(tmp)));
+ return (null_value= !d->is_valid_date());
}
-bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_datetime_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- fuzzy_date |= sql_mode_for_dates(current_thd);
- if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY))
- return 1;
+ date_mode_t tmp= (fuzzydate | sql_mode_for_dates(thd)) & ~TIME_TIME_ONLY;
+ // Force rounding if the current sql_mode says so
+ Datetime::Options opt(date_conv_mode_t(tmp), thd);
+ Datetime *dt= new(ltime) Datetime(thd, args[0], opt,
+ MY_MIN(decimals, TIME_SECOND_PART_DIGITS));
+ return (null_value= !dt->is_valid_datetime());
+}
- if (decimals < TIME_SECOND_PART_DIGITS)
- my_time_trunc(ltime, decimals);
- DBUG_ASSERT(ltime->time_type != MYSQL_TIMESTAMP_TIME);
- ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
- return 0;
+Sql_mode_dependency Item_datetime_typecast::value_depends_on_sql_mode() const
+{
+ return Item_datetimefunc::value_depends_on_sql_mode() |
+ Sql_mode_dependency(decimals < args[0]->decimals ?
+ MODE_TIME_ROUND_FRACTIONAL : 0, 0);
}
@@ -2660,20 +2509,17 @@ bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
0099-12-31
*/
-bool Item_func_makedate::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_func_makedate::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed == 1);
- long daynr= (long) args[1]->val_int();
- long year= (long) args[0]->val_int();
- long days;
+ long year, days, daynr= (long) args[1]->val_int();
- if (args[0]->null_value || args[1]->null_value ||
- year < 0 || year > 9999 || daynr <= 0)
+ VYear vyear(args[0]);
+ if (vyear.is_null() || args[1]->null_value || vyear.truncated() || daynr <= 0)
goto err;
- if (year < 100)
+ if ((year= (long) vyear.year()) < 100)
year= year_2000_handling(year);
-
days= calc_daynr(year,1,1) + daynr - 1;
if (get_date_from_daynr(days, &ltime->year, &ltime->month, &ltime->day))
goto err;
@@ -2712,101 +2558,24 @@ bool Item_func_add_time::fix_length_and_dec()
arg0_field_type= args[0]->field_type();
if (arg0_field_type == MYSQL_TYPE_DATE ||
arg0_field_type == MYSQL_TYPE_DATETIME ||
- arg0_field_type == MYSQL_TYPE_TIMESTAMP ||
- is_date)
+ arg0_field_type == MYSQL_TYPE_TIMESTAMP)
{
- uint dec= MY_MAX(args[0]->datetime_precision(), args[1]->time_precision());
- set_handler(&type_handler_datetime2);
- fix_attributes_datetime(dec);
+ set_func_handler(sign > 0 ? &func_handler_add_time_datetime_add :
+ &func_handler_add_time_datetime_sub);
}
else if (arg0_field_type == MYSQL_TYPE_TIME)
{
- uint dec= MY_MAX(args[0]->time_precision(), args[1]->time_precision());
- set_handler(&type_handler_time2);
- fix_attributes_time(dec);
+ set_func_handler(sign > 0 ? &func_handler_add_time_time_add :
+ &func_handler_add_time_time_sub);
}
else
{
- uint dec= MY_MAX(args[0]->decimals, args[1]->decimals);
- set_handler(&type_handler_string);
- collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
- fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec);
+ set_func_handler(sign > 0 ? &func_handler_add_time_string_add :
+ &func_handler_add_time_string_sub);
}
- maybe_null= true;
- return FALSE;
-}
-
-/**
- ADDTIME(t,a) and SUBTIME(t,a) are time functions that calculate a
- time/datetime value
-
- t: time_or_datetime_expression
- a: time_expression
-
- Result: Time value or datetime value
-*/
-
-bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
-{
- DBUG_ASSERT(fixed == 1);
- MYSQL_TIME l_time1, l_time2;
- bool is_time= 0;
- long days, microseconds;
- longlong seconds;
- int l_sign= sign;
-
- if (Item_func_add_time::field_type() == MYSQL_TYPE_DATETIME)
- {
- // TIMESTAMP function OR the first argument is DATE/DATETIME/TIMESTAMP
- if (get_arg0_date(&l_time1, 0) ||
- args[1]->get_time(&l_time2) ||
- l_time1.time_type == MYSQL_TIMESTAMP_TIME ||
- l_time2.time_type != MYSQL_TIMESTAMP_TIME)
- return (null_value= 1);
- }
- else
- {
- // ADDTIME function AND the first argument is TIME
- if (args[0]->get_time(&l_time1) ||
- args[1]->get_time(&l_time2) ||
- l_time2.time_type != MYSQL_TIMESTAMP_TIME)
- return (null_value= 1);
- is_time= (l_time1.time_type == MYSQL_TIMESTAMP_TIME);
- }
- if (l_time1.neg != l_time2.neg)
- l_sign= -l_sign;
-
- bzero(ltime, sizeof(*ltime));
-
- ltime->neg= calc_time_diff(&l_time1, &l_time2, -l_sign,
- &seconds, &microseconds);
-
- /*
- If first argument was negative and diff between arguments
- is non-zero we need to swap sign to get proper result.
- */
- if (l_time1.neg && (seconds || microseconds))
- ltime->neg= 1-ltime->neg; // Swap sign of result
-
- if (!is_time && ltime->neg)
- return (null_value= 1);
-
- days= (long) (seconds / SECONDS_IN_24H);
-
- calc_time_from_sec(ltime, (long)(seconds % SECONDS_IN_24H), microseconds);
- ltime->time_type= is_time ? MYSQL_TIMESTAMP_TIME : MYSQL_TIMESTAMP_DATETIME;
-
- if (!is_time)
- {
- if (get_date_from_daynr(days,&ltime->year,&ltime->month,&ltime->day) ||
- !ltime->day)
- return (null_value= 1);
- return (null_value= 0);
- }
-
- ltime->hour+= days*24;
- return (null_value= adjust_time_range_with_warn(ltime, decimals));
+ maybe_null= true;
+ return m_func_handler->fix_length_and_dec(this);
}
@@ -2818,7 +2587,7 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
Result: Time value
*/
-bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_func_timediff::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed == 1);
int l_sign= 1;
@@ -2826,55 +2595,47 @@ bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
ErrConvTime str(&l_time3);
/* the following may be true in, for example, date_add(timediff(...), ... */
- if (fuzzy_date & TIME_NO_ZERO_IN_DATE)
+ if (fuzzydate & TIME_NO_ZERO_IN_DATE)
return (null_value= 1);
- if (args[0]->get_time(&l_time1) ||
- args[1]->get_time(&l_time2) ||
+ if (args[0]->get_time(thd, &l_time1) ||
+ args[1]->get_time(thd, &l_time2) ||
l_time1.time_type != l_time2.time_type)
return (null_value= 1);
if (l_time1.neg != l_time2.neg)
l_sign= -l_sign;
- if (calc_time_diff(&l_time1, &l_time2, l_sign, &l_time3, fuzzy_date))
+ if (calc_time_diff(&l_time1, &l_time2, l_sign, &l_time3, fuzzydate))
return (null_value= 1);
*ltime= l_time3;
- return (null_value= adjust_time_range_with_warn(ltime, decimals));
+ return (null_value= adjust_time_range_with_warn(thd, ltime, decimals));
}
+
/**
MAKETIME(h,m,s) is a time function that calculates a time value
from the total number of hours, minutes, and seconds.
Result: Time value
*/
-bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_func_maketime::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed == 1);
Longlong_hybrid hour(args[0]->val_int(), args[0]->unsigned_flag);
longlong minute= args[1]->val_int();
- ulonglong second;
- ulong microsecond;
- bool neg= args[2]->get_seconds(&second, &microsecond);
+ VSec9 sec(thd, args[2], "seconds", 59);
- if (args[0]->null_value || args[1]->null_value || args[2]->null_value ||
- minute < 0 || minute > 59 || neg || second > 59)
+ DBUG_ASSERT(sec.is_null() || sec.sec() <= 59);
+ if (args[0]->null_value || args[1]->null_value || sec.is_null() ||
+ minute < 0 || minute > 59 || sec.neg() || sec.truncated())
return (null_value= 1);
- bzero(ltime, sizeof(*ltime));
- ltime->time_type= MYSQL_TIMESTAMP_TIME;
- ltime->neg= hour.neg();
-
- if (hour.abs() <= TIME_MAX_HOUR)
- {
- ltime->hour= (uint) hour.abs();
- ltime->minute= (uint) minute;
- ltime->second= (uint) second;
- ltime->second_part= microsecond;
- }
- else
+ int warn;
+ new(ltime) Time(&warn, hour.neg(), hour.abs(), (uint) minute,
+ sec.to_const_sec9(), thd->temporal_round_mode(), decimals);
+ if (warn)
{
// use check_time_range() to set ltime to the max value depending on dec
int unused;
@@ -2882,10 +2643,10 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
check_time_range(ltime, decimals, &unused);
char buf[28];
char *ptr= longlong10_to_str(hour.value(), buf, hour.is_unsigned() ? 10 : -10);
- int len = (int)(ptr - buf) + sprintf(ptr, ":%02u:%02u", (uint)minute, (uint)second);
- make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
- buf, len, MYSQL_TIMESTAMP_TIME,
- NULL, NULL, NULL);
+ int len = (int)(ptr - buf) + sprintf(ptr, ":%02u:%02u",
+ (uint) minute, (uint) sec.sec());
+ ErrConvString err(buf, len, &my_charset_bin);
+ thd->push_warning_truncated_wrong_value("time", err.ptr());
}
return (null_value= 0);
@@ -2903,7 +2664,8 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
longlong Item_func_microsecond::val_int()
{
DBUG_ASSERT(fixed == 1);
- Time tm(args[0], Time::Options_for_cast());
+ THD *thd= current_thd;
+ Time tm(thd, args[0], Time::Options_for_cast(thd));
return ((null_value= !tm.is_valid_time())) ?
0 : tm.get_mysql_time()->second_part;
}
@@ -2912,17 +2674,17 @@ longlong Item_func_microsecond::val_int()
longlong Item_func_timestamp_diff::val_int()
{
MYSQL_TIME ltime1, ltime2;
- longlong seconds;
- long microseconds;
+ ulonglong seconds;
+ ulong microseconds;
long months= 0;
int neg= 1;
THD *thd= current_thd;
- ulonglong fuzzydate= TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE;
+ Datetime::Options opt(TIME_NO_ZEROS, thd);
null_value= 0;
- if (Datetime(thd, args[0], fuzzydate).copy_to_mysql_time(&ltime1) ||
- Datetime(thd, args[1], fuzzydate).copy_to_mysql_time(&ltime2))
+ if (Datetime(thd, args[0], opt).copy_to_mysql_time(&ltime1) ||
+ Datetime(thd, args[1], opt).copy_to_mysql_time(&ltime2))
goto null_date;
if (calc_time_diff(&ltime2,&ltime1, 1,
@@ -2992,21 +2754,21 @@ longlong Item_func_timestamp_diff::val_int()
case INTERVAL_MONTH:
return months*neg;
case INTERVAL_WEEK:
- return seconds / SECONDS_IN_24H / 7L * neg;
+ return ((longlong) (seconds / SECONDS_IN_24H / 7L)) * neg;
case INTERVAL_DAY:
- return seconds / SECONDS_IN_24H * neg;
+ return ((longlong) (seconds / SECONDS_IN_24H)) * neg;
case INTERVAL_HOUR:
- return seconds/3600L*neg;
+ return ((longlong) (seconds / 3600L)) * neg;
case INTERVAL_MINUTE:
- return seconds/60L*neg;
+ return ((longlong) (seconds / 60L)) * neg;
case INTERVAL_SECOND:
- return seconds*neg;
+ return ((longlong) seconds) * neg;
case INTERVAL_MICROSECOND:
/*
In MySQL difference between any two valid datetime values
in microseconds fits into longlong.
*/
- return (seconds*1000000L+microseconds)*neg;
+ return ((longlong) ((ulonglong) seconds * 1000000L + microseconds)) * neg;
default:
break;
}
@@ -3136,15 +2898,10 @@ void Item_func_get_format::print(String *str, enum_query_type query_type)
specifiers supported by extract_date_time() function.
@return
- One of date_time_format_types values:
- - DATE_TIME_MICROSECOND
- - DATE_TIME
- - DATE_ONLY
- - TIME_MICROSECOND
- - TIME_ONLY
+ A function handler corresponding the given format
*/
-static date_time_format_types
+static const Item_handled_func::Handler *
get_date_time_result_type(const char *format, uint length)
{
const char *time_part_frms= "HISThiklrs";
@@ -3171,21 +2928,21 @@ get_date_time_result_type(const char *format, uint length)
frac_second_used implies time_part_used, and thus we already
have all types of date-time components and can end our search.
*/
- return DATE_TIME_MICROSECOND;
+ return &func_handler_str_to_date_datetime_usec;
}
}
}
/* We don't have all three types of date-time components */
if (frac_second_used)
- return TIME_MICROSECOND;
+ return &func_handler_str_to_date_time_usec;
if (time_part_used)
{
if (date_part_used)
- return DATE_TIME;
- return TIME_ONLY;
+ return &func_handler_str_to_date_datetime_sec;
+ return &func_handler_str_to_date_time_sec;
}
- return DATE_ONLY;
+ return &func_handler_str_to_date_date;
}
@@ -3205,56 +2962,27 @@ bool Item_func_str_to_date::fix_length_and_dec()
internal_charset= &my_charset_utf8mb4_general_ci;
maybe_null= true;
- set_handler(&type_handler_datetime2);
- fix_attributes_datetime(TIME_SECOND_PART_DIGITS);
+ set_func_handler(&func_handler_str_to_date_datetime_usec);
if ((const_item= args[1]->const_item()))
{
- char format_buff[64];
- String format_str(format_buff, sizeof(format_buff), &my_charset_bin);
+ StringBuffer<64> format_str;
String *format= args[1]->val_str(&format_str, &format_converter,
internal_charset);
- decimals= 0;
if (!args[1]->null_value)
- {
- date_time_format_types cached_format_type=
- get_date_time_result_type(format->ptr(), format->length());
- switch (cached_format_type) {
- case DATE_ONLY:
- set_handler(&type_handler_newdate);
- fix_attributes_date();
- break;
- case TIME_MICROSECOND:
- set_handler(&type_handler_time2);
- fix_attributes_time(TIME_SECOND_PART_DIGITS);
- break;
- case TIME_ONLY:
- set_handler(&type_handler_time2);
- fix_attributes_time(0);
- break;
- case DATE_TIME_MICROSECOND:
- set_handler(&type_handler_datetime2);
- fix_attributes_datetime(TIME_SECOND_PART_DIGITS);
- break;
- case DATE_TIME:
- set_handler(&type_handler_datetime2);
- fix_attributes_datetime(0);
- break;
- }
- }
+ set_func_handler(get_date_time_result_type(format->ptr(), format->length()));
}
- cached_timestamp_type= mysql_timestamp_type();
- return FALSE;
+ return m_func_handler->fix_length_and_dec(this);
}
-bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_func_str_to_date::get_date_common(THD *thd, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate,
+ timestamp_type tstype)
{
DATE_TIME_FORMAT date_time_format;
- char val_buff[64], format_buff[64];
- String val_string(val_buff, sizeof(val_buff), &my_charset_bin), *val;
- String format_str(format_buff, sizeof(format_buff), &my_charset_bin),
- *format;
+ StringBuffer<64> val_string, format_str;
+ String *val, *format;
val= args[0]->val_str(&val_string, &subject_converter, internal_charset);
format= args[1]->val_str(&format_str, &format_converter, internal_charset);
@@ -3263,29 +2991,22 @@ bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
date_time_format.format.str= (char*) format->ptr();
date_time_format.format.length= format->length();
- if (extract_date_time(&date_time_format, val->ptr(), val->length(),
- ltime, cached_timestamp_type, 0, "datetime",
- fuzzy_date | sql_mode_for_dates(current_thd)))
+ if (extract_date_time(thd, &date_time_format, val->ptr(), val->length(),
+ ltime, tstype, 0, "datetime",
+ date_conv_mode_t(fuzzydate) |
+ sql_mode_for_dates(thd)))
return (null_value=1);
- if (cached_timestamp_type == MYSQL_TIMESTAMP_TIME && ltime->day)
- {
- /*
- Day part for time type can be nonzero value and so
- we should add hours from day part to hour part to
- keep valid time value.
- */
- ltime->hour+= ltime->day*24;
- ltime->day= 0;
- }
return (null_value= 0);
}
-bool Item_func_last_day::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+bool Item_func_last_day::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY) ||
- (ltime->month == 0))
- return (null_value=1);
+ Datetime::Options opt(date_conv_mode_t(fuzzydate & ~TIME_TIME_ONLY),
+ time_round_mode_t(fuzzydate));
+ Datetime *d= new(ltime) Datetime(thd, args[0], opt);
+ if ((null_value= (!d->is_valid_datetime() || ltime->month == 0)))
+ return true;
uint month_idx= ltime->month-1;
ltime->day= days_in_month[month_idx];
if ( month_idx == 1 && calc_days_in_year(ltime->year) == 366)
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 518b573173f..9ee561f6abb 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -25,13 +25,9 @@
class MY_LOCALE;
-enum date_time_format_types
-{
- TIME_ONLY= 0, TIME_MICROSECOND, DATE_ONLY, DATE_TIME, DATE_TIME_MICROSECOND
-};
-
-bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval);
+bool get_interval_value(THD *thd, Item *args,
+ interval_type int_type, INTERVAL *interval);
class Item_long_func_date_field: public Item_long_func
@@ -437,9 +433,9 @@ public:
{
return (odbc_type ? "dayofweek" : "weekday");
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate);
}
bool fix_length_and_dec()
{
@@ -499,7 +495,7 @@ public:
}
double real_op() { DBUG_ASSERT(0); return 0; }
String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
- bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(0);
return true;
@@ -535,7 +531,8 @@ public:
}
bool fix_length_and_dec()
{
- fix_length_and_dec_generic(arg_count ? args[0]->datetime_precision() : 0);
+ fix_length_and_dec_generic(arg_count ?
+ args[0]->datetime_precision(current_thd) : 0);
return FALSE;
}
longlong int_op();
@@ -559,7 +556,7 @@ public:
}
bool fix_length_and_dec()
{
- fix_length_and_dec_generic(args[0]->time_precision());
+ fix_length_and_dec_generic(args[0]->time_precision(current_thd));
return FALSE;
}
longlong int_op();
@@ -569,66 +566,17 @@ public:
};
-class Item_temporal_func: public Item_func
+class Item_datefunc :public Item_func
{
public:
- Item_temporal_func(THD *thd): Item_func(thd) {}
- Item_temporal_func(THD *thd, Item *a): Item_func(thd, a) {}
- Item_temporal_func(THD *thd, Item *a, Item *b): Item_func(thd, a, b) {}
- Item_temporal_func(THD *thd, Item *a, Item *b, Item *c): Item_func(thd, a, b, c) {}
- String *val_str(String *str);
- longlong val_int() { return val_int_from_date(); }
- double val_real() { return val_real_from_date(); }
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date) { DBUG_ASSERT(0); return 1; }
- my_decimal *val_decimal(my_decimal *decimal_value)
- { return val_decimal_from_date(decimal_value); }
-};
-
-
-/**
- Abstract class for functions returning TIME, DATE, DATETIME or string values,
- whose data type depends on parameters and is set at fix_fields time.
-*/
-class Item_temporal_hybrid_func: public Item_hybrid_func
-{
-protected:
- String ascii_buf; // Conversion buffer
-public:
- Item_temporal_hybrid_func(THD *thd, Item *a, Item *b):
- Item_hybrid_func(thd, a, b) {}
-
- longlong val_int() { return val_int_from_date(); }
- double val_real() { return val_real_from_date(); }
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date)= 0;
- my_decimal *val_decimal(my_decimal *decimal_value)
- { return val_decimal_from_date(decimal_value); }
-
- /**
- Fix the returned timestamp to match field_type(),
- which is important for val_str().
- */
- bool fix_temporal_type(MYSQL_TIME *ltime);
- /**
- Return string value in ASCII character set.
- */
- String *val_str_ascii(String *str);
- /**
- Return string value in @@character_set_connection.
- */
- String *val_str(String *str)
- {
- return val_str_from_val_str_ascii(str, &ascii_buf);
- }
-};
-
-
-class Item_datefunc :public Item_temporal_func
-{
-public:
- Item_datefunc(THD *thd): Item_temporal_func(thd) { }
- Item_datefunc(THD *thd, Item *a): Item_temporal_func(thd, a) { }
- Item_datefunc(THD *thd, Item *a, Item *b): Item_temporal_func(thd, a, b) { }
+ Item_datefunc(THD *thd): Item_func(thd) { }
+ Item_datefunc(THD *thd, Item *a): Item_func(thd, a) { }
+ Item_datefunc(THD *thd, Item *a, Item *b): Item_func(thd, a, b) { }
const Type_handler *type_handler() const { return &type_handler_newdate; }
+ longlong val_int() { return Date(this).to_longlong(); }
+ double val_real() { return Date(this).to_double(); }
+ String *val_str(String *to) { return Date(this).to_string(to); }
+ my_decimal *val_decimal(my_decimal *to) { return Date(this).to_decimal(to); }
bool fix_length_and_dec()
{
fix_attributes_date();
@@ -638,26 +586,38 @@ public:
};
-class Item_timefunc :public Item_temporal_func
+class Item_timefunc :public Item_func
{
public:
- Item_timefunc(THD *thd): Item_temporal_func(thd) {}
- Item_timefunc(THD *thd, Item *a): Item_temporal_func(thd, a) {}
- Item_timefunc(THD *thd, Item *a, Item *b): Item_temporal_func(thd, a, b) {}
- Item_timefunc(THD *thd, Item *a, Item *b, Item *c):
- Item_temporal_func(thd, a, b ,c) {}
+ Item_timefunc(THD *thd): Item_func(thd) {}
+ Item_timefunc(THD *thd, Item *a): Item_func(thd, a) {}
+ Item_timefunc(THD *thd, Item *a, Item *b): Item_func(thd, a, b) {}
+ Item_timefunc(THD *thd, Item *a, Item *b, Item *c): Item_func(thd, a, b ,c) {}
const Type_handler *type_handler() const { return &type_handler_time2; }
+ longlong val_int() { return Time(this).to_longlong(); }
+ double val_real() { return Time(this).to_double(); }
+ String *val_str(String *to) { return Time(this).to_string(to, decimals); }
+ my_decimal *val_decimal(my_decimal *to) { return Time(this).to_decimal(to); }
+ bool val_native(THD *thd, Native *to)
+ {
+ return Time(thd, this).to_native(to, decimals);
+ }
};
-class Item_datetimefunc :public Item_temporal_func
+class Item_datetimefunc :public Item_func
{
public:
- Item_datetimefunc(THD *thd): Item_temporal_func(thd) {}
- Item_datetimefunc(THD *thd, Item *a): Item_temporal_func(thd, a) {}
+ Item_datetimefunc(THD *thd): Item_func(thd) {}
+ Item_datetimefunc(THD *thd, Item *a): Item_func(thd, a) {}
+ Item_datetimefunc(THD *thd, Item *a, Item *b): Item_func(thd, a, b) {}
Item_datetimefunc(THD *thd, Item *a, Item *b, Item *c):
- Item_temporal_func(thd, a, b ,c) {}
+ Item_func(thd, a, b ,c) {}
const Type_handler *type_handler() const { return &type_handler_datetime2; }
+ longlong val_int() { return Datetime(this).to_longlong(); }
+ double val_real() { return Datetime(this).to_double(); }
+ String *val_str(String *to) { return Datetime(this).to_string(to, decimals); }
+ my_decimal *val_decimal(my_decimal *to) { return Datetime(this).to_decimal(to); }
};
@@ -672,7 +632,7 @@ public:
{ decimals= dec; }
bool fix_fields(THD *, Item **);
bool fix_length_and_dec() { fix_attributes_time(decimals); return FALSE; }
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
/*
Abstract method that defines which time zone is used for conversion.
Converts time current time in my_time_t representation to broken-down
@@ -717,7 +677,7 @@ class Item_func_curdate :public Item_datefunc
MYSQL_TIME ltime;
public:
Item_func_curdate(THD *thd): Item_datefunc(thd), last_query_id(0) {}
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)=0;
bool check_vcol_func_processor(void *arg)
{
@@ -760,7 +720,7 @@ public:
bool fix_fields(THD *, Item **);
bool fix_length_and_dec()
{ fix_attributes_datetime(decimals); return FALSE;}
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)=0;
bool check_vcol_func_processor(void *arg)
{
@@ -815,7 +775,7 @@ public:
bool const_item() const { return 0; }
const char *func_name() const { return "sysdate"; }
void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time);
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
table_map used_tables() const { return RAND_TABLE_BIT; }
bool check_vcol_func_processor(void *arg)
{
@@ -835,7 +795,7 @@ class Item_func_from_days :public Item_datefunc
public:
Item_func_from_days(THD *thd, Item *a): Item_datefunc(thd, a) {}
const char *func_name() const { return "from_days"; }
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
bool check_partition_func_processor(void *int_arg) {return FALSE;}
bool check_vcol_func_processor(void *arg) { return FALSE;}
bool check_valid_arguments_processor(void *int_arg)
@@ -900,7 +860,7 @@ class Item_func_from_unixtime :public Item_datetimefunc
Item_func_from_unixtime(THD *thd, Item *a): Item_datetimefunc(thd, a) {}
const char *func_name() const { return "from_unixtime"; }
bool fix_length_and_dec();
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
bool check_vcol_func_processor(void *arg)
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC);
@@ -945,11 +905,11 @@ class Item_func_convert_tz :public Item_datetimefunc
const char *func_name() const { return "convert_tz"; }
bool fix_length_and_dec()
{
- fix_attributes_datetime(args[0]->datetime_precision());
+ fix_attributes_datetime(args[0]->datetime_precision(current_thd));
maybe_null= true;
return FALSE;
}
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
void cleanup();
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_convert_tz>(thd, this); }
@@ -962,7 +922,7 @@ class Item_func_sec_to_time :public Item_timefunc
{ return args[0]->check_type_can_return_decimal(func_name()); }
public:
Item_func_sec_to_time(THD *thd, Item *item): Item_timefunc(thd, item) {}
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
bool fix_length_and_dec()
{
fix_attributes_time(args[0]->decimals);
@@ -975,18 +935,17 @@ public:
};
-class Item_date_add_interval :public Item_temporal_hybrid_func
+class Item_date_add_interval :public Item_handled_func
{
public:
const interval_type int_type; // keep it public
const bool date_sub_interval; // keep it public
Item_date_add_interval(THD *thd, Item *a, Item *b, interval_type type_arg,
bool neg_arg):
- Item_temporal_hybrid_func(thd, a, b),int_type(type_arg),
+ Item_handled_func(thd, a, b), int_type(type_arg),
date_sub_interval(neg_arg) {}
const char *func_name() const { return "date_add_interval"; }
bool fix_length_and_dec();
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
bool eq(const Item *item, bool binary_cmp) const;
void print(String *str, enum_query_type query_type);
enum precedence precedence() const { return INTERVAL_PRECEDENCE; }
@@ -996,9 +955,17 @@ public:
};
-class Item_extract :public Item_int_func
+class Item_extract :public Item_int_func,
+ public Type_handler_hybrid_field_type
{
- bool date_value;
+ date_mode_t m_date_mode;
+ const Type_handler_int_result *handler_by_length(uint32 length,
+ uint32 threashold)
+ {
+ if (length >= threashold)
+ return &type_handler_longlong;
+ return &type_handler_long;
+ }
void set_date_length(uint32 length)
{
/*
@@ -1007,48 +974,34 @@ class Item_extract :public Item_int_func
because all around the code we assume that max_length is sign inclusive.
Another options is to set unsigned_flag to "true".
*/
- max_length= length; //QQ: see above
- date_value= true;
+ set_handler(handler_by_length(max_length= length, 10)); // QQ: see above
+ m_date_mode= date_mode_t(0);
+ }
+ void set_day_length(uint32 length)
+ {
+ /*
+ Units starting with DAY can be negative:
+ EXTRACT(DAY FROM '-24:00:00') -> -1
+ */
+ set_handler(handler_by_length(max_length= length + 1/*sign*/, 11));
+ m_date_mode= Temporal::Options(TIME_INTERVAL_DAY, current_thd);
}
void set_time_length(uint32 length)
{
- max_length= length + 1/*sign*/;
- date_value= false;
+ set_handler(handler_by_length(max_length= length + 1/*sign*/, 11));
+ m_date_mode= Temporal::Options(TIME_INTERVAL_hhmmssff, current_thd);
}
public:
const interval_type int_type; // keep it public
Item_extract(THD *thd, interval_type type_arg, Item *a):
- Item_int_func(thd, a), int_type(type_arg) {}
+ Item_int_func(thd, a),
+ Type_handler_hybrid_field_type(&type_handler_longlong),
+ m_date_mode(date_mode_t(0)),
+ int_type(type_arg)
+ { }
const Type_handler *type_handler() const
{
- switch (int_type) {
- case INTERVAL_YEAR:
- case INTERVAL_YEAR_MONTH:
- case INTERVAL_QUARTER:
- case INTERVAL_MONTH:
- case INTERVAL_WEEK:
- case INTERVAL_DAY:
- case INTERVAL_DAY_HOUR:
- case INTERVAL_DAY_MINUTE:
- case INTERVAL_DAY_SECOND:
- case INTERVAL_HOUR:
- case INTERVAL_HOUR_MINUTE:
- case INTERVAL_HOUR_SECOND:
- case INTERVAL_MINUTE:
- case INTERVAL_MINUTE_SECOND:
- case INTERVAL_SECOND:
- case INTERVAL_MICROSECOND:
- case INTERVAL_SECOND_MICROSECOND:
- return &type_handler_long;
- case INTERVAL_DAY_MICROSECOND:
- case INTERVAL_HOUR_MICROSECOND:
- case INTERVAL_MINUTE_MICROSECOND:
- return &type_handler_longlong;
- case INTERVAL_LAST:
- break;
- }
- DBUG_ASSERT(0);
- return &type_handler_longlong;
+ return Type_handler_hybrid_field_type::type_handler();
}
longlong val_int();
enum Functype functype() const { return EXTRACT_FUNC; }
@@ -1118,6 +1071,9 @@ class Item_char_typecast :public Item_str_func
void check_truncation_with_warn(String *src, size_t dstlen);
void fix_length_and_dec_internal(CHARSET_INFO *fromcs);
public:
+ // Methods used by ColumnStore
+ uint get_cast_length() const { return cast_length; }
+public:
Item_char_typecast(THD *thd, Item *a, uint length_arg, CHARSET_INFO *cs_arg):
Item_str_func(thd, a), cast_length(length_arg), cast_cs(cs_arg),
m_suppress_warning_to_error_escalation(false) {}
@@ -1144,22 +1100,34 @@ public:
};
-class Item_temporal_typecast: public Item_temporal_func
+class Item_interval_DDhhmmssff_typecast :public Item_char_typecast
{
+ uint m_fsp;
public:
- Item_temporal_typecast(THD *thd, Item *a): Item_temporal_func(thd, a) {}
- virtual const char *cast_type() const = 0;
- void print(String *str, enum_query_type query_type);
+ Item_interval_DDhhmmssff_typecast(THD *thd, Item *a, uint fsp)
+ :Item_char_typecast(thd, a,Interval_DDhhmmssff::max_char_length(fsp),
+ &my_charset_latin1),
+ m_fsp(fsp)
+ { }
+ String *val_str(String *to)
+ {
+ Interval_DDhhmmssff it(current_thd, args[0], m_fsp);
+ null_value= !it.is_valid_interval_DDhhmmssff();
+ return it.to_string(to, m_fsp);
+ }
};
-class Item_date_typecast :public Item_temporal_typecast
+
+class Item_date_typecast :public Item_datefunc
{
public:
- Item_date_typecast(THD *thd, Item *a): Item_temporal_typecast(thd, a) {}
+ Item_date_typecast(THD *thd, Item *a): Item_datefunc(thd, a) {}
const char *func_name() const { return "cast_as_date"; }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
- const char *cast_type() const { return "date"; }
- const Type_handler *type_handler() const { return &type_handler_newdate; }
+ void print(String *str, enum_query_type query_type)
+ {
+ print_cast_temporal(str, query_type);
+ }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool fix_length_and_dec()
{
return args[0]->type_handler()->Item_date_typecast_fix_length_and_dec(this);
@@ -1169,39 +1137,45 @@ public:
};
-class Item_time_typecast :public Item_temporal_typecast
+class Item_time_typecast :public Item_timefunc
{
public:
Item_time_typecast(THD *thd, Item *a, uint dec_arg):
- Item_temporal_typecast(thd, a) { decimals= dec_arg; }
+ Item_timefunc(thd, a) { decimals= dec_arg; }
const char *func_name() const { return "cast_as_time"; }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
- const char *cast_type() const { return "time"; }
- const Type_handler *type_handler() const { return &type_handler_time2; }
+ void print(String *str, enum_query_type query_type)
+ {
+ print_cast_temporal(str, query_type);
+ }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool fix_length_and_dec()
{
return args[0]->type_handler()->
Item_time_typecast_fix_length_and_dec(this);
}
+ Sql_mode_dependency value_depends_on_sql_mode() const;
Item *get_copy(THD *thd)
{ return get_item_copy<Item_time_typecast>(thd, this); }
};
-class Item_datetime_typecast :public Item_temporal_typecast
+class Item_datetime_typecast :public Item_datetimefunc
{
public:
Item_datetime_typecast(THD *thd, Item *a, uint dec_arg):
- Item_temporal_typecast(thd, a) { decimals= dec_arg; }
+ Item_datetimefunc(thd, a) { decimals= dec_arg; }
const char *func_name() const { return "cast_as_datetime"; }
- const char *cast_type() const { return "datetime"; }
- const Type_handler *type_handler() const { return &type_handler_datetime2; }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+ void print(String *str, enum_query_type query_type)
+ {
+ print_cast_temporal(str, query_type);
+ }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool fix_length_and_dec()
{
return args[0]->type_handler()->
Item_datetime_typecast_fix_length_and_dec(this);
}
+ Sql_mode_dependency value_depends_on_sql_mode() const;
Item *get_copy(THD *thd)
{ return get_item_copy<Item_datetime_typecast>(thd, this); }
};
@@ -1215,31 +1189,76 @@ public:
Item_func_makedate(THD *thd, Item *a, Item *b):
Item_datefunc(thd, a, b) {}
const char *func_name() const { return "makedate"; }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_makedate>(thd, this); }
};
-class Item_func_add_time :public Item_temporal_hybrid_func
+class Item_func_timestamp :public Item_datetimefunc
{
- const bool is_date;
- int sign;
-
+ bool check_arguments() const
+ {
+ return args[0]->check_type_can_return_date(func_name()) ||
+ args[1]->check_type_can_return_time(func_name());
+ }
public:
- Item_func_add_time(THD *thd, Item *a, Item *b, bool type_arg, bool neg_arg):
- Item_temporal_hybrid_func(thd, a, b), is_date(type_arg)
- { sign= neg_arg ? -1 : 1; }
- bool fix_length_and_dec();
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
- const char *func_name() const
+ Item_func_timestamp(THD *thd, Item *a, Item *b)
+ :Item_datetimefunc(thd, a, b)
+ { }
+ const char *func_name() const { return "timestamp"; }
+ bool fix_length_and_dec()
{
- return is_date ? "timestamp" : sign > 0 ? "addtime" : "subtime";
+ THD *thd= current_thd;
+ uint dec0= args[0]->datetime_precision(thd);
+ uint dec1= Interval_DDhhmmssff::fsp(thd, args[1]);
+ fix_attributes_datetime(MY_MAX(dec0, dec1));
+ maybe_null= true;
+ return false;
+ }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ {
+ Datetime dt(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd));
+ if (!dt.is_valid_datetime())
+ return null_value= true;
+ Interval_DDhhmmssff it(thd, args[1]);
+ if (!it.is_valid_interval_DDhhmmssff())
+ return null_value= true;
+ return (null_value= Sec6_add(dt.get_mysql_time(), it.get_mysql_time(), 1).
+ to_datetime(ltime));
}
Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_timestamp>(thd, this); }
+};
+
+
+/**
+ ADDTIME(t,a) and SUBTIME(t,a) are time functions that calculate a
+ time/datetime value
+
+ t: time_or_datetime_expression
+ a: time_expression
+
+ Result: Time value or datetime value
+*/
+
+class Item_func_add_time :public Item_handled_func
+{
+ int sign;
+public:
+ // Methods used by ColumnStore
+ int get_sign() const { return sign; }
+public:
+ Item_func_add_time(THD *thd, Item *a, Item *b, bool neg_arg)
+ :Item_handled_func(thd, a, b), sign(neg_arg ? -1 : 1)
+ { }
+ bool fix_length_and_dec();
+ const char *func_name() const { return sign > 0 ? "addtime" : "subtime"; }
+ Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_add_time>(thd, this); }
};
+
class Item_func_timediff :public Item_timefunc
{
bool check_arguments() const
@@ -1249,12 +1268,14 @@ public:
const char *func_name() const { return "timediff"; }
bool fix_length_and_dec()
{
- uint dec= MY_MAX(args[0]->time_precision(), args[1]->time_precision());
+ THD *thd= current_thd;
+ uint dec= MY_MAX(args[0]->time_precision(thd),
+ args[1]->time_precision(thd));
fix_attributes_time(dec);
maybe_null= true;
return FALSE;
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_timediff>(thd, this); }
};
@@ -1277,7 +1298,7 @@ public:
return FALSE;
}
const char *func_name() const { return "maketime"; }
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_maketime>(thd, this); }
};
@@ -1313,6 +1334,9 @@ class Item_func_timestamp_diff :public Item_longlong_func
{ return check_argument_types_can_return_date(0, arg_count); }
const interval_type int_type;
public:
+ // Methods used by ColumnStore
+ interval_type get_int_type() const { return int_type; };
+public:
Item_func_timestamp_diff(THD *thd, Item *a, Item *b, interval_type type_arg):
Item_longlong_func(thd, a, b), int_type(type_arg) {}
const char *func_name() const { return "timestampdiff"; }
@@ -1356,19 +1380,19 @@ public:
};
-class Item_func_str_to_date :public Item_temporal_hybrid_func
+class Item_func_str_to_date :public Item_handled_func
{
- timestamp_type cached_timestamp_type;
bool const_item;
String subject_converter;
String format_converter;
CHARSET_INFO *internal_charset;
public:
Item_func_str_to_date(THD *thd, Item *a, Item *b):
- Item_temporal_hybrid_func(thd, a, b), const_item(false),
+ Item_handled_func(thd, a, b), const_item(false),
internal_charset(NULL)
{}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+ bool get_date_common(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate,
+ timestamp_type);
const char *func_name() const { return "str_to_date"; }
bool fix_length_and_dec();
Item *get_copy(THD *thd)
@@ -1383,9 +1407,365 @@ class Item_func_last_day :public Item_datefunc
public:
Item_func_last_day(THD *thd, Item *a): Item_datefunc(thd, a) {}
const char *func_name() const { return "last_day"; }
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_last_day>(thd, this); }
};
+
+/*****************************************************************************/
+
+class Func_handler_date_add_interval
+{
+protected:
+ static uint interval_dec(const Item *item, interval_type int_type)
+ {
+ if (int_type == INTERVAL_MICROSECOND ||
+ (int_type >= INTERVAL_DAY_MICROSECOND &&
+ int_type <= INTERVAL_SECOND_MICROSECOND))
+ return TIME_SECOND_PART_DIGITS;
+ if (int_type == INTERVAL_SECOND && item->decimals > 0)
+ return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
+ return 0;
+ }
+ interval_type int_type(const Item_handled_func *item) const
+ {
+ return static_cast<const Item_date_add_interval*>(item)->int_type;
+ }
+ bool sub(const Item_handled_func *item) const
+ {
+ return static_cast<const Item_date_add_interval*>(item)->date_sub_interval;
+ }
+ bool add(THD *thd, Item *item, interval_type type, bool sub, MYSQL_TIME *to) const
+ {
+ INTERVAL interval;
+ if (get_interval_value(thd, item, type, &interval))
+ return true;
+ if (sub)
+ interval.neg = !interval.neg;
+ return date_add_interval(thd, to, type, interval);
+ }
+};
+
+
+class Func_handler_date_add_interval_datetime:
+ public Item_handled_func::Handler_datetime,
+ public Func_handler_date_add_interval
+{
+public:
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ uint dec= MY_MAX(item->arguments()[0]->datetime_precision(current_thd),
+ interval_dec(item->arguments()[1], int_type(item)));
+ item->fix_attributes_datetime(dec);
+ return false;
+ }
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const
+ {
+ Datetime::Options opt(TIME_CONV_NONE, thd);
+ Datetime dt(thd, item->arguments()[0], opt);
+ if (!dt.is_valid_datetime() ||
+ dt.check_date_with_warn(thd, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
+ return (item->null_value= true);
+ dt.copy_to_mysql_time(to);
+ return (item->null_value= add(thd, item->arguments()[1],
+ int_type(item), sub(item), to));
+ }
+};
+
+
+class Func_handler_date_add_interval_datetime_arg0_time:
+ public Func_handler_date_add_interval_datetime
+{
+public:
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const;
+};
+
+
+class Func_handler_date_add_interval_date:
+ public Item_handled_func::Handler_date,
+ public Func_handler_date_add_interval
+{
+public:
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const
+ {
+ /*
+ The first argument is known to be of the DATE data type (not DATETIME).
+ We don't need rounding here.
+ */
+ Date d(thd, item->arguments()[0], TIME_CONV_NONE);
+ if (!d.is_valid_date() ||
+ d.check_date_with_warn(thd, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
+ return (item->null_value= true);
+ d.copy_to_mysql_time(to);
+ return (item->null_value= add(thd, item->arguments()[1],
+ int_type(item), sub(item), to));
+ }
+};
+
+
+class Func_handler_date_add_interval_time:
+ public Item_handled_func::Handler_time,
+ public Func_handler_date_add_interval
+{
+public:
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ uint dec= MY_MAX(item->arguments()[0]->time_precision(current_thd),
+ interval_dec(item->arguments()[1], int_type(item)));
+ item->fix_attributes_time(dec);
+ return false;
+ }
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const
+ {
+ Time t(thd, item->arguments()[0]);
+ if (!t.is_valid_time())
+ return (item->null_value= true);
+ t.copy_to_mysql_time(to);
+ return (item->null_value= add(thd, item->arguments()[1],
+ int_type(item), sub(item), to));
+ }
+};
+
+
+class Func_handler_date_add_interval_string:
+ public Item_handled_func::Handler_temporal_string,
+ public Func_handler_date_add_interval
+{
+public:
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ uint dec= MY_MAX(item->arguments()[0]->datetime_precision(current_thd),
+ interval_dec(item->arguments()[1], int_type(item)));
+ item->collation.set(item->default_charset(),
+ DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ item->fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec);
+ return false;
+ }
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const
+ {
+ if (item->arguments()[0]->
+ get_date(thd, to, Datetime::Options(TIME_CONV_NONE, thd)) ||
+ (to->time_type != MYSQL_TIMESTAMP_TIME &&
+ check_date_with_warn(thd, to, TIME_NO_ZEROS, MYSQL_TIMESTAMP_ERROR)))
+ return (item->null_value= true);
+ return (item->null_value= add(thd, item->arguments()[1],
+ int_type(item), sub(item), to));
+ }
+};
+
+
+class Func_handler_sign
+{
+protected:
+ int m_sign;
+ Func_handler_sign(int sign) :m_sign(sign) { }
+};
+
+
+class Func_handler_add_time_datetime:
+ public Item_handled_func::Handler_datetime,
+ public Func_handler_sign
+{
+public:
+ Func_handler_add_time_datetime(int sign)
+ :Func_handler_sign(sign)
+ { }
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ THD *thd= current_thd;
+ uint dec0= item->arguments()[0]->datetime_precision(thd);
+ uint dec1= Interval_DDhhmmssff::fsp(thd, item->arguments()[1]);
+ item->fix_attributes_datetime(MY_MAX(dec0, dec1));
+ return false;
+ }
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ Datetime::Options opt(TIME_CONV_NONE, thd);
+ Datetime dt(thd, item->arguments()[0], opt);
+ if (!dt.is_valid_datetime())
+ return item->null_value= true;
+ Interval_DDhhmmssff it(thd, item->arguments()[1]);
+ if (!it.is_valid_interval_DDhhmmssff())
+ return item->null_value= true;
+ return (item->null_value= (Sec6_add(dt.get_mysql_time(),
+ it.get_mysql_time(), m_sign).
+ to_datetime(to)));
+ }
+};
+
+
+class Func_handler_add_time_time:
+ public Item_handled_func::Handler_time,
+ public Func_handler_sign
+{
+public:
+ Func_handler_add_time_time(int sign)
+ :Func_handler_sign(sign)
+ { }
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ THD *thd= current_thd;
+ uint dec0= item->arguments()[0]->time_precision(thd);
+ uint dec1= Interval_DDhhmmssff::fsp(thd, item->arguments()[1]);
+ item->fix_attributes_time(MY_MAX(dec0, dec1));
+ return false;
+ }
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ Time t(thd, item->arguments()[0]);
+ if (!t.is_valid_time())
+ return item->null_value= true;
+ Interval_DDhhmmssff i(thd, item->arguments()[1]);
+ if (!i.is_valid_interval_DDhhmmssff())
+ return item->null_value= true;
+ return (item->null_value= (Sec6_add(t.get_mysql_time(),
+ i.get_mysql_time(), m_sign).
+ to_time(thd, to, item->decimals)));
+ }
+};
+
+
+class Func_handler_add_time_string:
+ public Item_handled_func::Handler_temporal_string,
+ public Func_handler_sign
+{
+public:
+ Func_handler_add_time_string(int sign)
+ :Func_handler_sign(sign)
+ { }
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ uint dec0= item->arguments()[0]->decimals;
+ uint dec1= Interval_DDhhmmssff::fsp(current_thd, item->arguments()[1]);
+ uint dec= MY_MAX(dec0, dec1);
+ item->collation.set(item->default_charset(),
+ DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ item->fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec);
+ return false;
+ }
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ // Detect a proper timestamp type based on the argument values
+ Temporal_hybrid l_time1(thd, item->arguments()[0],
+ Temporal::Options(TIME_TIME_ONLY, thd));
+ if (!l_time1.is_valid_temporal())
+ return (item->null_value= true);
+ Interval_DDhhmmssff l_time2(thd, item->arguments()[1]);
+ if (!l_time2.is_valid_interval_DDhhmmssff())
+ return (item->null_value= true);
+ Sec6_add add(l_time1.get_mysql_time(), l_time2.get_mysql_time(), m_sign);
+ return (item->null_value= (l_time1.get_mysql_time()->time_type ==
+ MYSQL_TIMESTAMP_TIME ?
+ add.to_time(thd, to, item->decimals) :
+ add.to_datetime(to)));
+ }
+};
+
+
+class Func_handler_str_to_date_datetime_sec:
+ public Item_handled_func::Handler_datetime
+{
+public:
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ item->fix_attributes_datetime(0);
+ return false;
+ }
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const
+ {
+ return static_cast<Item_func_str_to_date*>(item)->
+ get_date_common(thd, to, fuzzy, MYSQL_TIMESTAMP_DATETIME);
+ }
+};
+
+
+class Func_handler_str_to_date_datetime_usec:
+ public Item_handled_func::Handler_datetime
+{
+public:
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ item->fix_attributes_datetime(TIME_SECOND_PART_DIGITS);
+ return false;
+ }
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const
+ {
+ return static_cast<Item_func_str_to_date*>(item)->
+ get_date_common(thd, to, fuzzy, MYSQL_TIMESTAMP_DATETIME);
+ }
+};
+
+
+class Func_handler_str_to_date_date: public Item_handled_func::Handler_date
+{
+public:
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const
+ {
+ return static_cast<Item_func_str_to_date*>(item)->
+ get_date_common(thd, to, fuzzy, MYSQL_TIMESTAMP_DATE);
+ }
+};
+
+
+class Func_handler_str_to_date_time: public Item_handled_func::Handler_time
+{
+public:
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzy) const
+ {
+ if (static_cast<Item_func_str_to_date*>(item)->
+ get_date_common(thd, to, fuzzy, MYSQL_TIMESTAMP_TIME))
+ return true;
+ if (to->day)
+ {
+ /*
+ Day part for time type can be nonzero value and so
+ we should add hours from day part to hour part to
+ keep valid time value.
+ */
+ to->hour+= to->day * 24;
+ to->day= 0;
+ }
+ return false;
+ }
+};
+
+
+class Func_handler_str_to_date_time_sec: public Func_handler_str_to_date_time
+{
+public:
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ item->fix_attributes_time(0);
+ return false;
+ }
+};
+
+
+class Func_handler_str_to_date_time_usec: public Func_handler_str_to_date_time
+{
+public:
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ item->fix_attributes_time(TIME_SECOND_PART_DIGITS);
+ return false;
+ }
+};
+
+
#endif /* ITEM_TIMEFUNC_INCLUDED */
diff --git a/sql/item_vers.cc b/sql/item_vers.cc
index 76cda6a5ea2..792c434b8c3 100644
--- a/sql/item_vers.cc
+++ b/sql/item_vers.cc
@@ -53,9 +53,8 @@ Item_func_trt_ts::Item_func_trt_ts(THD *thd, Item* a, TR_table::field_id_t _trt_
bool
-Item_func_trt_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date)
+Item_func_trt_ts::get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate)
{
- THD *thd= current_thd; // can it differ from constructor's?
DBUG_ASSERT(thd);
DBUG_ASSERT(args[0]);
if (args[0]->result_type() != INT_RESULT)
@@ -80,7 +79,7 @@ Item_func_trt_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date)
if (null_value)
return true;
- return trt[trt_field]->get_date(res, fuzzy_date);
+ return trt[trt_field]->get_date(res, fuzzydate);
}
@@ -156,7 +155,9 @@ Item_func_trt_id::val_int()
else
{
MYSQL_TIME commit_ts;
- if (args[0]->get_date(&commit_ts, 0))
+ THD *thd= current_thd;
+ Datetime::Options opt(TIME_CONV_NONE, thd);
+ if (args[0]->get_date(thd, &commit_ts, opt))
{
null_value= true;
return 0;
diff --git a/sql/item_vers.h b/sql/item_vers.h
index 2be3f683913..0799d04a0bc 100644
--- a/sql/item_vers.h
+++ b/sql/item_vers.h
@@ -65,7 +65,7 @@ public:
}
return "trt_commit_ts";
}
- bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+ bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_trt_ts>(thd, this); }
bool fix_length_and_dec()
diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc
index e3e2af8587f..5a114b7a193 100644
--- a/sql/item_windowfunc.cc
+++ b/sql/item_windowfunc.cc
@@ -120,7 +120,6 @@ Item_window_func::fix_fields(THD *thd, Item **ref)
const_item_cache= false;
with_window_func= true;
- with_sum_func= false;
if (fix_length_and_dec())
return TRUE;
@@ -445,12 +444,20 @@ Item_sum_hybrid_simple::val_str(String *str)
return retval;
}
-bool Item_sum_hybrid_simple::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+bool Item_sum_hybrid_simple::val_native(THD *thd, Native *to)
{
DBUG_ASSERT(fixed == 1);
if (null_value)
return true;
- bool retval= value->get_date(ltime, fuzzydate);
+ return val_native_from_item(thd, value, to);
+}
+
+bool Item_sum_hybrid_simple::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (null_value)
+ return true;
+ bool retval= value->get_date(thd, ltime, fuzzydate);
if ((null_value= value->null_value))
DBUG_ASSERT(retval == true);
return retval;
@@ -519,11 +526,11 @@ void Item_sum_hybrid_simple::reset_field()
}
case DECIMAL_RESULT:
{
- my_decimal value_buff, *arg_dec= args[0]->val_decimal(&value_buff);
+ VDec arg_dec(args[0]);
if (maybe_null)
{
- if (args[0]->null_value)
+ if (arg_dec.is_null())
result_field->set_null();
else
result_field->set_notnull();
@@ -532,9 +539,7 @@ void Item_sum_hybrid_simple::reset_field()
We must store zero in the field as we will use the field value in
add()
*/
- if (!arg_dec) // Null
- arg_dec= &decimal_zero;
- result_field->store_decimal(arg_dec);
+ result_field->store_decimal(arg_dec.ptr_or(&decimal_zero));
break;
}
case ROW_RESULT:
diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h
index 81bfa8d7cd0..c038cb8d15f 100644
--- a/sql/item_windowfunc.h
+++ b/sql/item_windowfunc.h
@@ -129,6 +129,7 @@ public:
return false;
}
+ void reset_field() { DBUG_ASSERT(0); }
void update_field() {}
enum Sumfunctype sum_func() const
@@ -192,11 +193,8 @@ public:
return cur_rank;
}
+ void reset_field() { DBUG_ASSERT(0); }
void update_field() {}
- /*
- void reset_field();
- TODO: ^^ what does this do ? It is not called ever?
- */
enum Sumfunctype sum_func () const
{
@@ -259,6 +257,7 @@ class Item_sum_dense_rank: public Item_sum_int
first_add= true;
}
bool add();
+ void reset_field() { DBUG_ASSERT(0); }
void update_field() {}
longlong val_int()
{
@@ -314,7 +313,10 @@ class Item_sum_hybrid_simple : public Item_sum_hybrid
my_decimal *val_decimal(my_decimal *);
void reset_field();
String *val_str(String *);
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool val_native(THD *thd, Native *to);
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ const Type_handler *type_handler() const
+ { return Type_handler_hybrid_field_type::type_handler(); }
void update_field();
Field *create_tmp_field(bool group, TABLE *table);
void clear()
@@ -540,6 +542,8 @@ class Item_sum_percent_rank: public Item_sum_double,
void setup_window_func(THD *thd, Window_spec *window_spec);
+ void reset_field() { DBUG_ASSERT(0); }
+
void set_partition_row_count(ulonglong count)
{
Partition_row_count::set_partition_row_count(count);
@@ -624,6 +628,8 @@ class Item_sum_cume_dist: public Item_sum_double,
return FALSE;
}
+ void reset_field() { DBUG_ASSERT(0); }
+
void set_partition_row_count(ulonglong count)
{
Partition_row_count::set_partition_row_count(count);
@@ -695,6 +701,8 @@ class Item_sum_ntile : public Item_sum_int,
void update_field() {}
+ void reset_field() { DBUG_ASSERT(0); }
+
void set_partition_row_count(ulonglong count)
{
Partition_row_count::set_partition_row_count(count);
@@ -763,7 +771,7 @@ public:
return value->val_str(str);
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
if (get_row_count() == 0 || get_arg(0)->is_null())
{
@@ -771,7 +779,7 @@ public:
return 0;
}
null_value= false;
- return value->get_date(ltime, fuzzydate);
+ return value->get_date(thd, ltime, fuzzydate);
}
bool add()
@@ -845,6 +853,8 @@ public:
return FALSE;
}
+ void reset_field() { DBUG_ASSERT(0); }
+
void set_partition_row_count(ulonglong count)
{
Partition_row_count::set_partition_row_count(count);
@@ -979,6 +989,8 @@ public:
return FALSE;
}
+ void reset_field() { DBUG_ASSERT(0); }
+
void set_partition_row_count(ulonglong count)
{
Partition_row_count::set_partition_row_count(count);
@@ -1263,6 +1275,15 @@ public:
return res;
}
+ bool val_native(THD *thd, Native *to)
+ {
+ if (force_return_blank)
+ return null_value= true;
+ if (read_value_from_result_field)
+ return val_native_from_field(result_field, to);
+ return val_native_from_item(thd, window_func(), to);
+ }
+
my_decimal* val_decimal(my_decimal* dec)
{
my_decimal *res;
@@ -1286,7 +1307,7 @@ public:
return res;
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
bool res;
if (force_return_blank)
@@ -1303,7 +1324,7 @@ public:
}
else
{
- res= window_func()->get_date(ltime, fuzzydate);
+ res= window_func()->get_date(thd, ltime, fuzzydate);
null_value= window_func()->null_value;
}
return res;
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index 885ef1c397d..28bddb75df2 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -29,10 +29,8 @@
/*
TODO: future development directions:
- 1. add real constants for XPATH_NODESET_CMP and XPATH_NODESET
- into enum Type in item.h.
- 2. add nodeset_to_nodeset_comparator
- 3. add lacking functions:
+ 1. add nodeset_to_nodeset_comparator
+ 2. add lacking functions:
- name()
- lang()
- string()
@@ -44,7 +42,7 @@
- substring-after()
- normalize-space()
- substring-before()
- 4. add lacking axis:
+ 3. add lacking axis:
- following-sibling
- following,
- preceding-sibling
@@ -151,6 +149,9 @@ public:
};
+static Type_handler_long_blob type_handler_xpath_nodeset;
+
+
/*
Common features of the functions returning a node set.
*/
@@ -181,16 +182,29 @@ public:
void prepare(String *nodeset)
{
prepare_nodes();
- String *res= args[0]->val_nodeset(&tmp_value);
+ String *res= args[0]->val_raw(&tmp_value);
fltbeg= (MY_XPATH_FLT*) res->ptr();
fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
nodeset->length(0);
}
- enum Type type() const { return XPATH_NODESET; }
+ const Type_handler *type_handler() const
+ {
+ return &type_handler_xpath_nodeset;
+ }
+ const Type_handler *fixed_type_handler() const
+ {
+ return &type_handler_xpath_nodeset;
+ }
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
String *val_str(String *str)
{
prepare_nodes();
- String *res= val_nodeset(&tmp2_value);
+ String *res= val_raw(&tmp2_value);
fltbeg= (MY_XPATH_FLT*) res->ptr();
fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
String active;
@@ -247,7 +261,7 @@ public:
Item_nodeset_func_rootelement(THD *thd, String *pxml):
Item_nodeset_func(thd, pxml) {}
const char *func_name() const { return "xpath_rootelement"; }
- String *val_nodeset(String *nodeset);
+ String *val_raw(String *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_rootelement>(thd, this); }
};
@@ -260,7 +274,7 @@ public:
Item_nodeset_func_union(THD *thd, Item *a, Item *b, String *pxml):
Item_nodeset_func(thd, a, b, pxml) {}
const char *func_name() const { return "xpath_union"; }
- String *val_nodeset(String *nodeset);
+ String *val_raw(String *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_union>(thd, this); }
};
@@ -294,7 +308,7 @@ public:
String *pxml):
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_selfbyname"; }
- String *val_nodeset(String *nodeset);
+ String *val_raw(String *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_selfbyname>(thd, this); }
};
@@ -308,7 +322,7 @@ public:
String *pxml):
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_childbyname"; }
- String *val_nodeset(String *nodeset);
+ String *val_raw(String *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_childbyname>(thd, this); }
};
@@ -324,7 +338,7 @@ public:
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml),
need_self(need_self_arg) {}
const char *func_name() const { return "xpath_descendantbyname"; }
- String *val_nodeset(String *nodeset);
+ String *val_raw(String *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_descendantbyname>(thd, this); }
};
@@ -340,7 +354,7 @@ public:
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml),
need_self(need_self_arg) {}
const char *func_name() const { return "xpath_ancestorbyname"; }
- String *val_nodeset(String *nodeset);
+ String *val_raw(String *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_ancestorbyname>(thd, this); }
};
@@ -354,7 +368,7 @@ public:
String *pxml):
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_parentbyname"; }
- String *val_nodeset(String *nodeset);
+ String *val_raw(String *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_parentbyname>(thd, this); }
};
@@ -368,7 +382,7 @@ public:
uint l_arg, String *pxml):
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_attributebyname"; }
- String *val_nodeset(String *nodeset);
+ String *val_raw(String *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_attributebyname>(thd, this); }
};
@@ -385,7 +399,7 @@ public:
Item_nodeset_func_predicate(THD *thd, Item *a, Item *b, String *pxml):
Item_nodeset_func(thd, a, b, pxml) {}
const char *func_name() const { return "xpath_predicate"; }
- String *val_nodeset(String *nodeset);
+ String *val_raw(String *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_predicate>(thd, this); }
};
@@ -398,7 +412,7 @@ public:
Item_nodeset_func_elementbyindex(THD *thd, Item *a, Item *b, String *pxml):
Item_nodeset_func(thd, a, b, pxml) { }
const char *func_name() const { return "xpath_elementbyindex"; }
- String *val_nodeset(String *nodeset);
+ String *val_raw(String *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_elementbyindex>(thd, this); }
};
@@ -420,9 +434,9 @@ public:
const char *func_name() const { return "xpath_cast_bool"; }
longlong val_int()
{
- if (args[0]->type() == XPATH_NODESET)
+ if (args[0]->fixed_type_handler() == &type_handler_xpath_nodeset)
{
- String *flt= args[0]->val_nodeset(&tmp_value);
+ String *flt= args[0]->val_raw(&tmp_value);
return flt->length() == sizeof(MY_XPATH_FLT) ? 1 : 0;
}
return args[0]->val_real() ? 1 : 0;
@@ -455,7 +469,7 @@ public:
String *string_cache;
Item_nodeset_context_cache(THD *thd, String *str_arg, String *pxml):
Item_nodeset_func(thd, pxml), string_cache(str_arg) { }
- String *val_nodeset(String *res)
+ String *val_raw(String *res)
{ return string_cache; }
bool fix_length_and_dec() { max_length= MAX_BLOB_WIDTH;; return FALSE; }
Item *get_copy(THD *thd)
@@ -474,7 +488,7 @@ public:
bool fix_length_and_dec() { max_length=10; return FALSE; }
longlong val_int()
{
- String *flt= args[0]->val_nodeset(&tmp_value);
+ String *flt= args[0]->val_raw(&tmp_value);
if (flt->length() == sizeof(MY_XPATH_FLT))
return ((MY_XPATH_FLT*)flt->ptr())->pos + 1;
return 0;
@@ -496,7 +510,7 @@ public:
longlong val_int()
{
uint predicate_supplied_context_size;
- String *res= args[0]->val_nodeset(&tmp_value);
+ String *res= args[0]->val_raw(&tmp_value);
if (res->length() == sizeof(MY_XPATH_FLT) &&
(predicate_supplied_context_size= ((MY_XPATH_FLT*)res->ptr())->size))
return predicate_supplied_context_size;
@@ -519,7 +533,7 @@ public:
double val_real()
{
double sum= 0;
- String *res= args[0]->val_nodeset(&tmp_value);
+ String *res= args[0]->val_raw(&tmp_value);
MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr();
MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
uint numnodes= pxml->length() / sizeof(MY_XML_NODE);
@@ -587,19 +601,23 @@ public:
Item_nodeset_to_const_comparator(THD *thd, Item *nodeset, Item *cmpfunc,
String *p):
Item_bool_func(thd, nodeset, cmpfunc), pxml(p) {}
- enum Type type() const { return XPATH_NODESET_CMP; };
const char *func_name() const { return "xpath_nodeset_to_const_comparator"; }
bool check_vcol_func_processor(void *arg)
{
return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE);
}
-
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
longlong val_int()
{
Item_func *comp= (Item_func*)args[1];
Item_string_xml_non_const *fake=
(Item_string_xml_non_const*)(comp->arguments()[0]);
- String *res= args[0]->val_nodeset(&tmp_nodeset);
+ String *res= args[0]->val_raw(&tmp_nodeset);
MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr();
MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml->ptr();
@@ -630,7 +648,7 @@ public:
};
-String *Item_nodeset_func_rootelement::val_nodeset(String *nodeset)
+String *Item_nodeset_func_rootelement::val_raw(String *nodeset)
{
nodeset->length(0);
((XPathFilter*)nodeset)->append_element(0, 0);
@@ -638,11 +656,11 @@ String *Item_nodeset_func_rootelement::val_nodeset(String *nodeset)
}
-String * Item_nodeset_func_union::val_nodeset(String *nodeset)
+String * Item_nodeset_func_union::val_raw(String *nodeset)
{
uint num_nodes= pxml->length() / sizeof(MY_XML_NODE);
- String set0, *s0= args[0]->val_nodeset(&set0);
- String set1, *s1= args[1]->val_nodeset(&set1);
+ String set0, *s0= args[0]->val_raw(&set0);
+ String set1, *s1= args[1]->val_raw(&set1);
String both_str;
both_str.alloc(num_nodes);
char *both= (char*) both_str.ptr();
@@ -669,7 +687,7 @@ String * Item_nodeset_func_union::val_nodeset(String *nodeset)
}
-String *Item_nodeset_func_selfbyname::val_nodeset(String *nodeset)
+String *Item_nodeset_func_selfbyname::val_raw(String *nodeset)
{
prepare(nodeset);
for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
@@ -683,7 +701,7 @@ String *Item_nodeset_func_selfbyname::val_nodeset(String *nodeset)
}
-String *Item_nodeset_func_childbyname::val_nodeset(String *nodeset)
+String *Item_nodeset_func_childbyname::val_raw(String *nodeset)
{
prepare(nodeset);
for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
@@ -704,7 +722,7 @@ String *Item_nodeset_func_childbyname::val_nodeset(String *nodeset)
}
-String *Item_nodeset_func_descendantbyname::val_nodeset(String *nodeset)
+String *Item_nodeset_func_descendantbyname::val_raw(String *nodeset)
{
prepare(nodeset);
for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
@@ -726,7 +744,7 @@ String *Item_nodeset_func_descendantbyname::val_nodeset(String *nodeset)
}
-String *Item_nodeset_func_ancestorbyname::val_nodeset(String *nodeset)
+String *Item_nodeset_func_ancestorbyname::val_raw(String *nodeset)
{
char *active;
String active_str;
@@ -768,7 +786,7 @@ String *Item_nodeset_func_ancestorbyname::val_nodeset(String *nodeset)
}
-String *Item_nodeset_func_parentbyname::val_nodeset(String *nodeset)
+String *Item_nodeset_func_parentbyname::val_raw(String *nodeset)
{
char *active;
String active_str;
@@ -791,7 +809,7 @@ String *Item_nodeset_func_parentbyname::val_nodeset(String *nodeset)
}
-String *Item_nodeset_func_attributebyname::val_nodeset(String *nodeset)
+String *Item_nodeset_func_attributebyname::val_raw(String *nodeset)
{
prepare(nodeset);
for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
@@ -812,7 +830,7 @@ String *Item_nodeset_func_attributebyname::val_nodeset(String *nodeset)
}
-String *Item_nodeset_func_predicate::val_nodeset(String *str)
+String *Item_nodeset_func_predicate::val_raw(String *str)
{
Item_nodeset_func *nodeset_func= (Item_nodeset_func*) args[0];
uint pos= 0, size;
@@ -831,7 +849,7 @@ String *Item_nodeset_func_predicate::val_nodeset(String *str)
}
-String *Item_nodeset_func_elementbyindex::val_nodeset(String *nodeset)
+String *Item_nodeset_func_elementbyindex::val_raw(String *nodeset)
{
Item_nodeset_func *nodeset_func= (Item_nodeset_func*) args[0];
prepare(nodeset);
@@ -844,7 +862,9 @@ String *Item_nodeset_func_elementbyindex::val_nodeset(String *nodeset)
flt->pos,
size);
int index= (int) (args[1]->val_int()) - 1;
- if (index >= 0 && (flt->pos == (uint) index || args[1]->is_bool_type()))
+ if (index >= 0 &&
+ (flt->pos == (uint) index ||
+ (args[1]->type_handler()->is_bool_type())))
((XPathFilter*)nodeset)->append_element(flt->num, pos++);
}
return nodeset;
@@ -857,7 +877,7 @@ String *Item_nodeset_func_elementbyindex::val_nodeset(String *nodeset)
*/
static Item* nodeset2bool(MY_XPATH *xpath, Item *item)
{
- if (item->type() == Item::XPATH_NODESET)
+ if (item->fixed_type_handler() == &type_handler_xpath_nodeset)
return new (xpath->thd->mem_root)
Item_xpath_cast_bool(xpath->thd, item, xpath->pxml);
return item;
@@ -987,13 +1007,13 @@ static Item *create_comparator(MY_XPATH *xpath,
int oper, MY_XPATH_LEX *context,
Item *a, Item *b)
{
- if (a->type() != Item::XPATH_NODESET &&
- b->type() != Item::XPATH_NODESET)
+ if (a->fixed_type_handler() != &type_handler_xpath_nodeset &&
+ b->fixed_type_handler() != &type_handler_xpath_nodeset)
{
return eq_func(xpath->thd, oper, a, b); // two scalar arguments
}
- else if (a->type() == Item::XPATH_NODESET &&
- b->type() == Item::XPATH_NODESET)
+ else if (a->fixed_type_handler() == &type_handler_xpath_nodeset &&
+ b->fixed_type_handler() == &type_handler_xpath_nodeset)
{
uint len= (uint)(xpath->query.end - context->beg);
if (len <= 32)
@@ -1023,7 +1043,7 @@ static Item *create_comparator(MY_XPATH *xpath,
Item_string_xml_non_const(thd, "", 0, xpath->cs));
Item_nodeset_func *nodeset;
Item *scalar, *comp;
- if (a->type() == Item::XPATH_NODESET)
+ if (a->fixed_type_handler() == &type_handler_xpath_nodeset)
{
nodeset= (Item_nodeset_func*) a;
scalar= b;
@@ -1057,7 +1077,7 @@ static Item* nametestfunc(MY_XPATH *xpath,
MEM_ROOT *mem_root= thd->mem_root;
DBUG_ASSERT(arg != 0);
- DBUG_ASSERT(arg->type() == Item::XPATH_NODESET);
+ DBUG_ASSERT(arg->fixed_type_handler() == &type_handler_xpath_nodeset);
DBUG_ASSERT(beg != 0);
DBUG_ASSERT(len > 0);
@@ -1310,7 +1330,7 @@ static Item *create_func_substr(MY_XPATH *xpath, Item **args, uint nargs)
static Item *create_func_count(MY_XPATH *xpath, Item **args, uint nargs)
{
- if (args[0]->type() != Item::XPATH_NODESET)
+ if (args[0]->fixed_type_handler() != &type_handler_xpath_nodeset)
return 0;
return new (xpath->thd->mem_root) Item_func_xpath_count(xpath->thd, args[0], xpath->pxml);
}
@@ -1318,7 +1338,7 @@ static Item *create_func_count(MY_XPATH *xpath, Item **args, uint nargs)
static Item *create_func_sum(MY_XPATH *xpath, Item **args, uint nargs)
{
- if (args[0]->type() != Item::XPATH_NODESET)
+ if (args[0]->fixed_type_handler() != &type_handler_xpath_nodeset)
return 0;
return new (xpath->thd->mem_root)
Item_func_xpath_sum(xpath->thd, args[0], xpath->pxml);
@@ -1797,7 +1817,8 @@ my_xpath_parse_AxisSpecifier_NodeTest_opt_Predicate_list(MY_XPATH *xpath)
xpath->item= nodeset2bool(xpath, xpath->item);
- if (xpath->item->is_bool_type())
+ const Type_handler *fh;
+ if ((fh= xpath->item->fixed_type_handler()) && fh->is_bool_type())
{
xpath->context= new (xpath->thd->mem_root)
Item_nodeset_func_predicate(xpath->thd, prev_context,
@@ -2051,11 +2072,11 @@ static int my_xpath_parse_UnionExpr(MY_XPATH *xpath)
while (my_xpath_parse_term(xpath, MY_XPATH_LEX_VLINE))
{
Item *prev= xpath->item;
- if (prev->type() != Item::XPATH_NODESET)
+ if (prev->fixed_type_handler() != &type_handler_xpath_nodeset)
return 0;
if (!my_xpath_parse_PathExpr(xpath)
- || xpath->item->type() != Item::XPATH_NODESET)
+ || xpath->item->fixed_type_handler() != &type_handler_xpath_nodeset)
{
xpath->error= 1;
return 0;
@@ -2093,7 +2114,7 @@ my_xpath_parse_FilterExpr_opt_slashes_RelativeLocationPath(MY_XPATH *xpath)
if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
return 1;
- if (xpath->item->type() != Item::XPATH_NODESET)
+ if (xpath->item->fixed_type_handler() != &type_handler_xpath_nodeset)
{
xpath->lasttok= xpath->prevtok;
xpath->error= 1;
@@ -3065,7 +3086,7 @@ String *Item_func_xml_update::val_str(String *str)
null_value= 0;
if (!nodeset_func || get_xml(&xml) ||
!(rep= args[2]->val_str(&tmp_value3)) ||
- !(nodeset= nodeset_func->val_nodeset(&tmp_value2)))
+ !(nodeset= nodeset_func->val_raw(&tmp_value2)))
{
null_value= 1;
return 0;
diff --git a/sql/lex.h b/sql/lex.h
index 070ac5cc147..92ce01a1094 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -55,6 +55,7 @@ static SYMBOL symbols[] = {
{ ">>", SYM(SHIFT_RIGHT)},
{ "<=>", SYM(EQUAL_SYM)},
{ "ACCESSIBLE", SYM(ACCESSIBLE_SYM)},
+ { "ACCOUNT", SYM(ACCOUNT_SYM)},
{ "ACTION", SYM(ACTION)},
{ "ADD", SYM(ADD)},
{ "ADMIN", SYM(ADMIN_SYM)},
@@ -230,6 +231,7 @@ static SYMBOL symbols[] = {
{ "EXISTS", SYM(EXISTS)},
{ "EXIT", SYM(EXIT_MARIADB_SYM)},
{ "EXPANSION", SYM(EXPANSION_SYM)},
+ { "EXPIRE", SYM(EXPIRE_SYM)},
{ "EXPORT", SYM(EXPORT_SYM)},
{ "EXPLAIN", SYM(DESCRIBE)},
{ "EXTENDED", SYM(EXTENDED_SYM)},
@@ -418,6 +420,7 @@ static SYMBOL symbols[] = {
{ "NATIONAL", SYM(NATIONAL_SYM)},
{ "NATURAL", SYM(NATURAL)},
{ "NCHAR", SYM(NCHAR_SYM)},
+ { "NEVER", SYM(NEVER_SYM)},
{ "NEW", SYM(NEW_SYM)},
{ "NEXT", SYM(NEXT_SYM)},
{ "NEXTVAL", SYM(NEXTVAL_SYM)},
@@ -476,6 +479,7 @@ static SYMBOL symbols[] = {
{ "POINT", SYM(POINT_SYM)},
{ "POLYGON", SYM(POLYGON)},
{ "PORT", SYM(PORT_SYM)},
+ { "PORTION", SYM(PORTION_SYM)},
{ "PRECEDES", SYM(PRECEDES_SYM)},
{ "PRECEDING", SYM(PRECEDING_SYM)},
{ "PRECISION", SYM(PRECISION)},
@@ -589,6 +593,7 @@ static SYMBOL symbols[] = {
{ "SONAME", SYM(SONAME_SYM)},
{ "SOUNDS", SYM(SOUNDS_SYM)},
{ "SOURCE", SYM(SOURCE_SYM)},
+ { "STAGE", SYM(STAGE_SYM)},
{ "STORED", SYM(STORED_SYM)},
{ "SPATIAL", SYM(SPATIAL_SYM)},
{ "SPECIFIC", SYM(SPECIFIC_SYM)},
diff --git a/sql/lex_string.h b/sql/lex_string.h
index ddda73b2200..b638e7111f8 100644
--- a/sql/lex_string.h
+++ b/sql/lex_string.h
@@ -37,6 +37,10 @@ static inline bool cmp(const LEX_CSTRING *a, const LEX_CSTRING *b)
return a->length != b->length ||
(a->length && memcmp(a->str, b->str, a->length));
}
+static inline bool cmp(const LEX_CSTRING a, const LEX_CSTRING b)
+{
+ return a.length != b.length || (a.length && memcmp(a.str, b.str, a.length));
+}
/*
Compare if two LEX_CSTRING are equal. Assumption is that
diff --git a/sql/lock.cc b/sql/lock.cc
index 4d055f58242..3a2001fbc34 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -1,5 +1,6 @@
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2020, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -77,6 +78,7 @@
#include "sql_base.h" // close_tables_for_reopen
#include "sql_parse.h" // is_log_table_write_query
#include "sql_acl.h" // SUPER_ACL
+#include "sql_handler.h"
#include <hash.h>
#include "wsrep_mysqld.h"
@@ -858,10 +860,9 @@ bool lock_schema_name(THD *thd, const char *db)
return TRUE;
}
- if (thd->global_read_lock.can_acquire_protection())
+ if (thd->has_read_only_protection())
return TRUE;
- global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
- MDL_STATEMENT);
+ global_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_DDL, MDL_STATEMENT);
mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE, MDL_TRANSACTION);
mdl_requests.push_front(&mdl_request);
@@ -917,10 +918,9 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
DBUG_ASSERT(name);
DEBUG_SYNC(thd, "before_wait_locked_pname");
- if (thd->global_read_lock.can_acquire_protection())
+ if (thd->has_read_only_protection())
return TRUE;
- global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
- MDL_STATEMENT);
+ global_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_DDL, MDL_STATEMENT);
schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE,
MDL_TRANSACTION);
mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE, MDL_TRANSACTION);
@@ -951,10 +951,10 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
semi-automatic. We assume that any statement which should be blocked
by global read lock will either open and acquires write-lock on tables
or acquires metadata locks on objects it is going to modify. For any
- such statement global IX metadata lock is automatically acquired for
- its duration (in case of LOCK TABLES until end of LOCK TABLES mode).
- And lock_global_read_lock() simply acquires global S metadata lock
- and thus prohibits execution of statements which modify data (unless
+ such statement MDL_BACKUP_STMT metadata lock is automatically acquired
+ for its duration (in case of LOCK TABLES until end of LOCK TABLES mode).
+ And lock_global_read_lock() simply acquires MDL_BACKUP_FTWRL1 metadata
+ lock and thus prohibits execution of statements which modify data (unless
they modify only temporary tables). If deadlock happens it is detected
by MDL subsystem and resolved in the standard fashion (by backing-off
metadata locks acquired so far and restarting open tables process
@@ -995,11 +995,23 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
/**
Take global read lock, wait if there is protection against lock.
- If the global read lock is already taken by this thread, then nothing is done.
+ If the global read lock is already taken by this thread, then nothing is
+ done.
+
+ Concurrent thread can acquire protection against global read lock either
+ before or after it got table metadata lock. This may lead to a deadlock if
+ there is pending global read lock request. E.g.
+ t1 does DML, holds SHARED table lock, waiting for t3 (GRL protection)
+ t2 does DDL, holds GRL protection, waiting for t1 (EXCLUSIVE)
+ t3 does FTWRL, has pending GRL, waiting for t2 (GRL)
+
+ Since this is very seldom deadlock and FTWRL connection must not hold any
+ other locks, FTWRL connection is made deadlock victim and attempt to acquire
+ GRL retried.
See also "Handling of global read locks" above.
- @param thd Reference to thread.
+ @param thd Reference to thread.
@retval False Success, global read lock set, commits are NOT blocked.
@retval True Failure, thread was killed.
@@ -1011,17 +1023,43 @@ bool Global_read_lock::lock_global_read_lock(THD *thd)
if (!m_state)
{
+ MDL_deadlock_and_lock_abort_error_handler mdl_deadlock_handler;
MDL_request mdl_request;
+ bool result;
- DBUG_ASSERT(! thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
- MDL_SHARED));
- mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED, MDL_EXPLICIT);
-
- if (thd->mdl_context.acquire_lock(&mdl_request,
- thd->variables.lock_wait_timeout))
+ if (thd->current_backup_stage != BACKUP_FINISHED)
+ {
+ my_error(ER_BACKUP_LOCK_IS_ACTIVE, MYF(0));
DBUG_RETURN(1);
+ }
+
+ /*
+ Release HANDLER OPEN by the current THD as they may cause deadlocks
+ if another thread is trying to simultaneous drop the table
+ */
+ mysql_ha_cleanup_no_free(thd);
+ DEBUG_SYNC(thd, "ftwrl_before_lock");
+
+ DBUG_ASSERT(! thd->mdl_context.is_lock_owner(MDL_key::BACKUP, "", "",
+ MDL_BACKUP_FTWRL1));
+ DBUG_ASSERT(! thd->mdl_context.is_lock_owner(MDL_key::BACKUP, "", "",
+ MDL_BACKUP_FTWRL2));
+ mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_FTWRL1,
+ MDL_EXPLICIT);
+
+ do
+ {
+ mdl_deadlock_handler.init();
+ thd->push_internal_handler(&mdl_deadlock_handler);
+ result= thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout);
+ thd->pop_internal_handler();
+ } while (mdl_deadlock_handler.need_reopen());
- m_mdl_global_shared_lock= mdl_request.ticket;
+ if (result)
+ DBUG_RETURN(true);
+
+ m_mdl_global_read_lock= mdl_request.ticket;
m_state= GRL_ACQUIRED;
}
/*
@@ -1050,7 +1088,7 @@ void Global_read_lock::unlock_global_read_lock(THD *thd)
{
DBUG_ENTER("unlock_global_read_lock");
- DBUG_ASSERT(m_mdl_global_shared_lock && m_state);
+ DBUG_ASSERT(m_mdl_global_read_lock && m_state);
if (thd->global_disable_checkpoint)
{
@@ -1061,31 +1099,29 @@ void Global_read_lock::unlock_global_read_lock(THD *thd)
}
}
- if (m_mdl_blocks_commits_lock)
- {
- thd->mdl_context.release_lock(m_mdl_blocks_commits_lock);
- m_mdl_blocks_commits_lock= NULL;
+ thd->mdl_context.release_lock(m_mdl_global_read_lock);
+
#ifdef WITH_WSREP
- if (WSREP(thd) || wsrep_node_is_donor())
+ if (m_state == GRL_ACQUIRED_AND_BLOCKS_COMMIT)
+ {
+ Wsrep_server_state& server_state= Wsrep_server_state::instance();
+ if (server_state.state() == Wsrep_server_state::s_donor ||
+ (WSREP_NNULL(thd) &&
+ server_state.state() != Wsrep_server_state::s_synced))
{
+ /* TODO: maybe redundant here?: */
wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED;
- wsrep->resume(wsrep);
- /* resync here only if we did implicit desync earlier */
- if (!wsrep_desync && wsrep_node_is_synced())
- {
- int ret = wsrep->resync(wsrep);
- if (ret != WSREP_OK)
- {
- WSREP_WARN("resync failed %d for FTWRL: db: %s, query: %s",
- ret, thd->get_db(), thd->query());
- DBUG_VOID_RETURN;
- }
- }
+ server_state.resume();
+ }
+ else if (WSREP_NNULL(thd) &&
+ server_state.state() == Wsrep_server_state::s_synced)
+ {
+ server_state.resume_and_resync();
}
-#endif /* WITH_WSREP */
}
- thd->mdl_context.release_lock(m_mdl_global_shared_lock);
- m_mdl_global_shared_lock= NULL;
+#endif /* WITH_WSREP */
+
+ m_mdl_global_read_lock= NULL;
m_state= GRL_NONE;
DBUG_VOID_RETURN;
@@ -1109,7 +1145,6 @@ void Global_read_lock::unlock_global_read_lock(THD *thd)
bool Global_read_lock::make_global_read_lock_block_commit(THD *thd)
{
- MDL_request mdl_request;
DBUG_ENTER("make_global_read_lock_block_commit");
/*
If we didn't succeed lock_global_read_lock(), or if we already succeeded
@@ -1119,77 +1154,40 @@ bool Global_read_lock::make_global_read_lock_block_commit(THD *thd)
if (m_state != GRL_ACQUIRED)
DBUG_RETURN(0);
-#ifdef WITH_WSREP
- if (WSREP(thd) && m_mdl_blocks_commits_lock)
- {
- WSREP_DEBUG("GRL was in block commit mode when entering "
- "make_global_read_lock_block_commit");
- DBUG_RETURN(FALSE);
- }
-#endif /* WITH_WSREP */
-
- mdl_request.init(MDL_key::COMMIT, "", "", MDL_SHARED, MDL_EXPLICIT);
-
- if (thd->mdl_context.acquire_lock(&mdl_request,
- thd->variables.lock_wait_timeout))
+ if (thd->mdl_context.upgrade_shared_lock(m_mdl_global_read_lock,
+ MDL_BACKUP_FTWRL2,
+ thd->variables.lock_wait_timeout))
DBUG_RETURN(TRUE);
- m_mdl_blocks_commits_lock= mdl_request.ticket;
m_state= GRL_ACQUIRED_AND_BLOCKS_COMMIT;
#ifdef WITH_WSREP
- /* Native threads should bail out before wsrep oprations to follow.
- Donor servicing thread is an exception, it should pause provider but not desync,
- as it is already desynced in donor state
+ /* Native threads should bail out before wsrep operations to follow.
+ Donor servicing thread is an exception, it should pause provider
+ but not desync, as it is already desynced in donor state.
+ Desync should be called only when we are in synced state.
*/
- if (!WSREP(thd) && !wsrep_node_is_donor())
+ Wsrep_server_state& server_state= Wsrep_server_state::instance();
+ wsrep::seqno paused_seqno;
+ if (server_state.state() == Wsrep_server_state::s_donor ||
+ (WSREP_NNULL(thd) &&
+ server_state.state() != Wsrep_server_state::s_synced))
{
- DBUG_RETURN(FALSE);
+ paused_seqno= server_state.pause();
}
-
- /* if already desynced or donor, avoid double desyncing
- if not in PC and synced, desyncing is not possible either
- */
- if (wsrep_desync || !wsrep_node_is_synced())
+ else if (WSREP_NNULL(thd) &&
+ server_state.state() == Wsrep_server_state::s_synced)
{
- WSREP_DEBUG("desync set upfont, skipping implicit desync for FTWRL: %d",
- wsrep_desync);
+ paused_seqno= server_state.desync_and_pause();
}
else
{
- int rcode;
- WSREP_DEBUG("running implicit desync for node");
- rcode = wsrep->desync(wsrep);
- if (rcode != WSREP_OK)
- {
- WSREP_WARN("FTWRL desync failed %d for schema: %s, query: %s",
- rcode, thd->get_db(), thd->query());
- my_message(ER_LOCK_DEADLOCK, "wsrep desync failed for FTWRL", MYF(0));
- DBUG_RETURN(TRUE);
- }
- }
-
- long long ret = wsrep->pause(wsrep);
- if (ret >= 0)
- {
- wsrep_locked_seqno= ret;
+ DBUG_RETURN(FALSE);
}
- else if (ret != -ENOSYS) /* -ENOSYS - no provider */
+ WSREP_INFO("Server paused at: %lld", paused_seqno.get());
+ if (paused_seqno.get() >= 0)
{
- long long ret = wsrep->pause(wsrep);
- if (ret >= 0)
- {
- wsrep_locked_seqno= ret;
- }
- else if (ret != -ENOSYS) /* -ENOSYS - no provider */
- {
- WSREP_ERROR("Failed to pause provider: %lld (%s)", -ret, strerror(-ret));
-
- DBUG_ASSERT(m_mdl_blocks_commits_lock == NULL);
- wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED;
- my_error(ER_LOCK_DEADLOCK, MYF(0));
- DBUG_RETURN(TRUE);
- }
+ wsrep_locked_seqno= paused_seqno.get();
}
#endif /* WITH_WSREP */
DBUG_RETURN(FALSE);
@@ -1204,10 +1202,8 @@ bool Global_read_lock::make_global_read_lock_block_commit(THD *thd)
void Global_read_lock::set_explicit_lock_duration(THD *thd)
{
- if (m_mdl_global_shared_lock)
- thd->mdl_context.set_lock_duration(m_mdl_global_shared_lock, MDL_EXPLICIT);
- if (m_mdl_blocks_commits_lock)
- thd->mdl_context.set_lock_duration(m_mdl_blocks_commits_lock, MDL_EXPLICIT);
+ if (m_mdl_global_read_lock)
+ thd->mdl_context.set_lock_duration(m_mdl_global_read_lock, MDL_EXPLICIT);
}
/**
diff --git a/sql/log.cc b/sql/log.cc
index 95cb60ea829..eb82100d16c 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -55,10 +55,14 @@
#include "sql_show.h"
#include "my_pthread.h"
#include "semisync_master.h"
-#include "wsrep_mysqld.h"
#include "sp_rcontext.h"
#include "sp_head.h"
+#include "wsrep_mysqld.h"
+#ifdef WITH_WSREP
+#include "wsrep_trans_observer.h"
+#endif /* WITH_WSREP */
+
/* max size of the log message */
#define MAX_LOG_BUFFER_SIZE 1024
#define MAX_TIME_SIZE 32
@@ -869,10 +873,10 @@ bool Log_to_csv_event_handler::
Open_tables_backup open_tables_backup;
CHARSET_INFO *client_cs= thd->variables.character_set_client;
bool save_time_zone_used;
- long query_time= (long) MY_MIN(query_utime/1000000, TIME_MAX_VALUE_SECONDS);
- long lock_time= (long) MY_MIN(lock_utime/1000000, TIME_MAX_VALUE_SECONDS);
- long query_time_micro= (long) (query_utime % 1000000);
- long lock_time_micro= (long) (lock_utime % 1000000);
+ ulong query_time= (ulong) MY_MIN(query_utime/1000000, TIME_MAX_VALUE_SECONDS);
+ ulong lock_time= (ulong) MY_MIN(lock_utime/1000000, TIME_MAX_VALUE_SECONDS);
+ ulong query_time_micro= (ulong) (query_utime % 1000000);
+ ulong lock_time_micro= (ulong) (lock_utime % 1000000);
DBUG_ENTER("Log_to_csv_event_handler::log_slow");
@@ -1165,6 +1169,10 @@ bool LOGGER::error_log_print(enum loglevel level, const char *format,
{
bool error= FALSE;
Log_event_handler **current_handler;
+ THD *thd= current_thd;
+
+ if (likely(thd))
+ thd->error_printed_to_log= 1;
/* currently we don't need locking here as there is no error_log table */
for (current_handler= error_log_handler_list ; *current_handler ;)
@@ -1702,7 +1710,7 @@ static int binlog_close_connection(handlerton *hton, THD *thd)
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
#ifdef WITH_WSREP
if (cache_mngr && !cache_mngr->trx_cache.empty()) {
- IO_CACHE* cache= get_trans_log(thd);
+ IO_CACHE* cache= cache_mngr->get_binlog_cache_log(true);
uchar *buf;
size_t len=0;
wsrep_write_cache_buf(cache, &buf, &len);
@@ -2186,18 +2194,30 @@ void MYSQL_BIN_LOG::set_write_error(THD *thd, bool is_transactional)
{
if (is_transactional)
{
- my_message(ER_TRANS_CACHE_FULL, ER_THD(thd, ER_TRANS_CACHE_FULL), MYF(MY_WME));
+ my_message(ER_TRANS_CACHE_FULL, ER_THD(thd, ER_TRANS_CACHE_FULL), MYF(0));
}
else
{
- my_message(ER_STMT_CACHE_FULL, ER_THD(thd, ER_STMT_CACHE_FULL), MYF(MY_WME));
+ my_message(ER_STMT_CACHE_FULL, ER_THD(thd, ER_STMT_CACHE_FULL), MYF(0));
}
}
else
{
- my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), name, errno);
+ my_error(ER_ERROR_ON_WRITE, MYF(0), name, errno);
}
-
+#ifdef WITH_WSREP
+ /* If wsrep transaction is active and binlog emulation is on,
+ binlog write error may leave transaction without any registered
+ htons. This makes wsrep rollback hooks to be skipped and the
+ transaction will remain alive in wsrep world after rollback.
+ Register binlog hton here to ensure that rollback happens in full. */
+ if (WSREP_EMULATE_BINLOG(thd))
+ {
+ if (is_transactional)
+ trans_register_ha(thd, TRUE, binlog_hton);
+ trans_register_ha(thd, FALSE, binlog_hton);
+ }
+#endif /* WITH_WSREP */
DBUG_VOID_RETURN;
}
@@ -2290,8 +2310,17 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
non-transactional table. Otherwise, truncate the binlog cache starting
from the SAVEPOINT command.
*/
+#ifdef WITH_WSREP
+ /* for streaming replication, we must replicate savepoint rollback so that
+ slaves can maintain SR transactions
+ */
+ if (unlikely(thd->wsrep_trx().is_streaming() ||
+ (trans_has_updated_non_trans_table(thd)) ||
+ (thd->variables.option_bits & OPTION_KEEP_LOG)))
+#else
if (unlikely(trans_has_updated_non_trans_table(thd) ||
(thd->variables.option_bits & OPTION_KEEP_LOG)))
+#endif /* WITH_WSREP */
{
char buf[1024];
String log_query(buf, sizeof(buf), &my_charset_bin);
@@ -2417,7 +2446,7 @@ static void setup_windows_event_source()
// Create the event source registry key
dwError= RegCreateKey(HKEY_LOCAL_MACHINE,
- "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\MySQL",
+ "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\MariaDB",
&hRegKey);
/* Name of the PE module that contains the message resource */
@@ -2449,11 +2478,19 @@ static void setup_windows_event_source()
exceeds FN_REFLEN; (ii) if the number of extensions is exhausted;
or (iii) some other error happened while examining the filesystem.
+ @param name Base name of file
+ @param min_log_number_to_use minimum log number to choose. Set by
+ CHANGE MASTER .. TO
+ @param last_used_log_number If 0, find log number based on files.
+ If not 0, then use *last_used_log_number +1
+ Will be update to new generated number
@return
+ 0 ok
nonzero if not possible to get unique filename.
*/
-static int find_uniq_filename(char *name, ulong next_log_number)
+static int find_uniq_filename(char *name, ulong min_log_number_to_use,
+ ulong *last_used_log_number)
{
uint i;
char buff[FN_REFLEN], ext_buf[FN_REFLEN];
@@ -2472,24 +2509,34 @@ static int find_uniq_filename(char *name, ulong next_log_number)
*end='.';
length= (size_t) (end - start + 1);
- if ((DBUG_EVALUATE_IF("error_unique_log_filename", 1,
- unlikely(!(dir_info= my_dir(buff,
- MYF(MY_DONT_SORT)))))))
- { // This shouldn't happen
- strmov(end,".1"); // use name+1
- DBUG_RETURN(1);
- }
- file_info= dir_info->dir_entry;
- max_found= next_log_number ? next_log_number-1 : 0;
- for (i= dir_info->number_of_files ; i-- ; file_info++)
+ /* The following matches the code for my_dir () below */
+ DBUG_EXECUTE_IF("error_unique_log_filename",
+ {
+ strmov(end,".1");
+ DBUG_RETURN(1);
+ });
+
+ if (*last_used_log_number)
+ max_found= *last_used_log_number;
+ else
{
- if (strncmp(file_info->name, start, length) == 0 &&
- test_if_number(file_info->name+length, &number,0))
+ if (unlikely(!(dir_info= my_dir(buff, MYF(MY_DONT_SORT)))))
+ { // This shouldn't happen
+ strmov(end,".1"); // use name+1
+ DBUG_RETURN(1);
+ }
+ file_info= dir_info->dir_entry;
+ max_found= min_log_number_to_use ? min_log_number_to_use-1 : 0;
+ for (i= dir_info->number_of_files ; i-- ; file_info++)
{
- set_if_bigger(max_found, number);
+ if (strncmp(file_info->name, start, length) == 0 &&
+ test_if_number(file_info->name+length, &number,0))
+ {
+ set_if_bigger(max_found, number);
+ }
}
+ my_dirend(dir_info);
}
- my_dirend(dir_info);
/* check if reached the maximum possible extension number */
if (max_found >= MAX_LOG_UNIQUE_FN_EXT)
@@ -2528,6 +2575,7 @@ index files.", name, ext_buf, (strlen(ext_buf) + (end - name)));
error= 1;
goto end;
}
+ *last_used_log_number= next;
/* print warning if reaching the end of available extensions. */
if ((next > (MAX_LOG_UNIQUE_FN_EXT - LOG_WARN_UNIQUE_FN_EXT_LEFT)))
@@ -2646,7 +2694,7 @@ bool MYSQL_LOG::open(
#endif
if ((file= mysql_file_open(log_file_key, log_file_name, open_flags,
- MYF(MY_WME | ME_WAITTANG))) < 0)
+ MYF(MY_WME))) < 0)
goto err;
if (is_fifo)
@@ -2779,19 +2827,24 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name,
ulong next_log_number)
{
fn_format(new_name, log_name, mysql_data_home, "", 4);
- if (log_type == LOG_BIN)
+ return 0;
+}
+
+int MYSQL_BIN_LOG::generate_new_name(char *new_name, const char *log_name,
+ ulong next_log_number)
+{
+ fn_format(new_name, log_name, mysql_data_home, "", 4);
+ if (!fn_ext(log_name)[0])
{
- if (!fn_ext(log_name)[0])
+ if (DBUG_EVALUATE_IF("binlog_inject_new_name_error", TRUE, FALSE) ||
+ unlikely(find_uniq_filename(new_name, next_log_number,
+ &last_used_log_number)))
{
- if (DBUG_EVALUATE_IF("binlog_inject_new_name_error", TRUE, FALSE) ||
- unlikely(find_uniq_filename(new_name, next_log_number)))
- {
- THD *thd= current_thd;
- if (unlikely(thd))
- my_error(ER_NO_UNIQUE_LOGFILE, MYF(ME_FATALERROR), log_name);
- sql_print_error(ER_DEFAULT(ER_NO_UNIQUE_LOGFILE), log_name);
- return 1;
- }
+ THD *thd= current_thd;
+ if (unlikely(thd))
+ my_error(ER_NO_UNIQUE_LOGFILE, MYF(ME_FATAL), log_name);
+ sql_print_error(ER_DEFAULT(ER_NO_UNIQUE_LOGFILE), log_name);
+ return 1;
}
}
return 0;
@@ -3191,7 +3244,8 @@ const char *MYSQL_LOG::generate_name(const char *log_name,
MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
:reset_master_pending(0), mark_xid_done_waiting(0),
- bytes_written(0), file_id(1), open_count(1),
+ bytes_written(0), last_used_log_number(0),
+ file_id(1), open_count(1),
group_commit_queue(0), group_commit_queue_busy(FALSE),
num_commits(0), num_group_commits(0),
group_commit_trigger_count(0), group_commit_trigger_timeout(0),
@@ -4167,6 +4221,8 @@ bool MYSQL_BIN_LOG::reset_logs(THD *thd, bool create_new_log,
name=0; // Protect against free
close(LOG_CLOSE_TO_BE_OPENED);
+ last_used_log_number= 0; // Reset log number cache
+
/*
First delete all old log files and then update the index file.
As we first delete the log files and do not use sort of logging,
@@ -4448,9 +4504,7 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
0, 0, &log_space_reclaimed);
mysql_mutex_lock(&rli->log_space_lock);
- my_atomic_add64_explicit((volatile int64*)(&rli->log_space_total),
- (-(int64)log_space_reclaimed),
- MY_MEMORY_ORDER_RELAXED);
+ rli->log_space_total-= log_space_reclaimed;
mysql_cond_broadcast(&rli->log_space_cond);
mysql_mutex_unlock(&rli->log_space_lock);
@@ -4622,7 +4676,7 @@ int MYSQL_BIN_LOG::open_purge_index_file(bool destroy)
if (!my_b_inited(&purge_index_file))
{
if ((file= my_open(purge_index_file_name, O_RDWR | O_CREAT | O_BINARY,
- MYF(MY_WME | ME_WAITTANG))) < 0 ||
+ MYF(MY_WME))) < 0 ||
init_io_cache(&purge_index_file, file, IO_SIZE,
(destroy ? WRITE_CACHE : READ_CACHE),
0, 0, MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)))
@@ -5104,7 +5158,11 @@ bool MYSQL_BIN_LOG::is_active(const char *log_file_name_arg)
int MYSQL_BIN_LOG::new_file()
{
- return new_file_impl(1);
+ int res;
+ mysql_mutex_lock(&LOCK_log);
+ res= new_file_impl();
+ mysql_mutex_unlock(&LOCK_log);
+ return res;
}
/*
@@ -5113,7 +5171,7 @@ int MYSQL_BIN_LOG::new_file()
*/
int MYSQL_BIN_LOG::new_file_without_locking()
{
- return new_file_impl(0);
+ return new_file_impl();
}
@@ -5129,7 +5187,7 @@ int MYSQL_BIN_LOG::new_file_without_locking()
The new file name is stored last in the index file
*/
-int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
+int MYSQL_BIN_LOG::new_file_impl()
{
int error= 0, close_on_error= FALSE;
char new_name[FN_REFLEN], *new_name_ptr, *old_name, *file_to_open;
@@ -5138,14 +5196,12 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
File UNINIT_VAR(old_file);
DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl");
- if (need_lock)
- mysql_mutex_lock(&LOCK_log);
+ DBUG_ASSERT(log_type == LOG_BIN);
mysql_mutex_assert_owner(&LOCK_log);
if (!is_open())
{
DBUG_PRINT("info",("log is closed"));
- mysql_mutex_unlock(&LOCK_log);
DBUG_RETURN(error);
}
@@ -5164,7 +5220,7 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
#ifdef ENABLE_AND_FIX_HANG
close_on_error= TRUE;
#endif
- goto end;
+ goto end2;
}
new_name_ptr=new_name;
@@ -5191,7 +5247,7 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
close_on_error= TRUE;
my_printf_error(ER_ERROR_ON_WRITE,
ER_THD_OR_DEFAULT(current_thd, ER_CANT_OPEN_FILE),
- MYF(ME_FATALERROR), name, errno);
+ MYF(ME_FATAL), name, errno);
goto end;
}
bytes_written += r.data_written;
@@ -5260,14 +5316,21 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
/* handle reopening errors */
if (unlikely(error))
{
- my_error(ER_CANT_OPEN_FILE, MYF(ME_FATALERROR), file_to_open, error);
+ my_error(ER_CANT_OPEN_FILE, MYF(ME_FATAL), file_to_open, error);
close_on_error= TRUE;
}
my_free(old_name);
end:
+ /* In case of errors, reuse the last generated log file name */
+ if (unlikely(error))
+ {
+ DBUG_ASSERT(last_used_log_number > 0);
+ last_used_log_number--;
+ }
+end2:
if (delay_close)
{
clear_inuse_flag_when_closing(old_file);
@@ -5293,8 +5356,6 @@ end:
}
mysql_mutex_unlock(&LOCK_index);
- if (need_lock)
- mysql_mutex_unlock(&LOCK_log);
DBUG_RETURN(error);
}
@@ -5654,7 +5715,18 @@ THD::binlog_start_trans_and_stmt()
this->binlog_set_stmt_begin();
bool mstmt_mode= in_multi_stmt_transaction_mode();
#ifdef WITH_WSREP
- /* Write Gtid
+ /*
+ With wsrep binlog emulation we can skip the rest because the
+ binlog cache will not be written into binlog. Note however that
+ because of this the hton callbacks will not get called to clean
+ up the cache, so this must be done explicitly when the transaction
+ terminates.
+ */
+ if (WSREP_EMULATE_BINLOG_NNULL(this))
+ {
+ DBUG_VOID_RETURN;
+ }
+ /* Write Gtid
Get domain id only when gtid mode is set
If this event is replicate through a master then ,
we will forward the same gtid another nodes
@@ -5759,10 +5831,10 @@ int THD::binlog_write_table_map(TABLE *table, bool is_transactional,
/* Ensure that all events in a GTID group are in the same cache */
if (variables.option_bits & OPTION_GTID_BEGIN)
is_transactional= 1;
-
+
/* Pre-conditions */
DBUG_ASSERT(is_current_stmt_binlog_format_row());
- DBUG_ASSERT(WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open());
+ DBUG_ASSERT(WSREP_EMULATE_BINLOG_NNULL(this) || mysql_bin_log.is_open());
DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);
Table_map_log_event
@@ -5960,7 +6032,9 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone,
DBUG_PRINT("enter", ("standalone: %d", standalone));
#ifdef WITH_WSREP
- if (WSREP(thd) && thd->wsrep_trx_meta.gtid.seqno != -1 && wsrep_gtid_mode && !thd->variables.gtid_seq_no)
+ if (WSREP(thd) &&
+ (wsrep_thd_trx_seqno(thd) > 0) &&
+ wsrep_gtid_mode && !thd->variables.gtid_seq_no)
{
domain_id= wsrep_gtid_domain_id;
} else {
@@ -6063,7 +6137,7 @@ MYSQL_BIN_LOG::write_state_to_file()
goto end;
err:
- sql_print_error("Error writing binlog state to file '%s'.\n", buf);
+ sql_print_error("Error writing binlog state to file '%s'.", buf);
if (log_inited)
end_io_cache(&cache);
end:
@@ -6123,7 +6197,7 @@ MYSQL_BIN_LOG::read_state_from_file()
goto end;
err:
- sql_print_error("Error reading binlog GTID state from file '%s'.\n", buf);
+ sql_print_error("Error reading binlog GTID state from file '%s'.", buf);
end:
if (log_inited)
end_io_cache(&cache);
@@ -6277,7 +6351,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
*/
/* applier and replayer can skip writing binlog events */
if ((WSREP_EMULATE_BINLOG(thd) &&
- IF_WSREP(thd->wsrep_exec_mode != REPL_RECV, 0)) || is_open())
+ IF_WSREP(thd->wsrep_cs().mode() == wsrep::client_state::m_local, 0)) || is_open())
{
my_off_t UNINIT_VAR(my_org_b_tell);
#ifdef HAVE_REPLICATION
@@ -6308,11 +6382,26 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
if (direct)
{
+ /* We come here only for incident events */
int res;
uint64 commit_id= 0;
+ MDL_request mdl_request;
DBUG_PRINT("info", ("direct is set"));
+ DBUG_ASSERT(!thd->backup_commit_lock);
+
+ mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT, MDL_EXPLICIT);
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(1);
+ thd->backup_commit_lock= &mdl_request;
+
if ((res= thd->wait_for_prior_commit()))
+ {
+ if (mdl_request.ticket)
+ thd->mdl_context.release_lock(mdl_request.ticket);
+ thd->backup_commit_lock= 0;
DBUG_RETURN(res);
+ }
file= &log_file;
my_org_b_tell= my_b_tell(file);
mysql_mutex_lock(&LOCK_log);
@@ -6327,7 +6416,11 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
commit_name.length);
commit_id= entry->val_int(&null_value);
});
- if (write_gtid_event(thd, true, using_trans, commit_id))
+ res= write_gtid_event(thd, true, using_trans, commit_id);
+ if (mdl_request.ticket)
+ thd->mdl_context.release_lock(mdl_request.ticket);
+ thd->backup_commit_lock= 0;
+ if (res)
goto err;
}
else
@@ -6468,25 +6561,8 @@ err:
it's list before dump-thread tries to send it
*/
update_binlog_end_pos(offset);
- /*
- If a transaction with the LOAD DATA statement is divided
- into logical mini-transactions (of the 10K rows) and binlog
- is rotated, then the last portion of data may be lost due to
- wsrep handler re-registration at the boundary of the split.
- Since splitting of the LOAD DATA into mini-transactions is
- logical, we should not allow these mini-transactions to fall
- into separate binlogs. Therefore, it is necessary to prohibit
- the rotation of binlog in the middle of processing LOAD DATA:
- */
-#ifdef WITH_WSREP
- if (!thd->wsrep_split_flag)
- {
-#endif /* WITH_WSREP */
if (unlikely((error= rotate(false, &check_purge))))
check_purge= false;
-#ifdef WITH_WSREP
- }
-#endif /* WITH_WSREP */
}
}
}
@@ -7227,25 +7303,8 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd)
likely(!(error= flush_and_sync(0))))
{
update_binlog_end_pos();
- /*
- If a transaction with the LOAD DATA statement is divided
- into logical mini-transactions (of the 10K rows) and binlog
- is rotated, then the last portion of data may be lost due to
- wsrep handler re-registration at the boundary of the split.
- Since splitting of the LOAD DATA into mini-transactions is
- logical, we should not allow these mini-transactions to fall
- into separate binlogs. Therefore, it is necessary to prohibit
- the rotation of binlog in the middle of processing LOAD DATA:
- */
-#ifdef WITH_WSREP
- if (!thd->wsrep_split_flag)
- {
-#endif /* WITH_WSREP */
if (unlikely((error= rotate(false, &check_purge))))
check_purge= false;
-#ifdef WITH_WSREP
- }
-#endif /* WITH_WSREP */
}
offset= my_b_tell(&log_file);
@@ -7296,7 +7355,7 @@ MYSQL_BIN_LOG::write_binlog_checkpoint_event_already_locked(const char *name_arg
ability to do crash recovery - crash recovery will just have to scan a
bit more of the binlog than strictly necessary.
*/
- sql_print_error("Failed to write binlog checkpoint event to binary log\n");
+ sql_print_error("Failed to write binlog checkpoint event to binary log");
}
offset= my_b_tell(&log_file);
@@ -7426,6 +7485,8 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd,
new transaction directly to participate in the group commit.
@retval < 0 Error
+ @retval -2 WSREP error with commit ordering
+ @retval -3 WSREP return code to mark the leader
@retval > 0 If queued as the first entry in the queue (meaning this
is the leader)
@retval 0 Otherwise (queued as participant, leader handles the commit)
@@ -7437,7 +7498,11 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
group_commit_entry *entry, *orig_queue, *last;
wait_for_commit *cur;
wait_for_commit *wfc;
+ bool backup_lock_released= 0;
+ int result= 0;
+ THD *thd= orig_entry->thd;
DBUG_ENTER("MYSQL_BIN_LOG::queue_for_group_commit");
+ DBUG_ASSERT(thd == current_thd);
/*
Check if we need to wait for another transaction to commit before us.
@@ -7449,8 +7514,10 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
*/
wfc= orig_entry->thd->wait_for_commit_ptr;
orig_entry->queued_by_other= false;
- if (wfc && wfc->waitee)
+ if (wfc && wfc->waitee.load(std::memory_order_acquire))
{
+ wait_for_commit *loc_waitee;
+
mysql_mutex_lock(&wfc->LOCK_wait_commit);
/*
Do an extra check here, this time safely under lock.
@@ -7462,10 +7529,25 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
before setting the flag, so there is no risk that we can queue ahead of
it.
*/
- if (wfc->waitee && !wfc->waitee->commit_started)
+ if ((loc_waitee= wfc->waitee.load(std::memory_order_relaxed)) &&
+ !loc_waitee->commit_started)
{
PSI_stage_info old_stage;
- wait_for_commit *loc_waitee;
+
+ /*
+ Release MDL_BACKUP_COMMIT LOCK while waiting for other threads to
+ commit.
+ This is needed to avoid deadlock between the other threads (which not
+ yet have the MDL_BACKUP_COMMIT_LOCK) and any threads using
+ BACKUP LOCK BLOCK_COMMIT.
+ */
+ if (thd->backup_commit_lock && thd->backup_commit_lock->ticket &&
+ !backup_lock_released)
+ {
+ backup_lock_released= 1;
+ thd->mdl_context.release_lock(thd->backup_commit_lock->ticket);
+ thd->backup_commit_lock->ticket= 0;
+ }
/*
By setting wfc->opaque_pointer to our own entry, we mark that we are
@@ -7487,7 +7569,8 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
&wfc->LOCK_wait_commit,
&stage_waiting_for_prior_transaction_to_commit,
&old_stage);
- while ((loc_waitee= wfc->waitee) && !orig_entry->thd->check_killed(1))
+ while ((loc_waitee= wfc->waitee.load(std::memory_order_relaxed)) &&
+ !orig_entry->thd->check_killed(1))
mysql_cond_wait(&wfc->COND_wait_commit, &wfc->LOCK_wait_commit);
wfc->opaque_pointer= NULL;
DBUG_PRINT("info", ("After waiting for prior commit, queued_by_other=%d",
@@ -7505,14 +7588,18 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
do
{
mysql_cond_wait(&wfc->COND_wait_commit, &wfc->LOCK_wait_commit);
- } while (wfc->waitee);
+ } while (wfc->waitee.load(std::memory_order_relaxed));
}
else
{
/* We were killed, so remove us from the list of waitee. */
wfc->remove_from_list(&loc_waitee->subsequent_commits_list);
mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit);
- wfc->waitee= NULL;
+ /*
+ This is the thread clearing its own status, it is no longer on
+ the list of waiters. So no memory barriers are needed here.
+ */
+ wfc->waitee.store(NULL, std::memory_order_relaxed);
orig_entry->thd->EXIT_COND(&old_stage);
/* Interrupted by kill. */
@@ -7522,7 +7609,8 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
wfc->wakeup_error= ER_QUERY_INTERRUPTED;
my_message(wfc->wakeup_error,
ER_THD(orig_entry->thd, wfc->wakeup_error), MYF(0));
- DBUG_RETURN(-1);
+ result= -1;
+ goto end;
}
}
orig_entry->thd->EXIT_COND(&old_stage);
@@ -7536,12 +7624,13 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
then there is nothing else to do.
*/
if (orig_entry->queued_by_other)
- DBUG_RETURN(0);
+ goto end;
if (wfc && wfc->wakeup_error)
{
my_error(ER_PRIOR_COMMIT_FAILED, MYF(0));
- DBUG_RETURN(-1);
+ result= -1;
+ goto end;
}
/* Now enqueue ourselves in the group commit queue. */
@@ -7695,6 +7784,22 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
cur= entry->thd->wait_for_commit_ptr;
}
+#ifdef WITH_WSREP
+ if (wsrep_is_active(entry->thd) &&
+ wsrep_run_commit_hook(entry->thd, entry->all))
+ {
+ /* Release commit order here */
+ if (wsrep_ordered_commit(entry->thd, entry->all, wsrep_apply_error()))
+ result= -2;
+
+ /* return -3, if this is leader */
+ if (orig_queue == NULL)
+ result= -3;
+ }
+ else
+ DBUG_ASSERT(result != -2 && result != -3);
+#endif /* WITH_WSREP */
+
if (opt_binlog_commit_wait_count > 0 && orig_queue != NULL)
mysql_cond_signal(&COND_prepare_ordered);
mysql_mutex_unlock(&LOCK_prepare_ordered);
@@ -7702,13 +7807,45 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
DBUG_PRINT("info", ("Queued for group commit as %s",
(orig_queue == NULL) ? "leader" : "participant"));
- DBUG_RETURN(orig_queue == NULL);
+ result= orig_queue == NULL;
+
+end:
+ if (backup_lock_released)
+ thd->mdl_context.acquire_lock(thd->backup_commit_lock,
+ thd->variables.lock_wait_timeout);
+ DBUG_RETURN(result);
}
bool
MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
{
int is_leader= queue_for_group_commit(entry);
+#ifdef WITH_WSREP
+ /* commit order was released in queue_for_group_commit() call,
+ here we check if wsrep_commit_ordered() failed or if we are leader */
+ switch (is_leader)
+ {
+ case -2: /* wsrep_ordered_commit() has failed */
+ DBUG_ASSERT(wsrep_is_active(entry->thd));
+ DBUG_ASSERT(wsrep_run_commit_hook(entry->thd, entry->all));
+ entry->thd->wakeup_subsequent_commits(1);
+ return true;
+ case -3: /* this is leader, wait for prior commit to
+ complete. This establishes total order for group leaders
+ */
+ DBUG_ASSERT(wsrep_is_active(entry->thd));
+ DBUG_ASSERT(wsrep_run_commit_hook(entry->thd, entry->all));
+ if (entry->thd->wait_for_prior_commit())
+ return true;
+
+ /* retain the correct is_leader value */
+ is_leader= 1;
+ break;
+
+ default: /* native MariaDB cases */
+ break;
+ }
+#endif /* WITH_WSREP */
/*
The first in the queue handles group commit for all; the others just wait
@@ -7791,10 +7928,10 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
switch (entry->error)
{
case ER_ERROR_ON_WRITE:
- my_error(ER_ERROR_ON_WRITE, MYF(ME_NOREFRESH), name, entry->commit_errno);
+ my_error(ER_ERROR_ON_WRITE, MYF(ME_ERROR_LOG), name, entry->commit_errno);
break;
case ER_ERROR_ON_READ:
- my_error(ER_ERROR_ON_READ, MYF(ME_NOREFRESH),
+ my_error(ER_ERROR_ON_READ, MYF(ME_ERROR_LOG),
entry->error_cache->file_name, entry->commit_errno);
break;
default:
@@ -7805,7 +7942,7 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
*/
my_printf_error(entry->error,
"Error writing transaction to binary log: %d",
- MYF(ME_NOREFRESH), entry->error);
+ MYF(ME_ERROR_LOG), entry->error);
}
/*
@@ -8014,20 +8151,6 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
mark_xids_active(binlog_id, xid_count);
}
- /*
- If a transaction with the LOAD DATA statement is divided
- into logical mini-transactions (of the 10K rows) and binlog
- is rotated, then the last portion of data may be lost due to
- wsrep handler re-registration at the boundary of the split.
- Since splitting of the LOAD DATA into mini-transactions is
- logical, we should not allow these mini-transactions to fall
- into separate binlogs. Therefore, it is necessary to prohibit
- the rotation of binlog in the middle of processing LOAD DATA:
- */
-#ifdef WITH_WSREP
- if (!leader->thd->wsrep_split_flag)
- {
-#endif /* WITH_WSREP */
if (rotate(false, &check_purge))
{
/*
@@ -8044,12 +8167,9 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
when the transaction has been safely committed in the engine.
*/
leader->cache_mngr->delayed_error= true;
- my_error(ER_ERROR_ON_WRITE, MYF(ME_NOREFRESH), name, errno);
+ my_error(ER_ERROR_ON_WRITE, MYF(ME_ERROR_LOG), name, errno);
check_purge= false;
}
-#ifdef WITH_WSREP
- }
-#endif /* WITH_WSREP */
/* In case of binlog rotate, update the correct current binlog offset. */
commit_offset= my_b_write_tell(&log_file);
}
@@ -8703,18 +8823,35 @@ bool flush_error_log()
}
#ifdef _WIN32
+struct eventlog_source
+{
+ HANDLE handle;
+ eventlog_source()
+ {
+ setup_windows_event_source();
+ handle = RegisterEventSource(NULL, "MariaDB");
+ }
+
+ ~eventlog_source()
+ {
+ if (handle)
+ DeregisterEventSource(handle);
+ }
+};
+
+static eventlog_source eventlog;
+
static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,
size_t length, size_t buffLen)
{
- HANDLE event;
+ HANDLE event= eventlog.handle;
char *buffptr= buff;
DBUG_ENTER("print_buffer_to_nt_eventlog");
/* Add ending CR/LF's to string, overwrite last chars if necessary */
strmov(buffptr+MY_MIN(length, buffLen-5), "\r\n\r\n");
- setup_windows_event_source();
- if ((event= RegisterEventSource(NULL,"MySQL")))
+ if (event)
{
switch (level) {
case ERROR_LEVEL:
@@ -8730,7 +8867,6 @@ static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,
0, (LPCSTR*) &buffptr, NULL);
break;
}
- DeregisterEventSource(event);
}
DBUG_VOID_RETURN;
@@ -10531,7 +10667,7 @@ set_binlog_snapshot_file(const char *src)
Copy out current values of status variables, for SHOW STATUS or
information_schema.global_status.
- This is called only under LOCK_show_status, so we can fill in a static array.
+ This is called only under LOCK_all_status_vars, so we can fill in a static array.
*/
void
TC_LOG_BINLOG::set_status_variables(THD *thd)
@@ -10656,7 +10792,9 @@ maria_declare_plugin(binlog)
maria_declare_plugin_end;
#ifdef WITH_WSREP
-IO_CACHE * get_trans_log(THD * thd)
+#include "wsrep_mysqld.h"
+
+IO_CACHE *wsrep_get_trans_cache(THD * thd)
{
DBUG_ASSERT(binlog_hton->slot != HA_SLOT_UNDEF);
binlog_cache_mngr *cache_mngr = (binlog_cache_mngr*)
@@ -10669,45 +10807,86 @@ IO_CACHE * get_trans_log(THD * thd)
return NULL;
}
-
-bool wsrep_trans_cache_is_empty(THD *thd)
-{
- binlog_cache_mngr *const cache_mngr=
- (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
- return (!cache_mngr || cache_mngr->trx_cache.empty());
-}
-
-
-void thd_binlog_trx_reset(THD * thd)
+void wsrep_thd_binlog_trx_reset(THD * thd)
{
+ DBUG_ENTER("wsrep_thd_binlog_trx_reset");
+ WSREP_DEBUG("wsrep_thd_binlog_reset");
/*
todo: fix autocommit select to not call the caller
*/
- if (thd_get_ha_data(thd, binlog_hton) != NULL)
+ binlog_cache_mngr *const cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
+ if (cache_mngr)
{
- binlog_cache_mngr *const cache_mngr=
- (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
- if (cache_mngr)
+ cache_mngr->reset(false, true);
+ if (!cache_mngr->stmt_cache.empty())
{
- cache_mngr->reset(false, true);
- if (!cache_mngr->stmt_cache.empty())
- {
- WSREP_DEBUG("pending events in stmt cache, sql: %s", thd->query());
- cache_mngr->stmt_cache.reset();
- }
+ WSREP_DEBUG("pending events in stmt cache, sql: %s", thd->query());
+ cache_mngr->stmt_cache.reset();
}
}
thd->clear_binlog_table_maps();
+ DBUG_VOID_RETURN;
}
-
-void thd_binlog_rollback_stmt(THD * thd)
+void wsrep_thd_binlog_stmt_rollback(THD * thd)
{
- WSREP_DEBUG("thd_binlog_rollback_stmt connection: %llu",
- thd->thread_id);
+ DBUG_ENTER("wsrep_thd_binlog_stmt_rollback");
+ WSREP_DEBUG("wsrep_thd_binlog_stmt_rollback");
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
if (cache_mngr)
- cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
+ {
+ thd->binlog_remove_pending_rows_event(TRUE, TRUE);
+ cache_mngr->stmt_cache.reset();
+ }
+ DBUG_VOID_RETURN;
}
+
+void wsrep_register_binlog_handler(THD *thd, bool trx)
+{
+ DBUG_ENTER("register_binlog_handler");
+ /*
+ If this is the first call to this function while processing a statement,
+ the transactional cache does not have a savepoint defined. So, in what
+ follows:
+ . an implicit savepoint is defined;
+ . callbacks are registered;
+ . binary log is set as read/write.
+
+ The savepoint allows for truncating the trx-cache transactional changes
+ fail. Callbacks are necessary to flush caches upon committing or rolling
+ back a statement or a transaction. However, notifications do not happen
+ if the binary log is set as read/write.
+ */
+ binlog_cache_mngr *cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
+ /* cache_mngr may be missing e.g. in mtr test ev51914.test */
+ if (cache_mngr)
+ {
+ /*
+ Set an implicit savepoint in order to be able to truncate a trx-cache.
+ */
+ if (cache_mngr->trx_cache.get_prev_position() == MY_OFF_T_UNDEF)
+ {
+ my_off_t pos= 0;
+ binlog_trans_log_savepos(thd, &pos);
+ cache_mngr->trx_cache.set_prev_position(pos);
+ }
+
+ /*
+ Set callbacks in order to be able to call commmit or rollback.
+ */
+ if (trx)
+ trans_register_ha(thd, TRUE, binlog_hton);
+ trans_register_ha(thd, FALSE, binlog_hton);
+
+ /*
+ Set the binary log as read/write otherwise callbacks are not called.
+ */
+ thd->ha_data[binlog_hton->slot].ha_info[0].set_trx_read_write();
+ }
+ DBUG_VOID_RETURN;
+}
+
#endif /* WITH_WSREP */
diff --git a/sql/log.h b/sql/log.h
index b3b598e08ea..8ba39614825 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -18,7 +18,6 @@
#define LOG_H
#include "handler.h" /* my_xid */
-#include "wsrep.h"
#include "wsrep_mysqld.h"
#include "rpl_constants.h"
@@ -249,10 +248,6 @@ extern TC_LOG_DUMMY tc_log_dummy;
class Relay_log_info;
-#ifdef HAVE_PSI_INTERFACE
-extern PSI_mutex_key key_LOG_INFO_lock;
-#endif
-
/*
Note that we destroy the lock mutex in the desctructor here.
This means that object instances cannot be destroyed/go out of scope,
@@ -264,19 +259,11 @@ typedef struct st_log_info
my_off_t index_file_offset, index_file_start_offset;
my_off_t pos;
bool fatal; // if the purge happens to give us a negative offset
- mysql_mutex_t lock;
st_log_info() : index_file_offset(0), index_file_start_offset(0),
pos(0), fatal(0)
{
DBUG_ENTER("LOG_INFO");
log_file_name[0] = '\0';
- mysql_mutex_init(key_LOG_INFO_lock, &lock, MY_MUTEX_INIT_FAST);
- DBUG_VOID_RETURN;
- }
- ~st_log_info()
- {
- DBUG_ENTER("~LOG_INFO");
- mysql_mutex_destroy(&lock);
DBUG_VOID_RETURN;
}
} LOG_INFO;
@@ -307,6 +294,7 @@ class MYSQL_LOG
{
public:
MYSQL_LOG();
+ virtual ~MYSQL_LOG() {}
void init_pthread_objects();
void cleanup();
bool open(
@@ -329,8 +317,8 @@ public:
const char *generate_name(const char *log_name,
const char *suffix,
bool strip_ext, char *buff);
- int generate_new_name(char *new_name, const char *log_name,
- ulong next_log_number);
+ virtual int generate_new_name(char *new_name, const char *log_name,
+ ulong next_log_number);
protected:
/* LOCK_log is inited by init_pthread_objects() */
mysql_mutex_t LOCK_log;
@@ -517,6 +505,11 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
GLOBAL MAX_BINLOG_SIZE|MAX_RELAY_LOG_SIZE) from sys_vars.cc
*/
ulong max_size;
+ /*
+ Number generated by last call of find_uniq_filename(). Corresponds
+ closely with current_binlog_id
+ */
+ ulong last_used_log_number;
// current file sequence number for load data infile binary logging
uint file_id;
uint open_count; // For replication
@@ -563,7 +556,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
LOCK_log.
*/
int new_file_without_locking();
- int new_file_impl(bool need_lock);
+ int new_file_impl();
void do_checkpoint_request(ulong binlog_id);
void purge();
int write_transaction_or_stmt(group_commit_entry *entry, uint64 commit_id);
@@ -707,6 +700,8 @@ public:
int open(const char *opt_name);
void close();
+ virtual int generate_new_name(char *new_name, const char *log_name,
+ ulong next_log_number);
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
@@ -725,15 +720,13 @@ public:
{
bytes_written = 0;
}
- void harvest_bytes_written(ulonglong* counter)
+ void harvest_bytes_written(Atomic_counter<uint64> *counter)
{
#ifndef DBUG_OFF
char buf1[22],buf2[22];
#endif
DBUG_ENTER("harvest_bytes_written");
-
- my_atomic_add64_explicit((volatile int64*)(counter), bytes_written,
- MY_MEMORY_ORDER_RELAXED);
+ (*counter)+=bytes_written;
DBUG_PRINT("info",("counter: %s bytes_written: %s", llstr(*counter,buf1),
llstr(bytes_written,buf2)));
bytes_written=0;
@@ -1226,6 +1219,11 @@ static inline TC_LOG *get_tc_log_implementation()
return &tc_log_mmap;
}
+#ifdef WITH_WSREP
+IO_CACHE* wsrep_get_trans_cache(THD *);
+void wsrep_thd_binlog_trx_reset(THD * thd);
+void wsrep_thd_binlog_stmt_rollback(THD * thd);
+#endif /* WITH_WSREP */
class Gtid_list_log_event;
const char *
diff --git a/sql/log_event.cc b/sql/log_event.cc
index cfebdaa3e3c..349e35488e2 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -54,7 +54,6 @@
#include "rpl_constants.h"
#include "sql_digest.h"
#include "zlib.h"
-#include "my_atomic.h"
#define my_b_write_string(A, B) my_b_write((A), (uchar*)(B), (uint) (sizeof(B) - 1))
@@ -102,16 +101,11 @@ TYPELIB binlog_checksum_typelib=
TODO: correct the constant when it has been determined
(which main tree to push and when)
*/
-const uchar checksum_version_split_mysql[3]= {5, 6, 1};
-const ulong checksum_version_product_mysql=
- (checksum_version_split_mysql[0] * 256 +
- checksum_version_split_mysql[1]) * 256 +
- checksum_version_split_mysql[2];
-const uchar checksum_version_split_mariadb[3]= {5, 3, 0};
-const ulong checksum_version_product_mariadb=
- (checksum_version_split_mariadb[0] * 256 +
- checksum_version_split_mariadb[1]) * 256 +
- checksum_version_split_mariadb[2];
+const Version checksum_version_split_mysql(5, 6, 1);
+const Version checksum_version_split_mariadb(5, 3, 0);
+
+// First MySQL version with fraction seconds
+const Version fsp_version_split_mysql(5, 6, 0);
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
static int rows_event_stmt_cleanup(rpl_group_info *rgi, THD* thd);
@@ -1639,8 +1633,11 @@ int Log_event_writer::encrypt_and_write(const uchar *pos, size_t len)
return 1;
uint dstlen;
- if (encryption_ctx_update(ctx, pos, (uint)len, dst, &dstlen))
+ if (len == 0)
+ dstlen= 0;
+ else if (encryption_ctx_update(ctx, pos, (uint)len, dst, &dstlen))
goto err;
+
if (maybe_write_event_len(dst, dstlen))
return 1;
pos= dst;
@@ -1858,8 +1855,16 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
{
uchar iv[BINLOG_IV_LENGTH];
fdle->crypto_data.set_iv(iv, (uint32) (my_b_tell(file) - data_len));
-
- char *newpkt= (char*)my_malloc(data_len + ev_offset + 1, MYF(MY_WME));
+ size_t sz= data_len + ev_offset + 1;
+#ifdef HAVE_WOLFSSL
+ /*
+ Workaround for MDEV-19582.
+ WolfSSL reads memory out of bounds with decryption/NOPAD)
+ We allocate a little more memory therefore.
+ */
+ sz += MY_AES_BLOCK_SIZE;
+#endif
+ char *newpkt= (char*)my_malloc(sz, MYF(MY_WME));
if (!newpkt)
DBUG_RETURN(LOG_READ_MEM);
memcpy(newpkt, packet->ptr(), ev_offset);
@@ -2761,9 +2766,7 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info,
goto return_null;
uint bin_size= my_decimal_get_binary_size(precision, decimals);
- my_decimal dec;
- binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) ptr, &dec,
- precision, decimals);
+ my_decimal dec((const uchar *) ptr, precision, decimals);
int length= DECIMAL_MAX_STR_LENGTH;
char buff[DECIMAL_MAX_STR_LENGTH + 1];
decimal2string(&dec, buff, &length, 0, 0, 0);
@@ -4336,9 +4339,14 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, size_t que
/*
If Query_log_event will contain non trans keyword (not BEGIN, COMMIT,
SAVEPOINT or ROLLBACK) we disable PA for this transaction.
+ Note that here WSREP(thd) might not be true e.g. when wsrep_shcema
+ is created we create tables with thd->variables.wsrep_on=false
+ to avoid replicating wsrep_schema tables to other nodes.
*/
if (WSREP_ON && !is_trans_keyword())
+ {
thd->wsrep_PA_safe= false;
+ }
#endif /* WITH_WSREP */
memset(&user, 0, sizeof(user));
@@ -4420,41 +4428,44 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, size_t que
bool trx_cache= FALSE;
cache_type= Log_event::EVENT_INVALID_CACHE;
- switch (lex->sql_command)
+ if (!direct)
{
- case SQLCOM_DROP_TABLE:
- case SQLCOM_DROP_SEQUENCE:
- use_cache= (lex->tmp_table() && thd->in_multi_stmt_transaction_mode());
- break;
+ switch (lex->sql_command)
+ {
+ case SQLCOM_DROP_TABLE:
+ case SQLCOM_DROP_SEQUENCE:
+ use_cache= (lex->tmp_table() && thd->in_multi_stmt_transaction_mode());
+ break;
- case SQLCOM_CREATE_TABLE:
- case SQLCOM_CREATE_SEQUENCE:
- /*
- If we are using CREATE ... SELECT or if we are a slave
- executing BEGIN...COMMIT (generated by CREATE...SELECT) we
- have to use the transactional cache to ensure we don't
- calculate any checksum for the CREATE part.
- */
- trx_cache= (lex->select_lex.item_list.elements &&
- thd->is_current_stmt_binlog_format_row()) ||
- (thd->variables.option_bits & OPTION_GTID_BEGIN);
- use_cache= (lex->tmp_table() &&
- thd->in_multi_stmt_transaction_mode()) || trx_cache;
- break;
- case SQLCOM_SET_OPTION:
- if (lex->autocommit)
- use_cache= trx_cache= FALSE;
- else
- use_cache= TRUE;
- break;
- case SQLCOM_RELEASE_SAVEPOINT:
- case SQLCOM_ROLLBACK_TO_SAVEPOINT:
- case SQLCOM_SAVEPOINT:
- use_cache= trx_cache= TRUE;
- break;
- default:
- use_cache= sqlcom_can_generate_row_events(thd);
- break;
+ case SQLCOM_CREATE_TABLE:
+ case SQLCOM_CREATE_SEQUENCE:
+ /*
+ If we are using CREATE ... SELECT or if we are a slave
+ executing BEGIN...COMMIT (generated by CREATE...SELECT) we
+ have to use the transactional cache to ensure we don't
+ calculate any checksum for the CREATE part.
+ */
+ trx_cache= (lex->first_select_lex()->item_list.elements &&
+ thd->is_current_stmt_binlog_format_row()) ||
+ (thd->variables.option_bits & OPTION_GTID_BEGIN);
+ use_cache= (lex->tmp_table() &&
+ thd->in_multi_stmt_transaction_mode()) || trx_cache;
+ break;
+ case SQLCOM_SET_OPTION:
+ if (lex->autocommit)
+ use_cache= trx_cache= FALSE;
+ else
+ use_cache= TRUE;
+ break;
+ case SQLCOM_RELEASE_SAVEPOINT:
+ case SQLCOM_ROLLBACK_TO_SAVEPOINT:
+ case SQLCOM_SAVEPOINT:
+ use_cache= trx_cache= TRUE;
+ break;
+ default:
+ use_cache= sqlcom_can_generate_row_events(thd);
+ break;
+ }
}
if (!use_cache || direct)
@@ -4801,6 +4812,30 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
}
}
+#if !defined(MYSQL_CLIENT)
+ if (description_event->server_version_split.kind ==
+ Format_description_log_event::master_version_split::KIND_MYSQL)
+ {
+ // Handle MariaDB/MySQL incompatible sql_mode bits
+ sql_mode_t mysql_sql_mode= sql_mode;
+ sql_mode&= MODE_MASK_MYSQL_COMPATIBLE; // Unset MySQL specific bits
+
+ /*
+ sql_mode flags related to fraction second rounding/truncation
+ have opposite meaning in MySQL vs MariaDB.
+ MySQL:
+ - rounds fractional seconds by default
+ - truncates if TIME_TRUNCATE_FRACTIONAL is set
+ MariaDB:
+ - truncates fractional seconds by default
+ - rounds if TIME_ROUND_FRACTIONAL is set
+ */
+ if (description_event->server_version_split >= fsp_version_split_mysql &&
+ !(mysql_sql_mode & MODE_MYSQL80_TIME_TRUNCATE_FRACTIONAL))
+ sql_mode|= MODE_TIME_ROUND_FRACTIONAL;
+ }
+#endif
+
/**
Layout for the data buffer is as follows
+--------+-----------+------+------+---------+----+-------+
@@ -5635,7 +5670,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
gtid= rgi->current_gtid;
if (unlikely(rpl_global_gtid_slave_state->record_gtid(thd, &gtid,
sub_id,
- rgi, false,
+ true, false,
&hton)))
{
int errcode= thd->get_stmt_da()->sql_errno();
@@ -5815,6 +5850,14 @@ compare_errors:
"unexpected success or fatal error"),
thd->get_db(), query_arg);
thd->is_slave_error= 1;
+#ifdef WITH_WSREP
+ if (wsrep_thd_is_toi(thd) && wsrep_must_ignore_error(thd))
+ {
+ thd->clear_error(1);
+ thd->killed= NOT_KILLED;
+ thd->wsrep_has_ignored_error= true;
+ }
+#endif /* WITH_WSREP */
}
/*
@@ -6610,26 +6653,24 @@ bool Format_description_log_event::start_decryption(Start_encryption_log_event*
return crypto_data.init(sele->crypto_scheme, sele->key_version);
}
-static inline void
-do_server_version_split(char* version,
- Format_description_log_event::master_version_split *split_versions)
+
+Version::Version(const char *version, const char **endptr)
{
- char *p= version, *r;
+ const char *p= version;
ulong number;
for (uint i= 0; i<=2; i++)
{
+ char *r;
number= strtoul(p, &r, 10);
/*
It is an invalid version if any version number greater than 255 or
first number is not followed by '.'.
*/
if (number < 256 && (*r == '.' || i != 0))
- split_versions->ver[i]= (uchar) number;
+ m_ver[i]= (uchar) number;
else
{
- split_versions->ver[0]= 0;
- split_versions->ver[1]= 0;
- split_versions->ver[2]= 0;
+ *this= Version();
break;
}
@@ -6637,12 +6678,19 @@ do_server_version_split(char* version,
if (*r == '.')
p++; // skip the dot
}
+ endptr[0]= p;
+}
+
+
+Format_description_log_event::
+ master_version_split::master_version_split(const char *version)
+{
+ const char *p;
+ static_cast<Version*>(this)[0]= Version(version, &p);
if (strstr(p, "MariaDB") != 0 || strstr(p, "-maria-") != 0)
- split_versions->kind=
- Format_description_log_event::master_version_split::KIND_MARIADB;
+ kind= KIND_MARIADB;
else
- split_versions->kind=
- Format_description_log_event::master_version_split::KIND_MYSQL;
+ kind= KIND_MYSQL;
}
@@ -6656,20 +6704,14 @@ do_server_version_split(char* version,
*/
void Format_description_log_event::calc_server_version_split()
{
- do_server_version_split(server_version, &server_version_split);
+ server_version_split= master_version_split(server_version);
DBUG_PRINT("info",("Format_description_log_event::server_version_split:"
" '%s' %d %d %d", server_version,
- server_version_split.ver[0],
- server_version_split.ver[1], server_version_split.ver[2]));
+ server_version_split[0],
+ server_version_split[1], server_version_split[2]));
}
-static inline ulong
-version_product(const Format_description_log_event::master_version_split* version_split)
-{
- return ((version_split->ver[0] * 256 + version_split->ver[1]) * 256
- + version_split->ver[2]);
-}
/**
@return TRUE is the event's version is earlier than one that introduced
@@ -6679,9 +6721,9 @@ bool
Format_description_log_event::is_version_before_checksum(const master_version_split
*version_split)
{
- return version_product(version_split) <
+ return *version_split <
(version_split->kind == master_version_split::KIND_MARIADB ?
- checksum_version_product_mariadb : checksum_version_product_mysql);
+ checksum_version_split_mariadb : checksum_version_split_mysql);
}
/**
@@ -6697,7 +6739,6 @@ enum enum_binlog_checksum_alg get_checksum_alg(const char* buf, ulong len)
{
enum enum_binlog_checksum_alg ret;
char version[ST_SERVER_VER_LEN];
- Format_description_log_event::master_version_split version_split;
DBUG_ENTER("get_checksum_alg");
DBUG_ASSERT(buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT);
@@ -6707,7 +6748,7 @@ enum enum_binlog_checksum_alg get_checksum_alg(const char* buf, ulong len)
ST_SERVER_VER_LEN);
version[ST_SERVER_VER_LEN - 1]= 0;
- do_server_version_split(version, &version_split);
+ Format_description_log_event::master_version_split version_split(version);
ret= Format_description_log_event::is_version_before_checksum(&version_split)
? BINLOG_CHECKSUM_ALG_UNDEF
: (enum_binlog_checksum_alg)buf[len - BINLOG_CHECKSUM_LEN - BINLOG_CHECKSUM_ALG_DESC_LEN];
@@ -7458,8 +7499,9 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi,
ex.skip_lines = skip_lines;
List<Item> field_list;
- thd->lex->select_lex.context.resolve_in_table_list_only(&tables);
- set_fields(tables.db.str, field_list, &thd->lex->select_lex.context);
+ thd->lex->first_select_lex()->context.resolve_in_table_list_only(&tables);
+ set_fields(tables.db.str,
+ field_list, &thd->lex->first_select_lex()->context);
thd->variables.pseudo_thread_id= thread_id;
if (net)
{
@@ -8098,16 +8140,13 @@ Gtid_log_event::do_apply_event(rpl_group_info *rgi)
switch (flags2 & (FL_DDL | FL_TRANSACTIONAL))
{
case FL_TRANSACTIONAL:
- my_atomic_add64_explicit((volatile int64 *)&mi->total_trans_groups, 1,
- MY_MEMORY_ORDER_RELAXED);
+ mi->total_trans_groups++;
break;
case FL_DDL:
- my_atomic_add64_explicit((volatile int64 *)&mi->total_ddl_groups, 1,
- MY_MEMORY_ORDER_RELAXED);
+ mi->total_ddl_groups++;
break;
default:
- my_atomic_add64_explicit((volatile int64 *)&mi->total_non_trans_groups, 1,
- MY_MEMORY_ORDER_RELAXED);
+ mi->total_non_trans_groups++;
}
if (flags2 & FL_STANDALONE)
@@ -8440,7 +8479,7 @@ Gtid_list_log_event::do_apply_event(rpl_group_info *rgi)
{
if ((ret= rpl_global_gtid_slave_state->record_gtid(thd, &list[i],
sub_id_list[i],
- NULL, false, &hton)))
+ false, false, &hton)))
return ret;
rpl_global_gtid_slave_state->update_state_hash(sub_id_list[i], &list[i],
hton, NULL);
@@ -8944,8 +8983,20 @@ err:
}
#endif /* MYSQL_CLIENT */
-
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+static bool wsrep_must_replay(THD *thd)
+{
+#ifdef WITH_WSREP
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ bool res= WSREP(thd) && thd->wsrep_trx().state() == wsrep::transaction::s_must_replay;
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ return res;
+#else
+ return false;
+#endif
+}
+
+
int Xid_log_event::do_apply_event(rpl_group_info *rgi)
{
bool res;
@@ -8977,7 +9028,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
rgi->gtid_pending= false;
gtid= rgi->current_gtid;
- err= rpl_global_gtid_slave_state->record_gtid(thd, &gtid, sub_id, rgi,
+ err= rpl_global_gtid_slave_state->record_gtid(thd, &gtid, sub_id, true,
false, &hton);
if (unlikely(err))
{
@@ -9010,17 +9061,8 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
res= trans_commit(thd); /* Automatically rolls back on error. */
thd->release_transactional_locks();
-#ifdef WITH_WSREP
- if (WSREP(thd)) mysql_mutex_lock(&thd->LOCK_thd_data);
- if ((!res || (WSREP(thd) && thd->wsrep_conflict_state == MUST_REPLAY)) && sub_id)
-#else
- if (likely(!res) && sub_id)
-#endif /* WITH_WSREP */
+ if (sub_id && (!res || wsrep_must_replay(thd)))
rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, hton, rgi);
-#ifdef WITH_WSREP
- if (WSREP(thd)) mysql_mutex_unlock(&thd->LOCK_thd_data);
-#endif /* WITH_WSREP */
-
/*
Increment the global status commit count variable
*/
@@ -9123,11 +9165,8 @@ void User_var_log_event::pack_info(Protocol* protocol)
String buf(buf_mem, sizeof(buf_mem), system_charset_info);
char buf2[DECIMAL_MAX_STR_LENGTH+1];
String str(buf2, sizeof(buf2), &my_charset_bin);
- my_decimal dec;
buf.length(0);
- binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) (val+2), &dec, val[0],
- val[1]);
- my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, 0, &str);
+ my_decimal((const uchar *) (val + 2), val[0], val[1]).to_string(&str);
if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
buf.append(buf2))
return;
@@ -11370,13 +11409,13 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
{
WSREP_WARN("BF applier failed to open_and_lock_tables: %u, fatal: %d "
"wsrep = (exec_mode: %d conflict_state: %d seqno: %lld)",
- thd->get_stmt_da()->sql_errno(),
- thd->is_fatal_error,
- thd->wsrep_exec_mode,
- thd->wsrep_conflict_state,
- (long long)wsrep_thd_trx_seqno(thd));
+ thd->get_stmt_da()->sql_errno(),
+ thd->is_fatal_error,
+ thd->wsrep_cs().mode(),
+ thd->wsrep_trx().state(),
+ (long long) wsrep_thd_trx_seqno(thd));
}
-#endif
+#endif /* WITH_WSREP */
if (thd->is_error() &&
!is_parallel_retry_error(rgi, error= thd->get_stmt_da()->sql_errno()))
{
@@ -11512,10 +11551,10 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
#ifdef HAVE_QUERY_CACHE
#ifdef WITH_WSREP
/*
- Moved invalidation right before the call to rows_event_stmt_cleanup(),
- to avoid query cache being polluted with stale entries.
+ Moved invalidation right before the call to rows_event_stmt_cleanup(),
+ to avoid query cache being polluted with stale entries,
*/
- if (! (WSREP(thd) && (thd->wsrep_exec_mode == REPL_RECV)))
+ if (! (WSREP(thd) && wsrep_thd_is_applying(thd)))
{
#endif /* WITH_WSREP */
query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
@@ -11628,6 +11667,13 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
bool ignored_error= (idempotent_error == 0 ?
ignored_error_code(actual_error) : 0);
+#ifdef WITH_WSREP
+ if (WSREP(thd) && wsrep_ignored_error_code(this, actual_error))
+ {
+ idempotent_error= true;
+ thd->wsrep_has_ignored_error= true;
+ }
+#endif /* WITH_WSREP */
if (idempotent_error || ignored_error)
{
if (global_system_variables.log_warnings)
@@ -11714,7 +11760,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
restore_empty_query_table_list(thd->lex);
#if defined(WITH_WSREP) && defined(HAVE_QUERY_CACHE)
- if (WSREP(thd) && thd->wsrep_exec_mode == REPL_RECV)
+ if (WSREP(thd) && wsrep_thd_is_applying(thd))
{
query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
}
diff --git a/sql/log_event.h b/sql/log_event.h
index 57282fe58ce..5719bb99a68 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -2747,6 +2747,38 @@ protected:
};
+class Version
+{
+protected:
+ uchar m_ver[3];
+ int cmp(const Version &other) const
+ {
+ return memcmp(m_ver, other.m_ver, 3);
+ }
+public:
+ Version()
+ {
+ m_ver[0]= m_ver[1]= m_ver[2]= '\0';
+ }
+ Version(uchar v0, uchar v1, uchar v2)
+ {
+ m_ver[0]= v0;
+ m_ver[1]= v1;
+ m_ver[2]= v2;
+ }
+ Version(const char *version, const char **endptr);
+ const uchar& operator [] (size_t i) const
+ {
+ DBUG_ASSERT(i < 3);
+ return m_ver[i];
+ }
+ bool operator<(const Version &other) const { return cmp(other) < 0; }
+ bool operator>(const Version &other) const { return cmp(other) > 0; }
+ bool operator<=(const Version &other) const { return cmp(other) <= 0; }
+ bool operator>=(const Version &other) const { return cmp(other) >= 0; }
+};
+
+
/**
@class Format_description_log_event
@@ -2773,10 +2805,17 @@ public:
by the checksum alg description byte
*/
uint8 *post_header_len;
- struct master_version_split {
+ class master_version_split: public Version {
+ public:
enum {KIND_MYSQL, KIND_MARIADB};
int kind;
- uchar ver[3];
+ master_version_split() :kind(KIND_MARIADB) { }
+ master_version_split(const char *version);
+ bool version_is_valid() const
+ {
+ /* It is invalid only when all version numbers are 0 */
+ return !(m_ver[0] == 0 && m_ver[1] == 0 && m_ver[2] == 0);
+ }
};
master_version_split server_version_split;
const uint8 *event_type_permutation;
@@ -2800,17 +2839,9 @@ public:
(post_header_len != NULL));
}
- bool version_is_valid() const
- {
- /* It is invalid only when all version numbers are 0 */
- return !(server_version_split.ver[0] == 0 &&
- server_version_split.ver[1] == 0 &&
- server_version_split.ver[2] == 0);
- }
-
bool is_valid() const
{
- return header_is_valid() && version_is_valid();
+ return header_is_valid() && server_version_split.version_is_valid();
}
int get_data_size()
diff --git a/sql/mdl.cc b/sql/mdl.cc
index a53a1f08c2c..5e54178db70 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -24,9 +24,6 @@
#include <mysql/plugin.h>
#include <mysql/service_thd_wait.h>
#include <mysql/psi/mysql_stage.h>
-#include "wsrep_mysqld.h"
-#include "wsrep_thd.h"
-
#ifdef HAVE_PSI_INTERFACE
static PSI_mutex_key key_MDL_wait_LOCK_wait_status;
@@ -80,7 +77,7 @@ static void init_mdl_psi_keys(void)
PSI_stage_info MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
{
- {0, "Waiting for global read lock", 0},
+ {0, "Waiting for backup lock", 0},
{0, "Waiting for schema metadata lock", 0},
{0, "Waiting for table metadata lock", 0},
{0, "Waiting for stored function metadata lock", 0},
@@ -88,10 +85,44 @@ PSI_stage_info MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
{0, "Waiting for stored package body metadata lock", 0},
{0, "Waiting for trigger metadata lock", 0},
{0, "Waiting for event metadata lock", 0},
- {0, "Waiting for commit lock", 0},
{0, "User lock", 0} /* Be compatible with old status. */
};
+
+static const LEX_STRING lock_types[]=
+{
+ { C_STRING_WITH_LEN("MDL_INTENTION_EXCLUSIVE") },
+ { C_STRING_WITH_LEN("MDL_SHARED") },
+ { C_STRING_WITH_LEN("MDL_SHARED_HIGH_PRIO") },
+ { C_STRING_WITH_LEN("MDL_SHARED_READ") },
+ { C_STRING_WITH_LEN("MDL_SHARED_WRITE") },
+ { C_STRING_WITH_LEN("MDL_SHARED_UPGRADABLE") },
+ { C_STRING_WITH_LEN("MDL_SHARED_READ_ONLY") },
+ { C_STRING_WITH_LEN("MDL_SHARED_NO_WRITE") },
+ { C_STRING_WITH_LEN("MDL_SHARED_NO_READ_WRITE") },
+ { C_STRING_WITH_LEN("MDL_EXCLUSIVE") },
+};
+
+
+static const LEX_STRING backup_lock_types[]=
+{
+ { C_STRING_WITH_LEN("MDL_BACKUP_START") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_FLUSH") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_WAIT_FLUSH") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_WAIT_DDL") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_WAIT_COMMIT") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_FTWRL1") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_FTWRL2") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_DML") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_TRANS_DML") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_SYS_DML") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_DDL") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_BLOCK_DDL") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_ALTER_COPY") },
+ { C_STRING_WITH_LEN("MDL_BACKUP_COMMIT") }
+};
+
+
#ifdef HAVE_PSI_INTERFACE
void MDL_key::init_psi_keys()
{
@@ -128,11 +159,9 @@ public:
LF_PINS *get_pins() { return lf_hash_get_pins(&m_locks); }
private:
LF_HASH m_locks; /**< All acquired locks in the server. */
- /** Pre-allocated MDL_lock object for GLOBAL namespace. */
- MDL_lock *m_global_lock;
- /** Pre-allocated MDL_lock object for COMMIT namespace. */
- MDL_lock *m_commit_lock;
- friend int mdl_iterate(int (*)(MDL_ticket *, void *), void *);
+ /** Pre-allocated MDL_lock object for BACKUP namespace. */
+ MDL_lock *m_backup_lock;
+ friend int mdl_iterate(mdl_iterator_callback, void *);
};
@@ -279,8 +308,6 @@ Deadlock_detection_visitor::opt_change_victim_to(MDL_context *new_victim)
and compatibility matrices.
*/
-#define MDL_BIT(A) static_cast<MDL_lock::bitmap_t>(1U << A)
-
/**
The lock context. Created internally for an acquired lock.
For a given name, there exists only one MDL_lock instance,
@@ -295,7 +322,7 @@ Deadlock_detection_visitor::opt_change_victim_to(MDL_context *new_victim)
class MDL_lock
{
public:
- typedef unsigned short bitmap_t;
+ typedef mdl_bitmap_t bitmap_t;
class Ticket_list
{
@@ -328,9 +355,10 @@ public:
/**
Helper struct which defines how different types of locks are handled
- for a specific MDL_lock. In practice we use only two strategies: "scoped"
- lock strategy for locks in GLOBAL, COMMIT and SCHEMA namespaces and
- "object" lock strategy for all other namespaces.
+ for a specific MDL_lock. In practice we use only three strategies:
+ "backup" lock strategy for locks in BACKUP namespace, "scoped" lock
+ strategy for locks in SCHEMA namespace and "object" lock strategy for
+ all other namespaces.
*/
struct MDL_lock_strategy
{
@@ -394,9 +422,10 @@ public:
{ return m_waiting_incompatible; }
virtual bool needs_notification(const MDL_ticket *ticket) const
{
- return ticket->get_type() == MDL_SHARED_NO_WRITE ||
- ticket->get_type() == MDL_SHARED_NO_READ_WRITE ||
- ticket->get_type() == MDL_EXCLUSIVE;
+ return (MDL_BIT(ticket->get_type()) &
+ (MDL_BIT(MDL_SHARED_NO_WRITE) |
+ MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
+ MDL_BIT(MDL_EXCLUSIVE)));
}
/**
@@ -427,6 +456,43 @@ public:
static const bitmap_t m_waiting_incompatible[MDL_TYPE_END];
};
+
+ struct MDL_backup_lock: public MDL_lock_strategy
+ {
+ MDL_backup_lock() {}
+ virtual const bitmap_t *incompatible_granted_types_bitmap() const
+ { return m_granted_incompatible; }
+ virtual const bitmap_t *incompatible_waiting_types_bitmap() const
+ { return m_waiting_incompatible; }
+ virtual bool needs_notification(const MDL_ticket *ticket) const
+ {
+ return (MDL_BIT(ticket->get_type()) & MDL_BIT(MDL_BACKUP_FTWRL1));
+ }
+
+ /**
+ Insert delayed threads may hold DML or TRANS_DML lock.
+ We need to kill such threads in order to get lock for FTWRL statements.
+ We do this by calling code outside of MDL.
+ */
+ virtual bool conflicting_locks(const MDL_ticket *ticket) const
+ {
+ return (MDL_BIT(ticket->get_type()) &
+ (MDL_BIT(MDL_BACKUP_DML) |
+ MDL_BIT(MDL_BACKUP_TRANS_DML)));
+ }
+
+ /*
+ In backup namespace DML/DDL may starve because of concurrent FTWRL or
+ BACKUP statements. This scenario is partically useless in real world,
+ so we just return 0 here.
+ */
+ virtual bitmap_t hog_lock_types_bitmap() const
+ { return 0; }
+ private:
+ static const bitmap_t m_granted_incompatible[MDL_BACKUP_END];
+ static const bitmap_t m_waiting_incompatible[MDL_BACKUP_END];
+ };
+
public:
/** The key of the object (data) being protected. */
MDL_key key;
@@ -538,10 +604,9 @@ public:
MDL_lock(const MDL_key *key_arg)
: key(key_arg),
m_hog_lock_count(0),
- m_strategy(&m_scoped_lock_strategy)
+ m_strategy(&m_backup_lock_strategy)
{
- DBUG_ASSERT(key_arg->mdl_namespace() == MDL_key::GLOBAL ||
- key_arg->mdl_namespace() == MDL_key::COMMIT);
+ DBUG_ASSERT(key_arg->mdl_namespace() == MDL_key::BACKUP);
mysql_prlock_init(key_MDL_lock_rwlock, &m_rwlock);
}
@@ -557,8 +622,7 @@ public:
static void lf_hash_initializer(LF_HASH *hash __attribute__((unused)),
MDL_lock *lock, MDL_key *key_arg)
{
- DBUG_ASSERT(key_arg->mdl_namespace() != MDL_key::GLOBAL &&
- key_arg->mdl_namespace() != MDL_key::COMMIT);
+ DBUG_ASSERT(key_arg->mdl_namespace() != MDL_key::BACKUP);
new (&lock->key) MDL_key(key_arg);
if (key_arg->mdl_namespace() == MDL_key::SCHEMA)
lock->m_strategy= &m_scoped_lock_strategy;
@@ -568,11 +632,13 @@ public:
const MDL_lock_strategy *m_strategy;
private:
+ static const MDL_backup_lock m_backup_lock_strategy;
static const MDL_scoped_lock m_scoped_lock_strategy;
static const MDL_object_lock m_object_lock_strategy;
};
+const MDL_lock::MDL_backup_lock MDL_lock::m_backup_lock_strategy;
const MDL_lock::MDL_scoped_lock MDL_lock::m_scoped_lock_strategy;
const MDL_lock::MDL_object_lock MDL_lock::m_object_lock_strategy;
@@ -636,7 +702,7 @@ void mdl_destroy()
struct mdl_iterate_arg
{
- int (*callback)(MDL_ticket *ticket, void *arg);
+ mdl_iterator_callback callback;
void *argument;
};
@@ -649,16 +715,19 @@ static my_bool mdl_iterate_lock(MDL_lock *lock, mdl_iterate_arg *arg)
must be empty for such locks anyway.
*/
mysql_prlock_rdlock(&lock->m_rwlock);
- MDL_lock::Ticket_iterator ticket_it(lock->m_granted);
+ MDL_lock::Ticket_iterator granted_it(lock->m_granted);
+ MDL_lock::Ticket_iterator waiting_it(lock->m_waiting);
MDL_ticket *ticket;
- while ((ticket= ticket_it++) && !(res= arg->callback(ticket, arg->argument)))
+ while ((ticket= granted_it++) && !(res= arg->callback(ticket, arg->argument, true)))
+ /* no-op */;
+ while ((ticket= waiting_it++) && !(res= arg->callback(ticket, arg->argument, false)))
/* no-op */;
mysql_prlock_unlock(&lock->m_rwlock);
return MY_TEST(res);
}
-int mdl_iterate(int (*callback)(MDL_ticket *ticket, void *arg), void *arg)
+int mdl_iterate(mdl_iterator_callback callback, void *arg)
{
DBUG_ENTER("mdl_iterate");
mdl_iterate_arg argument= { callback, arg };
@@ -667,8 +736,7 @@ int mdl_iterate(int (*callback)(MDL_ticket *ticket, void *arg), void *arg)
if (pins)
{
- res= mdl_iterate_lock(mdl_locks.m_global_lock, &argument) ||
- mdl_iterate_lock(mdl_locks.m_commit_lock, &argument) ||
+ res= mdl_iterate_lock(mdl_locks.m_backup_lock, &argument) ||
lf_hash_iterate(&mdl_locks.m_locks, pins,
(my_hash_walk_action) mdl_iterate_lock, &argument);
lf_hash_put_pins(pins);
@@ -689,11 +757,9 @@ my_hash_value_type mdl_hash_function(CHARSET_INFO *cs,
void MDL_map::init()
{
- MDL_key global_lock_key(MDL_key::GLOBAL, "", "");
- MDL_key commit_lock_key(MDL_key::COMMIT, "", "");
+ MDL_key backup_lock_key(MDL_key::BACKUP, "", "");
- m_global_lock= new (std::nothrow) MDL_lock(&global_lock_key);
- m_commit_lock= new (std::nothrow) MDL_lock(&commit_lock_key);
+ m_backup_lock= new (std::nothrow) MDL_lock(&backup_lock_key);
lf_hash_init(&m_locks, sizeof(MDL_lock), LF_HASH_UNIQUE, 0, 0,
mdl_locks_key, &my_charset_bin);
@@ -711,10 +777,9 @@ void MDL_map::init()
void MDL_map::destroy()
{
- delete m_global_lock;
- delete m_commit_lock;
+ delete m_backup_lock;
- DBUG_ASSERT(!my_atomic_load32(&m_locks.count));
+ DBUG_ASSERT(!lf_hash_size(&m_locks));
lf_hash_destroy(&m_locks);
}
@@ -732,26 +797,18 @@ MDL_lock* MDL_map::find_or_insert(LF_PINS *pins, const MDL_key *mdl_key)
{
MDL_lock *lock;
- if (mdl_key->mdl_namespace() == MDL_key::GLOBAL ||
- mdl_key->mdl_namespace() == MDL_key::COMMIT)
+ if (mdl_key->mdl_namespace() == MDL_key::BACKUP)
{
/*
- Avoid locking any m_mutex when lock for GLOBAL or COMMIT namespace is
- requested. Return pointer to pre-allocated MDL_lock instance instead.
- Such an optimization allows to save one mutex lock/unlock for any
- statement changing data.
+ Return pointer to pre-allocated MDL_lock instance. Such an optimization
+ allows to save one hash lookup for any statement changing data.
- It works since these namespaces contain only one element so keys
+ It works since this namespace contains only one element so keys
for them look like '<namespace-id>\0\0'.
*/
DBUG_ASSERT(mdl_key->length() == 3);
-
- lock= (mdl_key->mdl_namespace() == MDL_key::GLOBAL) ? m_global_lock :
- m_commit_lock;
-
- mysql_prlock_wrlock(&lock->m_rwlock);
-
- return lock;
+ mysql_prlock_wrlock(&m_backup_lock->m_rwlock);
+ return m_backup_lock;
}
retry:
@@ -780,22 +837,18 @@ retry:
unsigned long
MDL_map::get_lock_owner(LF_PINS *pins, const MDL_key *mdl_key)
{
- MDL_lock *lock;
unsigned long res= 0;
- if (mdl_key->mdl_namespace() == MDL_key::GLOBAL ||
- mdl_key->mdl_namespace() == MDL_key::COMMIT)
+ if (mdl_key->mdl_namespace() == MDL_key::BACKUP)
{
- lock= (mdl_key->mdl_namespace() == MDL_key::GLOBAL) ? m_global_lock :
- m_commit_lock;
- mysql_prlock_rdlock(&lock->m_rwlock);
- res= lock->get_lock_owner();
- mysql_prlock_unlock(&lock->m_rwlock);
+ mysql_prlock_rdlock(&m_backup_lock->m_rwlock);
+ res= m_backup_lock->get_lock_owner();
+ mysql_prlock_unlock(&m_backup_lock->m_rwlock);
}
else
{
- lock= (MDL_lock*) lf_hash_search(&m_locks, pins, mdl_key->ptr(),
- mdl_key->length());
+ MDL_lock *lock= (MDL_lock*) lf_hash_search(&m_locks, pins, mdl_key->ptr(),
+ mdl_key->length());
if (lock)
{
/*
@@ -820,13 +873,9 @@ MDL_map::get_lock_owner(LF_PINS *pins, const MDL_key *mdl_key)
void MDL_map::remove(LF_PINS *pins, MDL_lock *lock)
{
- if (lock->key.mdl_namespace() == MDL_key::GLOBAL ||
- lock->key.mdl_namespace() == MDL_key::COMMIT)
+ if (lock->key.mdl_namespace() == MDL_key::BACKUP)
{
- /*
- Never destroy pre-allocated MDL_lock objects for GLOBAL and
- COMMIT namespaces.
- */
+ /* Never destroy pre-allocated MDL_lock object in BACKUP namespace. */
mysql_prlock_unlock(&lock->m_rwlock);
return;
}
@@ -975,9 +1024,14 @@ void MDL_ticket::destroy(MDL_ticket *ticket)
uint MDL_ticket::get_deadlock_weight() const
{
- return (m_lock->key.mdl_namespace() == MDL_key::GLOBAL ||
- m_type >= MDL_SHARED_UPGRADABLE ?
- DEADLOCK_WEIGHT_DDL : DEADLOCK_WEIGHT_DML);
+ if (m_lock->key.mdl_namespace() == MDL_key::BACKUP)
+ {
+ if (m_type == MDL_BACKUP_FTWRL1)
+ return DEADLOCK_WEIGHT_FTWRL1;
+ return DEADLOCK_WEIGHT_DDL;
+ }
+ return m_type >= MDL_SHARED_UPGRADABLE ?
+ DEADLOCK_WEIGHT_DDL : DEADLOCK_WEIGHT_DML;
}
@@ -1161,13 +1215,12 @@ void MDL_lock::Ticket_list::add_ticket(MDL_ticket *ticket)
wsrep_thd_is_BF(ticket->get_ctx()->get_thd(), false))
{
Ticket_iterator itw(ticket->get_lock()->m_waiting);
- Ticket_iterator itg(ticket->get_lock()->m_granted);
-
- DBUG_ASSERT(WSREP_ON);
- MDL_ticket *waiting, *granted;
+ MDL_ticket *waiting;
MDL_ticket *prev=NULL;
bool added= false;
+ DBUG_ASSERT(WSREP(ticket->get_ctx()->get_thd()));
+
while ((waiting= itw++) && !added)
{
if (!wsrep_thd_is_BF(waiting->get_ctx()->get_thd(), true))
@@ -1183,20 +1236,8 @@ void MDL_lock::Ticket_list::add_ticket(MDL_ticket *ticket)
}
/* Otherwise, insert the ticket at the back of the waiting list. */
- if (!added) m_list.push_back(ticket);
-
- while ((granted= itg++))
- {
- if (granted->get_ctx() != ticket->get_ctx() &&
- granted->is_incompatible_when_granted(ticket->get_type()))
- {
- if (!wsrep_grant_mdl_exception(ticket->get_ctx(), granted,
- &ticket->get_lock()->key))
- {
- WSREP_DEBUG("MDL victim killed at add_ticket");
- }
- }
- }
+ if (!added)
+ m_list.push_back(ticket);
}
else
#endif /* WITH_WSREP */
@@ -1368,17 +1409,23 @@ void MDL_lock::reschedule_waiters()
/**
Compatibility (or rather "incompatibility") matrices for scoped metadata
- lock. Arrays of bitmaps which elements specify which granted/waiting locks
+ lock.
+ Scoped locks are database (or schema) locks.
+ Arrays of bitmaps which elements specify which granted/waiting locks
are incompatible with type of lock being requested.
The first array specifies if particular type of request can be satisfied
if there is granted scoped lock of certain type.
+ (*) Since intention shared scoped locks (IS) are compatible with all other
+ type of locks, they don't need to be implemented and there is no code
+ for them.
+
| Type of active |
Request | scoped lock |
type | IS(*) IX S X |
---------+------------------+
- IS | + + + + |
+ IS(*) | + + + + |
IX | + + - - |
S | + - + - |
X | + - - - |
@@ -1391,7 +1438,7 @@ void MDL_lock::reschedule_waiters()
Request | scoped lock |
type | IS(*) IX S X |
---------+-----------------+
- IS | + + + + |
+ IS(*) | + + + + |
IX | + + - - |
S | + + + - |
X | + + + + |
@@ -1399,9 +1446,6 @@ void MDL_lock::reschedule_waiters()
Here: "+" -- means that request can be satisfied
"-" -- means that request can't be satisfied and should wait
- (*) Since intention shared scoped locks are compatible with all other
- type of locks we don't even have any accounting for them.
-
Note that relation between scoped locks and objects locks requested
by statement is not straightforward and is therefore fully defined
by SQL-layer.
@@ -1440,41 +1484,41 @@ MDL_lock::MDL_scoped_lock::m_waiting_incompatible[MDL_TYPE_END]=
The first array specifies if particular type of request can be satisfied
if there is granted lock of certain type.
- Request | Granted requests for lock |
- type | S SH SR SW SU SRO SNW SNRW X |
- ----------+---------------------------------------+
- S | + + + + + + + + - |
- SH | + + + + + + + + - |
- SR | + + + + + + + - - |
- SW | + + + + + - - - - |
- SU | + + + + - + - - - |
- SRO | + + + - + + + - - |
- SNW | + + + - - + - - - |
- SNRW | + + - - - - - - - |
- X | - - - - - - - - - |
- SU -> X | - - - - 0 - 0 0 0 |
- SNW -> X | - - - 0 0 - 0 0 0 |
- SNRW -> X | - - 0 0 0 0 0 0 0 |
+ Request | Granted requests for lock |
+ type | S SH SR SW SU SRO SNW SNRW X |
+ ----------+------------------------------------+
+ S | + + + + + + + + - |
+ SH | + + + + + + + + - |
+ SR | + + + + + + + - - |
+ SW | + + + + + - - - - |
+ SU | + + + + - + - - - |
+ SRO | + + + - + + + - - |
+ SNW | + + + - - + - - - |
+ SNRW | + + - - - - - - - |
+ X | - - - - - - - - - |
+ SU -> X | - - - - 0 - 0 0 0 |
+ SNW -> X | - - - 0 0 - 0 0 0 |
+ SNRW -> X | - - 0 0 0 0 0 0 0 |
The second array specifies if particular type of request can be satisfied
if there is waiting request for the same lock of certain type. In other
words it specifies what is the priority of different lock types.
- Request | Pending requests for lock |
- type | S SH SR SW SU SRO SNW SNRW X |
- ----------+--------------------------------------+
- S | + + + + + + + + - |
- SH | + + + + + + + + + |
- SR | + + + + + + + - - |
- SW | + + + + + + - - - |
- SU | + + + + + + + + - |
- SRO | + + + - + + + - - |
- SNW | + + + + + + + + - |
- SNRW | + + + + + + + + - |
- X | + + + + + + + + + |
- SU -> X | + + + + + + + + + |
- SNW -> X | + + + + + + + + + |
- SNRW -> X | + + + + + + + + + |
+ Request | Pending requests for lock |
+ type | S SH SR SW SU SRO SNW SNRW X |
+ ----------+-----------------------------------+
+ S | + + + + + + + + - |
+ SH | + + + + + + + + + |
+ SR | + + + + + + + - - |
+ SW | + + + + + + - - - |
+ SU | + + + + + + + + - |
+ SRO | + + + - + + + - - |
+ SNW | + + + + + + + + - |
+ SNRW | + + + + + + + + - |
+ X | + + + + + + + + + |
+ SU -> X | + + + + + + + + + |
+ SNW -> X | + + + + + + + + + |
+ SNRW -> X | + + + + + + + + + |
Here: "+" -- means that request can be satisfied
"-" -- means that request can't be satisfied and should wait
@@ -1535,9 +1579,126 @@ MDL_lock::MDL_object_lock::m_waiting_incompatible[MDL_TYPE_END]=
/**
+ Compatibility (or rather "incompatibility") matrices for backup metadata
+ lock. Arrays of bitmaps which elements specify which granted/waiting locks
+ are incompatible with type of lock being requested.
+
+ The first array specifies if particular type of request can be satisfied
+ if there is granted backup lock of certain type.
+
+ Request | Type of active backup lock |
+ type | S0 S1 S2 S3 S4 F1 F2 D TD SD DD BL AC C |
+ ----------+---------------------------------------------------------+
+ S0 | - - - - - + + + + + + + + + |
+ S1 | - + + + + + + + + + + + + + |
+ S2 | - + + + + + + - + + + + + + |
+ S3 | - + + + + + + - + + - + + + |
+ S4 | - + + + + + + - + - - + + - |
+ FTWRL1 | + + + + + + + - - - - + - + |
+ FTWRL2 | + + + + + + + - - - - + - - |
+ D | + - - - - - - + + + + + + + |
+ TD | + + + + + - - + + + + + + + |
+ SD | + + + + - - - + + + + + + + |
+ DDL | + + + - - - - + + + + - + + |
+ BLOCK_DDL | + + + + + + + + + + - + + + |
+ ALTER_COP | + + + + + - - + + + + + + + |
+ COMMIT | + + + + - + - + + + + + + + |
+
+ The second array specifies if particular type of request can be satisfied
+ if there is already waiting request for the backup lock of certain type.
+ I.e. it specifies what is the priority of different lock types.
+
+ Request | Pending backup lock |
+ type | S0 S1 S2 S3 S4 F1 F2 D TD SD DD BL AC C |
+ ----------+---------------------------------------------------------+
+ S0 | + - - - - + + + + + + + + + |
+ S1 | + + + + + + + + + + + + + + |
+ S2 | + + + + + + + + + + + + + + |
+ S3 | + + + + + + + + + + + + + + |
+ S4 | + + + + + + + + + + + + + + |
+ FTWRL1 | + + + + + + + + + + + + + + |
+ FTWRL2 | + + + + + + + + + + + + + + |
+ D | + - - - - - - + + + + + + + |
+ TD | + + + + + - - + + + + + + + |
+ SD | + + + + - - - + + + + + + + |
+ DDL | + + + - - - - + + + + - + + |
+ BLOCK_DDL | + + + + + + + + + + + + + + |
+ ALTER_COP | + + + + + - - + + + + + + + |
+ COMMIT | + + + + - + - + + + + + + + |
+
+ Here: "+" -- means that request can be satisfied
+ "-" -- means that request can't be satisfied and should wait
+*/
+
+/*
+ NOTE: If you add a new MDL_BACKUP_XXX level lock, you have to also add it
+ to MDL_BACKUP_START in the two arrays below!
+*/
+
+const MDL_lock::bitmap_t
+MDL_lock::MDL_backup_lock::m_granted_incompatible[MDL_BACKUP_END]=
+{
+ /* MDL_BACKUP_START */
+ MDL_BIT(MDL_BACKUP_START) | MDL_BIT(MDL_BACKUP_FLUSH) | MDL_BIT(MDL_BACKUP_WAIT_FLUSH) | MDL_BIT(MDL_BACKUP_WAIT_DDL) | MDL_BIT(MDL_BACKUP_WAIT_COMMIT),
+ MDL_BIT(MDL_BACKUP_START),
+ MDL_BIT(MDL_BACKUP_START) | MDL_BIT(MDL_BACKUP_DML),
+ MDL_BIT(MDL_BACKUP_START) | MDL_BIT(MDL_BACKUP_DML) | MDL_BIT(MDL_BACKUP_DDL),
+ MDL_BIT(MDL_BACKUP_START) | MDL_BIT(MDL_BACKUP_DML) | MDL_BIT(MDL_BACKUP_SYS_DML) | MDL_BIT(MDL_BACKUP_DDL) | MDL_BIT(MDL_BACKUP_COMMIT),
+
+ /* MDL_BACKUP_FTWRL1 */
+ MDL_BIT(MDL_BACKUP_DML) | MDL_BIT(MDL_BACKUP_TRANS_DML) | MDL_BIT(MDL_BACKUP_SYS_DML) | MDL_BIT(MDL_BACKUP_DDL) | MDL_BIT(MDL_BACKUP_ALTER_COPY),
+ MDL_BIT(MDL_BACKUP_DML) | MDL_BIT(MDL_BACKUP_TRANS_DML) | MDL_BIT(MDL_BACKUP_SYS_DML) | MDL_BIT(MDL_BACKUP_DDL) | MDL_BIT(MDL_BACKUP_ALTER_COPY) | MDL_BIT(MDL_BACKUP_COMMIT),
+ /* MDL_BACKUP_DML */
+ MDL_BIT(MDL_BACKUP_FLUSH) | MDL_BIT(MDL_BACKUP_WAIT_FLUSH) | MDL_BIT(MDL_BACKUP_WAIT_DDL) | MDL_BIT(MDL_BACKUP_WAIT_COMMIT) | MDL_BIT(MDL_BACKUP_FTWRL1) | MDL_BIT(MDL_BACKUP_FTWRL2),
+ MDL_BIT(MDL_BACKUP_FTWRL1) | MDL_BIT(MDL_BACKUP_FTWRL2),
+ MDL_BIT(MDL_BACKUP_WAIT_COMMIT) | MDL_BIT(MDL_BACKUP_FTWRL1) | MDL_BIT(MDL_BACKUP_FTWRL2),
+ /* MDL_BACKUP_DDL */
+ MDL_BIT(MDL_BACKUP_WAIT_DDL) | MDL_BIT(MDL_BACKUP_WAIT_COMMIT) | MDL_BIT(MDL_BACKUP_FTWRL1) | MDL_BIT(MDL_BACKUP_FTWRL2) | MDL_BIT(MDL_BACKUP_BLOCK_DDL),
+ /* MDL_BACKUP_BLOCK_DDL */
+ MDL_BIT(MDL_BACKUP_DDL),
+ MDL_BIT(MDL_BACKUP_FTWRL1) | MDL_BIT(MDL_BACKUP_FTWRL2),
+ /* MDL_BACKUP_COMMIT */
+ MDL_BIT(MDL_BACKUP_WAIT_COMMIT) | MDL_BIT(MDL_BACKUP_FTWRL2)
+};
+
+
+const MDL_lock::bitmap_t
+MDL_lock::MDL_backup_lock::m_waiting_incompatible[MDL_BACKUP_END]=
+{
+ /* MDL_BACKUP_START */
+ MDL_BIT(MDL_BACKUP_FLUSH) | MDL_BIT(MDL_BACKUP_WAIT_FLUSH) | MDL_BIT(MDL_BACKUP_WAIT_DDL) | MDL_BIT(MDL_BACKUP_WAIT_COMMIT),
+ 0,
+ 0,
+ 0,
+ 0,
+ /* MDL_BACKUP_FTWRL1 */
+ 0,
+ 0,
+
+ /* MDL_BACKUP_DML */
+ MDL_BIT(MDL_BACKUP_FLUSH) | MDL_BIT(MDL_BACKUP_WAIT_FLUSH) | MDL_BIT(MDL_BACKUP_WAIT_DDL) | MDL_BIT(MDL_BACKUP_WAIT_COMMIT) | MDL_BIT(MDL_BACKUP_FTWRL1) | MDL_BIT(MDL_BACKUP_FTWRL2),
+ MDL_BIT(MDL_BACKUP_FTWRL1) | MDL_BIT(MDL_BACKUP_FTWRL2),
+ MDL_BIT(MDL_BACKUP_WAIT_COMMIT) | MDL_BIT(MDL_BACKUP_FTWRL1) | MDL_BIT(MDL_BACKUP_FTWRL2),
+ /* MDL_BACKUP_DDL */
+ MDL_BIT(MDL_BACKUP_WAIT_DDL) | MDL_BIT(MDL_BACKUP_WAIT_COMMIT) | MDL_BIT(MDL_BACKUP_FTWRL1) | MDL_BIT(MDL_BACKUP_FTWRL2) | MDL_BIT(MDL_BACKUP_BLOCK_DDL),
+ /* MDL_BACKUP_BLOCK_DDL */
+ 0,
+ MDL_BIT(MDL_BACKUP_FTWRL1) | MDL_BIT(MDL_BACKUP_FTWRL2),
+ /* MDL_BACKUP_COMMIT */
+ MDL_BIT(MDL_BACKUP_WAIT_COMMIT) | MDL_BIT(MDL_BACKUP_FTWRL2)
+};
+
+
+/**
Check if request for the metadata lock can be satisfied given its
current state.
+ New lock request can be satisfied iff:
+ - There are no incompatible types of satisfied requests
+ in other contexts
+ - There are no waiting requests which have higher priority
+ than this request when priority was not ignored.
+
@param type_arg The requested lock type.
@param requestor_ctx The MDL context of the requestor.
@param ignore_lock_priority Ignore lock priority.
@@ -1555,78 +1716,72 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg,
MDL_context *requestor_ctx,
bool ignore_lock_priority) const
{
- bool can_grant= FALSE;
bitmap_t waiting_incompat_map= incompatible_waiting_types_bitmap()[type_arg];
bitmap_t granted_incompat_map= incompatible_granted_types_bitmap()[type_arg];
- bool wsrep_can_grant= TRUE;
+#ifdef WITH_WSREP
/*
- New lock request can be satisfied iff:
- - There are no incompatible types of satisfied requests
- in other contexts
- - There are no waiting requests which have higher priority
- than this request when priority was not ignored.
+ Approve lock request in BACKUP namespace for BF threads.
+ We should get rid of this code and forbid FTWRL/BACKUP statements
+ when wsrep is active.
*/
- if (ignore_lock_priority || !(m_waiting.bitmap() & waiting_incompat_map))
+ if ((wsrep_thd_is_toi(requestor_ctx->get_thd()) ||
+ wsrep_thd_is_applying(requestor_ctx->get_thd())) &&
+ key.mdl_namespace() == MDL_key::BACKUP)
{
- if (! (m_granted.bitmap() & granted_incompat_map))
- can_grant= TRUE;
- else
+ bool waiting_incompatible= m_waiting.bitmap() & waiting_incompat_map;
+ bool granted_incompatible= m_granted.bitmap() & granted_incompat_map;
+ if (waiting_incompatible || granted_incompatible)
{
- Ticket_iterator it(m_granted);
- MDL_ticket *ticket;
+ WSREP_DEBUG("global lock granted for BF%s: %lu %s",
+ waiting_incompatible ? " (waiting queue)" : "",
+ thd_get_thread_id(requestor_ctx->get_thd()),
+ wsrep_thd_query(requestor_ctx->get_thd()));
+ }
+ return true;
+ }
+#endif /* WITH_WSREP */
+
+ if (!ignore_lock_priority && (m_waiting.bitmap() & waiting_incompat_map))
+ return false;
+
+ if (m_granted.bitmap() & granted_incompat_map)
+ {
+ Ticket_iterator it(m_granted);
+ bool can_grant= true;
- /* Check that the incompatible lock belongs to some other context. */
- while ((ticket= it++))
+ /* Check that the incompatible lock belongs to some other context. */
+ while (auto ticket= it++)
+ {
+ if (ticket->get_ctx() != requestor_ctx &&
+ ticket->is_incompatible_when_granted(type_arg))
{
- if (ticket->get_ctx() != requestor_ctx &&
- ticket->is_incompatible_when_granted(type_arg))
- {
+ can_grant= false;
#ifdef WITH_WSREP
- if (WSREP_ON && wsrep_thd_is_BF(requestor_ctx->get_thd(),false) &&
- key.mdl_namespace() == MDL_key::GLOBAL)
- {
- WSREP_DEBUG("global lock granted for BF: %lu %s",
- thd_get_thread_id(requestor_ctx->get_thd()),
- wsrep_thd_query(requestor_ctx->get_thd()));
- can_grant = true;
- }
- else if (!wsrep_grant_mdl_exception(requestor_ctx, ticket, &key))
+ /*
+ non WSREP threads must report conflict immediately
+ note: RSU processing wsrep threads, have wsrep_on==OFF
+ */
+ if (WSREP(requestor_ctx->get_thd()) ||
+ requestor_ctx->get_thd()->wsrep_cs().mode() ==
+ wsrep::client_state::m_rsu)
+ {
+ wsrep_handle_mdl_conflict(requestor_ctx, ticket, &key);
+ if (wsrep_log_conflicts)
{
- wsrep_can_grant= FALSE;
- if (wsrep_log_conflicts)
- {
- MDL_lock * lock = ticket->get_lock();
- WSREP_INFO(
- "MDL conflict db=%s table=%s ticket=%d solved by %s",
- lock->key.db_name(), lock->key.name(), ticket->get_type(),
- "abort" );
- }
+ auto key= ticket->get_key();
+ WSREP_INFO("MDL conflict db=%s table=%s ticket=%d solved by abort",
+ key->db_name(), key->name(), ticket->get_type());
}
- else
- can_grant= TRUE;
- /* Continue loop */
-#else
- break;
-#endif /* WITH_WSREP */
+ continue;
}
+#endif /* WITH_WSREP */
+ break;
}
- if ((ticket == NULL) && wsrep_can_grant)
- can_grant= TRUE; /* Incompatible locks are our own. */
- }
- }
- else
- {
- if (WSREP_ON && wsrep_thd_is_BF(requestor_ctx->get_thd(), false) &&
- key.mdl_namespace() == MDL_key::GLOBAL)
- {
- WSREP_DEBUG("global lock granted for BF (waiting queue): %lu %s",
- thd_get_thread_id(requestor_ctx->get_thd()),
- wsrep_thd_query(requestor_ctx->get_thd()));
- can_grant = true;
}
+ return can_grant;
}
- return can_grant;
+ return true;
}
@@ -1739,6 +1894,27 @@ bool MDL_ticket::is_incompatible_when_waiting(enum_mdl_type type) const
}
+static const LEX_STRING
+*get_mdl_lock_name(MDL_key::enum_mdl_namespace mdl_namespace,
+ enum_mdl_type type)
+{
+ return mdl_namespace == MDL_key::BACKUP ?
+ &backup_lock_types[type] :
+ &lock_types[type];
+}
+
+
+const LEX_STRING *MDL_ticket::get_type_name() const
+{
+ return get_mdl_lock_name(get_key()->mdl_namespace(), m_type);
+}
+
+const LEX_STRING *MDL_ticket::get_type_name(enum_mdl_type type) const
+{
+ return get_mdl_lock_name(get_key()->mdl_namespace(), type);
+}
+
+
/**
Check whether the context already holds a compatible lock ticket
on an object.
@@ -1772,8 +1948,10 @@ MDL_context::find_ticket(MDL_request *mdl_request,
if (mdl_request->key.is_equal(&ticket->m_lock->key) &&
ticket->has_stronger_or_equal_type(mdl_request->type))
{
- DBUG_PRINT("info", ("Adding mdl lock %d to %d",
- mdl_request->type, ticket->m_type));
+ DBUG_PRINT("info", ("Adding mdl lock %s to %s",
+ get_mdl_lock_name(mdl_request->key.mdl_namespace(),
+ mdl_request->type)->str,
+ ticket->get_type_name()->str));
*result_duration= duration;
return ticket;
}
@@ -1859,11 +2037,8 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
MDL_ticket *ticket;
enum_mdl_duration found_duration;
- DBUG_ASSERT(mdl_request->type != MDL_EXCLUSIVE ||
- is_lock_owner(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE));
- DBUG_ASSERT(mdl_request->ticket == NULL);
-
/* Don't take chances in production. */
+ DBUG_ASSERT(mdl_request->ticket == NULL);
mdl_request->ticket= NULL;
/*
@@ -2064,7 +2239,10 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout)
MDL_ticket *ticket;
MDL_wait::enum_wait_status wait_status;
DBUG_ENTER("MDL_context::acquire_lock");
- DBUG_PRINT("enter", ("lock_type: %d", mdl_request->type));
+ DBUG_PRINT("enter", ("lock_type: %s timeout: %f",
+ get_mdl_lock_name(mdl_request->key.mdl_namespace(),
+ mdl_request->type)->str,
+ lock_wait_timeout));
if (try_acquire_lock_impl(mdl_request, &ticket))
DBUG_RETURN(TRUE);
@@ -2180,6 +2358,7 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout)
switch (wait_status)
{
case MDL_wait::VICTIM:
+ mdl_dbug_print_locks();
my_error(ER_LOCK_DEADLOCK, MYF(0));
break;
case MDL_wait::TIMEOUT:
@@ -2319,15 +2498,23 @@ MDL_context::upgrade_shared_lock(MDL_ticket *mdl_ticket,
MDL_savepoint mdl_svp= mdl_savepoint();
bool is_new_ticket;
DBUG_ENTER("MDL_context::upgrade_shared_lock");
- DBUG_PRINT("enter",("new_type: %d lock_wait_timeout: %f", new_type,
+ DBUG_PRINT("enter",("old_type: %s new_type: %s lock_wait_timeout: %f",
+ mdl_ticket->get_type_name()->str,
+ mdl_ticket->get_type_name(new_type)->str,
lock_wait_timeout));
DEBUG_SYNC(get_thd(), "mdl_upgrade_lock");
/*
Do nothing if already upgraded. Used when we FLUSH TABLE under
LOCK TABLES and a table is listed twice in LOCK TABLES list.
+
+ In BACKUP namespace upgrade must always happen. Even though
+ MDL_BACKUP_START is not stronger than MDL_BACKUP_FLUSH from
+ has_stronger_or_equal_type(), the latter effectively blocks
+ new MDL_BACKUP_DML while the former doesn't.
*/
- if (mdl_ticket->has_stronger_or_equal_type(new_type))
+ if (mdl_ticket->has_stronger_or_equal_type(new_type) &&
+ mdl_ticket->get_key()->mdl_namespace() != MDL_key::BACKUP)
DBUG_RETURN(FALSE);
mdl_xlock_request.init(&mdl_ticket->m_lock->key, new_type,
@@ -2595,6 +2782,7 @@ void MDL_context::find_deadlock()
context was waiting is concurrently satisfied.
*/
(void) victim->m_wait.set_status(MDL_wait::VICTIM);
+ victim->inc_deadlock_overweight();
victim->unlock_deadlock_victim();
if (victim == this)
@@ -2728,9 +2916,13 @@ void MDL_ticket::downgrade_lock(enum_mdl_type type)
if (m_type == type || !has_stronger_or_equal_type(type))
return;
- /* Only allow downgrade from EXCLUSIVE and SHARED_NO_WRITE. */
- DBUG_ASSERT(m_type == MDL_EXCLUSIVE ||
- m_type == MDL_SHARED_NO_WRITE);
+ /* Only allow downgrade in some specific known cases */
+ DBUG_ASSERT((get_key()->mdl_namespace() != MDL_key::BACKUP &&
+ (m_type == MDL_EXCLUSIVE ||
+ m_type == MDL_SHARED_NO_WRITE)) ||
+ (get_key()->mdl_namespace() == MDL_key::BACKUP &&
+ (m_type == MDL_BACKUP_DDL ||
+ m_type == MDL_BACKUP_WAIT_FLUSH)));
mysql_prlock_wrlock(&m_lock->m_rwlock);
/*
@@ -2842,18 +3034,17 @@ void MDL_context::rollback_to_savepoint(const MDL_savepoint &mdl_savepoint)
implementation of COMMIT (implicit or explicit) and ROLLBACK.
*/
-void MDL_context::release_transactional_locks()
+void MDL_context::release_transactional_locks(THD *thd)
{
DBUG_ENTER("MDL_context::release_transactional_locks");
/* Fail if there are active transactions */
- DBUG_ASSERT(!(current_thd->server_status &
+ DBUG_ASSERT(!(thd->server_status &
(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY)));
release_locks_stored_before(MDL_STATEMENT, NULL);
release_locks_stored_before(MDL_TRANSACTION, NULL);
DBUG_VOID_RETURN;
}
-
void MDL_context::release_statement_locks()
{
DBUG_ENTER("MDL_context::release_transactional_locks");
@@ -3019,30 +3210,11 @@ bool MDL_context::has_explicit_locks()
#ifdef WITH_WSREP
static
-const char *wsrep_get_mdl_type_name(enum_mdl_type type)
-{
- switch (type)
- {
- case MDL_INTENTION_EXCLUSIVE : return "intention exclusive";
- case MDL_SHARED : return "shared";
- case MDL_SHARED_HIGH_PRIO : return "shared high prio";
- case MDL_SHARED_READ : return "shared read";
- case MDL_SHARED_WRITE : return "shared write";
- case MDL_SHARED_UPGRADABLE : return "shared upgradable";
- case MDL_SHARED_NO_WRITE : return "shared no write";
- case MDL_SHARED_NO_READ_WRITE : return "shared no read write";
- case MDL_EXCLUSIVE : return "exclusive";
- default: break;
- }
- return "UNKNOWN";
-}
-
-static
const char *wsrep_get_mdl_namespace_name(MDL_key::enum_mdl_namespace ns)
{
switch (ns)
{
- case MDL_key::GLOBAL : return "GLOBAL";
+ case MDL_key::BACKUP : return "BACKUP";
case MDL_key::SCHEMA : return "SCHEMA";
case MDL_key::TABLE : return "TABLE";
case MDL_key::FUNCTION : return "FUNCTION";
@@ -3050,7 +3222,6 @@ const char *wsrep_get_mdl_namespace_name(MDL_key::enum_mdl_namespace ns)
case MDL_key::PACKAGE_BODY: return "PACKAGE BODY";
case MDL_key::TRIGGER : return "TRIGGER";
case MDL_key::EVENT : return "EVENT";
- case MDL_key::COMMIT : return "COMMIT";
case MDL_key::USER_LOCK : return "USER_LOCK";
default: break;
}
@@ -3063,10 +3234,41 @@ void MDL_ticket::wsrep_report(bool debug)
const PSI_stage_info *psi_stage= m_lock->key.get_wait_state_name();
WSREP_DEBUG("MDL ticket: type: %s space: %s db: %s name: %s (%s)",
- wsrep_get_mdl_type_name(get_type()),
+ get_type_name()->str,
wsrep_get_mdl_namespace_name(m_lock->key.mdl_namespace()),
m_lock->key.db_name(),
m_lock->key.name(),
psi_stage->m_name);
}
#endif /* WITH_WSREP */
+
+
+#ifndef DBUG_OFF
+
+/*
+ Print a list of all locks to DBUG trace to help with debugging
+*/
+
+static int mdl_dbug_print_lock(MDL_ticket *mdl_ticket, void *arg, bool granted)
+{
+ String *tmp= (String*) arg;
+ char buffer[128];
+ MDL_key *mdl_key= mdl_ticket->get_key();
+ size_t length;
+ length= my_snprintf(buffer, sizeof(buffer)-1,
+ "\nname: %s db: %.*s key_name: %.*s (%s)",
+ mdl_ticket->get_type_name()->str,
+ (int) mdl_key->db_name_length(), mdl_key->db_name(),
+ (int) mdl_key->name_length(), mdl_key->name(),
+ granted ? "granted" : "waiting");
+ tmp->append(buffer, length);
+ return 0;
+}
+
+void mdl_dbug_print_locks()
+{
+ String tmp;
+ mdl_iterate(mdl_dbug_print_lock, (void*) &tmp);
+ DBUG_PRINT("mdl_locks", ("%s", tmp.c_ptr()));
+}
+#endif /* DBUG_OFF */
diff --git a/sql/mdl.h b/sql/mdl.h
index 93da280dda6..a2cb7c2aa85 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -28,6 +28,10 @@ class MDL_lock;
class MDL_ticket;
bool ok_for_lower_case_names(const char *name);
+typedef unsigned short mdl_bitmap_t;
+#define MDL_BIT(A) static_cast<mdl_bitmap_t>(1U << A)
+
+
/**
@def ENTER_COND(C, M, S, O)
Start a wait on a condition.
@@ -112,19 +116,27 @@ public:
@sa Comments for MDL_object_lock::can_grant_lock() and
MDL_scoped_lock::can_grant_lock() for details.
+
+ Scoped locks are database (or schema) locks.
+ The object locks are for tables, triggers etc.
*/
enum enum_mdl_type {
+ /* This means that the MDL_request is not initialized */
+ MDL_NOT_INITIALIZED= -1,
/*
- An intention exclusive metadata lock. Used only for scoped locks.
+ An intention exclusive metadata lock (IX). Used only for scoped locks.
Owner of this type of lock can acquire upgradable exclusive locks on
individual objects.
Compatible with other IX locks, but is incompatible with scoped S and
X locks.
+ IX lock is taken in SCHEMA namespace when we intend to modify
+ object metadata. Object may refer table, stored procedure, trigger,
+ view/etc.
*/
MDL_INTENTION_EXCLUSIVE= 0,
/*
- A shared metadata lock.
+ A shared metadata lock (S).
To be used in cases when we are interested in object metadata only
and there is no intention to access object data (e.g. for stored
routines or during preparing prepared statements).
@@ -144,6 +156,9 @@ enum enum_mdl_type {
use SNRW locks for them. It also does not arise when S locks are used
during PREPARE calls as table-level locks are not acquired in this
case.
+ This lock is taken for global read lock, when caching a stored
+ procedure in memory for the duration of the transaction and for
+ tables used by prepared statements.
*/
MDL_SHARED,
/*
@@ -164,8 +179,8 @@ enum enum_mdl_type {
*/
MDL_SHARED_HIGH_PRIO,
/*
- A shared metadata lock for cases when there is an intention to read data
- from table.
+ A shared metadata lock (SR) for cases when there is an intention to read
+ data from table.
A connection holding this kind of lock can read table metadata and read
table data (after acquiring appropriate table and row-level locks).
This means that one can only acquire TL_READ, TL_READ_NO_INSERT, and
@@ -175,7 +190,7 @@ enum enum_mdl_type {
*/
MDL_SHARED_READ,
/*
- A shared metadata lock for cases when there is an intention to modify
+ A shared metadata lock (SW) for cases when there is an intention to modify
(and not just read) data in the table.
A connection holding SW lock can read table metadata and modify or read
table data (after acquiring appropriate table and row-level locks).
@@ -185,8 +200,8 @@ enum enum_mdl_type {
*/
MDL_SHARED_WRITE,
/*
- An upgradable shared metadata lock for cases when there is an intention
- to modify (and not just read) data in the table.
+ An upgradable shared metadata lock for cases when there is an
+ intention to modify (and not just read) data in the table.
Can be upgraded to MDL_SHARED_NO_WRITE and MDL_EXCLUSIVE.
A connection holding SU lock can read table metadata and modify or read
table data (after acquiring appropriate table and row-level locks).
@@ -226,7 +241,7 @@ enum enum_mdl_type {
*/
MDL_SHARED_NO_READ_WRITE,
/*
- An exclusive metadata lock.
+ An exclusive metadata lock (X).
A connection holding this lock can modify both table's metadata and data.
No other type of metadata lock can be granted while this lock is held.
To be used for CREATE/DROP/RENAME TABLE statements and for execution of
@@ -234,7 +249,75 @@ enum enum_mdl_type {
*/
MDL_EXCLUSIVE,
/* This should be the last !!! */
- MDL_TYPE_END};
+ MDL_TYPE_END
+};
+
+
+/** Backup locks */
+
+/**
+ Block concurrent backup
+*/
+#define MDL_BACKUP_START enum_mdl_type(0)
+/**
+ Block new write requests to non transactional tables
+*/
+#define MDL_BACKUP_FLUSH enum_mdl_type(1)
+/**
+ In addition to previous locks, blocks running requests to non trans tables
+ Used to wait until all DML usage of on trans tables are finished
+*/
+#define MDL_BACKUP_WAIT_FLUSH enum_mdl_type(2)
+/**
+ In addition to previous locks, blocks new DDL's from starting
+*/
+#define MDL_BACKUP_WAIT_DDL enum_mdl_type(3)
+/**
+ In addition to previous locks, blocks commits
+*/
+#define MDL_BACKUP_WAIT_COMMIT enum_mdl_type(4)
+
+/**
+ Blocks (or is blocked by) statements that intend to modify data. Acquired
+ before commit lock by FLUSH TABLES WITH READ LOCK.
+*/
+#define MDL_BACKUP_FTWRL1 enum_mdl_type(5)
+
+/**
+ Blocks (or is blocked by) commits. Acquired after global read lock by
+ FLUSH TABLES WITH READ LOCK.
+*/
+#define MDL_BACKUP_FTWRL2 enum_mdl_type(6)
+
+#define MDL_BACKUP_DML enum_mdl_type(7)
+#define MDL_BACKUP_TRANS_DML enum_mdl_type(8)
+#define MDL_BACKUP_SYS_DML enum_mdl_type(9)
+
+/**
+ Must be acquired by DDL statements that intend to modify data.
+ Currently it's also used for LOCK TABLES.
+*/
+#define MDL_BACKUP_DDL enum_mdl_type(10)
+
+/**
+ Blocks new DDL's. Used by backup code to enable DDL logging
+*/
+#define MDL_BACKUP_BLOCK_DDL enum_mdl_type(11)
+
+/*
+ Statement is modifying data, but will not block MDL_BACKUP_DDL or earlier
+ BACKUP stages.
+ ALTER TABLE is started with MDL_BACKUP_DDL, but changed to
+ MDL_BACKUP_ALTER_COPY while alter table is copying or modifing data.
+*/
+
+#define MDL_BACKUP_ALTER_COPY enum_mdl_type(12)
+
+/**
+ Must be acquired during commit.
+*/
+#define MDL_BACKUP_COMMIT enum_mdl_type(13)
+#define MDL_BACKUP_END enum_mdl_type(14)
/** Duration of metadata lock. */
@@ -282,10 +365,13 @@ public:
/**
Object namespaces.
Sic: when adding a new member to this enum make sure to
- update m_namespace_to_wait_state_name array in mdl.cc!
+ update m_namespace_to_wait_state_name array in mdl.cc and
+ metadata_lock_info_lock_name in metadata_lock_info.cc!
Different types of objects exist in different namespaces
+ - SCHEMA is for databases (to protect against DROP DATABASE)
- TABLE is for tables and views.
+ - BACKUP is for locking DML, DDL and COMMIT's during BACKUP STAGES
- FUNCTION is for stored functions.
- PROCEDURE is for stored procedures.
- TRIGGER is for triggers.
@@ -294,7 +380,7 @@ public:
it's necessary to have a separate namespace for them since
MDL_key is also used outside of the MDL subsystem.
*/
- enum enum_mdl_namespace { GLOBAL=0,
+ enum enum_mdl_namespace { BACKUP=0,
SCHEMA,
TABLE,
FUNCTION,
@@ -302,7 +388,6 @@ public:
PACKAGE_BODY,
TRIGGER,
EVENT,
- COMMIT,
USER_LOCK, /* user level locks. */
/* This should be the last ! */
NAMESPACE_END };
@@ -509,12 +594,13 @@ public:
*/
MDL_request& operator=(const MDL_request &)
{
+ type= MDL_NOT_INITIALIZED;
ticket= NULL;
/* Do nothing, in particular, don't try to copy the key. */
return *this;
}
/* Another piece of ugliness for TABLE_LIST constructor */
- MDL_request() {}
+ MDL_request(): type(MDL_NOT_INITIALIZED), ticket(NULL) {}
MDL_request(const MDL_request *rhs)
:type(rhs->type),
@@ -562,7 +648,8 @@ public:
enum enum_deadlock_weight
{
- DEADLOCK_WEIGHT_DML= 0,
+ DEADLOCK_WEIGHT_FTWRL1= 0,
+ DEADLOCK_WEIGHT_DML= 1,
DEADLOCK_WEIGHT_DDL= 100
};
/* A helper used to determine which lock request should be aborted. */
@@ -620,6 +707,8 @@ public:
m_type == MDL_EXCLUSIVE;
}
enum_mdl_type get_type() const { return m_type; }
+ const LEX_STRING *get_type_name() const;
+ const LEX_STRING *get_type_name(enum_mdl_type type) const;
MDL_lock *get_lock() const { return m_lock; }
MDL_key *get_key() const;
void downgrade_lock(enum_mdl_type type);
@@ -812,7 +901,7 @@ public:
void set_lock_duration(MDL_ticket *mdl_ticket, enum_mdl_duration duration);
void release_statement_locks();
- void release_transactional_locks();
+ void release_transactional_locks(THD *thd);
void release_explicit_locks();
void rollback_to_savepoint(const MDL_savepoint &mdl_savepoint);
@@ -820,7 +909,8 @@ public:
/** @pre Only valid if we started waiting for lock. */
inline uint get_deadlock_weight() const
- { return m_waiting_for->get_deadlock_weight(); }
+ { return m_waiting_for->get_deadlock_weight() + m_deadlock_overweight; }
+ void inc_deadlock_overweight() { m_deadlock_overweight++; }
/**
Post signal to the context (and wake it up if necessary).
@@ -938,6 +1028,7 @@ private:
*/
MDL_wait_for_subgraph *m_waiting_for;
LF_PINS *m_pins;
+ uint m_deadlock_overweight= 0;
private:
MDL_ticket *find_ticket(MDL_request *mdl_req,
enum_mdl_duration *duration);
@@ -1011,6 +1102,13 @@ extern "C" int thd_is_connected(MYSQL_THD thd);
*/
extern "C" ulong max_write_lock_count;
+typedef int (*mdl_iterator_callback)(MDL_ticket *ticket, void *arg,
+ bool granted);
extern MYSQL_PLUGIN_IMPORT
-int mdl_iterate(int (*callback)(MDL_ticket *ticket, void *arg), void *arg);
-#endif
+int mdl_iterate(mdl_iterator_callback callback, void *arg);
+#ifndef DBUG_OFF
+void mdl_dbug_print_locks();
+#else
+ static inline void mdl_dbug_print_locks() {}
+#endif /* DBUG_OFF */
+#endif /* MDL_H */
diff --git a/sql/mem_root_array.h b/sql/mem_root_array.h
index 1230f41247c..3d03a5a5db2 100644
--- a/sql/mem_root_array.h
+++ b/sql/mem_root_array.h
@@ -87,9 +87,11 @@ public:
// Returns a pointer to the first element in the array.
Element_type *begin() { return &m_array[0]; }
+ const Element_type *begin() const { return &m_array[0]; }
// Returns a pointer to the past-the-end element in the array.
Element_type *end() { return &m_array[size()]; }
+ const Element_type *end() const { return &m_array[size()]; }
// Erases all of the elements.
void clear()
@@ -226,6 +228,7 @@ public:
size_t element_size() const { return sizeof(Element_type); }
bool empty() const { return size() == 0; }
size_t size() const { return m_size; }
+ const MEM_ROOT *mem_root() const { return m_root; }
private:
MEM_ROOT *const m_root;
diff --git a/sql/message.h b/sql/message.h
index 934503751d6..a6491736877 100644
--- a/sql/message.h
+++ b/sql/message.h
@@ -1,21 +1,3 @@
-#ifndef MESSAGE_INCLUDED
-#define MESSAGE_INCLUDED
-/* Copyright (c) 2008, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-
/*
To change or add messages mysqld writes to the Windows error log, run
mc.exe message.mc
@@ -24,10 +6,8 @@
mc.exe can be installed with Windows SDK, some Visual Studio distributions
do not include it.
*/
-
-
//
-// Values are 32 bit values layed out as follows:
+// Values are 32 bit values laid out as follows:
//
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
@@ -67,11 +47,8 @@
//
// MessageText:
//
-// %1For more information, see Help and Support Center at http://www.mysql.com.
-//
-//
+// %1
+//
//
#define MSG_DEFAULT 0xC0000064L
-#endif /* MESSAGE_INCLUDED */
-
diff --git a/sql/message.mc b/sql/message.mc
index 8d68d599365..c009b048d05 100644
--- a/sql/message.mc
+++ b/sql/message.mc
@@ -11,6 +11,5 @@ Severity = Error
Facility = Application
SymbolicName = MSG_DEFAULT
Language = English
-%1For more information, see Help and Support Center at http://www.mysql.com.
-
+%1
diff --git a/sql/message.rc b/sql/message.rc
index 0885a897e6f..0abcb0fa2c5 100644
--- a/sql/message.rc
+++ b/sql/message.rc
@@ -1,2 +1,2 @@
LANGUAGE 0x9,0x1
-1 11 MSG00001.bin
+1 11 "MSG00001.bin"
diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc
index 5e0afd6edb2..601268ec2f6 100644
--- a/sql/multi_range_read.cc
+++ b/sql/multi_range_read.cc
@@ -19,6 +19,7 @@
#include "sql_select.h"
#include "key.h"
#include "sql_statistics.h"
+#include "rowid_filter.h"
/****************************************************************************
* Default MRR implementation (MRR to non-MRR converter)
@@ -62,14 +63,22 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
{
KEY_MULTI_RANGE range;
range_seq_t seq_it;
- ha_rows rows, total_rows= 0;
+ ha_rows total_rows= 0;
uint n_ranges=0;
+ uint n_eq_ranges= 0;
+ ulonglong total_touched_blocks= 0;
+ ha_rows max_rows= stats.records;
THD *thd= table->in_use;
uint limit= thd->variables.eq_range_index_dive_limit;
-
bool use_statistics_for_eq_range= eq_ranges_exceeds_limit(seq,
seq_init_param,
limit);
+ uint len= table->key_info[keyno].key_length + table->file->ref_length;
+ if (keyno == table->s->primary_key && table->file->primary_key_is_clustered())
+ len= table->s->stored_rec_length;
+ /* Assume block is 75 % full */
+ uint avg_block_records= ((uint) (table->file->stats.block_size*3/4))/len + 1;
+ DBUG_ENTER("multi_range_read_info_const");
/* Default MRR implementation doesn't need buffer */
*bufsz= 0;
@@ -77,10 +86,14 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
seq_it= seq->init(seq_init_param, n_ranges, *flags);
while (!seq->next(seq_it, &range))
{
+ ha_rows rows;
+
if (unlikely(thd->killed != 0))
- return HA_POS_ERROR;
+ DBUG_RETURN(HA_POS_ERROR);
n_ranges++;
+ if (range.range_flag & EQ_RANGE)
+ n_eq_ranges++;
key_range *min_endp, *max_endp;
if (range.range_flag & GEOM_FLAG)
{
@@ -95,17 +108,22 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
max_endp= range.end_key.length? &range.end_key : NULL;
}
int keyparts_used= my_count_bits(range.start_key.keypart_map);
- if ((range.range_flag & UNIQUE_RANGE) && !(range.range_flag & NULL_RANGE))
- rows= 1; /* there can be at most one row */
- else if (use_statistics_for_eq_range &&
- !(range.range_flag & NULL_RANGE) &&
- (range.range_flag & EQ_RANGE) &&
- table->key_info[keyno].actual_rec_per_key(keyparts_used - 1) > 0.5)
- rows=
- (ha_rows) table->key_info[keyno].actual_rec_per_key(keyparts_used - 1);
+ if (use_statistics_for_eq_range &&
+ !(range.range_flag & NULL_RANGE) &&
+ (range.range_flag & EQ_RANGE) &&
+ table->key_info[keyno].actual_rec_per_key(keyparts_used - 1) > 0.5)
+ {
+ if ((range.range_flag & UNIQUE_RANGE) && !(range.range_flag & NULL_RANGE))
+ rows= 1; /* there can be at most one row */
+ else
+ rows=
+ (ha_rows) table->key_info[keyno].actual_rec_per_key(keyparts_used-1);
+ }
else
{
- if (HA_POS_ERROR == (rows= this->records_in_range(keyno, min_endp,
+ if ((range.range_flag & UNIQUE_RANGE) && !(range.range_flag & NULL_RANGE))
+ rows= 1; /* there can be at most one row */
+ else if (HA_POS_ERROR == (rows= this->records_in_range(keyno, min_endp,
max_endp)))
{
/* Can't scan one range => can't do MRR scan at all */
@@ -114,21 +132,45 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
}
}
total_rows += rows;
+ total_touched_blocks+= (rows / avg_block_records +1);
}
if (total_rows != HA_POS_ERROR)
{
+ set_if_smaller(total_rows, max_rows);
/* The following calculation is the same as in multi_range_read_info(): */
*flags |= HA_MRR_USE_DEFAULT_IMPL;
cost->reset();
cost->avg_io_cost= 1; /* assume random seeks */
- if ((*flags & HA_MRR_INDEX_ONLY) && total_rows > 2)
- cost->io_count= keyread_time(keyno, n_ranges, (uint)total_rows);
+ cost->idx_avg_io_cost= 1;
+ if (!((keyno == table->s->primary_key && primary_key_is_clustered()) ||
+ is_clustering_key(keyno)))
+ {
+ cost->idx_io_count= total_touched_blocks +
+ keyread_time(keyno, 0, total_rows);
+ cost->cpu_cost= cost->idx_cpu_cost=
+ (double) total_rows / TIME_FOR_COMPARE_IDX +
+ (2 * n_ranges - n_eq_ranges) * IDX_LOOKUP_COST;
+ if (!(*flags & HA_MRR_INDEX_ONLY))
+ {
+ cost->io_count= read_time(keyno, 0, total_rows);
+ cost->cpu_cost+= (double) total_rows / TIME_FOR_COMPARE;
+ }
+ }
else
- cost->io_count= read_time(keyno, n_ranges, total_rows);
- cost->cpu_cost= (double) total_rows / TIME_FOR_COMPARE + 0.01;
+ {
+ cost->io_count= read_time(keyno, n_ranges, (uint) total_rows);
+ cost->cpu_cost= (double) total_rows / TIME_FOR_COMPARE + 0.01;
+ }
}
- return total_rows;
+ DBUG_PRINT("statistics",
+ ("key: %s rows: %llu total_cost: %.3f io_blocks: %llu "
+ "idx_io_count: %.3f cpu_cost: %.3f io_count: %.3f",
+ table->s->keynames.type_names[keyno],
+ (ulonglong) total_rows, cost->total_cost(),
+ (ulonglong) total_touched_blocks,
+ cost->idx_io_count, cost->cpu_cost, cost->io_count));
+ DBUG_RETURN(total_rows);
}
@@ -183,10 +225,22 @@ ha_rows handler::multi_range_read_info(uint keyno, uint n_ranges, uint n_rows,
cost->avg_io_cost= 1; /* assume random seeks */
/* Produce the same cost as non-MRR code does */
- if (*flags & HA_MRR_INDEX_ONLY)
- cost->io_count= keyread_time(keyno, n_ranges, n_rows);
+ if (!(keyno == table->s->primary_key && primary_key_is_clustered()))
+ {
+ cost->idx_io_count= n_ranges + keyread_time(keyno, 0, n_rows);
+ cost->cpu_cost= cost->idx_cpu_cost=
+ (double) n_rows / TIME_FOR_COMPARE_IDX + n_ranges * IDX_LOOKUP_COST;
+ if (!(*flags & HA_MRR_INDEX_ONLY))
+ {
+ cost->io_count= read_time(keyno, 0, n_rows);
+ cost->cpu_cost+= (double) n_rows / TIME_FOR_COMPARE;
+ }
+ }
else
- cost->io_count= read_time(keyno, n_ranges, n_rows);
+ {
+ cost->io_count= read_time(keyno, n_ranges, (uint)n_rows);
+ cost->cpu_cost= (double) n_rows / TIME_FOR_COMPARE + 0.01;
+ }
return 0;
}
@@ -587,7 +641,8 @@ static int rowid_cmp_reverse(void *file, uchar *a, uchar *b)
int Mrr_ordered_rndpos_reader::init(handler *h_arg,
Mrr_index_reader *index_reader_arg,
uint mode,
- Lifo_buffer *buf)
+ Lifo_buffer *buf,
+ Rowid_filter *filter)
{
file= h_arg;
index_reader= index_reader_arg;
@@ -595,6 +650,8 @@ int Mrr_ordered_rndpos_reader::init(handler *h_arg,
is_mrr_assoc= !MY_TEST(mode & HA_MRR_NO_ASSOCIATION);
index_reader_exhausted= FALSE;
index_reader_needs_refill= TRUE;
+ rowid_filter= filter;
+
return 0;
}
@@ -687,6 +744,13 @@ int Mrr_ordered_rndpos_reader::refill_from_index_reader()
index_reader->position();
+ /*
+ If the built rowid filter cannot be used at the engine level, use it here.
+ */
+ if (rowid_filter && !file->pushed_rowid_filter &&
+ !rowid_filter->check((char *)index_rowid))
+ continue;
+
/* Put rowid, or {rowid, range_id} pair into the buffer */
rowid_buffer->write_ptr1= index_rowid;
rowid_buffer->write_ptr2= (uchar*)&range_info;
@@ -822,7 +886,8 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
void *seq_init_param, uint n_ranges, uint mode,
HANDLER_BUFFER *buf)
{
- THD *thd= h_arg->get_table()->in_use;
+ TABLE *table= h_arg->get_table();
+ THD *thd= table->in_use;
int res;
Key_parameters keypar;
uint UNINIT_VAR(key_buff_elem_size); /* set/used when do_sort_keys==TRUE */
@@ -877,6 +942,21 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
if (!(keyno == table->s->primary_key && h_idx->primary_key_is_clustered()))
{
strategy= disk_strategy= &reader_factory.ordered_rndpos_reader;
+ if (h_arg->pushed_rowid_filter)
+ {
+ /*
+ Currently usage of a rowid filter within InnoDB engine is not supported
+ if the table is accessed by the primary key.
+ With optimizer switches ''mrr' and 'mrr_sort_keys' are both enabled
+ any access by a secondary index is converted to the rndpos access. In
+ InnoDB the rndpos access is always uses the primary key.
+ Do not use pushed rowid filter if the table is accessed actually by the
+ primary key. Use the rowid filter outside the engine code (see
+ Mrr_ordered_rndpos_reader::refill_from_index_reader).
+ */
+ rowid_filter= h_arg->pushed_rowid_filter;
+ h_arg->cancel_pushed_rowid_filter();
+ }
}
full_buf= buf->buffer;
@@ -956,14 +1036,18 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
goto use_default_impl;
}
+ // setup_two_handlers() will call dsmrr_close() will clears the filter.
+ // Save its value and restore afterwards.
+ Rowid_filter *tmp = rowid_filter;
if ((res= setup_two_handlers()))
goto error;
+ rowid_filter= tmp;
if ((res= index_strategy->init(secondary_file, seq_funcs, seq_init_param,
n_ranges, mode, &keypar, key_buffer,
&buf_manager)) ||
(res= disk_strategy->init(primary_file, index_strategy, mode,
- &rowid_buffer)))
+ &rowid_buffer, rowid_filter)))
{
goto error;
}
@@ -1145,6 +1229,7 @@ void DsMrr_impl::close_second_handler()
void DsMrr_impl::dsmrr_close()
{
DBUG_ENTER("DsMrr_impl::dsmrr_close");
+ rowid_filter= NULL;
close_second_handler();
strategy= NULL;
DBUG_VOID_RETURN;
diff --git a/sql/multi_range_read.h b/sql/multi_range_read.h
index 0473fef04ae..37a00e3086f 100644
--- a/sql/multi_range_read.h
+++ b/sql/multi_range_read.h
@@ -364,7 +364,7 @@ class Mrr_ordered_rndpos_reader : public Mrr_reader
{
public:
int init(handler *file, Mrr_index_reader *index_reader, uint mode,
- Lifo_buffer *buf);
+ Lifo_buffer *buf, Rowid_filter *filter);
int get_next(range_id_t *range_info);
int refill_buffer(bool initial);
private:
@@ -399,6 +399,9 @@ private:
/* Buffer to store (rowid, range_id) pairs */
Lifo_buffer *rowid_buffer;
+ /* Rowid filter to be checked against (if any) */
+ Rowid_filter *rowid_filter;
+
int refill_from_index_reader();
};
@@ -554,7 +557,8 @@ public:
typedef void (handler::*range_check_toggle_func_t)(bool on);
DsMrr_impl()
- : secondary_file(NULL) {};
+ : secondary_file(NULL),
+ rowid_filter(NULL) {};
void init(handler *h_arg, TABLE *table_arg)
{
@@ -591,7 +595,13 @@ private:
to run both index scan and rnd_pos() scan at the same time)
*/
handler *secondary_file;
-
+
+ /*
+ The rowid filter that DS-MRR has "unpushed" from the storage engine.
+ If it's present, DS-MRR will use it.
+ */
+ Rowid_filter *rowid_filter;
+
uint keyno; /* index we're running the scan on */
/* TRUE <=> need range association, buffers hold {rowid, range_id} pairs */
bool is_mrr_assoc;
diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc
index 8acb46b9ef2..edf3e82de40 100644
--- a/sql/my_decimal.cc
+++ b/sql/my_decimal.cc
@@ -20,6 +20,7 @@
#ifndef MYSQL_CLIENT
#include "sql_class.h" // THD
+#include "field.h"
#endif
#define DIG_BASE 1000000000
@@ -95,9 +96,8 @@ int decimal_operation_results(int result, const char *value, const char *type)
@retval E_DEC_OOM
*/
-int my_decimal2string(uint mask, const my_decimal *d,
- uint fixed_prec, uint fixed_dec,
- char filler, String *str)
+int my_decimal::to_string_native(String *str, uint fixed_prec, uint fixed_dec,
+ char filler, uint mask) const
{
/*
Calculate the size of the string: For DECIMAL(a,b), fixed_prec==a
@@ -113,11 +113,11 @@ int my_decimal2string(uint mask, const my_decimal *d,
*/
int length= (fixed_prec
? (fixed_prec + ((fixed_prec == fixed_dec) ? 1 : 0) + 1)
- : my_decimal_string_length(d));
+ : my_decimal_string_length(this));
int result;
if (str->alloc(length))
return check_result(mask, E_DEC_OOM);
- result= decimal2string((decimal_t*) d, (char*) str->ptr(),
+ result= decimal2string(this, (char*) str->ptr(),
&length, (int)fixed_prec, fixed_dec,
filler);
str->length(length);
@@ -156,8 +156,8 @@ str_set_decimal(uint mask, const my_decimal *val,
{
if (!(cs->state & MY_CS_NONASCII))
{
- /* For ASCII-compatible character sets we can use my_decimal2string */
- my_decimal2string(mask, val, fixed_prec, fixed_dec, filler, str);
+ // For ASCII-compatible character sets we can use to_string_native()
+ val->to_string_native(str, fixed_prec, fixed_dec, filler, mask);
str->set_charset(cs);
return FALSE;
}
@@ -165,14 +165,13 @@ str_set_decimal(uint mask, const my_decimal *val,
{
/*
For ASCII-incompatible character sets (like UCS2) we
- call my_decimal2string() on a temporary buffer first,
+ call my_string_native() on a temporary buffer first,
and then convert the result to the target character
with help of str->copy().
*/
uint errors;
- char buf[DECIMAL_MAX_STR_LENGTH];
- String tmp(buf, sizeof(buf), &my_charset_latin1);
- my_decimal2string(mask, val, fixed_prec, fixed_dec, filler, &tmp);
+ StringBuffer<DECIMAL_MAX_STR_LENGTH> tmp;
+ val->to_string_native(&tmp, fixed_prec, fixed_dec, filler, mask);
return str->copy(tmp.ptr(), tmp.length(), &my_charset_latin1, cs, &errors);
}
}
@@ -182,7 +181,7 @@ str_set_decimal(uint mask, const my_decimal *val,
Convert from decimal to binary representation
SYNOPSIS
- my_decimal2binary()
+ to_binary()
mask error processing mask
d number for conversion
bin pointer to buffer where to write result
@@ -199,12 +198,11 @@ str_set_decimal(uint mask, const my_decimal *val,
E_DEC_OVERFLOW
*/
-int my_decimal2binary(uint mask, const my_decimal *d, uchar *bin, int prec,
- int scale)
+int my_decimal::to_binary(uchar *bin, int prec, int scale, uint mask) const
{
int err1= E_DEC_OK, err2;
my_decimal rounded;
- my_decimal2decimal(d, &rounded);
+ my_decimal2decimal(this, &rounded);
rounded.frac= decimal_actual_fraction(&rounded);
if (scale < rounded.frac)
{
@@ -270,7 +268,8 @@ int str2my_decimal(uint mask, const char *from, size_t length,
integer part cannot be larger that 1e18 (otherwise it's an overflow).
fractional part is microseconds.
*/
-bool my_decimal2seconds(const my_decimal *d, ulonglong *sec, ulong *microsec)
+bool my_decimal2seconds(const my_decimal *d, ulonglong *sec,
+ ulong *microsec, ulong *nanosec)
{
int pos;
@@ -288,6 +287,7 @@ bool my_decimal2seconds(const my_decimal *d, ulonglong *sec, ulong *microsec)
}
*microsec= d->frac ? static_cast<longlong>(d->buf[pos+1]) / (DIG_BASE/1000000) : 0;
+ *nanosec= d->frac ? static_cast<longlong>(d->buf[pos+1]) % (DIG_BASE/1000000) : 0;
if (pos > 1)
{
@@ -368,6 +368,26 @@ int my_decimal2int(uint mask, const decimal_t *d, bool unsigned_flag,
}
+longlong my_decimal::to_longlong(bool unsigned_flag) const
+{
+ longlong result;
+ my_decimal2int(E_DEC_FATAL_ERROR, this, unsigned_flag, &result);
+ return result;
+}
+
+
+my_decimal::my_decimal(Field *field)
+{
+ init();
+ DBUG_ASSERT(!field->is_null());
+#ifdef DBUG_ASSERT_EXISTS
+ my_decimal *dec=
+#endif
+ field->val_decimal(this);
+ DBUG_ASSERT(dec == this);
+}
+
+
#ifndef DBUG_OFF
/* routines for debugging print */
diff --git a/sql/my_decimal.h b/sql/my_decimal.h
index c6c6f4b55e3..9b1aebb98b5 100644
--- a/sql/my_decimal.h
+++ b/sql/my_decimal.h
@@ -29,6 +29,8 @@
#ifndef my_decimal_h
#define my_decimal_h
+#include "sql_basic_types.h"
+
#if defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY)
#include "sql_string.h" /* String */
#endif
@@ -39,6 +41,7 @@ C_MODE_START
C_MODE_END
class String;
+class Field;
typedef struct st_mysql_time MYSQL_TIME;
/**
@@ -63,6 +66,25 @@ inline int my_decimal_int_part(uint precision, uint decimals)
}
+#ifndef MYSQL_CLIENT
+int decimal_operation_results(int result, const char *value, const char *type);
+#else
+inline int decimal_operation_results(int result, const char *value,
+ const char *type)
+{
+ return result;
+}
+#endif /*MYSQL_CLIENT*/
+
+
+inline int check_result(uint mask, int result)
+{
+ if (result & mask)
+ decimal_operation_results(result, "", "DECIMAL");
+ return result;
+}
+
+
/**
my_decimal class limits 'decimal_t' type to what we need in MySQL.
@@ -125,6 +147,12 @@ public:
{
init();
}
+ my_decimal(const uchar *bin, int prec, int scale)
+ {
+ init();
+ check_result(E_DEC_FATAL_ERROR, bin2decimal(bin, this, prec, scale));
+ }
+ my_decimal(Field *field);
~my_decimal()
{
sanity_check();
@@ -141,7 +169,57 @@ public:
bool sign() const { return decimal_t::sign; }
void sign(bool s) { decimal_t::sign= s; }
uint precision() const { return intg + frac; }
+ void set_zero()
+ {
+ /*
+ We need the up-cast here, since my_decimal has sign() member functions,
+ which conflicts with decimal_t::sign
+ (and decimal_make_zero is a macro, rather than a funcion).
+ */
+ decimal_make_zero(static_cast<decimal_t*>(this));
+ }
+ int cmp(const my_decimal *other) const
+ {
+ return decimal_cmp(this, other);
+ }
+#ifndef MYSQL_CLIENT
+ bool to_bool() const
+ {
+ return !decimal_is_zero(this);
+ }
+ double to_double() const
+ {
+ double res;
+ decimal2double(this, &res);
+ return res;
+ }
+ longlong to_longlong(bool unsigned_flag) const;
+ // Convert to string returning decimal2string() error code
+ int to_string_native(String *to, uint prec, uint dec, char filler,
+ uint mask= E_DEC_FATAL_ERROR) const;
+ // Convert to string returning the String pointer
+ String *to_string(String *to, uint prec, uint dec, char filler) const
+ {
+ return to_string_native(to, prec, dec, filler) ? NULL : to;
+ }
+ String *to_string(String *to) const
+ {
+ return to_string(to, 0, 0, 0);
+ }
+ String *to_string_round(String *to, int scale, my_decimal *round_buff) const
+ {
+ (void) round_to(round_buff, scale, HALF_UP); // QQ: check result?
+ return round_buff->to_string(to);
+ }
+ int round_to(my_decimal *to, int scale, decimal_round_mode mode,
+ int mask= E_DEC_FATAL_ERROR) const
+ {
+ return check_result(mask, decimal_round(this, to, scale, mode));
+ }
+ int to_binary(uchar *bin, int prec, int scale,
+ uint mask= E_DEC_FATAL_ERROR) const;
+#endif
/** Swap two my_decimal values */
void swap(my_decimal &rhs)
{
@@ -164,16 +242,6 @@ bool str_set_decimal(uint mask, const my_decimal *val, uint fixed_prec,
extern my_decimal decimal_zero;
-#ifndef MYSQL_CLIENT
-int decimal_operation_results(int result, const char *value, const char *type);
-#else
-inline int decimal_operation_results(int result, const char *value,
- const char *type)
-{
- return result;
-}
-#endif /*MYSQL_CLIENT*/
-
inline
void max_my_decimal(my_decimal *to, int precision, int frac)
{
@@ -187,13 +255,6 @@ inline void max_internal_decimal(my_decimal *to)
max_my_decimal(to, DECIMAL_MAX_PRECISION, 0);
}
-inline int check_result(uint mask, int result)
-{
- if (result & mask)
- decimal_operation_results(result, "", "DECIMAL");
- return result;
-}
-
inline int check_result_and_overflow(uint mask, int result, my_decimal *val)
{
if (check_result(mask, result) & E_DEC_OVERFLOW)
@@ -271,10 +332,6 @@ void my_decimal2decimal(const my_decimal *from, my_decimal *to)
}
-int my_decimal2binary(uint mask, const my_decimal *d, uchar *bin, int prec,
- int scale);
-
-
inline
int binary2my_decimal(uint mask, const uchar *bin, my_decimal *d, int prec,
int scale)
@@ -286,12 +343,7 @@ int binary2my_decimal(uint mask, const uchar *bin, my_decimal *d, int prec,
inline
int my_decimal_set_zero(my_decimal *d)
{
- /*
- We need the up-cast here, since my_decimal has sign() member functions,
- which conflicts with decimal_t::sign
- (and decimal_make_zero is a macro, rather than a funcion).
- */
- decimal_make_zero(static_cast<decimal_t*>(d));
+ d->set_zero();
return 0;
}
@@ -303,53 +355,15 @@ bool my_decimal_is_zero(const my_decimal *decimal_value)
}
-inline
-int my_decimal_round_if_needed(uint mask, my_decimal *dec, int scale,
- bool truncate)
-{
- if (scale >= dec->frac)
- return E_DEC_OK;
- return check_result(mask, decimal_round(dec, dec, scale,
- (truncate ? TRUNCATE : HALF_UP)));
-}
-
-
-inline
-int my_decimal_round(uint mask, const my_decimal *from, int scale,
- bool truncate, my_decimal *to)
-{
- return check_result(mask, decimal_round(from, to, scale,
- (truncate ? TRUNCATE : HALF_UP)));
-}
-
-
-inline
-int my_decimal_floor(uint mask, const my_decimal *from, my_decimal *to)
-{
- return check_result(mask, decimal_round(from, to, 0, FLOOR));
-}
-
-
-inline
-int my_decimal_ceiling(uint mask, const my_decimal *from, my_decimal *to)
-{
- return check_result(mask, decimal_round(from, to, 0, CEILING));
-}
-
-
inline bool str_set_decimal(const my_decimal *val, String *str,
CHARSET_INFO *cs)
{
return str_set_decimal(E_DEC_FATAL_ERROR, val, 0, 0, 0, str, cs);
}
-#ifndef MYSQL_CLIENT
-class String;
-int my_decimal2string(uint mask, const my_decimal *d, uint fixed_prec,
- uint fixed_dec, char filler, String *str);
-#endif
-bool my_decimal2seconds(const my_decimal *d, ulonglong *sec, ulong *microsec);
+bool my_decimal2seconds(const my_decimal *d, ulonglong *sec,
+ ulong *microsec, ulong *nanosec);
my_decimal *seconds2my_decimal(bool sign, ulonglong sec, ulong microsec,
my_decimal *d);
diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc
index 656023d4033..f4cf8204d61 100644
--- a/sql/my_json_writer.cc
+++ b/sql/my_json_writer.cc
@@ -16,7 +16,6 @@
#include "mariadb.h"
#include "sql_priv.h"
#include "sql_string.h"
-
#include "my_json_writer.h"
void Json_writer::append_indent()
@@ -62,6 +61,7 @@ void Json_writer::end_object()
indent_level-=INDENT_SIZE;
if (!first_child)
append_indent();
+ first_child= false;
output.append("}");
}
@@ -79,7 +79,23 @@ void Json_writer::end_array()
Json_writer& Json_writer::add_member(const char *name)
{
- if (fmt_helper.on_add_member(name))
+ size_t len= strlen(name);
+ if (fmt_helper.on_add_member(name, len))
+ return *this; // handled
+
+ // assert that we are in an object
+ DBUG_ASSERT(!element_started);
+ start_element();
+
+ output.append('"');
+ output.append(name, len);
+ output.append("\": ", 3);
+ return *this;
+}
+
+Json_writer& Json_writer::add_member(const char *name, size_t len)
+{
+ if (fmt_helper.on_add_member(name, len))
return *this; // handled
// assert that we are in an object
@@ -87,7 +103,7 @@ Json_writer& Json_writer::add_member(const char *name)
start_element();
output.append('"');
- output.append(name);
+ output.append(name, len);
output.append("\": ");
return *this;
}
@@ -141,28 +157,31 @@ void Json_writer::add_ull(ulonglong val)
void Json_writer::add_size(longlong val)
{
char buf[64];
+ size_t len;
if (val < 1024)
- my_snprintf(buf, sizeof(buf), "%lld", val);
+ len= my_snprintf(buf, sizeof(buf), "%lld", val);
else if (val < 1024*1024*16)
{
/* Values less than 16MB are specified in KB for precision */
- size_t len= my_snprintf(buf, sizeof(buf), "%lld", val/1024);
+ len= my_snprintf(buf, sizeof(buf), "%lld", val/1024);
strcpy(buf + len, "Kb");
+ len+= 2;
}
else
{
- size_t len= my_snprintf(buf, sizeof(buf), "%lld", val/(1024*1024));
+ len= my_snprintf(buf, sizeof(buf), "%lld", val/(1024*1024));
strcpy(buf + len, "Mb");
+ len+= 2;
}
- add_str(buf);
+ add_str(buf, len);
}
void Json_writer::add_double(double val)
{
char buf[64];
- my_snprintf(buf, sizeof(buf), "%lg", val);
- add_unquoted_str(buf);
+ size_t len= my_snprintf(buf, sizeof(buf), "%lg", val);
+ add_unquoted_str(buf, len);
}
@@ -174,45 +193,85 @@ void Json_writer::add_bool(bool val)
void Json_writer::add_null()
{
- add_unquoted_str("null");
+ add_unquoted_str("null", (size_t) 4);
}
void Json_writer::add_unquoted_str(const char* str)
{
- if (fmt_helper.on_add_str(str))
+ size_t len= strlen(str);
+ if (fmt_helper.on_add_str(str, len))
return;
if (!element_started)
start_element();
- output.append(str);
+ output.append(str, len);
element_started= false;
}
+void Json_writer::add_unquoted_str(const char* str, size_t len)
+{
+ if (fmt_helper.on_add_str(str, len))
+ return;
+
+ if (!element_started)
+ start_element();
+
+ output.append(str, len);
+ element_started= false;
+}
void Json_writer::add_str(const char *str)
{
- if (fmt_helper.on_add_str(str))
+ size_t len= strlen(str);
+ if (fmt_helper.on_add_str(str, len))
return;
if (!element_started)
start_element();
output.append('"');
- output.append(str);
+ output.append(str, len);
output.append('"');
element_started= false;
}
+/*
+ This function is used to add only num_bytes of str to the output string
+*/
+
+void Json_writer::add_str(const char* str, size_t num_bytes)
+{
+ if (fmt_helper.on_add_str(str, num_bytes))
+ return;
+
+ if (!element_started)
+ start_element();
+
+ output.append('"');
+ output.append(str, num_bytes);
+ output.append('"');
+ element_started= false;
+}
void Json_writer::add_str(const String &str)
{
- add_str(str.ptr());
+ add_str(str.ptr(), str.length());
}
+Json_writer_temp_disable::Json_writer_temp_disable(THD *thd_arg)
+{
+ thd= thd_arg;
+ thd->opt_trace.disable_tracing_if_required();
+}
+Json_writer_temp_disable::~Json_writer_temp_disable()
+{
+ thd->opt_trace.enable_tracing_if_required();
+}
-bool Single_line_formatting_helper::on_add_member(const char *name)
+bool Single_line_formatting_helper::on_add_member(const char *name,
+ size_t len)
{
DBUG_ASSERT(state== INACTIVE || state == DISABLED);
if (state != DISABLED)
@@ -221,7 +280,6 @@ bool Single_line_formatting_helper::on_add_member(const char *name)
buf_ptr= buffer;
//append member name to the array
- size_t len= strlen(name);
if (len < MAX_LINE_LEN)
{
memcpy(buf_ptr, name, len);
@@ -274,12 +332,11 @@ void Single_line_formatting_helper::on_start_object()
}
-bool Single_line_formatting_helper::on_add_str(const char *str)
+bool Single_line_formatting_helper::on_add_str(const char *str,
+ size_t len)
{
if (state == IN_ARRAY)
{
- size_t len= strlen(str);
-
// New length will be:
// "$string",
// quote + quote + comma + space = 4
@@ -355,9 +412,11 @@ void Single_line_formatting_helper::disable_and_flush()
while (ptr < buf_ptr)
{
char *str= ptr;
+ size_t len= strlen(str);
+
if (nr == 0)
{
- owner->add_member(str);
+ owner->add_member(str, len);
if (start_array)
owner->start_array();
}
@@ -365,13 +424,11 @@ void Single_line_formatting_helper::disable_and_flush()
{
//if (nr == 1)
// owner->start_array();
- owner->add_str(str);
+ owner->add_str(str, len);
}
nr++;
- while (*ptr!=0)
- ptr++;
- ptr++;
+ ptr+= len+1;
}
buf_ptr= buffer;
state= INACTIVE;
diff --git a/sql/my_json_writer.h b/sql/my_json_writer.h
index 4c7ca46ef7c..bc8002de529 100644
--- a/sql/my_json_writer.h
+++ b/sql/my_json_writer.h
@@ -13,7 +13,15 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+#ifndef JSON_WRITER_INCLUDED
+#define JSON_WRITER_INCLUDED
+#include "my_base.h"
+#include "sql_select.h"
+class Opt_trace_stmt;
+class Opt_trace_context;
class Json_writer;
+struct TABLE_LIST;
+
/*
Single_line_formatting_helper is used by Json_writer to do better formatting
@@ -78,14 +86,14 @@ public:
void init(Json_writer *owner_arg) { owner= owner_arg; }
- bool on_add_member(const char *name);
+ bool on_add_member(const char *name, size_t len);
bool on_start_array();
bool on_end_array();
void on_start_object();
// on_end_object() is not needed.
- bool on_add_str(const char *str);
+ bool on_add_str(const char *str, size_t num_bytes);
void flush_on_one_line();
void disable_and_flush();
@@ -93,6 +101,80 @@ public:
/*
+ Something that looks like class String, but has an internal limit of
+ how many bytes one can append to it.
+
+ Bytes that were truncated due to the size limitation are counted.
+*/
+
+class String_with_limit
+{
+public:
+
+ String_with_limit() : size_limit(SIZE_T_MAX), truncated_len(0)
+ {
+ str.length(0);
+ }
+
+ size_t get_truncated_bytes() const { return truncated_len; }
+ size_t get_size_limit() { return size_limit; }
+
+ void set_size_limit(size_t limit_arg)
+ {
+ // Setting size limit to be shorter than length will not have the desired
+ // effect
+ DBUG_ASSERT(str.length() < size_limit);
+ size_limit= limit_arg;
+ }
+
+ void append(const char *s, size_t size)
+ {
+ if (str.length() + size <= size_limit)
+ {
+ // Whole string can be added, just do it
+ str.append(s, size);
+ }
+ else
+ {
+ // We cannot add the whole string
+ if (str.length() < size_limit)
+ {
+ // But we can still add something
+ size_t bytes_to_add = size_limit - str.length();
+ str.append(s, bytes_to_add);
+ truncated_len += size - bytes_to_add;
+ }
+ else
+ truncated_len += size;
+ }
+ }
+
+ void append(const char *s)
+ {
+ append(s, strlen(s));
+ }
+
+ void append(char c)
+ {
+ if (str.length() + 1 > size_limit)
+ truncated_len++;
+ else
+ str.append(c);
+ }
+
+ const String *get_string() { return &str; }
+ size_t length() { return str.length(); }
+private:
+ String str;
+
+ // str must not get longer than this many bytes.
+ size_t size_limit;
+
+ // How many bytes were truncated from the string
+ size_t truncated_len;
+};
+
+/*
A class to write well-formed JSON documents. The documents are also formatted
for human readability.
*/
@@ -102,10 +184,15 @@ class Json_writer
public:
/* Add a member. We must be in an object. */
Json_writer& add_member(const char *name);
+ Json_writer& add_member(const char *name, size_t len);
/* Add atomic values */
void add_str(const char* val);
+ void add_str(const char* val, size_t num_bytes);
void add_str(const String &str);
+ void add_str(Item *item);
+ void add_table_name(const JOIN_TAB *tab);
+ void add_table_name(const TABLE* table);
void add_ll(longlong val);
void add_ull(ulonglong val);
@@ -116,6 +203,7 @@ public:
private:
void add_unquoted_str(const char* val);
+ void add_unquoted_str(const char* val, size_t len);
public:
/* Start a child object */
void start_object();
@@ -124,6 +212,14 @@ public:
void end_object();
void end_array();
+ /*
+ One can set a limit of how large a JSON document should be.
+ Writes beyond that size will be counted, but will not be collected.
+ */
+ void set_size_limit(size_t mem_size) { output.set_size_limit(mem_size); }
+
+ size_t get_truncated_bytes() { return output.get_truncated_bytes(); }
+
Json_writer() :
indent_level(0), document_start(true), element_started(false),
first_child(true)
@@ -147,13 +243,413 @@ private:
void start_element();
void start_sub_element();
- //const char *new_member_name;
public:
- String output;
+ String_with_limit output;
+};
+
+/* A class to add values to Json_writer_object and Json_writer_array */
+class Json_value_helper
+{
+ Json_writer* writer;
+
+public:
+ void init(Json_writer *my_writer) { writer= my_writer; }
+ void add_str(const char* val)
+ {
+ writer->add_str(val);
+ }
+ void add_str(const char* val, size_t length)
+ {
+ writer->add_str(val, length);
+ }
+ void add_str(const String &str)
+ {
+ writer->add_str(str.ptr(), str.length());
+ }
+ void add_str(const LEX_CSTRING &str)
+ {
+ writer->add_str(str.str, str.length);
+ }
+ void add_str(Item *item)
+ {
+ writer->add_str(item);
+ }
+
+ void add_ll(longlong val)
+ {
+ writer->add_ll(val);
+ }
+ void add_size(longlong val)
+ {
+ writer->add_size(val);
+ }
+ void add_double(double val)
+ {
+ writer->add_double(val);
+ }
+ void add_bool(bool val)
+ {
+ writer->add_bool(val);
+ }
+ void add_null()
+ {
+ writer->add_null();
+ }
+ void add_table_name(const JOIN_TAB *tab)
+ {
+ writer->add_table_name(tab);
+ }
+ void add_table_name(const TABLE* table)
+ {
+ writer->add_table_name(table);
+ }
+};
+
+/* A common base for Json_writer_object and Json_writer_array */
+class Json_writer_struct
+{
+protected:
+ Json_writer* my_writer;
+ Json_value_helper context;
+ /*
+ Tells when a json_writer_struct has been closed or not
+ */
+ bool closed;
+
+public:
+ explicit Json_writer_struct(THD *thd)
+ {
+ my_writer= thd->opt_trace.get_current_json();
+ context.init(my_writer);
+ closed= false;
+ }
+ bool trace_started()
+ {
+ return my_writer != 0;
+ }
};
/*
+ RAII-based class to start/end writing a JSON object into the JSON document
+
+ There is "ignore mode": one can initialize Json_writer_object with a NULL
+ Json_writer argument, and then all its calls will do nothing. This is used
+ by optimizer trace which can be enabled or disabled.
+*/
+
+class Json_writer_object : public Json_writer_struct
+{
+private:
+ void add_member(const char *name)
+ {
+ my_writer->add_member(name);
+ }
+public:
+ explicit Json_writer_object(THD *thd)
+ : Json_writer_struct(thd)
+ {
+ if (unlikely(my_writer))
+ my_writer->start_object();
+ }
+
+ explicit Json_writer_object(THD* thd, const char *str)
+ : Json_writer_struct(thd)
+ {
+ if (unlikely(my_writer))
+ my_writer->add_member(str).start_object();
+ }
+
+ ~Json_writer_object()
+ {
+ if (my_writer && !closed)
+ my_writer->end_object();
+ closed= TRUE;
+ }
+
+ Json_writer_object& add(const char *name, bool value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ {
+ add_member(name);
+ context.add_bool(value);
+ }
+ return *this;
+ }
+ Json_writer_object& add(const char *name, ulonglong value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ {
+ add_member(name);
+ context.add_ll(static_cast<longlong>(value));
+ }
+ return *this;
+ }
+ Json_writer_object& add(const char *name, longlong value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ {
+ add_member(name);
+ context.add_ll(value);
+ }
+ return *this;
+ }
+ Json_writer_object& add(const char *name, double value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ {
+ add_member(name);
+ context.add_double(value);
+ }
+ return *this;
+ }
+ #ifndef _WIN64
+ Json_writer_object& add(const char *name, size_t value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ {
+ add_member(name);
+ context.add_ll(static_cast<longlong>(value));
+ }
+ return *this;
+ }
+ #endif
+ Json_writer_object& add(const char *name, const char *value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ {
+ add_member(name);
+ context.add_str(value);
+ }
+ return *this;
+ }
+ Json_writer_object& add(const char *name, const char *value, size_t num_bytes)
+ {
+ add_member(name);
+ context.add_str(value, num_bytes);
+ return *this;
+ }
+ Json_writer_object& add(const char *name, const LEX_CSTRING &value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ {
+ add_member(name);
+ context.add_str(value.str, value.length);
+ }
+ return *this;
+ }
+ Json_writer_object& add(const char *name, Item *value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ {
+ add_member(name);
+ context.add_str(value);
+ }
+ return *this;
+ }
+ Json_writer_object& add_null(const char*name)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ {
+ add_member(name);
+ context.add_null();
+ }
+ return *this;
+ }
+ Json_writer_object& add_table_name(const JOIN_TAB *tab)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ {
+ add_member("table");
+ context.add_table_name(tab);
+ }
+ return *this;
+ }
+ Json_writer_object& add_table_name(const TABLE *table)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ {
+ add_member("table");
+ context.add_table_name(table);
+ }
+ return *this;
+ }
+ Json_writer_object& add_select_number(uint select_number)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ {
+ add_member("select_id");
+ if (unlikely(select_number >= INT_MAX))
+ context.add_str("fake");
+ else
+ context.add_ll(static_cast<longlong>(select_number));
+ }
+ return *this;
+ }
+ void end()
+ {
+ DBUG_ASSERT(!closed);
+ if (unlikely(my_writer))
+ my_writer->end_object();
+ closed= TRUE;
+ }
+};
+
+
+/*
+ RAII-based class to start/end writing a JSON array into the JSON document
+
+ There is "ignore mode": one can initialize Json_writer_array with a NULL
+ Json_writer argument, and then all its calls will do nothing. This is used
+ by optimizer trace which can be enabled or disabled.
+*/
+
+class Json_writer_array : public Json_writer_struct
+{
+public:
+ Json_writer_array(THD *thd): Json_writer_struct(thd)
+ {
+ if (unlikely(my_writer))
+ my_writer->start_array();
+ }
+
+ Json_writer_array(THD *thd, const char *str) : Json_writer_struct(thd)
+ {
+ if (unlikely(my_writer))
+ my_writer->add_member(str).start_array();
+ }
+ ~Json_writer_array()
+ {
+ if (unlikely(my_writer && !closed))
+ {
+ my_writer->end_array();
+ closed= TRUE;
+ }
+ }
+ void end()
+ {
+ DBUG_ASSERT(!closed);
+ if (unlikely(my_writer))
+ my_writer->end_array();
+ closed= TRUE;
+ }
+
+ Json_writer_array& add(bool value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ context.add_bool(value);
+ return *this;
+ }
+ Json_writer_array& add(ulonglong value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ context.add_ll(static_cast<longlong>(value));
+ return *this;
+ }
+ Json_writer_array& add(longlong value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ context.add_ll(value);
+ return *this;
+ }
+ Json_writer_array& add(double value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ context.add_double(value);
+ return *this;
+ }
+ #ifndef _WIN64
+ Json_writer_array& add(size_t value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ context.add_ll(static_cast<longlong>(value));
+ return *this;
+ }
+ #endif
+ Json_writer_array& add(const char *value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ context.add_str(value);
+ return *this;
+ }
+ Json_writer_array& add(const char *value, size_t num_bytes)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ context.add_str(value, num_bytes);
+ return *this;
+ }
+ Json_writer_array& add(const LEX_CSTRING &value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ context.add_str(value.str, value.length);
+ return *this;
+ }
+ Json_writer_array& add(Item *value)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ context.add_str(value);
+ return *this;
+ }
+ Json_writer_array& add_null()
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ context.add_null();
+ return *this;
+ }
+ Json_writer_array& add_table_name(const JOIN_TAB *tab)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ context.add_table_name(tab);
+ return *this;
+ }
+ Json_writer_array& add_table_name(const TABLE *table)
+ {
+ DBUG_ASSERT(!closed);
+ if (my_writer)
+ context.add_table_name(table);
+ return *this;
+ }
+};
+
+/*
+ RAII-based class to disable writing into the JSON document
+ The tracing is disabled as soon as the object is created.
+ The destuctor is called as soon as we exit the scope of the object
+ and the tracing is enabled back.
+*/
+
+class Json_writer_temp_disable
+{
+public:
+ Json_writer_temp_disable(THD *thd_arg);
+ ~Json_writer_temp_disable();
+ THD *thd;
+};
+
+/*
RAII-based helper class to detect incorrect use of Json_writer.
The idea is that a function typically must leave Json_writer at the same
@@ -193,4 +689,4 @@ public:
#endif
};
-
+#endif
diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc
index 9639051e93c..47ce6171357 100644
--- a/sql/mysql_install_db.cc
+++ b/sql/mysql_install_db.cc
@@ -39,7 +39,8 @@ struct IUnknown;
extern "C" const char* mysql_bootstrap_sql[];
-char default_os_user[]= "NT AUTHORITY\\NetworkService";
+static char default_os_user[]= "NT AUTHORITY\\NetworkService";
+static char default_datadir[MAX_PATH];
static int create_db_instance();
static uint opt_silent;
static char datadir_buffer[FN_REFLEN];
@@ -169,8 +170,27 @@ int main(int argc, char **argv)
exit(error);
if (!opt_datadir)
{
- my_print_help(my_long_options);
- die("parameter --datadir=# is mandatory");
+ /*
+ Figure out default data directory. It "data" directory, next to "bin" directory, where
+ mysql_install_db.exe resides.
+ */
+ strcpy(default_datadir, self_name);
+ p = strrchr(default_datadir, FN_LIBCHAR);
+ if (p)
+ {
+ *p= 0;
+ p= strrchr(default_datadir, FN_LIBCHAR);
+ if (p)
+ *p= 0;
+ }
+ if (!p)
+ {
+ die("--datadir option not provided, and default datadir not found");
+ my_print_help(my_long_options);
+ }
+ strcat_s(default_datadir, "\\data");
+ opt_datadir= default_datadir;
+ printf("Default data directory is %s\n",opt_datadir);
}
/* Print some help on errors */
@@ -198,7 +218,7 @@ int main(int argc, char **argv)
die("database creation failed");
}
- printf("Creation of the database was successful");
+ printf("Creation of the database was successful\n");
return 0;
}
@@ -343,17 +363,20 @@ static int create_myini()
static const char update_root_passwd_part1[]=
- "UPDATE mysql.user SET Password = PASSWORD(";
+ "UPDATE mysql.global_priv SET priv=json_set(priv,"
+ "'$.password_last_changed', UNIX_TIMESTAMP(),"
+ "'$.plugin','mysql_native_password',"
+ "'$.authentication_string',PASSWORD(";
static const char update_root_passwd_part2[]=
- ") where User='root';\n";
-static const char remove_default_user_cmd[]=
+ ")) 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' "
+ "CREATE TEMPORARY TABLE tmp_user LIKE global_priv;\n"
+ "INSERT INTO tmp_user SELECT * from global_priv where user='root' "
" AND host='localhost';\n"
"UPDATE tmp_user SET host='%';\n"
- "INSERT INTO user SELECT * FROM tmp_user;\n"
+ "INSERT INTO global_priv SELECT * FROM tmp_user;\n"
"DROP TABLE tmp_user;\n";
static const char end_of_script[]="-- end.";
diff --git a/sql/mysql_upgrade_service.cc b/sql/mysql_upgrade_service.cc
index b3683618e6e..a10b5bb5162 100644
--- a/sql/mysql_upgrade_service.cc
+++ b/sql/mysql_upgrade_service.cc
@@ -496,7 +496,7 @@ int main(int argc, char **argv)
old_mysqld_exe_exists?",this can take some time":"(skipped)");
char socket_param[FN_REFLEN];
- sprintf_s(socket_param, "--socket=mysql_upgrade_service_%d",
+ sprintf_s(socket_param, "--socket=mysql_upgrade_service_%u",
GetCurrentProcessId());
DWORD start_duration_ms = 0;
@@ -513,7 +513,7 @@ int main(int argc, char **argv)
die("Cannot start mysqld.exe process, last error =%u", GetLastError());
}
char pipe_name[64];
- snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\pipe\\mysql_upgrade_service_%u",
+ snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\pipe\\mysql_upgrade_service_%lu",
GetCurrentProcessId());
for (;;)
{
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 612e905ef68..7e3ce878cdc 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -54,6 +54,7 @@
#include <m_ctype.h>
#include <my_dir.h>
#include <my_bit.h>
+#include "my_cpu.h"
#include "slave.h"
#include "rpl_mi.h"
#include "sql_repl.h"
@@ -72,8 +73,10 @@
#include "debug_sync.h"
#include "wsrep_mysqld.h"
#include "wsrep_var.h"
+#ifdef WITH_WSREP
#include "wsrep_thd.h"
#include "wsrep_sst.h"
+#endif /* WITH_WSREP */
#include "proxy_protocol.h"
#include "sql_callback.h"
@@ -117,13 +120,17 @@
#include <poll.h>
#endif
+#ifdef _WIN32
+#include <handle_connections_win.h>
+#endif
+
#include <my_service_manager.h>
#define mysqld_charset &my_charset_latin1
/* We have HAVE_valgrind below as this speeds up the shutdown of MySQL */
-#if defined(SIGNALS_DONT_BREAK_READ) || defined(HAVE_valgrind) && defined(__linux__)
+#if defined(HAVE_valgrind) && defined(__linux__)
#define HAVE_CLOSE_SERVER_SOCK 1
#endif
@@ -311,23 +318,6 @@ MY_TIMER_INFO sys_timer_info;
/* static variables */
#ifdef HAVE_PSI_INTERFACE
-#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
-static PSI_thread_key key_thread_handle_con_namedpipes;
-static PSI_cond_key key_COND_handler_count;
-#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */
-
-#if defined(HAVE_SMEM) && !defined(EMBEDDED_LIBRARY)
-static PSI_thread_key key_thread_handle_con_sharedmem;
-#endif /* HAVE_SMEM && !EMBEDDED_LIBRARY */
-
-#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
-static PSI_thread_key key_thread_handle_con_sockets;
-#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */
-
-#ifdef __WIN__
-static PSI_thread_key key_thread_handle_shutdown;
-#endif /* __WIN__ */
-
#ifdef HAVE_OPENSSL10
static PSI_rwlock_key key_rwlock_openssl;
#endif
@@ -348,7 +338,6 @@ PSI_statement_info stmt_info_rpl;
static bool lower_case_table_names_used= 0;
static bool max_long_data_size_used= false;
static bool volatile select_thread_in_use, signal_thread_in_use;
-static volatile bool ready_to_exit;
static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0;
static my_bool opt_short_log_format= 0, opt_silent_startup= 0;
@@ -362,6 +351,7 @@ static char *character_set_filesystem_name;
static char *lc_messages;
static char *lc_time_names_name;
char *my_bind_addr_str;
+int server_socket_ai_family;
static char *default_collation_name;
char *default_storage_engine, *default_tmp_storage_engine;
char *enforced_storage_engine=NULL;
@@ -395,25 +385,10 @@ bool opt_endinfo, using_udf_functions;
my_bool locked_in_memory;
bool opt_using_transactions;
bool volatile abort_loop;
-bool volatile shutdown_in_progress;
uint volatile global_disable_checkpoint;
#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
ulong slow_start_timeout;
#endif
-/*
- True if the bootstrap thread is running. Protected by LOCK_start_thread.
- Used in bootstrap() function to determine if the bootstrap thread
- has completed. Note, that we can't use 'thread_count' instead,
- since in 5.1, in presence of the Event Scheduler, there may be
- event threads running in parallel, so it's impossible to know
- what value of 'thread_count' is a sign of completion of the
- bootstrap thread.
-
- At the same time, we can't start the event scheduler after
- bootstrap either, since we want to be able to process event-related
- SQL commands in the init file and in --bootstrap mode.
-*/
-bool volatile in_bootstrap= FALSE;
/**
@brief 'grant_option' is used to indicate if privileges needs
to be checked, in which case the lock, LOCK_grant, is used
@@ -481,8 +456,9 @@ ulong delay_key_write_options;
uint protocol_version;
uint lower_case_table_names;
ulong tc_heuristic_recover= 0;
-int32 thread_count, service_thread_count;
-int32 slave_open_temp_tables;
+Atomic_counter<uint32_t> thread_count;
+bool shutdown_wait_for_slaves;
+Atomic_counter<uint32_t> slave_open_temp_tables;
ulong thread_created;
ulong back_log, connect_timeout, concurrency, server_id;
ulong what_to_log;
@@ -506,8 +482,8 @@ ulonglong test_flags;
ulonglong query_cache_size=0;
ulong query_cache_limit=0;
ulong executed_events=0;
-query_id_t global_query_id;
-ulong aborted_threads, aborted_connects;
+Atomic_counter<query_id_t> global_query_id;
+ulong aborted_threads, aborted_connects, aborted_connects_preauth;
ulong delayed_insert_timeout, delayed_insert_limit, delayed_queue_size;
ulong delayed_insert_threads, delayed_insert_writes, delayed_rows_in_use;
ulong delayed_insert_errors,flush_time;
@@ -515,6 +491,7 @@ ulong specialflag=0;
ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0;
ulong max_connections, max_connect_errors;
+uint max_password_errors;
ulong extra_max_connections;
uint max_digest_length= 0;
ulong slave_retried_transactions;
@@ -526,6 +503,8 @@ ulong feature_files_opened_with_delayed_keys= 0, feature_check_constraint= 0;
ulonglong denied_connections;
my_decimal decimal_zero;
long opt_secure_timestamp;
+uint default_password_lifetime;
+my_bool disconnect_on_expired_password;
/*
Maximum length of parameter value which can be set through
@@ -556,7 +535,6 @@ ulong slow_launch_threads = 0;
uint sync_binlog_period= 0, sync_relaylog_period= 0,
sync_relayloginfo_period= 0, sync_masterinfo_period= 0;
ulong expire_logs_days = 0;
-ulong rpl_recovery_rank=0;
/**
Soft upper limit for number of sp_head objects that can be stored
in the sp_cache for one connection.
@@ -570,6 +548,7 @@ ulong opt_binlog_commit_wait_count= 0;
ulong opt_binlog_commit_wait_usec= 0;
ulong opt_slave_parallel_max_queued= 131072;
my_bool opt_gtid_ignore_duplicates= FALSE;
+uint opt_gtid_cleanup_batch_size= 64;
const double log_10[] = {
1e000, 1e001, 1e002, 1e003, 1e004, 1e005, 1e006, 1e007, 1e008, 1e009,
@@ -661,29 +640,11 @@ Lt_creator lt_creator;
Ge_creator ge_creator;
Le_creator le_creator;
-MYSQL_FILE *bootstrap_file;
-int bootstrap_error;
-
-I_List<THD> threads;
+THD_list server_threads;
Rpl_filter* cur_rpl_filter;
Rpl_filter* global_rpl_filter;
Rpl_filter* binlog_filter;
-THD *first_global_thread()
-{
- if (threads.is_empty())
- return NULL;
- return threads.head();
-}
-
-THD *next_global_thread(THD *thd)
-{
- if (threads.is_last(thd))
- return NULL;
- struct ilink *next= thd->next;
- return static_cast<THD*>(next);
-}
-
struct system_variables global_system_variables;
/**
Following is just for options parsing, used with a difference against
@@ -713,29 +674,19 @@ SHOW_COMP_OPTION have_crypt, have_compress;
SHOW_COMP_OPTION have_profiling;
SHOW_COMP_OPTION have_openssl;
+#ifndef EMBEDDED_LIBRARY
+static std::atomic<char*> shutdown_user;
+#endif //EMBEDDED_LIBRARY
+
/* Thread specific variables */
pthread_key(THD*, THR_THD);
/*
- LOCK_thread_count protects the following variables:
- thread_count Number of threads with THD that servers queries.
- threads Linked list of active THD's.
- The effect of this is that one can't unlink and
- delete a THD as long as one has locked
- LOCK_thread_count.
- ready_to_exit
- delayed_insert_threads
-*/
-mysql_mutex_t LOCK_thread_count;
-
-/*
LOCK_start_thread is used to syncronize thread start and stop with
other threads.
It also protects these variables:
- handler_count
- in_bootstrap
select_thread_in_use
slave_init_thread_running
check_temp_dir() call
@@ -744,13 +695,12 @@ mysql_mutex_t LOCK_start_thread;
mysql_mutex_t LOCK_thread_cache;
mysql_mutex_t
- LOCK_status, LOCK_show_status, LOCK_error_log, LOCK_short_uuid_generator,
+ LOCK_status, LOCK_error_log, LOCK_short_uuid_generator,
LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
LOCK_crypt,
LOCK_global_system_variables,
- LOCK_user_conn, LOCK_slave_list,
+ LOCK_user_conn,
LOCK_connection_count, LOCK_error_messages;
-
mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats,
LOCK_global_table_stats, LOCK_global_index_stats;
@@ -772,8 +722,10 @@ mysql_mutex_t LOCK_prepared_stmt_count;
mysql_mutex_t LOCK_des_key_file;
#endif
mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
+mysql_rwlock_t LOCK_ssl_refresh;
+mysql_rwlock_t LOCK_all_status_vars;
mysql_prlock_t LOCK_system_variables_hash;
-mysql_cond_t COND_thread_count, COND_start_thread;
+mysql_cond_t COND_start_thread;
pthread_t signal_thread;
pthread_attr_t connection_attrib;
mysql_mutex_t LOCK_server_started;
@@ -793,7 +745,6 @@ char *opt_binlog_index_name=0;
/* Static variables */
-static volatile sig_atomic_t kill_in_progress;
my_bool opt_stack_trace;
my_bool opt_expect_abort= 0, opt_bootstrap= 0;
static my_bool opt_myisam_log;
@@ -913,7 +864,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_LOCK_manager,
key_LOCK_prepared_stmt_count,
key_LOCK_rpl_status, key_LOCK_server_started,
- key_LOCK_status, key_LOCK_show_status,
+ key_LOCK_status,
key_LOCK_system_variables_hash, key_LOCK_thd_data, key_LOCK_thd_kill,
key_LOCK_user_conn, key_LOCK_uuid_short_generator, key_LOG_LOCK_log,
key_master_info_data_lock, key_master_info_run_lock,
@@ -922,9 +873,9 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_rpl_group_info_sleep_lock,
key_relay_log_info_log_space_lock, key_relay_log_info_run_lock,
key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data,
- key_LOCK_error_messages, key_LOG_INFO_lock,
+ key_LOCK_error_messages,
key_LOCK_start_thread,
- key_LOCK_thread_count, key_LOCK_thread_cache,
+ key_LOCK_thread_cache,
key_PARTITION_LOCK_auto_inc;
PSI_mutex_key key_RELAYLOG_LOCK_index;
PSI_mutex_key key_LOCK_relaylog_end_pos;
@@ -985,7 +936,6 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_rpl_status, "LOCK_rpl_status", PSI_FLAG_GLOBAL},
{ &key_LOCK_server_started, "LOCK_server_started", PSI_FLAG_GLOBAL},
{ &key_LOCK_status, "LOCK_status", PSI_FLAG_GLOBAL},
- { &key_LOCK_show_status, "LOCK_show_status", PSI_FLAG_GLOBAL},
{ &key_LOCK_system_variables_hash, "LOCK_system_variables_hash", PSI_FLAG_GLOBAL},
{ &key_LOCK_stats, "LOCK_stats", PSI_FLAG_GLOBAL},
{ &key_LOCK_global_user_client_stats, "LOCK_global_user_client_stats", PSI_FLAG_GLOBAL},
@@ -1016,8 +966,6 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_prepare_ordered, "LOCK_prepare_ordered", PSI_FLAG_GLOBAL},
{ &key_LOCK_after_binlog_sync, "LOCK_after_binlog_sync", PSI_FLAG_GLOBAL},
{ &key_LOCK_commit_ordered, "LOCK_commit_ordered", PSI_FLAG_GLOBAL},
- { &key_LOG_INFO_lock, "LOG_INFO::lock", 0},
- { &key_LOCK_thread_count, "LOCK_thread_count", PSI_FLAG_GLOBAL},
{ &key_LOCK_thread_cache, "LOCK_thread_cache", PSI_FLAG_GLOBAL},
{ &key_PARTITION_LOCK_auto_inc, "HA_DATA_PARTITION::LOCK_auto_inc", 0},
{ &key_LOCK_slave_state, "LOCK_slave_state", 0},
@@ -1035,7 +983,10 @@ PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger,
key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave,
key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock,
key_LOCK_SEQUENCE,
- key_rwlock_LOCK_vers_stats, key_rwlock_LOCK_stat_serial;
+ key_rwlock_LOCK_vers_stats, key_rwlock_LOCK_stat_serial,
+ key_rwlock_LOCK_ssl_refresh,
+ key_rwlock_THD_list,
+ key_rwlock_LOCK_all_status_vars;
static PSI_rwlock_info all_server_rwlocks[]=
{
@@ -1050,7 +1001,10 @@ static PSI_rwlock_info all_server_rwlocks[]=
{ &key_rwlock_LOCK_system_variables_hash, "LOCK_system_variables_hash", PSI_FLAG_GLOBAL},
{ &key_rwlock_query_cache_query_lock, "Query_cache_query::lock", 0},
{ &key_rwlock_LOCK_vers_stats, "Vers_field_stats::lock", 0},
- { &key_rwlock_LOCK_stat_serial, "TABLE_SHARE::LOCK_stat_serial", 0}
+ { &key_rwlock_LOCK_stat_serial, "TABLE_SHARE::LOCK_stat_serial", 0},
+ { &key_rwlock_LOCK_ssl_refresh, "LOCK_ssl_refresh", PSI_FLAG_GLOBAL },
+ { &key_rwlock_THD_list, "THD_list::lock", PSI_FLAG_GLOBAL },
+ { &key_rwlock_LOCK_all_status_vars, "LOCK_all_status_vars", PSI_FLAG_GLOBAL }
};
#ifdef HAVE_MMAP
@@ -1071,7 +1025,7 @@ PSI_cond_key key_BINLOG_COND_xid_list,
key_relay_log_info_start_cond, key_relay_log_info_stop_cond,
key_rpl_group_info_sleep_cond,
key_TABLE_SHARE_cond, key_user_level_lock_cond,
- key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache,
+ key_COND_thread_cache, key_COND_flush_thread_cache,
key_COND_start_thread, key_COND_binlog_send,
key_BINLOG_COND_queue_busy;
PSI_cond_key key_RELAYLOG_COND_relay_log_updated,
@@ -1088,9 +1042,6 @@ PSI_cond_key key_COND_ack_receiver;
static PSI_cond_info all_server_conds[]=
{
-#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
- { &key_COND_handler_count, "COND_handler_count", PSI_FLAG_GLOBAL},
-#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */
#ifdef HAVE_MMAP
{ &key_PAGE_cond, "PAGE::cond", 0},
{ &key_COND_active, "TC_LOG_MMAP::COND_active", 0},
@@ -1124,7 +1075,6 @@ static PSI_cond_info all_server_conds[]=
{ &key_rpl_group_info_sleep_cond, "Rpl_group_info::sleep_cond", 0},
{ &key_TABLE_SHARE_cond, "TABLE_SHARE::cond", 0},
{ &key_user_level_lock_cond, "User_level_lock::cond", 0},
- { &key_COND_thread_count, "COND_thread_count", PSI_FLAG_GLOBAL},
{ &key_COND_thread_cache, "COND_thread_cache", PSI_FLAG_GLOBAL},
{ &key_COND_flush_thread_cache, "COND_flush_thread_cache", PSI_FLAG_GLOBAL},
{ &key_COND_rpl_thread, "COND_rpl_thread", 0},
@@ -1142,7 +1092,7 @@ static PSI_cond_info all_server_conds[]=
{ &key_TABLE_SHARE_COND_rotation, "TABLE_SHARE::COND_rotation", 0}
};
-PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
+PSI_thread_key key_thread_delayed_insert,
key_thread_handle_manager, key_thread_main,
key_thread_one_connection, key_thread_signal_hand,
key_thread_slave_background, key_rpl_parallel_thread;
@@ -1150,23 +1100,6 @@ PSI_thread_key key_thread_ack_receiver;
static PSI_thread_info all_server_threads[]=
{
-#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
- { &key_thread_handle_con_namedpipes, "con_named_pipes", PSI_FLAG_GLOBAL},
-#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */
-
-#if defined(HAVE_SMEM) && !defined(EMBEDDED_LIBRARY)
- { &key_thread_handle_con_sharedmem, "con_shared_mem", PSI_FLAG_GLOBAL},
-#endif /* HAVE_SMEM && !EMBEDDED_LIBRARY */
-
-#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
- { &key_thread_handle_con_sockets, "con_sockets", PSI_FLAG_GLOBAL},
-#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */
-
-#ifdef __WIN__
- { &key_thread_handle_shutdown, "shutdown", PSI_FLAG_GLOBAL},
-#endif /* __WIN__ */
-
- { &key_thread_bootstrap, "bootstrap", PSI_FLAG_GLOBAL},
{ &key_thread_delayed_insert, "delayed_insert", 0},
{ &key_thread_handle_manager, "manager", PSI_FLAG_GLOBAL},
{ &key_thread_main, "main", PSI_FLAG_GLOBAL},
@@ -1317,10 +1250,10 @@ void Buffered_log::print()
switch(m_level)
{
case ERROR_LEVEL:
- sql_print_error("Buffered error: %s\n", m_message.c_ptr_safe());
+ sql_print_error("Buffered error: %s", m_message.c_ptr_safe());
break;
case WARNING_LEVEL:
- sql_print_warning("Buffered warning: %s\n", m_message.c_ptr_safe());
+ sql_print_warning("Buffered warning: %s", m_message.c_ptr_safe());
break;
case INFORMATION_LEVEL:
/*
@@ -1406,10 +1339,10 @@ void Buffered_logs::print()
/** Logs reported before a logger is available. */
static Buffered_logs buffered_logs;
-static MYSQL_SOCKET unix_sock, base_ip_sock, extra_ip_sock;
struct my_rnd_struct sql_rand; ///< used by sql_class.cc:THD::THD()
#ifndef EMBEDDED_LIBRARY
+MYSQL_SOCKET unix_sock, base_ip_sock, extra_ip_sock;
/**
Error reporter that buffer log messages.
@param level log message level
@@ -1465,27 +1398,18 @@ static pthread_t select_thread;
#undef getpid
#include <process.h>
-static mysql_cond_t COND_handler_count;
-static uint handler_count;
static bool start_mode=0, use_opt_args;
static int opt_argc;
static char **opt_argv;
#if !defined(EMBEDDED_LIBRARY)
-static HANDLE hEventShutdown;
+HANDLE hEventShutdown;
static char shutdown_event_name[40];
#include "nt_servc.h"
static NTService Service; ///< Service object for WinNT
#endif /* EMBEDDED_LIBRARY */
#endif /* __WIN__ */
-#ifdef _WIN32
-#include <sddl.h> /* ConvertStringSecurityDescriptorToSecurityDescriptor */
-static char pipe_name[512];
-static SECURITY_ATTRIBUTES saPipeSecurity;
-static HANDLE hPipe = INVALID_HANDLE_VALUE;
-#endif
-
#ifndef EMBEDDED_LIBRARY
bool mysqld_embedded=0;
#else
@@ -1506,17 +1430,13 @@ int deny_severity = LOG_WARNING;
ulong query_cache_min_res_unit= QUERY_CACHE_MIN_RESULT_DATA_SIZE;
Query_cache query_cache;
#endif
-#ifdef HAVE_SMEM
-const char *shared_memory_base_name= default_shared_memory_base_name;
-my_bool opt_enable_shared_memory;
-HANDLE smem_event_connect_request= 0;
-#endif
+
my_bool opt_use_ssl = 0;
char *opt_ssl_ca= NULL, *opt_ssl_capath= NULL, *opt_ssl_cert= NULL,
*opt_ssl_cipher= NULL, *opt_ssl_key= NULL, *opt_ssl_crl= NULL,
- *opt_ssl_crlpath= NULL;
-
+ *opt_ssl_crlpath= NULL, *opt_tls_version= NULL;
+ulonglong tls_version= 0;
static scheduler_functions thread_scheduler_struct, extra_thread_scheduler_struct;
scheduler_functions *thread_scheduler= &thread_scheduler_struct,
@@ -1524,7 +1444,7 @@ scheduler_functions *thread_scheduler= &thread_scheduler_struct,
#ifdef HAVE_OPENSSL
#include <openssl/crypto.h>
-#ifdef HAVE_OPENSSL10
+#if defined(HAVE_OPENSSL10) && !defined(HAVE_WOLFSSL)
typedef struct CRYPTO_dynlock_value
{
mysql_rwlock_t lock;
@@ -1562,19 +1482,11 @@ extern "C" my_bool mysqld_get_one_option(int, const struct my_option *, char *);
static int init_thread_environment();
static char *get_relative_path(const char *path);
static int fix_paths(void);
+#ifndef _WIN32
void handle_connections_sockets();
-#ifdef _WIN32
-pthread_handler_t handle_connections_sockets_thread(void *arg);
#endif
-pthread_handler_t kill_server_thread(void *arg);
-static void bootstrap(MYSQL_FILE *file);
+
static bool read_init_file(char *file_name);
-#ifdef _WIN32
-pthread_handler_t handle_connections_namedpipes(void *arg);
-#endif
-#ifdef HAVE_SMEM
-pthread_handler_t handle_connections_shared_memory(void *arg);
-#endif
pthread_handler_t handle_slave(void *arg);
static void clean_up(bool print_message);
static int test_if_case_insensitive(const char *dir_name);
@@ -1598,19 +1510,106 @@ static void end_ssl();
** Code to end mysqld
****************************************************************************/
-static void close_connections(void)
+/* common callee of two shutdown phases */
+static void kill_thread(THD *thd)
+{
+ mysql_mutex_lock(&thd->LOCK_thd_kill);
+ thd->abort_current_cond_wait(true);
+ mysql_mutex_unlock(&thd->LOCK_thd_kill);
+}
+
+
+/**
+ First shutdown everything but slave threads and binlog dump connections
+*/
+static my_bool kill_thread_phase_1(THD *thd, void *)
+{
+ DBUG_PRINT("quit", ("Informing thread %ld that it's time to die",
+ (ulong) thd->thread_id));
+ if (thd->slave_thread || thd->is_binlog_dump_thread())
+ return 0;
+
+ if (DBUG_EVALUATE_IF("only_kill_system_threads", !thd->system_thread, 0))
+ return 0;
+
+ thd->set_killed(KILL_SERVER_HARD);
+ MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (thd));
+ kill_thread(thd);
+ return 0;
+}
+
+
+/**
+ Last shutdown binlog dump connections
+*/
+static my_bool kill_thread_phase_2(THD *thd, void *)
+{
+ if (shutdown_wait_for_slaves)
+ {
+ thd->set_killed(KILL_SERVER);
+ }
+ else
+ {
+ thd->set_killed(KILL_SERVER_HARD);
+ MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (thd));
+ }
+ kill_thread(thd);
+ return 0;
+}
+
+
+/* associated with the kill thread phase 1 */
+static my_bool warn_threads_active_after_phase_1(THD *thd, void *)
+{
+ if (!thd->is_binlog_dump_thread() && thd->vio_ok())
+ sql_print_warning("%s: Thread %llu (user : '%s') did not exit\n", my_progname,
+ (ulonglong) thd->thread_id,
+ (thd->main_security_ctx.user ?
+ thd->main_security_ctx.user : ""));
+ return 0;
+}
+
+
+/* associated with the kill thread phase 2 */
+static my_bool warn_threads_active_after_phase_2(THD *thd, void *)
+{
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ // dump thread may not have yet (or already) current_linfo set
+ sql_print_warning("Dump thread %llu last sent to server %lu "
+ "binlog file:pos %s:%llu",
+ thd->thread_id, thd->variables.server_id,
+ thd->current_linfo ?
+ my_basename(thd->current_linfo->log_file_name) : "NULL",
+ thd->current_linfo ? thd->current_linfo->pos : 0);
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+
+ return 0;
+}
+
+
+/**
+ Kills main thread.
+
+ @note this function is responsible for setting abort_loop and breaking
+ poll() in main thread. Shutdown as such is supposed to be performed by main
+ thread itself.
+*/
+
+static void break_connect_loop()
{
#ifdef EXTRA_DEBUG
int count=0;
#endif
- DBUG_ENTER("close_connections");
- /* Clear thread cache */
- kill_cached_threads++;
- flush_thread_cache();
+ abort_loop= 1;
- /* kill connection thread */
-#if !defined(__WIN__)
+#if defined(__WIN__)
+ if (!SetEvent(hEventShutdown))
+ DBUG_PRINT("error", ("Got error: %ld from SetEvent", GetLastError()));
+#else
+ /* Avoid waiting for ourselves when thread-handling=no-threads. */
+ if (pthread_equal(pthread_self(), select_thread))
+ return;
DBUG_PRINT("quit", ("waiting for select thread: %lu",
(ulong)select_thread));
@@ -1631,7 +1630,7 @@ static void close_connections(void)
error= mysql_cond_timedwait(&COND_start_thread, &LOCK_start_thread,
&abstime);
if (error != EINTR)
- break;
+ break;
}
#ifdef EXTRA_DEBUG
if (error != 0 && error != ETIMEDOUT && !count++)
@@ -1641,8 +1640,59 @@ static void close_connections(void)
}
mysql_mutex_unlock(&LOCK_start_thread);
#endif /* __WIN__ */
+}
+/**
+ A wrapper around kill_main_thrad().
+
+ Sets shutdown user. This function may be called by multiple threads
+ concurrently, thus it performs safe update of shutdown_user
+ (first thread wins).
+*/
+
+void kill_mysql(THD *thd)
+{
+ char user_host_buff[MAX_USER_HOST_SIZE + 1];
+ char *user, *expected_shutdown_user= 0;
+
+ make_user_name(thd, user_host_buff);
+
+ if ((user= my_strdup(user_host_buff, MYF(0))) &&
+ !shutdown_user.compare_exchange_strong(expected_shutdown_user,
+ user,
+ std::memory_order_relaxed,
+ std::memory_order_relaxed))
+ {
+ my_free(user);
+ }
+
+ DBUG_EXECUTE_IF("mysql_admin_shutdown_wait_for_slaves",
+ thd->lex->is_shutdown_wait_for_slaves= true;);
+ DBUG_EXECUTE_IF("simulate_delay_at_shutdown",
+ {
+ DBUG_ASSERT(binlog_dump_thread_count == 3);
+ const char act[]=
+ "now "
+ "SIGNAL greetings_from_kill_mysql";
+ DBUG_ASSERT(!debug_sync_set_action(thd,
+ STRING_WITH_LEN(act)));
+ };);
+
+ if (thd->lex->is_shutdown_wait_for_slaves)
+ shutdown_wait_for_slaves= true;
+ break_connect_loop();
+}
+
+
+static void close_connections(void)
+{
+ DBUG_ENTER("close_connections");
+
+ /* Clear thread cache */
+ kill_cached_threads++;
+ flush_thread_cache();
+
/* Abort listening to new connections */
DBUG_PRINT("quit",("Closing sockets"));
if (!opt_disable_networking )
@@ -1658,30 +1708,7 @@ static void close_connections(void)
extra_ip_sock= MYSQL_INVALID_SOCKET;
}
}
-#ifdef _WIN32
- if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe)
- {
- HANDLE temp;
- DBUG_PRINT("quit", ("Closing named pipes") );
-
- /* Create connection to the handle named pipe handler to break the loop */
- if ((temp = CreateFile(pipe_name,
- GENERIC_READ | GENERIC_WRITE,
- 0,
- NULL,
- OPEN_EXISTING,
- 0,
- NULL )) != INVALID_HANDLE_VALUE)
- {
- WaitNamedPipe(pipe_name, 1000);
- DWORD dwMode = PIPE_READMODE_BYTE | PIPE_WAIT;
- SetNamedPipeHandleState(temp, &dwMode, NULL, NULL);
- CancelIo(temp);
- DisconnectNamedPipe(temp);
- CloseHandle(temp);
- }
- }
-#endif
+
#ifdef HAVE_SYS_UN_H
if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET)
{
@@ -1697,56 +1724,7 @@ static void close_connections(void)
This will give the threads some time to gracefully abort their
statements and inform their clients that the server is about to die.
*/
-
- THD *tmp;
- mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
-
- I_List_iterator<THD> it(threads);
- while ((tmp=it++))
- {
- DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
- (ulong) tmp->thread_id));
- /* We skip slave threads on this first loop through. */
- if (tmp->slave_thread)
- continue;
-
- /* cannot use 'continue' inside DBUG_EXECUTE_IF()... */
- if (DBUG_EVALUATE_IF("only_kill_system_threads", !tmp->system_thread, 0))
- continue;
-
-#ifdef WITH_WSREP
- /* skip wsrep system threads as well */
- if (WSREP(tmp) && (tmp->wsrep_exec_mode==REPL_RECV || tmp->wsrep_applier))
- continue;
-#endif
- tmp->set_killed(KILL_SERVER_HARD);
- MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (tmp));
- mysql_mutex_lock(&tmp->LOCK_thd_kill);
- if (tmp->mysys_var)
- {
- tmp->mysys_var->abort=1;
- mysql_mutex_lock(&tmp->mysys_var->mutex);
- if (tmp->mysys_var->current_cond)
- {
- uint i;
- for (i=0; i < 2; i++)
- {
- int ret= mysql_mutex_trylock(tmp->mysys_var->current_mutex);
- mysql_cond_broadcast(tmp->mysys_var->current_cond);
- if (!ret)
- {
- /* Thread has surely got the signal, unlock and abort */
- mysql_mutex_unlock(tmp->mysys_var->current_mutex);
- break;
- }
- sleep(1);
- }
- }
- mysql_mutex_unlock(&tmp->mysys_var->mutex);
- }
- mysql_mutex_unlock(&tmp->LOCK_thd_kill);
- }
- mysql_mutex_unlock(&LOCK_thread_count); // For unlink from list
+ server_threads.iterate(kill_thread_phase_1);
Events::deinit();
slave_prepare_for_shutdown();
@@ -1767,85 +1745,40 @@ static void close_connections(void)
much smaller than even 2 seconds, this is only a safety fallback against
stuck threads so server shutdown is not held up forever.
*/
- DBUG_PRINT("info", ("thread_count: %d", thread_count));
+ DBUG_PRINT("info", ("thread_count: %u", uint32_t(thread_count)));
- for (int i= 0; *(volatile int32*) &thread_count && i < 1000; i++)
+ for (int i= 0; (thread_count - binlog_dump_thread_count) && i < 1000; i++)
my_sleep(20000);
- /*
- Force remaining threads to die by closing the connection to the client
- This will ensure that threads that are waiting for a command from the
- client on a blocking read call are aborted.
- */
-
- for (;;)
- {
- mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
- if (!(tmp=threads.get()))
- {
- mysql_mutex_unlock(&LOCK_thread_count);
- break;
- }
-#ifndef __bsdi__ // Bug in BSDI kernel
- if (tmp->vio_ok())
- {
- if (global_system_variables.log_warnings)
- sql_print_warning(ER_DEFAULT(ER_FORCING_CLOSE),my_progname,
- (ulong) tmp->thread_id,
- (tmp->main_security_ctx.user ?
- tmp->main_security_ctx.user : ""));
- /*
- close_connection() might need a valid current_thd
- for memory allocation tracking.
- */
- THD* save_thd= current_thd;
- set_current_thd(tmp);
- close_connection(tmp);
- set_current_thd(save_thd);
- }
-#endif
+ if (global_system_variables.log_warnings)
+ server_threads.iterate(warn_threads_active_after_phase_1);
#ifdef WITH_WSREP
- /*
- * WSREP_TODO:
- * this code block may turn out redundant. wsrep->disconnect()
- * should terminate slave threads gracefully, and we don't need
- * to signal them here.
- * The code here makes sure mysqld will not hang during shutdown
- * even if wsrep provider has problems in shutting down.
- */
- if (WSREP(tmp) && tmp->wsrep_exec_mode==REPL_RECV)
- {
- sql_print_information("closing wsrep system thread");
- tmp->set_killed(KILL_CONNECTION);
- MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (tmp));
- if (tmp->mysys_var)
- {
- tmp->mysys_var->abort=1;
- mysql_mutex_lock(&tmp->mysys_var->mutex);
- if (tmp->mysys_var->current_cond)
- {
- mysql_mutex_lock(tmp->mysys_var->current_mutex);
- mysql_cond_broadcast(tmp->mysys_var->current_cond);
- mysql_mutex_unlock(tmp->mysys_var->current_mutex);
- }
- mysql_mutex_unlock(&tmp->mysys_var->mutex);
- }
- }
-#endif
- DBUG_PRINT("quit",("Unlocking LOCK_thread_count"));
- mysql_mutex_unlock(&LOCK_thread_count);
+ if (wsrep_inited == 1)
+ {
+ wsrep_deinit(true);
}
- end_slave();
+#endif
/* All threads has now been aborted */
- DBUG_PRINT("quit",("Waiting for threads to die (count=%u)",thread_count));
- mysql_mutex_lock(&LOCK_thread_count);
- while (thread_count || service_thread_count)
+ DBUG_PRINT("quit", ("Waiting for threads to die (count=%u)",
+ uint32_t(thread_count)));
+
+ while (thread_count - binlog_dump_thread_count)
+ my_sleep(1000);
+
+ /* Kill phase 2 */
+ server_threads.iterate(kill_thread_phase_2);
+ for (uint64 i= 0; thread_count; i++)
{
- mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
- DBUG_PRINT("quit",("One thread died (count=%u)",thread_count));
+ /*
+ This time the warnings are emitted within the loop to provide a
+ dynamic view on the shutdown status through the errorlog.
+ */
+ if (global_system_variables.log_warnings > 2 && i % 60000 == 0)
+ server_threads.iterate(warn_threads_active_after_phase_2);
+ my_sleep(1000);
}
- mysql_mutex_unlock(&LOCK_thread_count);
+ /* End of kill phase 2 */
DBUG_PRINT("quit",("close_connections thread"));
DBUG_VOID_RETURN;
@@ -1887,190 +1820,6 @@ static void close_server_sock()
#endif /*EMBEDDED_LIBRARY*/
-/**
- Set shutdown user
-
- @note this function may be called by multiple threads concurrently, thus
- it performs safe update of shutdown_user (first thread wins).
-*/
-
-static volatile char *shutdown_user;
-static void set_shutdown_user(THD *thd)
-{
- char user_host_buff[MAX_USER_HOST_SIZE + 1];
- char *user, *expected_shutdown_user= 0;
-
- make_user_name(thd, user_host_buff);
-
- if ((user= my_strdup(user_host_buff, MYF(0))) &&
- !my_atomic_casptr((void **) &shutdown_user,
- (void **) &expected_shutdown_user, user))
- my_free(user);
-}
-
-
-void kill_mysql(THD *thd)
-{
- DBUG_ENTER("kill_mysql");
-
- if (thd)
- set_shutdown_user(thd);
-
-#if defined(SIGNALS_DONT_BREAK_READ) && !defined(EMBEDDED_LIBRARY)
- abort_loop=1; // Break connection loops
- close_server_sock(); // Force accept to wake up
-#endif
-
-#if defined(__WIN__)
-#if !defined(EMBEDDED_LIBRARY)
- {
- if (!SetEvent(hEventShutdown))
- {
- DBUG_PRINT("error",("Got error: %ld from SetEvent",GetLastError()));
- }
- /*
- or:
- HANDLE hEvent=OpenEvent(0, FALSE, "MySqlShutdown");
- SetEvent(hEventShutdown);
- CloseHandle(hEvent);
- */
- }
-#endif
-#elif defined(HAVE_PTHREAD_KILL)
- if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL))
- {
- DBUG_PRINT("error",("Got error %d from pthread_kill",errno)); /* purecov: inspected */
- }
-#elif !defined(SIGNALS_DONT_BREAK_READ)
- kill(current_pid, MYSQL_KILL_SIGNAL);
-#endif
- DBUG_PRINT("quit",("After pthread_kill"));
- shutdown_in_progress=1; // Safety if kill didn't work
-#ifdef SIGNALS_DONT_BREAK_READ
- if (!kill_in_progress)
- {
- pthread_t tmp;
- int error;
- abort_loop=1;
- if (unlikely((error= mysql_thread_create(0, /* Not instrumented */
- &tmp, &connection_attrib,
- kill_server_thread, (void*) 0))))
- sql_print_error("Can't create thread to kill server (errno= %d).",
- error);
- }
-#endif
- DBUG_VOID_RETURN;
-}
-
-/**
- Force server down. Kill all connections and threads and exit.
-
- @param sig_ptr Signal number that caused kill_server to be called.
-
- @note
- A signal number of 0 mean that the function was not called
- from a signal handler and there is thus no signal to block
- or stop, we just want to kill the server.
-*/
-
-#if !defined(__WIN__)
-static void *kill_server(void *sig_ptr)
-#define RETURN_FROM_KILL_SERVER return 0
-#else
-static void __cdecl kill_server(int sig_ptr)
-#define RETURN_FROM_KILL_SERVER return
-#endif
-{
- DBUG_ENTER("kill_server");
-#ifndef EMBEDDED_LIBRARY
- int sig=(int) (long) sig_ptr; // This is passed a int
- // if there is a signal during the kill in progress, ignore the other
- if (kill_in_progress) // Safety
- {
- DBUG_LEAVE;
- RETURN_FROM_KILL_SERVER;
- }
- kill_in_progress=TRUE;
- abort_loop=1; // This should be set
- if (sig != 0) // 0 is not a valid signal number
- my_sigset(sig, SIG_IGN); /* purify inspected */
- if (sig == MYSQL_KILL_SIGNAL || sig == 0)
- {
- char *user= (char *) my_atomic_loadptr((void**) &shutdown_user);
- sql_print_information(ER_DEFAULT(ER_NORMAL_SHUTDOWN), my_progname,
- user ? user : "unknown");
- if (user)
- my_free(user);
- }
- else
- sql_print_error(ER_DEFAULT(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */
-
-#ifdef HAVE_SMEM
- /*
- Send event to smem_event_connect_request for aborting
- */
- if (opt_enable_shared_memory)
- {
- if (!SetEvent(smem_event_connect_request))
- {
- DBUG_PRINT("error",
- ("Got error: %ld from SetEvent of smem_event_connect_request",
- GetLastError()));
- }
- }
-#endif
-
- /* Stop wsrep threads in case they are running. */
- if (wsrep_running_threads > 0)
- {
- wsrep_stop_replication(NULL);
- }
-
- close_connections();
-
-#ifdef WITH_WSREP
- if (wsrep_inited == 1)
- wsrep_deinit(true);
- wsrep_sst_auth_free();
-#endif /* WITH_WSREP */
-
- if (sig != MYSQL_KILL_SIGNAL &&
- sig != 0)
- unireg_abort(1); /* purecov: inspected */
- else
- unireg_end();
-
- /* purecov: begin deadcode */
- DBUG_LEAVE; // Must match DBUG_ENTER()
- my_thread_end();
- pthread_exit(0);
- /* purecov: end */
-
- RETURN_FROM_KILL_SERVER; // Avoid compiler warnings
-
-#else /* EMBEDDED_LIBRARY*/
-
- DBUG_LEAVE;
- RETURN_FROM_KILL_SERVER;
-
-#endif /* EMBEDDED_LIBRARY */
-}
-
-
-#if defined(USE_ONE_SIGNAL_HAND)
-pthread_handler_t kill_server_thread(void *arg __attribute__((unused)))
-{
- my_thread_init(); // Initialize new thread
- kill_server(0);
- /* purecov: begin deadcode */
- my_thread_end();
- pthread_exit(0);
- return 0;
- /* purecov: end */
-}
-#endif
-
-
extern "C" sig_handler print_signal_warning(int sig)
{
if (global_system_variables.log_warnings)
@@ -2086,42 +1835,6 @@ extern "C" sig_handler print_signal_warning(int sig)
}
#ifndef EMBEDDED_LIBRARY
-
-static void init_error_log_mutex()
-{
- mysql_mutex_init(key_LOCK_error_log, &LOCK_error_log, MY_MUTEX_INIT_FAST);
-}
-
-
-static void clean_up_error_log_mutex()
-{
- mysql_mutex_destroy(&LOCK_error_log);
-}
-
-
-/**
- cleanup all memory and end program nicely.
-
- If SIGNALS_DONT_BREAK_READ is defined, this function is called
- by the main thread. To get MySQL to shut down nicely in this case
- (Mac OS X) we have to call exit() instead if pthread_exit().
-
- @note
- This function never returns.
-*/
-void unireg_end(void)
-{
- clean_up(1);
- my_thread_end();
- sd_notify(0, "STATUS=MariaDB server is down");
-#if defined(SIGNALS_DONT_BREAK_READ)
- exit(0);
-#else
- pthread_exit(0); // Exit is in main thread
-#endif
-}
-
-
extern "C" void unireg_abort(int exit_code)
{
DBUG_ENTER("unireg_abort");
@@ -2129,31 +1842,36 @@ extern "C" void unireg_abort(int exit_code)
if (opt_help)
usage();
if (exit_code)
- sql_print_error("Aborting\n");
+ sql_print_error("Aborting");
/* Don't write more notes to the log to not hide error message */
disable_log_notes= 1;
#ifdef WITH_WSREP
- /* Check if wsrep class is used. If yes, then cleanup wsrep */
- if (wsrep)
+ // Note that we do not have thd here, thus can't use
+ // WSREP(thd)
+
+ if (WSREP_ON &&
+ Wsrep_server_state::is_inited() &&
+ Wsrep_server_state::instance().state() != wsrep::server_state::s_disconnected)
{
/*
This is an abort situation, we cannot expect to gracefully close all
wsrep threads here, we can only diconnect from service
*/
wsrep_close_client_connections(FALSE);
- shutdown_in_progress= 1;
- wsrep->disconnect(wsrep);
+ Wsrep_server_state::instance().disconnect();
WSREP_INFO("Service disconnected.");
wsrep_close_threads(NULL); /* this won't close all threads */
sleep(1); /* so give some time to exit for those which can */
WSREP_INFO("Some threads may fail to exit.");
+ }
- /* In bootstrap mode we deinitialize wsrep here. */
- if (opt_bootstrap && wsrep_inited)
- wsrep_deinit(true);
- wsrep_sst_auth_free();
+ if (WSREP_ON && wsrep_inited)
+ {
+ wsrep_deinit(true);
+ wsrep_deinit_server();
}
+ wsrep_sst_auth_free();
#endif // WITH_WSREP
clean_up(!opt_abort && (exit_code || !opt_bootstrap)); /* purecov: inspected */
@@ -2180,9 +1898,11 @@ static void mysqld_exit(int exit_code)
rpl_deinit_gtid_waiting();
rpl_deinit_gtid_slave_state();
wait_for_signal_thread_to_end();
+#ifdef WITH_WSREP
+ wsrep_deinit_server();
+#endif /* WITH_WSREP */
mysql_audit_finalize();
clean_up_mutexes();
- clean_up_error_log_mutex();
my_end((opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0));
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
shutdown_performance_schema(); // we do it as late as possible
@@ -2203,7 +1923,7 @@ static void mysqld_exit(int exit_code)
#endif /* !EMBEDDED_LIBRARY */
-void clean_up(bool print_message)
+static void clean_up(bool print_message)
{
DBUG_PRINT("exit",("clean_up"));
if (cleanup_done++)
@@ -2270,9 +1990,6 @@ void clean_up(bool print_message)
free_global_index_stats();
delete_dynamic(&all_options); // This should be empty
free_all_rpl_filters();
-#ifdef HAVE_REPLICATION
- end_slave_list();
-#endif
wsrep_thr_deinit();
my_uuid_end();
delete type_handler_data;
@@ -2302,16 +2019,6 @@ void clean_up(bool print_message)
sys_var_end();
free_charsets();
- /*
- Signal mysqld_main() that it can exit
- do the broadcast inside the lock to ensure that my_end() is not called
- during broadcast()
- */
- mysql_mutex_lock(&LOCK_thread_count);
- ready_to_exit=1;
- mysql_cond_broadcast(&COND_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
-
my_free(const_cast<char*>(log_bin_basename));
my_free(const_cast<char*>(log_bin_index));
#ifndef EMBEDDED_LIBRARY
@@ -2354,12 +2061,12 @@ static void wait_for_signal_thread_to_end()
static void clean_up_mutexes()
{
DBUG_ENTER("clean_up_mutexes");
+ server_threads.destroy();
mysql_rwlock_destroy(&LOCK_grant);
- mysql_mutex_destroy(&LOCK_thread_count);
mysql_mutex_destroy(&LOCK_thread_cache);
mysql_mutex_destroy(&LOCK_start_thread);
mysql_mutex_destroy(&LOCK_status);
- mysql_mutex_destroy(&LOCK_show_status);
+ mysql_rwlock_destroy(&LOCK_all_status_vars);
mysql_mutex_destroy(&LOCK_delayed_insert);
mysql_mutex_destroy(&LOCK_delayed_status);
mysql_mutex_destroy(&LOCK_delayed_create);
@@ -2373,7 +2080,7 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_global_index_stats);
#ifdef HAVE_OPENSSL
mysql_mutex_destroy(&LOCK_des_key_file);
-#ifdef HAVE_OPENSSL10
+#if defined(HAVE_OPENSSL10) && !defined(HAVE_WOLFSSL)
for (int i= 0; i < CRYPTO_num_locks(); ++i)
mysql_rwlock_destroy(&openssl_stdlocks[i].lock);
OPENSSL_free(openssl_stdlocks);
@@ -2383,6 +2090,7 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_rpl_status);
#endif /* HAVE_REPLICATION */
mysql_mutex_destroy(&LOCK_active_mi);
+ mysql_rwlock_destroy(&LOCK_ssl_refresh);
mysql_rwlock_destroy(&LOCK_sys_init_connect);
mysql_rwlock_destroy(&LOCK_sys_init_slave);
mysql_mutex_destroy(&LOCK_global_system_variables);
@@ -2390,7 +2098,6 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_short_uuid_generator);
mysql_mutex_destroy(&LOCK_prepared_stmt_count);
mysql_mutex_destroy(&LOCK_error_messages);
- mysql_cond_destroy(&COND_thread_count);
mysql_cond_destroy(&COND_thread_cache);
mysql_cond_destroy(&COND_start_thread);
mysql_cond_destroy(&COND_flush_thread_cache);
@@ -2400,6 +2107,9 @@ static void clean_up_mutexes()
mysql_cond_destroy(&COND_prepare_ordered);
mysql_mutex_destroy(&LOCK_after_binlog_sync);
mysql_mutex_destroy(&LOCK_commit_ordered);
+#ifndef EMBEDDED_LIBRARY
+ mysql_mutex_destroy(&LOCK_error_log);
+#endif
DBUG_VOID_RETURN;
}
@@ -2409,9 +2119,6 @@ static void clean_up_mutexes()
****************************************************************************/
#ifdef EMBEDDED_LIBRARY
-static void set_ports()
-{
-}
void close_connection(THD *thd, uint sql_errno)
{
}
@@ -2610,6 +2317,7 @@ static MYSQL_SOCKET activate_tcp_port(uint port)
}
else
{
+ server_socket_ai_family= a->ai_family;
sql_print_information("Server socket created on IP: '%s'.",
(const char *) ip_addr);
break;
@@ -2704,67 +2412,6 @@ static MYSQL_SOCKET activate_tcp_port(uint port)
DBUG_RETURN(ip_sock);
}
-#ifdef _WIN32
-/*
- Create a security descriptor for pipe.
- - Use low integrity level, so that it is possible to connect
- from any process.
- - Give current user read/write access to pipe.
- - Give Everyone read/write access to pipe minus FILE_CREATE_PIPE_INSTANCE
-*/
-static void init_pipe_security_descriptor()
-{
-#define SDDL_FMT "S:(ML;; NW;;; LW) D:(A;; 0x%08x;;; WD)(A;; FRFW;;; %s)"
-#define EVERYONE_PIPE_ACCESS_MASK \
- (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES | READ_CONTROL | \
- SYNCHRONIZE | FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
-
-#ifndef SECURITY_MAX_SID_STRING_CHARACTERS
-/* Old SDK does not have this constant */
-#define SECURITY_MAX_SID_STRING_CHARACTERS 187
-#endif
-
- /*
- Figure out SID of the user that runs the server, then create SDDL string
- for pipe permissions, and convert it to the security descriptor.
- */
- char sddl_string[sizeof(SDDL_FMT) + 8 + SECURITY_MAX_SID_STRING_CHARACTERS];
- struct
- {
- TOKEN_USER token_user;
- BYTE buffer[SECURITY_MAX_SID_SIZE];
- } token_buffer;
- HANDLE token;
- DWORD tmp;
-
- if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
- goto fail;
-
- if (!GetTokenInformation(token, TokenUser, &token_buffer,
- (DWORD) sizeof(token_buffer), &tmp))
- goto fail;
-
- CloseHandle(token);
-
- char *current_user_string_sid;
- if (!ConvertSidToStringSid(token_buffer.token_user.User.Sid,
- &current_user_string_sid))
- goto fail;
-
- snprintf(sddl_string, sizeof(sddl_string), SDDL_FMT,
- EVERYONE_PIPE_ACCESS_MASK, current_user_string_sid);
- LocalFree(current_user_string_sid);
-
- if (ConvertStringSecurityDescriptorToSecurityDescriptor(sddl_string,
- SDDL_REVISION_1, &saPipeSecurity.lpSecurityDescriptor, 0))
- return;
-
-fail:
- sql_perror("Can't start server : Initialize security descriptor");
- unireg_abort(1);
-}
-#endif
-
static void network_init(void)
{
#ifdef HAVE_SYS_UN_H
@@ -2797,41 +2444,16 @@ static void network_init(void)
extra_ip_sock= activate_tcp_port(mysqld_extra_port);
}
-#ifdef _WIN32
- /* create named pipe */
- if (mysqld_unix_port[0] && !opt_bootstrap &&
- opt_enable_named_pipe)
- {
-
- strxnmov(pipe_name, sizeof(pipe_name)-1, "\\\\.\\pipe\\",
- mysqld_unix_port, NullS);
- init_pipe_security_descriptor();
- saPipeSecurity.nLength = sizeof(SECURITY_ATTRIBUTES);
- saPipeSecurity.bInheritHandle = FALSE;
- if ((hPipe= CreateNamedPipe(pipe_name,
- PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE,
- PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
- PIPE_UNLIMITED_INSTANCES,
- (int) global_system_variables.net_buffer_length,
- (int) global_system_variables.net_buffer_length,
- NMPWAIT_USE_DEFAULT_WAIT,
- &saPipeSecurity)) == INVALID_HANDLE_VALUE)
- {
- sql_perror("Create named pipe failed");
- unireg_abort(1);
- }
- }
-#endif
-
#if defined(HAVE_SYS_UN_H)
/*
** Create the UNIX socket
*/
if (mysqld_unix_port[0] && !opt_bootstrap)
{
+ size_t port_len;
DBUG_PRINT("general",("UNIX Socket is %s",mysqld_unix_port));
- if (strlen(mysqld_unix_port) > (sizeof(UNIXaddr.sun_path) - 1))
+ if ((port_len= strlen(mysqld_unix_port)) > sizeof(UNIXaddr.sun_path) - 1)
{
sql_print_error("The socket file path is too long (> %u): %s",
(uint) sizeof(UNIXaddr.sun_path) - 1, mysqld_unix_port);
@@ -2849,14 +2471,26 @@ static void network_init(void)
bzero((char*) &UNIXaddr, sizeof(UNIXaddr));
UNIXaddr.sun_family = AF_UNIX;
strmov(UNIXaddr.sun_path, mysqld_unix_port);
- (void) unlink(mysqld_unix_port);
+#if defined(__linux__)
+ /* Abstract socket */
+ if (mysqld_unix_port[0] == '@')
+ {
+ UNIXaddr.sun_path[0]= '\0';
+ port_len+= offsetof(struct sockaddr_un, sun_path);
+ }
+ else
+#endif
+ {
+ (void) unlink(mysqld_unix_port);
+ port_len= sizeof(UNIXaddr);
+ }
arg= 1;
(void) mysql_socket_setsockopt(unix_sock,SOL_SOCKET,SO_REUSEADDR,
(char*)&arg, sizeof(arg));
umask(0);
if (mysql_socket_bind(unix_sock,
reinterpret_cast<struct sockaddr *>(&UNIXaddr),
- sizeof(UNIXaddr)) < 0)
+ port_len) < 0)
{
sql_perror("Can't start server : Bind on unix socket"); /* purecov: tested */
sql_print_error("Do you already have another mysqld server running on socket: %s ?",mysqld_unix_port);
@@ -2891,13 +2525,19 @@ static void network_init(void)
void close_connection(THD *thd, uint sql_errno)
{
+ int lvl= (thd->main_security_ctx.user ? 3 : 1);
DBUG_ENTER("close_connection");
if (sql_errno)
+ {
net_send_error(thd, sql_errno, ER_DEFAULT(sql_errno), NULL);
-
- thd->print_aborted_warning(3, sql_errno ? ER_DEFAULT(sql_errno)
- : "CLOSE_CONNECTION");
+ thd->print_aborted_warning(lvl, ER_DEFAULT(sql_errno));
+ }
+ else
+ thd->print_aborted_warning(lvl, (thd->main_security_ctx.user ?
+ "This connection closed normally" :
+ "This connection closed normally without"
+ " authentication"));
thd->disconnect();
@@ -2910,7 +2550,6 @@ void close_connection(THD *thd, uint sql_errno)
mysql_audit_notify_connection_disconnect(thd, sql_errno);
DBUG_VOID_RETURN;
}
-#endif /* EMBEDDED_LIBRARY */
/** Called when mysqld is aborted with ^C */
@@ -2918,11 +2557,12 @@ void close_connection(THD *thd, uint sql_errno)
extern "C" sig_handler end_mysqld_signal(int sig __attribute__((unused)))
{
DBUG_ENTER("end_mysqld_signal");
- /* Don't call kill_mysql() if signal thread is not running */
+ /* Don't kill if signal thread is not running */
if (signal_thread_in_use)
- kill_mysql(); // Take down mysqld nicely
+ break_connect_loop(); // Take down mysqld nicely
DBUG_VOID_RETURN; /* purecov: deadcode */
}
+#endif /* EMBEDDED_LIBRARY */
/*
Decrease number of connections
@@ -2940,30 +2580,6 @@ void dec_connection_count(scheduler_functions *scheduler)
/*
- Send a signal to unblock close_conneciton() if there is no more
- threads running with a THD attached
-
- It's safe to check for thread_count and service_thread_count outside
- of a mutex as we are only interested to see if they where decremented
- to 0 by a previous unlink_thd() call.
-
- We should only signal COND_thread_count if both variables are 0,
- false positives are ok.
-*/
-
-void signal_thd_deleted()
-{
- if (!thread_count && !service_thread_count)
- {
- /* Signal close_connections() that all THD's are freed */
- mysql_mutex_lock(&LOCK_thread_count);
- mysql_cond_broadcast(&COND_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
- }
-}
-
-
-/*
Unlink thd from global list of available connections
SYNOPSIS
@@ -2978,14 +2594,16 @@ void unlink_thd(THD *thd)
thd->cleanup();
thd->add_status_to_global();
- unlink_not_visible_thd(thd);
+ server_threads.erase(thd);
+#ifdef WITH_WSREP
/*
Do not decrement when its wsrep system thread. wsrep_applier is set for
applier as well as rollbacker threads.
*/
- if (IF_WSREP(!thd->wsrep_applier, 1))
- dec_connection_count(thd->scheduler);
+ if (!thd->wsrep_applier)
+#endif /* WITH_WSREP */
+ dec_connection_count(thd->scheduler);
thd->free_connection();
@@ -3087,7 +2705,7 @@ static bool cache_thread(THD *thd)
thd->thr_create_utime= microsecond_interval_timer();
thd->start_utime= thd->thr_create_utime;
- add_to_active_threads(thd);
+ server_threads.insert(thd);
DBUG_RETURN(1);
}
}
@@ -3189,7 +2807,7 @@ static BOOL WINAPI console_event_handler( DWORD type )
*/
#ifndef EMBEDDED_LIBRARY
if(hEventShutdown)
- kill_mysql();
+ break_connect_loop();
else
#endif
sql_print_warning("CTRL-C ignored during startup");
@@ -3527,6 +3145,18 @@ static void start_signal_handler(void)
}
+#if defined(USE_ONE_SIGNAL_HAND)
+pthread_handler_t kill_server_thread(void *arg __attribute__((unused)))
+{
+ my_thread_init(); // Initialize new thread
+ break_connect_loop();
+ my_thread_end();
+ pthread_exit(0);
+ return 0;
+}
+#endif
+
+
/** This threads handles all signals and alarms. */
/* ARGSUSED */
pthread_handler_t signal_hand(void *arg __attribute__((unused)))
@@ -3580,14 +3210,10 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
(void) pthread_sigmask(SIG_BLOCK,&set,NULL);
for (;;)
{
- int error; // Used when debugging
- if (shutdown_in_progress && !abort_loop)
- {
- sig= SIGTERM;
- error=0;
- }
- else
- while ((error=my_sigwait(&set,&sig)) == EINTR) ;
+ int error;
+ int origin;
+
+ while ((error= my_sigwait(&set, &sig, &origin)) == EINTR) /* no-op */;
if (cleanup_done)
{
DBUG_PRINT("quit",("signal_handler: calling my_thread_end()"));
@@ -3610,7 +3236,6 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
DBUG_PRINT("info",("Got signal: %d abort_loop: %d",sig,abort_loop));
if (!abort_loop)
{
- abort_loop=1; // mark abort for threads
/* Delete the instrumentation for the signal thread */
PSI_CALL_delete_current_thread();
#ifdef USE_ONE_SIGNAL_HAND
@@ -3622,12 +3247,19 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
sql_print_error("Can't create thread to kill server (errno= %d)",
error);
#else
- kill_server((void*) sig); // MIT THREAD has a alarm thread
+ my_sigset(sig, SIG_IGN);
+ break_connect_loop(); // MIT THREAD has a alarm thread
#endif
}
break;
case SIGHUP:
+#if defined(SI_KERNEL)
+ if (!abort_loop && origin != SI_KERNEL)
+#elif defined(SI_USER)
+ if (!abort_loop && origin <= SI_USER)
+#else
if (!abort_loop)
+#endif
{
int not_used;
mysql_print_status(); // Print some debug info
@@ -3636,21 +3268,14 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
REFRESH_GRANT |
REFRESH_THREADS | REFRESH_HOSTS),
(TABLE_LIST*) 0, &not_used); // Flush logs
- }
- /* reenable logs after the options were reloaded */
- if (log_output_options & LOG_NONE)
- {
- logger.set_handlers(LOG_FILE,
- global_system_variables.sql_log_slow ?
- LOG_TABLE : LOG_NONE,
- opt_log ? LOG_TABLE : LOG_NONE);
- }
- else
- {
- logger.set_handlers(LOG_FILE,
- global_system_variables.sql_log_slow ?
- log_output_options : LOG_NONE,
- opt_log ? log_output_options : LOG_NONE);
+
+ /* reenable logs after the options were reloaded */
+ ulonglong fixed_log_output_options=
+ log_output_options & LOG_NONE ? LOG_TABLE : log_output_options;
+
+ logger.set_handlers(LOG_FILE, global_system_variables.sql_log_slow
+ ? fixed_log_output_options : LOG_NONE,
+ opt_log ? fixed_log_output_options : LOG_NONE);
}
break;
#ifdef USE_ONE_SIGNAL_HAND
@@ -3684,7 +3309,7 @@ extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
void my_message_sql(uint error, const char *str, myf MyFlags)
{
- THD *thd= current_thd;
+ THD *thd= MyFlags & ME_ERROR_LOG_ONLY ? NULL : current_thd;
Sql_condition::enum_warning_level level;
sql_print_message_func func;
DBUG_ENTER("my_message_sql");
@@ -3693,13 +3318,15 @@ void my_message_sql(uint error, const char *str, myf MyFlags)
DBUG_ASSERT(str != NULL);
DBUG_ASSERT(error != 0);
+ DBUG_ASSERT((MyFlags & ~(ME_BELL | ME_ERROR_LOG | ME_ERROR_LOG_ONLY |
+ ME_NOTE | ME_WARNING | ME_FATAL)) == 0);
- if (MyFlags & ME_JUST_INFO)
+ if (MyFlags & ME_NOTE)
{
level= Sql_condition::WARN_LEVEL_NOTE;
func= sql_print_information;
}
- else if (MyFlags & ME_JUST_WARNING)
+ else if (MyFlags & ME_WARNING)
{
level= Sql_condition::WARN_LEVEL_WARN;
func= sql_print_warning;
@@ -3712,7 +3339,7 @@ void my_message_sql(uint error, const char *str, myf MyFlags)
if (likely(thd))
{
- if (unlikely(MyFlags & ME_FATALERROR))
+ if (unlikely(MyFlags & ME_FATAL))
thd->is_fatal_error= 1;
(void) thd->raise_condition(error, NULL, level, str);
}
@@ -3722,7 +3349,7 @@ void my_message_sql(uint error, const char *str, myf MyFlags)
/* When simulating OOM, skip writing to error log to avoid mtr errors */
DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_VOID_RETURN;);
- if (unlikely(!thd) || thd->log_all_errors || (MyFlags & ME_NOREFRESH))
+ if (unlikely(!thd) || thd->log_all_errors || (MyFlags & ME_ERROR_LOG))
(*func)("%s: %s", my_progname_short, str); /* purecov: inspected */
DBUG_VOID_RETURN;
}
@@ -3736,23 +3363,6 @@ void *my_str_malloc_mysqld(size_t size)
}
-#ifdef __WIN__
-
-pthread_handler_t handle_shutdown(void *arg)
-{
- MSG msg;
- my_thread_init();
-
- /* this call should create the message queue for this thread */
- PeekMessage(&msg, NULL, 1, 65534,PM_NOREMOVE);
-#if !defined(EMBEDDED_LIBRARY)
- if (WaitForSingleObject(hEventShutdown,INFINITE)==WAIT_OBJECT_0)
-#endif /* EMBEDDED_LIBRARY */
- kill_server(MYSQL_KILL_SIGNAL);
- return 0;
-}
-#endif
-
#include <mysqld_default_groups.h>
#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
@@ -3860,6 +3470,8 @@ SHOW_VAR com_status_vars[]= {
{"alter_user", STMT_STATUS(SQLCOM_ALTER_USER)},
{"analyze", STMT_STATUS(SQLCOM_ANALYZE)},
{"assign_to_keycache", STMT_STATUS(SQLCOM_ASSIGN_TO_KEYCACHE)},
+ {"backup", STMT_STATUS(SQLCOM_BACKUP)},
+ {"backup_lock", STMT_STATUS(SQLCOM_BACKUP_LOCK)},
{"begin", STMT_STATUS(SQLCOM_BEGIN)},
{"binlog", STMT_STATUS(SQLCOM_BINLOG_BASE64_EVENT)},
{"call_procedure", STMT_STATUS(SQLCOM_CALL)},
@@ -4155,8 +3767,27 @@ static void my_malloc_size_cb_func(long long size, my_bool is_thread_specific)
else
update_global_memory_status(size);
}
+
+int json_escape_string(const char *str,const char *str_end,
+ char *json, char *json_end)
+{
+ return json_escape(system_charset_info,
+ (const uchar *) str, (const uchar *) str_end,
+ &my_charset_utf8mb4_bin,
+ (uchar *) json, (uchar *) json_end);
+}
+
+
+int json_unescape_json(const char *json_str, const char *json_end,
+ char *res, char *res_end)
+{
+ return json_unescape(&my_charset_utf8mb4_bin,
+ (const uchar *) json_str, (const uchar *) json_end,
+ system_charset_info, (uchar *) res, (uchar *) res_end);
}
+} /*extern "C"*/
+
/**
Create a replication file name or base for file names.
@@ -4853,11 +4484,10 @@ static int init_common_variables()
static int init_thread_environment()
{
DBUG_ENTER("init_thread_environment");
- mysql_mutex_init(key_LOCK_thread_count, &LOCK_thread_count, MY_MUTEX_INIT_FAST);
+ server_threads.init();
mysql_mutex_init(key_LOCK_thread_cache, &LOCK_thread_cache, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_start_thread, &LOCK_start_thread, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_status, &LOCK_status, MY_MUTEX_INIT_FAST);
- mysql_mutex_init(key_LOCK_show_status, &LOCK_show_status, MY_MUTEX_INIT_SLOW);
mysql_mutex_init(key_LOCK_delayed_insert,
&LOCK_delayed_insert, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_delayed_status,
@@ -4870,7 +4500,6 @@ static int init_thread_environment()
mysql_mutex_init(key_LOCK_global_system_variables,
&LOCK_global_system_variables, MY_MUTEX_INIT_FAST);
mysql_mutex_record_order(&LOCK_active_mi, &LOCK_global_system_variables);
- mysql_mutex_record_order(&LOCK_status, &LOCK_thread_count);
mysql_prlock_init(key_rwlock_LOCK_system_variables_hash,
&LOCK_system_variables_hash);
mysql_mutex_init(key_LOCK_prepared_stmt_count,
@@ -4901,7 +4530,7 @@ static int init_thread_environment()
#ifdef HAVE_OPENSSL
mysql_mutex_init(key_LOCK_des_key_file,
&LOCK_des_key_file, MY_MUTEX_INIT_FAST);
-#ifdef HAVE_OPENSSL10
+#if defined(HAVE_OPENSSL10) && !defined(HAVE_WOLFSSL)
openssl_stdlocks= (openssl_lock_t*) OPENSSL_malloc(CRYPTO_num_locks() *
sizeof(openssl_lock_t));
for (int i= 0; i < CRYPTO_num_locks(); ++i)
@@ -4914,8 +4543,9 @@ static int init_thread_environment()
#endif /* HAVE_OPENSSL */
mysql_rwlock_init(key_rwlock_LOCK_sys_init_connect, &LOCK_sys_init_connect);
mysql_rwlock_init(key_rwlock_LOCK_sys_init_slave, &LOCK_sys_init_slave);
+ mysql_rwlock_init(key_rwlock_LOCK_ssl_refresh, &LOCK_ssl_refresh);
mysql_rwlock_init(key_rwlock_LOCK_grant, &LOCK_grant);
- mysql_cond_init(key_COND_thread_count, &COND_thread_count, NULL);
+ mysql_rwlock_init(key_rwlock_LOCK_all_status_vars, &LOCK_all_status_vars);
mysql_cond_init(key_COND_thread_cache, &COND_thread_cache, NULL);
mysql_cond_init(key_COND_start_thread, &COND_start_thread, NULL);
mysql_cond_init(key_COND_flush_thread_cache, &COND_flush_thread_cache, NULL);
@@ -4945,7 +4575,7 @@ static int init_thread_environment()
}
-#ifdef HAVE_OPENSSL10
+#if defined(HAVE_OPENSSL10) && !defined(HAVE_WOLFSSL)
static openssl_lock_t *openssl_dynlock_create(const char *file, int line)
{
openssl_lock_t *lock= new openssl_lock_t;
@@ -5007,6 +4637,60 @@ static void openssl_lock(int mode, openssl_lock_t *lock, const char *file,
}
#endif /* HAVE_OPENSSL10 */
+
+struct SSL_ACCEPTOR_STATS
+{
+ long accept;
+ long accept_good;
+ long cache_size;
+ long verify_mode;
+ long verify_depth;
+ long zero;
+ const char *session_cache_mode;
+
+ SSL_ACCEPTOR_STATS():
+ accept(),accept_good(),cache_size(),verify_mode(),verify_depth(),zero(),
+ session_cache_mode("NONE")
+ {
+ }
+
+ void init()
+ {
+ DBUG_ASSERT(ssl_acceptor_fd !=0 && ssl_acceptor_fd->ssl_context != 0);
+ SSL_CTX *ctx= ssl_acceptor_fd->ssl_context;
+ accept= 0;
+ accept_good= 0;
+ verify_mode= SSL_CTX_get_verify_mode(ctx);
+ verify_depth= SSL_CTX_get_verify_depth(ctx);
+ cache_size= SSL_CTX_sess_get_cache_size(ctx);
+ switch (SSL_CTX_get_session_cache_mode(ctx))
+ {
+ case SSL_SESS_CACHE_OFF:
+ session_cache_mode= "OFF"; break;
+ case SSL_SESS_CACHE_CLIENT:
+ session_cache_mode= "CLIENT"; break;
+ case SSL_SESS_CACHE_SERVER:
+ session_cache_mode= "SERVER"; break;
+ case SSL_SESS_CACHE_BOTH:
+ session_cache_mode= "BOTH"; break;
+ case SSL_SESS_CACHE_NO_AUTO_CLEAR:
+ session_cache_mode= "NO_AUTO_CLEAR"; break;
+ case SSL_SESS_CACHE_NO_INTERNAL_LOOKUP:
+ session_cache_mode= "NO_INTERNAL_LOOKUP"; break;
+ default:
+ session_cache_mode= "Unknown"; break;
+ }
+ }
+};
+
+static SSL_ACCEPTOR_STATS ssl_acceptor_stats;
+void ssl_acceptor_stats_update(int sslaccept_ret)
+{
+ statistic_increment(ssl_acceptor_stats.accept, &LOCK_status);
+ if (!sslaccept_ret)
+ statistic_increment(ssl_acceptor_stats.accept_good,&LOCK_status);
+}
+
static void init_ssl()
{
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
@@ -5018,7 +4702,8 @@ static void init_ssl()
ssl_acceptor_fd= new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert,
opt_ssl_ca, opt_ssl_capath,
opt_ssl_cipher, &error,
- opt_ssl_crl, opt_ssl_crlpath);
+ opt_ssl_crl, opt_ssl_crlpath,
+ tls_version);
DBUG_PRINT("info",("ssl_acceptor_fd: %p", ssl_acceptor_fd));
if (!ssl_acceptor_fd)
{
@@ -5027,11 +4712,18 @@ static void init_ssl()
opt_use_ssl = 0;
have_ssl= SHOW_OPTION_DISABLED;
}
+ else
+ ssl_acceptor_stats.init();
+
if (global_system_variables.log_warnings > 0)
{
ulong err;
while ((err= ERR_get_error()))
- sql_print_warning("SSL error: %s", ERR_error_string(err, NULL));
+ {
+ char buf[256];
+ ERR_error_string_n(err, buf, sizeof(buf));
+ sql_print_warning("SSL error: %s",buf);
+ }
}
else
ERR_remove_state(0);
@@ -5045,6 +4737,33 @@ static void init_ssl()
#endif /* HAVE_OPENSSL && ! EMBEDDED_LIBRARY */
}
+/* Reinitialize SSL (FLUSH SSL) */
+int reinit_ssl()
+{
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
+ if (!opt_use_ssl)
+ return 0;
+
+ enum enum_ssl_init_error error = SSL_INITERR_NOERROR;
+ st_VioSSLFd *new_fd = new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert,
+ opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher, &error, opt_ssl_crl,
+ opt_ssl_crlpath, tls_version);
+
+ if (!new_fd)
+ {
+ my_printf_error(ER_UNKNOWN_ERROR, "Failed to refresh SSL, error: %s", MYF(0),
+ sslGetErrString(error));
+ ERR_clear_error();
+ return 1;
+ }
+ mysql_rwlock_wrlock(&LOCK_ssl_refresh);
+ free_vio_ssl_acceptor_fd(ssl_acceptor_fd);
+ ssl_acceptor_fd= new_fd;
+ ssl_acceptor_stats.init();
+ mysql_rwlock_unlock(&LOCK_ssl_refresh);
+#endif
+ return 0;
+}
static void end_ssl()
{
@@ -5121,7 +4840,6 @@ static int init_default_storage_engine_impl(const char *opt_name,
return 0;
}
-
static int
init_gtid_pos_auto_engines(void)
{
@@ -5148,7 +4866,6 @@ init_gtid_pos_auto_engines(void)
return 0;
}
-
static int init_server_components()
{
DBUG_ENTER("init_server_components");
@@ -5176,6 +4893,7 @@ static int init_server_components()
my_rnd_init(&sql_rand,(ulong) server_start_time,(ulong) server_start_time/2);
setup_fpu();
init_thr_lock();
+ backup_init();
#ifndef EMBEDDED_LIBRARY
if (init_thr_timer(thread_scheduler->max_threads + extra_max_connections))
@@ -5186,9 +4904,6 @@ static int init_server_components()
#endif
my_uuid_init((ulong) (my_rnd(&sql_rand))*12345,12345);
-#ifdef HAVE_REPLICATION
- init_slave_list();
-#endif
wt_init();
/* Setup logs */
@@ -5373,7 +5088,10 @@ static int init_server_components()
wsrep_thr_init();
#endif
- if (WSREP_ON && !wsrep_recovery && !opt_abort) /* WSREP BEFORE SE */
+#ifdef WITH_WSREP
+ if (wsrep_init_server()) unireg_abort(1);
+
+ if (WSREP_ON && !wsrep_recovery && !opt_abort)
{
if (opt_bootstrap) // bootsrap option given - disable wsrep functionality
{
@@ -5406,6 +5124,7 @@ static int init_server_components()
}
}
}
+#endif /* WITH_WSREP */
if (opt_bin_log)
{
@@ -5711,100 +5430,17 @@ static int init_server_components()
#ifndef EMBEDDED_LIBRARY
-
-static void create_shutdown_thread()
+#ifdef _WIN32
+static void create_shutdown_event()
{
-#ifdef __WIN__
hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name);
- pthread_t hThread;
- int error;
- if (unlikely((error= mysql_thread_create(key_thread_handle_shutdown,
- &hThread, &connection_attrib,
- handle_shutdown, 0))))
- sql_print_warning("Can't create thread to handle shutdown requests"
- " (errno= %d)", error);
-
// On "Stop Service" we have to do regular shutdown
Service.SetShutdownEvent(hEventShutdown);
-#endif /* __WIN__ */
}
-
-#endif /* EMBEDDED_LIBRARY */
-
-#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
-static void handle_connections_methods()
-{
- pthread_t hThread;
- int error;
- DBUG_ENTER("handle_connections_methods");
- if (hPipe == INVALID_HANDLE_VALUE &&
- (!have_tcpip || opt_disable_networking) &&
- !opt_enable_shared_memory)
- {
- sql_print_error("TCP/IP, --shared-memory, or --named-pipe should be configured on NT OS");
- unireg_abort(1); // Will not return
- }
-
- mysql_mutex_lock(&LOCK_start_thread);
- mysql_cond_init(key_COND_handler_count, &COND_handler_count, NULL);
- handler_count=0;
- if (hPipe != INVALID_HANDLE_VALUE)
- {
- handler_count++;
- if ((error= mysql_thread_create(key_thread_handle_con_namedpipes,
- &hThread, &connection_attrib,
- handle_connections_namedpipes, 0)))
- {
- sql_print_warning("Can't create thread to handle named pipes"
- " (errno= %d)", error);
- handler_count--;
- }
- }
- if (have_tcpip && !opt_disable_networking)
- {
- handler_count++;
- if ((error= mysql_thread_create(key_thread_handle_con_sockets,
- &hThread, &connection_attrib,
- handle_connections_sockets_thread, 0)))
- {
- sql_print_warning("Can't create thread to handle TCP/IP",
- " (errno= %d)", error);
- handler_count--;
- }
- }
-#ifdef HAVE_SMEM
- if (opt_enable_shared_memory)
- {
- handler_count++;
- if ((error= mysql_thread_create(key_thread_handle_con_sharedmem,
- &hThread, &connection_attrib,
- handle_connections_shared_memory, 0)))
- {
- sql_print_warning("Can't create thread to handle shared memory",
- " (errno= %d)", error);
- handler_count--;
- }
- }
+#else /*_WIN32*/
+#define create_shutdown_event()
#endif
-
- while (handler_count > 0)
- mysql_cond_wait(&COND_handler_count, &LOCK_start_thread);
- mysql_mutex_unlock(&LOCK_start_thread);
- DBUG_VOID_RETURN;
-}
-
-void decrement_handler_count()
-{
- mysql_mutex_lock(&LOCK_start_thread);
- if (--handler_count == 0)
- mysql_cond_signal(&COND_handler_count);
- mysql_mutex_unlock(&LOCK_start_thread);
- my_thread_end();
-}
-#else
-#define decrement_handler_count()
-#endif /* defined(_WIN32) || defined(HAVE_SMEM) */
-
+#endif /* EMBEDDED_LIBRARY */
#ifndef EMBEDDED_LIBRARY
@@ -5984,7 +5620,7 @@ int mysqld_main(int argc, char **argv)
}
#endif /* HAVE_PSI_INTERFACE */
- init_error_log_mutex();
+ mysql_mutex_init(key_LOCK_error_log, &LOCK_error_log, MY_MUTEX_INIT_FAST);
/* Initialize audit interface globals. Audit plugins are inited later. */
mysql_audit_initialize();
@@ -6072,8 +5708,8 @@ int mysqld_main(int argc, char **argv)
}
#ifdef WITH_WSREP
- if (WSREP_ON && wsrep_check_opts())
- global_system_variables.wsrep_on= 0;
+ wsrep_set_wsrep_on();
+ if (WSREP_ON && wsrep_check_opts()) unireg_abort(1);
#endif
/*
@@ -6090,17 +5726,11 @@ int mysqld_main(int argc, char **argv)
init_ssl();
network_init();
-#ifdef __WIN__
+#ifdef _WIN32
if (!opt_console)
{
FreeConsole(); // Remove window
}
-
- if (fileno(stdin) >= 0)
- {
- /* Disable CRLF translation (MDEV-9409). */
- _setmode(fileno(stdin), O_BINARY);
- }
#endif
#ifdef WITH_WSREP
@@ -6124,18 +5754,7 @@ int mysqld_main(int argc, char **argv)
if (mysql_rm_tmp_tables() || acl_init(opt_noacl) ||
my_tz_init((THD *)0, default_tz_name, opt_bootstrap))
- {
- abort_loop=1;
- select_thread_in_use=0;
-
- (void) pthread_kill(signal_thread, MYSQL_KILL_SIGNAL);
-
- delete_pid_file(MYF(MY_WME));
-
- if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET)
- unlink(mysqld_unix_port);
- exit(1);
- }
+ unireg_abort(1);
if (!opt_noacl)
(void) grant_init();
@@ -6174,36 +5793,27 @@ int mysqld_main(int argc, char **argv)
}
else
{
- wsrep_SE_initialized();
-
- if (wsrep_before_SE())
- {
- /*! in case of no SST wsrep waits in view handler callback */
- wsrep_SE_init_grab();
- wsrep_SE_init_done();
- /*! in case of SST wsrep waits for wsrep->sst_received */
- if (wsrep_sst_continue())
- {
- WSREP_ERROR("Failed to signal the wsrep provider to continue.");
- }
- }
- else
+ wsrep_init_globals();
+ if (!wsrep_before_SE())
{
wsrep_init_startup (false);
}
- WSREP_DEBUG("Startup creating %ld applier threads running %lu",
- wsrep_slave_threads - 1, wsrep_running_applier_threads);
- wsrep_create_appliers(wsrep_slave_threads - 1);
+ if (wsrep_cluster_address_exists())
+ {
+ WSREP_DEBUG("Startup creating %ld applier threads running %lu",
+ wsrep_slave_threads - 1, wsrep_running_applier_threads);
+ wsrep_create_appliers(wsrep_slave_threads - 1);
+ }
}
}
if (opt_bootstrap)
{
select_thread_in_use= 0; // Allow 'kill' to work
- bootstrap(mysql_stdin);
- if (!kill_in_progress)
- unireg_abort(bootstrap_error ? 1 : 0);
+ int bootstrap_error= bootstrap(mysql_stdin);
+ if (!abort_loop)
+ unireg_abort(bootstrap_error);
else
{
sleep(2); // Wait for kill
@@ -6211,7 +5821,7 @@ int mysqld_main(int argc, char **argv)
}
}
- create_shutdown_thread();
+ create_shutdown_event();
start_handle_manager();
/* Copy default global rpl_filter to global_rpl_filter */
@@ -6272,7 +5882,7 @@ int mysqld_main(int argc, char **argv)
/* Signal threads waiting for server to be started */
mysql_mutex_lock(&LOCK_server_started);
mysqld_server_started= 1;
- mysql_cond_signal(&COND_server_started);
+ mysql_cond_broadcast(&COND_server_started);
mysql_mutex_unlock(&LOCK_server_started);
MYSQL_SET_STAGE(0 ,__FILE__, __LINE__);
@@ -6280,22 +5890,40 @@ int mysqld_main(int argc, char **argv)
/* Memory used when everything is setup */
start_memory_used= global_status_var.global_memory_used;
-#if defined(_WIN32) || defined(HAVE_SMEM)
- handle_connections_methods();
+#ifdef _WIN32
+ handle_connections_win();
#else
handle_connections_sockets();
-#endif /* _WIN32 || HAVE_SMEM */
-
- /* (void) pthread_attr_destroy(&connection_attrib); */
- DBUG_PRINT("quit",("Exiting main thread"));
-
-#ifndef __WIN__
mysql_mutex_lock(&LOCK_start_thread);
- select_thread_in_use=0; // For close_connections
+ select_thread_in_use=0;
mysql_cond_broadcast(&COND_start_thread);
mysql_mutex_unlock(&LOCK_start_thread);
-#endif /* __WIN__ */
+#endif /* _WIN32 */
+
+ /* Shutdown requested */
+ char *user= shutdown_user.load(std::memory_order_relaxed);
+ sql_print_information(ER_DEFAULT(ER_NORMAL_SHUTDOWN), my_progname,
+ user ? user : "unknown");
+ if (user)
+ my_free(user);
+
+#ifdef WITH_WSREP
+ /* Stop wsrep threads in case they are running. */
+ if (wsrep_running_threads > 0)
+ {
+ wsrep_shutdown_replication();
+ }
+#endif
+
+ close_connections();
+
+ clean_up(1);
+ sd_notify(0, "STATUS=MariaDB server is down");
+
+ /* (void) pthread_attr_destroy(&connection_attrib); */
+
+ DBUG_PRINT("quit",("Exiting main thread"));
/*
Disable the main thread instrumentation,
@@ -6303,12 +5931,6 @@ int mysqld_main(int argc, char **argv)
*/
PSI_CALL_delete_current_thread();
- /* Wait until cleanup is done */
- mysql_mutex_lock(&LOCK_thread_count);
- while (!ready_to_exit)
- mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
-
#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
if (start_mode)
Service.Stop();
@@ -6319,7 +5941,7 @@ int mysqld_main(int argc, char **argv)
CloseHandle(hEventShutdown);
}
#endif
-#if (defined(HAVE_OPENSSL) && !defined(HAVE_YASSL)) && !defined(EMBEDDED_LIBRARY)
+#if (defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY))
ERR_remove_state(0);
#endif
mysqld_exit(0);
@@ -6541,54 +6163,6 @@ int mysqld_main(int argc, char **argv)
#endif
-/**
- Execute all commands from a file. Used by the mysql_install_db script to
- create MySQL privilege tables without having to start a full MySQL server
- and by read_init_file() if mysqld was started with the option --init-file.
-*/
-
-static void bootstrap(MYSQL_FILE *file)
-{
- DBUG_ENTER("bootstrap");
-
- THD *thd= new THD(next_thread_id());
-#ifdef WITH_WSREP
- thd->variables.wsrep_on= 0;
-#endif
- thd->bootstrap=1;
- my_net_init(&thd->net,(st_vio*) 0, thd, MYF(0));
- thd->max_client_packet_length= thd->net.max_packet;
- thd->security_ctx->master_access= ~(ulong)0;
- in_bootstrap= TRUE;
-
- bootstrap_file=file;
-#ifndef EMBEDDED_LIBRARY // TODO: Enable this
- int error;
- if ((error= mysql_thread_create(key_thread_bootstrap,
- &thd->real_id, &connection_attrib,
- handle_bootstrap,
- (void*) thd)))
- {
- sql_print_warning("Can't create thread to handle bootstrap (errno= %d)",
- error);
- bootstrap_error=-1;
- delete thd;
- DBUG_VOID_RETURN;
- }
- /* Wait for thread to die */
- mysql_mutex_lock(&LOCK_thread_count);
- while (in_bootstrap)
- mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
-#else
- thd->mysql= 0;
- do_handle_bootstrap(thd);
-#endif
-
- DBUG_VOID_RETURN;
-}
-
-
static bool read_init_file(char *file_name)
{
MYSQL_FILE *file;
@@ -6693,7 +6267,7 @@ void create_thread_to_handle_connection(CONNECT *connect)
@param[in,out] thd Thread handle of future thread.
*/
-static void create_new_thread(CONNECT *connect)
+void create_new_thread(CONNECT *connect)
{
DBUG_ENTER("create_new_thread");
@@ -6738,29 +6312,101 @@ static void create_new_thread(CONNECT *connect)
#endif /* EMBEDDED_LIBRARY */
-#ifdef SIGNALS_DONT_BREAK_READ
-inline void kill_broken_server()
+ /* Handle new connections and spawn new process to handle them */
+
+#ifndef EMBEDDED_LIBRARY
+
+void handle_accepted_socket(MYSQL_SOCKET new_sock, MYSQL_SOCKET sock)
{
- /* hack to get around signals ignored in syscalls for problem OS's */
- if (mysql_socket_getfd(unix_sock) == INVALID_SOCKET ||
- (!opt_disable_networking &&
- mysql_socket_getfd(base_ip_sock) == INVALID_SOCKET))
+ CONNECT *connect;
+ bool is_unix_sock;
+
+#ifdef FD_CLOEXEC
+ (void) fcntl(mysql_socket_getfd(new_sock), F_SETFD, FD_CLOEXEC);
+#endif
+
+#ifdef HAVE_LIBWRAP
{
- select_thread_in_use = 0;
- /* The following call will never return */
- DBUG_PRINT("general", ("killing server because socket is closed"));
- kill_server((void*) MYSQL_KILL_SIGNAL);
+ if (mysql_socket_getfd(sock) == mysql_socket_getfd(base_ip_sock) ||
+ mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock))
+ {
+ struct request_info req;
+ signal(SIGCHLD, SIG_DFL);
+ request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE,
+ mysql_socket_getfd(new_sock), NULL);
+ my_fromhost(&req);
+ if (!my_hosts_access(&req))
+ {
+ /*
+ This may be stupid but refuse() includes an exit(0)
+ which we surely don't want...
+ clean_exit() - same stupid thing ...
+ */
+ syslog(deny_severity, "refused connect from %s",
+ my_eval_client(&req));
+
+ /*
+ C++ sucks (the gibberish in front just translates the supplied
+ sink function pointer in the req structure from a void (*sink)();
+ to a void(*sink)(int) if you omit the cast, the C++ compiler
+ will cry...
+ */
+ if (req.sink)
+ ((void(*)(int))req.sink)(req.fd);
+
+ (void)mysql_socket_shutdown(new_sock, SHUT_RDWR);
+ (void)mysql_socket_close(new_sock);
+ /*
+ The connection was refused by TCP wrappers.
+ There are no details (by client IP) available to update the
+ host_cache.
+ */
+ statistic_increment(connection_errors_tcpwrap, &LOCK_status);
+ return;
+ }
+ }
}
-}
-#define MAYBE_BROKEN_SYSCALL kill_broken_server();
-#else
-#define MAYBE_BROKEN_SYSCALL
-#endif
+#endif /* HAVE_LIBWRAP */
- /* Handle new connections and spawn new process to handle them */
+ DBUG_PRINT("info", ("Creating CONNECT for new connection"));
-#ifndef EMBEDDED_LIBRARY
+ if ((connect= new CONNECT()))
+ {
+ is_unix_sock= (mysql_socket_getfd(sock) ==
+ mysql_socket_getfd(unix_sock));
+
+ if (!(connect->vio=
+ mysql_socket_vio_new(new_sock,
+ is_unix_sock ? VIO_TYPE_SOCKET :
+ VIO_TYPE_TCPIP,
+ is_unix_sock ? VIO_LOCALHOST : 0)))
+ {
+ delete connect;
+ connect= 0; // Error handling below
+ }
+ }
+
+ if (!connect)
+ {
+ /* Connect failure */
+ (void)mysql_socket_close(new_sock);
+ statistic_increment(aborted_connects, &LOCK_status);
+ statistic_increment(connection_errors_internal, &LOCK_status);
+ return;
+ }
+
+ if (is_unix_sock)
+ connect->host= my_localhost;
+ if (mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock))
+ {
+ connect->extra_port= 1;
+ connect->scheduler= extra_thread_scheduler;
+ }
+ create_new_thread(connect);
+}
+
+#ifndef _WIN32
void handle_connections_sockets()
{
MYSQL_SOCKET sock= mysql_socket_invalid();
@@ -6808,7 +6454,6 @@ void handle_connections_sockets()
"STATUS=Taking your SQL requests now...\n");
DBUG_PRINT("general",("Waiting for connections."));
- MAYBE_BROKEN_SYSCALL;
while (!abort_loop)
{
#ifdef HAVE_POLL
@@ -6831,15 +6476,11 @@ void handle_connections_sockets()
if (!select_errors++ && !abort_loop) /* purecov: inspected */
sql_print_error("mysqld: Got error %d from select",socket_errno); /* purecov: inspected */
}
- MAYBE_BROKEN_SYSCALL
continue;
}
if (abort_loop)
- {
- MAYBE_BROKEN_SYSCALL;
break;
- }
/* Is this a new connection request ? */
#ifdef HAVE_POLL
@@ -6890,7 +6531,6 @@ void handle_connections_sockets()
if (mysql_socket_getfd(new_sock) != INVALID_SOCKET ||
(socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN))
break;
- MAYBE_BROKEN_SYSCALL;
#if !defined(NO_FCNTL_NONBLOCK)
if (!(test_flags & TEST_BLOCKING))
{
@@ -6902,10 +6542,7 @@ void handle_connections_sockets()
}
#endif
}
-#if !defined(NO_FCNTL_NONBLOCK)
- if (!(test_flags & TEST_BLOCKING))
- fcntl(mysql_socket_getfd(sock), F_SETFL, flags);
-#endif
+
if (mysql_socket_getfd(new_sock) == INVALID_SOCKET)
{
/*
@@ -6916,444 +6553,22 @@ void handle_connections_sockets()
statistic_increment(connection_errors_accept, &LOCK_status);
if ((error_count++ & 255) == 0) // This can happen often
sql_perror("Error in accept");
- MAYBE_BROKEN_SYSCALL;
if (socket_errno == SOCKET_ENFILE || socket_errno == SOCKET_EMFILE)
sleep(1); // Give other threads some time
continue;
}
-#ifdef FD_CLOEXEC
- (void) fcntl(mysql_socket_getfd(new_sock), F_SETFD, FD_CLOEXEC);
+#if !defined(NO_FCNTL_NONBLOCK)
+ if (!(test_flags & TEST_BLOCKING))
+ fcntl(mysql_socket_getfd(sock), F_SETFL, flags);
#endif
-
-#ifdef HAVE_LIBWRAP
- {
- if (mysql_socket_getfd(sock) == mysql_socket_getfd(base_ip_sock) ||
- mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock))
- {
- struct request_info req;
- signal(SIGCHLD, SIG_DFL);
- request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE,
- mysql_socket_getfd(new_sock), NULL);
- my_fromhost(&req);
- if (!my_hosts_access(&req))
- {
- /*
- This may be stupid but refuse() includes an exit(0)
- which we surely don't want...
- clean_exit() - same stupid thing ...
- */
- syslog(deny_severity, "refused connect from %s",
- my_eval_client(&req));
-
- /*
- C++ sucks (the gibberish in front just translates the supplied
- sink function pointer in the req structure from a void (*sink)();
- to a void(*sink)(int) if you omit the cast, the C++ compiler
- will cry...
- */
- if (req.sink)
- ((void (*)(int))req.sink)(req.fd);
-
- (void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
- (void) mysql_socket_close(new_sock);
- /*
- The connection was refused by TCP wrappers.
- There are no details (by client IP) available to update the
- host_cache.
- */
- statistic_increment(connection_errors_tcpwrap, &LOCK_status);
- continue;
- }
- }
- }
-#endif /* HAVE_LIBWRAP */
-
- DBUG_PRINT("info", ("Creating CONNECT for new connection"));
-
- if (CONNECT *connect= new CONNECT())
- {
- const bool is_unix_sock= (mysql_socket_getfd(sock) ==
- mysql_socket_getfd(unix_sock));
-
- if ((connect->vio=
- mysql_socket_vio_new(new_sock,
- is_unix_sock ? VIO_TYPE_SOCKET :
- VIO_TYPE_TCPIP,
- is_unix_sock ? VIO_LOCALHOST: 0)))
- {
- if (is_unix_sock)
- connect->host= my_localhost;
-
- if (mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock))
- {
- connect->extra_port= 1;
- connect->scheduler= extra_thread_scheduler;
- }
- create_new_thread(connect);
- continue;
- }
-
- delete connect;
- }
-
- /* Connect failure */
- (void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
- (void) mysql_socket_close(new_sock);
- statistic_increment(aborted_connects,&LOCK_status);
- statistic_increment(connection_errors_internal, &LOCK_status);
+ handle_accepted_socket(new_sock, sock);
}
sd_notify(0, "STOPPING=1\n"
"STATUS=Shutdown in progress\n");
DBUG_VOID_RETURN;
}
-
-#ifdef _WIN32
-pthread_handler_t handle_connections_sockets_thread(void *arg)
-{
- my_thread_init();
- handle_connections_sockets();
- decrement_handler_count();
- return 0;
-}
-
-pthread_handler_t handle_connections_namedpipes(void *arg)
-{
- HANDLE hConnectedPipe;
- OVERLAPPED connectOverlapped= {0};
- my_thread_init();
- DBUG_ENTER("handle_connections_namedpipes");
- connectOverlapped.hEvent= CreateEvent(NULL, TRUE, FALSE, NULL);
- if (!connectOverlapped.hEvent)
- {
- sql_print_error("Can't create event, last error=%u", GetLastError());
- unireg_abort(1);
- }
- DBUG_PRINT("general",("Waiting for named pipe connections."));
- while (!abort_loop)
- {
- /* wait for named pipe connection */
- BOOL fConnected= ConnectNamedPipe(hPipe, &connectOverlapped);
- if (!fConnected && (GetLastError() == ERROR_IO_PENDING))
- {
- /*
- ERROR_IO_PENDING says async IO has started but not yet finished.
- GetOverlappedResult will wait for completion.
- */
- DWORD bytes;
- fConnected= GetOverlappedResult(hPipe, &connectOverlapped,&bytes, TRUE);
- }
- if (abort_loop)
- break;
- if (!fConnected)
- fConnected = GetLastError() == ERROR_PIPE_CONNECTED;
- if (!fConnected)
- {
- CloseHandle(hPipe);
- if ((hPipe= CreateNamedPipe(pipe_name,
- PIPE_ACCESS_DUPLEX |
- FILE_FLAG_OVERLAPPED,
- PIPE_TYPE_BYTE |
- PIPE_READMODE_BYTE |
- PIPE_WAIT,
- PIPE_UNLIMITED_INSTANCES,
- (int) global_system_variables.
- net_buffer_length,
- (int) global_system_variables.
- net_buffer_length,
- NMPWAIT_USE_DEFAULT_WAIT,
- &saPipeSecurity)) ==
- INVALID_HANDLE_VALUE)
- {
- sql_perror("Can't create new named pipe!");
- break; // Abort
- }
- }
- hConnectedPipe = hPipe;
- /* create new pipe for new connection */
- if ((hPipe = CreateNamedPipe(pipe_name,
- PIPE_ACCESS_DUPLEX |
- FILE_FLAG_OVERLAPPED,
- PIPE_TYPE_BYTE |
- PIPE_READMODE_BYTE |
- PIPE_WAIT,
- PIPE_UNLIMITED_INSTANCES,
- (int) global_system_variables.net_buffer_length,
- (int) global_system_variables.net_buffer_length,
- NMPWAIT_USE_DEFAULT_WAIT,
- &saPipeSecurity)) ==
- INVALID_HANDLE_VALUE)
- {
- sql_perror("Can't create new named pipe!");
- hPipe=hConnectedPipe;
- continue; // We have to try again
- }
- CONNECT *connect;
- if (!(connect= new CONNECT) ||
- !(connect->vio= vio_new_win32pipe(hConnectedPipe)))
- {
- DisconnectNamedPipe(hConnectedPipe);
- CloseHandle(hConnectedPipe);
- delete connect;
- statistic_increment(aborted_connects,&LOCK_status);
- statistic_increment(connection_errors_internal, &LOCK_status);
- continue;
- }
- connect->host= my_localhost;
- create_new_thread(connect);
- }
- LocalFree(saPipeSecurity.lpSecurityDescriptor);
- CloseHandle(connectOverlapped.hEvent);
- DBUG_LEAVE;
- decrement_handler_count();
- return 0;
-}
-#endif /* _WIN32 */
-
-
-#ifdef HAVE_SMEM
-
-/**
- Thread of shared memory's service.
-
- @param arg Arguments of thread
-*/
-pthread_handler_t handle_connections_shared_memory(void *arg)
-{
- /* file-mapping object, use for create shared memory */
- HANDLE handle_connect_file_map= 0;
- char *handle_connect_map= 0; // pointer on shared memory
- HANDLE event_connect_answer= 0;
- ulong smem_buffer_length= shared_memory_buffer_length + 4;
- ulong connect_number= 1;
- char *tmp= NULL;
- char *suffix_pos;
- char connect_number_char[22], *p;
- const char *errmsg= 0;
- SECURITY_ATTRIBUTES *sa_event= 0, *sa_mapping= 0;
- my_thread_init();
- DBUG_ENTER("handle_connections_shared_memorys");
- DBUG_PRINT("general",("Waiting for allocated shared memory."));
-
- /*
- get enough space base-name + '_' + longest suffix we might ever send
- */
- if (!(tmp= (char *)my_malloc(strlen(shared_memory_base_name) + 32L,
- MYF(MY_FAE))))
- goto error;
-
- if (my_security_attr_create(&sa_event, &errmsg,
- GENERIC_ALL, SYNCHRONIZE | EVENT_MODIFY_STATE))
- goto error;
-
- if (my_security_attr_create(&sa_mapping, &errmsg,
- GENERIC_ALL, FILE_MAP_READ | FILE_MAP_WRITE))
- goto error;
-
- /*
- The name of event and file-mapping events create agree next rule:
- shared_memory_base_name+unique_part
- Where:
- shared_memory_base_name is unique value for each server
- unique_part is unique value for each object (events and file-mapping)
- */
- suffix_pos= strxmov(tmp,shared_memory_base_name,"_",NullS);
- strmov(suffix_pos, "CONNECT_REQUEST");
- if ((smem_event_connect_request= CreateEvent(sa_event,
- FALSE, FALSE, tmp)) == 0)
- {
- errmsg= "Could not create request event";
- goto error;
- }
- strmov(suffix_pos, "CONNECT_ANSWER");
- if ((event_connect_answer= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0)
- {
- errmsg="Could not create answer event";
- goto error;
- }
- strmov(suffix_pos, "CONNECT_DATA");
- if ((handle_connect_file_map=
- CreateFileMapping(INVALID_HANDLE_VALUE, sa_mapping,
- PAGE_READWRITE, 0, sizeof(connect_number), tmp)) == 0)
- {
- errmsg= "Could not create file mapping";
- goto error;
- }
- if ((handle_connect_map= (char *)MapViewOfFile(handle_connect_file_map,
- FILE_MAP_WRITE,0,0,
- sizeof(DWORD))) == 0)
- {
- errmsg= "Could not create shared memory service";
- goto error;
- }
-
- while (!abort_loop)
- {
- /* Wait a request from client */
- WaitForSingleObject(smem_event_connect_request,INFINITE);
-
- /*
- it can be after shutdown command
- */
- if (abort_loop)
- goto error;
-
- HANDLE handle_client_file_map= 0;
- char *handle_client_map= 0;
- HANDLE event_client_wrote= 0;
- HANDLE event_client_read= 0; // for transfer data server <-> client
- HANDLE event_server_wrote= 0;
- HANDLE event_server_read= 0;
- HANDLE event_conn_closed= 0;
- CONNECT *connect= 0;
-
- p= int10_to_str(connect_number, connect_number_char, 10);
- /*
- The name of event and file-mapping events create agree next rule:
- shared_memory_base_name+unique_part+number_of_connection
- Where:
- shared_memory_base_name is uniquel value for each server
- unique_part is unique value for each object (events and file-mapping)
- number_of_connection is connection-number between server and client
- */
- suffix_pos= strxmov(tmp,shared_memory_base_name,"_",connect_number_char,
- "_",NullS);
- strmov(suffix_pos, "DATA");
- if ((handle_client_file_map=
- CreateFileMapping(INVALID_HANDLE_VALUE, sa_mapping,
- PAGE_READWRITE, 0, smem_buffer_length, tmp)) == 0)
- {
- errmsg= "Could not create file mapping";
- goto errorconn;
- }
- if ((handle_client_map= (char*)MapViewOfFile(handle_client_file_map,
- FILE_MAP_WRITE,0,0,
- smem_buffer_length)) == 0)
- {
- errmsg= "Could not create memory map";
- goto errorconn;
- }
- strmov(suffix_pos, "CLIENT_WROTE");
- if ((event_client_wrote= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0)
- {
- errmsg= "Could not create client write event";
- goto errorconn;
- }
- strmov(suffix_pos, "CLIENT_READ");
- if ((event_client_read= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0)
- {
- errmsg= "Could not create client read event";
- goto errorconn;
- }
- strmov(suffix_pos, "SERVER_READ");
- if ((event_server_read= CreateEvent(sa_event, FALSE, FALSE, tmp)) == 0)
- {
- errmsg= "Could not create server read event";
- goto errorconn;
- }
- strmov(suffix_pos, "SERVER_WROTE");
- if ((event_server_wrote= CreateEvent(sa_event,
- FALSE, FALSE, tmp)) == 0)
- {
- errmsg= "Could not create server write event";
- goto errorconn;
- }
- strmov(suffix_pos, "CONNECTION_CLOSED");
- if ((event_conn_closed= CreateEvent(sa_event,
- TRUE, FALSE, tmp)) == 0)
- {
- errmsg= "Could not create closed connection event";
- goto errorconn;
- }
- if (abort_loop)
- goto errorconn;
-
- if (!(connect= new CONNECT))
- {
- errmsg= "Could not create CONNECT object";
- goto errorconn;
- }
-
- /* Send number of connection to client */
- int4store(handle_connect_map, connect_number);
- if (!SetEvent(event_connect_answer))
- {
- errmsg= "Could not send answer event";
- goto errorconn;
- }
- /* Set event that client should receive data */
- if (!SetEvent(event_client_read))
- {
- errmsg= "Could not set client to read mode";
- goto errorconn;
- }
- if (!(connect->vio= vio_new_win32shared_memory(handle_client_file_map,
- handle_client_map,
- event_client_wrote,
- event_client_read,
- event_server_wrote,
- event_server_read,
- event_conn_closed)))
- {
- errmsg= "Could not create VIO object";
- goto errorconn;
- }
- connect->host= my_localhost; /* Host is unknown */
- create_new_thread(connect);
- connect_number++;
- continue;
-
-errorconn:
- /* Could not form connection; Free used handlers/memort and retry */
- if (errmsg)
- {
- char buff[180];
- strxmov(buff, "Can't create shared memory connection: ", errmsg, ".",
- NullS);
- sql_perror(buff);
- }
- if (handle_client_file_map)
- CloseHandle(handle_client_file_map);
- if (handle_client_map)
- UnmapViewOfFile(handle_client_map);
- if (event_server_wrote)
- CloseHandle(event_server_wrote);
- if (event_server_read)
- CloseHandle(event_server_read);
- if (event_client_wrote)
- CloseHandle(event_client_wrote);
- if (event_client_read)
- CloseHandle(event_client_read);
- if (event_conn_closed)
- CloseHandle(event_conn_closed);
-
- delete connect;
- statistic_increment(aborted_connects,&LOCK_status);
- statistic_increment(connection_errors_internal, &LOCK_status);
- }
-
- /* End shared memory handling */
-error:
- if (tmp)
- my_free(tmp);
-
- if (errmsg)
- {
- char buff[180];
- strxmov(buff, "Can't create shared memory service: ", errmsg, ".", NullS);
- sql_perror(buff);
- }
- my_security_attr_free(sa_event);
- my_security_attr_free(sa_mapping);
- if (handle_connect_map) UnmapViewOfFile(handle_connect_map);
- if (handle_connect_file_map) CloseHandle(handle_connect_file_map);
- if (event_connect_answer) CloseHandle(event_connect_answer);
- if (smem_event_connect_request) CloseHandle(smem_event_connect_request);
- DBUG_LEAVE;
- decrement_handler_count();
- return 0;
-}
-#endif /* HAVE_SMEM */
+#endif /* _WIN32*/
#endif /* EMBEDDED_LIBRARY */
@@ -7883,20 +7098,17 @@ struct my_option my_long_options[]=
MYSQL_COMPATIBILITY_OPTION("binlog-order-commits"),
MYSQL_TO_BE_IMPLEMENTED_OPTION("log-throttle-queries-not-using-indexes"),
MYSQL_TO_BE_IMPLEMENTED_OPTION("end-markers-in-json"),
- MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace"), // OPTIMIZER_TRACE
MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-features"), // OPTIMIZER_TRACE
MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-offset"), // OPTIMIZER_TRACE
MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-limit"), // OPTIMIZER_TRACE
- MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-max-mem-size"), // OPTIMIZER_TRACE
MYSQL_COMPATIBILITY_OPTION("server-id-bits"),
MYSQL_TO_BE_IMPLEMENTED_OPTION("slave-rows-search-algorithms"), // HAVE_REPLICATION
MYSQL_TO_BE_IMPLEMENTED_OPTION("slave-allow-batching"), // HAVE_REPLICATION
MYSQL_COMPATIBILITY_OPTION("slave-checkpoint-period"), // HAVE_REPLICATION
MYSQL_COMPATIBILITY_OPTION("slave-checkpoint-group"), // HAVE_REPLICATION
MYSQL_SUGGEST_ANALOG_OPTION("slave-pending-jobs-size-max", "--slave-parallel-max-queued"), // HAVE_REPLICATION
- MYSQL_TO_BE_IMPLEMENTED_OPTION("disconnect-on-expired-password"),
- MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-private-key-path"), // HAVE_OPENSSL && !HAVE_YASSL
- MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-public-key-path"), // HAVE_OPENSSL && !HAVE_YASSL
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-private-key-path"), // HAVE_OPENSSL
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-public-key-path"), // HAVE_OPENSSL
/* The following options exist in 5.5 and 5.6 but not in 10.0 */
MYSQL_SUGGEST_ANALOG_OPTION("abort-slave-event-count", "--debug-abort-slave-event-count"),
@@ -7982,22 +7194,6 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff,
}
-/* How many slaves are connected to this master */
-
-static int show_slaves_connected(THD *thd, SHOW_VAR *var, char *buff)
-{
-
- var->type= SHOW_LONGLONG;
- var->value= buff;
- mysql_mutex_lock(&LOCK_slave_list);
-
- *((longlong *)buff)= slave_list.records;
-
- mysql_mutex_unlock(&LOCK_slave_list);
- return 0;
-}
-
-
/* How many masters this slave is connected to */
@@ -8095,187 +7291,6 @@ static int show_flush_commands(THD *thd, SHOW_VAR *var, char *buff,
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
-/* Functions relying on CTX */
-static int show_ssl_ctx_sess_accept(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_accept(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_sess_accept_good(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_accept_good(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_sess_connect_good(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_connect_good(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_sess_accept_renegotiate(THD *thd, SHOW_VAR *var,
- char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_accept_renegotiate(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_sess_connect_renegotiate(THD *thd, SHOW_VAR *var,
- char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_connect_renegotiate(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_sess_cb_hits(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_cb_hits(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_sess_hits(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_hits(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_sess_cache_full(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_cache_full(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_sess_misses(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_misses(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_sess_timeouts(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_timeouts(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_sess_number(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_number(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_sess_connect(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_connect(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_sess_get_cache_size(THD *thd, SHOW_VAR *var,
- char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_get_cache_size(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_get_verify_mode(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_get_verify_mode(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_get_verify_depth(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (!ssl_acceptor_fd ? 0 :
- SSL_CTX_get_verify_depth(ssl_acceptor_fd->ssl_context));
- return 0;
-}
-
-static int show_ssl_ctx_get_session_cache_mode(THD *thd, SHOW_VAR *var,
- char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_CHAR;
- if (!ssl_acceptor_fd)
- var->value= const_cast<char*>("NONE");
- else
- switch (SSL_CTX_get_session_cache_mode(ssl_acceptor_fd->ssl_context))
- {
- case SSL_SESS_CACHE_OFF:
- var->value= const_cast<char*>("OFF"); break;
- case SSL_SESS_CACHE_CLIENT:
- var->value= const_cast<char*>("CLIENT"); break;
- case SSL_SESS_CACHE_SERVER:
- var->value= const_cast<char*>("SERVER"); break;
- case SSL_SESS_CACHE_BOTH:
- var->value= const_cast<char*>("BOTH"); break;
- case SSL_SESS_CACHE_NO_AUTO_CLEAR:
- var->value= const_cast<char*>("NO_AUTO_CLEAR"); break;
- case SSL_SESS_CACHE_NO_INTERNAL_LOOKUP:
- var->value= const_cast<char*>("NO_INTERNAL_LOOKUP"); break;
- default:
- var->value= const_cast<char*>("Unknown"); break;
- }
- return 0;
-}
/*
Functions relying on SSL
@@ -8296,18 +7311,6 @@ static int show_ssl_get_version(THD *thd, SHOW_VAR *var, char *buff,
return 0;
}
-static int show_ssl_session_reused(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONG;
- var->value= buff;
- if( thd->vio_ok() && thd->net.vio->ssl_arg )
- *((long *)buff)= (long)SSL_session_reused((SSL*) thd->net.vio->ssl_arg);
- else
- *((long *)buff)= 0;
- return 0;
-}
-
static int show_ssl_get_default_timeout(THD *thd, SHOW_VAR *var, char *buff,
enum enum_var_type scope)
{
@@ -8325,10 +7328,14 @@ static int show_ssl_get_verify_mode(THD *thd, SHOW_VAR *var, char *buff,
{
var->type= SHOW_LONG;
var->value= buff;
+#ifndef HAVE_WOLFSSL
if( thd->net.vio && thd->net.vio->ssl_arg )
*((long *)buff)= (long)SSL_get_verify_mode((SSL*)thd->net.vio->ssl_arg);
else
*((long *)buff)= 0;
+#else
+ *((long *)buff)= 0;
+#endif
return 0;
}
@@ -8341,6 +7348,7 @@ static int show_ssl_get_verify_depth(THD *thd, SHOW_VAR *var, char *buff,
*((long *)buff)= (long)SSL_get_verify_depth((SSL*)thd->net.vio->ssl_arg);
else
*((long *)buff)= 0;
+
return 0;
}
@@ -8400,15 +7408,6 @@ DEF_SHOW_FUNC(net_wait_num, SHOW_LONGLONG)
DEF_SHOW_FUNC(avg_net_wait_time, SHOW_LONG)
DEF_SHOW_FUNC(avg_trx_wait_time, SHOW_LONG)
-#ifdef HAVE_YASSL
-
-static char *
-my_asn1_time_to_string(const ASN1_TIME *time, char *buf, size_t len)
-{
- return yaSSL_ASN1_TIME_to_string(time, buf, len);
-}
-
-#else /* openssl */
static char *
my_asn1_time_to_string(const ASN1_TIME *time, char *buf, size_t len)
@@ -8436,8 +7435,6 @@ end:
return res;
}
-#endif
-
/**
Handler function for the 'ssl_get_server_not_before' variable
@@ -8613,6 +7610,16 @@ int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff,
*(int *)buff= tp_get_idle_thread_count();
return 0;
}
+
+
+static int show_threadpool_threads(THD *thd, SHOW_VAR *var, char *buff,
+ enum enum_var_type scope)
+{
+ var->type= SHOW_INT;
+ var->value= buff;
+ *(reinterpret_cast<int*>(buff))= tp_get_thread_count();
+ return 0;
+}
#endif
/*
@@ -8622,6 +7629,7 @@ int show_threadpool_idle_threads(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},
+ {"Aborted_connects_preauth", (char*) &aborted_connects_preauth, SHOW_LONG},
{"Acl", (char*) acl_statistics, SHOW_ARRAY},
{"Access_denied_errors", (char*) offsetof(STATUS_VAR, access_denied_errors), SHOW_LONG_STATUS},
{"Binlog_bytes_written", (char*) offsetof(STATUS_VAR, binlog_bytes_written), SHOW_LONGLONG_STATUS},
@@ -8668,6 +7676,7 @@ SHOW_VAR status_vars[]= {
{"Feature_locale", (char*) offsetof(STATUS_VAR, feature_locale), SHOW_LONG_STATUS},
{"Feature_subquery", (char*) offsetof(STATUS_VAR, feature_subquery), SHOW_LONG_STATUS},
{"Feature_system_versioning", (char*) offsetof(STATUS_VAR, feature_system_versioning), SHOW_LONG_STATUS},
+ {"Feature_application_time_periods", (char*) offsetof(STATUS_VAR, feature_application_time_periods), SHOW_LONG_STATUS},
{"Feature_timezone", (char*) offsetof(STATUS_VAR, feature_timezone), SHOW_LONG_STATUS},
{"Feature_trigger", (char*) offsetof(STATUS_VAR, feature_trigger), SHOW_LONG_STATUS},
{"Feature_window_functions", (char*) offsetof(STATUS_VAR, feature_window_functions), SHOW_LONG_STATUS},
@@ -8763,9 +7772,9 @@ SHOW_VAR status_vars[]= {
{"Select_range", (char*) offsetof(STATUS_VAR, select_range_count_), SHOW_LONG_STATUS},
{"Select_range_check", (char*) offsetof(STATUS_VAR, select_range_check_count_), SHOW_LONG_STATUS},
{"Select_scan", (char*) offsetof(STATUS_VAR, select_scan_count_), SHOW_LONG_STATUS},
- {"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_INT},
+ {"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_ATOMIC_COUNTER_UINT32_T},
#ifdef HAVE_REPLICATION
- {"Slaves_connected", (char*) &show_slaves_connected, SHOW_SIMPLE_FUNC },
+ {"Slaves_connected", (char*) &binlog_dump_thread_count, SHOW_ATOMIC_COUNTER_UINT32_T},
{"Slaves_running", (char*) &show_slaves_running, SHOW_SIMPLE_FUNC },
{"Slave_connections", (char*) offsetof(STATUS_VAR, com_register_slave), SHOW_LONG_STATUS},
{"Slave_heartbeat_period", (char*) &show_heartbeat_period, SHOW_SIMPLE_FUNC},
@@ -8783,28 +7792,28 @@ SHOW_VAR status_vars[]= {
{"Sort_scan", (char*) offsetof(STATUS_VAR, filesort_scan_count_), SHOW_LONG_STATUS},
#ifdef HAVE_OPENSSL
#ifndef EMBEDDED_LIBRARY
- {"Ssl_accept_renegotiates", (char*) &show_ssl_ctx_sess_accept_renegotiate, SHOW_SIMPLE_FUNC},
- {"Ssl_accepts", (char*) &show_ssl_ctx_sess_accept, SHOW_SIMPLE_FUNC},
- {"Ssl_callback_cache_hits", (char*) &show_ssl_ctx_sess_cb_hits, SHOW_SIMPLE_FUNC},
+ {"Ssl_accept_renegotiates", (char*) &ssl_acceptor_stats.zero, SHOW_LONG},
+ {"Ssl_accepts", (char*) &ssl_acceptor_stats.accept, SHOW_LONG},
+ {"Ssl_callback_cache_hits", (char*) &ssl_acceptor_stats.zero, SHOW_LONG},
{"Ssl_cipher", (char*) &show_ssl_get_cipher, SHOW_SIMPLE_FUNC},
{"Ssl_cipher_list", (char*) &show_ssl_get_cipher_list, SHOW_SIMPLE_FUNC},
- {"Ssl_client_connects", (char*) &show_ssl_ctx_sess_connect, SHOW_SIMPLE_FUNC},
- {"Ssl_connect_renegotiates", (char*) &show_ssl_ctx_sess_connect_renegotiate, SHOW_SIMPLE_FUNC},
- {"Ssl_ctx_verify_depth", (char*) &show_ssl_ctx_get_verify_depth, SHOW_SIMPLE_FUNC},
- {"Ssl_ctx_verify_mode", (char*) &show_ssl_ctx_get_verify_mode, SHOW_SIMPLE_FUNC},
+ {"Ssl_client_connects", (char*) &ssl_acceptor_stats.zero, SHOW_LONG},
+ {"Ssl_connect_renegotiates", (char*) &ssl_acceptor_stats.zero, SHOW_LONG},
+ {"Ssl_ctx_verify_depth", (char*) &ssl_acceptor_stats.verify_depth, SHOW_LONG},
+ {"Ssl_ctx_verify_mode", (char*) &ssl_acceptor_stats.verify_mode, SHOW_LONG},
{"Ssl_default_timeout", (char*) &show_ssl_get_default_timeout, SHOW_SIMPLE_FUNC},
- {"Ssl_finished_accepts", (char*) &show_ssl_ctx_sess_accept_good, SHOW_SIMPLE_FUNC},
- {"Ssl_finished_connects", (char*) &show_ssl_ctx_sess_connect_good, SHOW_SIMPLE_FUNC},
+ {"Ssl_finished_accepts", (char*) &ssl_acceptor_stats.accept_good, SHOW_LONG},
+ {"Ssl_finished_connects", (char*) &ssl_acceptor_stats.zero, SHOW_LONG},
{"Ssl_server_not_after", (char*) &show_ssl_get_server_not_after, SHOW_SIMPLE_FUNC},
{"Ssl_server_not_before", (char*) &show_ssl_get_server_not_before, SHOW_SIMPLE_FUNC},
- {"Ssl_session_cache_hits", (char*) &show_ssl_ctx_sess_hits, SHOW_SIMPLE_FUNC},
- {"Ssl_session_cache_misses", (char*) &show_ssl_ctx_sess_misses, SHOW_SIMPLE_FUNC},
- {"Ssl_session_cache_mode", (char*) &show_ssl_ctx_get_session_cache_mode, SHOW_SIMPLE_FUNC},
- {"Ssl_session_cache_overflows", (char*) &show_ssl_ctx_sess_cache_full, SHOW_SIMPLE_FUNC},
- {"Ssl_session_cache_size", (char*) &show_ssl_ctx_sess_get_cache_size, SHOW_SIMPLE_FUNC},
- {"Ssl_session_cache_timeouts", (char*) &show_ssl_ctx_sess_timeouts, SHOW_SIMPLE_FUNC},
- {"Ssl_sessions_reused", (char*) &show_ssl_session_reused, SHOW_SIMPLE_FUNC},
- {"Ssl_used_session_cache_entries",(char*) &show_ssl_ctx_sess_number, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_hits", (char*) &ssl_acceptor_stats.zero, SHOW_LONG},
+ {"Ssl_session_cache_misses", (char*) &ssl_acceptor_stats.zero, SHOW_LONG},
+ {"Ssl_session_cache_mode", (char*) &ssl_acceptor_stats.session_cache_mode, SHOW_CHAR_PTR},
+ {"Ssl_session_cache_overflows", (char*) &ssl_acceptor_stats.zero, SHOW_LONG},
+ {"Ssl_session_cache_size", (char*) &ssl_acceptor_stats.cache_size, SHOW_LONG},
+ {"Ssl_session_cache_timeouts", (char*) &ssl_acceptor_stats.zero, SHOW_LONG},
+ {"Ssl_sessions_reused", (char*) &ssl_acceptor_stats.zero, SHOW_LONG},
+ {"Ssl_used_session_cache_entries",(char*) &ssl_acceptor_stats.zero, SHOW_LONG},
{"Ssl_verify_depth", (char*) &show_ssl_get_verify_depth, SHOW_SIMPLE_FUNC},
{"Ssl_verify_mode", (char*) &show_ssl_get_verify_mode, SHOW_SIMPLE_FUNC},
{"Ssl_version", (char*) &show_ssl_get_version, SHOW_SIMPLE_FUNC},
@@ -8819,7 +7828,7 @@ SHOW_VAR status_vars[]= {
{"Subquery_cache_miss", (char*) &subquery_cache_miss, SHOW_LONG},
{"Table_locks_immediate", (char*) &locks_immediate, SHOW_LONG},
{"Table_locks_waited", (char*) &locks_waited, SHOW_LONG},
- {"Table_open_cache_active_instances", (char*) &tc_active_instances, SHOW_UINT},
+ {"Table_open_cache_active_instances", (char*) &show_tc_active_instances, SHOW_SIMPLE_FUNC},
{"Table_open_cache_hits", (char*) offsetof(STATUS_VAR, table_open_cache_hits), SHOW_LONGLONG_STATUS},
{"Table_open_cache_misses", (char*) offsetof(STATUS_VAR, table_open_cache_misses), SHOW_LONGLONG_STATUS},
{"Table_open_cache_overflows", (char*) offsetof(STATUS_VAR, table_open_cache_overflows), SHOW_LONGLONG_STATUS},
@@ -8830,7 +7839,7 @@ SHOW_VAR status_vars[]= {
#endif
#ifdef HAVE_POOL_OF_THREADS
{"Threadpool_idle_threads", (char *) &show_threadpool_idle_threads, SHOW_SIMPLE_FUNC},
- {"Threadpool_threads", (char *) &tp_stats.num_worker_threads, SHOW_INT},
+ {"Threadpool_threads", (char *) &show_threadpool_threads, SHOW_SIMPLE_FUNC},
#endif
{"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_NOFLUSH},
{"Threads_connected", (char*) &connection_count, SHOW_INT},
@@ -8845,6 +7854,22 @@ SHOW_VAR status_vars[]= {
{"Uptime_since_flush_status",(char*) &show_flushstatustime, SHOW_SIMPLE_FUNC},
#endif
#ifdef WITH_WSREP
+ {"wsrep_connected", (char*) &wsrep_connected, SHOW_BOOL},
+ {"wsrep_ready", (char*) &wsrep_show_ready, SHOW_FUNC},
+ {"wsrep_cluster_state_uuid",(char*) &wsrep_cluster_state_uuid,SHOW_CHAR_PTR},
+ {"wsrep_cluster_conf_id", (char*) &wsrep_cluster_conf_id, SHOW_LONGLONG},
+ {"wsrep_cluster_status", (char*) &wsrep_cluster_status, SHOW_CHAR_PTR},
+ {"wsrep_cluster_size", (char*) &wsrep_cluster_size, SHOW_LONG_NOFLUSH},
+ {"wsrep_local_index", (char*) &wsrep_local_index, SHOW_LONG_NOFLUSH},
+ {"wsrep_local_bf_aborts", (char*) &wsrep_show_bf_aborts, SHOW_FUNC},
+ {"wsrep_provider_name", (char*) &wsrep_provider_name, SHOW_CHAR_PTR},
+ {"wsrep_provider_version", (char*) &wsrep_provider_version, SHOW_CHAR_PTR},
+ {"wsrep_provider_vendor", (char*) &wsrep_provider_vendor, SHOW_CHAR_PTR},
+ {"wsrep_provider_capabilities", (char*) &wsrep_provider_capabilities, SHOW_CHAR_PTR},
+ {"wsrep_thread_count", (char*) &wsrep_running_threads, SHOW_LONG_NOFLUSH},
+ {"wsrep_applier_thread_count", (char*) &wsrep_running_applier_threads, SHOW_LONG_NOFLUSH},
+ {"wsrep_rollbacker_thread_count", (char *) &wsrep_running_rollbacker_threads, SHOW_LONG_NOFLUSH},
+ {"wsrep_cluster_capabilities", (char*) &wsrep_cluster_capabilities, SHOW_CHAR_PTR},
{"wsrep", (char*) &wsrep_show_status, SHOW_FUNC},
#endif
{NullS, NullS, SHOW_LONG}
@@ -9009,18 +8034,16 @@ static int mysql_init_variables(void)
opt_bootstrap= opt_myisam_log= 0;
disable_log_notes= 0;
mqh_used= 0;
- kill_in_progress= 0;
cleanup_done= 0;
- test_flags= select_errors= dropping_tables= ha_open_options=0;
+ select_errors= dropping_tables= ha_open_options=0;
thread_count= kill_cached_threads= wake_thread= 0;
- service_thread_count= 0;
slave_open_temp_tables= 0;
cached_thread_count= 0;
opt_endinfo= using_udf_functions= 0;
opt_using_transactions= 0;
abort_loop= select_thread_in_use= signal_thread_in_use= 0;
- ready_to_exit= shutdown_in_progress= grant_option= 0;
- aborted_threads= aborted_connects= 0;
+ grant_option= 0;
+ aborted_threads= aborted_connects= aborted_connects_preauth= 0;
subquery_cache_miss= subquery_cache_hit= 0;
delayed_insert_threads= delayed_insert_writes= delayed_rows_in_use= 0;
delayed_insert_errors= thread_created= 0;
@@ -9049,7 +8072,9 @@ static int mysql_init_variables(void)
character_set_filesystem= &my_charset_bin;
opt_specialflag= SPECIAL_ENGLISH;
+#ifndef EMBEDDED_LIBRARY
unix_sock= base_ip_sock= extra_ip_sock= MYSQL_INVALID_SOCKET;
+#endif
mysql_home_ptr= mysql_home;
log_error_file_ptr= log_error_file;
protocol_version= PROTOCOL_VERSION;
@@ -9059,7 +8084,6 @@ static int mysql_init_variables(void)
global_query_id= 1;
global_thread_id= 0;
strnmov(server_version, MYSQL_SERVER_VERSION, sizeof(server_version)-1);
- threads.empty();
thread_cache.empty();
key_caches.empty();
if (!(dflt_key_cache= get_or_create_key_cache(default_key_cache_base.str,
@@ -9111,7 +8135,7 @@ static int mysql_init_variables(void)
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
have_ssl=SHOW_OPTION_YES;
-#if defined(HAVE_YASSL)
+#if defined(HAVE_WOLFSSL)
have_openssl= SHOW_OPTION_NO;
#else
have_openssl= SHOW_OPTION_YES;
@@ -9163,9 +8187,6 @@ static int mysql_init_variables(void)
ssl_acceptor_fd= 0;
#endif /* ! EMBEDDED_LIBRARY */
#endif /* HAVE_OPENSSL */
-#ifdef HAVE_SMEM
- shared_memory_base_name= default_shared_memory_base_name;
-#endif
#if defined(__WIN__)
/* Allow Win32 users to move MySQL anywhere */
@@ -9375,7 +8396,7 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
if (!(p= strstr(argument, "->")))
{
- sql_print_error("Bad syntax in replicate-rewrite-db - missing '->'!\n");
+ sql_print_error("Bad syntax in replicate-rewrite-db - missing '->'!");
return 1;
}
val= p--;
@@ -9384,7 +8405,7 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
/* Db name can be one char also */
if (p == argument && my_isspace(mysqld_charset, *p))
{
- sql_print_error("Bad syntax in replicate-rewrite-db - empty FROM db!\n");
+ sql_print_error("Bad syntax in replicate-rewrite-db - empty FROM db!");
return 1;
}
*val= 0;
@@ -9393,7 +8414,7 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
val++;
if (!*val)
{
- sql_print_error("Bad syntax in replicate-rewrite-db - empty TO db!\n");
+ sql_print_error("Bad syntax in replicate-rewrite-db - empty TO db!");
return 1;
}
@@ -9422,7 +8443,7 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
{
if (cur_rpl_filter->add_do_table(argument))
{
- sql_print_error("Could not add do table rule '%s'!\n", argument);
+ sql_print_error("Could not add do table rule '%s'!", argument);
return 1;
}
break;
@@ -9431,7 +8452,7 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
{
if (cur_rpl_filter->add_wild_do_table(argument))
{
- sql_print_error("Could not add do table rule '%s'!\n", argument);
+ sql_print_error("Could not add do table rule '%s'!", argument);
return 1;
}
break;
@@ -9440,7 +8461,7 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
{
if (cur_rpl_filter->add_wild_ignore_table(argument))
{
- sql_print_error("Could not add ignore table rule '%s'!\n", argument);
+ sql_print_error("Could not add ignore table rule '%s'!", argument);
return 1;
}
break;
@@ -9449,7 +8470,7 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
{
if (cur_rpl_filter->add_ignore_table(argument))
{
- sql_print_error("Could not add ignore table rule '%s'!\n", argument);
+ sql_print_error("Could not add ignore table rule '%s'!", argument);
return 1;
}
break;
@@ -9810,7 +8831,7 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
if (ft_boolean_check_syntax_string((uchar*) ft_boolean_syntax))
{
- sql_print_error("Invalid ft-boolean-syntax string: %s\n",
+ sql_print_error("Invalid ft-boolean-syntax string: %s",
ft_boolean_syntax);
return 1;
}
@@ -9912,10 +8933,10 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
errors.
*/
if (global_system_variables.log_warnings >= 10)
- my_global_flags= MY_WME | ME_JUST_INFO;
+ my_global_flags= MY_WME | ME_NOTE;
/* 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;
+ my_global_flags|= ME_ERROR_LOG;
if (my_assert_on_error)
debug_assert_if_crashed_table= 1;
@@ -10312,7 +9333,9 @@ void refresh_status(THD *thd)
reset_status_vars();
#ifdef WITH_WSREP
if (WSREP_ON)
- wsrep->stats_reset(wsrep);
+ {
+ Wsrep_server_state::instance().provider().reset_status();
+ }
#endif /* WITH_WSREP */
/* Reset the counters of all key caches (default and named). */
@@ -10772,6 +9795,14 @@ static my_thread_id thread_id_max= UINT_MAX32;
@param[out] low - lower bound for the range
@param[out] high - upper bound for the range
*/
+
+static my_bool recalculate_callback(THD *thd, std::vector<my_thread_id> *ids)
+{
+ ids->push_back(thd->thread_id);
+ return 0;
+}
+
+
static void recalculate_thread_id_range(my_thread_id *low, my_thread_id *high)
{
std::vector<my_thread_id> ids;
@@ -10779,15 +9810,7 @@ static void recalculate_thread_id_range(my_thread_id *low, my_thread_id *high)
// Add sentinels
ids.push_back(0);
ids.push_back(UINT_MAX32);
-
- mysql_mutex_lock(&LOCK_thread_count);
-
- I_List_iterator<THD> it(threads);
- THD *thd;
- while ((thd=it++))
- ids.push_back(thd->thread_id);
-
- mysql_mutex_unlock(&LOCK_thread_count);
+ server_threads.iterate(recalculate_callback, &ids);
std::sort(ids.begin(), ids.end());
my_thread_id max_gap= 0;
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 47f19b76c93..f4d0d891a0f 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -23,8 +23,9 @@
#include "sql_bitmap.h" /* Bitmap */
#include "my_decimal.h" /* my_decimal */
#include "mysql_com.h" /* SERVER_VERSION_LENGTH */
-#include "my_atomic.h"
+#include "my_counter.h"
#include "mysql/psi/mysql_file.h" /* MYSQL_FILE */
+#include "mysql/psi/mysql_socket.h" /* MYSQL_SOCKET */
#include "sql_list.h" /* I_List */
#include "sql_cmd.h"
#include <my_rnd.h>
@@ -40,15 +41,7 @@ struct scheduler_functions;
typedef struct st_mysql_show_var SHOW_VAR;
-#if MAX_INDEXES <= 64
-typedef Bitmap<64> key_map; /* Used for finding keys */
-#elif MAX_INDEXES > 128
-#error "MAX_INDEXES values greater than 128 is not supported."
-#else
-typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */
-#endif
-
- /* Bits from testflag */
+/* Bits from testflag */
#define TEST_PRINT_CACHED_TABLES 1U
#define TEST_NO_KEY_GROUP 2U
#define TEST_MIT_THREAD 4U
@@ -81,11 +74,10 @@ enum enum_slave_parallel_mode {
};
/* Function prototypes */
-void kill_mysql(THD *thd= 0);
+void kill_mysql(THD *thd);
void close_connection(THD *thd, uint sql_errno= 0);
void handle_connection_in_main_thread(CONNECT *thd);
void create_thread_to_handle_connection(CONNECT *connect);
-void signal_thd_deleted();
void unlink_thd(THD *thd);
bool one_thread_per_connection_end(THD *thd, bool put_in_cache);
void flush_thread_cache();
@@ -93,6 +85,11 @@ void refresh_status(THD *thd);
bool is_secure_file_path(char *path);
void dec_connection_count(scheduler_functions *scheduler);
extern void init_net_server_extension(THD *thd);
+extern void handle_accepted_socket(MYSQL_SOCKET new_sock, MYSQL_SOCKET sock);
+extern void create_new_thread(CONNECT *connect);
+
+extern void ssl_acceptor_stats_update(int sslaccept_ret);
+extern int reinit_ssl();
extern "C" MYSQL_PLUGIN_IMPORT CHARSET_INFO *system_charset_info;
extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *files_charset_info ;
@@ -122,7 +119,6 @@ extern bool opt_ignore_builtin_innodb;
extern my_bool opt_character_set_client_handshake;
extern my_bool debug_assert_on_not_freed_memory;
extern bool volatile abort_loop;
-extern bool volatile in_bootstrap;
extern uint connection_count;
extern my_bool opt_safe_user_create;
extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
@@ -153,6 +149,7 @@ extern ulong opt_replicate_events_marked_for_skip;
extern char *default_tz_name;
extern Time_zone *default_tz;
extern char *my_bind_addr_str;
+extern int server_socket_ai_family;
extern char *default_storage_engine, *default_tmp_storage_engine;
extern char *enforced_storage_engine;
extern char *gtid_pos_auto_engines;
@@ -219,12 +216,12 @@ extern ulonglong thd_startup_options;
extern my_thread_id global_thread_id;
extern ulong binlog_cache_use, binlog_cache_disk_use;
extern ulong binlog_stmt_cache_use, binlog_stmt_cache_disk_use;
-extern ulong aborted_threads,aborted_connects;
+extern ulong aborted_threads, aborted_connects, aborted_connects_preauth;
extern ulong delayed_insert_timeout;
extern ulong delayed_insert_limit, delayed_queue_size;
extern ulong delayed_insert_threads, delayed_insert_writes;
extern ulong delayed_rows_in_use,delayed_insert_errors;
-extern int32 slave_open_temp_tables;
+extern Atomic_counter<uint32_t> slave_open_temp_tables;
extern ulonglong query_cache_size;
extern ulong query_cache_limit;
extern ulong query_cache_min_res_unit;
@@ -232,6 +229,7 @@ extern ulong slow_launch_threads, slow_launch_time;
extern MYSQL_PLUGIN_IMPORT ulong max_connections;
extern uint max_digest_length;
extern ulong max_connect_errors, connect_timeout;
+extern uint max_password_errors;
extern my_bool slave_allow_batching;
extern my_bool allow_slave_start;
extern LEX_CSTRING reason_slave_blocked;
@@ -248,7 +246,7 @@ extern ulonglong max_binlog_cache_size, max_binlog_stmt_cache_size;
extern ulong max_binlog_size;
extern ulong slave_max_allowed_packet;
extern ulong opt_binlog_rows_event_max_size;
-extern ulong rpl_recovery_rank, thread_cache_size;
+extern ulong thread_cache_size;
extern ulong stored_program_cache_size;
extern ulong opt_slave_parallel_threads;
extern ulong opt_slave_domain_parallel_threads;
@@ -257,6 +255,7 @@ extern ulong opt_slave_parallel_mode;
extern ulong opt_binlog_commit_wait_count;
extern ulong opt_binlog_commit_wait_usec;
extern my_bool opt_gtid_ignore_duplicates;
+extern uint opt_gtid_cleanup_batch_size;
extern ulong back_log;
extern ulong executed_events;
extern char language[FN_REFLEN];
@@ -286,11 +285,8 @@ extern int mysqld_server_started, mysqld_server_initialized;
extern "C" MYSQL_PLUGIN_IMPORT int orig_argc;
extern "C" MYSQL_PLUGIN_IMPORT char **orig_argv;
extern pthread_attr_t connection_attrib;
-extern MYSQL_FILE *bootstrap_file;
extern my_bool old_mode;
extern LEX_STRING opt_init_connect, opt_init_slave;
-extern int bootstrap_error;
-extern I_List<THD> threads;
extern char err_shared_dir[];
extern ulong connection_errors_select;
extern ulong connection_errors_accept;
@@ -304,6 +300,8 @@ extern my_bool encrypt_tmp_disk_tables, encrypt_tmp_files;
extern ulong encryption_algorithm;
extern const char *encryption_algorithm_names[];
extern long opt_secure_timestamp;
+extern uint default_password_lifetime;
+extern my_bool disconnect_on_expired_password;
enum secure_timestamp { SECTIME_NO, SECTIME_SUPER, SECTIME_REPL, SECTIME_YES };
@@ -327,7 +325,7 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_LOCK_logger, key_LOCK_manager,
key_LOCK_prepared_stmt_count,
key_LOCK_rpl_status, key_LOCK_server_started,
- key_LOCK_status, key_LOCK_show_status,
+ key_LOCK_status,
key_LOCK_thd_data, key_LOCK_thd_kill,
key_LOCK_user_conn, key_LOG_LOCK_log,
key_master_info_data_lock, key_master_info_run_lock,
@@ -337,7 +335,8 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_rpl_group_info_sleep_lock,
key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data,
key_LOCK_start_thread,
- key_LOCK_error_messages, key_LOCK_thread_count, key_PARTITION_LOCK_auto_inc;
+ key_LOCK_error_messages,
+ key_PARTITION_LOCK_auto_inc;
extern PSI_mutex_key key_RELAYLOG_LOCK_index;
extern PSI_mutex_key key_LOCK_relaylog_end_pos;
extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state,
@@ -353,7 +352,8 @@ extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger,
key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave,
key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock,
key_LOCK_SEQUENCE,
- key_rwlock_LOCK_vers_stats, key_rwlock_LOCK_stat_serial;
+ key_rwlock_LOCK_vers_stats, key_rwlock_LOCK_stat_serial,
+ key_rwlock_THD_list;
#ifdef HAVE_MMAP
extern PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool;
@@ -373,7 +373,7 @@ extern PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond,
key_rpl_group_info_sleep_cond,
key_TABLE_SHARE_cond, key_user_level_lock_cond,
key_COND_start_thread,
- key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache;
+ key_COND_thread_cache, key_COND_flush_thread_cache;
extern PSI_cond_key key_RELAYLOG_COND_relay_log_updated,
key_RELAYLOG_COND_bin_log_updated, key_COND_wakeup_ready,
key_COND_wait_commit;
@@ -385,7 +385,7 @@ extern PSI_cond_key key_COND_rpl_thread, key_COND_rpl_thread_queue,
extern PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates;
extern PSI_cond_key key_TABLE_SHARE_COND_rotation;
-extern PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
+extern PSI_thread_key key_thread_delayed_insert,
key_thread_handle_manager, key_thread_kill_server, key_thread_main,
key_thread_one_connection, key_thread_signal_hand,
key_thread_slave_background, key_rpl_parallel_thread;
@@ -611,13 +611,13 @@ extern MYSQL_PLUGIN_IMPORT key_map key_map_full; /* Should be threaded
Server mutex locks and condition variables.
*/
extern mysql_mutex_t
- LOCK_item_func_sleep, LOCK_status, LOCK_show_status,
+ LOCK_item_func_sleep, LOCK_status,
LOCK_error_log, LOCK_delayed_insert, LOCK_short_uuid_generator,
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
- LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_user_conn,
- LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count;
-extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count,
- LOCK_global_system_variables;
+ LOCK_active_mi, LOCK_manager, LOCK_user_conn,
+ LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count ;
+extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_global_system_variables;
+extern mysql_rwlock_t LOCK_all_status_vars;
extern mysql_mutex_t LOCK_start_thread;
#ifdef HAVE_OPENSSL
extern char* des_key_file;
@@ -626,13 +626,15 @@ extern mysql_mutex_t LOCK_des_key_file;
extern mysql_mutex_t LOCK_server_started;
extern mysql_cond_t COND_server_started;
extern mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
+extern mysql_rwlock_t LOCK_ssl_refresh;
extern mysql_prlock_t LOCK_system_variables_hash;
-extern mysql_cond_t COND_thread_count, COND_start_thread;
+extern mysql_cond_t COND_start_thread;
extern mysql_cond_t COND_manager;
-extern int32 thread_count, service_thread_count;
+extern Atomic_counter<uint32_t> thread_count;
extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher,
*opt_ssl_key, *opt_ssl_crl, *opt_ssl_crlpath;
+extern ulonglong tls_version;
extern MYSQL_PLUGIN_IMPORT pthread_key(THD*, THR_THD);
@@ -695,6 +697,7 @@ enum options_mysqld
OPT_WSREP_SYNC_WAIT,
#endif /* WITH_WSREP */
OPT_MYSQL_COMPATIBILITY,
+ OPT_TLS_VERSION,
OPT_MYSQL_TO_BE_IMPLEMENTED,
OPT_which_is_always_the_last
};
@@ -732,6 +735,8 @@ enum enum_query_type
/// SHOW CREATE {VIEW|PROCEDURE|FUNCTION} and other cases where the
/// original representation is required, should set this flag.
QT_ITEM_ORIGINAL_FUNC_NULLIF= (1 << 7),
+ /// good for parsing
+ QT_PARSABLE= (1 << 8),
/// This value means focus on readability, not on ability to parse back, etc.
QT_EXPLAIN= QT_TO_SYSTEM_CHARSET |
@@ -755,19 +760,17 @@ enum enum_query_type
/* query_id */
-extern query_id_t global_query_id;
-
-ATTRIBUTE_NORETURN void unireg_end(void);
+extern Atomic_counter<query_id_t> global_query_id;
/* increment query_id and return it. */
inline __attribute__((warn_unused_result)) query_id_t next_query_id()
{
- return my_atomic_add64_explicit(&global_query_id, 1, MY_MEMORY_ORDER_RELAXED);
+ return global_query_id++;
}
inline query_id_t get_query_id()
{
- return my_atomic_load64_explicit(&global_query_id, MY_MEMORY_ORDER_RELAXED);
+ return global_query_id;
}
/* increment global_thread_id and return it. */
diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc
index af6a75cdef2..f8d11da1d5e 100644
--- a/sql/opt_index_cond_pushdown.cc
+++ b/sql/opt_index_cond_pushdown.cc
@@ -264,6 +264,10 @@ static Item *make_cond_for_index(THD *thd, Item *cond, TABLE *table, uint keyno,
static Item *make_cond_remainder(THD *thd, Item *cond, TABLE *table, uint keyno,
bool other_tbls_ok, bool exclude_index)
{
+ if (exclude_index &&
+ uses_index_fields_only(cond, table, keyno, other_tbls_ok))
+ return 0;
+
if (cond->type() == Item::COND_ITEM)
{
table_map tbl_map= 0;
@@ -272,7 +276,7 @@ static Item *make_cond_remainder(THD *thd, Item *cond, TABLE *table, uint keyno,
/* Create new top level AND item */
Item_cond_and *new_cond= new (thd->mem_root) Item_cond_and(thd);
if (!new_cond)
- return (COND*) 0;
+ return (COND*) 0;
List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
Item *item;
while ((item=li++))
@@ -318,14 +322,7 @@ static Item *make_cond_remainder(THD *thd, Item *cond, TABLE *table, uint keyno,
return new_cond;
}
}
- else
- {
- if (exclude_index &&
- uses_index_fields_only(cond, table, keyno, other_tbls_ok))
- return 0;
- else
- return cond;
- }
+ return cond;
}
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 8f8767c10b3..d47aa1ee41e 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -119,6 +119,7 @@
#include "sql_select.h"
#include "sql_statistics.h"
#include "uniques.h"
+#include "my_json_writer.h"
#ifndef EXTRA_DEBUG
#define test_rb_tree(A,B) {}
@@ -314,7 +315,7 @@ public:
*/
key_map possible_keys;
longlong baseflag;
- uint max_key_part, range_count;
+ uint max_key_parts, range_count;
bool quick; // Don't calulate possible keys
@@ -327,8 +328,6 @@ public:
uint *imerge_cost_buff; /* buffer for index_merge cost estimates */
uint imerge_cost_buff_size; /* size of the buffer */
- /* TRUE if last checked tree->key can be used for ROR-scan */
- bool is_ror_scan;
/* Number of ranges in the last checked tree->key */
uint n_ranges;
uint8 first_null_comp; /* first null component if any, 0 - otherwise */
@@ -350,7 +349,7 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts);
static ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
SEL_ARG *tree, bool update_tbl_stats,
uint *mrr_flags, uint *bufsize,
- Cost_estimate *cost);
+ Cost_estimate *cost, bool *is_ror_scan);
QUICK_RANGE_SELECT *get_quick_select(PARAM *param,uint index,
SEL_ARG *key_tree, uint mrr_flags,
@@ -429,6 +428,28 @@ static int and_range_trees(RANGE_OPT_PARAM *param,
SEL_TREE *result);
static bool remove_nonrange_trees(RANGE_OPT_PARAM *param, SEL_TREE *tree);
+static void print_key_value(String *out, const KEY_PART_INFO *key_part,
+ const uchar* key, uint length);
+static void print_keyparts_name(String *out, const KEY_PART_INFO *key_part,
+ uint n_keypart, key_part_map keypart_map);
+
+static void trace_ranges(Json_writer_array *range_trace,
+ PARAM *param, uint idx,
+ SEL_ARG *keypart,
+ const KEY_PART_INFO *key_parts);
+
+static
+void print_range(String *out, const KEY_PART_INFO *key_part,
+ KEY_MULTI_RANGE *range, uint n_key_parts);
+
+static
+void print_range_for_non_indexed_field(String *out, Field *field,
+ KEY_MULTI_RANGE *range);
+
+static void print_min_range_operator(String *out, const ha_rkey_function flag);
+static void print_max_range_operator(String *out, const ha_rkey_function flag);
+
+static bool is_field_an_unique_index(RANGE_OPT_PARAM *param, Field *field);
/*
SEL_IMERGE is a list of possible ways to do index merge, i.e. it is
@@ -662,7 +683,7 @@ int SEL_IMERGE::or_sel_tree_with_checks(RANGE_OPT_PARAM *param,
{
bool was_ored= FALSE;
*is_last_check_pass= is_first_check_pass;
- SEL_TREE** or_tree = trees;
+ SEL_TREE** or_tree= trees;
for (uint i= 0; i < n_trees; i++, or_tree++)
{
SEL_TREE *result= 0;
@@ -859,7 +880,7 @@ SEL_IMERGE::SEL_IMERGE(SEL_IMERGE *arg, uint cnt,
trees_next= trees + (cnt ? cnt : arg->trees_next-arg->trees);
trees_end= trees + elements;
- for (SEL_TREE **tree = trees, **arg_tree= arg->trees; tree < trees_next;
+ for (SEL_TREE **tree= trees, **arg_tree= arg->trees; tree < trees_next;
tree++, arg_tree++)
{
if (!(*tree= new SEL_TREE(*arg_tree, TRUE, param)))
@@ -1477,7 +1498,6 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler,
{
handler *save_file= file, *org_file;
THD *thd= head->in_use;
- MY_BITMAP * const save_vcol_set= head->vcol_set;
MY_BITMAP * const save_read_set= head->read_set;
MY_BITMAP * const save_write_set= head->write_set;
DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_merged_scan");
@@ -1535,14 +1555,14 @@ end:
org_file= head->file;
head->file= file;
- head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap, &column_bitmap);
+ head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap);
head->prepare_for_keyread(index, &column_bitmap);
head->prepare_for_position();
head->file= org_file;
/* Restore head->read_set (and write_set) to what they had before the call */
- head->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set);
+ head->column_bitmaps_set(save_read_set, save_write_set);
if (reset())
{
@@ -1557,7 +1577,7 @@ end:
DBUG_RETURN(0);
failure:
- head->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set);
+ head->column_bitmaps_set(save_read_set, save_write_set);
delete file;
file= save_file;
free_file= false;
@@ -1895,6 +1915,118 @@ SEL_ARG::SEL_ARG(Field *field_,uint8 part_,
left=right= &null_element;
}
+
+/*
+ A number of helper classes:
+ SEL_ARG_LE, SEL_ARG_LT, SEL_ARG_GT, SEL_ARG_GE,
+ to share the code between:
+ Field::stored_field_make_mm_leaf()
+ Field::stored_field_make_mm_leaf_exact()
+*/
+class SEL_ARG_LE: public SEL_ARG
+{
+public:
+ SEL_ARG_LE(const uchar *key, Field *field)
+ :SEL_ARG(field, key, key)
+ {
+ if (!field->real_maybe_null())
+ min_flag= NO_MIN_RANGE; // From start
+ else
+ {
+ min_value= is_null_string;
+ min_flag= NEAR_MIN; // > NULL
+ }
+ }
+};
+
+
+class SEL_ARG_LT: public SEL_ARG_LE
+{
+public:
+ /*
+ Use this constructor if value->save_in_field() went precisely,
+ without any data rounding or truncation.
+ */
+ SEL_ARG_LT(const uchar *key, Field *field)
+ :SEL_ARG_LE(key, field)
+ { max_flag= NEAR_MAX; }
+ /*
+ Use this constructor if value->save_in_field() returned success,
+ but we don't know if rounding or truncation happened
+ (as some Field::store() do not report minor data changes).
+ */
+ SEL_ARG_LT(THD *thd, const uchar *key, Field *field, Item *value)
+ :SEL_ARG_LE(key, field)
+ {
+ if (stored_field_cmp_to_item(thd, field, value) == 0)
+ max_flag= NEAR_MAX;
+ }
+};
+
+
+class SEL_ARG_GT: public SEL_ARG
+{
+public:
+ /*
+ Use this constructor if value->save_in_field() went precisely,
+ without any data rounding or truncation.
+ */
+ SEL_ARG_GT(const uchar *key, const KEY_PART *key_part, Field *field)
+ :SEL_ARG(field, key, key)
+ {
+ // Don't use open ranges for partial key_segments
+ if (!(key_part->flag & HA_PART_KEY_SEG))
+ min_flag= NEAR_MIN;
+ max_flag= NO_MAX_RANGE;
+ }
+ /*
+ Use this constructor if value->save_in_field() returned success,
+ but we don't know if rounding or truncation happened
+ (as some Field::store() do not report minor data changes).
+ */
+ SEL_ARG_GT(THD *thd, const uchar *key,
+ const KEY_PART *key_part, Field *field, Item *value)
+ :SEL_ARG(field, key, key)
+ {
+ // Don't use open ranges for partial key_segments
+ if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
+ (stored_field_cmp_to_item(thd, field, value) <= 0))
+ min_flag= NEAR_MIN;
+ max_flag= NO_MAX_RANGE;
+ }
+};
+
+
+class SEL_ARG_GE: public SEL_ARG
+{
+public:
+ /*
+ Use this constructor if value->save_in_field() went precisely,
+ without any data rounding or truncation.
+ */
+ SEL_ARG_GE(const uchar *key, Field *field)
+ :SEL_ARG(field, key, key)
+ {
+ max_flag= NO_MAX_RANGE;
+ }
+ /*
+ Use this constructor if value->save_in_field() returned success,
+ but we don't know if rounding or truncation happened
+ (as some Field::store() do not report minor data changes).
+ */
+ SEL_ARG_GE(THD *thd, const uchar *key,
+ const KEY_PART *key_part, Field *field, Item *value)
+ :SEL_ARG(field, key, key)
+ {
+ // Don't use open ranges for partial key_segments
+ if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
+ (stored_field_cmp_to_item(thd, field, value) < 0))
+ min_flag= NEAR_MIN;
+ max_flag= NO_MAX_RANGE;
+ }
+};
+
+
SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent,
SEL_ARG **next_arg)
{
@@ -2081,6 +2213,14 @@ public:
static void operator delete(void *ptr,size_t size) { TRASH_FREE(ptr, size); }
static void operator delete(void *ptr, MEM_ROOT *mem_root) { /* Never called */ }
virtual ~TABLE_READ_PLAN() {} /* Remove gcc warning */
+ /**
+ Add basic info for this TABLE_READ_PLAN to the optimizer trace.
+
+ @param param Parameters for range analysis of this table
+ @param trace_object The optimizer trace object the info is appended to
+ */
+ virtual void trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const= 0;
};
@@ -2122,8 +2262,32 @@ public:
}
DBUG_RETURN(quick);
}
+ void trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const;
};
+void TRP_RANGE::trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const
+{
+ DBUG_ASSERT(trace_object->trace_started());
+ DBUG_ASSERT(param->using_real_indexes);
+ const uint keynr_in_table= param->real_keynr[key_idx];
+
+ const KEY &cur_key= param->table->key_info[keynr_in_table];
+ const KEY_PART_INFO *key_part= cur_key.key_part;
+
+ trace_object->add("type", "range_scan")
+ .add("index", cur_key.name)
+ .add("rows", records);
+
+ Json_writer_array trace_range(param->thd, "ranges");
+
+ // TRP_RANGE should not be created if there are no range intervals
+ DBUG_ASSERT(key);
+
+ trace_ranges(&trace_range, param, key_idx, key, key_part);
+}
+
/* Plan for QUICK_ROR_INTERSECT_SELECT scan. */
@@ -2141,9 +2305,12 @@ public:
struct st_ror_scan_info *cpk_scan; /* Clustered PK scan, if there is one */
bool is_covering; /* TRUE if no row retrieval phase is necessary */
double index_scan_costs; /* SUM(cost(index_scan)) */
+ void trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const;
};
+
/*
Plan for QUICK_ROR_UNION_SELECT scan.
QUICK_ROR_UNION_SELECT always retrieves full rows, so retrieve_full_rows
@@ -2159,8 +2326,23 @@ public:
MEM_ROOT *parent_alloc);
TABLE_READ_PLAN **first_ror; /* array of ptrs to plans for merged scans */
TABLE_READ_PLAN **last_ror; /* end of the above array */
+ void trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const;
};
+void TRP_ROR_UNION::trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const
+{
+ THD *thd= param->thd;
+ DBUG_ASSERT(trace_object->trace_started());
+ trace_object->add("type", "index_roworder_union");
+ Json_writer_array smth_trace(thd, "union_of");
+ for (TABLE_READ_PLAN **current= first_ror; current != last_ror; current++)
+ {
+ Json_writer_object trp_info(thd);
+ (*current)->trace_basic_info(param, &trp_info);
+ }
+}
/*
Plan for QUICK_INDEX_INTERSECT_SELECT scan.
@@ -2178,9 +2360,26 @@ public:
TRP_RANGE **range_scans; /* array of ptrs to plans of intersected scans */
TRP_RANGE **range_scans_end; /* end of the array */
/* keys whose scans are to be filtered by cpk conditions */
- key_map filtered_scans;
+ key_map filtered_scans;
+ void trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const;
+
};
+void TRP_INDEX_INTERSECT::trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const
+{
+ THD *thd= param->thd;
+ DBUG_ASSERT(trace_object->trace_started());
+ trace_object->add("type", "index_sort_intersect");
+ Json_writer_array smth_trace(thd, "index_sort_intersect_of");
+ for (TRP_RANGE **current= range_scans; current != range_scans_end;
+ current++)
+ {
+ Json_writer_object trp_info(thd);
+ (*current)->trace_basic_info(param, &trp_info);
+ }
+}
/*
Plan for QUICK_INDEX_MERGE_SELECT scan.
@@ -2197,8 +2396,23 @@ public:
MEM_ROOT *parent_alloc);
TRP_RANGE **range_scans; /* array of ptrs to plans of merged scans */
TRP_RANGE **range_scans_end; /* end of the array */
+ void trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const;
};
+void TRP_INDEX_MERGE::trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const
+{
+ THD *thd= param->thd;
+ DBUG_ASSERT(trace_object->trace_started());
+ trace_object->add("type", "index_merge");
+ Json_writer_array smth_trace(thd, "index_merge_of");
+ for (TRP_RANGE **current= range_scans; current != range_scans_end; current++)
+ {
+ Json_writer_object trp_info(thd);
+ (*current)->trace_basic_info(param, &trp_info);
+ }
+}
/*
Plan for a QUICK_GROUP_MIN_MAX_SELECT scan.
@@ -2250,9 +2464,51 @@ public:
QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows,
MEM_ROOT *parent_alloc);
void use_index_scan() { is_index_scan= TRUE; }
+ void trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const;
};
+void TRP_GROUP_MIN_MAX::trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const
+{
+ THD *thd= param->thd;
+ DBUG_ASSERT(trace_object->trace_started());
+
+ trace_object->add("type", "index_group").add("index", index_info->name);
+
+ if (min_max_arg_part)
+ trace_object->add("min_max_arg", min_max_arg_part->field->field_name);
+ else
+ trace_object->add_null("min_max_arg");
+
+ trace_object->add("min_aggregate", have_min)
+ .add("max_aggregate", have_max)
+ .add("distinct_aggregate", have_agg_distinct)
+ .add("rows", records)
+ .add("cost", read_cost);
+
+ const KEY_PART_INFO *key_part= index_info->key_part;
+ {
+ Json_writer_array trace_keyparts(thd, "key_parts_used_for_access");
+ for (uint partno= 0; partno < used_key_parts; partno++)
+ {
+ const KEY_PART_INFO *cur_key_part= key_part + partno;
+ trace_keyparts.add(cur_key_part->field->field_name);
+ }
+ }
+
+ Json_writer_array trace_range(thd, "ranges");
+
+ // can have group quick without ranges
+ if (index_tree)
+ {
+ trace_ranges(&trace_range, param, param_idx,
+ index_tree, key_part);
+ }
+}
+
+
typedef struct st_index_scan_info
{
uint idx; /* # of used key in param->keys */
@@ -2340,6 +2596,9 @@ static int fill_used_fields_bitmap(PARAM *param)
limit Query limit
force_quick_range Prefer to use range (instead of full table scan) even
if it is more expensive.
+ remove_false_parts_of_where Remove parts of OR-clauses for which range
+ analysis produced SEL_TREE(IMPOSSIBLE)
+ only_single_index_range_scan Evaluate only single index range scans
NOTES
Updates the following in the select parameter:
@@ -2398,7 +2657,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
table_map prev_tables,
ha_rows limit, bool force_quick_range,
bool ordered_output,
- bool remove_false_parts_of_where)
+ bool remove_false_parts_of_where,
+ bool only_single_index_range_scan)
{
uint idx;
double scan_time;
@@ -2411,6 +2671,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
quick=0;
needed_reg.clear_all();
quick_keys.clear_all();
+ head->with_impossible_ranges.clear_all();
DBUG_ASSERT(!head->is_filled_at_execution());
if (keys_to_use.is_clear_all() || head->is_filled_at_execution())
DBUG_RETURN(0);
@@ -2432,6 +2693,15 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
DBUG_PRINT("info",("Time to scan table: %g", read_time));
+ Json_writer_object table_records(thd);
+ table_records.add_table_name(head);
+
+ Json_writer_object trace_range(thd, "range_analysis");
+ {
+ Json_writer_object table_rec(thd, "table_scan");
+ table_rec.add("rows", records).add("cost", read_time);
+ }
+
keys_to_use.intersect(head->keys_in_use_for_query);
if (!keys_to_use.is_clear_all())
{
@@ -2460,7 +2730,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
param.imerge_cost_buff_size= 0;
param.using_real_indexes= TRUE;
param.remove_jump_scans= TRUE;
- param.is_ror_scan= 0;
+ param.max_key_parts= 0;
param.remove_false_where_parts= remove_false_parts_of_where;
param.force_default_mrr= ordered_output;
param.possible_keys.clear_all();
@@ -2487,19 +2757,33 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
*/
key_info= head->key_info;
uint max_key_len= 0;
+
+ Json_writer_array trace_idx(thd, "potential_range_indexes");
+
for (idx=0 ; idx < head->s->keys ; idx++, key_info++)
{
+ Json_writer_object trace_idx_details(thd);
+ trace_idx_details.add("index", key_info->name);
KEY_PART_INFO *key_part_info;
uint n_key_parts= head->actual_n_key_parts(key_info);
if (!keys_to_use.is_set(idx))
- continue;
+ {
+ trace_idx_details.add("usable", false)
+ .add("cause", "not applicable");
+ continue;
+ }
if (key_info->flags & HA_FULLTEXT)
- continue; // ToDo: ft-keys in non-ft ranges, if possible SerG
+ {
+ trace_idx_details.add("usable", false).add("cause", "fulltext");
+ continue; // ToDo: ft-keys in non-ft ranges, if possible SerG
+ }
+ trace_idx_details.add("usable", true);
param.key[param.keys]=key_parts;
key_part_info= key_info->key_part;
uint cur_key_len= 0;
+ Json_writer_array trace_keypart(thd, "key_parts");
for (uint part= 0 ; part < n_key_parts ;
part++, key_parts++, key_part_info++)
{
@@ -2514,11 +2798,14 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
(key_info->flags & HA_SPATIAL) ? Field::itMBR : Field::itRAW;
/* Only HA_PART_KEY_SEG is used */
key_parts->flag= (uint8) key_part_info->key_part_flag;
+ trace_keypart.add(key_parts->field->field_name);
}
param.real_keynr[param.keys++]=idx;
if (cur_key_len > max_key_len)
max_key_len= cur_key_len;
}
+ trace_idx.end();
+
param.key_parts_end=key_parts;
param.alloced_sel_args= 0;
@@ -2536,26 +2823,42 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
if (!force_quick_range && !head->covering_keys.is_clear_all())
{
int key_for_use= find_shortest_key(head, &head->covering_keys);
- double key_read_time= head->file->keyread_time(key_for_use, 1, records) +
- (double) records / TIME_FOR_COMPARE;
+ double key_read_time= head->file->key_scan_time(key_for_use) +
+ (double) records / TIME_FOR_COMPARE_IDX;
DBUG_PRINT("info", ("'all'+'using index' scan will be using key %d, "
"read time %g", key_for_use, key_read_time));
+
+ Json_writer_object trace_cov(thd, "best_covering_index_scan");
+ bool chosen= FALSE;
if (key_read_time < read_time)
+ {
read_time= key_read_time;
+ chosen= TRUE;
+ }
+ trace_cov.add("index", head->key_info[key_for_use].name)
+ .add("cost", key_read_time).add("chosen", chosen);
+ if (!chosen)
+ trace_cov.add("cause", "cost");
}
TABLE_READ_PLAN *best_trp= NULL;
- TRP_GROUP_MIN_MAX *group_trp;
+ TRP_GROUP_MIN_MAX *group_trp= NULL;
double best_read_time= read_time;
if (cond)
{
- if ((tree= cond->get_mm_tree(&param, &cond)))
+ {
+ Json_writer_array trace_range_summary(thd,
+ "setup_range_conditions");
+ tree= cond->get_mm_tree(&param, &cond);
+ }
+ if (tree)
{
if (tree->type == SEL_TREE::IMPOSSIBLE)
{
records=0L; /* Return -1 from this function. */
read_time= (double) HA_POS_ERROR;
+ trace_range.add("impossible_range", true);
goto free_mem;
}
/*
@@ -2563,7 +2866,10 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
can construct a group-min-max quick select
*/
if (tree->type != SEL_TREE::KEY && tree->type != SEL_TREE::KEY_SMALLER)
+ {
+ trace_range.add("range_scan_possible", false);
tree= NULL;
+ }
}
else if (thd->is_error())
{
@@ -2579,13 +2885,20 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
Notice that it can be constructed no matter if there is a range tree.
*/
DBUG_EXECUTE_IF("force_group_by", force_group_by = true; );
- group_trp= get_best_group_min_max(&param, tree, best_read_time);
+ if (!only_single_index_range_scan)
+ group_trp= get_best_group_min_max(&param, tree, best_read_time);
if (group_trp)
{
param.table->quick_condition_rows= MY_MIN(group_trp->records,
head->stat_records());
+ Json_writer_object grp_summary(thd, "best_group_range_summary");
+
+ if (unlikely(thd->trace_started()))
+ group_trp->trace_basic_info(&param, &grp_summary);
+
if (group_trp->read_cost < best_read_time || force_group_by)
{
+ grp_summary.add("chosen", true);
best_trp= group_trp;
best_read_time= best_trp->read_cost;
if (force_group_by)
@@ -2593,6 +2906,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
goto force_plan;
}
}
+ else
+ grp_summary.add("chosen", false).add("cause", "cost");
}
if (tree)
@@ -2601,16 +2916,17 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
It is possible to use a range-based quick select (but it might be
slower than 'all' table scan).
*/
- TRP_RANGE *range_trp;
TRP_ROR_INTERSECT *rori_trp;
TRP_INDEX_INTERSECT *intersect_trp;
bool can_build_covering= FALSE;
-
+ Json_writer_object trace_range(thd, "analyzing_range_alternatives");
+
remove_nonrange_trees(&param, tree);
/* Get best 'range' plan and prepare data for making other plans */
- if ((range_trp= get_key_scans_params(&param, tree, FALSE, TRUE,
- best_read_time)))
+ if (auto range_trp= get_key_scans_params(&param, tree,
+ only_single_index_range_scan,
+ true, best_read_time))
{
best_trp= range_trp;
best_read_time= best_trp->read_cost;
@@ -2622,7 +2938,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
table deletes.
*/
if ((thd->lex->sql_command != SQLCOM_DELETE) &&
- optimizer_flag(thd, OPTIMIZER_SWITCH_INDEX_MERGE))
+ optimizer_flag(thd, OPTIMIZER_SWITCH_INDEX_MERGE) &&
+ !only_single_index_range_scan)
{
/*
Get best non-covering ROR-intersection plan and prepare data for
@@ -2650,7 +2967,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
*/
if (param.table->covering_keys.is_clear_all() &&
optimizer_flag(thd, OPTIMIZER_SWITCH_INDEX_MERGE) &&
- optimizer_flag(thd, OPTIMIZER_SWITCH_INDEX_MERGE_SORT_INTERSECT))
+ optimizer_flag(thd, OPTIMIZER_SWITCH_INDEX_MERGE_SORT_INTERSECT) &&
+ !only_single_index_range_scan)
{
if ((intersect_trp= get_best_index_intersect(&param, tree,
best_read_time)))
@@ -2663,7 +2981,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
}
if (optimizer_flag(thd, OPTIMIZER_SWITCH_INDEX_MERGE) &&
- head->stat_records() != 0)
+ head->stat_records() != 0 && !only_single_index_range_scan)
{
/* Try creating index_merge/ROR-union scan. */
SEL_IMERGE *imerge;
@@ -2672,6 +2990,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
DBUG_PRINT("info",("No range reads possible,"
" trying to construct index_merge"));
List_iterator_fast<SEL_IMERGE> it(tree->merges);
+ Json_writer_array trace_idx_merge(thd, "analyzing_index_merge_union");
while ((imerge= it++))
{
new_conj_trp= get_best_disjunct_quick(&param, imerge, best_read_time);
@@ -2707,6 +3026,19 @@ force_plan:
possible_keys= param.possible_keys;
free_mem:
+ if (unlikely(quick && best_trp && thd->trace_started()))
+ {
+ Json_writer_object trace_range_summary(thd,
+ "chosen_range_access_summary");
+ {
+ Json_writer_object trace_range_plan(thd, "range_access_plan");
+ best_trp->trace_basic_info(&param, &trace_range_plan);
+ }
+ trace_range_summary.add("rows_for_plan", quick->records)
+ .add("cost_for_plan", quick->read_time)
+ .add("chosen", true);
+ }
+
free_root(&alloc,MYF(0)); // Return memory & allocator
thd->mem_root= param.old_root;
thd->no_errors=0;
@@ -2851,6 +3183,7 @@ static
double records_in_column_ranges(PARAM *param, uint idx,
SEL_ARG *tree)
{
+ THD *thd= param->thd;
SEL_ARG_RANGE_SEQ seq;
KEY_MULTI_RANGE range;
range_seq_t seq_it;
@@ -2873,16 +3206,36 @@ double records_in_column_ranges(PARAM *param, uint idx,
seq.real_keyno= MAX_KEY;
seq.param= param;
seq.start= tree;
+ seq.is_ror_scan= FALSE;
seq_it= seq_if.init((void *) &seq, 0, flags);
+ Json_writer_array range_trace(thd, "ranges");
+
while (!seq_if.next(seq_it, &range))
{
key_range *min_endp, *max_endp;
min_endp= range.start_key.length? &range.start_key : NULL;
max_endp= range.end_key.length? &range.end_key : NULL;
- rows= get_column_range_cardinality(field, min_endp, max_endp,
- range.range_flag);
+ int range_flag= range.range_flag;
+
+ if (!range.start_key.length)
+ range_flag |= NO_MIN_RANGE;
+ if (!range.end_key.length)
+ range_flag |= NO_MAX_RANGE;
+ if (range.start_key.flag == HA_READ_AFTER_KEY)
+ range_flag |= NEAR_MIN;
+ if (range.start_key.flag == HA_READ_BEFORE_KEY)
+ range_flag |= NEAR_MAX;
+
+ if (unlikely(thd->trace_started()))
+ {
+ StringBuffer<128> range_info(system_charset_info);
+ print_range_for_non_indexed_field(&range_info, field, &range);
+ range_trace.add(range_info.c_ptr_safe(), range_info.length());
+ }
+
+ rows= get_column_range_cardinality(field, min_endp, max_endp, range_flag);
if (DBL_MAX == rows)
{
total_rows= DBL_MAX;
@@ -2969,6 +3322,9 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
estimate sources.
*/
+ Json_writer_object trace_wrapper(thd);
+ Json_writer_array selectivity_for_indexes(thd, "selectivity_for_indexes");
+
for (keynr= 0; keynr < table->s->keys; keynr++)
{
if (table->quick_keys.is_set(keynr))
@@ -3018,6 +3374,10 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
not yet been accounted for.
*/
table->cond_selectivity*= quick_cond_selectivity;
+ Json_writer_object selectivity_for_index(thd);
+ selectivity_for_index.add("index_name", key_info->name)
+ .add("selectivity_from_index",
+ quick_cond_selectivity);
if (i != used_key_parts)
{
/*
@@ -3037,7 +3397,9 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
*/
selectivity_mult= ((double)(i+1)) / i;
}
- table->cond_selectivity*= selectivity_mult;
+ table->cond_selectivity*= selectivity_mult;
+ selectivity_for_index.add("selectivity_multiplier",
+ selectivity_mult);
}
/*
We need to set selectivity for fields supported by indexes.
@@ -3058,12 +3420,14 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
}
}
}
+ selectivity_for_indexes.end();
/*
Second step: calculate the selectivity of the range conditions not
supported by any index and selectivity of the range condition
over the fields whose selectivity has not been set yet.
*/
+ Json_writer_array selectivity_for_columns(thd, "selectivity_for_columns");
if (thd->variables.optimizer_use_condition_selectivity > 2 &&
!bitmap_is_clear_all(used_fields) &&
@@ -3081,7 +3445,6 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
param.mem_root= &alloc;
param.old_root= thd->mem_root;
param.table= table;
- param.is_ror_scan= FALSE;
param.remove_false_where_parts= true;
if (create_key_parts_for_pseudo_indexes(&param, used_fields))
@@ -3092,6 +3455,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
param.using_real_indexes= FALSE;
param.real_keynr[0]= 0;
param.alloced_sel_args= 0;
+ param.max_key_parts= 0;
thd->no_errors=1;
@@ -3123,17 +3487,25 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
SEL_ARG *key= tree->keys[idx];
if (key)
{
+ Json_writer_object selectivity_for_column(thd);
+ selectivity_for_column.add("column_name", key->field->field_name);
if (key->type == SEL_ARG::IMPOSSIBLE)
- {
+ {
rows= 0;
table->reginfo.impossible_range= 1;
+ selectivity_for_column.add("selectivity_from_histogram", rows);
+ selectivity_for_column.add("cause", "impossible range");
goto free_alloc;
}
else
{
rows= records_in_column_ranges(&param, idx, key);
if (rows != DBL_MAX)
+ {
key->field->cond_selectivity= rows/table_records;
+ selectivity_for_column.add("selectivity_from_histogram",
+ key->field->cond_selectivity);
+ }
}
}
}
@@ -3155,6 +3527,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
free_root(&alloc, MYF(0));
}
+ selectivity_for_columns.end();
if (quick && (quick->get_type() == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
quick->get_type() == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE))
@@ -3229,7 +3602,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
table->cond_selectivity_sampling_explain= &dt->list;
}
}
-
+ trace_wrapper.add("cond_selectivity", table->cond_selectivity);
DBUG_RETURN(FALSE);
}
@@ -4560,7 +4933,8 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records)
{
double result;
DBUG_ENTER("get_sweep_read_cost");
- if (param->table->file->primary_key_is_clustered())
+ if (param->table->file->primary_key_is_clustered() ||
+ param->table->file->stats.block_size == 0 /* HEAP */)
{
/*
We are using the primary key to find the rows.
@@ -4590,7 +4964,7 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records)
if (max_cost != DBL_MAX && (busy_blocks+index_reads_cost) >= n_blocks)
return 1;
*/
- JOIN *join= param->thd->lex->select_lex.join;
+ JOIN *join= param->thd->lex->first_select_lex()->join;
if (!join || join->table_count == 1)
{
/* No join, assume reading is done in one 'sweep' */
@@ -4699,6 +5073,8 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
ha_rows roru_total_records;
double roru_intersect_part= 1.0;
size_t n_child_scans;
+ double limit_read_time= read_time;
+ THD *thd= param->thd;
DBUG_ENTER("get_best_disjunct_quick");
DBUG_PRINT("info", ("Full table scan cost: %g", read_time));
@@ -4725,6 +5101,8 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
n_child_scans)))
DBUG_RETURN(NULL);
+ Json_writer_object trace_best_disjunct(thd);
+ Json_writer_array to_merge(thd, "indexes_to_merge");
/*
Collect best 'range' scan for each of disjuncts, and, while doing so,
analyze possibility of ROR scans. Also calculate some values needed by
@@ -4736,6 +5114,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
{
DBUG_EXECUTE("info", print_sel_tree(param, *ptree, &(*ptree)->keys_map,
"tree in SEL_IMERGE"););
+ Json_writer_object trace_idx(thd);
if (!(*cur_child= get_key_scans_params(param, *ptree, TRUE, FALSE,
read_time)))
{
@@ -4748,8 +5127,11 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
imerge_too_expensive= TRUE;
}
if (imerge_too_expensive)
+ {
+ trace_idx.add("chosen", false).add("cause", "cost");
continue;
-
+ }
+ const uint keynr_in_table= param->real_keynr[(*cur_child)->key_idx];
imerge_cost += (*cur_child)->read_cost;
all_scans_ror_able &= ((*ptree)->n_ror_scans > 0);
all_scans_rors &= (*cur_child)->is_ror;
@@ -4762,9 +5144,16 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
}
else
non_cpk_scan_records += (*cur_child)->records;
+ trace_idx.add("index_to_merge",
+ param->table->key_info[keynr_in_table].name)
+ .add("cumulated_cost", imerge_cost);
}
+ to_merge.end();
+
DBUG_PRINT("info", ("index_merge scans cost %g", imerge_cost));
+ trace_best_disjunct.add("cost_of_reading_ranges", imerge_cost);
+
if (imerge_too_expensive || (imerge_cost > read_time) ||
((non_cpk_scan_records+cpk_scan_records >=
param->table->stat_records()) &&
@@ -4776,6 +5165,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
*/
DBUG_PRINT("info", ("Sum of index_merge scans is more expensive than "
"full table scan, bailing out"));
+ trace_best_disjunct.add("chosen", false).add("cause", "cost");
DBUG_RETURN(NULL);
}
@@ -4788,6 +5178,9 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
optimizer_flag(param->thd, OPTIMIZER_SWITCH_INDEX_MERGE_UNION))
{
roru_read_plans= (TABLE_READ_PLAN**)range_scans;
+ trace_best_disjunct.add("use_roworder_union", true)
+ .add("cause",
+ "always cheaper than non roworder retrieval");
goto skip_to_ror_scan;
}
@@ -4797,16 +5190,26 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
Add one ROWID comparison for each row retrieved on non-CPK scan. (it
is done in QUICK_RANGE_SELECT::row_in_ranges)
*/
- imerge_cost += non_cpk_scan_records / TIME_FOR_COMPARE_ROWID;
+ double rid_comp_cost= static_cast<double>(non_cpk_scan_records) /
+ TIME_FOR_COMPARE_ROWID;
+ imerge_cost+= rid_comp_cost;
+ trace_best_disjunct.add("cost_of_mapping_rowid_in_non_clustered_pk_scan",
+ rid_comp_cost);
}
/* Calculate cost(rowid_to_row_scan) */
- imerge_cost += get_sweep_read_cost(param, non_cpk_scan_records);
+ {
+ double sweep_cost= get_sweep_read_cost(param, non_cpk_scan_records);
+ imerge_cost+= sweep_cost;
+ trace_best_disjunct.add("cost_sort_rowid_and_read_disk", sweep_cost);
+ }
DBUG_PRINT("info",("index_merge cost with rowid-to-row scan: %g",
imerge_cost));
if (imerge_cost > read_time ||
!optimizer_flag(param->thd, OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION))
{
+ trace_best_disjunct.add("use_roworder_index_merge", true);
+ trace_best_disjunct.add("cause", "cost");
goto build_ror_index_merge;
}
@@ -4823,12 +5226,18 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
param->imerge_cost_buff_size= unique_calc_buff_size;
}
- imerge_cost +=
- Unique::get_use_cost(param->imerge_cost_buff, (uint)non_cpk_scan_records,
- param->table->file->ref_length,
- (size_t)param->thd->variables.sortbuff_size,
- TIME_FOR_COMPARE_ROWID,
- FALSE, NULL);
+ {
+ const double dup_removal_cost= Unique::get_use_cost(
+ param->imerge_cost_buff, (uint)non_cpk_scan_records,
+ param->table->file->ref_length,
+ (size_t)param->thd->variables.sortbuff_size,
+ TIME_FOR_COMPARE_ROWID,
+ FALSE, NULL);
+ imerge_cost+= dup_removal_cost;
+ trace_best_disjunct.add("cost_duplicate_removal", dup_removal_cost)
+ .add("total_cost", imerge_cost);
+ }
+
DBUG_PRINT("info",("index_merge total cost: %g (wanted: less then %g)",
imerge_cost, read_time));
if (imerge_cost < read_time)
@@ -4846,7 +5255,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
if (imerge_trp)
{
TABLE_READ_PLAN *trp= merge_same_index_scans(param, imerge, imerge_trp,
- read_time);
+ limit_read_time);
if (trp != imerge_trp)
DBUG_RETURN(trp);
}
@@ -4871,11 +5280,16 @@ skip_to_ror_scan:
roru_total_records= 0;
cur_roru_plan= roru_read_plans;
+ Json_writer_array trace_analyze_ror(thd, "analyzing_roworder_scans");
+
/* Find 'best' ROR scan for each of trees in disjunction */
for (ptree= imerge->trees, cur_child= range_scans;
ptree != imerge->trees_next;
ptree++, cur_child++, cur_roru_plan++)
{
+ Json_writer_object trp_info(thd);
+ if (unlikely(thd->trace_started()))
+ (*cur_child)->trace_basic_info(param, &trp_info);
/*
Assume the best ROR scan is the one that has cheapest full-row-retrieval
scan cost.
@@ -4911,7 +5325,7 @@ skip_to_ror_scan:
roru_intersect_part *= (*cur_roru_plan)->records /
param->table->stat_records();
}
-
+ trace_analyze_ror.end();
/*
rows to retrieve=
SUM(rows_in_scan_i) - table_rows * PROD(rows_in_scan_i / table_rows).
@@ -4937,11 +5351,14 @@ skip_to_ror_scan:
DBUG_PRINT("info", ("ROR-union: cost %g, %zu members",
roru_total_cost, n_child_scans));
+ trace_best_disjunct.add("index_roworder_union_cost", roru_total_cost)
+ .add("members", n_child_scans);
TRP_ROR_UNION* roru;
if (roru_total_cost < read_time)
{
if ((roru= new (param->mem_root) TRP_ROR_UNION))
{
+ trace_best_disjunct.add("chosen", true);
roru->first_ror= roru_read_plans;
roru->last_ror= roru_read_plans + n_child_scans;
roru->read_cost= roru_total_cost;
@@ -4949,7 +5366,9 @@ skip_to_ror_scan:
DBUG_RETURN(roru);
}
}
- DBUG_RETURN(imerge_trp);
+ else
+ trace_best_disjunct.add("chosen", false);
+ DBUG_RETURN(imerge_trp);
}
@@ -5140,7 +5559,6 @@ typedef struct st_partial_index_intersect_info
intersect_fields= NULL;
records_sent_to_unique= records= length= in_memory= use_cpk_filter= 0;
cost= index_read_cost= in_memory_cost= 0.0;
- filtered_scans.init();
filtered_scans.clear_all();
}
} PARTIAL_INDEX_INTERSECT_INFO;
@@ -5223,6 +5641,17 @@ ha_rows get_table_cardinality_for_index_intersect(TABLE *table)
}
}
+static
+void print_keyparts(THD *thd, KEY *key, uint key_parts)
+{
+ DBUG_ASSERT(thd->trace_started());
+
+ KEY_PART_INFO *part= key->key_part;
+ Json_writer_array keyparts= Json_writer_array(thd, "keyparts");
+ for(uint i= 0; i < key_parts; i++, part++)
+ keyparts.add(part->field->field_name);
+}
+
static
ha_rows records_in_index_intersect_extension(PARTIAL_INDEX_INTERSECT_INFO *curr,
@@ -5275,8 +5704,9 @@ bool prepare_search_best_index_intersect(PARAM *param,
INDEX_SCAN_INFO *cpk_scan= NULL;
TABLE *table= param->table;
uint n_index_scans= (uint)(tree->index_scans_end - tree->index_scans);
+ THD *thd= param->thd;
- if (!n_index_scans)
+ if (n_index_scans <= 1)
return 1;
init->init();
@@ -5292,9 +5722,6 @@ bool prepare_search_best_index_intersect(PARAM *param,
common->table_cardinality=
get_table_cardinality_for_index_intersect(table);
- if (n_index_scans <= 1)
- return TRUE;
-
if (table->file->primary_key_is_clustered())
{
INDEX_SCAN_INFO **index_scan_end;
@@ -5319,23 +5746,38 @@ bool prepare_search_best_index_intersect(PARAM *param,
bzero(common->search_scans, sizeof(INDEX_SCAN_INFO *) * i);
INDEX_SCAN_INFO **selected_index_scans= common->search_scans;
-
+ Json_writer_array potential_idx_scans(thd, "potential_index_scans");
for (i=0, index_scan= tree->index_scans; i < n_index_scans; i++, index_scan++)
{
+ Json_writer_object idx_scan(thd);
uint used_key_parts= (*index_scan)->used_key_parts;
KEY *key_info= (*index_scan)->key_info;
+ idx_scan.add("index", key_info->name);
if (*index_scan == cpk_scan)
+ {
+ idx_scan.add("chosen", "false")
+ .add("cause", "clustered index used for filtering");
continue;
+ }
if (cpk_scan && cpk_scan->used_key_parts >= used_key_parts &&
same_index_prefix(cpk_scan->key_info, key_info, used_key_parts))
+ {
+ idx_scan.add("chosen", "false")
+ .add("cause", "clustered index used for filtering");
continue;
+ }
+
+ cost= table->quick_index_only_costs[(*index_scan)->keynr];
+
+ idx_scan.add("cost", cost);
- cost= table->file->keyread_time((*index_scan)->keynr,
- (*index_scan)->range_count,
- (*index_scan)->records);
if (cost >= cutoff_cost)
+ {
+ idx_scan.add("chosen", false);
+ idx_scan.add("cause", "cost");
continue;
+ }
for (scan_ptr= selected_index_scans; *scan_ptr ; scan_ptr++)
{
@@ -5352,10 +5794,20 @@ bool prepare_search_best_index_intersect(PARAM *param,
}
if (!*scan_ptr || cost < (*scan_ptr)->index_read_cost)
{
+ idx_scan.add("chosen", true);
+ if (!*scan_ptr)
+ idx_scan.add("cause", "first occurence of index prefix");
+ else
+ idx_scan.add("cause", "better cost for same idx prefix");
*scan_ptr= *index_scan;
(*scan_ptr)->index_read_cost= cost;
}
- }
+ else
+ {
+ idx_scan.add("chosen", false).add("cause", "cost");
+ }
+ }
+ potential_idx_scans.end();
ha_rows records_in_scans= 0;
@@ -5365,6 +5817,7 @@ bool prepare_search_best_index_intersect(PARAM *param,
return TRUE;
records_in_scans+= (*scan_ptr)->records;
}
+
n_search_scans= i;
if (cpk_scan && create_fields_bitmap(param, &cpk_scan->used_fields))
@@ -5394,6 +5847,7 @@ bool prepare_search_best_index_intersect(PARAM *param,
my_qsort(selected_index_scans, n_search_scans, sizeof(INDEX_SCAN_INFO *),
(qsort_cmp) cmp_intersect_index_scan);
+ Json_writer_array selected_idx_scans(thd, "selected_index_scans");
if (cpk_scan)
{
PARTIAL_INDEX_INTERSECT_INFO curr;
@@ -5406,16 +5860,36 @@ bool prepare_search_best_index_intersect(PARAM *param,
curr.length= 1;
for (scan_ptr=selected_index_scans; *scan_ptr; scan_ptr++)
{
+ KEY *key_info= (*scan_ptr)->key_info;
ha_rows scan_records= (*scan_ptr)->records;
ha_rows records= records_in_index_intersect_extension(&curr, *scan_ptr);
(*scan_ptr)->filtered_out= records >= scan_records ?
- 0 : scan_records-records;
+ 0 : scan_records-records;
+ if (thd->trace_started())
+ {
+ Json_writer_object selected_idx(thd);
+ selected_idx.add("index", key_info->name);
+ print_keyparts(thd, key_info, (*scan_ptr)->used_key_parts);
+ selected_idx.add("records", (*scan_ptr)->records)
+ .add("filtered_records", (*scan_ptr)->filtered_out);
+ }
}
}
else
{
for (scan_ptr=selected_index_scans; *scan_ptr; scan_ptr++)
+ {
+ KEY *key_info= (*scan_ptr)->key_info;
(*scan_ptr)->filtered_out= 0;
+ if (thd->trace_started())
+ {
+ Json_writer_object selected_idx(thd);
+ selected_idx.add("index", key_info->name);
+ print_keyparts(thd, key_info, (*scan_ptr)->used_key_parts);
+ selected_idx.add("records", (*scan_ptr)->records)
+ .add("filtered_records", (*scan_ptr)->filtered_out);
+ }
+ }
}
return FALSE;
@@ -5872,10 +6346,12 @@ TRP_INDEX_INTERSECT *get_best_index_intersect(PARAM *param, SEL_TREE *tree,
PARTIAL_INDEX_INTERSECT_INFO init;
TRP_INDEX_INTERSECT *intersect_trp= NULL;
TABLE *table= param->table;
-
+ THD *thd= param->thd;
DBUG_ENTER("get_best_index_intersect");
+ Json_writer_object trace_idx_interect(thd, "analyzing_sort_intersect");
+
if (prepare_search_best_index_intersect(param, tree, &common, &init,
read_time))
DBUG_RETURN(NULL);
@@ -5937,11 +6413,15 @@ TRP_INDEX_INTERSECT *get_best_index_intersect(PARAM *param, SEL_TREE *tree,
if ((intersect_trp= new (param->mem_root)TRP_INDEX_INTERSECT))
{
+
intersect_trp->read_cost= common.best_cost;
intersect_trp->records= common.best_records;
intersect_trp->range_scans= range_scans;
intersect_trp->range_scans_end= cur_range;
intersect_trp->filtered_scans= common.filtered_scans;
+ trace_idx_interect.add("rows", intersect_trp->records)
+ .add("cost", intersect_trp->read_cost)
+ .add("chosen",true);
}
DBUG_RETURN(intersect_trp);
}
@@ -5951,6 +6431,37 @@ typedef struct st_ror_scan_info : INDEX_SCAN_INFO
{
} ROR_SCAN_INFO;
+void TRP_ROR_INTERSECT::trace_basic_info(PARAM *param,
+ Json_writer_object *trace_object) const
+{
+ THD *thd= param->thd;
+ DBUG_ASSERT(trace_object->trace_started());
+
+ trace_object->add("type", "index_roworder_intersect");
+ trace_object->add("rows", records);
+ trace_object->add("cost", read_cost);
+ trace_object->add("covering", is_covering);
+ trace_object->add("clustered_pk_scan", cpk_scan != NULL);
+
+ Json_writer_array smth_trace(thd, "intersect_of");
+ for (ROR_SCAN_INFO **cur_scan= first_scan; cur_scan != last_scan;
+ cur_scan++)
+ {
+ const KEY &cur_key= param->table->key_info[(*cur_scan)->keynr];
+ const KEY_PART_INFO *key_part= cur_key.key_part;
+
+ Json_writer_object trace_isect_idx(thd);
+ trace_isect_idx.add("type", "range_scan");
+ trace_isect_idx.add("index", cur_key.name);
+ trace_isect_idx.add("rows", (*cur_scan)->records);
+
+ Json_writer_array trace_range(thd, "ranges");
+
+ trace_ranges(&trace_range, param, (*cur_scan)->idx,
+ (*cur_scan)->sel_arg, key_part);
+ }
+}
+
/*
Create ROR_SCAN_INFO* structure with a single ROR scan on index idx using
@@ -6336,7 +6847,9 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info,
*/
static bool ror_intersect_add(ROR_INTERSECT_INFO *info,
- ROR_SCAN_INFO* ror_scan, bool is_cpk_scan)
+ ROR_SCAN_INFO* ror_scan,
+ Json_writer_object *trace_costs,
+ bool is_cpk_scan)
{
double selectivity_mult= 1.0;
@@ -6363,13 +6876,16 @@ static bool ror_intersect_add(ROR_INTERSECT_INFO *info,
each record of every scan. Assuming 1/TIME_FOR_COMPARE_ROWID
per check this gives us:
*/
- info->index_scan_costs += rows2double(info->index_records) /
+ const double idx_cost= rows2double(info->index_records) /
TIME_FOR_COMPARE_ROWID;
+ info->index_scan_costs+= idx_cost;
+ trace_costs->add("index_scan_cost", idx_cost);
}
else
{
info->index_records += info->param->quick_rows[ror_scan->keynr];
info->index_scan_costs += ror_scan->index_read_cost;
+ trace_costs->add("index_scan_cost", ror_scan->index_read_cost);
bitmap_union(&info->covered_fields, &ror_scan->covered_fields);
if (!info->is_covering && bitmap_is_subset(&info->param->needed_fields,
&info->covered_fields))
@@ -6380,13 +6896,19 @@ static bool ror_intersect_add(ROR_INTERSECT_INFO *info,
}
info->total_cost= info->index_scan_costs;
+ trace_costs->add("cumulated_index_scan_cost", info->index_scan_costs);
DBUG_PRINT("info", ("info->total_cost: %g", info->total_cost));
if (!info->is_covering)
{
- info->total_cost +=
- get_sweep_read_cost(info->param, double2rows(info->out_rows));
+ double sweep_cost= get_sweep_read_cost(info->param,
+ double2rows(info->out_rows));
+ info->total_cost+= sweep_cost;
+ trace_costs->add("disk_sweep_cost", sweep_cost);
DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost));
}
+ else
+ trace_costs->add("disk_sweep_cost", static_cast<longlong>(0));
+
DBUG_PRINT("info", ("New out_rows: %g", info->out_rows));
DBUG_PRINT("info", ("New cost: %g, %scovering", info->total_cost,
info->is_covering?"" : "non-"));
@@ -6466,10 +6988,16 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
uint idx;
double min_cost= DBL_MAX;
DBUG_ENTER("get_best_ror_intersect");
+ THD *thd= param->thd;
+ Json_writer_object trace_ror(thd, "analyzing_roworder_intersect");
if ((tree->n_ror_scans < 2) || !param->table->stat_records() ||
!optimizer_flag(param->thd, OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT))
- DBUG_RETURN(NULL);
+ {
+ if (tree->n_ror_scans < 2)
+ trace_ror.add("cause", "too few roworder scans");
+ DBUG_RETURN(NULL);
+ }
/*
Step1: Collect ROR-able SEL_ARGs and create ROR_SCAN_INFO for each of
@@ -6544,15 +7072,27 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
ROR_SCAN_INFO **intersect_scans_best;
cur_ror_scan= tree->ror_scans;
intersect_scans_best= intersect_scans;
+ Json_writer_array trace_isect_idx(thd, "intersecting_indexes");
while (cur_ror_scan != tree->ror_scans_end && !intersect->is_covering)
{
+ Json_writer_object trace_idx(thd);
+ trace_idx.add("index",
+ param->table->key_info[(*cur_ror_scan)->keynr].name);
+
/* S= S + first(R); R= R - first(R); */
- if (!ror_intersect_add(intersect, *cur_ror_scan, FALSE))
+ if (!ror_intersect_add(intersect, *cur_ror_scan, &trace_idx, FALSE))
{
+ trace_idx.add("usable", false)
+ .add("cause", "does not reduce cost of intersect");
cur_ror_scan++;
continue;
}
+ trace_idx.add("cumulative_total_cost", intersect->total_cost)
+ .add("usable", true)
+ .add("matching_rows_now", intersect->out_rows)
+ .add("intersect_covering_with_this_index", intersect->is_covering);
+
*(intersect_scans_end++)= *(cur_ror_scan++);
if (intersect->total_cost < min_cost)
@@ -6561,12 +7101,21 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
ror_intersect_cpy(intersect_best, intersect);
intersect_scans_best= intersect_scans_end;
min_cost = intersect->total_cost;
+ trace_idx.add("chosen", true);
+ }
+ else
+ {
+ trace_idx.add("chosen", false)
+ .add("cause", "does not reduce cost");
}
}
+ trace_isect_idx.end();
if (intersect_scans_best == intersect_scans)
{
DBUG_PRINT("info", ("None of scans increase selectivity"));
+ trace_ror.add("chosen", false)
+ .add("cause","does not increase selectivity");
DBUG_RETURN(NULL);
}
@@ -6584,16 +7133,31 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
Check if we should add a CPK scan. If the obtained ROR-intersection is
covering, it doesn't make sense to add CPK scan.
*/
+ Json_writer_object trace_cpk(thd, "clustered_pk");
if (cpk_scan && !intersect->is_covering)
{
- if (ror_intersect_add(intersect, cpk_scan, TRUE) &&
+ if (ror_intersect_add(intersect, cpk_scan, &trace_cpk, TRUE) &&
(intersect->total_cost < min_cost))
+ {
+ trace_cpk.add("clustered_pk_scan_added_to_intersect", true)
+ .add("cumulated_cost", intersect->total_cost);
intersect_best= intersect; //just set pointer here
+ }
else
+ {
+ trace_cpk.add("clustered_pk_added_to_intersect", false)
+ .add("cause", "cost");
cpk_scan= 0; // Don't use cpk_scan
+ }
}
else
+ {
+ trace_cpk.add("clustered_pk_added_to_intersect", false)
+ .add("cause", cpk_scan ? "roworder is covering"
+ : "no clustered pk index");
cpk_scan= 0; // Don't use cpk_scan
+ }
+ trace_cpk.end();
/* Ok, return ROR-intersect plan if we have found one */
TRP_ROR_INTERSECT *trp= NULL;
@@ -6620,6 +7184,17 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
DBUG_PRINT("info", ("Returning non-covering ROR-intersect plan:"
"cost %g, records %lu",
trp->read_cost, (ulong) trp->records));
+ trace_ror.add("rows", trp->records)
+ .add("cost", trp->read_cost)
+ .add("covering", trp->is_covering)
+ .add("chosen", true);
+ }
+ else
+ {
+ trace_ror.add("chosen", false)
+ .add("cause", (read_time > min_cost)
+ ? "too few indexes to merge"
+ : "cost");
}
DBUG_RETURN(trp);
}
@@ -6809,6 +7384,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
UNINIT_VAR(best_buf_size); /* protected by key_to_read */
TRP_RANGE* read_plan= NULL;
DBUG_ENTER("get_key_scans_params");
+ THD *thd= param->thd;
/*
Note that there may be trees that have type SEL_TREE::KEY but contain no
key reads at all, e.g. tree for expression "key1 is not null" where key1
@@ -6816,6 +7392,8 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
*/
DBUG_EXECUTE("info", print_sel_tree(param, tree, &tree->keys_map,
"tree scans"););
+ Json_writer_array range_scan_alt(thd, "range_scan_alternatives");
+
tree->ror_scans_map.clear_all();
tree->n_ror_scans= 0;
tree->index_scans= 0;
@@ -6825,7 +7403,8 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
(INDEX_SCAN_INFO **) alloc_root(param->mem_root,
sizeof(INDEX_SCAN_INFO *) * param->keys);
}
- tree->index_scans_end= tree->index_scans;
+ tree->index_scans_end= tree->index_scans;
+
for (idx= 0; idx < param->keys; idx++)
{
SEL_ARG *key= tree->keys[idx];
@@ -6835,6 +7414,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
Cost_estimate cost;
double found_read_time;
uint mrr_flags, buf_size;
+ bool is_ror_scan= FALSE;
INDEX_SCAN_INFO *index_scan;
uint keynr= param->real_keynr[idx];
if (key->type == SEL_ARG::MAYBE_KEY ||
@@ -6844,11 +7424,14 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
bool read_index_only= index_read_must_be_used ? TRUE :
(bool) param->table->covering_keys.is_set(keynr);
+ Json_writer_object trace_idx(thd);
+ trace_idx.add("index", param->table->key_info[keynr].name);
+
found_records= check_quick_select(param, idx, read_index_only, key,
for_range_access, &mrr_flags,
- &buf_size, &cost);
+ &buf_size, &cost, &is_ror_scan);
- if (!for_range_access && !param->is_ror_scan &&
+ if (!for_range_access && !is_ror_scan &&
!optimizer_flag(param->thd,OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION))
{
/* The scan is not a ROR-scan, just skip it */
@@ -6859,16 +7442,31 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
(index_scan= (INDEX_SCAN_INFO *)alloc_root(param->mem_root,
sizeof(INDEX_SCAN_INFO))))
{
+ Json_writer_array trace_range(thd, "ranges");
+
+ const KEY &cur_key= param->table->key_info[keynr];
+ const KEY_PART_INFO *key_part= cur_key.key_part;
+
index_scan->idx= idx;
index_scan->keynr= keynr;
index_scan->key_info= &param->table->key_info[keynr];
- index_scan->used_key_parts= param->max_key_part+1;
+ index_scan->used_key_parts= param->max_key_parts;
index_scan->range_count= param->range_count;
index_scan->records= found_records;
index_scan->sel_arg= key;
*tree->index_scans_end++= index_scan;
- }
- if ((found_records != HA_POS_ERROR) && param->is_ror_scan)
+
+ if (unlikely(thd->trace_started()))
+ trace_ranges(&trace_range, param, idx, key, key_part);
+ trace_range.end();
+
+ trace_idx.add("rowid_ordered", is_ror_scan)
+ .add("using_mrr", !(mrr_flags & HA_MRR_USE_DEFAULT_IMPL))
+ .add("index_only", read_index_only)
+ .add("rows", found_records)
+ .add("cost", cost.total_cost());
+ }
+ if ((found_records != HA_POS_ERROR) && is_ror_scan)
{
tree->n_ror_scans++;
tree->ror_scans_map.set_bit(idx);
@@ -6882,6 +7480,20 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
best_idx= idx;
best_mrr_flags= mrr_flags;
best_buf_size= buf_size;
+ trace_idx.add("chosen", true);
+ }
+ else
+ {
+ trace_idx.add("chosen", false);
+ if (found_records == HA_POS_ERROR)
+ {
+ if (key->type == SEL_ARG::Type::MAYBE_KEY)
+ trace_idx.add("cause", "depends on unread values");
+ else
+ trace_idx.add("cause", "unknown");
+ }
+ else
+ trace_idx.add("cause", "cost");
}
}
}
@@ -6986,10 +7598,11 @@ QUICK_SELECT_I *TRP_ROR_INTERSECT::make_quick(PARAM *param,
"creating ROR-intersect",
first_scan, last_scan););
alloc= parent_alloc? parent_alloc: &quick_intrsect->alloc;
- for (; first_scan != last_scan;++first_scan)
+ for (ROR_SCAN_INFO **curr_scan= first_scan; curr_scan != last_scan;
+ ++curr_scan)
{
- if (!(quick= get_quick_select(param, (*first_scan)->idx,
- (*first_scan)->sel_arg,
+ if (!(quick= get_quick_select(param, (*curr_scan)->idx,
+ (*curr_scan)->sel_arg,
HA_MRR_USE_DEFAULT_IMPL | HA_MRR_SORTED,
0, alloc)) ||
quick_intrsect->push_quick_back(alloc, quick))
@@ -7077,6 +7690,21 @@ SEL_TREE *Item_bool_func::get_ne_mm_tree(RANGE_OPT_PARAM *param,
}
+SEL_TREE *Item_func_ne::get_func_mm_tree(RANGE_OPT_PARAM *param,
+ Field *field, Item *value)
+{
+ DBUG_ENTER("Item_func_ne::get_func_mm_tree");
+ /*
+ If this condition is a "col1<>...", where there is a UNIQUE KEY(col1),
+ do not construct a SEL_TREE from it. A condition that excludes just one
+ row in the table is not selective (unless there are only a few rows)
+ */
+ if (is_field_an_unique_index(param, field))
+ DBUG_RETURN(NULL);
+ DBUG_RETURN(get_ne_mm_tree(param, field, value, value));
+}
+
+
SEL_TREE *Item_func_between::get_func_mm_tree(RANGE_OPT_PARAM *param,
Field *field, Item *value)
{
@@ -7175,28 +7803,16 @@ SEL_TREE *Item_func_in::get_func_mm_tree(RANGE_OPT_PARAM *param,
DBUG_RETURN(0);
/*
- If this is "unique_key NOT IN (...)", do not consider it sargable (for
- any index, not just the unique one). The logic is as follows:
+ if this is a "col1 NOT IN (...)", and there is a UNIQUE KEY(col1), do
+ not constuct a SEL_TREE from it. The rationale is as follows:
- if there are only a few constants, this condition is not selective
(unless the table is also very small in which case we won't gain
anything)
- - If there are a lot of constants, the overhead of building and
+ - if there are a lot of constants, the overhead of building and
processing enormous range list is not worth it.
*/
- if (param->using_real_indexes)
- {
- key_map::Iterator it(field->key_start);
- uint key_no;
- while ((key_no= it++) != key_map::Iterator::BITMAP_END)
- {
- KEY *key_info= &field->table->key_info[key_no];
- if (key_info->user_defined_key_parts == 1 &&
- (key_info->flags & HA_NOSAME))
- {
- DBUG_RETURN(0);
- }
- }
- }
+ if (is_field_an_unique_index(param, field))
+ DBUG_RETURN(0);
/* Get a SEL_TREE for "(-inf|NULL) < X < c_0" interval. */
uint i=0;
@@ -7914,6 +8530,38 @@ SEL_TREE *Item_equal::get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
}
+/*
+ @brief
+ Check if there is an one-segment unique key that matches the field exactly
+
+ @detail
+ In the future we could also add "almost unique" indexes where any value is
+ present only in a few rows (but necessarily exactly one row)
+*/
+static bool is_field_an_unique_index(RANGE_OPT_PARAM *param, Field *field)
+{
+ DBUG_ENTER("is_field_an_unique_index");
+
+ // The check for using_real_indexes is there because of the heuristics
+ // this function is used for.
+ if (param->using_real_indexes)
+ {
+ key_map::Iterator it(field->key_start);
+ uint key_no;
+ while ((key_no= it++) != key_map::Iterator::BITMAP_END)
+ {
+ KEY *key_info= &field->table->key_info[key_no];
+ if (key_info->user_defined_key_parts == 1 &&
+ (key_info->flags & HA_NOSAME))
+ {
+ DBUG_RETURN(true);
+ }
+ }
+ }
+ DBUG_RETURN(false);
+}
+
+
SEL_TREE *
Item_bool_func::get_mm_parts(RANGE_OPT_PARAM *param, Field *field,
Item_func::Functype type, Item *value)
@@ -8110,52 +8758,112 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param,
SEL_ARG *
Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
Field *field, KEY_PART *key_part,
- Item_func::Functype type, Item *value)
+ Item_func::Functype functype, Item *value)
{
- uint maybe_null=(uint) field->real_maybe_null();
- SEL_ARG *tree= 0;
- MEM_ROOT *alloc= param->mem_root;
- uchar *str;
- int err;
DBUG_ENTER("Item_bool_func::get_mm_leaf");
-
DBUG_ASSERT(value); // IS NULL and IS NOT NULL are handled separately
-
if (key_part->image_type != Field::itRAW)
DBUG_RETURN(0); // e.g. SPATIAL index
+ DBUG_RETURN(field->get_mm_leaf(param, key_part, this,
+ functype_to_scalar_comparison_op(functype),
+ value));
+}
- if (param->using_real_indexes &&
- !field->optimize_range(param->real_keynr[key_part->key],
- key_part->part) &&
- type != EQ_FUNC &&
- type != EQUAL_FUNC)
- goto end; // Can't optimize this
- if (!field->can_optimize_range(this, value,
- type == EQUAL_FUNC || type == EQ_FUNC))
- goto end;
+bool Field::can_optimize_scalar_range(const RANGE_OPT_PARAM *param,
+ const KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op,
+ const Item *value) const
+{
+ bool is_eq_func= op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL;
+ if ((param->using_real_indexes &&
+ !optimize_range(param->real_keynr[key_part->key],
+ key_part->part) && !is_eq_func) ||
+ !can_optimize_range(cond, value, is_eq_func))
+ return false;
+ return true;
+}
+
+
+uchar *Field::make_key_image(MEM_ROOT *mem_root, const KEY_PART *key_part)
+{
+ DBUG_ENTER("Field::make_key_image");
+ uint maybe_null= (uint) real_maybe_null();
+ uchar *str;
+ if (!(str= (uchar*) alloc_root(mem_root, key_part->store_length + 1)))
+ DBUG_RETURN(0);
+ if (maybe_null)
+ *str= (uchar) is_real_null(); // Set to 1 if null
+ get_key_image(str + maybe_null, key_part->length, key_part->image_type);
+ DBUG_RETURN(str);
+}
+
+
+SEL_ARG *Field::stored_field_make_mm_leaf_truncated(RANGE_OPT_PARAM *param,
+ scalar_comparison_op op,
+ Item *value)
+{
+ DBUG_ENTER("Field::stored_field_make_mm_leaf_truncated");
+ if ((op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL) &&
+ value->result_type() == item_cmp_type(result_type(),
+ value->result_type()))
+ DBUG_RETURN(new (param->mem_root) SEL_ARG_IMPOSSIBLE(this));
+ /*
+ TODO: We should return trees of the type SEL_ARG::IMPOSSIBLE
+ for the cases like int_field > 999999999999999999999999 as well.
+ */
+ DBUG_RETURN(0);
+}
+
- err= value->save_in_field_no_warnings(field, 1);
+SEL_ARG *Field_num::get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op, Item *value)
+{
+ DBUG_ENTER("Field_num::get_mm_leaf");
+ if (!can_optimize_scalar_range(prm, key_part, cond, op, value))
+ DBUG_RETURN(0);
+ int err= value->save_in_field_no_warnings(this, 1);
+ if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0)
+ DBUG_RETURN(&null_element);
+ if (err > 0 && cmp_type() != value->result_type())
+ DBUG_RETURN(stored_field_make_mm_leaf_truncated(prm, op, value));
+ DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
+}
+
+
+SEL_ARG *Field_temporal::get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op, Item *value)
+{
+ DBUG_ENTER("Field_temporal::get_mm_leaf");
+ if (!can_optimize_scalar_range(prm, key_part, cond, op, value))
+ DBUG_RETURN(0);
+ int err= value->save_in_field_no_warnings(this, 1);
+ if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0)
+ DBUG_RETURN(&null_element);
if (err > 0)
- {
- if (field->type_handler() == &type_handler_enum ||
- field->type_handler() == &type_handler_set)
- {
- if (type == EQ_FUNC || type == EQUAL_FUNC)
- tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
- goto end;
- }
+ DBUG_RETURN(stored_field_make_mm_leaf_truncated(prm, op, value));
+ DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
+}
- if (err == 2 && field->cmp_type() == STRING_RESULT)
- {
- if (type == EQ_FUNC || type == EQUAL_FUNC)
- tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
- else
- tree= NULL; /* Cannot infer anything */
- goto end;
- }
- if (err == 3 && field->type() == FIELD_TYPE_DATE)
+SEL_ARG *Field_date_common::get_mm_leaf(RANGE_OPT_PARAM *prm,
+ KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op,
+ Item *value)
+{
+ DBUG_ENTER("Field_date_common::get_mm_leaf");
+ if (!can_optimize_scalar_range(prm, key_part, cond, op, value))
+ DBUG_RETURN(0);
+ int err= value->save_in_field_no_warnings(this, 1);
+ if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0)
+ DBUG_RETURN(&null_element);
+ if (err > 0)
+ {
+ if (err == 3)
{
/*
We were saving DATETIME into a DATE column, the conversion went ok
@@ -8175,76 +8883,86 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
be done together with other types at the end of this function
(grep for stored_field_cmp_to_item)
*/
- if (type == EQ_FUNC || type == EQUAL_FUNC)
- {
- tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
- goto end;
- }
- // Continue with processing non-equality ranges
- }
- else if (field->cmp_type() != value->result_type())
- {
- if ((type == EQ_FUNC || type == EQUAL_FUNC) &&
- value->result_type() == item_cmp_type(field->result_type(),
- value->result_type()))
- {
- tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
- goto end;
- }
- else
- {
- /*
- TODO: We should return trees of the type SEL_ARG::IMPOSSIBLE
- for the cases like int_field > 999999999999999999999999 as well.
- */
- tree= 0;
- goto end;
- }
- }
-
- /*
- guaranteed at this point: err > 0; field and const of same type
- If an integer got bounded (e.g. to within 0..255 / -128..127)
- for < or >, set flags as for <= or >= (no NEAR_MAX / NEAR_MIN)
- */
- else if (err == 1 && field->result_type() == INT_RESULT)
- {
- if (type == LT_FUNC && (value->val_int() > 0))
- type= LE_FUNC;
- else if (type == GT_FUNC &&
- (field->type() != FIELD_TYPE_BIT) &&
- !((Field_num*)field)->unsigned_flag &&
- !((Item_int*)value)->unsigned_flag &&
- (value->val_int() < 0))
- type= GE_FUNC;
+ if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL)
+ DBUG_RETURN(new (prm->mem_root) SEL_ARG_IMPOSSIBLE(this));
+ DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
}
+ DBUG_RETURN(stored_field_make_mm_leaf_truncated(prm, op, value));
}
- else if (err < 0)
+ DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
+}
+
+
+SEL_ARG *Field_str::get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op, Item *value)
+{
+ DBUG_ENTER("Field_str::get_mm_leaf");
+ if (!can_optimize_scalar_range(prm, key_part, cond, op, value))
+ DBUG_RETURN(0);
+ int err= value->save_in_field_no_warnings(this, 1);
+ if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0)
+ DBUG_RETURN(&null_element);
+ if (err > 0)
{
- /* This happens when we try to insert a NULL field in a not null column */
- tree= &null_element; // cmp with NULL is never TRUE
- goto end;
+ if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL)
+ DBUG_RETURN(new (prm->mem_root) SEL_ARG_IMPOSSIBLE(this));
+ DBUG_RETURN(NULL); /* Cannot infer anything */
}
+ DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
+}
- /*
- Any sargable predicate except "<=>" involving NULL as a constant is always
- FALSE
- */
- if (type != EQUAL_FUNC && field->is_real_null())
+
+SEL_ARG *Field::get_mm_leaf_int(RANGE_OPT_PARAM *prm, KEY_PART *key_part,
+ const Item_bool_func *cond,
+ scalar_comparison_op op, Item *value,
+ bool unsigned_field)
+{
+ DBUG_ENTER("Field::get_mm_leaf_int");
+ if (!can_optimize_scalar_range(prm, key_part, cond, op, value))
+ DBUG_RETURN(0);
+ int err= value->save_in_field_no_warnings(this, 1);
+ if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0)
+ DBUG_RETURN(&null_element);
+ if (err > 0)
{
- tree= &null_element;
- goto end;
+ if (value->result_type() != INT_RESULT)
+ DBUG_RETURN(stored_field_make_mm_leaf_truncated(prm, op, value));
+ else
+ DBUG_RETURN(stored_field_make_mm_leaf_bounded_int(prm, key_part,
+ op, value,
+ unsigned_field));
}
-
- str= (uchar*) alloc_root(alloc, key_part->store_length+1);
- if (!str)
- goto end;
- if (maybe_null)
- *str= (uchar) field->is_real_null(); // Set to 1 if null
- field->get_key_image(str+maybe_null, key_part->length,
- key_part->image_type);
- if (!(tree= new (alloc) SEL_ARG(field, str, str)))
- goto end; // out of memory
+ if (value->result_type() != INT_RESULT)
+ DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
+ DBUG_RETURN(stored_field_make_mm_leaf_exact(prm, key_part, op, value));
+}
+
+
+/*
+ This method is called when:
+ - value->save_in_field_no_warnings() returned err > 0
+ - and both field and "value" are of integer data types
+ If an integer got bounded (e.g. to within 0..255 / -128..127)
+ for < or >, set flags as for <= or >= (no NEAR_MAX / NEAR_MIN)
+*/
+
+SEL_ARG *Field::stored_field_make_mm_leaf_bounded_int(RANGE_OPT_PARAM *param,
+ KEY_PART *key_part,
+ scalar_comparison_op op,
+ Item *value,
+ bool unsigned_field)
+{
+ DBUG_ENTER("Field::stored_field_make_mm_leaf_bounded_int");
+ if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL) // e.g. tinyint = 200
+ DBUG_RETURN(new (param->mem_root) SEL_ARG_IMPOSSIBLE(this));
+ longlong item_val= value->val_int();
+
+ if (op == SCALAR_CMP_LT && item_val > 0)
+ op= SCALAR_CMP_LE; // e.g. rewrite (tinyint < 200) to (tinyint <= 127)
+ else if (op == SCALAR_CMP_GT && !unsigned_field &&
+ !value->unsigned_flag && item_val < 0)
+ op= SCALAR_CMP_GE; // e.g. rewrite (tinyint > -200) to (tinyint >= -128)
/*
Check if we are comparing an UNSIGNED integer with a negative constant.
@@ -8257,66 +8975,74 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
negative integers (which otherwise fails because at query execution time
negative integers are cast to unsigned if compared with unsigned).
*/
- if (field->result_type() == INT_RESULT &&
- value->result_type() == INT_RESULT &&
- ((field->type() == FIELD_TYPE_BIT ||
- ((Field_num *) field)->unsigned_flag) &&
- !((Item_int*) value)->unsigned_flag))
+ if (unsigned_field && !value->unsigned_flag && item_val < 0)
{
- longlong item_val= value->val_int();
- if (item_val < 0)
- {
- if (type == LT_FUNC || type == LE_FUNC)
- {
- tree->type= SEL_ARG::IMPOSSIBLE;
- goto end;
- }
- if (type == GT_FUNC || type == GE_FUNC)
- {
- tree= 0;
- goto end;
- }
- }
+ if (op == SCALAR_CMP_LT || op == SCALAR_CMP_LE) // e.g. uint < -1
+ DBUG_RETURN(new (param->mem_root) SEL_ARG_IMPOSSIBLE(this));
+ if (op == SCALAR_CMP_GT || op == SCALAR_CMP_GE) // e.g. uint > -1
+ DBUG_RETURN(0);
}
+ DBUG_RETURN(stored_field_make_mm_leaf_exact(param, key_part, op, value));
+}
- switch (type) {
- case LT_FUNC:
- if (stored_field_cmp_to_item(param->thd, field, value) == 0)
- tree->max_flag=NEAR_MAX;
- /* fall through */
- case LE_FUNC:
- if (!maybe_null)
- tree->min_flag=NO_MIN_RANGE; /* From start */
- else
- { // > NULL
- tree->min_value=is_null_string;
- tree->min_flag=NEAR_MIN;
- }
- break;
- case GT_FUNC:
- /* Don't use open ranges for partial key_segments */
- if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
- (stored_field_cmp_to_item(param->thd, field, value) <= 0))
- tree->min_flag=NEAR_MIN;
- tree->max_flag= NO_MAX_RANGE;
- break;
- case GE_FUNC:
- /* Don't use open ranges for partial key_segments */
- if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
- (stored_field_cmp_to_item(param->thd, field, value) < 0))
- tree->min_flag= NEAR_MIN;
- tree->max_flag=NO_MAX_RANGE;
- break;
- case EQ_FUNC:
- case EQUAL_FUNC:
- break;
- default:
- DBUG_ASSERT(0);
+
+SEL_ARG *Field::stored_field_make_mm_leaf(RANGE_OPT_PARAM *param,
+ KEY_PART *key_part,
+ scalar_comparison_op op,
+ Item *value)
+{
+ DBUG_ENTER("Field::stored_field_make_mm_leaf");
+ THD *thd= param->thd;
+ MEM_ROOT *mem_root= param->mem_root;
+ uchar *str;
+ if (!(str= make_key_image(param->mem_root, key_part)))
+ DBUG_RETURN(0);
+
+ switch (op) {
+ case SCALAR_CMP_LE:
+ DBUG_RETURN(new (mem_root) SEL_ARG_LE(str, this));
+ case SCALAR_CMP_LT:
+ DBUG_RETURN(new (mem_root) SEL_ARG_LT(thd, str, this, value));
+ case SCALAR_CMP_GT:
+ DBUG_RETURN(new (mem_root) SEL_ARG_GT(thd, str, key_part, this, value));
+ case SCALAR_CMP_GE:
+ DBUG_RETURN(new (mem_root) SEL_ARG_GE(thd, str, key_part, this, value));
+ case SCALAR_CMP_EQ:
+ case SCALAR_CMP_EQUAL:
+ DBUG_RETURN(new (mem_root) SEL_ARG(this, str, str));
break;
}
+ DBUG_ASSERT(0);
+ DBUG_RETURN(NULL);
+}
-end:
- DBUG_RETURN(tree);
+
+SEL_ARG *Field::stored_field_make_mm_leaf_exact(RANGE_OPT_PARAM *param,
+ KEY_PART *key_part,
+ scalar_comparison_op op,
+ Item *value)
+{
+ DBUG_ENTER("Field::stored_field_make_mm_leaf_exact");
+ uchar *str;
+ if (!(str= make_key_image(param->mem_root, key_part)))
+ DBUG_RETURN(0);
+
+ switch (op) {
+ case SCALAR_CMP_LE:
+ DBUG_RETURN(new (param->mem_root) SEL_ARG_LE(str, this));
+ case SCALAR_CMP_LT:
+ DBUG_RETURN(new (param->mem_root) SEL_ARG_LT(str, this));
+ case SCALAR_CMP_GT:
+ DBUG_RETURN(new (param->mem_root) SEL_ARG_GT(str, key_part, this));
+ case SCALAR_CMP_GE:
+ DBUG_RETURN(new (param->mem_root) SEL_ARG_GE(str, this));
+ case SCALAR_CMP_EQ:
+ case SCALAR_CMP_EQUAL:
+ DBUG_RETURN(new (param->mem_root) SEL_ARG(this, str, str));
+ break;
+ }
+ DBUG_ASSERT(0);
+ DBUG_RETURN(NULL);
}
@@ -8435,6 +9161,11 @@ int and_range_trees(RANGE_OPT_PARAM *param, SEL_TREE *tree1, SEL_TREE *tree2,
if (key && key->type == SEL_ARG::IMPOSSIBLE)
{
result->type= SEL_TREE::IMPOSSIBLE;
+ if (param->using_real_indexes)
+ {
+ param->table->with_impossible_ranges.set_bit(param->
+ real_keynr[key_no]);
+ }
DBUG_RETURN(1);
}
result_keys.set_bit(key_no);
@@ -10416,7 +11147,8 @@ void SEL_ARG::test_use_count(SEL_ARG *root)
static
ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
SEL_ARG *tree, bool update_tbl_stats,
- uint *mrr_flags, uint *bufsize, Cost_estimate *cost)
+ uint *mrr_flags, uint *bufsize, Cost_estimate *cost,
+ bool *is_ror_scan)
{
SEL_ARG_RANGE_SEQ seq;
RANGE_SEQ_IF seq_if = {NULL, sel_arg_range_seq_init, sel_arg_range_seq_next, 0, 0};
@@ -10425,7 +11157,6 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
uint keynr= param->real_keynr[idx];
DBUG_ENTER("check_quick_select");
- param->is_ror_scan= FALSE;
/* Handle cases when we don't have a valid non-empty list of range */
if (!tree)
DBUG_RETURN(HA_POS_ERROR);
@@ -10440,11 +11171,11 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
seq.start= tree;
param->range_count=0;
- param->max_key_part=0;
+ param->max_key_parts=0;
- param->is_ror_scan= TRUE;
+ seq.is_ror_scan= TRUE;
if (file->index_flags(keynr, 0, TRUE) & HA_KEY_SCAN_NOT_ROR)
- param->is_ror_scan= FALSE;
+ seq.is_ror_scan= FALSE;
*mrr_flags= param->force_default_mrr? HA_MRR_USE_DEFAULT_IMPL: 0;
/*
@@ -10453,9 +11184,13 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
*mrr_flags|= HA_MRR_NO_ASSOCIATION | HA_MRR_SORTED;
bool pk_is_clustered= file->primary_key_is_clustered();
+ // TODO: param->max_key_parts holds 0 now, and not the #keyparts used.
+ // Passing wrong second argument to index_flags() makes no difference for
+ // most storage engines but might be an issue for MyRocks with certain
+ // datatypes.
if (index_only &&
- (file->index_flags(keynr, param->max_key_part, 1) & HA_KEYREAD_ONLY) &&
- !(file->index_flags(keynr, param->max_key_part, 1) & HA_CLUSTERED_INDEX))
+ (file->index_flags(keynr, param->max_key_parts, 1) & HA_KEYREAD_ONLY) &&
+ !(file->index_flags(keynr, param->max_key_parts, 1) & HA_CLUSTERED_INDEX))
*mrr_flags |= HA_MRR_INDEX_ONLY;
if (param->thd->lex->sql_command != SQLCOM_SELECT)
@@ -10486,12 +11221,16 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
if (update_tbl_stats)
{
param->table->quick_keys.set_bit(keynr);
- param->table->quick_key_parts[keynr]= param->max_key_part+1;
+ param->table->quick_key_parts[keynr]= param->max_key_parts;
param->table->quick_n_ranges[keynr]= param->range_count;
param->table->quick_condition_rows=
MY_MIN(param->table->quick_condition_rows, rows);
param->table->quick_rows[keynr]= rows;
param->table->quick_costs[keynr]= cost->total_cost();
+ if (keynr == param->table->s->primary_key && pk_is_clustered)
+ param->table->quick_index_only_costs[keynr]= 0;
+ else
+ param->table->quick_index_only_costs[keynr]= cost->index_only_cost();
}
}
/* Figure out if the key scan is ROR (returns rows in ROWID order) or not */
@@ -10503,12 +11242,12 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
TODO: Don't have this logic here, make table engines return
appropriate flags instead.
*/
- param->is_ror_scan= FALSE;
+ seq.is_ror_scan= FALSE;
}
else if (param->table->s->primary_key == keynr && pk_is_clustered)
{
/* Clustered PK scan is always a ROR scan (TODO: same as above) */
- param->is_ror_scan= TRUE;
+ seq.is_ror_scan= TRUE;
}
else if (param->range_count > 1)
{
@@ -10518,8 +11257,9 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
(1,3)" returns ROR order for all records with x=1, then ROR
order for records with x=3
*/
- param->is_ror_scan= FALSE;
+ seq.is_ror_scan= FALSE;
}
+ *is_ror_scan= seq.is_ror_scan;
DBUG_PRINT("exit", ("Records: %lu", (ulong) rows));
DBUG_RETURN(rows); //psergey-merge:todo: maintain first_null_comp.
@@ -11460,7 +12200,6 @@ int QUICK_RANGE_SELECT::reset()
HANDLER_BUFFER empty_buf;
MY_BITMAP * const save_read_set= head->read_set;
MY_BITMAP * const save_write_set= head->write_set;
- MY_BITMAP * const save_vcol_set= head->vcol_set;
DBUG_ENTER("QUICK_RANGE_SELECT::reset");
last_range= NULL;
cur_range= (QUICK_RANGE**) ranges.buffer;
@@ -11474,8 +12213,7 @@ int QUICK_RANGE_SELECT::reset()
}
if (in_ror_merged_scan)
- head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap,
- &column_bitmap);
+ head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap);
if (file->inited == handler::NONE)
{
@@ -11521,8 +12259,7 @@ int QUICK_RANGE_SELECT::reset()
err:
/* Restore bitmaps set on entry */
if (in_ror_merged_scan)
- head->column_bitmaps_set_no_signal(save_read_set, save_write_set,
- save_vcol_set);
+ head->column_bitmaps_set_no_signal(save_read_set, save_write_set);
DBUG_RETURN(error);
}
@@ -11553,16 +12290,13 @@ int QUICK_RANGE_SELECT::get_next()
MY_BITMAP * const save_read_set= head->read_set;
MY_BITMAP * const save_write_set= head->write_set;
- MY_BITMAP * const save_vcol_set= head->vcol_set;
/*
We don't need to signal the bitmap change as the bitmap is always the
same for this head->file
*/
- head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap,
- &column_bitmap);
+ head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap);
result= file->multi_range_read_next(&dummy);
- head->column_bitmaps_set_no_signal(save_read_set, save_write_set,
- save_vcol_set);
+ head->column_bitmaps_set_no_signal(save_read_set, save_write_set);
DBUG_RETURN(result);
}
@@ -12498,16 +13232,27 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
DBUG_ENTER("get_best_group_min_max");
+ Json_writer_object trace_group(thd, "group_index_range");
+ const char* cause= NULL;
+
/* Perform few 'cheap' tests whether this access method is applicable. */
- if (!join)
- DBUG_RETURN(NULL); /* This is not a select statement. */
- if ((join->table_count != 1) || /* The query must reference one table. */
- (join->select_lex->olap == ROLLUP_TYPE)) /* Check (B3) for ROLLUP */
+ if (!join) /* This is not a select statement. */
+ cause= "no join";
+ else if (join->table_count != 1) /* The query must reference one table. */
+ cause= "not single_table";
+ else if (join->select_lex->olap == ROLLUP_TYPE) /* Check (B3) for ROLLUP */
+ cause= "rollup";
+ else if (table->s->keys == 0) /* There are no indexes to use. */
+ cause= "no index";
+ else if (join->conds && join->conds->used_tables()
+ & OUTER_REF_TABLE_BIT) /* Cannot execute with correlated conditions. */
+ cause= "correlated conditions";
+
+ if (cause)
+ {
+ trace_group.add("chosen", false).add("cause", cause);
DBUG_RETURN(NULL);
- if (table->s->keys == 0) /* There are no indexes to use. */
- DBUG_RETURN(NULL);
- if (join->conds && join->conds->used_tables() & OUTER_REF_TABLE_BIT)
- DBUG_RETURN(NULL); /* Cannot execute with correlated conditions. */
+ }
/* Check (SA1,SA4) and store the only MIN/MAX argument - the C attribute.*/
List_iterator<Item> select_items_it(join->fields_list);
@@ -12516,7 +13261,10 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
if ((!join->group_list) && /* Neither GROUP BY nor a DISTINCT query. */
(!join->select_distinct) &&
!is_agg_distinct)
+ {
+ trace_group.add("chosen", false).add("cause","no group by or distinct");
DBUG_RETURN(NULL);
+ }
/* Analyze the query in more detail. */
if (join->sum_funcs[0])
@@ -12535,7 +13283,11 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
min_max_item->sum_func() == Item_sum::AVG_DISTINCT_FUNC))
continue;
else
+ {
+ trace_group.add("chosen", false)
+ .add("cause", "not applicable aggregate function");
DBUG_RETURN(NULL);
+ }
/* The argument of MIN/MAX. */
Item *expr= min_max_item->get_arg(0)->real_item();
@@ -12544,26 +13296,41 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
if (! min_max_arg_item)
min_max_arg_item= (Item_field*) expr;
else if (! min_max_arg_item->eq(expr, 1))
+ {
+ trace_group.add("chosen", false)
+ .add("cause", "arguments different in min max function");
DBUG_RETURN(NULL);
+ }
}
else
+ {
+ trace_group.add("chosen", false)
+ .add("cause", "no field item in min max function");
DBUG_RETURN(NULL);
+ }
}
}
/* Check (SA7). */
if (is_agg_distinct && (have_max || have_min))
{
+ trace_group.add("chosen", false)
+ .add("cause", "have both agg distinct and min max");
DBUG_RETURN(NULL);
}
/* Check (SA5). */
if (join->select_distinct)
{
+ trace_group.add("distinct_query", true);
while ((item= select_items_it++))
{
if (item->real_item()->type() != Item::FIELD_ITEM)
+ {
+ trace_group.add("chosen", false)
+ .add("cause", "distinct field is expression");
DBUG_RETURN(NULL);
+ }
}
}
@@ -12572,7 +13339,11 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
for (tmp_group= join->group_list; tmp_group; tmp_group= tmp_group->next)
{
if ((*tmp_group->item)->real_item()->type() != Item::FIELD_ITEM)
+ {
+ trace_group.add("chosen", false)
+ .add("cause", "group field is expression");
DBUG_RETURN(NULL);
+ }
elements_in_group++;
}
@@ -12594,10 +13365,16 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
ha_rows cur_quick_prefix_records= 0;
// We go through allowed indexes
+ Json_writer_array trace_indexes(thd, "potential_group_range_indexes");
+
for (uint cur_param_idx= 0; cur_param_idx < param->keys ; ++cur_param_idx)
{
const uint cur_index= param->real_keynr[cur_param_idx];
KEY *const cur_index_info= &table->key_info[cur_index];
+
+ Json_writer_object trace_idx(thd);
+ trace_idx.add("index", cur_index_info->name);
+
KEY_PART_INFO *cur_part;
KEY_PART_INFO *end_part; /* Last part for loops. */
/* Last index part. */
@@ -12622,7 +13399,10 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
*/
if (!table->covering_keys.is_set(cur_index) ||
!table->keys_in_use_for_group_by.is_set(cur_index))
- continue;
+ {
+ cause= "not covering";
+ goto next_index;
+ }
/*
This function is called on the precondition that the index is covering.
@@ -12630,7 +13410,10 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
these are duplicates. The GROUP BY list cannot be a prefix of the index.
*/
if (elements_in_group > table->actual_n_key_parts(cur_index_info))
- continue;
+ {
+ cause= "group key parts greater than index key parts";
+ goto next_index;
+ }
/*
Unless extended keys can be used for cur_index:
@@ -12656,10 +13439,15 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
*/
if (bitmap_is_set(table->read_set, cur_field->field_index) &&
!cur_field->part_of_key_not_clustered.is_set(cur_index))
+ {
+ cause= "not covering";
goto next_index; // Field was not part of key
+ }
}
}
+ trace_idx.add("covering", true);
+
max_key_part= 0;
used_key_parts_map.clear_all();
@@ -12690,7 +13478,10 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
used_key_parts_map.set_bit(max_key_part);
}
else
+ {
+ cause= "group attribute not prefix in index";
goto next_index;
+ }
}
}
/*
@@ -12719,7 +13510,10 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
/* not doing loose index scan for derived tables */
if (!item_field->field)
+ {
+ cause= "derived table";
goto next_index;
+ }
/* Find the order of the key part in the index. */
key_part_nr= get_field_keypart(cur_index_info, item_field->field);
@@ -12731,7 +13525,10 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
continue;
if (key_part_nr < 1 ||
(!is_agg_distinct && key_part_nr > join->fields_list.elements))
+ {
+ cause= "select attribute not prefix in index";
goto next_index;
+ }
cur_part= cur_index_info->key_part + key_part_nr - 1;
cur_group_prefix_len+= cur_part->store_length;
used_key_parts_map.set_bit(key_part_nr);
@@ -12756,7 +13553,10 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
{
key_part_nr= get_field_keypart(cur_index_info, min_max_arg_item->field);
if (key_part_nr <= cur_group_key_parts)
+ {
+ cause= "aggregate column not suffix in idx";
goto next_index;
+ }
min_max_arg_part= cur_index_info->key_part + key_part_nr - 1;
}
@@ -12767,6 +13567,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
if (cur_index_info->flags & HA_NOSAME &&
cur_group_key_parts == cur_index_info->user_defined_key_parts)
{
+ cause= "using unique index";
goto next_index;
}
@@ -12806,7 +13607,10 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
last_part, thd, cur_key_infix,
&cur_key_infix_len,
&first_non_infix_part))
+ {
+ cause= "nonconst equality gap attribute";
goto next_index;
+ }
}
else if (min_max_arg_part &&
(min_max_arg_part - first_non_group_part > 0))
@@ -12815,6 +13619,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
There is a gap but no range tree, thus no predicates at all for the
non-group keyparts.
*/
+ cause= "no nongroup keypart predicate";
goto next_index;
}
else if (first_non_group_part && join->conds)
@@ -12838,7 +13643,10 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
/* Check if cur_part is referenced in the WHERE clause. */
if (join->conds->walk(&Item::find_item_in_field_list_processor, 0,
key_part_range))
+ {
+ cause= "keypart reference from where clause";
goto next_index;
+ }
}
}
@@ -12853,7 +13661,10 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
for (; cur_part != last_part; cur_part++)
{
if (bitmap_is_set(table->read_set, cur_part->field->field_index))
+ {
+ cause= "keypart after infix in query";
goto next_index;
+ }
}
}
@@ -12870,6 +13681,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
index_range_tree, &cur_range) ||
(cur_range && cur_range->type != SEL_ARG::KEY_RANGE))
{
+ cause= "minmax keypart in disjunctive query";
goto next_index;
}
}
@@ -12887,11 +13699,20 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
Cost_estimate dummy_cost;
uint mrr_flags= HA_MRR_USE_DEFAULT_IMPL;
uint mrr_bufsize=0;
+ bool is_ror_scan= FALSE;
cur_quick_prefix_records= check_quick_select(param, cur_param_idx,
FALSE /*don't care*/,
cur_index_tree, TRUE,
&mrr_flags, &mrr_bufsize,
- &dummy_cost);
+ &dummy_cost, &is_ror_scan);
+ if (unlikely(cur_index_tree && thd->trace_started()))
+ {
+ Json_writer_array trace_range(thd, "ranges");
+
+ const KEY_PART_INFO *key_part= cur_index_info->key_part;
+ trace_ranges(&trace_range, param, cur_param_idx,
+ cur_index_tree, key_part);
+ }
}
cost_group_min_max(table, cur_index_info, cur_used_key_parts,
cur_group_key_parts, tree, cur_index_tree,
@@ -12902,6 +13723,8 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
Do not compare doubles directly because they may have different
representations (64 vs. 80 bits).
*/
+ trace_idx.add("rows", cur_records).add("cost", cur_read_cost);
+
if (cur_read_cost < best_read_cost - (DBL_EPSILON * cur_read_cost))
{
index_info= cur_index_info;
@@ -12919,8 +13742,16 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
used_key_parts= cur_used_key_parts;
}
- next_index:;
+ next_index:
+ if (cause)
+ {
+ trace_idx.add("usable", false).add("cause", cause);
+ cause= NULL;
+ }
}
+
+ trace_indexes.end();
+
if (!index_info) /* No usable index found. */
DBUG_RETURN(NULL);
@@ -12931,14 +13762,22 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
(index_info->flags & HA_SPATIAL) ?
Field::itMBR : Field::itRAW,
&has_min_max_fld, &has_other_fld))
+ {
+ trace_group.add("usable", false)
+ .add("cause", "unsupported predicate on agg attribute");
DBUG_RETURN(NULL);
+ }
/*
Check (SA6) if clustered key is used
*/
if (is_agg_distinct && index == table->s->primary_key &&
table->file->primary_key_is_clustered())
+ {
+ trace_group.add("usable", false)
+ .add("cause", "index is clustered");
DBUG_RETURN(NULL);
+ }
/* The query passes all tests, so construct a new TRP object. */
read_plan= new (param->mem_root)
@@ -12959,6 +13798,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
read_plan->records= best_records;
if (read_time < best_read_cost && is_agg_distinct)
{
+ trace_group.add("index_scan", true);
read_plan->read_cost= 0;
read_plan->use_index_scan();
}
@@ -13152,7 +13992,8 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
if (args[0] && args[1]) // this is a binary function or BETWEEN
{
- DBUG_ASSERT(pred->is_bool_type());
+ DBUG_ASSERT(pred->fixed_type_handler());
+ DBUG_ASSERT(pred->fixed_type_handler()->is_bool_type());
Item_bool_func *bool_func= (Item_bool_func*) pred;
Field *field= min_max_arg_item->field;
if (!args[2]) // this is a binary function
@@ -13566,7 +14407,7 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
1/double(2*TIME_FOR_COMPARE);
const double cpu_cost= num_groups *
- (tree_traversal_cost + 1/double(TIME_FOR_COMPARE));
+ (tree_traversal_cost + 1/double(TIME_FOR_COMPARE_IDX));
*read_cost= io_cost + cpu_cost;
*records= num_groups;
@@ -15022,7 +15863,6 @@ static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg)
DBUG_VOID_RETURN;
}
-
void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose)
{
/* purecov: begin inspected */
@@ -15150,3 +15990,221 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose)
}
#endif /* !DBUG_OFF */
+
+
+/*
+ @brief Print the comparison operator for the min range
+*/
+
+static void print_min_range_operator(String *out, const ha_rkey_function flag)
+{
+ if (flag == HA_READ_AFTER_KEY)
+ out->append(STRING_WITH_LEN(" < "));
+ else if (flag == HA_READ_KEY_EXACT || flag == HA_READ_KEY_OR_NEXT)
+ out->append(STRING_WITH_LEN(" <= "));
+ else
+ out->append(STRING_WITH_LEN(" ? "));
+}
+
+
+/*
+ @brief Print the comparison operator for the max range
+*/
+
+static void print_max_range_operator(String *out, const ha_rkey_function flag)
+{
+ if (flag == HA_READ_BEFORE_KEY)
+ out->append(STRING_WITH_LEN(" < "));
+ else if (flag == HA_READ_AFTER_KEY)
+ out->append(STRING_WITH_LEN(" <= "));
+ else
+ out->append(STRING_WITH_LEN(" ? "));
+}
+
+
+static
+void print_range(String *out, const KEY_PART_INFO *key_part,
+ KEY_MULTI_RANGE *range, uint n_key_parts)
+{
+ uint flag= range->range_flag;
+ String key_name;
+ key_name.set_charset(system_charset_info);
+ key_part_map keypart_map= range->start_key.keypart_map |
+ range->end_key.keypart_map;
+
+ if (flag & GEOM_FLAG)
+ {
+ /*
+ The flags of GEOM ranges do not work the same way as for other
+ range types, so printing "col < some_geom" doesn't make sense.
+ Just print the column name, not operator.
+ */
+ print_keyparts_name(out, key_part, n_key_parts, keypart_map);
+ out->append(STRING_WITH_LEN(" "));
+ print_key_value(out, key_part, range->start_key.key,
+ range->start_key.length);
+ return;
+ }
+
+ if (range->start_key.length)
+ {
+ print_key_value(out, key_part, range->start_key.key,
+ range->start_key.length);
+ print_min_range_operator(out, range->start_key.flag);
+ }
+
+ print_keyparts_name(out, key_part, n_key_parts, keypart_map);
+
+ if (range->end_key.length)
+ {
+ print_max_range_operator(out, range->end_key.flag);
+ print_key_value(out, key_part, range->end_key.key,
+ range->end_key.length);
+ }
+}
+
+
+/*
+ @brief Print range created for non-indexed columns
+
+ @param
+ out output string
+ field field for which the range is printed
+ range range for the field
+*/
+
+static
+void print_range_for_non_indexed_field(String *out, Field *field,
+ KEY_MULTI_RANGE *range)
+{
+ TABLE *table= field->table;
+ MY_BITMAP *old_sets[2];
+ dbug_tmp_use_all_columns(table, old_sets, &table->read_set, &table->write_set);
+
+ if (range->start_key.length)
+ {
+ field->print_key_part_value(out, range->start_key.key, field->key_length());
+ print_min_range_operator(out, range->start_key.flag);
+ }
+
+ out->append(field->field_name);
+
+ if (range->end_key.length)
+ {
+ print_max_range_operator(out, range->end_key.flag);
+ field->print_key_part_value(out, range->end_key.key, field->key_length());
+ }
+ dbug_tmp_restore_column_maps(&table->read_set, &table->write_set, old_sets);
+}
+
+
+
+/*
+
+ Add ranges to the trace
+ For ex:
+ lets say we have an index a_b(a,b)
+ query: select * from t1 where a=2 and b=4 ;
+ so we create a range:
+ (2,4) <= (a,b) <= (2,4)
+ this is added to the trace
+*/
+
+static void trace_ranges(Json_writer_array *range_trace,
+ PARAM *param, uint idx,
+ SEL_ARG *keypart,
+ const KEY_PART_INFO *key_parts)
+{
+ SEL_ARG_RANGE_SEQ seq;
+ KEY_MULTI_RANGE range;
+ range_seq_t seq_it;
+ uint flags= 0;
+ RANGE_SEQ_IF seq_if = {NULL, sel_arg_range_seq_init,
+ sel_arg_range_seq_next, 0, 0};
+ KEY *keyinfo= param->table->key_info + param->real_keynr[idx];
+ uint n_key_parts= param->table->actual_n_key_parts(keyinfo);
+ DBUG_ASSERT(range_trace->trace_started());
+ seq.keyno= idx;
+ seq.real_keyno= param->real_keynr[idx];
+ seq.param= param;
+ seq.start= keypart;
+ /*
+ is_ror_scan is set to FALSE here, because we are only interested
+ in iterating over all the ranges and printing them.
+ */
+ seq.is_ror_scan= FALSE;
+ const KEY_PART_INFO *cur_key_part= key_parts + keypart->part;
+ seq_it= seq_if.init((void *) &seq, 0, flags);
+
+ while (!seq_if.next(seq_it, &range))
+ {
+ StringBuffer<128> range_info(system_charset_info);
+ print_range(&range_info, cur_key_part, &range, n_key_parts);
+ range_trace->add(range_info.c_ptr_safe(), range_info.length());
+ }
+}
+
+/**
+ Print a key to a string
+
+ @param[out] out String the key is appended to
+ @param[in] key_part Index components description
+ @param[in] key Key tuple
+ @param[in] used_length length of the key tuple
+*/
+
+static void print_key_value(String *out, const KEY_PART_INFO *key_part,
+ const uchar* key, uint used_length)
+{
+ out->append(STRING_WITH_LEN("("));
+ Field *field= key_part->field;
+ StringBuffer<128> tmp(system_charset_info);
+ TABLE *table= field->table;
+ uint store_length;
+ MY_BITMAP *old_sets[2];
+ dbug_tmp_use_all_columns(table, old_sets, &table->read_set, &table->write_set);
+ const uchar *key_end= key+used_length;
+
+ for (; key < key_end; key+=store_length, key_part++)
+ {
+ field= key_part->field;
+ store_length= key_part->store_length;
+
+ field->print_key_part_value(out, key, key_part->length);
+
+ if (key + store_length < key_end)
+ out->append(STRING_WITH_LEN(","));
+ }
+ dbug_tmp_restore_column_maps(&table->read_set, &table->write_set, old_sets);
+ out->append(STRING_WITH_LEN(")"));
+}
+
+/**
+ Print key parts involed in a range
+ @param[out] out String the key is appended to
+ @param[in] key_part Index components description
+ @param[in] n_keypart Number of keyparts in index
+ @param[in] keypart_map map for keyparts involved in the range
+*/
+
+void print_keyparts_name(String *out, const KEY_PART_INFO *key_part,
+ uint n_keypart, key_part_map keypart_map)
+{
+ uint i;
+ out->append(STRING_WITH_LEN("("));
+ bool first_keypart= TRUE;
+ for (i=0; i < n_keypart; key_part++, i++)
+ {
+ if (keypart_map & (1 << i))
+ {
+ if (first_keypart)
+ first_keypart= FALSE;
+ else
+ out->append(STRING_WITH_LEN(","));
+ out->append(key_part->field->field_name);
+ }
+ else
+ break;
+ }
+ out->append(STRING_WITH_LEN(")"));
+}
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 84b1cd08ba8..11d9f80865a 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -458,6 +458,9 @@ public:
SEL_ARG *key_tree= first();
uint res= key_tree->store_min(key[key_tree->part].store_length,
range_key, *range_key_flag);
+ // add flags only if a key_part is written to the buffer
+ if (!res)
+ return 0;
*range_key_flag|= key_tree->min_flag;
if (key_tree->next_key_part &&
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE &&
@@ -480,7 +483,9 @@ public:
SEL_ARG *key_tree= last();
uint res=key_tree->store_max(key[key_tree->part].store_length,
range_key, *range_key_flag);
- (*range_key_flag)|= key_tree->max_flag;
+ if (!res)
+ return 0;
+ *range_key_flag|= key_tree->max_flag;
if (key_tree->next_key_part &&
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE &&
key_tree->part != last_part &&
@@ -566,7 +571,7 @@ public:
FALSE Otherwise
*/
- bool is_singlepoint()
+ bool is_singlepoint() const
{
/*
Check for NEAR_MIN ("strictly less") and NO_MIN_RANGE (-inf < field)
@@ -1646,7 +1651,8 @@ class SQL_SELECT :public Sql_alloc {
{
key_map tmp;
tmp.set_all();
- return test_quick_select(thd, tmp, 0, limit, force_quick_range, FALSE, FALSE) < 0;
+ return test_quick_select(thd, tmp, 0, limit, force_quick_range,
+ FALSE, FALSE, FALSE) < 0;
}
/*
RETURN
@@ -1663,7 +1669,8 @@ class SQL_SELECT :public Sql_alloc {
}
int test_quick_select(THD *thd, key_map keys, table_map prev_tables,
ha_rows limit, bool force_quick_range,
- bool ordered_output, bool remove_false_parts_of_where);
+ bool ordered_output, bool remove_false_parts_of_where,
+ bool only_single_index_range_scan);
};
diff --git a/sql/opt_range_mrr.cc b/sql/opt_range_mrr.cc
index 85c1eec3ff3..20413f5df63 100644
--- a/sql/opt_range_mrr.cc
+++ b/sql/opt_range_mrr.cc
@@ -53,6 +53,11 @@ typedef struct st_sel_arg_range_seq
int i; /* Index of last used element in the above array */
bool at_start; /* TRUE <=> The traversal has just started */
+ /*
+ Iteration functions will set this to FALSE
+ if ranges being traversed do not allow to construct a ROR-scan"
+ */
+ bool is_ror_scan;
} SEL_ARG_RANGE_SEQ;
@@ -74,7 +79,7 @@ range_seq_t sel_arg_range_seq_init(void *init_param, uint n_ranges, uint flags)
SEL_ARG_RANGE_SEQ *seq= (SEL_ARG_RANGE_SEQ*)init_param;
seq->param->range_count=0;
seq->at_start= TRUE;
- seq->param->max_key_part= 0;
+ seq->param->max_key_parts= 0;
seq->stack[0].key_tree= NULL;
seq->stack[0].min_key= seq->param->min_key;
seq->stack[0].min_key_flag= 0;
@@ -166,7 +171,7 @@ bool sel_arg_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range)
seq->i--;
step_down_to(seq, key_tree->next);
key_tree= key_tree->next;
- seq->param->is_ror_scan= FALSE;
+ seq->is_ror_scan= FALSE;
goto walk_right_n_up;
}
@@ -208,7 +213,7 @@ walk_right_n_up:
!memcmp(cur[-1].min_key, cur[-1].max_key, len) &&
!key_tree->min_flag && !key_tree->max_flag))
{
- seq->param->is_ror_scan= FALSE;
+ seq->is_ror_scan= FALSE;
if (!key_tree->min_flag)
cur->min_key_parts +=
key_tree->next_key_part->store_min_key(seq->param->key[seq->keyno],
@@ -243,6 +248,7 @@ walk_up_n_right:
uint min_key_length= (uint)(cur->min_key - seq->param->min_key);
range->ptr= (char*)(intptr)(key_tree->part);
+ uint max_key_parts;
if (cur->min_key_flag & GEOM_FLAG)
{
range->range_flag= cur->min_key_flag;
@@ -252,10 +258,11 @@ walk_up_n_right:
range->start_key.length= min_key_length;
range->start_key.keypart_map= make_prev_keypart_map(cur->min_key_parts);
range->start_key.flag= (ha_rkey_function) (cur->min_key_flag ^ GEOM_FLAG);
+ max_key_parts= cur->min_key_parts;
}
else
{
- range->range_flag= cur->min_key_flag | cur->max_key_flag;
+ max_key_parts= MY_MAX(cur->min_key_parts, cur->max_key_parts);
range->start_key.key= seq->param->min_key;
range->start_key.length= (uint)(cur->min_key - seq->param->min_key);
@@ -291,6 +298,7 @@ walk_up_n_right:
!memcmp(seq->param->min_key, seq->param->max_key, // (2)
range->start_key.length);
+ range->range_flag= 0;
if (is_eq_range_pred)
{
range->range_flag = EQ_RANGE;
@@ -313,7 +321,7 @@ walk_up_n_right:
range->range_flag |= UNIQUE_RANGE | (cur->min_key_flag & NULL_RANGE);
}
- if (seq->param->is_ror_scan)
+ if (seq->is_ror_scan)
{
/*
If we get here, the condition on the key was converted to form
@@ -328,11 +336,11 @@ walk_up_n_right:
(range->start_key.length == range->end_key.length) &&
!memcmp(range->start_key.key, range->end_key.key, range->start_key.length) &&
is_key_scan_ror(seq->param, seq->real_keyno, key_tree->part + 1)))
- seq->param->is_ror_scan= FALSE;
+ seq->is_ror_scan= FALSE;
}
}
seq->param->range_count++;
- seq->param->max_key_part=MY_MAX(seq->param->max_key_part,key_tree->part);
+ seq->param->max_key_parts= MY_MAX(seq->param->max_key_parts, max_key_parts);
return 0;
}
diff --git a/sql/opt_split.cc b/sql/opt_split.cc
index c3a2d03a93b..395422de3c3 100644
--- a/sql/opt_split.cc
+++ b/sql/opt_split.cc
@@ -204,7 +204,7 @@ struct SplM_field_info
struct SplM_plan_info
{
/* The cached splitting execution plan P */
- struct st_position *best_positions;
+ POSITION *best_positions;
/* The cost of the above plan */
double cost;
/* Selectivity of splitting used in P */
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index d65e00f4b97..7bd778e339f 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -34,6 +34,7 @@
#include "opt_subselect.h"
#include "sql_test.h"
#include <my_bit.h>
+#include "opt_trace.h"
/*
This file contains optimizations for semi-join subqueries.
@@ -438,7 +439,7 @@ Currently, solution #2 is implemented.
LEX_CSTRING weedout_key= {STRING_WITH_LEN("weedout_key")};
static
-bool subquery_types_allow_materialization(Item_in_subselect *in_subs);
+bool subquery_types_allow_materialization(THD *thd, Item_in_subselect *in_subs);
static bool replace_where_subcondition(JOIN *, Item **, Item *, Item *, bool);
static int subq_sj_candidate_cmp(Item_in_subselect* el1, Item_in_subselect* el2,
void *arg);
@@ -515,8 +516,9 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs,
if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0
!child_select->is_part_of_union() && // 1
parent_unit->first_select()->leaf_tables.elements && // 2
+ child_select->outer_select() &&
child_select->outer_select()->table_list.first && // 2A
- subquery_types_allow_materialization(in_subs) &&
+ subquery_types_allow_materialization(thd, in_subs) &&
(in_subs->is_top_level_item() || //3
optimizer_flag(thd,
OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE) || //3
@@ -587,7 +589,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
{
Item_in_subselect *in_subs= NULL;
Item_allany_subselect *allany_subs= NULL;
- switch (subselect->substype()) {
+ Item_subselect::subs_type substype= subselect->substype();
+ switch (substype) {
case Item_subselect::IN_SUBS:
in_subs= (Item_in_subselect *)subselect;
break;
@@ -599,6 +602,26 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
break;
}
+ /*
+ Try removing "ORDER BY" or even "ORDER BY ... LIMIT" from certain kinds
+ of subqueries. The removal might enable further transformations.
+ */
+ if (substype == Item_subselect::IN_SUBS ||
+ substype == Item_subselect::EXISTS_SUBS ||
+ substype == Item_subselect::ANY_SUBS ||
+ substype == Item_subselect::ALL_SUBS)
+ {
+ // (1) - ORDER BY without LIMIT can be removed from IN/EXISTS subqueries
+ // (2) - for EXISTS, can also remove "ORDER BY ... LIMIT n",
+ // but cannot remove "ORDER BY ... LIMIT n OFFSET m"
+ if (!select_lex->select_limit || // (1)
+ (substype == Item_subselect::EXISTS_SUBS && // (2)
+ !select_lex->offset_limit)) // (2)
+ {
+ select_lex->join->order= 0;
+ select_lex->join->skip_sort_order= 1;
+ }
+ }
/* Resolve expressions and perform semantic analysis for IN query */
if (in_subs != NULL)
@@ -677,7 +700,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
{
DBUG_PRINT("info", ("Subquery is semi-join conversion candidate"));
- (void)subquery_types_allow_materialization(in_subs);
+ (void)subquery_types_allow_materialization(thd, in_subs);
in_subs->is_flattenable_semijoin= TRUE;
@@ -691,6 +714,10 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
if (arena)
thd->restore_active_arena(arena, &backup);
in_subs->is_registered_semijoin= TRUE;
+ OPT_TRACE_TRANSFORM(thd, trace_wrapper, trace_transform,
+ select_lex->select_number,
+ "IN (SELECT)", "semijoin");
+ trace_transform.add("chosen", true);
}
}
else
@@ -817,18 +844,23 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
*/
static
-bool subquery_types_allow_materialization(Item_in_subselect *in_subs)
+bool subquery_types_allow_materialization(THD* thd, Item_in_subselect *in_subs)
{
DBUG_ENTER("subquery_types_allow_materialization");
- DBUG_ASSERT(in_subs->left_expr->fixed);
+ DBUG_ASSERT(in_subs->left_expr->is_fixed());
List_iterator<Item> it(in_subs->unit->first_select()->item_list);
uint elements= in_subs->unit->first_select()->item_list.elements;
+ const char* cause= NULL;
in_subs->types_allow_materialization= FALSE; // Assign default values
in_subs->sjm_scan_allowed= FALSE;
+ OPT_TRACE_TRANSFORM(thd, trace_wrapper, trace_transform,
+ in_subs->get_select_lex()->select_number,
+ "IN (SELECT)", "materialization");
+
/*
The checks here must be kept in sync with the one in
Item_func_in::in_predicate_to_in_subs_transformer().
@@ -844,13 +876,16 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs)
all_are_fields &= (outer->real_item()->type() == Item::FIELD_ITEM &&
inner->real_item()->type() == Item::FIELD_ITEM);
total_key_length += inner->max_length;
-
if (!inner->
type_handler()->
subquery_type_allows_materialization(inner,
outer,
converted_from_in_predicate))
+ {
+ trace_transform.add("possible", false);
+ trace_transform.add("cause", "types mismatch");
DBUG_RETURN(FALSE);
+ }
}
/*
@@ -860,14 +895,23 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs)
Make sure that the length of the key for the temp_table is atleast
greater than 0.
*/
- if (!total_key_length || total_key_length > tmp_table_max_key_length() ||
- elements > tmp_table_max_key_parts())
- DBUG_RETURN(FALSE);
-
- in_subs->types_allow_materialization= TRUE;
- in_subs->sjm_scan_allowed= all_are_fields;
- DBUG_PRINT("info",("subquery_types_allow_materialization: ok, allowed"));
- DBUG_RETURN(TRUE);
+ if (!total_key_length)
+ cause= "zero length key for materialized table";
+ else if (total_key_length > tmp_table_max_key_length())
+ cause= "length of key greater than allowed key length for materialized tables";
+ else if (elements > tmp_table_max_key_parts())
+ cause= "#keyparts greater than allowed key parts for materialized tables";
+ else
+ {
+ in_subs->types_allow_materialization= TRUE;
+ in_subs->sjm_scan_allowed= all_are_fields;
+ trace_transform.add("sjm_scan_allowed", all_are_fields)
+ .add("possible", true);
+ DBUG_PRINT("info",("subquery_types_allow_materialization: ok, allowed"));
+ DBUG_RETURN(TRUE);
+ }
+ trace_transform.add("possible", false).add("cause", cause);
+ DBUG_RETURN(FALSE);
}
@@ -907,7 +951,7 @@ bool make_in_exists_conversion(THD *thd, JOIN *join, Item_in_subselect *item)
/*
We're going to finalize IN->EXISTS conversion.
Normally, IN->EXISTS conversion takes place inside the
- Item_subselect::fix_fields() call, where item_subselect->fixed==FALSE (as
+ Item_subselect::fix_fields() call, where item_subselect->is_fixed()==FALSE (as
fix_fields() haven't finished yet) and item_subselect->changed==FALSE (as
the conversion haven't been finalized)
@@ -934,7 +978,7 @@ bool make_in_exists_conversion(THD *thd, JOIN *join, Item_in_subselect *item)
item->fixed= 1;
Item *substitute= item->substitution;
- bool do_fix_fields= !item->substitution->fixed;
+ bool do_fix_fields= !item->substitution->is_fixed();
/*
The Item_subselect has already been wrapped with Item_in_optimizer, so we
should search for item->optimizer, not 'item'.
@@ -1217,15 +1261,31 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
/* Stop processing if we've reached a subquery that's attached to the ON clause */
if (in_subq->do_not_convert_to_sj)
+ {
+ OPT_TRACE_TRANSFORM(thd, trace_wrapper, trace_transform,
+ in_subq->get_select_lex()->select_number,
+ "IN (SELECT)", "semijoin");
+ trace_transform.add("converted_to_semi_join", false)
+ .add("cause", "subquery attached to the ON clause");
break;
+ }
if (in_subq->is_flattenable_semijoin)
{
+ OPT_TRACE_TRANSFORM(thd, trace_wrapper, trace_transform,
+ in_subq->get_select_lex()->select_number,
+ "IN (SELECT)", "semijoin");
if (join->table_count +
in_subq->unit->first_select()->join->table_count >= MAX_TABLES)
+ {
+ trace_transform.add("converted_to_semi_join", false);
+ trace_transform.add("cause",
+ "table in parent join now exceeds MAX_TABLES");
break;
+ }
if (convert_subq_to_sj(join, in_subq))
goto restore_arena_and_fail;
+ trace_transform.add("converted_to_semi_join", true);
}
else
{
@@ -1270,7 +1330,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
in_subq->fixed= 1;
Item *substitute= in_subq->substitution;
- bool do_fix_fields= !in_subq->substitution->fixed;
+ bool do_fix_fields= !in_subq->substitution->is_fixed();
Item **tree= (in_subq->emb_on_expr_nest == NO_JOIN_NEST)?
&join->conds : &(in_subq->emb_on_expr_nest->on_expr);
Item *replace_me= in_subq->original_item();
@@ -1805,7 +1865,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
subq_lex->ref_pointer_array[i]);
if (!item_eq)
DBUG_RETURN(TRUE);
- DBUG_ASSERT(subq_pred->left_expr->element_index(i)->fixed);
+ DBUG_ASSERT(subq_pred->left_expr->element_index(i)->is_fixed());
if (subq_pred->left_expr_orig->element_index(i) !=
subq_pred->left_expr->element_index(i))
thd->change_item_tree(item_eq->arguments(),
@@ -2134,12 +2194,15 @@ int pull_out_semijoin_tables(JOIN *join)
TABLE_LIST *sj_nest;
DBUG_ENTER("pull_out_semijoin_tables");
List_iterator<TABLE_LIST> sj_list_it(join->select_lex->sj_nests);
-
+
/* Try pulling out of the each of the semi-joins */
while ((sj_nest= sj_list_it++))
{
List_iterator<TABLE_LIST> child_li(sj_nest->nested_join->join_list);
TABLE_LIST *tbl;
+ Json_writer_object trace_wrapper(join->thd);
+ Json_writer_object trace(join->thd, "semijoin_table_pullout");
+ Json_writer_array trace_arr(join->thd, "pulled_out_tables");
/*
Don't do table pull-out for nested joins (if we get nested joins here, it
@@ -2238,7 +2301,8 @@ int pull_out_semijoin_tables(JOIN *join)
pulled_a_table= TRUE;
pulled_tables |= tbl->table->map;
DBUG_PRINT("info", ("Table %s pulled out (reason: func dep)",
- tbl->table->alias.c_ptr()));
+ tbl->table->alias.c_ptr_safe()));
+ trace_arr.add(tbl->table->alias.c_ptr_safe());
/*
Pulling a table out of uncorrelated subquery in general makes
makes it correlated. See the NOTE to this funtion.
@@ -2344,8 +2408,15 @@ int pull_out_semijoin_tables(JOIN *join)
bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
{
DBUG_ENTER("optimize_semijoin_nests");
+ THD *thd= join->thd;
List_iterator<TABLE_LIST> sj_list_it(join->select_lex->sj_nests);
TABLE_LIST *sj_nest;
+ if (!join->select_lex->sj_nests.elements)
+ DBUG_RETURN(FALSE);
+ Json_writer_object wrapper(thd);
+ Json_writer_object trace_semijoin_nest(thd,
+ "execution_plan_for_potential_materialization");
+ Json_writer_array trace_steps_array(thd, "steps");
while ((sj_nest= sj_list_it++))
{
/* semi-join nests with only constant tables are not valid */
@@ -2716,14 +2787,6 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx,
{
POSITION *pos= join->positions + idx;
const JOIN_TAB *new_join_tab= pos->table;
- Semi_join_strategy_picker *pickers[]=
- {
- &pos->firstmatch_picker,
- &pos->loosescan_picker,
- &pos->sjmat_picker,
- &pos->dups_weedout_picker,
- NULL,
- };
#ifdef HAVE_valgrind
new (&pos->firstmatch_picker) Firstmatch_picker;
@@ -2732,18 +2795,30 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx,
new (&pos->dups_weedout_picker) Duplicate_weedout_picker;
#endif
- if (join->emb_sjm_nest)
+ if (join->emb_sjm_nest || //(1)
+ !join->select_lex->have_merged_subqueries) //(2)
{
/*
- We're performing optimization inside SJ-Materialization nest:
+ (1): We're performing optimization inside SJ-Materialization nest:
- there are no other semi-joins inside semi-join nests
- attempts to build semi-join strategies here will confuse
the optimizer, so bail out.
+ (2): Don't waste time on semi-join optimizations if we don't have any
+ semi-joins
*/
pos->sj_strategy= SJ_OPT_NONE;
return;
}
+ Semi_join_strategy_picker *pickers[]=
+ {
+ &pos->firstmatch_picker,
+ &pos->loosescan_picker,
+ &pos->sjmat_picker,
+ &pos->dups_weedout_picker,
+ NULL,
+ };
+ Json_writer_array trace_steps(join->thd, "semijoin_strategy_choice");
/*
Update join->cur_sj_inner_tables (Used by FirstMatch in this function and
LooseScan detector in best_access_path)
@@ -2842,6 +2917,7 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx,
*current_read_time= read_time;
*current_record_count= rec_count;
dups_producing_tables &= ~handled_fanout;
+
//TODO: update bitmap of semi-joins that were handled together with
// others.
if (is_multiple_semi_joins(join, join->positions, idx,
@@ -2869,6 +2945,33 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx,
}
}
}
+
+ if (unlikely(join->thd->trace_started() && pos->sj_strategy != SJ_OPT_NONE))
+ {
+ Json_writer_object tr(join->thd);
+ const char *sname;
+ switch (pos->sj_strategy) {
+ case SJ_OPT_MATERIALIZE:
+ sname= "SJ-Materialization";
+ break;
+ case SJ_OPT_MATERIALIZE_SCAN:
+ sname= "SJ-Materialization-Scan";
+ break;
+ case SJ_OPT_FIRST_MATCH:
+ sname= "FirstMatch";
+ break;
+ case SJ_OPT_DUPS_WEEDOUT:
+ sname= "DuplicateWeedout";
+ break;
+ case SJ_OPT_LOOSE_SCAN:
+ sname= "LooseScan";
+ break;
+ default:
+ DBUG_ASSERT(0);
+ sname="Invalid";
+ }
+ tr.add("chosen_strategy", sname);
+ }
}
if ((emb_sj_nest= new_join_tab->emb_sj_nest))
@@ -2887,7 +2990,7 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx,
}
-void Sj_materialization_picker::set_from_prev(struct st_position *prev)
+void Sj_materialization_picker::set_from_prev(POSITION *prev)
{
if (prev->sjmat_picker.is_used)
set_empty();
@@ -2912,6 +3015,7 @@ bool Sj_materialization_picker::check_qep(JOIN *join,
{
bool sjm_scan;
SJ_MATERIALIZATION_INFO *mat_info;
+ THD *thd= join->thd;
if ((mat_info= at_sjmat_pos(join, remaining_tables,
new_join_tab, idx, &sjm_scan)))
{
@@ -2948,6 +3052,9 @@ bool Sj_materialization_picker::check_qep(JOIN *join,
Cost_estimate prefix_cost;
signed int first_tab= (int)idx - mat_info->tables;
double prefix_rec_count;
+ Json_writer_object trace(join->thd);
+ trace.add("strategy", "SJ-Materialization");
+
if (first_tab < (int)join->const_tables)
{
prefix_cost.reset();
@@ -2976,6 +3083,11 @@ bool Sj_materialization_picker::check_qep(JOIN *join,
*record_count= prefix_rec_count;
*handled_fanout= new_join_tab->emb_sj_nest->sj_inner_tables;
*strategy= SJ_OPT_MATERIALIZE;
+ if (unlikely(trace.trace_started()))
+ {
+ trace.add("records", *record_count);
+ trace.add("read_time", *read_time);
+ }
return TRUE;
}
}
@@ -2984,6 +3096,8 @@ bool Sj_materialization_picker::check_qep(JOIN *join,
if (sjm_scan_need_tables && /* Have SJM-Scan prefix */
!(sjm_scan_need_tables & remaining_tables))
{
+ Json_writer_object trace(join->thd);
+ trace.add("strategy", "SJ-Materialization-Scan");
TABLE_LIST *mat_nest=
join->positions[sjm_scan_last_inner].table->emb_sj_nest;
SJ_MATERIALIZATION_INFO *mat_info= mat_nest->sj_mat_info;
@@ -3019,6 +3133,7 @@ bool Sj_materialization_picker::check_qep(JOIN *join,
POSITION curpos, dummy;
/* Need to re-run best-access-path as we prefix_rec_count has changed */
bool disable_jbuf= (join->thd->variables.join_cache_level == 0);
+ Json_writer_temp_disable trace_semijoin_mat_scan(thd);
for (i= first_tab + mat_info->tables; i <= idx; i++)
{
best_access_path(join, join->positions[i].table, rem_tables,
@@ -3050,13 +3165,18 @@ bool Sj_materialization_picker::check_qep(JOIN *join,
*/
*record_count= prefix_rec_count;
*handled_fanout= mat_nest->sj_inner_tables;
+ if (unlikely(trace.trace_started()))
+ {
+ trace.add("records", *record_count);
+ trace.add("read_time", *read_time);
+ }
return TRUE;
}
return FALSE;
}
-void LooseScan_picker::set_from_prev(struct st_position *prev)
+void LooseScan_picker::set_from_prev(POSITION *prev)
{
if (prev->loosescan_picker.is_used)
set_empty();
@@ -3077,7 +3197,7 @@ bool LooseScan_picker::check_qep(JOIN *join,
double *read_time,
table_map *handled_fanout,
sj_strategy_enum *strategy,
- struct st_position *loose_scan_pos)
+ POSITION *loose_scan_pos)
{
POSITION *first= join->positions + first_loosescan_table;
/*
@@ -3113,6 +3233,8 @@ bool LooseScan_picker::check_qep(JOIN *join,
!(remaining_tables & loosescan_need_tables) &&
(new_join_tab->table->map & loosescan_need_tables))
{
+ Json_writer_object trace(join->thd);
+ trace.add("strategy", "LooseScan");
/*
Ok we have LooseScan plan and also have all LooseScan sj-nest's
inner tables and outer correlated tables into the prefix.
@@ -3143,12 +3265,17 @@ bool LooseScan_picker::check_qep(JOIN *join,
*/
*strategy= SJ_OPT_LOOSE_SCAN;
*handled_fanout= first->table->emb_sj_nest->sj_inner_tables;
+ if (unlikely(trace.trace_started()))
+ {
+ trace.add("records", *record_count);
+ trace.add("read_time", *read_time);
+ }
return TRUE;
}
return FALSE;
}
-void Firstmatch_picker::set_from_prev(struct st_position *prev)
+void Firstmatch_picker::set_from_prev(POSITION *prev)
{
if (prev->firstmatch_picker.is_used)
invalidate_firstmatch_prefix();
@@ -3222,6 +3349,8 @@ bool Firstmatch_picker::check_qep(JOIN *join,
if (in_firstmatch_prefix() &&
!(firstmatch_need_tables & remaining_tables))
{
+ Json_writer_object trace(join->thd);
+ trace.add("strategy", "FirstMatch");
/*
Got a complete FirstMatch range. Calculate correct costs and fanout
*/
@@ -3254,6 +3383,11 @@ bool Firstmatch_picker::check_qep(JOIN *join,
*handled_fanout= firstmatch_need_tables;
/* *record_count and *read_time were set by the above call */
*strategy= SJ_OPT_FIRST_MATCH;
+ if (unlikely(trace.trace_started()))
+ {
+ trace.add("records", *record_count);
+ trace.add("read_time", *read_time);
+ }
return TRUE;
}
}
@@ -3332,6 +3466,9 @@ bool Duplicate_weedout_picker::check_qep(JOIN *join,
double sj_inner_fanout= 1.0;
double sj_outer_fanout= 1.0;
uint temptable_rec_size;
+ Json_writer_object trace(join->thd);
+ trace.add("strategy", "DuplicateWeedout");
+
if (first_tab == join->const_tables)
{
prefix_rec_count= 1.0;
@@ -3392,6 +3529,11 @@ bool Duplicate_weedout_picker::check_qep(JOIN *join,
*record_count= prefix_rec_count * sj_outer_fanout;
*handled_fanout= dups_removed_fanout;
*strategy= SJ_OPT_DUPS_WEEDOUT;
+ if (unlikely(trace.trace_started()))
+ {
+ trace.add("records", *record_count);
+ trace.add("read_time", *read_time);
+ }
return TRUE;
}
return FALSE;
@@ -3585,12 +3727,20 @@ static void recalculate_prefix_record_count(JOIN *join, uint start, uint end)
void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
{
+ join->sjm_lookup_tables= 0;
+ join->sjm_scan_tables= 0;
+ if (!join->select_lex->sj_nests.elements)
+ return;
+
+ THD *thd= join->thd;
uint table_count=join->table_count;
uint tablenr;
table_map remaining_tables= 0;
table_map handled_tabs= 0;
- join->sjm_lookup_tables= 0;
- join->sjm_scan_tables= 0;
+ Json_writer_object trace_wrapper(thd);
+ Json_writer_array trace_semijoin_strategies(thd,
+ "fix_semijoin_strategies_for_picked_join_order");
+
for (tablenr= table_count - 1 ; tablenr != join->const_tables - 1; tablenr--)
{
POSITION *pos= join->best_positions + tablenr;
@@ -3615,8 +3765,18 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
first= tablenr - sjm->tables + 1;
join->best_positions[first].n_sj_tables= sjm->tables;
join->best_positions[first].sj_strategy= SJ_OPT_MATERIALIZE;
+ Json_writer_object semijoin_strategy(thd);
+ semijoin_strategy.add("semi_join_strategy","SJ-Materialization");
+ Json_writer_array semijoin_plan(thd, "join_order");
for (uint i= first; i < first+ sjm->tables; i++)
+ {
+ if (unlikely(thd->trace_started()))
+ {
+ Json_writer_object trace_one_table(thd);
+ trace_one_table.add_table_name(join->best_positions[i].table);
+ }
join->sjm_lookup_tables |= join->best_positions[i].table->table->map;
+ }
}
else if (pos->sj_strategy == SJ_OPT_MATERIALIZE_SCAN)
{
@@ -3654,8 +3814,16 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
POSITION dummy;
join->cur_sj_inner_tables= 0;
+ Json_writer_object semijoin_strategy(thd);
+ semijoin_strategy.add("semi_join_strategy","SJ-Materialization-Scan");
+ Json_writer_array semijoin_plan(thd, "join_order");
for (i= first + sjm->tables; i <= tablenr; i++)
{
+ if (unlikely(thd->trace_started()))
+ {
+ Json_writer_object trace_one_table(thd);
+ trace_one_table.add_table_name(join->best_positions[i].table);
+ }
best_access_path(join, join->best_positions[i].table, rem_tables,
join->best_positions, i,
FALSE, prefix_rec_count,
@@ -3685,8 +3853,16 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
join buffering
*/
join->cur_sj_inner_tables= 0;
+ Json_writer_object semijoin_strategy(thd);
+ semijoin_strategy.add("semi_join_strategy","FirstMatch");
+ Json_writer_array semijoin_plan(thd, "join_order");
for (idx= first; idx <= tablenr; idx++)
{
+ if (unlikely(thd->trace_started()))
+ {
+ Json_writer_object trace_one_table(thd);
+ trace_one_table.add_table_name(join->best_positions[idx].table);
+ }
if (join->best_positions[idx].use_join_buffer)
{
best_access_path(join, join->best_positions[idx].table,
@@ -3716,8 +3892,16 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
join buffering
*/
join->cur_sj_inner_tables= 0;
+ Json_writer_object semijoin_strategy(thd);
+ semijoin_strategy.add("semi_join_strategy","LooseScan");
+ Json_writer_array semijoin_plan(thd, "join_order");
for (idx= first; idx <= tablenr; idx++)
{
+ if (unlikely(thd->trace_started()))
+ {
+ Json_writer_object trace_one_table(thd);
+ trace_one_table.add_table_name(join->best_positions[idx].table);
+ }
if (join->best_positions[idx].use_join_buffer || (idx == first))
{
best_access_path(join, join->best_positions[idx].table,
@@ -3752,6 +3936,8 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
if (pos->sj_strategy == SJ_OPT_DUPS_WEEDOUT)
{
+ Json_writer_object semijoin_strategy(thd);
+ semijoin_strategy.add("semi_join_strategy","DuplicateWeedout");
/*
Duplicate Weedout starting at pos->first_dupsweedout_table, ending at
this table.
@@ -4294,17 +4480,12 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
table->temp_pool_slot = temp_pool_slot;
table->copy_blobs= 1;
table->in_use= thd;
- table->quick_keys.init();
- table->covering_keys.init();
- table->keys_in_use_for_query.init();
table->s= share;
init_tmp_table_share(thd, share, "", 0, tmpname, tmpname);
share->blob_field= blob_field;
share->table_charset= NULL;
share->primary_key= MAX_KEY; // Indicate no primary key
- share->keys_for_keyread.init();
- share->keys_in_use.init();
/* Create the field */
{
@@ -4318,9 +4499,9 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
if (!field)
DBUG_RETURN(0);
field->table= table;
- field->key_start.init(0);
- field->part_of_key.init(0);
- field->part_of_sortkey.init(0);
+ field->key_start.clear_all();
+ field->part_of_key.clear_all();
+ field->part_of_sortkey.clear_all();
field->unireg_check= Field::NONE;
field->flags= (NOT_NULL_FLAG | BINARY_FLAG | NO_DEFAULT_VALUE_FLAG);
field->reset_fields();
@@ -5556,31 +5737,515 @@ int select_value_catcher::send_data(List<Item> &items)
}
-/*
- Setup JTBM join tabs for execution
+/**
+ @brief
+ Attach conditions to already optimized condition
+
+ @param thd the thread handle
+ @param cond the condition to which add new conditions
+ @param cond_eq IN/OUT the multiple equalities of cond
+ @param new_conds the list of conditions to be added
+ @param cond_value the returned value of the condition
+ if it can be evaluated
+
+ @details
+ The method creates new condition through union of cond and
+ the conditions from new_conds list.
+ The method is called after optimize_cond() for cond. The result
+ of the union should be the same as if it was done before the
+ the optimize_cond() call.
+
+ @retval otherwise the created condition
+ @retval NULL if an error occurs
+*/
+
+Item *and_new_conditions_to_optimized_cond(THD *thd, Item *cond,
+ COND_EQUAL **cond_eq,
+ List<Item> &new_conds,
+ Item::cond_result *cond_value)
+{
+ COND_EQUAL new_cond_equal;
+ Item *item;
+ Item_equal *mult_eq;
+ bool is_simplified_cond= false;
+ /* The list where parts of the new condition are stored. */
+ List_iterator<Item> li(new_conds);
+ List_iterator_fast<Item_equal> it(new_cond_equal.current_level);
+
+ /*
+ Create multiple equalities from the equalities of the list new_conds.
+ Save the created multiple equalities in new_cond_equal.
+ If multiple equality can't be created or the condition
+ from new_conds list isn't an equality leave it in new_conds
+ list.
+
+ The equality can't be converted into the multiple equality if it
+ is a knowingly false or true equality.
+ For example, (3 = 1) equality.
+ */
+ while ((item=li++))
+ {
+ if (item->type() == Item::FUNC_ITEM &&
+ ((Item_func *) item)->functype() == Item_func::EQ_FUNC &&
+ check_simple_equality(thd,
+ Item::Context(Item::ANY_SUBST,
+ ((Item_func_eq *)item)->compare_type_handler(),
+ ((Item_func_eq *)item)->compare_collation()),
+ ((Item_func *)item)->arguments()[0],
+ ((Item_func *)item)->arguments()[1],
+ &new_cond_equal))
+ li.remove();
+ }
+
+ it.rewind();
+ if (cond && cond->type() == Item::COND_ITEM &&
+ ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ /*
+ Case when cond is an AND-condition.
+ Union AND-condition cond, created multiple equalities from
+ new_cond_equal and remaining conditions from new_conds.
+ */
+ COND_EQUAL *cond_equal= &((Item_cond_and *) cond)->m_cond_equal;
+ List<Item_equal> *cond_equalities= &cond_equal->current_level;
+ List<Item> *and_args= ((Item_cond_and *)cond)->argument_list();
+
+ /*
+ Disjoin multiple equalities of cond.
+ Merge these multiple equalities with the multiple equalities of
+ new_cond_equal. Save the result in new_cond_equal.
+ Check if after the merge some multiple equalities are knowingly
+ true or false.
+ */
+ and_args->disjoin((List<Item> *) cond_equalities);
+ while ((mult_eq= it++))
+ {
+ mult_eq->upper_levels= 0;
+ mult_eq->merge_into_list(thd, cond_equalities, false, false);
+ }
+ List_iterator_fast<Item_equal> ei(*cond_equalities);
+ while ((mult_eq= ei++))
+ {
+ if (mult_eq->const_item() && !mult_eq->val_int())
+ is_simplified_cond= true;
+ else
+ {
+ mult_eq->unfix_fields();
+ if (mult_eq->fix_fields(thd, NULL))
+ return NULL;
+ }
+ }
+
+ li.rewind();
+ while ((item=li++))
+ {
+ /*
+ There still can be some equalities at not top level of new_conds
+ conditions that are not transformed into multiple equalities.
+ To transform them build_item_equal() is called.
+
+ Examples of not top level equalities:
+
+ 1. (t1.a = 3) OR (t1.b > 5)
+ (t1.a = 3) - not top level equality.
+ It is inside OR condition
+
+ 2. ((t3.d = t3.c) AND (t3.c < 15)) OR (t3.d > 1)
+ (t1.d = t3.c) - not top level equality.
+ It is inside AND condition which is a part of OR condition
+ */
+ if (item->type() == Item::COND_ITEM &&
+ ((Item_cond *)item)->functype() == Item_func::COND_OR_FUNC)
+ {
+ item= item->build_equal_items(thd,
+ &((Item_cond_and *) cond)->m_cond_equal,
+ false, NULL);
+ }
+ and_args->push_back(item, thd->mem_root);
+ }
+ and_args->append((List<Item> *) cond_equalities);
+ *cond_eq= &((Item_cond_and *) cond)->m_cond_equal;
+ }
+ else
+ {
+ /*
+ Case when cond isn't an AND-condition or is NULL.
+ There can be several cases:
+
+ 1. cond is a multiple equality.
+ In this case merge cond with the multiple equalities of
+ new_cond_equal.
+ Create new condition from the created multiple equalities
+ and new_conds list conditions.
+ 2. cond is NULL
+ Create new condition from new_conds list conditions
+ and multiple equalities from new_cond_equal.
+ 3. Otherwise
+ Create new condition through union of cond, conditions from new_conds
+ list and created multiple equalities from new_cond_equal.
+ */
+ List<Item> new_conds_list;
+ /* Flag is set to true if cond is a multiple equality */
+ bool is_mult_eq= (cond && cond->type() == Item::FUNC_ITEM &&
+ ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC);
+
+ /*
+ If cond is non-empty and is not multiple equality save it as
+ a part of a new condition.
+ */
+ if (cond && !is_mult_eq &&
+ new_conds_list.push_back(cond, thd->mem_root))
+ return NULL;
+
+ /*
+ If cond is a multiple equality merge it with new_cond_equal
+ multiple equalities.
+ */
+ if (is_mult_eq)
+ {
+ Item_equal *eq_cond= (Item_equal *)cond;
+ eq_cond->upper_levels= 0;
+ eq_cond->merge_into_list(thd, &new_cond_equal.current_level,
+ false, false);
+ }
+
+ /**
+ Fix created multiple equalities and check if they are knowingly
+ true or false.
+ */
+ List_iterator_fast<Item_equal> ei(new_cond_equal.current_level);
+ while ((mult_eq=ei++))
+ {
+ if (mult_eq->const_item() && !mult_eq->val_int())
+ is_simplified_cond= true;
+ else
+ {
+ mult_eq->unfix_fields();
+ if (mult_eq->fix_fields(thd, NULL))
+ return NULL;
+ }
+ }
+
+ /*
+ Create AND condition if new condition will have two or
+ more elements.
+ */
+ Item_cond_and *and_cond= 0;
+ COND_EQUAL *inherited= 0;
+ if (new_conds_list.elements +
+ new_conds.elements +
+ new_cond_equal.current_level.elements > 1)
+ {
+ and_cond= new (thd->mem_root) Item_cond_and(thd);
+ and_cond->m_cond_equal.copy(new_cond_equal);
+ inherited= &and_cond->m_cond_equal;
+ }
+
+ li.rewind();
+ while ((item=li++))
+ {
+ /*
+ Look for the comment in the case when cond is an
+ AND condition above the build_equal_items() call.
+ */
+ if (item->type() == Item::COND_ITEM &&
+ ((Item_cond *)item)->functype() == Item_func::COND_OR_FUNC)
+ {
+ item= item->build_equal_items(thd, inherited, false, NULL);
+ }
+ new_conds_list.push_back(item, thd->mem_root);
+ }
+ new_conds_list.append((List<Item> *)&new_cond_equal.current_level);
+
+ if (and_cond)
+ {
+ and_cond->argument_list()->append(&new_conds_list);
+ cond= (Item *)and_cond;
+ *cond_eq= &((Item_cond_and *) cond)->m_cond_equal;
+ }
+ else
+ {
+ List_iterator_fast<Item> iter(new_conds_list);
+ cond= iter++;
+ if (cond->type() == Item::FUNC_ITEM &&
+ ((Item_func *)cond)->functype() == Item_func::MULT_EQUAL_FUNC)
+ {
+ if (!(*cond_eq))
+ *cond_eq= new COND_EQUAL();
+ (*cond_eq)->copy(new_cond_equal);
+ }
+ else
+ *cond_eq= 0;
+ }
+ }
+
+ if (!cond)
+ return NULL;
+
+ if (*cond_eq)
+ {
+ /*
+ The multiple equalities are attached only to the upper level
+ of AND-condition cond.
+ Push them down to the bottom levels of cond AND-condition if needed.
+ */
+ propagate_new_equalities(thd, cond,
+ &(*cond_eq)->current_level,
+ 0,
+ &is_simplified_cond);
+ cond= cond->propagate_equal_fields(thd,
+ Item::Context_boolean(),
+ *cond_eq);
+ cond->update_used_tables();
+ }
+ /* Check if conds has knowingly true or false parts. */
+ if (cond &&
+ !is_simplified_cond &&
+ cond->walk(&Item::is_simplified_cond_processor, 0, 0))
+ is_simplified_cond= true;
+
+
+ /*
+ If it was found that there are some knowingly true or false equalities
+ remove them from cond and set cond_value to the appropriate value.
+ */
+ if (cond && is_simplified_cond)
+ cond= cond->remove_eq_conds(thd, cond_value, true);
+
+ if (cond && cond->fix_fields_if_needed(thd, NULL))
+ return NULL;
+
+ return cond;
+}
+
+
+/**
+ @brief Materialize a degenerate jtbm semi join
+
+ @param thd thread handler
+ @param tbl table list for the target jtbm semi join table
+ @param subq_pred IN subquery predicate with the degenerate jtbm semi join
+ @param eq_list IN/OUT the list where to add produced equalities
+
+ @details
+ The method materializes the degenerate jtbm semi join for the
+ subquery from the IN subquery predicate subq_pred taking table
+ as the target for materialization.
+ Any degenerate table is guaranteed to produce 0 or 1 record.
+ Examples of both cases:
+
+ select * from ot where col in (select ... from it where 2>3)
+ select * from ot where col in (select MY_MIN(it.key) from it)
+
+ in this case, there is no necessity to create a temp.table for
+ materialization.
+ We now just need to
+ 1. Check whether 1 or 0 records are produced, setup this as a
+ constant join tab.
+ 2. Create a dummy temporary table, because all of the join
+ optimization code relies on TABLE object being present.
+
+ In the case when materialization produces one row the function
+ additionally creates equalities between the expressions from the
+ left part of the IN subquery predicate and the corresponding
+ columns of the produced row. These equalities are added to the
+ list eq_list. They are supposed to be conjuncted with the condition
+ of the WHERE clause.
+
+ @retval TRUE if an error occurs
+ @retval FALSE otherwise
+*/
+
+bool execute_degenerate_jtbm_semi_join(THD *thd,
+ TABLE_LIST *tbl,
+ Item_in_subselect *subq_pred,
+ List<Item> &eq_list)
+{
+ DBUG_ENTER("execute_degenerate_jtbm_semi_join");
+ select_value_catcher *new_sink;
+
+ DBUG_ASSERT(subq_pred->engine->engine_type() ==
+ subselect_engine::SINGLE_SELECT_ENGINE);
+ subselect_single_select_engine *engine=
+ (subselect_single_select_engine*)subq_pred->engine;
+ if (!(new_sink= new (thd->mem_root) select_value_catcher(thd, subq_pred)))
+ DBUG_RETURN(TRUE);
+ if (new_sink->setup(&engine->select_lex->join->fields_list) ||
+ engine->select_lex->join->change_result(new_sink, NULL) ||
+ engine->exec())
+ {
+ DBUG_RETURN(TRUE);
+ }
+ subq_pred->is_jtbm_const_tab= TRUE;
+
+ if (new_sink->assigned)
+ {
+ /*
+ Subselect produced one row, which is saved in new_sink->row.
+ Save "left_expr[i] == row[i]" equalities into the eq_list.
+ */
+ subq_pred->jtbm_const_row_found= TRUE;
+
+ Item *eq_cond;
+ for (uint i= 0; i < subq_pred->left_expr->cols(); i++)
+ {
+ eq_cond=
+ new (thd->mem_root) Item_func_eq(thd,
+ subq_pred->left_expr->element_index(i),
+ new_sink->row[i]);
+ if (!eq_cond || eq_cond->fix_fields(thd, NULL) ||
+ eq_list.push_back(eq_cond, thd->mem_root))
+ DBUG_RETURN(TRUE);
+ }
+ }
+ else
+ {
+ /* Subselect produced no rows. Just set the flag */
+ subq_pred->jtbm_const_row_found= FALSE;
+ }
+
+ TABLE *dummy_table;
+ if (!(dummy_table= create_dummy_tmp_table(thd)))
+ DBUG_RETURN(TRUE);
+ tbl->table= dummy_table;
+ tbl->table->pos_in_table_list= tbl;
+ /*
+ Note: the table created above may be freed by:
+ 1. JOIN_TAB::cleanup(), when the parent join is a regular join.
+ 2. cleanup_empty_jtbm_semi_joins(), when the parent join is a
+ degenerate join (e.g. one with "Impossible where").
+ */
+ setup_table_map(tbl->table, tbl, tbl->jtbm_table_no);
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ @brief
+ Execute degenerate jtbm semi joins before optimize_cond() for parent
+
+ @param join the parent join for jtbm semi joins
+ @param join_list the list of tables where jtbm semi joins are processed
+ @param eq_list IN/OUT the list where to add equalities produced after
+ materialization of single-row degenerate jtbm semi joins
+
+ @details
+ The method traverses join_list trying to find any degenerate jtbm semi
+ joins for subqueries of IN predicates. For each degenerate jtbm
+ semi join execute_degenerate_jtbm_semi_join() is called. As a result
+ of this call new equalities that substitute for single-row materialized
+ jtbm semi join are added to eq_list.
+
+ In the case when a table is nested in another table 'nested_join' the
+ method is recursively called for the join_list of the 'nested_join' trying
+ to find in the list any degenerate jtbm semi joins. Currently a jtbm semi
+ join may occur in a mergeable semi join nest.
+
+ @retval TRUE if an error occurs
+ @retval FALSE otherwise
+*/
+
+bool setup_degenerate_jtbm_semi_joins(JOIN *join,
+ List<TABLE_LIST> *join_list,
+ List<Item> &eq_list)
+{
+ TABLE_LIST *table;
+ NESTED_JOIN *nested_join;
+ List_iterator<TABLE_LIST> li(*join_list);
+ THD *thd= join->thd;
+ DBUG_ENTER("setup_degenerate_jtbm_semi_joins");
+
+ while ((table= li++))
+ {
+ Item_in_subselect *subq_pred;
+
+ if ((subq_pred= table->jtbm_subselect))
+ {
+ JOIN *subq_join= subq_pred->unit->first_select()->join;
+
+ if (!subq_join->tables_list || !subq_join->table_count)
+ {
+ if (execute_degenerate_jtbm_semi_join(thd,
+ table,
+ subq_pred,
+ eq_list))
+ DBUG_RETURN(TRUE);
+ join->is_orig_degenerated= true;
+ }
+ }
+ if ((nested_join= table->nested_join))
+ {
+ if (setup_degenerate_jtbm_semi_joins(join,
+ &nested_join->join_list,
+ eq_list))
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ @brief
+ Optimize jtbm semi joins for materialization
+
+ @param join the parent join for jtbm semi joins
+ @param join_list the list of TABLE_LIST objects where jtbm semi join
+ can occur
+ @param eq_list IN/OUT the list where to add produced equalities
+
+ @details
+ This method is called by the optimizer after the call of
+ optimize_cond() for parent select.
+ The method traverses join_list trying to find any jtbm semi joins for
+ subqueries from IN predicates and optimizes them.
+ After the optimization some of jtbm semi joins may become degenerate.
+ For example the subquery 'SELECT MAX(b) FROM t2' from the query
+
+ SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2);
+
+ will become degenerate if there is an index on t2.b.
+ If a subquery becomes degenerate it is handled by the function
+ execute_degenerate_jtbm_semi_join().
+
+ Otherwise the method creates a temporary table in which the subquery
+ of the jtbm semi join will be materialied.
+
+ The function saves the equalities between all pairs of the expressions
+ from the left part of the IN subquery predicate and the corresponding
+ columns of the subquery from the predicate in eq_list appending them
+ to the list. The equalities of eq_list will be later conjucted with the
+ condition of the WHERE clause.
+
+ In the case when a table is nested in another table 'nested_join' the
+ method is recursively called for the join_list of the 'nested_join' trying
+ to find in the list any degenerate jtbm semi joins. Currently a jtbm semi
+ join may occur in a mergeable semi join nest.
+
+ @retval TRUE if an error occurs
+ @retval FALSE otherwise
*/
-bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
- Item **join_where)
+bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
+ List<Item> &eq_list)
{
TABLE_LIST *table;
NESTED_JOIN *nested_join;
List_iterator<TABLE_LIST> li(*join_list);
THD *thd= join->thd;
DBUG_ENTER("setup_jtbm_semi_joins");
-
+
while ((table= li++))
{
- Item_in_subselect *item;
+ Item_in_subselect *subq_pred;
- if ((item= table->jtbm_subselect))
+ if ((subq_pred= table->jtbm_subselect))
{
- Item_in_subselect *subq_pred= item;
double rows;
double read_time;
/*
- Perform optimization of the subquery, so that we know estmated
+ Perform optimization of the subquery, so that we know estimated
- cost of materialization process
- how many records will be in the materialized temp.table
*/
@@ -5593,102 +6258,37 @@ bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
if (!subq_join->tables_list || !subq_join->table_count)
{
- /*
- A special case; subquery's join is degenerate, and it either produces
- 0 or 1 record. Examples of both cases:
-
- select * from ot where col in (select ... from it where 2>3)
- select * from ot where col in (select MY_MIN(it.key) from it)
-
- in this case, the subquery predicate has not been setup for
- materialization. In particular, there is no materialized temp.table.
- We'll now need to
- 1. Check whether 1 or 0 records are produced, setup this as a
- constant join tab.
- 2. Create a dummy temporary table, because all of the join
- optimization code relies on TABLE object being present (here we
- follow a bad tradition started by derived tables)
- */
- DBUG_ASSERT(subq_pred->engine->engine_type() ==
- subselect_engine::SINGLE_SELECT_ENGINE);
- subselect_single_select_engine *engine=
- (subselect_single_select_engine*)subq_pred->engine;
- select_value_catcher *new_sink;
- if (!(new_sink=
- new (thd->mem_root) select_value_catcher(thd, subq_pred)))
- DBUG_RETURN(TRUE);
- if (new_sink->setup(&engine->select_lex->join->fields_list) ||
- engine->select_lex->join->change_result(new_sink, NULL) ||
- engine->exec())
- {
+ if (!join->is_orig_degenerated &&
+ execute_degenerate_jtbm_semi_join(thd, table, subq_pred,
+ eq_list))
DBUG_RETURN(TRUE);
- }
- subq_pred->is_jtbm_const_tab= TRUE;
-
- if (new_sink->assigned)
- {
- subq_pred->jtbm_const_row_found= TRUE;
- /*
- Subselect produced one row, which is saved in new_sink->row.
- Inject "left_expr[i] == row[i] equalities into parent's WHERE.
- */
- Item *eq_cond;
- for (uint i= 0; i < subq_pred->left_expr->cols(); i++)
- {
- eq_cond= new (thd->mem_root)
- Item_func_eq(thd, subq_pred->left_expr->element_index(i),
- new_sink->row[i]);
- if (!eq_cond)
- DBUG_RETURN(1);
-
- if (!((*join_where)= and_items(thd, *join_where, eq_cond)) ||
- (*join_where)->fix_fields(thd, join_where))
- DBUG_RETURN(1);
- }
- }
- else
- {
- /* Subselect produced no rows. Just set the flag, */
- subq_pred->jtbm_const_row_found= FALSE;
- }
-
- /* Set up a dummy TABLE*, optimizer code needs JOIN_TABs to have TABLE */
- TABLE *dummy_table;
- if (!(dummy_table= create_dummy_tmp_table(thd)))
- DBUG_RETURN(1);
- table->table= dummy_table;
- table->table->pos_in_table_list= table;
- /*
- Note: the table created above may be freed by:
- 1. JOIN_TAB::cleanup(), when the parent join is a regular join.
- 2. cleanup_empty_jtbm_semi_joins(), when the parent join is a
- degenerate join (e.g. one with "Impossible where").
- */
- setup_table_map(table->table, table, table->jtbm_table_no);
}
else
{
DBUG_ASSERT(subq_pred->test_set_strategy(SUBS_MATERIALIZATION));
subq_pred->is_jtbm_const_tab= FALSE;
subselect_hash_sj_engine *hash_sj_engine=
- ((subselect_hash_sj_engine*)item->engine);
+ ((subselect_hash_sj_engine*)subq_pred->engine);
table->table= hash_sj_engine->tmp_table;
table->table->pos_in_table_list= table;
setup_table_map(table->table, table, table->jtbm_table_no);
- Item *sj_conds= hash_sj_engine->semi_join_conds;
-
- (*join_where)= and_items(thd, *join_where, sj_conds);
- (*join_where)->fix_fields_if_needed(thd, join_where);
+ List_iterator<Item> li(*hash_sj_engine->semi_join_conds->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ item->update_used_tables();
+ if (eq_list.push_back(item, thd->mem_root))
+ DBUG_RETURN(TRUE);
+ }
}
table->table->maybe_null= MY_TEST(join->mixed_implicit_grouping);
}
-
if ((nested_join= table->nested_join))
{
- if (setup_jtbm_semi_joins(join, &nested_join->join_list, join_where))
+ if (setup_jtbm_semi_joins(join, &nested_join->join_list, eq_list))
DBUG_RETURN(TRUE);
}
}
@@ -5796,8 +6396,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
/* A strategy must be chosen earlier. */
DBUG_ASSERT(in_subs->has_strategy());
DBUG_ASSERT(in_to_exists_where || in_to_exists_having);
- DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->fixed);
- DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->fixed);
+ DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->is_fixed());
+ DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->is_fixed());
/* The original QEP of the subquery. */
Join_plan_state save_qep(table_count);
@@ -6086,3 +6686,428 @@ bool JOIN::choose_tableless_subquery_plan()
exec_const_cond= zero_result_cause ? 0 : conds;
return FALSE;
}
+
+
+bool Item::pushable_equality_checker_for_subquery(uchar *arg)
+{
+ return
+ get_corresponding_field_pair(this,
+ ((Item_in_subselect *)arg)->corresponding_fields);
+}
+
+
+/*
+ Checks if 'item' or some item equal to it is equal to the field from
+ some Field_pair of 'pair_list' and returns matching Field_pair or
+ NULL if the matching Field_pair wasn't found.
+*/
+
+Field_pair *find_matching_field_pair(Item *item, List<Field_pair> pair_list)
+{
+ Field_pair *field_pair= get_corresponding_field_pair(item, pair_list);
+ if (field_pair)
+ return field_pair;
+
+ Item_equal *item_equal= item->get_item_equal();
+ if (item_equal)
+ {
+ Item_equal_fields_iterator it(*item_equal);
+ Item *equal_item;
+ while ((equal_item= it++))
+ {
+ if (equal_item->const_item())
+ continue;
+ field_pair= get_corresponding_field_pair(equal_item, pair_list);
+ if (field_pair)
+ return field_pair;
+ }
+ }
+ return NULL;
+}
+
+
+bool Item_field::excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred)
+{
+ if (find_matching_field_pair(((Item *) this), subq_pred->corresponding_fields))
+ return true;
+ return false;
+}
+
+
+bool Item_direct_view_ref::excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred)
+{
+ if (item_equal)
+ {
+ DBUG_ASSERT(real_item()->type() == Item::FIELD_ITEM);
+ if (get_corresponding_field_pair(((Item *)this), subq_pred->corresponding_fields))
+ return true;
+ }
+ return (*ref)->excl_dep_on_in_subq_left_part(subq_pred);
+}
+
+
+bool Item_equal::excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred)
+{
+ Item *left_item = get_const();
+ Item_equal_fields_iterator it(*this);
+ Item *item;
+ if (!left_item)
+ {
+ while ((item=it++))
+ {
+ if (item->excl_dep_on_in_subq_left_part(subq_pred))
+ {
+ left_item= item;
+ break;
+ }
+ }
+ }
+ if (!left_item)
+ return false;
+ while ((item=it++))
+ {
+ if (item->excl_dep_on_in_subq_left_part(subq_pred))
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ @brief
+ Get corresponding item from the select of the right part of IN subquery
+
+ @param thd the thread handle
+ @param item the item from the left part of subq_pred for which
+ corresponding item should be found
+ @param subq_pred the IN subquery predicate
+
+ @details
+ This method looks through the fields of the select of the right part of
+ the IN subquery predicate subq_pred trying to find the corresponding
+ item 'new_item' for item. If item has equal items it looks through
+ the fields of the select of the right part of subq_pred for each equal
+ item trying to find the corresponding item.
+ The method assumes that the given item is either a field item or
+ a reference to a field item.
+
+ @retval <item*> reference to the corresponding item
+ @retval NULL if item was not found
+*/
+
+static
+Item *get_corresponding_item(THD *thd, Item *item,
+ Item_in_subselect *subq_pred)
+{
+ DBUG_ASSERT(item->type() == Item::FIELD_ITEM ||
+ (item->type() == Item::REF_ITEM &&
+ ((Item_ref *) item)->ref_type() == Item_ref::VIEW_REF));
+
+ Field_pair *field_pair;
+ Item_equal *item_equal= item->get_item_equal();
+
+ if (item_equal)
+ {
+ Item_equal_fields_iterator it(*item_equal);
+ Item *equal_item;
+ while ((equal_item= it++))
+ {
+ field_pair=
+ get_corresponding_field_pair(equal_item, subq_pred->corresponding_fields);
+ if (field_pair)
+ return field_pair->corresponding_item;
+ }
+ }
+ else
+ {
+ field_pair=
+ get_corresponding_field_pair(item, subq_pred->corresponding_fields);
+ if (field_pair)
+ return field_pair->corresponding_item;
+ }
+ return NULL;
+}
+
+
+Item *Item_field::in_subq_field_transformer_for_where(THD *thd, uchar *arg)
+{
+ Item_in_subselect *subq_pred= (Item_in_subselect *)arg;
+ Item *producing_item= get_corresponding_item(thd, this, subq_pred);
+ if (producing_item)
+ return producing_item->build_clone(thd);
+ return this;
+}
+
+
+Item *Item_direct_view_ref::in_subq_field_transformer_for_where(THD *thd,
+ uchar *arg)
+{
+ if (item_equal)
+ {
+ Item_in_subselect *subq_pred= (Item_in_subselect *)arg;
+ Item *producing_item= get_corresponding_item(thd, this, subq_pred);
+ DBUG_ASSERT (producing_item != NULL);
+ return producing_item->build_clone(thd);
+ }
+ return this;
+}
+
+
+/**
+ @brief
+ Transforms item so it can be pushed into the IN subquery HAVING clause
+
+ @param thd the thread handle
+ @param in_item the item for which pushable item should be created
+ @param subq_pred the IN subquery predicate
+
+ @details
+ This method finds for in_item that is a field from the left part of the
+ IN subquery predicate subq_pred its corresponding item from the right part
+ of subq_pred.
+ If corresponding item is found, a shell for this item is created.
+ This shell can be pushed into the HAVING part of subq_pred select.
+
+ @retval <item*> reference to the created corresponding item shell for in_item
+ @retval NULL if mistake occurs
+*/
+
+static Item*
+get_corresponding_item_for_in_subq_having(THD *thd, Item *in_item,
+ Item_in_subselect *subq_pred)
+{
+ Item *new_item= get_corresponding_item(thd, in_item, subq_pred);
+
+ if (new_item)
+ {
+ Item_ref *ref=
+ new (thd->mem_root) Item_ref(thd,
+ &subq_pred->unit->first_select()->context,
+ NullS, NullS,
+ &new_item->name);
+ if (!ref)
+ DBUG_ASSERT(0);
+ return ref;
+ }
+ return new_item;
+}
+
+
+Item *Item_field::in_subq_field_transformer_for_having(THD *thd, uchar *arg)
+{
+ return get_corresponding_item_for_in_subq_having(thd, this,
+ (Item_in_subselect *)arg);
+}
+
+
+Item *Item_direct_view_ref::in_subq_field_transformer_for_having(THD *thd,
+ uchar *arg)
+{
+ if (!item_equal)
+ return this;
+ else
+ {
+ Item *new_item= get_corresponding_item_for_in_subq_having(thd, this,
+ (Item_in_subselect *)arg);
+ if (!new_item)
+ return this;
+ return new_item;
+ }
+}
+
+
+/**
+ @brief
+ Find fields that are used in the GROUP BY of the select
+
+ @param thd the thread handle
+ @param sel the select of the IN subquery predicate
+ @param fields fields of the left part of the IN subquery predicate
+ @param grouping_list GROUP BY clause
+
+ @details
+ This method traverses fields which are used in the GROUP BY of
+ sel and saves them with their corresponding items from fields.
+*/
+
+bool grouping_fields_in_the_in_subq_left_part(THD *thd,
+ st_select_lex *sel,
+ List<Field_pair> *fields,
+ ORDER *grouping_list)
+{
+ DBUG_ENTER("grouping_fields_in_the_in_subq_left_part");
+ sel->grouping_tmp_fields.empty();
+ List_iterator<Field_pair> it(*fields);
+ Field_pair *item;
+ while ((item= it++))
+ {
+ for (ORDER *ord= grouping_list; ord; ord= ord->next)
+ {
+ if ((*ord->item)->eq(item->corresponding_item, 0))
+ {
+ if (sel->grouping_tmp_fields.push_back(item, thd->mem_root))
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ @brief
+ Extract condition that can be pushed into select of this IN subquery
+
+ @param thd the thread handle
+ @param cond current condition
+
+ @details
+ This function builds the most restrictive condition depending only on
+ the list of fields of the left part of this IN subquery predicate
+ (directly or indirectly through equality) that can be extracted from the
+ given condition cond and pushes it into this IN subquery.
+
+ Example of the transformation:
+
+ SELECT * FROM t1
+ WHERE a>3 AND b>10 AND
+ (a,b) IN (SELECT x,MAX(y) FROM t2 GROUP BY x);
+
+ =>
+
+ SELECT * FROM t1
+ WHERE a>3 AND b>10 AND
+ (a,b) IN (SELECT x,max(y)
+ FROM t2
+ WHERE x>3
+ GROUP BY x
+ HAVING MAX(y)>10);
+
+
+ In details:
+ 1. Check what pushable formula can be extracted from cond
+ 2. Build a clone PC of the formula that can be extracted
+ (the clone is built only if the extracted formula is a AND subformula
+ of cond or conjunction of such subformulas)
+ 3. If there is no HAVING clause prepare PC to be conjuncted with
+ WHERE clause of this subquery. Otherwise do 4-7.
+ 4. Check what formula PC_where can be extracted from PC to be pushed
+ into the WHERE clause of the subquery
+ 5. Build PC_where and if PC_where is a conjunct(s) of PC remove it from PC
+ getting PC_having
+ 6. Prepare PC_where to be conjuncted with the WHERE clause of
+ the IN subquery
+ 7. Prepare PC_having to be conjuncted with the HAVING clause of
+ the IN subquery
+
+ @note
+ This method is similar to pushdown_cond_for_derived()
+
+ @retval TRUE if an error occurs
+ @retval FALSE otherwise
+*/
+
+bool Item_in_subselect::pushdown_cond_for_in_subquery(THD *thd, Item *cond)
+{
+ DBUG_ENTER("Item_in_subselect::pushdown_cond_for_in_subquery");
+ Item *remaining_cond= NULL;
+
+ if (!cond)
+ DBUG_RETURN(FALSE);
+
+ st_select_lex *sel = unit->first_select();
+
+ if (is_jtbm_const_tab)
+ DBUG_RETURN(FALSE);
+
+ if (!sel->cond_pushdown_is_allowed())
+ DBUG_RETURN(FALSE);
+
+ /*
+ Create a list of Field_pair items for this IN subquery.
+ It consists of the pairs of fields from the left part of this IN subquery
+ predicate 'left_part' and the respective fields from the select of the
+ right part of the IN subquery 'sel' (the field from left_part with the
+ corresponding field from the sel projection list).
+ Attach this list to the IN subquery.
+ */
+ corresponding_fields.empty();
+ List_iterator_fast<Item> it(sel->join->fields_list);
+ Item *item;
+ for (uint i= 0; i < left_expr->cols(); i++)
+ {
+ item= it++;
+ Item *elem= left_expr->element_index(i);
+
+ if (elem->real_item()->type() != Item::FIELD_ITEM)
+ continue;
+
+ if (corresponding_fields.push_back(
+ new Field_pair(((Item_field *)(elem->real_item()))->field,
+ item)))
+ DBUG_RETURN(TRUE);
+ }
+
+ /* 1. Check what pushable formula can be extracted from cond */
+ Item *extracted_cond;
+ cond->check_pushable_cond(&Item::pushable_cond_checker_for_subquery,
+ (uchar *)this);
+ /* 2. Build a clone PC of the formula that can be extracted */
+ extracted_cond=
+ cond->build_pushable_cond(thd,
+ &Item::pushable_equality_checker_for_subquery,
+ (uchar *)this);
+ /* Nothing to push */
+ if (!extracted_cond)
+ {
+ DBUG_RETURN(FALSE);
+ }
+
+ /* Collect fields that are used in the GROUP BY of sel */
+ st_select_lex *save_curr_select= thd->lex->current_select;
+ if (sel->have_window_funcs())
+ {
+ if (sel->group_list.first || sel->join->implicit_grouping)
+ goto exit;
+ ORDER *common_partition_fields=
+ sel->find_common_window_func_partition_fields(thd);
+ if (!common_partition_fields)
+ goto exit;
+
+ if (grouping_fields_in_the_in_subq_left_part(thd, sel, &corresponding_fields,
+ common_partition_fields))
+ DBUG_RETURN(TRUE);
+ }
+ else if (grouping_fields_in_the_in_subq_left_part(thd, sel,
+ &corresponding_fields,
+ sel->group_list.first))
+ DBUG_RETURN(TRUE);
+
+ /* Do 4-6 */
+ sel->pushdown_cond_into_where_clause(thd, extracted_cond,
+ &remaining_cond,
+ &Item::in_subq_field_transformer_for_where,
+ (uchar *) this);
+ if (!remaining_cond)
+ goto exit;
+ /*
+ 7. Prepare PC_having to be conjuncted with the HAVING clause of
+ the IN subquery
+ */
+ remaining_cond=
+ remaining_cond->transform(thd,
+ &Item::in_subq_field_transformer_for_having,
+ (uchar *)this);
+ if (!remaining_cond ||
+ remaining_cond->walk(&Item::cleanup_excluding_const_fields_processor,
+ 0, 0))
+ goto exit;
+
+ mark_or_conds_to_avoid_pushdown(remaining_cond);
+
+ sel->cond_pushed_into_having= remaining_cond;
+
+exit:
+ thd->lex->current_select= save_curr_select;
+ DBUG_RETURN(FALSE);
+}
diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h
index 509fb370fd7..d7978e9ef73 100644
--- a/sql/opt_subselect.h
+++ b/sql/opt_subselect.h
@@ -26,8 +26,11 @@ int check_and_do_in_subquery_rewrites(JOIN *join);
bool convert_join_subqueries_to_semijoins(JOIN *join);
int pull_out_semijoin_tables(JOIN *join);
bool optimize_semijoin_nests(JOIN *join, table_map all_table_map);
-bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
- Item **join_where);
+bool setup_degenerate_jtbm_semi_joins(JOIN *join,
+ List<TABLE_LIST> *join_list,
+ List<Item> &eq_list);
+bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
+ List<Item> &eq_list);
void cleanup_empty_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list);
// used by Loose_scan_opt
@@ -300,6 +303,7 @@ public:
pos->loosescan_picker.loosescan_parts= best_max_loose_keypart + 1;
pos->use_join_buffer= FALSE;
pos->table= tab;
+ pos->range_rowid_filter_info= tab->range_rowid_filter_info;
pos->ref_depend_map= best_ref_depend_map;
DBUG_PRINT("info", ("Produced a LooseScan plan, key %s, %s",
tab->table->key_info[best_loose_scan_key].name.str,
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index a6c56bbea37..27360d4a10c 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -318,7 +318,7 @@ int opt_sum_query(THD *thd,
error= tl->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
if (unlikely(error))
{
- tl->table->file->print_error(error, MYF(ME_FATALERROR));
+ tl->table->file->print_error(error, MYF(ME_FATAL));
DBUG_RETURN(error);
}
count*= tl->table->file->stats.records;
diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc
index c204ee4e066..3958797ec44 100644
--- a/sql/opt_table_elimination.cc
+++ b/sql/opt_table_elimination.cc
@@ -31,6 +31,8 @@
#include "mariadb.h"
#include "my_bit.h"
#include "sql_select.h"
+#include "opt_trace.h"
+#include "my_json_writer.h"
/*
OVERVIEW
@@ -522,7 +524,8 @@ eliminate_tables_for_list(JOIN *join,
List<TABLE_LIST> *join_list,
table_map tables_in_list,
Item *on_expr,
- table_map tables_used_elsewhere);
+ table_map tables_used_elsewhere,
+ Json_writer_array* trace_eliminate_tables);
static
bool check_func_dependency(JOIN *join,
table_map dep_tables,
@@ -541,7 +544,8 @@ static
Dep_module_expr *merge_eq_mods(Dep_module_expr *start,
Dep_module_expr *new_fields,
Dep_module_expr *end, uint and_level);
-static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl);
+static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl,
+ Json_writer_array* trace_eliminate_tables);
static
void add_module_expr(Dep_analysis_context *dac, Dep_module_expr **eq_mod,
uint and_level, Dep_value_field *field_val, Item *right,
@@ -608,6 +612,8 @@ void eliminate_tables(JOIN *join)
if (!optimizer_flag(thd, OPTIMIZER_SWITCH_TABLE_ELIMINATION))
DBUG_VOID_RETURN; /* purecov: inspected */
+ Json_writer_object trace_wrapper(thd);
+
/* Find the tables that are referred to from WHERE/HAVING */
used_tables= (join->conds? join->conds->used_tables() : 0) |
(join->having? join->having->used_tables() : 0);
@@ -617,12 +623,12 @@ void eliminate_tables(JOIN *join)
we should also take into account tables mentioned in "val".
*/
if (join->thd->lex->sql_command == SQLCOM_INSERT_SELECT &&
- join->select_lex == &thd->lex->select_lex)
+ join->select_lex == thd->lex->first_select_lex())
{
List_iterator<Item> val_it(thd->lex->value_list);
while ((item= val_it++))
{
- DBUG_ASSERT(item->fixed);
+ DBUG_ASSERT(item->is_fixed());
used_tables |= item->used_tables();
}
}
@@ -640,7 +646,7 @@ void eliminate_tables(JOIN *join)
used_tables |= (*(cur_list->item))->used_tables();
}
- if (join->select_lex == &thd->lex->select_lex)
+ if (join->select_lex == thd->lex->first_select_lex())
{
/* Multi-table UPDATE: don't eliminate tables referred from SET statement */
@@ -663,13 +669,14 @@ void eliminate_tables(JOIN *join)
}
}
}
-
+
table_map all_tables= join->all_tables_map();
+ Json_writer_array trace_eliminated_tables(thd,"eliminated_tables");
if (all_tables & ~used_tables)
{
/* There are some tables that we probably could eliminate. Try it. */
eliminate_tables_for_list(join, join->join_list, all_tables, NULL,
- used_tables);
+ used_tables, &trace_eliminated_tables);
}
DBUG_VOID_RETURN;
}
@@ -712,7 +719,8 @@ void eliminate_tables(JOIN *join)
static bool
eliminate_tables_for_list(JOIN *join, List<TABLE_LIST> *join_list,
table_map list_tables, Item *on_expr,
- table_map tables_used_elsewhere)
+ table_map tables_used_elsewhere,
+ Json_writer_array *trace_eliminate_tables)
{
TABLE_LIST *tbl;
List_iterator<TABLE_LIST> it(*join_list);
@@ -734,9 +742,10 @@ eliminate_tables_for_list(JOIN *join, List<TABLE_LIST> *join_list,
&tbl->nested_join->join_list,
tbl->nested_join->used_tables,
tbl->on_expr,
- outside_used_tables))
+ outside_used_tables,
+ trace_eliminate_tables))
{
- mark_as_eliminated(join, tbl);
+ mark_as_eliminated(join, tbl, trace_eliminate_tables);
}
else
all_eliminated= FALSE;
@@ -748,7 +757,7 @@ eliminate_tables_for_list(JOIN *join, List<TABLE_LIST> *join_list,
check_func_dependency(join, tbl->table->map, NULL, tbl,
tbl->on_expr))
{
- mark_as_eliminated(join, tbl);
+ mark_as_eliminated(join, tbl, trace_eliminate_tables);
}
else
all_eliminated= FALSE;
@@ -1788,7 +1797,8 @@ Dep_module* Dep_value_field::get_next_unbound_module(Dep_analysis_context *dac,
Mark one table or the whole join nest as eliminated.
*/
-static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl)
+static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl,
+ Json_writer_array* trace_eliminate_tables)
{
TABLE *table;
/*
@@ -1801,7 +1811,7 @@ static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl)
TABLE_LIST *child;
List_iterator<TABLE_LIST> it(tbl->nested_join->join_list);
while ((child= it++))
- mark_as_eliminated(join, child);
+ mark_as_eliminated(join, child, trace_eliminate_tables);
}
else if ((table= tbl->table))
{
@@ -1812,6 +1822,7 @@ static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl)
tab->type= JT_CONST;
tab->table->const_table= 1;
join->eliminated_tables |= table->map;
+ trace_eliminate_tables->add(table->alias.c_ptr_safe());
join->const_table_map|= table->map;
set_position(join, join->const_tables++, tab, (KEYUSE*)0);
}
diff --git a/sql/opt_trace.cc b/sql/opt_trace.cc
new file mode 100644
index 00000000000..9957e524e90
--- /dev/null
+++ b/sql/opt_trace.cc
@@ -0,0 +1,745 @@
+/* This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mariadb.h"
+#include "sql_array.h"
+#include "sql_string.h"
+#include "sql_class.h"
+#include "sql_show.h"
+#include "field.h"
+#include "table.h"
+#include "opt_trace.h"
+#include "sql_parse.h"
+#include "set_var.h"
+#include "my_json_writer.h"
+#include "sp_head.h"
+
+#include "rowid_filter.h"
+
+const char I_S_table_name[]= "OPTIMIZER_TRACE";
+
+/**
+ Whether a list of tables contains information_schema.OPTIMIZER_TRACE.
+ @param tbl list of tables
+
+ Can we do better than this here??
+ @note this does not catch that a stored routine or view accesses
+ the OPTIMIZER_TRACE table. So using a stored routine or view to read
+ OPTIMIZER_TRACE will overwrite OPTIMIZER_TRACE as it runs and provide
+ uninteresting info.
+*/
+bool list_has_optimizer_trace_table(const TABLE_LIST *tbl)
+{
+ for (; tbl; tbl= tbl->next_global)
+ {
+ if (tbl->schema_table &&
+ 0 == strcmp(tbl->schema_table->table_name, I_S_table_name))
+ return true;
+ }
+ return false;
+}
+
+/*
+ Returns if a query has a set command with optimizer_trace being switched on/off.
+ True: Don't trace the query(uninteresting)
+*/
+
+bool sets_var_optimizer_trace(enum enum_sql_command sql_command,
+ List<set_var_base> *set_vars)
+{
+ if (sql_command == SQLCOM_SET_OPTION)
+ {
+ List_iterator_fast<set_var_base> it(*set_vars);
+ const set_var_base *var;
+ while ((var= it++))
+ if (var->is_var_optimizer_trace()) return true;
+ }
+ return false;
+}
+
+
+ST_FIELD_INFO optimizer_trace_info[]=
+{
+ /* name, length, type, value, maybe_null, old_name, open_method */
+ {"QUERY", 65535, MYSQL_TYPE_STRING, 0, false, NULL, SKIP_OPEN_TABLE},
+ {"TRACE", 65535, MYSQL_TYPE_STRING, 0, false, NULL, SKIP_OPEN_TABLE},
+ {"MISSING_BYTES_BEYOND_MAX_MEM_SIZE", 20, MYSQL_TYPE_LONG, 0, false, NULL,
+ SKIP_OPEN_TABLE},
+ {"INSUFFICIENT_PRIVILEGES", 1, MYSQL_TYPE_TINY, 0, false, NULL,
+ SKIP_OPEN_TABLE},
+ {NULL, 0, MYSQL_TYPE_STRING, 0, true, NULL, 0}
+};
+
+/*
+ TODO: one-line needs to be implemented seperately
+*/
+const char *Opt_trace_context::flag_names[]= {"enabled", "default",
+ NullS};
+
+/*
+ Returns if a particular command will be traced or not
+*/
+
+inline bool sql_command_can_be_traced(enum enum_sql_command sql_command)
+{
+ /*
+ For first iteration we are only allowing select queries.
+ TODO: change to allow other queries.
+ */
+ return sql_command == SQLCOM_SELECT ||
+ sql_command == SQLCOM_UPDATE ||
+ sql_command == SQLCOM_DELETE ||
+ sql_command == SQLCOM_DELETE_MULTI ||
+ sql_command == SQLCOM_UPDATE_MULTI;
+}
+
+void opt_trace_print_expanded_query(THD *thd, SELECT_LEX *select_lex,
+ Json_writer_object *writer)
+
+{
+ DBUG_ASSERT(thd->trace_started());
+
+ StringBuffer<1024> str(system_charset_info);
+ ulonglong save_option_bits= thd->variables.option_bits;
+ thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE;
+ select_lex->print(thd, &str,
+ enum_query_type(QT_TO_SYSTEM_CHARSET |
+ QT_SHOW_SELECT_NUMBER |
+ QT_ITEM_IDENT_SKIP_DB_NAMES |
+ QT_VIEW_INTERNAL));
+ thd->variables.option_bits= save_option_bits;
+ /*
+ The output is not very pretty lots of back-ticks, the output
+ is as the one in explain extended , lets try to improved it here.
+ */
+ writer->add("expanded_query", str.c_ptr_safe(), str.length());
+}
+
+void opt_trace_disable_if_no_security_context_access(THD *thd)
+{
+ if (likely(!(thd->variables.optimizer_trace &
+ Opt_trace_context::FLAG_ENABLED)) || // (1)
+ thd->system_thread) // (2)
+ {
+ /*
+ (1) We know that the routine's execution starts with "enabled=off".
+ If it stays so until the routine ends, we needn't do security checks on
+ the routine.
+ If it does not stay so, it means the definer sets it to "on" somewhere
+ in the routine's body. Then it is his conscious decision to generate
+ traces, thus it is still correct to skip the security check.
+
+ (2) Threads of the Events Scheduler have an unusual security context
+ (thd->m_main_security_ctx.priv_user==NULL, see comment in
+ Security_context::change_security_context()).
+ */
+ return;
+ }
+ Opt_trace_context *const trace= &thd->opt_trace;
+ if (!thd->trace_started())
+ {
+ /*
+ @@optimizer_trace has "enabled=on" but trace is not started.
+ Either Opt_trace_start ctor was not called for our statement (3), or it
+ was called but at that time, the variable had "enabled=off" (4).
+
+ There are no known cases of (3).
+
+ (4) suggests that the user managed to change the variable during
+ execution of the statement, and this statement is using
+ view/routine (note that we have not been able to provoke this, maybe
+ this is impossible). If it happens it is suspicious.
+
+ We disable I_S output. And we cannot do otherwise: we have no place to
+ store a possible "missing privilege" information (no Opt_trace_stmt, as
+ is_started() is false), so cannot do security checks, so cannot safely
+ do tracing, so have to disable I_S output. And even then, we don't know
+ when to re-enable I_S output, as we have no place to store the
+ information "re-enable tracing at the end of this statement", and we
+ don't even have a notion of statement here (statements in the optimizer
+ trace world mean an Opt_trace_stmt object, and there is none here). So
+ we must disable for the session's life.
+
+ COM_FIELD_LIST opens views, thus used to be a case of (3). To avoid
+ disabling I_S output for the session's life when this command is issued
+ (like in: "SET OPTIMIZER_TRACE='ENABLED=ON';USE somedb;" in the 'mysql'
+ command-line client), we have decided to create a Opt_trace_start for
+ this command. The command itself is not traced though
+ (SQLCOM_SHOW_FIELDS does not have CF_OPTIMIZER_TRACE).
+ */
+ return;
+ }
+ /*
+ Note that thd->main_security_ctx.master_access is probably invariant
+ accross the life of THD: GRANT/REVOKE don't affect global privileges of an
+ existing connection, per the manual.
+ */
+ if (!(thd->main_security_ctx.check_access(GLOBAL_ACLS & ~GRANT_ACL)) &&
+ (0 != strcmp(thd->main_security_ctx.priv_user,
+ thd->security_context()->priv_user) ||
+ 0 != my_strcasecmp(system_charset_info,
+ thd->main_security_ctx.priv_host,
+ thd->security_context()->priv_host)))
+ trace->missing_privilege();
+}
+
+void opt_trace_disable_if_no_stored_proc_func_access(THD *thd, sp_head *sp)
+{
+ if (likely(!(thd->variables.optimizer_trace &
+ Opt_trace_context::FLAG_ENABLED)) ||
+ thd->system_thread ||
+ !thd->trace_started())
+ return;
+
+ Opt_trace_context *const trace= &thd->opt_trace;
+ bool full_access;
+ Security_context *const backup_thd_sctx= thd->security_context();
+ thd->set_security_context(&thd->main_security_ctx);
+ const bool rc= check_show_routine_access(thd, sp, &full_access) || !full_access;
+ thd->set_security_context(backup_thd_sctx);
+ if (rc)
+ trace->missing_privilege();
+}
+
+/**
+ If tracing is on, checks additional privileges on a list of tables/views,
+ to make sure that the user has the right to do SHOW CREATE TABLE/VIEW and
+ "SELECT *". For that:
+ - this functions checks table-level SELECT
+ - which is sufficient for SHOW CREATE TABLE and "SELECT *", if a base table
+ - if a view, if the view has not been identified as such then
+ opt_trace_disable_if_no_view_access() will be later called and check SHOW
+ VIEW; other we check SHOW VIEW here; SHOW VIEW + SELECT is sufficient for
+ SHOW CREATE VIEW.
+ If a privilege is missing, notifies the trace system.
+
+ @param thd
+ @param tbl list of tables to check
+*/
+
+void opt_trace_disable_if_no_tables_access(THD *thd, TABLE_LIST *tbl)
+{
+ if (likely(!(thd->variables.optimizer_trace &
+ Opt_trace_context::FLAG_ENABLED)) ||
+ thd->system_thread ||
+ !thd->trace_started())
+ return;
+
+ Opt_trace_context *const trace= &thd->opt_trace;
+ Security_context *const backup_thd_sctx= thd->security_context();
+ thd->set_security_context(&thd->main_security_ctx);
+ const TABLE_LIST *const first_not_own_table= thd->lex->first_not_own_table();
+ for (TABLE_LIST *t= tbl; t != NULL && t != first_not_own_table;
+ t= t->next_global)
+ {
+ /*
+ Anonymous derived tables (as in
+ "SELECT ... FROM (SELECT ...)") don't have their grant.privilege set.
+ */
+ if (!t->is_anonymous_derived_table())
+ {
+ const GRANT_INFO backup_grant_info= t->grant;
+ Security_context *const backup_table_sctx= t->security_ctx;
+ t->security_ctx= NULL;
+ /*
+ (1) check_table_access() fills t->grant.privilege.
+ (2) Because SELECT privileges can be column-based,
+ check_table_access() will return 'false' as long as there is SELECT
+ privilege on one column. But we want a table-level privilege.
+ */
+
+ bool rc =
+ check_table_access(thd, SELECT_ACL, t, false, 1, true) || // (1)
+ ((t->grant.privilege & SELECT_ACL) == 0); // (2)
+ if (t->is_view())
+ {
+ /*
+ It's a view which has already been opened: we are executing a
+ prepared statement. The view has been unfolded in the global list of
+ tables. So underlying tables will be automatically checked in the
+ present function, but we need an explicit check of SHOW VIEW:
+ */
+ rc |= check_table_access(thd, SHOW_VIEW_ACL, t, false, 1, true);
+ }
+ t->security_ctx= backup_table_sctx;
+ t->grant= backup_grant_info;
+ if (rc)
+ {
+ trace->missing_privilege();
+ break;
+ }
+ }
+ }
+ thd->set_security_context(backup_thd_sctx);
+ return;
+}
+
+void opt_trace_disable_if_no_view_access(THD *thd, TABLE_LIST *view,
+ TABLE_LIST *underlying_tables)
+{
+
+ if (likely(!(thd->variables.optimizer_trace &
+ Opt_trace_context::FLAG_ENABLED)) ||
+ thd->system_thread ||
+ !thd->trace_started())
+ return;
+
+ Opt_trace_context *const trace= &thd->opt_trace;
+ Security_context *const backup_table_sctx= view->security_ctx;
+ Security_context *const backup_thd_sctx= thd->security_context();
+ const GRANT_INFO backup_grant_info= view->grant;
+
+ view->security_ctx= NULL; // no SUID context for view
+ // no SUID context for THD
+ thd->set_security_context(&thd->main_security_ctx);
+ const int rc= check_table_access(thd, SHOW_VIEW_ACL, view, false, 1, true);
+
+ view->security_ctx= backup_table_sctx;
+ thd->set_security_context(backup_thd_sctx);
+ view->grant= backup_grant_info;
+
+ if (rc)
+ {
+ trace->missing_privilege();
+ return;
+ }
+ /*
+ We needn't check SELECT privilege on this view. Some
+ opt_trace_disable_if_no_tables_access() call has or will check it.
+
+ Now we check underlying tables/views of our view:
+ */
+ opt_trace_disable_if_no_tables_access(thd, underlying_tables);
+ return;
+}
+
+
+/**
+ @class Opt_trace_stmt
+
+ The trace of one statement.
+*/
+
+Opt_trace_stmt::Opt_trace_stmt(Opt_trace_context *ctx_arg)
+{
+ ctx= ctx_arg;
+ current_json= new Json_writer();
+ missing_priv= false;
+ I_S_disabled= 0;
+}
+
+Opt_trace_stmt::~Opt_trace_stmt()
+{
+ delete current_json;
+}
+
+size_t Opt_trace_stmt::get_length()
+{
+ return current_json->output.length();
+}
+
+size_t Opt_trace_stmt::get_truncated_bytes()
+{
+ return current_json->get_truncated_bytes();
+}
+
+void Opt_trace_stmt::set_query(const char *query_ptr, size_t length,
+ const CHARSET_INFO *charset)
+{
+ query.append(query_ptr, length, charset);
+}
+
+void Opt_trace_context::missing_privilege()
+{
+ if (current_trace)
+ current_trace->missing_privilege();
+}
+
+void Opt_trace_context::set_allowed_mem_size(size_t mem_size)
+{
+ current_trace->set_allowed_mem_size(mem_size);
+}
+
+/*
+ TODO: In future when we would be saving multiple trace,
+ this function would return
+ max_mem_size - memory_occupied_by_the_saved_traces
+*/
+
+size_t Opt_trace_context::remaining_mem_size()
+{
+ return max_mem_size;
+}
+
+/*
+ Disable tracing for children if the current trace is already present.
+ Currently only one trace is stored and there is no mechanism
+ to restore traces, so disabling tracing for children is the best option.
+*/
+
+bool Opt_trace_context::disable_tracing_if_required()
+{
+ if (current_trace)
+ {
+ current_trace->disable_tracing_for_children();
+ return true;
+ }
+ return false;
+}
+
+bool Opt_trace_context::enable_tracing_if_required()
+{
+ if (current_trace)
+ {
+ current_trace->enable_tracing_for_children();
+ return true;
+ }
+ return false;
+}
+
+bool Opt_trace_context::is_enabled()
+{
+ if (current_trace)
+ return current_trace->is_enabled();
+ return false;
+}
+
+Opt_trace_context::Opt_trace_context()
+{
+ current_trace= NULL;
+ max_mem_size= 0;
+}
+Opt_trace_context::~Opt_trace_context()
+{
+ delete_traces();
+}
+
+void Opt_trace_context::set_query(const char *query, size_t length, const CHARSET_INFO *charset)
+{
+ current_trace->set_query(query, length, charset);
+}
+
+void Opt_trace_context::start(THD *thd, TABLE_LIST *tbl,
+ enum enum_sql_command sql_command,
+ const char *query,
+ size_t query_length,
+ const CHARSET_INFO *query_charset,
+ ulong max_mem_size_arg)
+{
+ /*
+ This is done currently because we don't want to have multiple
+ traces open at the same time, so as soon as a new trace is created
+ we forcefully end the previous one, if it has not ended by itself.
+ This would mostly happen with stored functions or procedures.
+
+ TODO: handle multiple traces
+ */
+ DBUG_ASSERT(!current_trace);
+ current_trace= new Opt_trace_stmt(this);
+ max_mem_size= max_mem_size_arg;
+ set_allowed_mem_size(remaining_mem_size());
+}
+
+void Opt_trace_context::end()
+{
+ if (current_trace)
+ traces.push(current_trace);
+
+ if (!traces.elements())
+ return;
+ if (traces.elements() > 1)
+ {
+ Opt_trace_stmt *prev= traces.at(0);
+ delete prev;
+ traces.del(0);
+ }
+ current_trace= NULL;
+}
+
+Opt_trace_start::Opt_trace_start(THD *thd, TABLE_LIST *tbl,
+ enum enum_sql_command sql_command,
+ List<set_var_base> *set_vars,
+ const char *query,
+ size_t query_length,
+ const CHARSET_INFO *query_charset):ctx(&thd->opt_trace)
+{
+ /*
+ if optimizer trace is enabled and the statment we have is traceable,
+ then we start the context.
+ */
+ const ulonglong var= thd->variables.optimizer_trace;
+ traceable= FALSE;
+ if (unlikely(var & Opt_trace_context::FLAG_ENABLED) &&
+ sql_command_can_be_traced(sql_command) &&
+ !list_has_optimizer_trace_table(tbl) &&
+ !sets_var_optimizer_trace(sql_command, set_vars) &&
+ !thd->system_thread &&
+ !ctx->disable_tracing_if_required())
+ {
+ ctx->start(thd, tbl, sql_command, query, query_length, query_charset,
+ thd->variables.optimizer_trace_max_mem_size);
+ ctx->set_query(query, query_length, query_charset);
+ traceable= TRUE;
+ opt_trace_disable_if_no_tables_access(thd, tbl);
+ }
+}
+
+Opt_trace_start::~Opt_trace_start()
+{
+ if (traceable)
+ {
+ ctx->end();
+ traceable= FALSE;
+ }
+ else
+ {
+ ctx->enable_tracing_if_required();
+ }
+}
+
+void Opt_trace_stmt::fill_info(Opt_trace_info* info)
+{
+ if (unlikely(info->missing_priv= get_missing_priv()))
+ {
+ info->trace_ptr= info->query_ptr= "";
+ info->trace_length= info->query_length= 0;
+ info->query_charset= &my_charset_bin;
+ info->missing_bytes= 0;
+ }
+ else
+ {
+ info->trace_ptr= current_json->output.get_string()->ptr();
+ info->trace_length= get_length();
+ info->query_ptr= query.ptr();
+ info->query_length= query.length();
+ info->query_charset= query.charset();
+ info->missing_bytes= get_truncated_bytes();
+ info->missing_priv= get_missing_priv();
+ }
+}
+
+void Opt_trace_stmt::missing_privilege()
+{
+ missing_priv= true;
+}
+
+void Opt_trace_stmt::disable_tracing_for_children()
+{
+ ++I_S_disabled;
+}
+
+void Opt_trace_stmt::enable_tracing_for_children()
+{
+ if (I_S_disabled)
+ --I_S_disabled;
+}
+
+void Opt_trace_stmt::set_allowed_mem_size(size_t mem_size)
+{
+ current_json->set_size_limit(mem_size);
+}
+
+/*
+ Prefer this when you are iterating over JOIN_TABs
+*/
+
+void Json_writer::add_table_name(const JOIN_TAB *tab)
+{
+ DBUG_ASSERT(tab->join->thd->trace_started());
+ if (tab != NULL)
+ {
+ char table_name_buffer[SAFE_NAME_LEN];
+ if (tab->table && tab->table->derived_select_number)
+ {
+ /* Derived table name generation */
+ size_t len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1,
+ "<derived%u>",
+ tab->table->derived_select_number);
+ add_str(table_name_buffer, len);
+ }
+ else if (tab->bush_children)
+ {
+ JOIN_TAB *ctab= tab->bush_children->start;
+ size_t len= my_snprintf(table_name_buffer,
+ sizeof(table_name_buffer)-1,
+ "<subquery%d>",
+ ctab->emb_sj_nest->sj_subq_pred->get_identifier());
+ add_str(table_name_buffer, len);
+ }
+ else
+ {
+ TABLE_LIST *real_table= tab->table->pos_in_table_list;
+ add_str(real_table->alias.str, real_table->alias.length);
+ }
+ }
+ else
+ DBUG_ASSERT(0);
+}
+
+void Json_writer::add_table_name(const TABLE *table)
+{
+ add_str(table->pos_in_table_list->alias.str);
+}
+
+
+void add_table_scan_values_to_trace(THD *thd, JOIN_TAB *tab)
+{
+ DBUG_ASSERT(thd->trace_started());
+ Json_writer_object table_records(thd);
+ table_records.add_table_name(tab);
+ Json_writer_object table_rec(thd, "table_scan");
+ table_rec.add("rows", tab->found_records)
+ .add("cost", tab->read_time);
+}
+
+
+/*
+ @brief
+ Add the tables inside a partial join to the optimizer trace
+
+ @param join join handler
+ @param idx length of the partial QEP in 'join->positions'
+ @table_map map of all non-const tables of the join
+
+ @note
+ This function is used during best_access_path to print the tables
+ inside the partial join that were considered doing the cost based
+ analysis of the various join orders.
+*/
+
+void trace_plan_prefix(JOIN *join, uint idx, table_map join_tables)
+{
+ THD *const thd= join->thd;
+ DBUG_ASSERT(thd->trace_started());
+
+ Json_writer_array plan_prefix(thd, "plan_prefix");
+ for (uint i= 0; i < idx; i++)
+ {
+ TABLE_LIST *const tr= join->positions[i].table->tab_list;
+ if (!(tr->map & join_tables))
+ plan_prefix.add_table_name(join->positions[i].table);
+ }
+}
+
+
+/*
+ Print the join order of all the tables for top level select.
+
+ For example:
+
+ select * from ot1
+ where ot1.a IN (select it1.a from it1, it2 where it1.b=it2.a);
+
+ So this function would print
+ ot1, <subquery2> ----> For select #1
+*/
+
+void print_final_join_order(JOIN *join)
+{
+ DBUG_ASSERT(join->thd->trace_started());
+
+ Json_writer_object join_order(join->thd);
+ Json_writer_array best_order(join->thd, "best_join_order");
+ JOIN_TAB *j;
+ uint i;
+ for (j= join->join_tab,i=0 ; i < join->top_join_tab_count;
+ i++, j++)
+ best_order.add_table_name(j);
+}
+
+
+void print_best_access_for_table(THD *thd, POSITION *pos,
+ enum join_type type)
+{
+ DBUG_ASSERT(thd->trace_started());
+
+ Json_writer_object obj(thd, "chosen_access_method");
+ obj.add("type", type == JT_ALL ? "scan" : join_type_str[type]);
+ obj.add("records", pos->records_read);
+ obj.add("cost", pos->read_time);
+ obj.add("uses_join_buffering", pos->use_join_buffer);
+ if (pos->range_rowid_filter_info)
+ {
+ uint key_no= pos->range_rowid_filter_info->key_no;
+ obj.add("rowid_filter_key",
+ pos->table->table->key_info[key_no].name);
+ }
+}
+
+
+/*
+ Introduce enum_query_type flags parameter, maybe also allow
+ EXPLAIN also use this function.
+*/
+
+void Json_writer::add_str(Item *item)
+{
+ if (item)
+ {
+ THD *thd= current_thd;
+ StringBuffer<256> str(system_charset_info);
+
+ ulonglong save_option_bits= thd->variables.option_bits;
+ thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE;
+ item->print(&str,
+ enum_query_type(QT_TO_SYSTEM_CHARSET | QT_SHOW_SELECT_NUMBER
+ | QT_ITEM_IDENT_SKIP_DB_NAMES));
+ thd->variables.option_bits= save_option_bits;
+ add_str(str.c_ptr_safe());
+ }
+ else
+ add_null();
+}
+
+void Opt_trace_context::delete_traces()
+{
+ if (traces.elements())
+ {
+ while (traces.elements())
+ {
+ Opt_trace_stmt *prev= traces.at(0);
+ delete prev;
+ traces.del(0);
+ }
+ }
+}
+
+
+int fill_optimizer_trace_info(THD *thd, TABLE_LIST *tables, Item *)
+{
+ TABLE *table= tables->table;
+ Opt_trace_info info;
+
+ /* get_values of trace, query , missing bytes and missing_priv
+
+ @todo: Need an iterator here to walk over all the traces
+ */
+ Opt_trace_context* ctx= &thd->opt_trace;
+
+ if (!thd->opt_trace.empty())
+ {
+ Opt_trace_stmt *stmt= ctx->get_top_trace();
+ stmt->fill_info(&info);
+
+ table->field[0]->store(info.query_ptr, static_cast<uint>(info.query_length),
+ info.query_charset);
+ table->field[1]->store(info.trace_ptr, static_cast<uint>(info.trace_length),
+ system_charset_info);
+ table->field[2]->store(info.missing_bytes, true);
+ table->field[3]->store(info.missing_priv, true);
+ // Store in IS
+ if (schema_table_store_record(thd, table))
+ return 1;
+ }
+ return 0;
+}
diff --git a/sql/opt_trace.h b/sql/opt_trace.h
new file mode 100644
index 00000000000..550f18c0797
--- /dev/null
+++ b/sql/opt_trace.h
@@ -0,0 +1,210 @@
+#ifndef OPT_TRACE_INCLUDED
+#define OPT_TRACE_INCLUDED
+/* This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "opt_trace_context.h" // Opt_trace_context
+#include "sql_lex.h"
+#include "my_json_writer.h"
+#include "sql_select.h"
+class Item;
+class THD;
+struct TABLE_LIST;
+
+/*
+ User-visible information about a trace.
+*/
+
+struct Opt_trace_info
+{
+ /**
+ String containing trace.
+ If trace has been end()ed, this is 0-terminated, which is only to aid
+ debugging or unit testing; this property is not relied upon in normal
+ server usage.
+ If trace has not been ended, this is not 0-terminated. That rare case can
+ happen when a substatement reads OPTIMIZER_TRACE (at that stage, the top
+ statement is still executing so its trace is not ended yet, but may still
+ be read by the sub-statement).
+ */
+ const char *trace_ptr;
+ size_t trace_length;
+ //// String containing original query.
+ const char *query_ptr;
+ size_t query_length;
+ const CHARSET_INFO *query_charset; ///< charset of query string
+ /**
+ How many bytes this trace is missing (for traces which were truncated
+ because of @@@@optimizer-trace-max-mem-size).
+ The trace is not extended beyond trace-max-mem-size.
+ */
+ size_t missing_bytes;
+ /*
+ Whether user lacks privilege to see this trace.
+ If this is set to TRUE, then we return an empty trace
+ */
+ bool missing_priv;
+};
+
+/**
+ Instantiate this class to start tracing a THD's actions (generally at a
+ statement's start), and to set the "original" query (not transformed, as
+ sent by client) for the new trace. Destructor will end the trace.
+
+ @param thd the THD
+ @param tbl list of tables read/written by the statement.
+ @param sql_command SQL command being prepared or executed
+ @param set_vars what variables are set by this command (only used if
+ sql_command is SQLCOM_SET_OPTION)
+ @param query query
+ @param length query's length
+ @param charset charset which was used to encode this query
+*/
+
+
+class Opt_trace_start {
+ public:
+ Opt_trace_start(THD *thd_arg, TABLE_LIST *tbl,
+ enum enum_sql_command sql_command,
+ List<set_var_base> *set_vars,
+ const char *query,
+ size_t query_length,
+ const CHARSET_INFO *query_charset);
+ ~Opt_trace_start();
+
+ private:
+ Opt_trace_context *const ctx;
+ /*
+ True: the query will be traced
+ False: otherwise
+ */
+ bool traceable;
+};
+
+/**
+ Prints SELECT query to optimizer trace. It is not the original query (as in
+ @c Opt_trace_context::set_query()) but a printout of the parse tree
+ (Item-s).
+ @param thd the THD
+ @param select_lex query's parse tree
+ @param trace_object Json_writer object to which the query will be added
+*/
+void opt_trace_print_expanded_query(THD *thd, SELECT_LEX *select_lex,
+ Json_writer_object *trace_object);
+
+void add_table_scan_values_to_trace(THD *thd, JOIN_TAB *tab);
+void trace_plan_prefix(JOIN *join, uint idx, table_map join_tables);
+void print_final_join_order(JOIN *join);
+void print_best_access_for_table(THD *thd, POSITION *pos,
+ enum join_type type);
+
+/*
+ Security related (need to add a proper comment here)
+*/
+
+/**
+ If the security context is not that of the connected user, inform the trace
+ system that a privilege is missing. With one exception: see below.
+
+ @param thd
+
+ This serves to eliminate the following issue.
+ Any information readable by a SELECT may theoretically end up in
+ the trace. And a SELECT may read information from other places than tables:
+ - from views (reading their bodies)
+ - from stored routines (reading their bodies)
+ - from files (reading their content), with LOAD_FILE()
+ - from the list of connections (reading their queries...), with
+ I_S.PROCESSLIST.
+ If the connected user has EXECUTE privilege on a routine which does a
+ security context change, the routine can retrieve information internally
+ (if allowed by the SUID context's privileges), and present only a portion
+ of it to the connected user. But with tracing on, all information is
+ possibly in the trace. So the connected user receives more information than
+ the routine's definer intended to provide. Fixing this issue would require
+ adding, near many privilege checks in the server, a new
+ optimizer-trace-specific check done against the connected user's context,
+ to verify that the connected user has the right to see the retrieved
+ information.
+
+ Instead, our chosen simpler solution is that if we see a security context
+ change where SUID user is not the connected user, we disable tracing. With
+ only one safe exception: if the connected user has all global privileges
+ (because then she/he can find any information anyway). By "all global
+ privileges" we mean everything but WITH GRANT OPTION (that latter one isn't
+ related to information gathering).
+
+ Read access to I_S.OPTIMIZER_TRACE by another user than the connected user
+ is restricted: @see fill_optimizer_trace_info().
+*/
+void opt_trace_disable_if_no_security_context_access(THD *thd);
+
+void opt_trace_disable_if_no_tables_access(THD *thd, TABLE_LIST *tbl);
+
+/**
+ If tracing is on, checks additional privileges for a view, to make sure
+ that the user has the right to do SHOW CREATE VIEW. For that:
+ - this function checks SHOW VIEW
+ - SELECT is tested in opt_trace_disable_if_no_tables_access()
+ - SELECT + SHOW VIEW is sufficient for SHOW CREATE VIEW.
+ We also check underlying tables.
+ If a privilege is missing, notifies the trace system.
+ This function should be called when the view's underlying tables have not
+ yet been merged.
+
+ @param thd THD context
+ @param view view to check
+ @param underlying_tables underlying tables/views of 'view'
+ */
+
+void opt_trace_disable_if_no_view_access(THD *thd, TABLE_LIST *view,
+ TABLE_LIST *underlying_tables);
+
+/**
+ If tracing is on, checks additional privileges on a stored routine, to make
+ sure that the user has the right to do SHOW CREATE PROCEDURE/FUNCTION. For
+ that, we use the same checks as in those SHOW commands.
+ If a privilege is missing, notifies the trace system.
+
+ This function is not redundant with
+ opt_trace_disable_if_no_security_context_access().
+ Indeed, for a SQL SECURITY INVOKER routine, there is no context change, but
+ we must still verify that the invoker can do SHOW CREATE.
+
+ For triggers, see note in sp_head::execute_trigger().
+
+ @param thd
+ @param sp routine to check
+ */
+void opt_trace_disable_if_no_stored_proc_func_access(THD *thd, sp_head *sp);
+
+/**
+ Fills information_schema.OPTIMIZER_TRACE with rows (one per trace)
+ @retval 0 ok
+ @retval 1 error
+*/
+int fill_optimizer_trace_info(THD *thd, TABLE_LIST *tables, Item *);
+
+#define OPT_TRACE_TRANSFORM(thd, object_level0, object_level1, \
+ select_number, from, to) \
+ Json_writer_object object_level0(thd); \
+ Json_writer_object object_level1(thd, "transformation"); \
+ object_level1.add_select_number(select_number).add("from", from).add("to", to);
+
+#define OPT_TRACE_VIEWS_TRANSFORM(thd, object_level0, object_level1, \
+ derived, name, select_number, algorithm) \
+ Json_writer_object trace_wrapper(thd); \
+ Json_writer_object trace_derived(thd, derived); \
+ trace_derived.add("table", name).add_select_number(select_number) \
+ .add("algorithm", algorithm);
+#endif
diff --git a/sql/opt_trace_context.h b/sql/opt_trace_context.h
new file mode 100644
index 00000000000..f578a0c67ec
--- /dev/null
+++ b/sql/opt_trace_context.h
@@ -0,0 +1,135 @@
+#ifndef OPT_TRACE_CONTEXT_INCLUDED
+#define OPT_TRACE_CONTEXT_INCLUDED
+
+#include "sql_array.h"
+
+class Opt_trace_context;
+struct Opt_trace_info;
+class Json_writer;
+
+class Opt_trace_stmt {
+ public:
+ /**
+ Constructor, starts a trace for information_schema and dbug.
+ @param ctx_arg context
+ */
+ Opt_trace_stmt(Opt_trace_context *ctx_arg);
+ ~Opt_trace_stmt();
+ void set_query(const char *query_ptr, size_t length, const CHARSET_INFO *charset);
+ void open_struct(const char *key, char opening_bracket);
+ void close_struct(const char *saved_key, char closing_bracket);
+ void fill_info(Opt_trace_info* info);
+ void add(const char *key, char *opening_bracket, size_t val_length);
+ Json_writer* get_current_json() {return current_json;}
+ void missing_privilege();
+ void disable_tracing_for_children();
+ void enable_tracing_for_children();
+ bool is_enabled()
+ {
+ return I_S_disabled == 0;
+ }
+ void set_allowed_mem_size(size_t mem_size);
+ size_t get_length();
+ size_t get_truncated_bytes();
+ bool get_missing_priv() { return missing_priv; }
+
+private:
+ Opt_trace_context *ctx;
+ String query; // store the query sent by the user
+ Json_writer *current_json; // stores the trace
+ bool missing_priv; ///< whether user lacks privilege to see this trace
+ /*
+ 0 <=> this trace should be in information_schema.
+ !=0 tracing is disabled, this currently happens when we want to trace a
+ sub-statement. For now traces are only collect for the top statement
+ not for the sub-statments.
+ */
+ uint I_S_disabled;
+};
+
+
+class Opt_trace_context
+{
+public:
+ Opt_trace_context();
+ ~Opt_trace_context();
+
+ void start(THD *thd, TABLE_LIST *tbl,
+ enum enum_sql_command sql_command,
+ const char *query,
+ size_t query_length,
+ const CHARSET_INFO *query_charset,
+ ulong max_mem_size_arg);
+ void end();
+ void set_query(const char *query, size_t length, const CHARSET_INFO *charset);
+ void delete_traces();
+ void set_allowed_mem_size(size_t mem_size);
+ size_t remaining_mem_size();
+
+private:
+ Opt_trace_stmt* top_trace()
+ {
+ return *(traces.front());
+ }
+
+public:
+
+ /*
+ This returns the top trace from the list of traces. This function
+ is used when we want to see the contents of the INFORMATION_SCHEMA.OPTIMIZER_TRACE
+ table.
+ */
+
+ Opt_trace_stmt* get_top_trace()
+ {
+ if (!traces.elements())
+ return NULL;
+ return top_trace();
+ }
+
+ /*
+ This returns the current trace, to which we are still writing and has not been finished
+ */
+
+ Json_writer* get_current_json()
+ {
+ if (!is_started())
+ return NULL;
+ return current_trace->get_current_json();
+ }
+
+ bool empty()
+ {
+ return static_cast<uint>(traces.elements()) == 0;
+ }
+
+ bool is_started()
+ {
+ return current_trace && current_trace->is_enabled();
+ }
+
+ bool disable_tracing_if_required();
+
+ bool enable_tracing_if_required();
+
+ bool is_enabled();
+
+ void missing_privilege();
+
+ static const char *flag_names[];
+ enum
+ {
+ FLAG_DEFAULT = 0,
+ FLAG_ENABLED = 1 << 0
+ };
+
+private:
+ /*
+ List of traces (currently it stores only 1 trace)
+ */
+ Dynamic_array<Opt_trace_stmt*> traces;
+ Opt_trace_stmt *current_trace;
+ size_t max_mem_size;
+};
+
+#endif /* OPT_TRACE_CONTEXT_INCLUDED */
diff --git a/sql/partition_element.h b/sql/partition_element.h
index ff0d0d59fc4..e0a519065cc 100644
--- a/sql/partition_element.h
+++ b/sql/partition_element.h
@@ -144,6 +144,7 @@ public:
part_min_rows(part_elem->part_min_rows),
range_value(0), partition_name(NULL),
tablespace_name(part_elem->tablespace_name),
+ log_entry(NULL),
part_comment(part_elem->part_comment),
data_file_name(part_elem->data_file_name),
index_file_name(part_elem->index_file_name),
@@ -152,6 +153,8 @@ public:
part_state(part_elem->part_state),
nodegroup_id(part_elem->nodegroup_id),
has_null_value(FALSE),
+ signed_flag(part_elem->signed_flag),
+ max_value(part_elem->max_value),
id(part_elem->id),
empty(part_elem->empty),
type(CONVENTIONAL)
diff --git a/sql/partition_info.h b/sql/partition_info.h
index 2aed6cbc5db..00ef815ce09 100644
--- a/sql/partition_info.h
+++ b/sql/partition_info.h
@@ -402,7 +402,7 @@ public:
vers_info->interval.start= start;
if (item->fix_fields_if_needed_for_scalar(thd, &item))
return true;
- bool error= get_interval_value(item, int_type, &vers_info->interval.step) ||
+ bool error= get_interval_value(thd, item, int_type, &vers_info->interval.step) ||
vers_info->interval.step.neg || vers_info->interval.step.second_part ||
!(vers_info->interval.step.year || vers_info->interval.step.month ||
vers_info->interval.step.day || vers_info->interval.step.hour ||
diff --git a/sql/procedure.h b/sql/procedure.h
index 80930d6b707..15fd525ec65 100644
--- a/sql/procedure.h
+++ b/sql/procedure.h
@@ -44,24 +44,30 @@ public:
this->name.length= strlen(name_par);
}
enum Type type() const { return Item::PROC_ITEM; }
+ Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ /*
+ We can get to here when using a CURSOR for a query with PROCEDURE:
+ DECLARE c CURSOR FOR SELECT * FROM t1 PROCEDURE analyse();
+ OPEN c;
+ */
+ return create_tmp_field_ex_simple(table, src, param);
+ }
virtual void set(double nr)=0;
virtual void set(const char *str,uint length,CHARSET_INFO *cs)=0;
virtual void set(longlong nr)=0;
const Type_handler *type_handler() const=0;
void set(const char *str) { set(str,(uint) strlen(str), default_charset()); }
- void make_send_field(THD *thd, Send_field *tmp_field)
- {
- init_make_send_field(tmp_field,field_type());
- }
unsigned int size_of() { return sizeof(*this);}
bool check_vcol_func_processor(void *arg)
{
DBUG_ASSERT(0); // impossible
return mark_unsupported_function("proc", arg, VCOL_IMPOSSIBLE);
}
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- return type_handler()->Item_get_date(this, ltime, fuzzydate);
+ return type_handler()->Item_get_date_with_warn(thd, this, ltime, fuzzydate);
}
Item* get_copy(THD *thd) { return 0; }
};
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 26cc686ad0a..eb7f19d2bd0 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -563,8 +563,26 @@ static uchar *net_store_length_fast(uchar *packet, size_t length)
void Protocol::end_statement()
{
- /* sanity check*/
- DBUG_ASSERT_IF_WSREP(!(WSREP(thd) && thd->wsrep_conflict_state == REPLAYING));
+#ifdef WITH_WSREP
+ /*
+ Commented out: This sanity check does not hold in general.
+ Thd->LOCK_thd_data() must be unlocked before sending response
+ to client, so BF abort may sneak in here.
+ DBUG_ASSERT(!WSREP(thd) || thd->wsrep_conflict_state() == NO_CONFLICT);
+ */
+
+ /*
+ sanity check, don't send end statement while replaying
+ */
+ DBUG_ASSERT(thd->wsrep_trx().state() != wsrep::transaction::s_replaying);
+ if (WSREP(thd) && thd->wsrep_trx().state() ==
+ wsrep::transaction::s_replaying)
+ {
+ WSREP_ERROR("attempting net_end_statement while replaying");
+ return;
+ }
+#endif /* WITH_WSREP */
+
DBUG_ENTER("Protocol::end_statement");
DBUG_ASSERT(! thd->get_stmt_da()->is_sent());
bool error= FALSE;
@@ -751,7 +769,7 @@ void Protocol::init(THD *thd_arg)
packet= &thd->packet;
convert= &thd->convert_buffer;
#ifndef DBUG_OFF
- field_types= 0;
+ field_handlers= 0;
#endif
}
@@ -783,6 +801,73 @@ bool Protocol::flush()
#ifndef EMBEDDED_LIBRARY
+bool Protocol_text::store_field_metadata(const THD * thd,
+ const Send_field &field,
+ CHARSET_INFO *charset_for_protocol,
+ uint fieldnr)
+{
+ CHARSET_INFO *thd_charset= thd->variables.character_set_results;
+ char *pos;
+ CHARSET_INFO *cs= system_charset_info;
+ DBUG_ASSERT(field.is_sane());
+
+ if (thd->client_capabilities & CLIENT_PROTOCOL_41)
+ {
+ if (store(STRING_WITH_LEN("def"), cs, thd_charset) ||
+ store_str(field.db_name, cs, thd_charset) ||
+ store_str(field.table_name, cs, thd_charset) ||
+ store_str(field.org_table_name, cs, thd_charset) ||
+ store_str(field.col_name, cs, thd_charset) ||
+ store_str(field.org_col_name, cs, thd_charset) ||
+ packet->realloc(packet->length() + 12))
+ return true;
+ /* Store fixed length fields */
+ pos= (char*) packet->end();
+ *pos++= 12; // Length of packed fields
+ /* inject a NULL to test the client */
+ DBUG_EXECUTE_IF("poison_rs_fields", pos[-1]= (char) 0xfb;);
+ if (charset_for_protocol == &my_charset_bin || thd_charset == NULL)
+ {
+ /* No conversion */
+ int2store(pos, charset_for_protocol->number);
+ int4store(pos + 2, field.length);
+ }
+ else
+ {
+ /* With conversion */
+ int2store(pos, thd_charset->number);
+ uint32 field_length= field.max_octet_length(charset_for_protocol,
+ thd_charset);
+ int4store(pos + 2, field_length);
+ }
+ pos[6]= field.type_handler()->type_code_for_protocol();
+ int2store(pos + 7, field.flags);
+ pos[9]= (char) field.decimals;
+ pos[10]= 0; // For the future
+ pos[11]= 0; // For the future
+ pos+= 12;
+ }
+ else
+ {
+ if (store_str(field.table_name, cs, thd_charset) ||
+ store_str(field.col_name, cs, thd_charset) ||
+ packet->realloc(packet->length() + 10))
+ return true;
+ pos= (char*) packet->end();
+ pos[0]= 3;
+ int3store(pos + 1, field.length);
+ pos[4]= 1;
+ pos[5]= field.type_handler()->type_code_for_protocol();
+ pos[6]= 3;
+ int2store(pos + 7, field.flags);
+ pos[9]= (char) field.decimals;
+ pos+= 10;
+ }
+ packet->length((uint) (pos - packet->ptr()));
+ return false;
+}
+
+
/**
Send name and type of result to client.
@@ -805,10 +890,7 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
- ValueBuffer<MAX_FIELD_WIDTH> tmp;
- Protocol_text prot(thd);
- String *local_packet= prot.storage_packet();
- CHARSET_INFO *thd_charset= thd->variables.character_set_results;
+ Protocol_text prot(thd, thd->variables.net_buffer_length);
DBUG_ENTER("Protocol::send_result_set_metadata");
if (flags & SEND_NUM_ROWS)
@@ -821,119 +903,19 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
}
#ifndef DBUG_OFF
- field_types= (enum_field_types*) thd->alloc(sizeof(field_types) *
- list->elements);
- uint count= 0;
+ field_handlers= (const Type_handler**) thd->alloc(sizeof(field_handlers[0]) *
+ list->elements);
#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++))
+ for (uint pos= 0; (item=it++); pos++)
{
- char *pos;
- CHARSET_INFO *cs= system_charset_info;
- Send_field field;
- item->make_send_field(thd, &field);
-
- /* limit number of decimals for float and double */
- if (field.type == MYSQL_TYPE_FLOAT || field.type == MYSQL_TYPE_DOUBLE)
- set_if_smaller(field.decimals, FLOATING_POINT_DECIMALS);
-
- /* Keep things compatible for old clients */
- if (field.type == MYSQL_TYPE_VARCHAR)
- field.type= MYSQL_TYPE_VAR_STRING;
-
prot.prepare_for_resend();
-
- if (thd->client_capabilities & CLIENT_PROTOCOL_41)
- {
- if (prot.store(STRING_WITH_LEN("def"), cs, thd_charset) ||
- prot.store(field.db_name, (uint) strlen(field.db_name),
- cs, thd_charset) ||
- prot.store(field.table_name, (uint) strlen(field.table_name),
- cs, thd_charset) ||
- prot.store(field.org_table_name, (uint) strlen(field.org_table_name),
- cs, thd_charset) ||
- prot.store(field.col_name.str, (uint) field.col_name.length,
- cs, thd_charset) ||
- prot.store(field.org_col_name.str, (uint) field.org_col_name.length,
- cs, thd_charset) ||
- local_packet->realloc(local_packet->length()+12))
- goto err;
- /* Store fixed length fields */
- pos= (char*) local_packet->ptr()+local_packet->length();
- *pos++= 12; // Length of packed fields
- /* inject a NULL to test the client */
- DBUG_EXECUTE_IF("poison_rs_fields", pos[-1]= (char) 0xfb;);
- if (item->charset_for_protocol() == &my_charset_bin || thd_charset == NULL)
- {
- /* No conversion */
- int2store(pos, item->charset_for_protocol()->number);
- int4store(pos+2, field.length);
- }
- else
- {
- /* With conversion */
- uint32 field_length, max_length;
- int2store(pos, thd_charset->number);
- /*
- For TEXT/BLOB columns, field_length describes the maximum data
- length in bytes. There is no limit to the number of characters
- that a TEXT column can store, as long as the data fits into
- the designated space.
- For the rest of textual columns, field_length is evaluated as
- char_count * mbmaxlen, where character count is taken from the
- definition of the column. In other words, the maximum number
- of characters here is limited by the column definition.
-
- When one has a LONG TEXT column with a single-byte
- character set, and the connection character set is multi-byte, the
- client may get fields longer than UINT_MAX32, due to
- <character set column> -> <character set connection> conversion.
- In that case column max length does not fit into the 4 bytes
- reserved for it in the protocol.
- */
- max_length= (field.type >= MYSQL_TYPE_TINY_BLOB &&
- field.type <= MYSQL_TYPE_BLOB) ?
- field.length / item->collation.collation->mbminlen :
- field.length / item->collation.collation->mbmaxlen;
- field_length= char_to_byte_length_safe(max_length,
- thd_charset->mbmaxlen);
- int4store(pos + 2, field_length);
- }
- pos[6]= field.type;
- int2store(pos+7,field.flags);
- pos[9]= (char) field.decimals;
- pos[10]= 0; // For the future
- pos[11]= 0; // For the future
- pos+= 12;
- }
- else
- {
- if (prot.store(field.table_name, (uint) strlen(field.table_name),
- cs, thd_charset) ||
- prot.store(field.col_name.str, (uint) field.col_name.length,
- cs, thd_charset) ||
- local_packet->realloc(local_packet->length()+10))
- goto err;
- pos= (char*) local_packet->ptr()+local_packet->length();
- pos[0]=3;
- int3store(pos+1,field.length);
- pos[4]=1;
- pos[5]=field.type;
- pos[6]=3;
- int2store(pos+7,field.flags);
- pos[9]= (char) field.decimals;
- pos+= 10;
- }
- local_packet->length((uint) (pos - local_packet->ptr()));
- if (flags & SEND_DEFAULTS)
- item->send(&prot, &tmp); // Send default value
+ if (prot.store_field_metadata(thd, item, pos))
+ goto err;
if (prot.write())
DBUG_RETURN(1);
#ifndef DBUG_OFF
- field_types[count++]= field.type;
+ field_handlers[pos]= item->type_handler();
#endif
}
@@ -962,6 +944,43 @@ err:
}
+bool Protocol::send_list_fields(List<Field> *list, const TABLE_LIST *table_list)
+{
+ DBUG_ENTER("Protocol::send_list_fields");
+ List_iterator_fast<Field> it(*list);
+ Field *fld;
+ Protocol_text prot(thd, thd->variables.net_buffer_length);
+
+#ifndef DBUG_OFF
+ field_handlers= (const Type_handler **) thd->alloc(sizeof(field_handlers[0]) *
+ list->elements);
+#endif
+
+ for (uint pos= 0; (fld= it++); pos++)
+ {
+ prot.prepare_for_resend();
+ if (prot.store_field_metadata_for_list_fields(thd, fld, table_list, pos))
+ goto err;
+ prot.store(fld); // Send default value
+ if (prot.write())
+ DBUG_RETURN(1);
+#ifndef DBUG_OFF
+ /*
+ Historically all BLOB variant Fields are displayed as
+ MYSQL_TYPE_BLOB in metadata.
+ See Field_blob::make_send_field() for more comments.
+ */
+ field_handlers[pos]= Send_field(fld).type_handler();
+#endif
+ }
+ DBUG_RETURN(prepare_for_send(list->elements));
+
+err:
+ my_message(ER_OUT_OF_RESOURCES, ER_THD(thd, ER_OUT_OF_RESOURCES), MYF(0));
+ DBUG_RETURN(1);
+}
+
+
bool Protocol::write()
{
DBUG_ENTER("Protocol::write");
@@ -971,6 +990,25 @@ bool Protocol::write()
#endif /* EMBEDDED_LIBRARY */
+bool Protocol_text::store_field_metadata(THD *thd, Item *item, uint pos)
+{
+ Send_field field(thd, item);
+ return store_field_metadata(thd, field, item->charset_for_protocol(), pos);
+}
+
+
+bool Protocol_text::store_field_metadata_for_list_fields(const THD *thd,
+ Field *fld,
+ const TABLE_LIST *tl,
+ uint pos)
+{
+ Send_field field= tl->view ?
+ Send_field(fld, tl->view_db.str, tl->view_name.str) :
+ Send_field(fld);
+ return store_field_metadata(thd, field, fld->charset_for_protocol(), pos);
+}
+
+
/**
Send one result set row.
@@ -1111,12 +1149,7 @@ bool Protocol_text::store(const char *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
#ifndef DBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
- field_types[field_pos] == MYSQL_TYPE_BIT ||
- field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL ||
- (field_types[field_pos] >= MYSQL_TYPE_ENUM &&
- field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
+ DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_STRING));
field_pos++;
#endif
return store_string_aux(from, length, fromcs, tocs);
@@ -1130,14 +1163,8 @@ bool Protocol_text::store(const char *from, size_t length,
#ifndef DBUG_OFF
DBUG_PRINT("info", ("Protocol_text::store field %u (%u): %.*s", field_pos,
field_count, (int) length, (length == 0 ? "" : from)));
- DBUG_ASSERT(field_types == 0 || field_pos < field_count);
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
- field_types[field_pos] == MYSQL_TYPE_BIT ||
- field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL ||
- field_types[field_pos] == MYSQL_TYPE_NEWDATE ||
- (field_types[field_pos] >= MYSQL_TYPE_ENUM &&
- field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
+ DBUG_ASSERT(field_handlers == 0 || field_pos < field_count);
+ DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_STRING));
field_pos++;
#endif
return store_string_aux(from, length, fromcs, tocs);
@@ -1147,7 +1174,7 @@ bool Protocol_text::store(const char *from, size_t length,
bool Protocol_text::store_tiny(longlong from)
{
#ifndef DBUG_OFF
- DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_TINY);
+ DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_TINY));
field_pos++;
#endif
char buff[22];
@@ -1159,9 +1186,7 @@ bool Protocol_text::store_tiny(longlong from)
bool Protocol_text::store_short(longlong from)
{
#ifndef DBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_YEAR ||
- field_types[field_pos] == MYSQL_TYPE_SHORT);
+ DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_SHORT));
field_pos++;
#endif
char buff[22];
@@ -1174,9 +1199,7 @@ bool Protocol_text::store_short(longlong from)
bool Protocol_text::store_long(longlong from)
{
#ifndef DBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_INT24 ||
- field_types[field_pos] == MYSQL_TYPE_LONG);
+ DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_LONG));
field_pos++;
#endif
char buff[22];
@@ -1189,8 +1212,7 @@ bool Protocol_text::store_long(longlong from)
bool Protocol_text::store_longlong(longlong from, bool unsigned_flag)
{
#ifndef DBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_LONGLONG);
+ DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_LONGLONG));
field_pos++;
#endif
char buff[22];
@@ -1204,13 +1226,11 @@ bool Protocol_text::store_longlong(longlong from, bool unsigned_flag)
bool Protocol_text::store_decimal(const my_decimal *d)
{
#ifndef DBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL);
+ DBUG_ASSERT(0); // This method is not used yet
field_pos++;
#endif
- char buff[DECIMAL_MAX_STR_LENGTH];
- String str(buff, sizeof(buff), &my_charset_bin);
- (void) my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str);
+ StringBuffer<DECIMAL_MAX_STR_LENGTH> str;
+ (void) d->to_string(&str);
return net_store_data((uchar*) str.ptr(), str.length());
}
@@ -1218,8 +1238,7 @@ bool Protocol_text::store_decimal(const my_decimal *d)
bool Protocol_text::store(float from, uint32 decimals, String *buffer)
{
#ifndef DBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_FLOAT);
+ DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_FLOAT));
field_pos++;
#endif
Float(from).to_string(buffer, decimals);
@@ -1230,8 +1249,7 @@ bool Protocol_text::store(float from, uint32 decimals, String *buffer)
bool Protocol_text::store(double from, uint32 decimals, String *buffer)
{
#ifndef DBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DOUBLE);
+ DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_DOUBLE));
field_pos++;
#endif
buffer->set_real(from, decimals, thd->charset());
@@ -1269,9 +1287,7 @@ bool Protocol_text::store(Field *field)
bool Protocol_text::store(MYSQL_TIME *tm, int decimals)
{
#ifndef DBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DATETIME ||
- field_types[field_pos] == MYSQL_TYPE_TIMESTAMP);
+ DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_DATETIME));
field_pos++;
#endif
char buff[MAX_DATE_STRING_REP_LENGTH];
@@ -1283,8 +1299,7 @@ bool Protocol_text::store(MYSQL_TIME *tm, int decimals)
bool Protocol_text::store_date(MYSQL_TIME *tm)
{
#ifndef DBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DATE);
+ DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_DATE));
field_pos++;
#endif
char buff[MAX_DATE_STRING_REP_LENGTH];
@@ -1296,8 +1311,7 @@ bool Protocol_text::store_date(MYSQL_TIME *tm)
bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals)
{
#ifndef DBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_TIME);
+ DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_TIME));
field_pos++;
#endif
char buff[MAX_DATE_STRING_REP_LENGTH];
@@ -1317,11 +1331,10 @@ bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals)
bool Protocol_text::send_out_parameters(List<Item_param> *sp_params)
{
- DBUG_ASSERT(sp_params->elements ==
- thd->lex->prepared_stmt_params.elements);
+ DBUG_ASSERT(sp_params->elements == thd->lex->prepared_stmt.param_count());
List_iterator_fast<Item_param> item_param_it(*sp_params);
- List_iterator_fast<Item> param_it(thd->lex->prepared_stmt_params);
+ List_iterator_fast<Item> param_it(thd->lex->prepared_stmt.params());
while (true)
{
@@ -1455,13 +1468,11 @@ bool Protocol_binary::store_longlong(longlong from, bool unsigned_flag)
bool Protocol_binary::store_decimal(const my_decimal *d)
{
#ifndef DBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL);
+ DBUG_ASSERT(0); // This method is not used yet
field_pos++;
#endif
- char buff[DECIMAL_MAX_STR_LENGTH];
- String str(buff, sizeof(buff), &my_charset_bin);
- (void) my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str);
+ StringBuffer<DECIMAL_MAX_STR_LENGTH> str;
+ (void) d->to_string(&str);
return store(str.ptr(), str.length(), str.charset());
}
@@ -1515,7 +1526,7 @@ bool Protocol_binary::store(MYSQL_TIME *tm, int decimals)
DBUG_ASSERT(decimals == AUTO_SEC_PART_DIGITS ||
(decimals >= 0 && decimals <= TIME_SECOND_PART_DIGITS));
if (decimals != AUTO_SEC_PART_DIGITS)
- my_time_trunc(tm, decimals);
+ my_datetime_trunc(tm, decimals);
int4store(pos+7, tm->second_part);
if (tm->second_part)
length=11;
diff --git a/sql/protocol.h b/sql/protocol.h
index 6d6bf4b2297..3b2c905ed9e 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -22,11 +22,14 @@
#include "sql_error.h"
#include "my_decimal.h" /* my_decimal */
+#include "sql_type.h"
class i_string;
class Field;
+class Send_field;
class THD;
class Item_param;
+struct TABLE_LIST;
typedef struct st_mysql_field MYSQL_FIELD;
typedef struct st_mysql_rows MYSQL_ROWS;
@@ -38,7 +41,12 @@ protected:
String *convert;
uint field_pos;
#ifndef DBUG_OFF
- enum enum_field_types *field_types;
+ const Type_handler **field_handlers;
+ bool valid_handler(uint pos, protocol_send_type_t type) const
+ {
+ return field_handlers == 0 ||
+ field_handlers[field_pos]->protocol_send_type() == type;
+ }
#endif
uint field_count;
#ifndef EMBEDDED_LIBRARY
@@ -75,8 +83,9 @@ public:
virtual ~Protocol() {}
void init(THD* thd_arg);
- enum { SEND_NUM_ROWS= 1, SEND_DEFAULTS= 2, SEND_EOF= 4 };
+ enum { SEND_NUM_ROWS= 1, SEND_EOF= 2 };
virtual bool send_result_set_metadata(List<Item> *list, uint flags);
+ bool send_list_fields(List<Field> *list, const TABLE_LIST *table_list);
bool send_result_set_row(List<Item> *row_items);
bool store(I_List<i_string> *str_list);
@@ -113,6 +122,15 @@ public:
virtual bool store(const char *from, size_t length, CHARSET_INFO *cs)=0;
virtual bool store(const char *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)=0;
+ bool store_str(const char *s, CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
+ {
+ DBUG_ASSERT(s);
+ return store(s, (uint) strlen(s), fromcs, tocs);
+ }
+ bool store_str(const LEX_CSTRING &s, CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
+ {
+ return store(s.str, (uint) s.length, fromcs, tocs);
+ }
virtual bool store(float from, uint32 decimals, String *buffer)=0;
virtual bool store(double from, uint32 decimals, String *buffer)=0;
virtual bool store(MYSQL_TIME *time, int decimals)=0;
@@ -122,7 +140,8 @@ public:
virtual bool send_out_parameters(List<Item_param> *sp_params)=0;
#ifdef EMBEDDED_LIBRARY
- int begin_dataset();
+ bool begin_dataset();
+ bool begin_dataset(THD *thd, uint numfields);
virtual void remove_last_row() {}
#else
void remove_last_row() {}
@@ -150,7 +169,12 @@ public:
class Protocol_text :public Protocol
{
public:
- Protocol_text(THD *thd_arg) :Protocol(thd_arg) {}
+ Protocol_text(THD *thd_arg, ulong prealloc= 0)
+ :Protocol(thd_arg)
+ {
+ if (prealloc)
+ packet->alloc(prealloc);
+ }
virtual void prepare_for_resend();
virtual bool store_null();
virtual bool store_tiny(longlong from);
@@ -172,6 +196,13 @@ public:
#ifdef EMBEDDED_LIBRARY
void remove_last_row();
#endif
+ bool store_field_metadata(const THD *thd, const Send_field &field,
+ CHARSET_INFO *charset_for_protocol,
+ uint pos);
+ bool store_field_metadata(THD *thd, Item *item, uint pos);
+ bool store_field_metadata_for_list_fields(const THD *thd, Field *field,
+ const TABLE_LIST *table_list,
+ uint pos);
virtual enum enum_protocol_type type() { return PROTOCOL_TEXT; };
};
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 5b5b4931582..18fc3d9431a 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -38,13 +38,21 @@
#include "log_event.h"
#include <mysql.h>
-#define SLAVE_LIST_CHUNK 128
-#define SLAVE_ERRMSG_SIZE (FN_REFLEN+64)
+
+struct Slave_info
+{
+ uint32 server_id;
+ uint32 master_id;
+ char host[HOSTNAME_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
+ char user[USERNAME_LENGTH+1];
+ char password[MAX_PASSWORD_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
+ uint16 port;
+};
+Atomic_counter<uint32_t> binlog_dump_thread_count;
ulong rpl_status=RPL_NULL;
mysql_mutex_t LOCK_rpl_status;
-HASH slave_list;
const char *rpl_role_type[] = {"MASTER","SLAVE",NullS};
TYPELIB rpl_role_typelib = {array_elements(rpl_role_type)-1,"",
@@ -81,33 +89,26 @@ void change_rpl_status(ulong from_status, ulong to_status)
errmsg= msg;\
goto err; \
}\
- strmake(obj,(char*) p,len); \
+ ::strmake(obj, (char*) p, len); \
p+= len; \
}\
-void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
+void THD::unregister_slave()
{
- uint32 thd_server_id= thd->variables.server_id;
- if (thd_server_id)
+ if (auto old_si= slave_info)
{
- if (need_mutex)
- mysql_mutex_lock(&LOCK_slave_list);
-
- SLAVE_INFO* old_si;
- if ((old_si = (SLAVE_INFO*)my_hash_search(&slave_list,
- (uchar*)&thd_server_id, 4)) &&
- (!only_mine || old_si->thd == thd))
- my_hash_delete(&slave_list, (uchar*)old_si);
-
- if (need_mutex)
- mysql_mutex_unlock(&LOCK_slave_list);
+ mysql_mutex_lock(&LOCK_thd_data);
+ slave_info= 0;
+ mysql_mutex_unlock(&LOCK_thd_data);
+ delete old_si;
+ binlog_dump_thread_count--;
}
}
/**
- Register slave in 'slave_list' hash table.
+ Register slave
@return
0 ok
@@ -115,19 +116,18 @@ void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
1 Error. Error message sent to client
*/
-int register_slave(THD* thd, uchar* packet, size_t packet_length)
+int THD::register_slave(uchar *packet, size_t packet_length)
{
- int res;
- SLAVE_INFO *si;
+ Slave_info *si;
uchar *p= packet, *p_end= packet + packet_length;
const char *errmsg= "Wrong parameters to function register_slave";
- if (check_access(thd, REPL_SLAVE_ACL, any_db, NULL, NULL, 0, 0))
+ if (check_access(this, REPL_SLAVE_ACL, any_db, NULL, NULL, 0, 0))
+ return 1;
+ if (!(si= new Slave_info))
return 1;
- if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME))))
- goto err2;
- thd->variables.server_id= si->server_id= uint4korr(p);
+ variables.server_id= si->server_id= uint4korr(p);
p+= 4;
get_object(p,si->host, "Failed to register slave: too long 'report-host'");
get_object(p,si->user, "Failed to register slave: too long 'report-user'");
@@ -146,77 +146,54 @@ int register_slave(THD* thd, uchar* packet, size_t packet_length)
p += 4;
if (!(si->master_id= uint4korr(p)))
si->master_id= global_system_variables.server_id;
- si->thd= thd;
- mysql_mutex_lock(&LOCK_slave_list);
- unregister_slave(thd,0,0);
- res= my_hash_insert(&slave_list, (uchar*) si);
- mysql_mutex_unlock(&LOCK_slave_list);
- return res;
+ unregister_slave();
+ mysql_mutex_lock(&LOCK_thd_data);
+ slave_info= si;
+ mysql_mutex_unlock(&LOCK_thd_data);
+ binlog_dump_thread_count++;
+ return 0;
err:
- my_free(si);
+ delete si;
my_message(ER_UNKNOWN_ERROR, errmsg, MYF(0)); /* purecov: inspected */
-err2:
return 1;
}
-extern "C" uint32
-*slave_list_key(SLAVE_INFO* si, size_t *len,
- my_bool not_used __attribute__((unused)))
-{
- *len = 4;
- return &si->server_id;
-}
-extern "C" void slave_info_free(void *s)
+bool THD::is_binlog_dump_thread()
{
- my_free(s);
-}
-
-#ifdef HAVE_PSI_INTERFACE
-static PSI_mutex_key key_LOCK_slave_list;
+ mysql_mutex_lock(&LOCK_thd_data);
+ bool res= slave_info != NULL;
+ mysql_mutex_unlock(&LOCK_thd_data);
-static PSI_mutex_info all_slave_list_mutexes[]=
-{
- { &key_LOCK_slave_list, "LOCK_slave_list", PSI_FLAG_GLOBAL}
-};
-
-static void init_all_slave_list_mutexes(void)
-{
- const char* category= "sql";
- int count;
-
- if (PSI_server == NULL)
- return;
-
- count= array_elements(all_slave_list_mutexes);
- PSI_server->register_mutex(category, all_slave_list_mutexes, count);
+ return res;
}
-#endif /* HAVE_PSI_INTERFACE */
-void init_slave_list()
-{
-#ifdef HAVE_PSI_INTERFACE
- init_all_slave_list_mutexes();
-#endif
-
- my_hash_init(&slave_list, system_charset_info, SLAVE_LIST_CHUNK, 0, 0,
- (my_hash_get_key) slave_list_key,
- (my_hash_free_key) slave_info_free, 0);
- mysql_mutex_init(key_LOCK_slave_list, &LOCK_slave_list, MY_MUTEX_INIT_FAST);
-}
-void end_slave_list()
+static my_bool show_slave_hosts_callback(THD *thd, Protocol *protocol)
{
- /* No protection by a mutex needed as we are only called at shutdown */
- if (my_hash_inited(&slave_list))
+ my_bool res= FALSE;
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ if (auto si= thd->slave_info)
{
- my_hash_free(&slave_list);
- mysql_mutex_destroy(&LOCK_slave_list);
+ protocol->prepare_for_resend();
+ protocol->store(si->server_id);
+ protocol->store(si->host, &my_charset_bin);
+ if (opt_show_slave_auth_info)
+ {
+ protocol->store(si->user, &my_charset_bin);
+ protocol->store(si->password, &my_charset_bin);
+ }
+ protocol->store((uint32) si->port);
+ protocol->store(si->master_id);
+ res= protocol->write();
}
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ return res;
}
+
/**
Execute a SHOW SLAVE HOSTS statement.
@@ -258,28 +235,9 @@ bool show_slave_hosts(THD* thd)
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
- mysql_mutex_lock(&LOCK_slave_list);
+ if (server_threads.iterate(show_slave_hosts_callback, protocol))
+ DBUG_RETURN(true);
- for (uint i = 0; i < slave_list.records; ++i)
- {
- SLAVE_INFO* si = (SLAVE_INFO*) my_hash_element(&slave_list, i);
- protocol->prepare_for_resend();
- protocol->store((uint32) si->server_id);
- protocol->store(si->host, &my_charset_bin);
- if (opt_show_slave_auth_info)
- {
- protocol->store(si->user, &my_charset_bin);
- protocol->store(si->password, &my_charset_bin);
- }
- protocol->store((uint32) si->port);
- protocol->store((uint32) si->master_id);
- if (protocol->write())
- {
- mysql_mutex_unlock(&LOCK_slave_list);
- DBUG_RETURN(TRUE);
- }
- }
- mysql_mutex_unlock(&LOCK_slave_list);
my_eof(thd);
DBUG_RETURN(FALSE);
}
diff --git a/sql/repl_failsafe.h b/sql/repl_failsafe.h
index 90415532c7d..6f8bdfc5216 100644
--- a/sql/repl_failsafe.h
+++ b/sql/repl_failsafe.h
@@ -22,6 +22,7 @@
#include <my_sys.h>
#include "slave.h"
+extern Atomic_counter<uint32_t> binlog_dump_thread_count;
typedef enum {RPL_AUTH_MASTER=0,RPL_IDLE_SLAVE,RPL_ACTIVE_SLAVE,
RPL_LOST_SOLDIER,RPL_TROOP_SOLDIER,
RPL_RECOVERY_CAPTAIN,RPL_NULL /* inactive */,
@@ -36,13 +37,7 @@ extern const char* rpl_role_type[], *rpl_status_type[];
void change_rpl_status(ulong from_status, ulong to_status);
int find_recovery_captain(THD* thd, MYSQL* mysql);
-extern HASH slave_list;
-
bool show_slave_hosts(THD* thd);
-void init_slave_list();
-void end_slave_list();
-int register_slave(THD* thd, uchar* packet, size_t packet_length);
-void unregister_slave(THD* thd, bool only_mine, bool need_mutex);
#endif /* HAVE_REPLICATION */
#endif /* REPL_FAILSAFE_INCLUDED */
diff --git a/sql/rowid_filter.cc b/sql/rowid_filter.cc
new file mode 100644
index 00000000000..9faab828871
--- /dev/null
+++ b/sql/rowid_filter.cc
@@ -0,0 +1,663 @@
+/*
+ Copyright (c) 2018, 2019 MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mariadb.h"
+#include "table.h"
+#include "sql_class.h"
+#include "opt_range.h"
+#include "rowid_filter.h"
+#include "sql_select.h"
+#include "opt_trace.h"
+
+
+inline
+double Range_rowid_filter_cost_info::lookup_cost(
+ Rowid_filter_container_type cont_type)
+{
+ switch (cont_type) {
+ case SORTED_ARRAY_CONTAINER:
+ return log(est_elements)*0.01;
+ default:
+ DBUG_ASSERT(0);
+ return 0;
+ }
+}
+
+
+/**
+ @brief
+ The average gain in cost per row to use the range filter with this cost info
+*/
+
+inline
+double Range_rowid_filter_cost_info::avg_access_and_eval_gain_per_row(
+ Rowid_filter_container_type cont_type)
+{
+ return (1+1.0/TIME_FOR_COMPARE) * (1 - selectivity) -
+ lookup_cost(cont_type);
+}
+
+
+/**
+ @brief
+ The average adjusted gain in cost per row of using the filter
+
+ @param access_cost_factor the adjusted cost of access a row
+
+ @details
+ The current code to estimate the cost of a ref access is quite inconsistent:
+ in some cases the effect of page buffers is taken into account, for others
+ just the engine dependent read_time() is employed. That's why the average
+ cost of one random seek might differ from 1.
+ The parameter access_cost_factor can be considered as the cost of a random
+ seek that is used for the given ref access. Changing the cost of a random
+ seek we have to change the first coefficient in the linear formula by which
+ we calculate the gain of usage the given filter for a_adj. This function
+ calculates the value of a_adj.
+
+ @note
+ Currently we require that access_cost_factor should be a number between
+ 0.0 and 1.0
+*/
+
+inline
+double Range_rowid_filter_cost_info::avg_adjusted_gain_per_row(
+ double access_cost_factor)
+{
+ return a - (1 - access_cost_factor) * (1 - selectivity);
+}
+
+
+/**
+ @brief
+ Set the parameters used to choose the filter with the best adjusted gain
+
+ @note
+ This function must be called before the call of get_adjusted_gain()
+ for the given filter.
+*/
+
+inline void
+Range_rowid_filter_cost_info::set_adjusted_gain_param(double access_cost_factor)
+{
+ a_adj= avg_adjusted_gain_per_row(access_cost_factor);
+ cross_x_adj= b / a_adj;
+}
+
+
+/**
+ @brief
+ Initialize the cost info structure for a range filter
+
+ @param cont_type The type of the container of the range filter
+ @param tab The table for which the range filter is evaluated
+ @param idx The index used to create this range filter
+*/
+
+void Range_rowid_filter_cost_info::init(Rowid_filter_container_type cont_type,
+ TABLE *tab, uint idx)
+{
+ container_type= cont_type;
+ table= tab;
+ key_no= idx;
+ est_elements= (ulonglong) (table->quick_rows[key_no]);
+ b= build_cost(container_type);
+ selectivity= est_elements/((double) table->stat_records());
+ a= avg_access_and_eval_gain_per_row(container_type);
+ if (a > 0)
+ cross_x= b/a;
+ else
+ cross_x= b+1;
+ abs_independent.clear_all();
+}
+
+
+/**
+ @brief
+ Return the cost of building a range filter of a certain type
+*/
+
+double
+Range_rowid_filter_cost_info::build_cost(Rowid_filter_container_type cont_type)
+{
+ double cost= 0;
+
+ cost+= table->quick_index_only_costs[key_no];
+
+ switch (cont_type) {
+
+ case SORTED_ARRAY_CONTAINER:
+ cost+= ARRAY_WRITE_COST * est_elements; /* cost filling the container */
+ cost+= ARRAY_SORT_C * est_elements * log(est_elements); /* sorting cost */
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+
+ return cost;
+}
+
+
+Rowid_filter_container *Range_rowid_filter_cost_info::create_container()
+{
+ THD *thd= table->in_use;
+ uint elem_sz= table->file->ref_length;
+ Rowid_filter_container *res= 0;
+
+ switch (container_type) {
+ case SORTED_ARRAY_CONTAINER:
+ res= new (thd->mem_root) Rowid_filter_sorted_array((uint) est_elements,
+ elem_sz);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ return res;
+}
+
+
+static
+int compare_range_rowid_filter_cost_info_by_a(
+ Range_rowid_filter_cost_info **filter_ptr_1,
+ Range_rowid_filter_cost_info **filter_ptr_2)
+{
+ double diff= (*filter_ptr_2)->get_a() - (*filter_ptr_1)->get_a();
+ return (diff < 0 ? -1 : (diff > 0 ? 1 : 0));
+}
+
+
+/**
+ @brief
+ Prepare the array with cost info on range filters to be used by optimizer
+
+ @details
+ The function removes the array of cost info on range filters the elements
+ for those range filters that won't be ever chosen as the best filter, no
+ matter what index will be used to access the table and at what step the
+ table will be joined.
+*/
+
+void TABLE::prune_range_rowid_filters()
+{
+ /*
+ For the elements of the array with cost info on range filters
+ build a bit matrix of absolutely independent elements.
+ Two elements are absolutely independent if they such indexes that
+ there is no other index that overlaps both of them or is constraint
+ correlated with both of them. Use abs_independent key maps to store
+ the elements if this bit matrix.
+ */
+
+ Range_rowid_filter_cost_info **filter_ptr_1= range_rowid_filter_cost_info_ptr;
+ for (uint i= 0;
+ i < range_rowid_filter_cost_info_elems;
+ i++, filter_ptr_1++)
+ {
+ uint key_no= (*filter_ptr_1)->key_no;
+ Range_rowid_filter_cost_info **filter_ptr_2= filter_ptr_1 + 1;
+ for (uint j= i+1;
+ j < range_rowid_filter_cost_info_elems;
+ j++, filter_ptr_2++)
+ {
+ key_map map_1= key_info[key_no].overlapped;
+ map_1.merge(key_info[key_no].constraint_correlated);
+ key_map map_2= key_info[(*filter_ptr_2)->key_no].overlapped;
+ map_2.merge(key_info[(*filter_ptr_2)->key_no].constraint_correlated);
+ map_1.intersect(map_2);
+ if (map_1.is_clear_all())
+ {
+ (*filter_ptr_1)->abs_independent.set_bit((*filter_ptr_2)->key_no);
+ (*filter_ptr_2)->abs_independent.set_bit(key_no);
+ }
+ }
+ }
+
+ /* Sort the array range_filter_cost_info by 'a' in descending order */
+ my_qsort(range_rowid_filter_cost_info_ptr,
+ range_rowid_filter_cost_info_elems,
+ sizeof(Range_rowid_filter_cost_info *),
+ (qsort_cmp) compare_range_rowid_filter_cost_info_by_a);
+
+ /*
+ For each element check whether it is created for the filter that
+ can be ever chosen as the best one. If it's not the case remove
+ from the array. Otherwise put it in the array in such a place
+ that all already checked elements left the array are ordered by
+ cross_x.
+ */
+
+ Range_rowid_filter_cost_info **cand_filter_ptr=
+ range_rowid_filter_cost_info_ptr;
+ for (uint i= 0;
+ i < range_rowid_filter_cost_info_elems;
+ i++, cand_filter_ptr++)
+ {
+ bool is_pruned= false;
+ Range_rowid_filter_cost_info **usable_filter_ptr=
+ range_rowid_filter_cost_info_ptr;
+ key_map abs_indep;
+ abs_indep.clear_all();
+ for (uint j= 0; j < i; j++, usable_filter_ptr++)
+ {
+ if ((*cand_filter_ptr)->cross_x >= (*usable_filter_ptr)->cross_x)
+ {
+ if (abs_indep.is_set((*usable_filter_ptr)->key_no))
+ {
+ /*
+ The following is true here for the element e being checked:
+ There are at 2 elements e1 and e2 among already selected such that
+ e1.cross_x < e.cross_x and e1.a > e.a
+ and
+ e2.cross_x < e_cross_x and e2.a > e.a,
+ i.e. the range filters f1, f2 of both e1 and e2 always promise
+ better gains then the range filter of e.
+ As e1 and e2 are absolutely independent one of the range filters
+ f1, f2 will be always a better choice than f1 no matter what index
+ is chosen to access the table. Because of this the element e
+ can be safely removed from the array.
+ */
+
+ is_pruned= true;
+ break;
+ }
+ abs_indep.merge((*usable_filter_ptr)->abs_independent);
+ }
+ else
+ {
+ /*
+ Move the element being checked to the proper position to have all
+ elements that have been already checked to be sorted by cross_x
+ */
+ Range_rowid_filter_cost_info *moved= *cand_filter_ptr;
+ memmove(usable_filter_ptr+1, usable_filter_ptr,
+ sizeof(Range_rowid_filter_cost_info *) * (i-j-1));
+ *usable_filter_ptr= moved;
+ }
+ }
+ if (is_pruned)
+ {
+ /* Remove the checked element from the array */
+ memmove(cand_filter_ptr, cand_filter_ptr+1,
+ sizeof(Range_rowid_filter_cost_info *) *
+ (range_rowid_filter_cost_info_elems - 1 - i));
+ range_rowid_filter_cost_info_elems--;
+ }
+ }
+}
+
+
+/**
+ @brief
+ Return maximum number of elements that a container allowed to have
+ */
+
+static ulonglong
+get_max_range_rowid_filter_elems_for_table(
+ THD *thd, TABLE *tab,
+ Rowid_filter_container_type cont_type)
+{
+ switch (cont_type) {
+ case SORTED_ARRAY_CONTAINER :
+ return thd->variables.max_rowid_filter_size/tab->file->ref_length;
+ default :
+ DBUG_ASSERT(0);
+ return 0;
+ }
+}
+
+
+/**
+ @brief
+ Prepare info on possible range filters used by optimizer
+
+ @param table The thread handler
+
+ @details
+ The function first selects the indexes of the table that potentially
+ can be used for range filters and allocates an array of the objects
+ of the Range_rowid_filter_cost_info type to store cost info on
+ possible range filters and an array of pointers to these objects.
+ The latter is created for easy sorting of the objects with cost info
+ by different sort criteria. Then the function initializes the allocated
+ array with cost info for each possible range filter. After this
+ the function calls the method TABLE::prune_range_rowid_filters().
+ The method removes the elements of the array for the filters that
+ promise less gain then others remaining in the array in any situation
+ and optimizes the order of the elements for faster choice of the best
+ range filter.
+*/
+
+void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd)
+{
+ uint key_no;
+ key_map usable_range_filter_keys;
+ usable_range_filter_keys.clear_all();
+ key_map::Iterator it(quick_keys);
+
+ /*
+ From all indexes that can be used for range accesses select only such that
+ - range filter pushdown is supported by the engine for them (1)
+ - they are not clustered primary (2)
+ - the range filter containers for them are not too large (3)
+ */
+ while ((key_no= it++) != key_map::Iterator::BITMAP_END)
+ {
+ if (!(file->index_flags(key_no, 0, 1) & HA_DO_RANGE_FILTER_PUSHDOWN)) // !1
+ continue;
+ if (key_no == s->primary_key && file->primary_key_is_clustered()) // !2
+ continue;
+ if (quick_rows[key_no] >
+ get_max_range_rowid_filter_elems_for_table(thd, this,
+ SORTED_ARRAY_CONTAINER)) // !3
+ continue;
+ usable_range_filter_keys.set_bit(key_no);
+ }
+
+ /*
+ Allocate an array of objects to store cost info for the selected filters
+ and allocate an array of pointers to these objects
+ */
+
+ range_rowid_filter_cost_info_elems= usable_range_filter_keys.bits_set();
+ if (!range_rowid_filter_cost_info_elems)
+ return;
+
+ range_rowid_filter_cost_info_ptr=
+ (Range_rowid_filter_cost_info **)
+ thd->calloc(sizeof(Range_rowid_filter_cost_info *) *
+ range_rowid_filter_cost_info_elems);
+ range_rowid_filter_cost_info=
+ new (thd->mem_root)
+ Range_rowid_filter_cost_info[range_rowid_filter_cost_info_elems];
+ if (!range_rowid_filter_cost_info_ptr || !range_rowid_filter_cost_info)
+ {
+ range_rowid_filter_cost_info_elems= 0;
+ return;
+ }
+
+ /* Fill the allocated array with cost info on the selected range filters */
+
+ Range_rowid_filter_cost_info **curr_ptr= range_rowid_filter_cost_info_ptr;
+ Range_rowid_filter_cost_info *curr_filter_cost_info=
+ range_rowid_filter_cost_info;
+
+ key_map::Iterator li(usable_range_filter_keys);
+ while ((key_no= li++) != key_map::Iterator::BITMAP_END)
+ {
+ *curr_ptr= curr_filter_cost_info;
+ curr_filter_cost_info->init(SORTED_ARRAY_CONTAINER, this, key_no);
+ curr_ptr++;
+ curr_filter_cost_info++;
+ }
+
+ prune_range_rowid_filters();
+
+ if (unlikely(thd->trace_started()))
+ trace_range_rowid_filters(thd);
+}
+
+
+void TABLE::trace_range_rowid_filters(THD *thd) const
+{
+ if (!range_rowid_filter_cost_info_elems)
+ return;
+
+ Range_rowid_filter_cost_info **p= range_rowid_filter_cost_info_ptr;
+ Range_rowid_filter_cost_info **end= p + range_rowid_filter_cost_info_elems;
+
+ Json_writer_object js_obj(thd);
+ js_obj.add_table_name(this);
+ Json_writer_array js_arr(thd, "rowid_filters");
+
+ for (; p < end; p++)
+ (*p)->trace_info(thd);
+}
+
+
+void Range_rowid_filter_cost_info::trace_info(THD *thd)
+{
+ Json_writer_object js_obj(thd);
+ js_obj.add("key", table->key_info[key_no].name);
+ js_obj.add("build_cost", b);
+ js_obj.add("rows", est_elements);
+}
+
+/**
+ @brief
+ Choose the best range filter for the given access of the table
+
+ @param access_key_no The index by which the table is accessed
+ @param records The estimated total number of key tuples with this access
+ @param access_cost_factor the cost of a random seek to access the table
+
+ @details
+ The function looks through the array of cost info for range filters
+ and chooses the element for the range filter that promise the greatest
+ gain with the the ref or range access of the table by access_key_no.
+ As the array is sorted by cross_x in ascending order the function stops
+ the look through as soon as it reaches the first element with
+ cross_x_adj > records because the range filter for this element and the
+ range filters for all remaining elements do not promise positive gains.
+
+ @note
+ It is easy to see that if cross_x[i] > cross_x[j] then
+ cross_x_adj[i] > cross_x_adj[j]
+
+ @retval Pointer to the cost info for the range filter that promises
+ the greatest gain, NULL if there is no such range filter
+*/
+
+Range_rowid_filter_cost_info *
+TABLE::best_range_rowid_filter_for_partial_join(uint access_key_no,
+ double records,
+ double access_cost_factor)
+{
+ if (range_rowid_filter_cost_info_elems == 0 ||
+ covering_keys.is_set(access_key_no))
+ return 0;
+
+ // Disallow use of range filter if the key contains partially-covered
+ // columns.
+ for (uint i= 0; i < key_info[access_key_no].usable_key_parts; i++)
+ {
+ if (key_info[access_key_no].key_part[i].field->type() == MYSQL_TYPE_BLOB)
+ return 0;
+ }
+
+ /*
+ Currently we do not support usage of range filters if the table
+ is accessed by the clustered primary key. It does not make sense
+ if a full key is used. If the table is accessed by a partial
+ clustered primary key it would, but the current InnoDB code does not
+ allow it. Later this limitation will be lifted
+ */
+ if (access_key_no == s->primary_key && file->primary_key_is_clustered())
+ return 0;
+
+ Range_rowid_filter_cost_info *best_filter= 0;
+ double best_filter_gain= 0;
+
+ key_map no_filter_usage= key_info[access_key_no].overlapped;
+ no_filter_usage.merge(key_info[access_key_no].constraint_correlated);
+ for (uint i= 0; i < range_rowid_filter_cost_info_elems ; i++)
+ {
+ double curr_gain = 0;
+ Range_rowid_filter_cost_info *filter= range_rowid_filter_cost_info_ptr[i];
+
+ /*
+ Do not use a range filter that uses an in index correlated with
+ the index by which the table is accessed
+ */
+ if ((filter->key_no == access_key_no) ||
+ no_filter_usage.is_set(filter->key_no))
+ continue;
+
+ filter->set_adjusted_gain_param(access_cost_factor);
+
+ if (records < filter->cross_x_adj)
+ {
+ /* Does not make sense to look through the remaining filters */
+ break;
+ }
+
+ curr_gain= filter->get_adjusted_gain(records);
+ if (best_filter_gain < curr_gain)
+ {
+ best_filter_gain= curr_gain;
+ best_filter= filter;
+ }
+ }
+ return best_filter;
+}
+
+
+/**
+ @brief
+ Fill the range rowid filter performing the associated range index scan
+
+ @details
+ This function performs the range index scan associated with this
+ range filter and place into the filter the rowids / primary keys
+ read from key tuples when doing this scan.
+ @retval
+ false on success
+ true otherwise
+
+ @note
+ The function assumes that the quick select object to perform
+ the index range scan has been already created.
+
+ @note
+ Currently the same table handler is used to access the joined table
+ and to perform range index scan filling the filter.
+ In the future two different handlers will be used for this
+ purposes to facilitate a lazy building of the filter.
+*/
+
+bool Range_rowid_filter::fill()
+{
+ int rc= 0;
+ handler *file= table->file;
+ THD *thd= table->in_use;
+ QUICK_RANGE_SELECT* quick= (QUICK_RANGE_SELECT*) select->quick;
+
+ uint table_status_save= table->status;
+ Item *pushed_idx_cond_save= file->pushed_idx_cond;
+ uint pushed_idx_cond_keyno_save= file->pushed_idx_cond_keyno;
+ bool in_range_check_pushed_down_save= file->in_range_check_pushed_down;
+
+ table->status= 0;
+ file->pushed_idx_cond= 0;
+ file->pushed_idx_cond_keyno= MAX_KEY;
+ file->in_range_check_pushed_down= false;
+
+ /* We're going to just read rowids / primary keys */
+ table->prepare_for_position();
+
+ table->file->ha_start_keyread(quick->index);
+
+ if (quick->init() || quick->reset())
+ rc= 1;
+
+ while (!rc)
+ {
+ rc= quick->get_next();
+ if (thd->killed)
+ rc= 1;
+ if (!rc)
+ {
+ file->position(quick->record);
+ if (container->add(NULL, (char*) file->ref))
+ rc= 1;
+ else
+ tracker->increment_container_elements_count();
+ }
+ }
+
+ quick->range_end();
+ table->file->ha_end_keyread();
+
+ table->status= table_status_save;
+ file->pushed_idx_cond= pushed_idx_cond_save;
+ file->pushed_idx_cond_keyno= pushed_idx_cond_keyno_save;
+ file->in_range_check_pushed_down= in_range_check_pushed_down_save;
+ tracker->report_container_buff_size(table->file->ref_length);
+
+ if (rc != HA_ERR_END_OF_FILE)
+ return 1;
+ table->file->rowid_filter_is_active= true;
+ return 0;
+}
+
+
+/**
+ @brief
+ Binary search in the sorted array of a rowid filter
+
+ @param ctxt context of the search
+ @parab elem rowid / primary key to look for
+
+ @details
+ The function looks for the rowid / primary key ' elem' in this container
+ assuming that ctxt contains a pointer to the TABLE structure created
+ for the table to whose row elem refers to.
+
+ @retval
+ true elem is found in the container
+ false otherwise
+*/
+
+bool Rowid_filter_sorted_array::check(void *ctxt, char *elem)
+{
+ TABLE *table= (TABLE *) ctxt;
+ if (!is_checked)
+ {
+ refpos_container.sort(refpos_order_cmp, (void *) (table->file));
+ is_checked= true;
+ }
+ int l= 0;
+ int r= refpos_container.elements()-1;
+ while (l <= r)
+ {
+ int m= (l + r) / 2;
+ int cmp= refpos_order_cmp((void *) (table->file),
+ refpos_container.get_pos(m), elem);
+ if (cmp == 0)
+ return true;
+ if (cmp < 0)
+ l= m + 1;
+ else
+ r= m-1;
+ }
+ return false;
+}
+
+
+Range_rowid_filter::~Range_rowid_filter()
+{
+ delete container;
+ container= 0;
+ if (select)
+ {
+ if (select->quick)
+ {
+ delete select->quick;
+ select->quick= 0;
+ }
+ delete select;
+ select= 0;
+ }
+}
diff --git a/sql/rowid_filter.h b/sql/rowid_filter.h
new file mode 100644
index 00000000000..467b6884ca6
--- /dev/null
+++ b/sql/rowid_filter.h
@@ -0,0 +1,470 @@
+/*
+ Copyright (c) 2018, 2019 MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef ROWID_FILTER_INCLUDED
+#define ROWID_FILTER_INCLUDED
+
+
+#include "mariadb.h"
+#include "sql_array.h"
+
+/*
+
+ What rowid / primary filters are
+ --------------------------------
+
+ Consider a join query Q of the form
+ SELECT * FROM T1, ... , Tk WHERE P.
+
+ For any of the table reference Ti(Q) from the from clause of Q different
+ rowid / primary key filters (pk-filters for short) can be built.
+ A pk-filter F built for Ti(Q) is a set of rowids / primary keys of Ti
+ F= {pk1,...,pkN} such that for any row r=r1||...||rk from the result set of Q
+ ri's rowid / primary key pk(ri) is contained in F.
+
+ When pk-filters are useful
+ --------------------------
+
+ If building a pk-filter F for Ti(Q )is not too costly and its cardinality #F
+ is much less than the cardinality of T - #T then using the pk-filter when
+ executing Q might be quite beneficial.
+
+ Let r be a random row from Ti. Let s(F) be the probability that pk(r)
+ belongs to F. Let BC(F) be the cost of building F.
+
+ Suppose that the optimizer has chosen for Q a plan with this join order
+ T1 => ... Tk and that the table Ti is accessed by a ref access using index I.
+ Let K = {k1,...,kM} be the set of all rowid/primary keys values used to access
+ rows of Ti when looking for matches in this table.to join Ti by index I.
+
+ Let's assume that two set sets K and F are uncorrelated. With this assumption
+ if before accessing data from Ti by the rowid / primary key k we first
+ check whether k is in F then we can expect saving on M*(1-s(S)) accesses of
+ data rows from Ti. If we can guarantee that test whether k is in F is
+ relatively cheap then we can gain a lot assuming that BC(F) is much less
+ then the cost of fetching M*(1-s(S)) records from Ti and following
+ evaluation of conditions pushed into Ti.
+
+ Making pk-filter test cheap
+ ---------------------------
+
+ If the search structure to test whether an element is in F can be fully
+ placed in RAM then this test is expected to be be much cheaper than a random
+ access of a record from Ti. We'll consider two search structures for
+ pk-filters: ordered array and bloom filter. Ordered array is easy to
+ implement, but it's space consuming. If a filter contains primary keys
+ then at least space for each primary key from the filter must be allocated
+ in the search structure. On a the opposite a bloom filter requires a
+ fixed number of bits and this number does not depend on the cardinality
+ of the pk-filter (10 bits per element will serve pk-filter of any size).
+
+*/
+
+/*
+
+ How and when the optimizer builds and uses range rowid filters
+ --------------------------------------------------------------
+
+ 1. In make_join_statistics()
+ for each join table s
+ after the call of get_quick_record_count()
+ the TABLE::method init_cost_info_for_usable_range_rowid_filters()
+ is called
+ The method build an array of Range_rowid_filter_cost_info elements
+ containing the cost info on possible range filters for s->table.
+ The array is optimized for further usage.
+
+ 2. For each partial join order when the optimizer considers joining
+ table s to this partial join
+ In the function best_access_path()
+ a. When evaluating a ref access r by index idx to join s
+ the optimizer estimates the effect of usage of each possible
+ range filter f and chooses one with the best gain. The gain
+ is taken into account when the cost of thr ref access r is
+ calculated. If it turns out that this is the best ref access
+ to join s then the info about the chosen filter together
+ with the info on r is remembered in the corresponding element
+ of the array of POSITION structures.
+ [We evaluate every pair (ref access, range_filter) rather then
+ every pair (best ref access, range filter) because if the index
+ ref_idx used for ref access r correlates with the index rf_idx
+ used by the filter f then the pair (r,f) is not evaluated
+ at all as we don't know how to estimate the effect of correlation
+ between ref_idx and rf_idx.]
+ b. When evaluating the best range access to join table s the
+ optimizer estimates the effect of usage of each possible
+ range filter f and chooses one with the best gain.
+ [Here we should have evaluated every pair (range access,
+ range filter) as well, but it's not done yet.]
+
+ 3. When the cheapest execution plan has been chosen and after the
+ call of JOIN::get_best_combination()
+ The method JOIN::make_range_rowid_filters() is called
+ For each range rowid filter used in the chosen execution plan
+ the method creates a quick select object to be able to perform
+ index range scan to fill the filter at the execution stage.
+ The method also creates Range_rowid_filter objects that are
+ used at the execution stage.
+
+ 4. Just before the execution stage
+ The method JOIN::init_range_rowid_filters() is called.
+ For each join table s that is to be accessed with usage of a range
+ filter the method allocates containers for the range filter and
+ it lets the engine know that the filter will be used when
+ accessing s.
+
+ 5. At the execution stage
+ In the function sub_select() just before the first access of a join
+ table s employing a range filter
+ The method JOIN_TAB::build_range_rowid_filter_if_needed() is called
+ The method fills the filter using the quick select created by
+ JOIN::make_range_rowid_filters().
+
+ 6. The accessed key tuples are checked against the filter within the engine
+ using the info pushed into it.
+
+*/
+
+struct TABLE;
+class SQL_SELECT;
+class Rowid_filter_container;
+class Range_rowid_filter_cost_info;
+
+/* Cost to write rowid into array */
+#define ARRAY_WRITE_COST 0.005
+/* Factor used to calculate cost of sorting rowids in array */
+#define ARRAY_SORT_C 0.01
+/* Cost to evaluate condition */
+#define COST_COND_EVAL 0.2
+
+typedef enum
+{
+ SORTED_ARRAY_CONTAINER,
+ BLOOM_FILTER_CONTAINER // Not used yet
+} Rowid_filter_container_type;
+
+/**
+ @class Rowid_filter_container
+
+ The interface for different types of containers to store info on the set
+ of rowids / primary keys that defines a pk-filter.
+
+ There will be two implementations of this abstract class.
+ - sorted array
+ - bloom filter
+*/
+
+class Rowid_filter_container : public Sql_alloc
+{
+public:
+
+ virtual Rowid_filter_container_type get_type() = 0;
+
+ /* Allocate memory for the container */
+ virtual bool alloc() = 0;
+
+ /*
+ @brief Add info on a rowid / primary to the container
+ @param ctxt The context info (opaque)
+ @param elem The rowid / primary key to be added to the container
+ @retval true if elem is successfully added
+ */
+ virtual bool add(void *ctxt, char *elem) = 0;
+
+ /*
+ @brief Check whether a rowid / primary key is in container
+ @param ctxt The context info (opaque)
+ @param elem The rowid / primary key to be checked against the container
+ @retval False if elem is definitely not in the container
+ */
+ virtual bool check(void *ctxt, char *elem) = 0;
+
+ virtual ~Rowid_filter_container() {}
+};
+
+
+/**
+ @class Rowid_filter
+
+ The interface for different types of pk-filters
+
+ Currently we support only range pk filters.
+*/
+
+class Rowid_filter : public Sql_alloc
+{
+protected:
+
+ /* The container to store info the set of elements in the filter */
+ Rowid_filter_container *container;
+
+ Rowid_filter_tracker *tracker;
+
+public:
+ Rowid_filter(Rowid_filter_container *container_arg)
+ : container(container_arg) {}
+
+ /*
+ Build the filter :
+ fill it with info on the set of elements placed there
+ */
+ virtual bool build() = 0;
+
+ /*
+ Check whether an element is in the filter.
+ Returns false is the elements is definitely not in the filter.
+ */
+ virtual bool check(char *elem) = 0;
+
+ virtual ~Rowid_filter() {}
+
+ Rowid_filter_container *get_container() { return container; }
+
+ void set_tracker(Rowid_filter_tracker *track_arg) { tracker= track_arg; }
+ Rowid_filter_tracker *get_tracker() { return tracker; }
+};
+
+
+/**
+ @class Rowid_filter_container
+
+ The implementation of the Rowid_interface used for pk-filters
+ that are filled when performing range index scans.
+*/
+
+class Range_rowid_filter: public Rowid_filter
+{
+ /* The table for which the rowid filter is built */
+ TABLE *table;
+ /* The select to perform the range scan to fill the filter */
+ SQL_SELECT *select;
+ /* The cost info on the filter (used for EXPLAIN/ANALYZE) */
+ Range_rowid_filter_cost_info *cost_info;
+
+public:
+ Range_rowid_filter(TABLE *tab,
+ Range_rowid_filter_cost_info *cost_arg,
+ Rowid_filter_container *container_arg,
+ SQL_SELECT *sel)
+ : Rowid_filter(container_arg), table(tab), select(sel), cost_info(cost_arg)
+ {}
+
+ ~Range_rowid_filter();
+
+ bool build() { return fill(); }
+
+ bool check(char *elem)
+ {
+ bool was_checked= container->check(table, elem);
+ tracker->increment_checked_elements_count(was_checked);
+ return was_checked;
+ }
+
+ bool fill();
+
+ SQL_SELECT *get_select() { return select; }
+};
+
+
+/**
+ @class Refpos_container_sorted_array
+
+ The wrapper class over Dynamic_array<char> to facilitate operations over
+ array of elements of the type char[N] where N is the same for all elements
+*/
+
+class Refpos_container_sorted_array : public Sql_alloc
+{
+ /*
+ Maximum number of elements in the array
+ (Now is used only at the initialization of the dynamic array)
+ */
+ uint max_elements;
+ /* Number of bytes allocated for an element */
+ uint elem_size;
+ /* The dynamic array over which the wrapper is built */
+ Dynamic_array<char> *array;
+
+public:
+
+ Refpos_container_sorted_array(uint max_elems, uint elem_sz)
+ : max_elements(max_elems), elem_size(elem_sz), array(0) {}
+
+ ~Refpos_container_sorted_array()
+ {
+ delete array;
+ array= 0;
+ }
+
+ bool alloc()
+ {
+ array= new Dynamic_array<char> (elem_size * max_elements,
+ elem_size * max_elements/sizeof(char) + 1);
+ return array == NULL;
+ }
+
+ bool add(char *elem)
+ {
+ for (uint i= 0; i < elem_size; i++)
+ {
+ if (array->append(elem[i]))
+ return true;
+ }
+ return false;
+ }
+
+ char *get_pos(uint n)
+ {
+ return array->get_pos(n * elem_size);
+ }
+
+ uint elements() { return (uint) (array->elements() / elem_size); }
+
+ void sort (int (*cmp) (void *ctxt, const void *el1, const void *el2),
+ void *cmp_arg)
+ {
+ my_qsort2(array->front(), array->elements()/elem_size,
+ elem_size, (qsort2_cmp) cmp, cmp_arg);
+ }
+};
+
+
+/**
+ @class Rowid_filter_sorted_array
+
+ The implementation of the Rowid_filter_container interface as
+ a sorted array container of rowids / primary keys
+*/
+
+class Rowid_filter_sorted_array: public Rowid_filter_container
+{
+ /* The dynamic array to store rowids / primary keys */
+ Refpos_container_sorted_array refpos_container;
+ /* Initially false, becomes true after the first call of (check() */
+ bool is_checked;
+
+public:
+ Rowid_filter_sorted_array(uint elems, uint elem_size)
+ : refpos_container(elems, elem_size), is_checked(false) {}
+
+ Rowid_filter_container_type get_type()
+ { return SORTED_ARRAY_CONTAINER; }
+
+ bool alloc() { return refpos_container.alloc(); }
+
+ bool add(void *ctxt, char *elem) { return refpos_container.add(elem); }
+
+ bool check(void *ctxt, char *elem);
+};
+
+/**
+ @class Range_rowid_filter_cost_info
+
+ An objects of this class is created for each potentially usable
+ range filter. It contains the info that allows to figure out
+ whether usage of the range filter promises some gain.
+*/
+
+class Range_rowid_filter_cost_info : public Sql_alloc
+{
+ /* The table for which the range filter is to be built (if needed) */
+ TABLE *table;
+ /* Estimated number of elements in the filter */
+ ulonglong est_elements;
+ /* The cost of building the range filter */
+ double b;
+ /*
+ a*N-b yields the gain of the filter
+ for N key tuples of the index key_no
+ */
+ double a;
+ /* The value of N where the gain is 0 */
+ double cross_x;
+ /* Used for pruning of the potential range filters */
+ key_map abs_independent;
+
+ /*
+ These two parameters are used to choose the best range filter
+ in the function TABLE::best_range_rowid_filter_for_partial_join
+ */
+ double a_adj;
+ double cross_x_adj;
+
+public:
+ /* The type of the container of the range filter */
+ Rowid_filter_container_type container_type;
+ /* The index whose range scan would be used to build the range filter */
+ uint key_no;
+ /* The selectivity of the range filter */
+ double selectivity;
+
+ Range_rowid_filter_cost_info() : table(0), key_no(0) {}
+
+ void init(Rowid_filter_container_type cont_type,
+ TABLE *tab, uint key_no);
+
+ double build_cost(Rowid_filter_container_type container_type);
+
+ inline double lookup_cost(Rowid_filter_container_type cont_type);
+
+ inline double
+ avg_access_and_eval_gain_per_row(Rowid_filter_container_type cont_type);
+
+ inline double avg_adjusted_gain_per_row(double access_cost_factor);
+
+ inline void set_adjusted_gain_param(double access_cost_factor);
+
+ /* Get the gain that usage of filter promises for r key tuples */
+ inline double get_gain(double r)
+ {
+ return r * a - b;
+ }
+
+ /* Get the adjusted gain that usage of filter promises for r key tuples */
+ inline double get_adjusted_gain(double r)
+ {
+ return r * a_adj - b;
+ }
+
+ /*
+ The gain promised by usage of the filter for r key tuples
+ due to less condition evaluations
+ */
+ inline double get_cmp_gain(double r)
+ {
+ return r * (1 - selectivity) / TIME_FOR_COMPARE;
+ }
+
+ Rowid_filter_container *create_container();
+
+ double get_a() { return a; }
+
+ void trace_info(THD *thd);
+
+ friend
+ void TABLE::prune_range_rowid_filters();
+
+ friend
+ void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd);
+
+ friend
+ Range_rowid_filter_cost_info *
+ TABLE::best_range_rowid_filter_for_partial_join(uint access_key_no,
+ double records,
+ double access_cost_factor);
+};
+
+#endif /* ROWID_FILTER_INCLUDED */
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index 8408025d389..3da3f554577 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -80,7 +80,7 @@ rpl_slave_state::record_and_update_gtid(THD *thd, rpl_group_info *rgi)
rgi->gtid_pending= false;
if (rgi->gtid_ignore_duplicate_state!=rpl_group_info::GTID_DUPLICATE_IGNORE)
{
- if (record_gtid(thd, &rgi->current_gtid, sub_id, NULL, false, &hton))
+ if (record_gtid(thd, &rgi->current_gtid, sub_id, false, false, &hton))
DBUG_RETURN(1);
update_state_hash(sub_id, &rgi->current_gtid, hton, rgi);
}
@@ -245,7 +245,7 @@ rpl_slave_state_free_element(void *arg)
rpl_slave_state::rpl_slave_state()
- : last_sub_id(0), gtid_pos_tables(0), loaded(false)
+ : pending_gtid_count(0), last_sub_id(0), gtid_pos_tables(0), loaded(false)
{
mysql_mutex_init(key_LOCK_slave_state, &LOCK_slave_state,
MY_MUTEX_INIT_SLOW);
@@ -257,7 +257,7 @@ rpl_slave_state::rpl_slave_state()
rpl_slave_state::~rpl_slave_state()
{
- free_gtid_pos_tables((struct gtid_pos_table *)gtid_pos_tables);
+ free_gtid_pos_tables(gtid_pos_tables.load(std::memory_order_relaxed));
truncate_hash();
my_hash_free(&hash);
delete_dynamic(&gtid_sort_array);
@@ -332,14 +332,11 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id,
}
}
rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL;
-
-#ifdef HAVE_REPLICATION
- rgi->pending_gtid_deletes_clear();
-#endif
}
if (!(list_elem= (list_element *)my_malloc(sizeof(*list_elem), MYF(MY_WME))))
return 1;
+ list_elem->domain_id= domain_id;
list_elem->server_id= server_id;
list_elem->sub_id= sub_id;
list_elem->seq_no= seq_no;
@@ -349,6 +346,15 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id,
if (last_sub_id < sub_id)
last_sub_id= sub_id;
+#ifdef HAVE_REPLICATION
+ ++pending_gtid_count;
+ if (pending_gtid_count >= opt_gtid_cleanup_batch_size)
+ {
+ pending_gtid_count = 0;
+ slave_background_gtid_pending_delete_request();
+ }
+#endif
+
return 0;
}
@@ -383,20 +389,22 @@ rpl_slave_state::get_element(uint32 domain_id)
int
-rpl_slave_state::put_back_list(uint32 domain_id, list_element *list)
+rpl_slave_state::put_back_list(list_element *list)
{
- element *e;
+ element *e= NULL;
int err= 0;
mysql_mutex_lock(&LOCK_slave_state);
- if (!(e= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0)))
- {
- err= 1;
- goto end;
- }
while (list)
{
list_element *next= list->next;
+
+ if ((!e || e->domain_id != list->domain_id) &&
+ !(e= (element *)my_hash_search(&hash, (const uchar *)&list->domain_id, 0)))
+ {
+ err= 1;
+ goto end;
+ }
e->add(list);
list= next;
}
@@ -489,21 +497,18 @@ gtid_check_rpl_slave_state_table(TABLE *table)
void
rpl_slave_state::select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename)
{
- struct gtid_pos_table *list, *table_entry, *default_entry;
-
/*
See comments on rpl_slave_state::gtid_pos_tables for rules around proper
access to the list.
*/
- list= (struct gtid_pos_table *)
- my_atomic_loadptr_explicit(&gtid_pos_tables, MY_MEMORY_ORDER_ACQUIRE);
+ auto list= gtid_pos_tables.load(std::memory_order_acquire);
Ha_trx_info *ha_info;
uint count = 0;
for (ha_info= thd->transaction.all.ha_list; ha_info; ha_info= ha_info->next())
{
void *trx_hton= ha_info->ht();
- table_entry= list;
+ auto table_entry= list;
if (!ha_info->is_trx_read_write() || trx_hton == binlog_hton)
continue;
@@ -557,9 +562,8 @@ rpl_slave_state::select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename)
already active in the transaction, or if there is no current transaction
engines available, we return the default gtid_slave_pos table.
*/
- default_entry= (struct gtid_pos_table *)
- my_atomic_loadptr_explicit(&default_gtid_pos_table, MY_MEMORY_ORDER_ACQUIRE);
- *out_tablename= default_entry->table_name;
+ *out_tablename=
+ default_gtid_pos_table.load(std::memory_order_acquire)->table_name;
/* Record in status that we failed to find a suitable gtid_pos table. */
if (count > 0)
{
@@ -573,12 +577,12 @@ rpl_slave_state::select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename)
/*
Write a gtid to the replication slave state table.
+ Do it as part of the transaction, to get slave crash safety, or as a separate
+ transaction if !in_transaction (eg. MyISAM or DDL).
+
gtid The global transaction id for this event group.
sub_id Value allocated within the sub_id when the event group was
read (sub_id must be consistent with commit order in master binlog).
- rgi rpl_group_info context, if we are recording the gtid transactionally
- as part of replicating a transactional event. NULL if called from
- outside of a replicated transaction.
Note that caller must later ensure that the new gtid and sub_id is inserted
into the appropriate HASH element with rpl_slave_state.add(), so that it can
@@ -586,16 +590,13 @@ rpl_slave_state::select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename)
*/
int
rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
- rpl_group_info *rgi, bool in_statement,
+ bool in_transaction, bool in_statement,
void **out_hton)
{
TABLE_LIST tlist;
int err= 0, not_sql_thread;
bool table_opened= false;
TABLE *table;
- list_element *delete_list= 0, *next, *cur, **next_ptr_ptr, **best_ptr_ptr;
- uint64 best_sub_id;
- element *elem;
ulonglong thd_saved_option= thd->variables.option_bits;
Query_tables_list lex_backup;
wait_for_commit* suspended_wfc;
@@ -685,7 +686,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
thd->wsrep_ignore_table= true;
#endif
- if (!rgi)
+ if (!in_transaction)
{
DBUG_PRINT("info", ("resetting OPTION_BEGIN"));
thd->variables.option_bits&=
@@ -717,168 +718,287 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
my_error(ER_OUT_OF_RESOURCES, MYF(0));
goto end;
}
+end:
- mysql_mutex_lock(&LOCK_slave_state);
- if ((elem= get_element(gtid->domain_id)) == NULL)
+#ifdef WITH_WSREP
+ thd->wsrep_ignore_table= false;
+#endif
+
+ if (table_opened)
{
- mysql_mutex_unlock(&LOCK_slave_state);
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- err= 1;
- goto end;
+ if (err || (err= ha_commit_trans(thd, FALSE)))
+ ha_rollback_trans(thd, FALSE);
+
+ close_thread_tables(thd);
+ if (in_transaction)
+ thd->mdl_context.release_statement_locks();
+ else
+ thd->release_transactional_locks();
}
+ thd->lex->restore_backup_query_tables_list(&lex_backup);
+ thd->variables.option_bits= thd_saved_option;
+ thd->resume_subsequent_commits(suspended_wfc);
+ DBUG_EXECUTE_IF("inject_record_gtid_serverid_100_sleep",
+ {
+ if (gtid->server_id == 100)
+ my_sleep(500000);
+ });
+ DBUG_RETURN(err);
+}
- /* Now pull out all GTIDs that were recorded in this engine. */
- delete_list = NULL;
- next_ptr_ptr= &elem->list;
- cur= elem->list;
- best_sub_id= 0;
- best_ptr_ptr= NULL;
- while (cur)
+
+/*
+ Return a list of all old GTIDs in any mysql.gtid_slave_pos* table that are
+ no longer needed and can be deleted from the table.
+
+ Within each domain, we need to keep around the latest GTID (the one with the
+ highest sub_id), but any others in that domain can be deleted.
+*/
+rpl_slave_state::list_element *
+rpl_slave_state::gtid_grab_pending_delete_list()
+{
+ uint32 i;
+ list_element *full_list;
+
+ mysql_mutex_lock(&LOCK_slave_state);
+ full_list= NULL;
+ for (i= 0; i < hash.records; ++i)
{
- list_element *next= cur->next;
- if (cur->hton == hton)
- {
- /* Belongs to same engine, so move it to the delete list. */
- cur->next= delete_list;
- delete_list= cur;
- if (cur->sub_id > best_sub_id)
+ element *elem= (element *)my_hash_element(&hash, i);
+ list_element *elist= elem->list;
+ list_element *last_elem, **best_ptr_ptr, *cur, *next;
+ uint64 best_sub_id;
+
+ if (!elist)
+ continue; /* Nothing here */
+
+ /* Delete any old stuff, but keep around the most recent one. */
+ cur= elist;
+ best_sub_id= cur->sub_id;
+ best_ptr_ptr= &elist;
+ last_elem= cur;
+ while ((next= cur->next)) {
+ last_elem= next;
+ if (next->sub_id > best_sub_id)
{
- best_sub_id= cur->sub_id;
- best_ptr_ptr= &delete_list;
- }
- else if (best_ptr_ptr == &delete_list)
+ best_sub_id= next->sub_id;
best_ptr_ptr= &cur->next;
- }
- else
- {
- /* Another engine, leave it in the list. */
- if (cur->sub_id > best_sub_id)
- {
- best_sub_id= cur->sub_id;
- /* Current best is not on the delete list. */
- best_ptr_ptr= NULL;
}
- *next_ptr_ptr= cur;
- next_ptr_ptr= &cur->next;
+ cur= next;
}
- cur= next;
- }
- *next_ptr_ptr= NULL;
- /*
- If the highest sub_id element is on the delete list, put it back on the
- original list, to preserve the highest sub_id element in the table for
- GTID position recovery.
- */
- if (best_ptr_ptr)
- {
+ /*
+ Append the new elements to the full list. Note the order is important;
+ we do it here so that we do not break the list if best_sub_id is the
+ last of the new elements.
+ */
+ last_elem->next= full_list;
+ /*
+ Delete the highest sub_id element from the old list, and put it back as
+ the single-element new list.
+ */
cur= *best_ptr_ptr;
*best_ptr_ptr= cur->next;
- cur->next= elem->list;
+ cur->next= NULL;
elem->list= cur;
+
+ /*
+ Collect the full list so far here. Note that elist may have moved if we
+ deleted the first element, so order is again important.
+ */
+ full_list= elist;
}
mysql_mutex_unlock(&LOCK_slave_state);
- if (!delete_list)
- goto end;
+ return full_list;
+}
- /* Now delete any already committed GTIDs. */
- bitmap_set_bit(table->read_set, table->field[0]->field_index);
- bitmap_set_bit(table->read_set, table->field[1]->field_index);
- if ((err= table->file->ha_index_init(0, 0)))
+/* Find the mysql.gtid_slave_posXXX table associated with a given hton. */
+LEX_CSTRING *
+rpl_slave_state::select_gtid_pos_table(void *hton)
+{
+ /*
+ See comments on rpl_slave_state::gtid_pos_tables for rules around proper
+ access to the list.
+ */
+ auto table_entry= gtid_pos_tables.load(std::memory_order_acquire);
+
+ while (table_entry)
{
- table->file->print_error(err, MYF(0));
- goto end;
+ if (table_entry->table_hton == hton)
+ {
+ if (likely(table_entry->state == GTID_POS_AVAILABLE))
+ return &table_entry->table_name;
+ }
+ table_entry= table_entry->next;
}
- cur = delete_list;
- while (cur)
- {
- uchar key_buffer[4+8];
- DBUG_EXECUTE_IF("gtid_slave_pos_simulate_failed_delete",
- { err= ENOENT;
- table->file->print_error(err, MYF(0));
- /* `break' does not work inside DBUG_EXECUTE_IF */
- goto dbug_break; });
+ return &default_gtid_pos_table.load(std::memory_order_acquire)->table_name;
+}
+
- next= cur->next;
+void
+rpl_slave_state::gtid_delete_pending(THD *thd,
+ rpl_slave_state::list_element **list_ptr)
+{
+ int err= 0;
+ ulonglong thd_saved_option;
+
+ if (unlikely(!loaded))
+ return;
+
+#ifdef WITH_WSREP
+ /*
+ Updates in slave state table should not be appended to galera transaction
+ writeset.
+ */
+ thd->wsrep_ignore_table= true;
+#endif
+
+ thd_saved_option= thd->variables.option_bits;
+ thd->variables.option_bits&=
+ ~(ulonglong)(OPTION_NOT_AUTOCOMMIT |OPTION_BEGIN |OPTION_BIN_LOG |
+ OPTION_GTID_BEGIN);
+
+ while (*list_ptr)
+ {
+ LEX_CSTRING *gtid_pos_table_name, *tmp_table_name;
+ Query_tables_list lex_backup;
+ TABLE_LIST tlist;
+ TABLE *table;
+ handler::Table_flags direct_pos= 0;
+ list_element *cur, **cur_ptr_ptr;
+ bool table_opened= false;
+ bool index_inited= false;
+ void *hton= (*list_ptr)->hton;
+
+ thd->reset_for_next_command();
- table->field[1]->store(cur->sub_id, true);
- /* domain_id is already set in table->record[0] from write_row() above. */
- key_copy(key_buffer, table->record[0], &table->key_info[0], 0, false);
- if (table->file->ha_index_read_map(table->record[1], key_buffer,
- HA_WHOLE_KEY, HA_READ_KEY_EXACT))
- /* We cannot find the row, assume it is already deleted. */
- ;
- else if ((err= table->file->ha_delete_row(table->record[1])))
- table->file->print_error(err, MYF(0));
/*
- In case of error, we still discard the element from the list. We do
- not want to endlessly error on the same element in case of table
- corruption or such.
+ Only the SQL thread can call select_gtid_pos_table without a mutex
+ Other threads needs to use a mutex and take into account that the
+ result may change during execution, so we have to make a copy.
*/
- cur= next;
- if (err)
+ mysql_mutex_lock(&LOCK_slave_state);
+ tmp_table_name= select_gtid_pos_table(hton);
+ gtid_pos_table_name= thd->make_clex_string(tmp_table_name->str,
+ tmp_table_name->length);
+ mysql_mutex_unlock(&LOCK_slave_state);
+ if (!gtid_pos_table_name)
+ {
+ /* Out of memory - we can try again later. */
break;
- }
-IF_DBUG(dbug_break:, )
- table->file->ha_index_end();
+ }
-end:
+ thd->lex->reset_n_backup_query_tables_list(&lex_backup);
+ tlist.init_one_table(&MYSQL_SCHEMA_NAME, gtid_pos_table_name, NULL, TL_WRITE);
+ if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
+ goto end;
+ table_opened= true;
+ table= tlist.table;
-#ifdef WITH_WSREP
- thd->wsrep_ignore_table= false;
-#endif
+ if ((err= gtid_check_rpl_slave_state_table(table)))
+ goto end;
- if (table_opened)
- {
- if (err || (err= ha_commit_trans(thd, FALSE)))
+ direct_pos= table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION;
+ bitmap_set_all(table->write_set);
+ table->rpl_write_set= table->write_set;
+
+ /* Now delete any already committed GTIDs. */
+ bitmap_set_bit(table->read_set, table->field[0]->field_index);
+ bitmap_set_bit(table->read_set, table->field[1]->field_index);
+
+ if (!direct_pos)
{
- /*
- If error, we need to put any remaining delete_list back into the HASH
- so we can do another delete attempt later.
- */
- if (delete_list)
+ if ((err= table->file->ha_index_init(0, 0)))
{
- put_back_list(gtid->domain_id, delete_list);
- delete_list = 0;
+ table->file->print_error(err, MYF(0));
+ goto end;
}
-
- ha_rollback_trans(thd, FALSE);
+ index_inited= true;
}
- close_thread_tables(thd);
- if (rgi)
+
+ cur = *list_ptr;
+ cur_ptr_ptr = list_ptr;
+ do
{
- thd->mdl_context.release_statement_locks();
+ uchar key_buffer[4+8];
+ list_element *next= cur->next;
+
+ if (cur->hton == hton)
+ {
+ int res;
+
+ table->field[0]->store((ulonglong)cur->domain_id, true);
+ table->field[1]->store(cur->sub_id, true);
+ if (direct_pos)
+ {
+ res= table->file->ha_rnd_pos_by_record(table->record[0]);
+ }
+ else
+ {
+ key_copy(key_buffer, table->record[0], &table->key_info[0], 0, false);
+ res= table->file->ha_index_read_map(table->record[0], key_buffer,
+ HA_WHOLE_KEY, HA_READ_KEY_EXACT);
+ }
+ DBUG_EXECUTE_IF("gtid_slave_pos_simulate_failed_delete",
+ { res= 1;
+ err= ENOENT;
+ sql_print_error("<DEBUG> Error deleting old GTID row");
+ });
+ if (res)
+ /* We cannot find the row, assume it is already deleted. */
+ ;
+ else if ((err= table->file->ha_delete_row(table->record[0])))
+ {
+ sql_print_error("Error deleting old GTID row: %s",
+ thd->get_stmt_da()->message());
+ /*
+ In case of error, we still discard the element from the list. We do
+ not want to endlessly error on the same element in case of table
+ corruption or such.
+ */
+ }
+ *cur_ptr_ptr= next;
+ my_free(cur);
+ }
+ else
+ {
+ /* Leave this one in the list until we get to the table for its hton. */
+ cur_ptr_ptr= &cur->next;
+ }
+ cur= next;
+ if (err)
+ break;
+ } while (cur);
+end:
+ if (table_opened)
+ {
+ DBUG_ASSERT(direct_pos || index_inited || err);
/*
- Save the list of old gtid entries we deleted. If this transaction
- fails later for some reason and is rolled back, the deletion of those
- entries will be rolled back as well, and we will need to put them back
- on the to-be-deleted list so we can re-do the deletion. Otherwise
- redundant rows in mysql.gtid_slave_pos may accumulate if transactions
- are rolled back and retried after record_gtid().
+ Index may not be initialized if there was a failure during
+ 'ha_index_init'. Hence check if index initialization is successful and
+ then invoke ha_index_end(). Ending an index which is not initialized
+ will lead to assert.
*/
-#ifdef HAVE_REPLICATION
- rgi->pending_gtid_deletes_save(gtid->domain_id, delete_list);
-#endif
- }
- else
- {
- thd->release_transactional_locks();
-#ifdef HAVE_REPLICATION
- rpl_group_info::pending_gtid_deletes_free(delete_list);
-#endif
+ if (index_inited)
+ table->file->ha_index_end();
+
+ if (err || (err= ha_commit_trans(thd, FALSE)))
+ ha_rollback_trans(thd, FALSE);
}
+ close_thread_tables(thd);
+ thd->release_transactional_locks();
+ thd->lex->restore_backup_query_tables_list(&lex_backup);
+
+ if (err)
+ break;
}
- thd->lex->restore_backup_query_tables_list(&lex_backup);
thd->variables.option_bits= thd_saved_option;
- thd->resume_subsequent_commits(suspended_wfc);
- DBUG_EXECUTE_IF("inject_record_gtid_serverid_100_sleep",
- {
- if (gtid->server_id == 100)
- my_sleep(500000);
- });
- DBUG_RETURN(err);
+
+#ifdef WITH_WSREP
+ thd->wsrep_ignore_table= false;
+#endif
}
@@ -1252,7 +1372,7 @@ rpl_slave_state::load(THD *thd, const char *state_from_master, size_t len,
if (gtid_parser_helper(&state_from_master, end, &gtid) ||
!(sub_id= next_sub_id(gtid.domain_id)) ||
- record_gtid(thd, &gtid, sub_id, NULL, in_statement, &hton) ||
+ record_gtid(thd, &gtid, sub_id, false, in_statement, &hton) ||
update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, hton, NULL))
return 1;
if (state_from_master == end)
@@ -1311,13 +1431,10 @@ void
rpl_slave_state::set_gtid_pos_tables_list(rpl_slave_state::gtid_pos_table *new_list,
rpl_slave_state::gtid_pos_table *default_entry)
{
- gtid_pos_table *old_list;
-
mysql_mutex_assert_owner(&LOCK_slave_state);
- old_list= (struct gtid_pos_table *)gtid_pos_tables;
- my_atomic_storeptr_explicit(&gtid_pos_tables, new_list, MY_MEMORY_ORDER_RELEASE);
- my_atomic_storeptr_explicit(&default_gtid_pos_table, default_entry,
- MY_MEMORY_ORDER_RELEASE);
+ auto old_list= gtid_pos_tables.load(std::memory_order_relaxed);
+ gtid_pos_tables.store(new_list, std::memory_order_release);
+ default_gtid_pos_table.store(default_entry, std::memory_order_release);
free_gtid_pos_tables(old_list);
}
@@ -1326,8 +1443,8 @@ void
rpl_slave_state::add_gtid_pos_table(rpl_slave_state::gtid_pos_table *entry)
{
mysql_mutex_assert_owner(&LOCK_slave_state);
- entry->next= (struct gtid_pos_table *)gtid_pos_tables;
- my_atomic_storeptr_explicit(&gtid_pos_tables, entry, MY_MEMORY_ORDER_RELEASE);
+ entry->next= gtid_pos_tables.load(std::memory_order_relaxed);
+ gtid_pos_tables.store(entry, std::memory_order_release);
}
diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h
index b3341b20165..b633716bfe5 100644
--- a/sql/rpl_gtid.h
+++ b/sql/rpl_gtid.h
@@ -118,8 +118,9 @@ struct rpl_slave_state
{
struct list_element *next;
uint64 sub_id;
- uint64 seq_no;
+ uint32 domain_id;
uint32 server_id;
+ uint64 seq_no;
/*
hton of mysql.gtid_slave_pos* table used to record this GTID.
Can be NULL if the gtid table failed to load (eg. missing
@@ -191,6 +192,8 @@ struct rpl_slave_state
/* Mapping from domain_id to its element. */
HASH hash;
+ /* GTIDs added since last purge of old mysql.gtid_slave_pos rows. */
+ uint32 pending_gtid_count;
/* Mutex protecting access to the state. */
mysql_mutex_t LOCK_slave_state;
/* Auxiliary buffer to sort gtid list. */
@@ -214,13 +217,10 @@ struct rpl_slave_state
The list can be read without lock by an SQL driver thread or worker thread
by reading the gtid_pos_tables pointer atomically with acquire semantics,
to ensure that it will see the correct next pointer of a new head element.
-
- The type is struct gtid_pos_table *, but needs to be void * to allow using
- my_atomic operations without violating C strict aliasing semantics.
*/
- void * volatile gtid_pos_tables;
+ std::atomic<gtid_pos_table*> gtid_pos_tables;
/* The default entry in gtid_pos_tables, mysql.gtid_slave_pos. */
- void * volatile default_gtid_pos_table;
+ std::atomic<gtid_pos_table*> default_gtid_pos_table;
bool loaded;
rpl_slave_state();
@@ -233,7 +233,10 @@ struct rpl_slave_state
int truncate_state_table(THD *thd);
void select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename);
int record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
- rpl_group_info *rgi, bool in_statement, void **out_hton);
+ bool in_transaction, bool in_statement, void **out_hton);
+ list_element *gtid_grab_pending_delete_list();
+ LEX_CSTRING *select_gtid_pos_table(void *hton);
+ void gtid_delete_pending(THD *thd, rpl_slave_state::list_element **list_ptr);
uint64 next_sub_id(uint32 domain_id);
int iterate(int (*cb)(rpl_gtid *, void *), void *data,
rpl_gtid *extra_gtids, uint32 num_extra,
@@ -245,7 +248,7 @@ struct rpl_slave_state
bool is_empty();
element *get_element(uint32 domain_id);
- int put_back_list(uint32 domain_id, list_element *list);
+ int put_back_list(list_element *list);
void update_state_hash(uint64 sub_id, rpl_gtid *gtid, void *hton,
rpl_group_info *rgi);
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 87bb995ba9a..43a02147496 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -1082,7 +1082,7 @@ bool Master_info_index::init_all_master_info()
if ((index_file_nr= my_open(index_file_name,
O_RDWR | O_CREAT | O_BINARY ,
- MYF(MY_WME | ME_NOREFRESH))) < 0 ||
+ MYF(MY_WME | ME_ERROR_LOG))) < 0 ||
my_sync(index_file_nr, MYF(MY_WME)) ||
init_io_cache(&index_file, index_file_nr,
IO_SIZE, READ_CACHE,
@@ -1298,7 +1298,7 @@ Master_info *get_master_info(const LEX_CSTRING *connection_name,
if (warning != Sql_condition::WARN_LEVEL_NOTE)
my_error(WARN_NO_MASTER_INFO,
MYF(warning == Sql_condition::WARN_LEVEL_WARN ?
- ME_JUST_WARNING : 0),
+ ME_WARNING : 0),
(int) connection_name->length, connection_name->str);
mysql_mutex_unlock(&LOCK_active_mi);
DBUG_RETURN(0);
@@ -1368,7 +1368,7 @@ Master_info_index::get_master_info(const LEX_CSTRING *connection_name,
if (!mi && warning != Sql_condition::WARN_LEVEL_NOTE)
{
my_error(WARN_NO_MASTER_INFO,
- MYF(warning == Sql_condition::WARN_LEVEL_WARN ? ME_JUST_WARNING :
+ MYF(warning == Sql_condition::WARN_LEVEL_WARN ? ME_WARNING :
0),
(int) connection_name->length,
connection_name->str);
@@ -1427,7 +1427,7 @@ bool Master_info_index::add_master_info(Master_info *mi, bool write_to_file)
We have to protect against shutdown to ensure we are not calling
my_hash_insert() while my_hash_free() is in progress
*/
- if (unlikely(shutdown_in_progress) ||
+ if (unlikely(abort_loop) ||
!my_hash_insert(&master_info_hash, (uchar*) mi))
{
if (global_system_variables.log_warnings > 1)
@@ -1570,7 +1570,7 @@ uint any_slave_sql_running(bool already_locked)
if (!already_locked)
mysql_mutex_lock(&LOCK_active_mi);
- if (unlikely(shutdown_in_progress || !master_info_index))
+ if (unlikely(abort_loop || !master_info_index))
count= 1;
else
{
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index 308d5af6f16..4d47689ac18 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -329,13 +329,13 @@ class Master_info : public Slave_reporting_capability
/* No of DDL event group */
- volatile uint64 total_ddl_groups;
+ Atomic_counter<uint64> total_ddl_groups;
/* No of non-transactional event group*/
- volatile uint64 total_non_trans_groups;
+ Atomic_counter<uint64> total_non_trans_groups;
/* No of transactional event group*/
- volatile uint64 total_trans_groups;
+ Atomic_counter<uint64> total_trans_groups;
/* domain-id based filter */
Domain_id_filter domain_id_filter;
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 7ebb609dc58..35901cb5263 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -5,6 +5,10 @@
#include "sql_parse.h"
#include "debug_sync.h"
#include "sql_repl.h"
+#include "wsrep_mysqld.h"
+#ifdef WITH_WSREP
+#include "wsrep_trans_observer.h"
+#endif
/*
Code for optional parallel execution of replicated events on the slave.
@@ -36,6 +40,13 @@ rpt_handle_event(rpl_parallel_thread::queued_event *qev,
DBUG_ASSERT(qev->typ == rpl_parallel_thread::queued_event::QUEUED_EVENT);
ev= qev->ev;
+#ifdef WITH_WSREP
+ if (wsrep_before_statement(thd))
+ {
+ WSREP_WARN("Parallel slave failed at wsrep_before_statement() hook");
+ return(1);
+ }
+#endif /* WITH_WSREP */
thd->system_thread_info.rpl_sql_info->rpl_filter = rli->mi->rpl_filter;
ev->thd= thd;
@@ -50,7 +61,14 @@ rpt_handle_event(rpl_parallel_thread::queued_event *qev,
rgi->last_master_timestamp= ev->when + (time_t)ev->exec_time;
err= apply_event_and_update_pos_for_parallel(ev, thd, rgi);
- thread_safe_increment64(&rli->executed_entries);
+ rli->executed_entries++;
+#ifdef WITH_WSREP
+ if (wsrep_after_statement(thd))
+ {
+ WSREP_WARN("Parallel slave failed at wsrep_after_statement() hook");
+ err= 1;
+ }
+#endif /* WITH_WSREP */
/* ToDo: error handling. */
return err;
}
@@ -454,6 +472,7 @@ pool_mark_busy(rpl_parallel_thread_pool *pool, THD *thd)
So we protect the infrequent operations of FLUSH TABLES WITH READ LOCK and
pool size changes with this condition wait.
*/
+ DBUG_EXECUTE_IF("mark_busy_mdev_22370",my_sleep(1000000););
mysql_mutex_lock(&pool->LOCK_rpl_thread_pool);
if (thd)
{
@@ -1049,13 +1068,13 @@ handle_rpl_parallel_thread(void *arg)
my_thread_init();
thd = new THD(next_thread_id());
thd->thread_stack = (char*)&thd;
- add_to_active_threads(thd);
+ server_threads.insert(thd);
set_current_thd(thd);
pthread_detach_this_thread();
+ thd->store_globals();
thd->init_for_queries();
thd->variables.binlog_annotate_row_events= 0;
init_thr_lock();
- thd->store_globals();
thd->system_thread= SYSTEM_THREAD_SLAVE_SQL;
thd->security_ctx->skip_grants();
thd->variables.max_allowed_packet= slave_max_allowed_packet;
@@ -1086,6 +1105,14 @@ handle_rpl_parallel_thread(void *arg)
mysql_cond_signal(&rpt->COND_rpl_thread);
thd->set_command(COM_SLAVE_WORKER);
+#ifdef WITH_WSREP
+ wsrep_open(thd);
+ if (wsrep_before_command(thd))
+ {
+ WSREP_WARN("Parallel slave failed at wsrep_before_command() hook");
+ rpt->stop = true;
+ }
+#endif /* WITH_WSREP */
while (!rpt->stop)
{
uint wait_count= 0;
@@ -1456,6 +1483,11 @@ handle_rpl_parallel_thread(void *arg)
rpt->pool->release_thread(rpt);
}
}
+#ifdef WITH_WSREP
+ wsrep_after_command_before_result(thd);
+ wsrep_after_command_after_result(thd);
+ wsrep_close(thd);
+#endif /* WITH_WSREP */
rpt->thd= NULL;
mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
@@ -1468,7 +1500,7 @@ handle_rpl_parallel_thread(void *arg)
thd->temporary_tables= 0;
THD_CHECK_SENTRY(thd);
- unlink_not_visible_thd(thd);
+ server_threads.erase(thd);
delete thd;
mysql_mutex_lock(&rpt->LOCK_rpl_thread);
@@ -1764,7 +1796,7 @@ rpl_parallel_thread::inuse_relaylog_refcount_update()
inuse_relaylog *ir= accumulated_ir_last;
if (ir)
{
- my_atomic_add64(&ir->dequeued_count, accumulated_ir_count);
+ ir->dequeued_count+= accumulated_ir_count;
accumulated_ir_count= 0;
accumulated_ir_last= NULL;
}
@@ -2000,9 +2032,23 @@ rpl_parallel_thread_pool::init(uint32 size)
void
rpl_parallel_thread_pool::destroy()
{
+ deactivate();
+ destroy_cond_mutex();
+}
+
+void
+rpl_parallel_thread_pool::deactivate()
+{
if (!inited)
return;
rpl_parallel_change_thread_count(this, 0, 1);
+}
+
+void
+rpl_parallel_thread_pool::destroy_cond_mutex()
+{
+ if (!inited)
+ return;
mysql_mutex_destroy(&LOCK_rpl_thread_pool);
mysql_cond_destroy(&COND_rpl_thread_pool);
inited= false;
diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h
index 4579d0da9bc..0fa28e32291 100644
--- a/sql/rpl_parallel.h
+++ b/sql/rpl_parallel.h
@@ -244,6 +244,8 @@ struct rpl_parallel_thread_pool {
rpl_parallel_thread_pool();
int init(uint32 size);
void destroy();
+ void deactivate();
+ void destroy_cond_mutex();
struct rpl_parallel_thread *get_thread(rpl_parallel_thread **owner,
rpl_parallel_entry *entry);
void release_thread(rpl_parallel_thread *rpt);
diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc
index b1fe34dc23d..7da296b47d9 100644
--- a/sql/rpl_record.cc
+++ b/sql/rpl_record.cc
@@ -329,21 +329,6 @@ unpack_row(rpl_group_info *rgi,
(int) (pack_ptr - old_pack_ptr)));
if (!pack_ptr)
{
- if (WSREP_ON)
- {
- /*
- Debug message to troubleshoot bug:
- https://mariadb.atlassian.net/browse/MDEV-4404
- Galera Node throws "Could not read field" error and drops out of cluster
- */
- WSREP_WARN("ROW event unpack field: %s metadata: 0x%x;"
- " conv_table %p conv_field %p table %s"
- " row_end: %p",
- f->field_name.str, metadata, conv_table, conv_field,
- (table_found) ? "found" : "not found", row_end
- );
- }
-
rgi->rli->report(ERROR_LEVEL, ER_SLAVE_CORRUPT_EVENT,
rgi->gtid_info(),
"Could not read field '%s' of table '%s.%s'",
@@ -496,7 +481,9 @@ int prepare_record(TABLE *const table, const uint skip, const bool check)
DBUG_RETURN(0);
}
/**
- Fills @c table->record[0] with computed values of extra persistent column which are present on slave but not on master.
+ Fills @c table->record[0] with computed values of extra persistent column
+ which are present on slave but not on master.
+
@param table Table whose record[0] buffer is prepared.
@param master_cols No of columns on master
@returns 0 on success
@@ -513,10 +500,8 @@ int fill_extra_persistent_columns(TABLE *table, int master_cols)
vfield= *vfield_ptr;
if (vfield->field_index >= master_cols && vfield->stored_in_db())
{
- /*Set bitmap for writing*/
- bitmap_set_bit(table->vcol_set, vfield->field_index);
+ bitmap_set_bit(table->write_set, vfield->field_index);
error= vfield->vcol_info->expr->save_in_field(vfield,0);
- bitmap_clear_bit(table->vcol_set, vfield->field_index);
}
}
return error;
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 58594f6f645..c8f77acf523 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -458,9 +458,8 @@ static inline int add_relay_log(Relay_log_info* rli,LOG_INFO* linfo)
linfo->log_file_name);
DBUG_RETURN(1);
}
- my_atomic_add64_explicit((volatile int64*)(&rli->log_space_total),
- s.st_size, MY_MEMORY_ORDER_RELAXED);
- DBUG_PRINT("info",("log_space_total: %llu", rli->log_space_total));
+ rli->log_space_total += s.st_size;
+ DBUG_PRINT("info",("log_space_total: %llu", uint64(rli->log_space_total)));
DBUG_RETURN(0);
}
@@ -469,8 +468,7 @@ static int count_relay_log_space(Relay_log_info* rli)
{
LOG_INFO linfo;
DBUG_ENTER("count_relay_log_space");
- my_atomic_store64_explicit((volatile int64*)(&rli->log_space_total), 0,
- MY_MEMORY_ORDER_RELAXED);
+ rli->log_space_total= 0;
if (rli->relay_log.find_log_pos(&linfo, NullS, 1))
{
sql_print_error("Could not find first log while counting relay log space");
@@ -1229,8 +1227,8 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
strmake_buf(rli->group_relay_log_name, rli->relay_log.get_log_fname());
strmake_buf(rli->event_relay_log_name, rli->relay_log.get_log_fname());
rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
- my_atomic_store64_explicit((volatile int64*)(&rli->log_space_total), 0,
- MY_MEMORY_ORDER_RELAXED);
+ rli->log_space_total= 0;
+
if (count_relay_log_space(rli))
{
*errmsg= "Error counting relay log space";
@@ -1254,7 +1252,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
mysql_mutex_unlock(rli->relay_log.get_log_lock());
}
err:
- DBUG_PRINT("info",("log_space_total: %llu",rli->log_space_total));
+ DBUG_PRINT("info",("log_space_total: %llu", uint64(rli->log_space_total)));
mysql_mutex_unlock(&rli->data_lock);
DBUG_RETURN(error);
}
@@ -1494,31 +1492,27 @@ Relay_log_info::alloc_inuse_relaylog(const char *name)
uint32 gtid_count;
rpl_gtid *gtid_list;
- if (!(ir= (inuse_relaylog *)my_malloc(sizeof(*ir), MYF(MY_WME|MY_ZEROFILL))))
- {
- my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*ir));
- return 1;
- }
gtid_count= relay_log_state.count();
if (!(gtid_list= (rpl_gtid *)my_malloc(sizeof(*gtid_list)*gtid_count,
MYF(MY_WME))))
{
- my_free(ir);
my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*gtid_list)*gtid_count);
return 1;
}
+ if (!(ir= new inuse_relaylog(this, gtid_list, gtid_count, name)))
+ {
+ my_free(gtid_list);
+ my_error(ER_OUTOFMEMORY, MYF(0), (int) sizeof(*ir));
+ return 1;
+ }
if (relay_log_state.get_gtid_list(gtid_list, gtid_count))
{
my_free(gtid_list);
- my_free(ir);
+ delete ir;
DBUG_ASSERT(0 /* Should not be possible as we allocated correct length */);
my_error(ER_OUT_OF_RESOURCES, MYF(0));
return 1;
}
- ir->rli= this;
- strmake_buf(ir->name, name);
- ir->relay_log_state= gtid_list;
- ir->relay_log_state_count= gtid_count;
if (!inuse_relaylog_list)
inuse_relaylog_list= ir;
@@ -1537,7 +1531,7 @@ void
Relay_log_info::free_inuse_relaylog(inuse_relaylog *ir)
{
my_free(ir->relay_log_state);
- my_free(ir);
+ delete ir;
}
@@ -1681,7 +1675,7 @@ end:
{
*out_hton= table->s->db_type();
close_thread_tables(thd);
- thd->mdl_context.release_transactional_locks();
+ thd->mdl_context.release_transactional_locks(thd);
}
return err;
}
@@ -1879,6 +1873,7 @@ rpl_load_gtid_slave_state(THD *thd)
int err= 0;
uint32 i;
load_gtid_state_cb_data cb_data;
+ rpl_slave_state::list_element *old_gtids_list;
DBUG_ENTER("rpl_load_gtid_slave_state");
mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
@@ -1964,6 +1959,13 @@ rpl_load_gtid_slave_state(THD *thd)
rpl_global_gtid_slave_state->loaded= true;
mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ /* Clear out no longer needed elements now. */
+ old_gtids_list=
+ rpl_global_gtid_slave_state->gtid_grab_pending_delete_list();
+ rpl_global_gtid_slave_state->gtid_delete_pending(thd, &old_gtids_list);
+ if (old_gtids_list)
+ rpl_global_gtid_slave_state->put_back_list(old_gtids_list);
+
end:
if (array_inited)
delete_dynamic(&array);
@@ -2070,10 +2072,9 @@ find_gtid_slave_pos_tables(THD *thd)
However we can add new entries, and warn about any tables that
disappeared, but may still be visible to running SQL threads.
*/
- rpl_slave_state::gtid_pos_table *old_entry, *new_entry, **next_ptr_ptr;
-
- old_entry= (rpl_slave_state::gtid_pos_table *)
- rpl_global_gtid_slave_state->gtid_pos_tables;
+ rpl_slave_state::gtid_pos_table *new_entry, **next_ptr_ptr;
+ auto old_entry= rpl_global_gtid_slave_state->
+ gtid_pos_tables.load(std::memory_order_relaxed);
while (old_entry)
{
new_entry= cb_data.table_list;
@@ -2095,8 +2096,8 @@ find_gtid_slave_pos_tables(THD *thd)
while (new_entry)
{
/* Check if we already have a table with this storage engine. */
- old_entry= (rpl_slave_state::gtid_pos_table *)
- rpl_global_gtid_slave_state->gtid_pos_tables;
+ old_entry= rpl_global_gtid_slave_state->
+ gtid_pos_tables.load(std::memory_order_relaxed);
while (old_entry)
{
if (new_entry->table_hton == old_entry->table_hton)
@@ -2145,7 +2146,6 @@ rpl_group_info::reinit(Relay_log_info *rli)
long_find_row_note_printed= false;
did_mark_start_commit= false;
gtid_ev_flags2= 0;
- pending_gtid_delete_list= NULL;
last_master_timestamp = 0;
gtid_ignore_duplicate_state= GTID_DUPLICATE_NULL;
speculation= SPECULATE_NO;
@@ -2276,12 +2276,6 @@ void rpl_group_info::cleanup_context(THD *thd, bool error)
erroneously update the GTID position.
*/
gtid_pending= false;
-
- /*
- Rollback will have undone any deletions of old rows we might have made
- in mysql.gtid_slave_pos. Put those rows back on the list to be deleted.
- */
- pending_gtid_deletes_put_back();
}
m_table_map.clear_tables();
slave_close_thread_tables(thd);
@@ -2507,78 +2501,6 @@ rpl_group_info::unmark_start_commit()
}
-/*
- When record_gtid() has deleted any old rows from the table
- mysql.gtid_slave_pos as part of a replicated transaction, save the list of
- rows deleted here.
-
- If later the transaction fails (eg. optimistic parallel replication), the
- deletes will be undone when the transaction is rolled back. Then we can
- put back the list of rows into the rpl_global_gtid_slave_state, so that
- we can re-do the deletes and avoid accumulating old rows in the table.
-*/
-void
-rpl_group_info::pending_gtid_deletes_save(uint32 domain_id,
- rpl_slave_state::list_element *list)
-{
- /*
- We should never get to a state where we try to save a new pending list of
- gtid deletes while we still have an old one. But make sure we handle it
- anyway just in case, so we avoid leaving stray entries in the
- mysql.gtid_slave_pos table.
- */
- DBUG_ASSERT(!pending_gtid_delete_list);
- if (unlikely(pending_gtid_delete_list))
- pending_gtid_deletes_put_back();
-
- pending_gtid_delete_list= list;
- pending_gtid_delete_list_domain= domain_id;
-}
-
-
-/*
- Take the list recorded by pending_gtid_deletes_save() and put it back into
- rpl_global_gtid_slave_state. This is needed if deletion of the rows was
- rolled back due to transaction failure.
-*/
-void
-rpl_group_info::pending_gtid_deletes_put_back()
-{
- if (pending_gtid_delete_list)
- {
- rpl_global_gtid_slave_state->put_back_list(pending_gtid_delete_list_domain,
- pending_gtid_delete_list);
- pending_gtid_delete_list= NULL;
- }
-}
-
-
-/*
- Free the list recorded by pending_gtid_deletes_save(). Done when the deletes
- in the list have been permanently committed.
-*/
-void
-rpl_group_info::pending_gtid_deletes_clear()
-{
- pending_gtid_deletes_free(pending_gtid_delete_list);
- pending_gtid_delete_list= NULL;
-}
-
-
-void
-rpl_group_info::pending_gtid_deletes_free(rpl_slave_state::list_element *list)
-{
- rpl_slave_state::list_element *next;
-
- while (list)
- {
- next= list->next;
- my_free(list);
- list= next;
- }
-}
-
-
rpl_sql_thread_info::rpl_sql_thread_info(Rpl_filter *filter)
: rpl_filter(filter)
{
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index cfe581ebb7a..1391c5cde82 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -240,7 +240,8 @@ public:
threads, the SQL thread sets it to unblock the I/O thread and make it
temporarily forget about the constraint.
*/
- ulonglong log_space_limit,log_space_total;
+ ulonglong log_space_limit;
+ Atomic_counter<uint64> log_space_total;
bool ignore_log_space_limit;
/*
@@ -347,7 +348,7 @@ public:
Number of executed events for SLAVE STATUS.
Protected by slave_executed_entries_lock
*/
- int64 executed_entries;
+ Atomic_counter<uint32_t> executed_entries;
/*
If the end of the hot relay log is made of master's events ignored by the
@@ -623,10 +624,20 @@ struct inuse_relaylog {
/* Number of events in this relay log queued for worker threads. */
int64 queued_count;
/* Number of events completed by worker threads. */
- volatile int64 dequeued_count;
+ Atomic_counter<int64> dequeued_count;
/* Set when all events have been read from a relaylog. */
bool completed;
char name[FN_REFLEN];
+
+ inuse_relaylog(Relay_log_info *rli_arg, rpl_gtid *relay_log_state_arg,
+ uint32 relay_log_state_count_arg,
+ const char *name_arg):
+ next(0), rli(rli_arg), relay_log_state(relay_log_state_arg),
+ relay_log_state_count(relay_log_state_count_arg), queued_count(0),
+ dequeued_count(0), completed(false)
+ {
+ strmake_buf(name, name_arg);
+ }
};
@@ -772,11 +783,6 @@ struct rpl_group_info
/* Needs room for "Gtid D-S-N\x00". */
char gtid_info_buf[5+10+1+10+1+20+1];
- /* List of not yet committed deletions in mysql.gtid_slave_pos. */
- rpl_slave_state::list_element *pending_gtid_delete_list;
- /* Domain associated with pending_gtid_delete_list. */
- uint32 pending_gtid_delete_list_domain;
-
/*
The timestamp, from the master, of the commit event.
Used to do delayed update of rli->last_master_timestamp, for getting
@@ -918,12 +924,6 @@ struct rpl_group_info
char *gtid_info();
void unmark_start_commit();
- static void pending_gtid_deletes_free(rpl_slave_state::list_element *list);
- void pending_gtid_deletes_save(uint32 domain_id,
- rpl_slave_state::list_element *list);
- void pending_gtid_deletes_put_back();
- void pending_gtid_deletes_clear();
-
longlong get_row_stmt_start_timestamp()
{
return row_stmt_start_timestamp;
diff --git a/sql/select_handler.cc b/sql/select_handler.cc
new file mode 100644
index 00000000000..495b6d957b1
--- /dev/null
+++ b/sql/select_handler.cc
@@ -0,0 +1,173 @@
+/*
+ Copyright (c) 2018, 2019 MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
+#include "mariadb.h"
+#include "sql_priv.h"
+#include "sql_select.h"
+#include "select_handler.h"
+
+
+/**
+ The methods of the Pushdown_select class.
+
+ The objects of this class are used for pushdown of the select queries
+ into engines. The main method of the class is Pushdown_select::execute()
+ that initiates execution of a select query by a foreign engine, receives the
+ rows of the result set, put it in a buffer of a temporary table and send
+ them from the buffer directly into output.
+
+ The method uses the functions of the select_handle interface to do this.
+ It also employes plus some helper functions to create the needed temporary
+ table and to send rows from the temporary table into output.
+ The constructor of the class gets the select_handler interface as a parameter.
+*/
+
+
+Pushdown_select::Pushdown_select(SELECT_LEX *sel, select_handler *h)
+ : select(sel), handler(h)
+{
+ is_analyze= handler->thd->lex->analyze_stmt;
+}
+
+
+Pushdown_select::~Pushdown_select()
+{
+ if (handler->table)
+ free_tmp_table(handler->thd, handler->table);
+ delete handler;
+ select->select_h= NULL;
+}
+
+
+bool Pushdown_select::init()
+{
+ List<Item> types;
+ TMP_TABLE_PARAM tmp_table_param;
+ THD *thd= handler->thd;
+ DBUG_ENTER("Pushdown_select::init");
+ if (select->master_unit()->join_union_item_types(thd, types, 1))
+ DBUG_RETURN(true);
+ tmp_table_param.init();
+ tmp_table_param.field_count= types.elements;
+
+ handler->table= create_tmp_table(thd, &tmp_table_param, types,
+ (ORDER *) 0, false, 0,
+ TMP_TABLE_ALL_COLUMNS, 1,
+ &empty_clex_str, true, false);
+ if (!handler->table)
+ DBUG_RETURN(true);
+ if (handler->table->fill_item_list(&result_columns))
+ DBUG_RETURN(true);
+ DBUG_RETURN(false);
+}
+
+
+bool Pushdown_select::send_result_set_metadata()
+{
+ DBUG_ENTER("Pushdown_select::send_result_set_metadata");
+
+#ifdef WITH_WSREP
+ THD *thd= handler->thd;
+ if (WSREP(thd) && thd->wsrep_retry_query)
+ {
+ WSREP_DEBUG("skipping select metadata");
+ DBUG_RETURN(false);
+ }
+ #endif /* WITH_WSREP */
+ if (select->join->result->send_result_set_metadata(result_columns,
+ Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF))
+ DBUG_RETURN(true);
+
+ DBUG_RETURN(false);
+}
+
+
+bool Pushdown_select::send_data()
+{
+ THD *thd= handler->thd;
+ DBUG_ENTER("Pushdown_select::send_data");
+
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(false);
+
+ if (select->join->result->send_data(result_columns))
+ DBUG_RETURN(true);
+
+ DBUG_RETURN(false);
+}
+
+
+bool Pushdown_select::send_eof()
+{
+ DBUG_ENTER("Pushdown_select::send_eof");
+
+ if (select->join->result->send_eof())
+ DBUG_RETURN(true);
+ DBUG_RETURN(false);
+}
+
+
+int Pushdown_select::execute()
+{
+ int err;
+ THD *thd= handler->thd;
+
+ DBUG_ENTER("Pushdown_select::execute");
+
+ if ((err= handler->init_scan()))
+ goto error;
+
+ if (is_analyze)
+ {
+ handler->end_scan();
+ DBUG_RETURN(0);
+ }
+
+ if (send_result_set_metadata())
+ DBUG_RETURN(-1);
+
+ while (!(err= handler->next_row()))
+ {
+ if (thd->check_killed() || send_data())
+ {
+ handler->end_scan();
+ DBUG_RETURN(-1);
+ }
+ }
+
+ if (err != 0 && err != HA_ERR_END_OF_FILE)
+ goto error;
+
+ if ((err= handler->end_scan()))
+ goto error_2;
+
+ if (send_eof())
+ DBUG_RETURN(-1);
+
+ DBUG_RETURN(0);
+
+error:
+ handler->end_scan();
+error_2:
+ handler->print_error(err, MYF(0));
+ DBUG_RETURN(-1); // Error not sent to client
+}
+
+void select_handler::print_error(int error, myf errflag)
+{
+ my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str);
+}
diff --git a/sql/select_handler.h b/sql/select_handler.h
new file mode 100644
index 00000000000..e2ad13b7cdf
--- /dev/null
+++ b/sql/select_handler.h
@@ -0,0 +1,72 @@
+/*
+ Copyright (c) 2018, 2019 MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SELECT_HANDLER_INCLUDED
+#define SELECT_HANDLER_INCLUDED
+
+#include "mariadb.h"
+#include "sql_priv.h"
+
+/**
+ @class select_handler
+
+ This interface class is to be used for execution of select queries
+ by foreign engines
+*/
+
+class select_handler
+{
+ public:
+ THD *thd;
+ handlerton *ht;
+
+ SELECT_LEX *select; // Select to be excuted
+
+ /*
+ Temporary table where all results should be stored in record[0]
+ The table has a field for every item from the select_lex::item_list.
+ The table is actually never filled. Only its record buffer is used.
+ */
+ TABLE *table;
+
+ select_handler(THD *thd_arg, handlerton *ht_arg)
+ : thd(thd_arg), ht(ht_arg), table(0) {}
+
+ virtual ~select_handler() {}
+
+ /*
+ Functions to scan the select result set.
+ All these returns 0 if ok, error code in case of error.
+ */
+
+ /* Initialize the process of producing rows of result set */
+ virtual int init_scan() = 0;
+
+ /*
+ Put the next produced row of the result set in table->record[0]
+ and return 0. Return HA_ERR_END_OF_FILE if there are no more rows,
+ return other error number in case of fatal error.
+ */
+ virtual int next_row() = 0;
+
+ /* Finish scanning */
+ virtual int end_scan() = 0;
+
+ /* Report errors */
+ virtual void print_error(int error, myf errflag);
+};
+
+#endif /* SELECT_HANDLER_INCLUDED */
diff --git a/sql/semisync_master_ack_receiver.cc b/sql/semisync_master_ack_receiver.cc
index cc3a56a56b2..b24d6452480 100644
--- a/sql/semisync_master_ack_receiver.cc
+++ b/sql/semisync_master_ack_receiver.cc
@@ -204,7 +204,6 @@ void Ack_receiver::run()
thd->thread_stack= (char*) &thd;
thd->store_globals();
thd->security_ctx->skip_grants();
- thread_safe_increment32(&service_thread_count);
thd->set_command(COM_DAEMON);
init_net(&net, net_buff, REPLY_MESSAGE_MAX_LENGTH);
@@ -288,8 +287,6 @@ end:
sql_print_information("Stopping ack receiver thread");
m_status= ST_DOWN;
delete thd;
- thread_safe_decrement32(&service_thread_count);
- signal_thd_deleted();
mysql_cond_broadcast(&m_cond);
mysql_mutex_unlock(&m_mutex);
DBUG_VOID_RETURN;
diff --git a/sql/service_wsrep.cc b/sql/service_wsrep.cc
new file mode 100644
index 00000000000..14f136ca480
--- /dev/null
+++ b/sql/service_wsrep.cc
@@ -0,0 +1,368 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include "mariadb.h"
+
+#include "mysql/service_wsrep.h"
+#include "wsrep/key.hpp"
+#include "wsrep_thd.h"
+#include "wsrep_trans_observer.h"
+#include "sql_class.h"
+#include "debug_sync.h"
+#include "log.h"
+
+extern "C" my_bool wsrep_on(const THD *thd)
+{
+ return my_bool(WSREP(thd));
+}
+
+extern "C" void wsrep_thd_LOCK(const THD *thd)
+{
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+}
+
+extern "C" void wsrep_thd_UNLOCK(const THD *thd)
+{
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+}
+
+extern "C" void wsrep_thd_kill_LOCK(const THD *thd)
+{
+ mysql_mutex_lock(&thd->LOCK_thd_kill);
+}
+
+extern "C" void wsrep_thd_kill_UNLOCK(const THD *thd)
+{
+ mysql_mutex_unlock(&thd->LOCK_thd_kill);
+}
+
+extern "C" const char* wsrep_thd_client_state_str(const THD *thd)
+{
+ return wsrep::to_c_string(thd->wsrep_cs().state());
+}
+
+extern "C" const char* wsrep_thd_client_mode_str(const THD *thd)
+{
+ return wsrep::to_c_string(thd->wsrep_cs().mode());
+}
+
+extern "C" const char* wsrep_thd_transaction_state_str(const THD *thd)
+{
+ return wsrep::to_c_string(thd->wsrep_cs().transaction().state());
+}
+
+
+extern "C" const char *wsrep_thd_query(const THD *thd)
+{
+ if (thd)
+ {
+ switch(thd->lex->sql_command)
+ {
+ case SQLCOM_CREATE_USER:
+ return "CREATE USER";
+ case SQLCOM_GRANT:
+ return "GRANT";
+ case SQLCOM_REVOKE:
+ return "REVOKE";
+ case SQLCOM_SET_OPTION:
+ if (thd->lex->definer)
+ return "SET PASSWORD";
+ /* fallthrough */
+ default:
+ if (thd->query())
+ return thd->query();
+ }
+ }
+ return "NULL";
+}
+
+extern "C" query_id_t wsrep_thd_transaction_id(const THD *thd)
+{
+ return thd->wsrep_cs().transaction().id().get();
+}
+
+extern "C" long long wsrep_thd_trx_seqno(const THD *thd)
+{
+ const wsrep::client_state& cs= thd->wsrep_cs();
+ if (cs.mode() == wsrep::client_state::m_toi)
+ {
+ return cs.toi_meta().seqno().get();
+ }
+ else
+ {
+ return cs.transaction().ws_meta().seqno().get();
+ }
+}
+
+extern "C" void wsrep_thd_self_abort(THD *thd)
+{
+ thd->wsrep_cs().bf_abort(wsrep::seqno(0));
+}
+
+extern "C" const char* wsrep_get_sr_table_name()
+{
+ return wsrep_sr_table_name_full;
+}
+
+extern "C" my_bool wsrep_get_debug()
+{
+ return wsrep_debug;
+}
+
+/*
+ Test if this connection is a true local (user) connection and not
+ a replication or wsrep applier thread.
+
+ Note that this is only usable for galera (as there are other kinds
+ of system threads, and only if WSREP_NNULL() is tested by the caller.
+ */
+extern "C" my_bool wsrep_thd_is_local(const THD *thd)
+{
+ /*
+ async replication IO and background threads have nothing to
+ replicate in the cluster, marking them as non-local here to
+ prevent write set population and replication
+
+ async replication SQL thread, applies client transactions from
+ mariadb master and will be replicated into cluster
+ */
+ return (
+ thd->system_thread != SYSTEM_THREAD_SLAVE_BACKGROUND &&
+ thd->system_thread != SYSTEM_THREAD_SLAVE_IO &&
+ thd->wsrep_cs().mode() == wsrep::client_state::m_local);
+}
+
+extern "C" my_bool wsrep_thd_is_applying(const THD *thd)
+{
+ return thd->wsrep_cs().mode() == wsrep::client_state::m_high_priority;
+}
+
+extern "C" my_bool wsrep_thd_is_toi(const THD *thd)
+{
+ return thd->wsrep_cs().mode() == wsrep::client_state::m_toi;
+}
+
+extern "C" my_bool wsrep_thd_is_local_toi(const THD *thd)
+{
+ return thd->wsrep_cs().mode() == wsrep::client_state::m_toi &&
+ thd->wsrep_cs().toi_mode() == wsrep::client_state::m_local;
+
+}
+
+extern "C" my_bool wsrep_thd_is_in_rsu(const THD *thd)
+{
+ return thd->wsrep_cs().mode() == wsrep::client_state::m_rsu;
+}
+
+extern "C" my_bool wsrep_thd_is_BF(const THD *thd, my_bool sync)
+{
+ my_bool status = FALSE;
+ if (thd && WSREP(thd))
+ {
+ if (sync) mysql_mutex_lock(&thd->LOCK_thd_data);
+ status = (wsrep_thd_is_applying(thd) || wsrep_thd_is_toi(thd));
+ if (sync) mysql_mutex_unlock(&thd->LOCK_thd_data);
+ }
+ return status;
+}
+
+extern "C" my_bool wsrep_thd_is_SR(const THD *thd)
+{
+ return thd && thd->wsrep_cs().transaction().is_streaming();
+}
+
+extern "C" void wsrep_handle_SR_rollback(THD *bf_thd,
+ THD *victim_thd)
+{
+ DBUG_ASSERT(victim_thd);
+ DBUG_ASSERT(wsrep_thd_is_SR(victim_thd));
+ if (!victim_thd || !wsrep_on(bf_thd)) return;
+
+ WSREP_DEBUG("handle rollback, for deadlock: thd %llu trx_id %" PRIu64 " frags %zu conf %s",
+ victim_thd->thread_id,
+ victim_thd->wsrep_trx_id(),
+ victim_thd->wsrep_sr().fragments_certified(),
+ wsrep_thd_transaction_state_str(victim_thd));
+
+ /* Note: do not store/reset globals before wsrep_bf_abort() call
+ to avoid losing BF thd context. */
+ if (!(bf_thd && bf_thd != victim_thd))
+ {
+ DEBUG_SYNC(victim_thd, "wsrep_before_SR_rollback");
+ }
+ if (bf_thd)
+ {
+ wsrep_bf_abort(bf_thd, victim_thd);
+ }
+ else
+ {
+ wsrep_thd_self_abort(victim_thd);
+ }
+ if (bf_thd)
+ {
+ wsrep_store_threadvars(bf_thd);
+ }
+}
+
+extern "C" my_bool wsrep_thd_bf_abort(THD *bf_thd, THD *victim_thd,
+ my_bool signal)
+{
+ mysql_mutex_assert_owner(&victim_thd->LOCK_thd_kill);
+ mysql_mutex_assert_not_owner(&victim_thd->LOCK_thd_data);
+ my_bool ret= wsrep_bf_abort(bf_thd, victim_thd);
+ /*
+ Send awake signal if victim was BF aborted or does not
+ have wsrep on. Note that this should never interrupt RSU
+ as RSU has paused the provider.
+ */
+ if ((ret || !wsrep_on(victim_thd)) && signal)
+ {
+ mysql_mutex_lock(&victim_thd->LOCK_thd_data);
+
+ if (victim_thd->wsrep_aborter && victim_thd->wsrep_aborter != bf_thd->thread_id)
+ {
+ WSREP_DEBUG("victim is killed already by %llu, skipping awake",
+ victim_thd->wsrep_aborter);
+ mysql_mutex_unlock(&victim_thd->LOCK_thd_data);
+ return false;
+ }
+
+ victim_thd->wsrep_aborter= bf_thd->thread_id;
+ victim_thd->awake_no_mutex(KILL_QUERY);
+ mysql_mutex_unlock(&victim_thd->LOCK_thd_data);
+ } else {
+ WSREP_DEBUG("wsrep_thd_bf_abort skipped awake");
+ }
+ return ret;
+}
+
+extern "C" my_bool wsrep_thd_skip_locking(const THD *thd)
+{
+ return thd && thd->wsrep_skip_locking;
+}
+
+extern "C" my_bool wsrep_thd_order_before(const THD *left, const THD *right)
+{
+ if (wsrep_thd_trx_seqno(left) < wsrep_thd_trx_seqno(right)) {
+ WSREP_DEBUG("BF conflict, order: %lld %lld\n",
+ (long long)wsrep_thd_trx_seqno(left),
+ (long long)wsrep_thd_trx_seqno(right));
+ return TRUE;
+ }
+ WSREP_DEBUG("waiting for BF, trx order: %lld %lld\n",
+ (long long)wsrep_thd_trx_seqno(left),
+ (long long)wsrep_thd_trx_seqno(right));
+ return FALSE;
+}
+
+extern "C" my_bool wsrep_thd_is_aborting(const MYSQL_THD thd)
+{
+ mysql_mutex_assert_owner(&thd->LOCK_thd_data);
+ if (thd != 0)
+ {
+ const wsrep::client_state& cs(thd->wsrep_cs());
+ const enum wsrep::transaction::state tx_state(cs.transaction().state());
+ switch (tx_state)
+ {
+ case wsrep::transaction::s_must_abort:
+ return (cs.state() == wsrep::client_state::s_exec ||
+ cs.state() == wsrep::client_state::s_result);
+ case wsrep::transaction::s_aborting:
+ case wsrep::transaction::s_aborted:
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+static inline enum wsrep::key::type
+map_key_type(enum Wsrep_service_key_type type)
+{
+ switch (type)
+ {
+ case WSREP_SERVICE_KEY_SHARED: return wsrep::key::shared;
+ case WSREP_SERVICE_KEY_REFERENCE: return wsrep::key::reference;
+ case WSREP_SERVICE_KEY_UPDATE: return wsrep::key::update;
+ case WSREP_SERVICE_KEY_EXCLUSIVE: return wsrep::key::exclusive;
+ }
+ return wsrep::key::exclusive;
+}
+
+extern "C" int wsrep_thd_append_key(THD *thd,
+ const struct wsrep_key* key,
+ int n_keys,
+ enum Wsrep_service_key_type key_type)
+{
+ Wsrep_client_state& client_state(thd->wsrep_cs());
+ DBUG_ASSERT(client_state.transaction().active());
+ int ret= 0;
+ for (int i= 0; i < n_keys && ret == 0; ++i)
+ {
+ wsrep::key wsrep_key(map_key_type(key_type));
+ for (size_t kp= 0; kp < key[i].key_parts_num; ++kp)
+ {
+ wsrep_key.append_key_part(key[i].key_parts[kp].ptr, key[i].key_parts[kp].len);
+ }
+ ret= client_state.append_key(wsrep_key);
+ }
+ return ret;
+}
+
+extern "C" void wsrep_commit_ordered(THD *thd)
+{
+ if (wsrep_is_active(thd) &&
+ thd->wsrep_trx().state() == wsrep::transaction::s_committing &&
+ !wsrep_commit_will_write_binlog(thd))
+ {
+ DEBUG_SYNC(thd, "before_wsrep_ordered_commit");
+ thd->wsrep_cs().ordered_commit();
+ }
+}
+
+extern "C" bool wsrep_thd_set_wsrep_aborter(THD *bf_thd, THD *victim_thd)
+{
+ WSREP_DEBUG("wsrep_thd_set_wsrep_aborter called");
+ mysql_mutex_assert_owner(&victim_thd->LOCK_thd_data);
+ if (victim_thd->wsrep_aborter && victim_thd->wsrep_aborter != bf_thd->thread_id)
+ {
+ return true;
+ }
+ victim_thd->wsrep_aborter = bf_thd->thread_id;
+ return false;
+}
+
+extern "C" void wsrep_report_bf_lock_wait(const THD *thd,
+ unsigned long long trx_id)
+{
+ if (thd)
+ {
+ WSREP_ERROR("Thread %s trx_id: %llu thread: %ld "
+ "seqno: %lld client_state: %s client_mode: %s transaction_mode: %s "
+ "applier: %d toi: %d local: %d "
+ "query: %s",
+ wsrep_thd_is_BF(thd, false) ? "BF" : "normal",
+ trx_id,
+ thd_get_thread_id(thd),
+ wsrep_thd_trx_seqno(thd),
+ wsrep_thd_client_state_str(thd),
+ wsrep_thd_client_mode_str(thd),
+ wsrep_thd_transaction_state_str(thd),
+ wsrep_thd_is_applying(thd),
+ wsrep_thd_is_toi(thd),
+ wsrep_thd_is_local(thd),
+ wsrep_thd_query(thd));
+ }
+}
diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc
index 3ddece6039b..2f6c5e29bf2 100644
--- a/sql/session_tracker.cc
+++ b/sql/session_tracker.cc
@@ -389,10 +389,9 @@ bool Session_sysvars_tracker::update(THD *thd, set_var *var)
size_t length= 1;
void *copy= var->save_result.string_value.str ?
my_memdup(var->save_result.string_value.str,
- length= var->save_result.string_value.length + 1,
+ (length= var->save_result.string_value.length + 1),
MYF(MY_WME | MY_THREAD_SPECIFIC)) :
my_strdup("", MYF(MY_WME | MY_THREAD_SPECIFIC));
-
if (!copy)
return true;
@@ -745,7 +744,7 @@ bool Transaction_state_tracker::store(THD *thd, String *buf)
if ((thd->variables.session_track_transaction_info == TX_TRACK_CHISTICS) &&
(tx_changed & TX_CHG_CHISTICS))
{
- bool is_xa= (thd->transaction.xid_state.xa_state != XA_NOTR);
+ bool is_xa= thd->transaction.xid_state.is_explicit_XA();
size_t start;
/* 2 length by 1 byte and code */
@@ -922,7 +921,7 @@ bool Transaction_state_tracker::store(THD *thd, String *buf)
if ((tx_curr_state & TX_EXPLICIT) && is_xa)
{
- XID *xid= &thd->transaction.xid_state.xid;
+ XID *xid= thd->transaction.xid_state.get_xid();
long glen, blen;
buf->append(STRING_WITH_LEN("XA START"));
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 58b6b392449..bebed453292 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -742,7 +742,7 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free)
err:
if (free)
- free_underlaid_joins(thd, &thd->lex->select_lex);
+ free_underlaid_joins(thd, thd->lex->first_select_lex());
DBUG_RETURN(error);
}
diff --git a/sql/set_var.h b/sql/set_var.h
index 12e025e4696..ca862b87fd4 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -275,6 +275,10 @@ public:
virtual int update(THD *thd)=0; /* To set the value */
virtual int light_check(THD *thd) { return check(thd); } /* for PS */
virtual bool is_system() { return FALSE; }
+ /**
+ @returns whether this variable is @@@@optimizer_trace.
+ */
+ virtual bool is_var_optimizer_trace() const { return false; }
};
@@ -306,6 +310,11 @@ public:
int check(THD *thd);
int update(THD *thd);
int light_check(THD *thd);
+ virtual bool is_var_optimizer_trace() const
+ {
+ extern sys_var *Sys_optimizer_trace_ptr;
+ return var == Sys_optimizer_trace_ptr;
+ }
};
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 2313b32dad1..1c6a1326538 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -5725,8 +5725,9 @@ ER_SP_RECURSION_LIMIT
eng "Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %.192s"
ger "Rekursionsgrenze %d (durch Variable max_sp_recursion_depth gegeben) wurde für Routine %.192s überschritten"
ER_SP_PROC_TABLE_CORRUPT
- eng "Failed to load routine %-.192s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)"
- ger "Routine %-.192s konnte nicht geladen werden. Die Tabelle mysql.proc fehlt, ist beschädigt, oder enthält fehlerhaften Daten (interner Code: %d)"
+ eng "Failed to load routine %-.192s (internal code %d). For more details, run SHOW WARNINGS"
+ ger "Routine %-.192s (interner Code %d) konnte nicht geladen werden. Weitere Einzelheiten erhalten Sie, wenn Sie SHOW WARNINGS ausführen"
+ ukr "Невдала спроба завантажити процедуру %-.192s (внутрішний код %d). Для отримання детальної інформації використовуйте SHOW WARNINGS"
ER_SP_WRONG_NAME 42000
eng "Incorrect routine name '%-.192s'"
ger "Ungültiger Routinenname '%-.192s'"
@@ -5826,10 +5827,10 @@ ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR
eng "Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed"
ger "Konstante oder Random-Ausdrücke in (Unter-)Partitionsfunktionen sind nicht erlaubt"
swe "Konstanta uttryck eller slumpmässiga uttryck är inte tillåtna (sub)partitioneringsfunktioner"
-ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR
- eng "Expression in RANGE/LIST VALUES must be constant"
- ger "Ausdrücke in RANGE/LIST VALUES müssen konstant sein"
- swe "Uttryck i RANGE/LIST VALUES måste vara ett konstant uttryck"
+ER_NOT_CONSTANT_EXPRESSION
+ eng "Expression in %s must be constant"
+ ger "Ausdrücke in %s müssen konstant sein"
+ swe "Uttryck i %s måste vara ett konstant uttryck"
ER_FIELD_NOT_FOUND_PART_ERROR
eng "Field in list of fields for partition function not found in table"
ger "Felder in der Feldliste der Partitionierungsfunktion wurden in der Tabelle nicht gefunden"
@@ -6558,7 +6559,7 @@ ER_ACCESS_DENIED_NO_PASSWORD_ERROR 28000
ukr "Доступ заборонено для користувача: '%s'@'%s'"
ER_SET_PASSWORD_AUTH_PLUGIN
- eng "SET PASSWORD has no significance for users authenticating via plugins"
+ eng "SET PASSWORD is ignored for users authenticating via %s plugin"
ER_GRANT_PLUGIN_USER_EXISTS
eng "GRANT with IDENTIFIED WITH is illegal because the user %-.*s already exists"
@@ -6930,6 +6931,7 @@ ER_NOT_VALID_PASSWORD
ER_MUST_CHANGE_PASSWORD
eng "You must SET PASSWORD before executing this statement"
bgn "Трябва първо да си смените паролата със SET PASSWORD за да можете да изпълните тази команда"
+ rum "Trebuie sa iti schimbi parola folosind SET PASSWORD inainte de a executa aceasta comanda"
ER_FK_NO_INDEX_CHILD
eng "Failed to add the foreign key constaint. Missing index for constraint '%s' in the foreign table '%s'"
@@ -7082,6 +7084,7 @@ ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL
ER_MUST_CHANGE_PASSWORD_LOGIN
eng "Your password has expired. To log in you must change it using a client that supports expired passwords"
bgn "Паролата ви е изтекла. За да влезете трябва да я смените използвайки клиент който поддрържа такива пароли"
+ rum "Parola ta a expirat. Pentru a te loga, trebuie sa o schimbi folosind un client ce suporta parole expirate"
ER_ROW_IN_WRONG_PARTITION
eng "Found a row in wrong partition %s"
@@ -7799,7 +7802,7 @@ ER_ARGUMENT_OUT_OF_RANGE
ER_WRONG_TYPE_OF_ARGUMENT
eng "%s function only accepts arguments that can be converted to numerical types"
ER_NOT_AGGREGATE_FUNCTION
- eng "Non-aggregate function contains aggregate specific instructions: (FETCH GROUP NEXT ROW)"
+ eng "Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context"
ER_INVALID_AGGREGATE_FUNCTION
eng "Aggregate specific instruction(FETCH GROUP NEXT ROW) missing from the aggregate function"
ER_INVALID_VALUE_TO_LIMIT
@@ -7880,7 +7883,7 @@ ER_DROP_VERSIONING_SYSTEM_TIME_PARTITION
eng "Can not DROP SYSTEM VERSIONING for table %`s partitioned BY SYSTEM_TIME"
ER_VERS_DB_NOT_SUPPORTED
- eng "System versioning tables in the %`s database are not supported"
+ eng "System-versioned tables in the %`s database are not supported"
ER_VERS_TRT_IS_DISABLED
eng "Transaction registry is disabled"
@@ -7894,11 +7897,11 @@ ER_VERS_ALREADY_VERSIONED
ER_UNUSED_24
eng "You should never see it"
-ER_VERS_TEMPORARY
- eng "TEMPORARY tables do not support system versioning"
+ER_VERS_NOT_SUPPORTED
+ eng "System-versioned tables do not support %s"
ER_VERS_TRX_PART_HISTORIC_ROW_NOT_SUPPORTED
- eng "Transaction-precise system versioned tables do not support partitioning by ROW START or ROW END"
+ eng "Transaction-precise system-versioned tables do not support partitioning by ROW START or ROW END"
ER_INDEX_FILE_FULL
eng "The index file for table '%-.192s' is full"
ER_UPDATED_COLUMN_ONLY_ONCE
@@ -7911,3 +7914,34 @@ ER_KEY_DOESNT_SUPPORT
eng "%s index %`s does not support this operation"
ER_ALTER_OPERATION_TABLE_OPTIONS_NEED_REBUILD
eng "Changing table options requires the table to be rebuilt"
+ER_BACKUP_LOCK_IS_ACTIVE
+ eng "Can't execute the command as you have a BACKUP STAGE active"
+ER_BACKUP_NOT_RUNNING
+ eng "You must start backup with \"BACKUP STAGE START\""
+ER_BACKUP_WRONG_STAGE
+ eng "Backup stage '%s' is same or before current backup stage '%s'"
+ER_BACKUP_STAGE_FAILED
+ eng "Backup stage '%s' failed"
+ER_BACKUP_UNKNOWN_STAGE
+ eng "Unknown backup stage: '%s'. Stage should be one of START, FLUSH, BLOCK_DDL, BLOCK_COMMIT or END"
+ER_USER_IS_BLOCKED
+ eng "User is blocked because of too many credential errors; unblock with 'FLUSH PRIVILEGES'"
+ER_ACCOUNT_HAS_BEEN_LOCKED
+ eng "Access denied, this account is locked"
+ rum "Acces refuzat, acest cont este blocat"
+ER_PERIOD_TEMPORARY_NOT_ALLOWED
+ eng "Application-time period table cannot be temporary"
+ER_PERIOD_TYPES_MISMATCH
+ eng "Fields of PERIOD FOR %`s have different types"
+ER_MORE_THAN_ONE_PERIOD
+ eng "Cannot specify more than one application-time period"
+ER_PERIOD_FIELD_WRONG_ATTRIBUTES
+ eng "Period field %`s cannot be %s"
+ER_PERIOD_NOT_FOUND
+ eng "Period %`s is not found in table"
+ER_PERIOD_COLUMNS_UPDATED
+ eng "Column %`s used in period %`s specified in update SET list"
+ER_PERIOD_CONSTRAINT_DROP
+ eng "Can't DROP CONSTRAINT `%s`. Use DROP PERIOD `%s` for this"
+ER_TOO_LONG_KEYPART 42000 S1009
+ eng "Specified key part was too long; max key part length is %u bytes"
diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc
index c0f31077e4d..f27676bee19 100644
--- a/sql/signal_handler.cc
+++ b/sql/signal_handler.cc
@@ -15,6 +15,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
#include "mariadb.h"
+#include "my_dbug.h"
#include <signal.h>
//#include "sys_vars.h"
@@ -130,8 +131,8 @@ extern "C" sig_handler handle_fatal_signal(int sig)
my_safe_printf_stderr("Fatal " SIGNAL_FMT " while backtracing\n", sig);
goto end;
}
-
segfaulted = 1;
+ DBUG_PRINT("error", ("handling fatal signal"));
curr_time= my_time(0);
localtime_r(&curr_time, &tm);
diff --git a/sql/slave.cc b/sql/slave.cc
index f145d644bb7..31bd9372a14 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2017, Oracle and/or its affiliates.
- Copyright (c) 2009, 2020, MariaDB Corporation
+ Copyright (c) 2009, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -53,6 +53,9 @@
// Create_file_log_event,
// Format_description_log_event
#include "wsrep_mysqld.h"
+#ifdef WITH_WSREP
+#include "wsrep_trans_observer.h"
+#endif
#ifdef HAVE_REPLICATION
@@ -360,6 +363,29 @@ end:
return err;
}
+static THD *new_bg_THD()
+{
+ THD *thd= new THD(next_thread_id());
+ thd->thread_stack= (char*) &thd;
+ thd->store_globals();
+ thd->system_thread = SYSTEM_THREAD_SLAVE_BACKGROUND;
+ thd->security_ctx->skip_grants();
+ thd->set_command(COM_DAEMON);
+ thd->variables.wsrep_on= 0;
+ return thd;
+}
+
+static void bg_gtid_delete_pending(void *)
+{
+ THD *thd= new_bg_THD();
+
+ rpl_slave_state::list_element *list;
+ list= rpl_global_gtid_slave_state->gtid_grab_pending_delete_list();
+ rpl_global_gtid_slave_state->gtid_delete_pending(thd, &list);
+ if (list)
+ rpl_global_gtid_slave_state->put_back_list(list);
+ delete thd;
+}
static void bg_gtid_pos_auto_create(void *hton)
{
@@ -394,8 +420,8 @@ static void bg_gtid_pos_auto_create(void *hton)
/* Find the entry for the table to auto-create. */
mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
- entry= (rpl_slave_state::gtid_pos_table *)
- rpl_global_gtid_slave_state->gtid_pos_tables;
+ entry= rpl_global_gtid_slave_state->
+ gtid_pos_tables.load(std::memory_order_relaxed);
while (entry)
{
if (entry->table_hton == hton &&
@@ -419,13 +445,7 @@ static void bg_gtid_pos_auto_create(void *hton)
table_name.str= loc_table_name.c_ptr_safe();
table_name.length= loc_table_name.length();
- thd= new THD(next_thread_id());
- thd->thread_stack= (char*) &thd; /* Set approximate stack start */
- thd->system_thread = SYSTEM_THREAD_SLAVE_BACKGROUND;
- thd->store_globals();
- thd->security_ctx->skip_grants();
- thd->set_command(COM_DAEMON);
- thd->variables.wsrep_on= 0;
+ thd= new_bg_THD();
err= gtid_pos_table_creation(thd, engine, &table_name);
if (err)
{
@@ -438,8 +458,8 @@ static void bg_gtid_pos_auto_create(void *hton)
/* Now enable the entry for the auto-created table. */
mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
- entry= (rpl_slave_state::gtid_pos_table *)
- rpl_global_gtid_slave_state->gtid_pos_tables;
+ entry= rpl_global_gtid_slave_state->
+ gtid_pos_tables.load(std::memory_order_relaxed);
while (entry)
{
if (entry->table_hton == hton &&
@@ -458,19 +478,11 @@ end:
plugin_unlock(NULL, engine);
}
-
static bool slave_background_thread_gtid_loaded;
static void bg_rpl_load_gtid_slave_state(void *)
{
- THD *thd= new THD(next_thread_id());
- thd->thread_stack= (char*) &thd; /* Set approximate stack start */
- thd->system_thread = SYSTEM_THREAD_SLAVE_BACKGROUND;
- thd->store_globals();
- thd->security_ctx->skip_grants();
- thd->set_command(COM_DAEMON);
- thd->variables.wsrep_on= 0;
-
+ THD *thd= new_bg_THD();
thd_proc_info(thd, "Loading slave GTID position from table");
if (rpl_load_gtid_slave_state(thd))
sql_print_warning("Failed to load slave replication state from table "
@@ -510,8 +522,7 @@ void slave_background_kill_request(THD *to_kill)
to ensure that the table_entry will not go away before we can lock the
LOCK_slave_state.
*/
-void
-slave_background_gtid_pos_create_request(
+void slave_background_gtid_pos_create_request(
rpl_slave_state::gtid_pos_table *table_entry)
{
if (table_entry->state != rpl_slave_state::GTID_POS_AUTO_CREATE)
@@ -529,6 +540,16 @@ slave_background_gtid_pos_create_request(
}
+/*
+ Request the manager thread to delete no longer used rows from the
+ mysql.gtid_slave_pos* tables.
+*/
+void slave_background_gtid_pending_delete_request(void)
+{
+ mysql_manager_submit(bg_gtid_delete_pending, NULL);
+}
+
+
/* Initialize slave structures */
int init_slave()
@@ -833,6 +854,8 @@ static void make_slave_transaction_retry_errors_printable(void)
}
+#define DEFAULT_SLAVE_RETRY_ERRORS 9
+
bool init_slave_transaction_retry_errors(const char* arg)
{
const char *p;
@@ -844,7 +867,7 @@ bool init_slave_transaction_retry_errors(const char* arg)
if (!arg)
arg= "";
- slave_transaction_retry_error_length= 2;
+ slave_transaction_retry_error_length= DEFAULT_SLAVE_RETRY_ERRORS;
for (;my_isspace(system_charset_info,*arg);++arg)
/* empty */;
for (p= arg; *p; )
@@ -867,15 +890,22 @@ bool init_slave_transaction_retry_errors(const char* arg)
currently, InnoDB deadlock detected by InnoDB or lock
wait timeout (innodb_lock_wait_timeout exceeded
*/
- slave_transaction_retry_errors[0]= ER_LOCK_DEADLOCK;
- slave_transaction_retry_errors[1]= ER_LOCK_WAIT_TIMEOUT;
+ slave_transaction_retry_errors[0]= ER_NET_READ_ERROR;
+ slave_transaction_retry_errors[1]= ER_NET_READ_INTERRUPTED;
+ slave_transaction_retry_errors[2]= ER_NET_ERROR_ON_WRITE;
+ slave_transaction_retry_errors[3]= ER_NET_WRITE_INTERRUPTED;
+ slave_transaction_retry_errors[4]= ER_LOCK_WAIT_TIMEOUT;
+ slave_transaction_retry_errors[5]= ER_LOCK_DEADLOCK;
+ slave_transaction_retry_errors[6]= ER_CONNECT_TO_FOREIGN_DATA_SOURCE;
+ slave_transaction_retry_errors[7]= 2013; /* CR_SERVER_LOST */
+ slave_transaction_retry_errors[8]= 12701; /* ER_SPIDER_REMOTE_SERVER_GONE_AWAY_NUM */
/* Add user codes after this */
- for (p= arg, i= 2; *p; )
+ for (p= arg, i= DEFAULT_SLAVE_RETRY_ERRORS; *p; )
{
if (!(p= str2int(p, 10, 0, LONG_MAX, &err_code)))
break;
- if (err_code > 0 && err_code < ER_ERROR_LAST)
+ if (err_code > 0)
slave_transaction_retry_errors[i++]= (uint) err_code;
while (!my_isdigit(system_charset_info,*p) && *p)
p++;
@@ -1040,6 +1070,7 @@ terminate_slave_thread(THD *thd,
DBUG_PRINT("loop", ("killing slave thread"));
mysql_mutex_lock(&thd->LOCK_thd_kill);
+ mysql_mutex_lock(&thd->LOCK_thd_data);
#ifndef DONT_USE_THR_ALARM
/*
Error codes from pthread_kill are:
@@ -1052,6 +1083,7 @@ terminate_slave_thread(THD *thd,
thd->awake_no_mutex(NOT_KILLED);
mysql_mutex_unlock(&thd->LOCK_thd_kill);
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
/*
There is a small chance that slave thread might miss the first
@@ -1261,6 +1293,9 @@ void slave_prepare_for_shutdown()
mysql_mutex_lock(&LOCK_active_mi);
master_info_index->free_connections();
mysql_mutex_unlock(&LOCK_active_mi);
+ // It's safe to destruct worker pool now when
+ // all driver threads are gone.
+ global_rpl_thread_pool.deactivate();
}
/*
@@ -1301,7 +1336,7 @@ static bool io_slave_killed(Master_info* mi)
DBUG_ENTER("io_slave_killed");
DBUG_ASSERT(mi->slave_running); // tracking buffer overrun
- DBUG_RETURN(mi->abort_slave || abort_loop || mi->io_thd->killed);
+ DBUG_RETURN(mi->abort_slave || mi->io_thd->killed);
}
/**
@@ -1326,7 +1361,7 @@ static bool sql_slave_killed(rpl_group_info *rgi)
DBUG_ASSERT(rli->sql_driver_thd == thd);
DBUG_ASSERT(rli->slave_running == 1);// tracking buffer overrun
- if (abort_loop || rli->sql_driver_thd->killed || rli->abort_slave)
+ if (rli->sql_driver_thd->killed || rli->abort_slave)
{
/*
The transaction should always be binlogged if OPTION_KEEP_LOG is
@@ -2552,10 +2587,7 @@ static bool wait_for_relay_log_space(Relay_log_info* rli)
&rli->log_space_lock,
&stage_waiting_for_relay_log_space,
&old_stage);
- while (rli->log_space_limit <
- (ulonglong)my_atomic_load64_explicit((volatile int64*)
- (&rli->log_space_total),
- MY_MEMORY_ORDER_RELAXED) &&
+ while (rli->log_space_limit < rli->log_space_total &&
!(slave_killed=io_slave_killed(mi)) &&
!rli->ignore_log_space_limit)
mysql_cond_wait(&rli->log_space_cond, &rli->log_space_lock);
@@ -2596,7 +2628,7 @@ static bool wait_for_relay_log_space(Relay_log_info* rli)
DBUG_PRINT("info", ("log_space_limit=%llu log_space_total=%llu "
"ignore_log_space_limit=%d "
"sql_force_rotate_relay=%d",
- rli->log_space_limit, rli->log_space_total,
+ rli->log_space_limit, uint64(rli->log_space_total),
(int) rli->ignore_log_space_limit,
(int) rli->sql_force_rotate_relay));
}
@@ -3118,10 +3150,7 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
protocol->store(mi->rli.last_error().message, &my_charset_bin);
protocol->store((uint32) mi->rli.slave_skip_counter);
protocol->store((ulonglong) mi->rli.group_master_log_pos);
- protocol->store((ulonglong)
- my_atomic_load64_explicit((volatile int64*)
- (&mi->rli.log_space_total),
- MY_MEMORY_ORDER_RELAXED));
+ protocol->store((ulonglong) mi->rli.log_space_total);
protocol->store(
mi->rli.until_condition==Relay_log_info::UNTIL_NONE ? "None":
@@ -3248,22 +3277,15 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
// Slave_SQL_Running_State
protocol->store(slave_sql_running_state, &my_charset_bin);
- uint64 events;
- events= (uint64)my_atomic_load64_explicit((volatile int64 *)
- &mi->total_ddl_groups, MY_MEMORY_ORDER_RELAXED);
- protocol->store(events);
- events= (uint64)my_atomic_load64_explicit((volatile int64 *)
- &mi->total_non_trans_groups, MY_MEMORY_ORDER_RELAXED);
- protocol->store(events);
- events= (uint64)my_atomic_load64_explicit((volatile int64 *)
- &mi->total_trans_groups, MY_MEMORY_ORDER_RELAXED);
- protocol->store(events);
+ protocol->store(mi->total_ddl_groups);
+ protocol->store(mi->total_non_trans_groups);
+ protocol->store(mi->total_trans_groups);
if (full)
{
protocol->store((uint32) mi->rli.retried_trans);
protocol->store((ulonglong) mi->rli.max_relay_log_size);
- protocol->store((uint32) mi->rli.executed_entries);
+ protocol->store(mi->rli.executed_entries);
protocol->store((uint32) mi->received_heartbeats);
protocol->store((double) mi->heartbeat_period, 3, &tmp);
protocol->store(gtid_pos->ptr(), gtid_pos->length(), &my_charset_bin);
@@ -3415,7 +3437,6 @@ static int init_slave_thread(THD* thd, Master_info *mi,
thd->system_thread = (thd_type == SLAVE_THD_SQL) ?
SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO;
- thread_safe_increment32(&service_thread_count);
/* We must call store_globals() before doing my_net_init() */
if (init_thr_lock() || thd->store_globals() ||
@@ -3786,32 +3807,26 @@ apply_event_and_update_pos_apply(Log_event* ev, THD* thd, rpl_group_info *rgi,
exec_res= ev->apply_event(rgi);
#ifdef WITH_WSREP
- if (exec_res)
- {
- switch (thd->wsrep_conflict_state) {
- case NO_CONFLICT: break;
- case MUST_REPLAY:
- WSREP_DEBUG("SQL apply failed for MUST_REPLAY, res %d", exec_res);
+ if (WSREP(thd)) {
+
+ if (exec_res) {
mysql_mutex_lock(&thd->LOCK_thd_data);
- wsrep_replay_transaction(thd);
- switch (thd->wsrep_conflict_state) {
- case NO_CONFLICT:
- exec_res = 0; /* replaying succeeded, and slave may continue */
+ switch(thd->wsrep_trx().state()) {
+ case wsrep::transaction::s_must_replay:
+ /* this transaction will be replayed,
+ so not raising slave error here */
+ WSREP_DEBUG("SQL apply failed for MUST_REPLAY, res %d", exec_res);
+ exec_res = 0;
break;
- case ABORTED: break; /* replaying has failed, trx is rolled back */
default:
- WSREP_WARN("unexpected result of slave transaction replaying: %lld, %d",
- thd->thread_id, thd->wsrep_conflict_state);
+ WSREP_DEBUG("SQL apply failed, res %d conflict state: %s",
+ exec_res, wsrep_thd_transaction_state_str(thd));
+ rli->abort_slave= 1;
+ rli->report(ERROR_LEVEL, ER_UNKNOWN_COM_ERROR, rgi->gtid_info(),
+ "Node has dropped from cluster");
+ break;
}
mysql_mutex_unlock(&thd->LOCK_thd_data);
- break;
- default:
- WSREP_DEBUG("SQL apply failed, res %d conflict state: %d",
- exec_res, thd->wsrep_conflict_state);
- rli->abort_slave= 1;
- rli->report(ERROR_LEVEL, ER_UNKNOWN_COM_ERROR, rgi->gtid_info(),
- "Node has dropped from cluster");
- break;
}
}
#endif
@@ -4106,6 +4121,13 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
}
if (ev)
{
+#ifdef WITH_WSREP
+ if (wsrep_before_statement(thd))
+ {
+ WSREP_INFO("Wsrep before statement error");
+ DBUG_RETURN(1);
+ }
+#endif /* WITH_WSREP */
int exec_res;
Log_event_type typ= ev->get_type_code();
@@ -4146,6 +4168,9 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
rli->abort_slave= 1;
rli->stop_for_until= true;
mysql_mutex_unlock(&rli->data_lock);
+#ifdef WITH_WSREP
+ wsrep_after_statement(thd);
+#endif /* WITH_WSREP */
delete ev;
DBUG_RETURN(1);
}
@@ -4183,7 +4208,12 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
if (res == 0)
rli->event_relay_log_pos= rli->future_event_relay_log_pos;
if (res >= 0)
+ {
+#ifdef WITH_WSREP
+ wsrep_after_statement(thd);
+#endif /* WITH_WSREP */
DBUG_RETURN(res);
+ }
/*
Else we proceed to execute the event non-parallel.
This is the case for pre-10.0 events without GTID, and for handling
@@ -4218,6 +4248,9 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
"aborted because of out-of-memory error");
mysql_mutex_unlock(&rli->data_lock);
delete ev;
+#ifdef WITH_WSREP
+ wsrep_after_statement(thd);
+#endif /* WITH_WSREP */
DBUG_RETURN(1);
}
@@ -4232,6 +4265,9 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
"thread aborted because of out-of-memory error");
mysql_mutex_unlock(&rli->data_lock);
delete ev;
+#ifdef WITH_WSREP
+ wsrep_after_statement(thd);
+#endif /* WITH_WSREP */
DBUG_RETURN(1);
}
/*
@@ -4260,13 +4296,17 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
retry.
*/
if (unlikely(exec_res == 2))
+ {
+#ifdef WITH_WSREP
+ wsrep_after_statement(thd);
+#endif /* WITH_WSREP */
DBUG_RETURN(1);
-
+ }
#ifdef WITH_WSREP
mysql_mutex_lock(&thd->LOCK_thd_data);
- if (thd->wsrep_conflict_state == NO_CONFLICT)
- {
- mysql_mutex_unlock(&thd->LOCK_thd_data);
+ enum wsrep::client_error wsrep_error= thd->wsrep_cs().current_error();
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ if (wsrep_error == wsrep::e_success)
#endif /* WITH_WSREP */
if (slave_trans_retries)
{
@@ -4279,8 +4319,8 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
We were in a transaction which has been rolled back because of a
temporary error;
let's seek back to BEGIN log event and retry it all again.
- Note, if lock wait timeout (innodb_lock_wait_timeout exceeded)
- there is no rollback since 5.0.13 (ref: manual).
+ Note, if lock wait timeout (innodb_lock_wait_timeout exceeded)
+ there is no rollback since 5.0.13 (ref: manual).
We have to not only seek but also
a) init_master_info(), to seek back to hot relay log's start
@@ -4341,13 +4381,11 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
serial_rgi->trans_retries));
}
}
+
+ rli->executed_entries++;
#ifdef WITH_WSREP
- }
- else
- mysql_mutex_unlock(&thd->LOCK_thd_data);
+ wsrep_after_statement(thd);
#endif /* WITH_WSREP */
-
- thread_safe_increment64(&rli->executed_entries);
DBUG_RETURN(exec_res);
}
mysql_mutex_unlock(&rli->data_lock);
@@ -4517,7 +4555,7 @@ pthread_handler_t handle_slave_io(void *arg)
goto err_during_init;
}
thd->system_thread_info.rpl_io_info= &io_info;
- add_to_active_threads(thd);
+ server_threads.insert(thd);
mi->slave_running = MYSQL_SLAVE_RUN_NOT_CONNECT;
mi->abort_slave = 0;
mysql_mutex_unlock(&mi->run_lock);
@@ -4845,15 +4883,13 @@ Stopping slave I/O thread due to out-of-memory error from master");
{
DBUG_PRINT("info", ("log_space_limit=%llu log_space_total=%llu "
"ignore_log_space_limit=%d",
- rli->log_space_limit, rli->log_space_total,
+ rli->log_space_limit, uint64(rli->log_space_total),
(int) rli->ignore_log_space_limit));
}
#endif
if (rli->log_space_limit && rli->log_space_limit <
- (ulonglong) my_atomic_load64_explicit((volatile int64*)
- (&rli->log_space_total),
- MY_MEMORY_ORDER_RELAXED) &&
+ rli->log_space_total &&
!rli->ignore_log_space_limit)
if (wait_for_relay_log_space(rli))
{
@@ -4907,7 +4943,7 @@ err:
flush_master_info(mi, TRUE, TRUE);
THD_STAGE_INFO(thd, stage_waiting_for_slave_mutex_on_exit);
thd->add_status_to_global();
- unlink_not_visible_thd(thd);
+ server_threads.erase(thd);
mysql_mutex_lock(&mi->run_lock);
err_during_init:
@@ -4919,8 +4955,6 @@ err_during_init:
thd->assert_not_linked();
delete thd;
- thread_safe_decrement32(&service_thread_count);
- signal_thd_deleted();
mi->abort_slave= 0;
mi->slave_running= MYSQL_SLAVE_NOT_RUN;
@@ -5195,7 +5229,7 @@ pthread_handler_t handle_slave_sql(void *arg)
/* Ensure that slave can exeute any alter table it gets from master */
thd->variables.alter_algorithm= (ulong) Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT;
- add_to_active_threads(thd);
+ server_threads.insert(thd);
/*
We are going to set slave_running to 1. Assuming slave I/O thread is
alive and connected, this is going to make Seconds_Behind_Master be 0
@@ -5282,13 +5316,6 @@ pthread_handler_t handle_slave_sql(void *arg)
}
#endif
-#ifdef WITH_WSREP
- thd->wsrep_exec_mode= LOCAL_STATE;
- wsrep_thd_set_query_state(thd, QUERY_EXEC);
- /* synchronize with wsrep replication */
- if (WSREP_ON)
- wsrep_ready_wait();
-#endif
DBUG_PRINT("master_info",("log_file_name: %s position: %llu",
rli->group_master_log_name,
rli->group_master_log_pos));
@@ -5389,7 +5416,14 @@ pthread_handler_t handle_slave_sql(void *arg)
goto err;
}
mysql_mutex_unlock(&rli->data_lock);
-
+#ifdef WITH_WSREP
+ wsrep_open(thd);
+ if (wsrep_before_command(thd))
+ {
+ WSREP_WARN("Slave SQL wsrep_before_command() failed");
+ goto err;
+ }
+#endif /* WITH_WSREP */
/* Read queries from the IO/THREAD until this thread is killed */
thd->set_command(COM_SLAVE_SQL);
@@ -5426,10 +5460,16 @@ pthread_handler_t handle_slave_sql(void *arg)
if (exec_relay_log_event(thd, rli, serial_rgi))
{
#ifdef WITH_WSREP
- if (thd->wsrep_conflict_state != NO_CONFLICT)
+ if (WSREP(thd))
{
- wsrep_node_dropped= TRUE;
- rli->abort_slave= TRUE;
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+
+ if (thd->wsrep_cs().current_error())
+ {
+ wsrep_node_dropped = TRUE;
+ rli->abort_slave = TRUE;
+ }
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
}
#endif /* WITH_WSREP */
@@ -5482,6 +5522,10 @@ pthread_handler_t handle_slave_sql(void *arg)
rli->group_master_log_pos, tmp.c_ptr_safe());
sql_print_information("master was %s:%d", mi->host, mi->port);
}
+#ifdef WITH_WSREP
+ wsrep_after_command_before_result(thd);
+ wsrep_after_command_after_result(thd);
+#endif /* WITH_WSREP */
err_before_start:
@@ -5555,7 +5599,7 @@ pthread_handler_t handle_slave_sql(void *arg)
}
THD_STAGE_INFO(thd, stage_waiting_for_slave_mutex_on_exit);
thd->add_status_to_global();
- unlink_not_visible_thd(thd);
+ server_threads.erase(thd);
mysql_mutex_lock(&rli->run_lock);
err_during_init:
@@ -5600,17 +5644,17 @@ err_during_init:
"SQL slave will continue");
wsrep_node_dropped= FALSE;
mysql_mutex_unlock(&rli->run_lock);
- WSREP_DEBUG("wsrep_conflict_state now: %d", thd->wsrep_conflict_state);
- WSREP_INFO("slave restart: %d", thd->wsrep_conflict_state);
- thd->wsrep_conflict_state= NO_CONFLICT;
goto wsrep_restart_point;
- } else {
+ }
+ else
+ {
WSREP_INFO("Slave error due to node going non-primary");
WSREP_INFO("wsrep_restart_slave was set and therefore slave will be "
- "automatically restarted when node joins back to cluster.");
+ "automatically restarted when node joins back to cluster");
wsrep_restart_slave_activated= TRUE;
}
}
+ wsrep_close(thd);
#endif /* WITH_WSREP */
/*
@@ -5627,8 +5671,6 @@ err_during_init:
delete serial_rgi;
delete thd;
- thread_safe_decrement32(&service_thread_count);
- signal_thd_deleted();
DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
@@ -7553,10 +7595,7 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size)
is are able to rotate and purge sometime soon.
*/
if (rli->log_space_limit &&
- rli->log_space_limit <
- (ulonglong) my_atomic_load64_explicit((volatile int64*)
- (&rli->log_space_total),
- MY_MEMORY_ORDER_RELAXED))
+ rli->log_space_limit < rli->log_space_total)
{
/* force rotation if not in an unfinished group */
rli->sql_force_rotate_relay= !rli->is_in_group();
@@ -7791,7 +7830,7 @@ enum Log_event_type wsrep_peak_event(rpl_group_info *rgi, ulonglong* event_size)
ev_type= (ev) ? ev->get_type_code() : UNKNOWN_EVENT;
delete ev;
future_pos+= *event_size;
- } while (ev_type == ANNOTATE_ROWS_EVENT);
+ } while (ev_type == ANNOTATE_ROWS_EVENT || ev_type == XID_EVENT);
/* scan the log back and re-set the positions to original values */
rgi->rli->event_relay_log_pos= event_pos;
@@ -7875,8 +7914,8 @@ bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
{
struct st_version_range_for_one_bug {
uint bug_id;
- const uchar introduced_in[3]; // first version with bug
- const uchar fixed_in[3]; // first version with fix
+ Version introduced_in; // first version with bug
+ Version fixed_in; // first version with fix
};
static struct st_version_range_for_one_bug versions_for_all_bugs[]=
{
@@ -7886,19 +7925,17 @@ bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
{33029, { 5, 1, 0 }, { 5, 1, 12 } },
{37426, { 5, 1, 0 }, { 5, 1, 26 } },
};
- const uchar *master_ver=
- rli->relay_log.description_event_for_exec->server_version_split.ver;
-
- DBUG_ASSERT(sizeof(rli->relay_log.description_event_for_exec->server_version_split.ver) == 3);
+ const Version &master_ver=
+ rli->relay_log.description_event_for_exec->server_version_split;
for (uint i= 0;
i < sizeof(versions_for_all_bugs)/sizeof(*versions_for_all_bugs);i++)
{
- const uchar *introduced_in= versions_for_all_bugs[i].introduced_in,
- *fixed_in= versions_for_all_bugs[i].fixed_in;
+ const Version &introduced_in= versions_for_all_bugs[i].introduced_in;
+ const Version &fixed_in= versions_for_all_bugs[i].fixed_in;
if ((versions_for_all_bugs[i].bug_id == bug_id) &&
- (memcmp(introduced_in, master_ver, 3) <= 0) &&
- (memcmp(fixed_in, master_ver, 3) > 0) &&
+ introduced_in <= master_ver &&
+ fixed_in > master_ver &&
(pred == NULL || (*pred)(param)))
{
if (!report)
diff --git a/sql/slave.h b/sql/slave.h
index 97fd4823a07..5ca6054a178 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -276,8 +276,8 @@ bool net_request_file(NET* net, const char* fname);
void slave_background_kill_request(THD *to_kill);
void slave_background_gtid_pos_create_request
(rpl_slave_state::gtid_pos_table *table_entry);
+void slave_background_gtid_pending_delete_request(void);
-extern bool volatile abort_loop;
extern Master_info *active_mi; /* active_mi for multi-master */
extern Master_info *default_master_info; /* To replace active_mi */
extern Master_info_index *master_info_index;
diff --git a/sql/sp.cc b/sql/sp.cc
index a4c4ca58414..de1a8a04756 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -200,7 +200,8 @@ TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] =
"'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
"'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
"'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH',"
- "'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT')") },
+ "'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT',"
+ "'TIME_ROUND_FRACTIONAL')") },
{ NULL, 0 }
},
{
@@ -1465,7 +1466,7 @@ log:
log_query.ptr(), log_query.length(),
FALSE, FALSE, FALSE, 0) > 0)
{
- my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), "binary log", -1);
+ my_error(ER_ERROR_ON_WRITE, MYF(0), "binary log", -1);
goto done;
}
thd->variables.sql_mode= 0;
@@ -1789,8 +1790,8 @@ bool lock_db_routines(THD *thd, const char *db)
close_system_tables(thd, &open_tables_state_backup);
/* We should already hold a global IX lock and a schema X lock. */
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
- MDL_INTENTION_EXCLUSIVE) &&
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::BACKUP, "", "",
+ MDL_BACKUP_DDL) &&
thd->mdl_context.is_lock_owner(MDL_key::SCHEMA, db, "",
MDL_EXCLUSIVE));
DBUG_RETURN(thd->mdl_context.acquire_locks(&mdl_requests,
diff --git a/sql/sp.h b/sql/sp.h
index 852fbccafdf..49bf1eb93cf 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -522,12 +522,11 @@ inline const Sp_handler *Sp_handler::handler(MDL_key::enum_mdl_namespace type)
return &sp_handler_procedure;
case MDL_key::PACKAGE_BODY:
return &sp_handler_package_body;
- case MDL_key::GLOBAL:
+ case MDL_key::BACKUP:
case MDL_key::SCHEMA:
case MDL_key::TABLE:
case MDL_key::TRIGGER:
case MDL_key::EVENT:
- case MDL_key::COMMIT:
case MDL_key::USER_LOCK:
case MDL_key::NAMESPACE_END:
break;
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index bd41de78c9a..aa4f80907f5 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2002, 2016, Oracle and/or its affiliates.
- Copyright (c) 2011, 2017, MariaDB
+ Copyright (c) 2011, 2020, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,6 +29,8 @@
#include "sql_derived.h" // mysql_handle_derived
#include "sql_cte.h"
#include "sql_select.h" // Virtual_tmp_table
+#include "opt_trace.h"
+#include "my_json_writer.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation
@@ -45,7 +47,7 @@
#include "sql_audit.h"
#include "debug_sync.h"
#ifdef WITH_WSREP
-#include "wsrep_thd.h"
+#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
/*
@@ -74,33 +76,6 @@ static void reset_start_time_for_sp(THD *thd)
}
-Item::Type
-sp_map_item_type(const Type_handler *handler)
-{
- if (handler == &type_handler_row)
- return Item::ROW_ITEM;
- enum_field_types type= real_type_to_type(handler->real_field_type());
-
- switch (type) {
- case MYSQL_TYPE_BIT:
- case MYSQL_TYPE_TINY:
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_LONGLONG:
- case MYSQL_TYPE_INT24:
- return Item::INT_ITEM;
- case MYSQL_TYPE_DECIMAL:
- case MYSQL_TYPE_NEWDECIMAL:
- return Item::DECIMAL_ITEM;
- case MYSQL_TYPE_FLOAT:
- case MYSQL_TYPE_DOUBLE:
- return Item::REAL_ITEM;
- default:
- return Item::STRING_ITEM;
- }
-}
-
-
bool Item_splocal::append_for_log(THD *thd, String *str)
{
if (fix_fields_if_needed(thd, NULL))
@@ -322,7 +297,7 @@ sp_get_flags_for_command(LEX *lex)
- EXPLAIN DELETE ...
- ANALYZE DELETE ...
*/
- if (lex->select_lex.item_list.is_empty() &&
+ if (lex->first_select_lex()->item_list.is_empty() &&
!lex->describe && !lex->analyze_stmt)
flags= 0;
else
@@ -477,13 +452,14 @@ check_routine_name(const LEX_CSTRING *ident)
*
*/
-sp_head *sp_head::create(sp_package *parent, const Sp_handler *handler)
+sp_head *sp_head::create(sp_package *parent, const Sp_handler *handler,
+ enum_sp_aggregate_type agg_type)
{
MEM_ROOT own_root;
init_sql_alloc(&own_root, "sp_head", MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC,
MYF(0));
sp_head *sp;
- if (!(sp= new (&own_root) sp_head(&own_root, parent, handler)))
+ if (!(sp= new (&own_root) sp_head(&own_root, parent, handler, agg_type)))
free_root(&own_root, MYF(0));
return sp;
@@ -511,8 +487,8 @@ void sp_head::destroy(sp_head *sp)
*
*/
-sp_head::sp_head(MEM_ROOT *mem_root_arg, sp_package *parent,
- const Sp_handler *sph)
+sp_head::sp_head(MEM_ROOT *mem_root_arg, sp_package *parent,
+ const Sp_handler *sph, enum_sp_aggregate_type agg_type)
:Query_arena(NULL, STMT_INITIALIZED_FOR_SP),
Database_qualified_name(&null_clex_str, &null_clex_str),
main_mem_root(*mem_root_arg),
@@ -548,6 +524,7 @@ sp_head::sp_head(MEM_ROOT *mem_root_arg, sp_package *parent,
{
mem_root= &main_mem_root;
+ set_chistics_agg_type(agg_type);
m_first_instance= this;
m_first_free_instance= this;
m_last_cached_sp= this;
@@ -565,6 +542,7 @@ sp_head::sp_head(MEM_ROOT *mem_root_arg, sp_package *parent,
my_hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
my_hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key,
0, 0);
+ m_security_ctx.init();
DBUG_VOID_RETURN;
}
@@ -588,7 +566,7 @@ sp_package::sp_package(MEM_ROOT *mem_root_arg,
LEX *top_level_lex,
const sp_name *name,
const Sp_handler *sph)
- :sp_head(mem_root_arg, NULL, sph),
+ :sp_head(mem_root_arg, NULL, sph, DEFAULT_AGGREGATE),
m_current_routine(NULL),
m_top_level_lex(top_level_lex),
m_rcontext(NULL),
@@ -1191,6 +1169,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
if (check_stack_overrun(thd, 7 * STACK_MIN_SIZE, (uchar*)&old_packet))
DBUG_RETURN(TRUE);
+ opt_trace_disable_if_no_security_context_access(thd);
+
/* init per-instruction memroot */
init_sql_alloc(&execute_mem_root, "per_instruction_memroot",
MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
@@ -1374,40 +1354,19 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
sql_digest_state *parent_digest= thd->m_digest;
thd->m_digest= NULL;
- err_status= i->execute(thd, &ip);
#ifdef WITH_WSREP
- if (m_handler->type() == TYPE_ENUM_PROCEDURE)
+ if (WSREP(thd) && thd->wsrep_next_trx_id() == WSREP_UNDEFINED_TRX_ID)
{
- mysql_mutex_lock(&thd->LOCK_thd_data);
- if (thd->wsrep_conflict_state == MUST_REPLAY)
- {
- wsrep_replay_sp_transaction(thd);
- err_status= thd->get_stmt_da()->is_set();
- thd->wsrep_conflict_state= NO_CONFLICT;
- }
- else if (thd->wsrep_conflict_state == ABORTED ||
- thd->wsrep_conflict_state == CERT_FAILURE)
- {
- /*
- If the statement execution was BF aborted or was aborted
- due to certification failure, clean up transaction here
- and reset conflict state to NO_CONFLICT and thd->killed
- to THD::NOT_KILLED. Error handling is done based on err_status
- below. Error must have been raised by wsrep hton code before
- entering here.
- */
- DBUG_ASSERT(err_status);
- DBUG_ASSERT(thd->get_stmt_da()->is_error());
- thd->wsrep_conflict_state= NO_CONFLICT;
- thd->killed= NOT_KILLED;
- }
- mysql_mutex_unlock(&thd->LOCK_thd_data);
+ thd->set_wsrep_next_trx_id(thd->query_id);
+ WSREP_DEBUG("assigned new next trx ID for SP, trx id: %" PRIu64, thd->wsrep_next_trx_id());
}
#endif /* WITH_WSREP */
-#ifdef WITH_WSREP_NO
+ err_status= i->execute(thd, &ip);
+
+#ifdef WITH_WSREP
if (WSREP(thd))
{
- if (((thd->wsrep_trx().state() == wsrep::transaction::s_executing) &&
+ if (((thd->wsrep_trx().state() == wsrep::transaction::s_executing || thd->in_sub_stmt) &&
(thd->is_fatal_error || thd->killed)))
{
WSREP_DEBUG("SP abort err status %d in sub %d trx state %d",
@@ -1420,6 +1379,11 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
otherwise it clears the error, and cleans up the
whole transaction. For now we just return and finish
our handling once we are back to mysql_parse.
+
+ Same applies to a SP execution, which was aborted due
+ to wsrep related conflict, but which is executing as sub statement.
+ SP in sub statement level should not commit not rollback,
+ we have to call for rollback is up-most SP level.
*/
WSREP_DEBUG("Skipping after_command hook for killed SP");
}
@@ -2032,7 +1996,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
for (arg_no= 0; arg_no < argcount; arg_no++)
{
/* Arguments must be fixed in Item_func_sp::fix_fields */
- DBUG_ASSERT(argp[arg_no]->fixed);
+ DBUG_ASSERT(argp[arg_no]->is_fixed());
if ((err_status= (*func_ctx)->set_parameter(thd, arg_no, &(argp[arg_no]))))
goto err_with_cleanup;
@@ -2109,6 +2073,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
thd->variables.option_bits&= ~OPTION_BIN_LOG;
}
+ opt_trace_disable_if_no_stored_proc_func_access(thd, this);
/*
Switch to call arena/mem_root so objects like sp_cursor or
Item_cache holders for case expressions can be allocated on it.
@@ -2399,9 +2364,11 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
err_status= set_routine_security_ctx(thd, this, &save_security_ctx);
#endif
+ opt_trace_disable_if_no_stored_proc_func_access(thd, this);
if (!err_status)
{
err_status= execute(thd, TRUE);
+ DBUG_PRINT("info", ("execute returned %d", (int) err_status));
}
if (save_log_general)
@@ -2800,6 +2767,17 @@ sp_head::set_chistics(const st_sp_chistics &chistics)
m_chistics.comment.length);
}
+
+void
+sp_head::set_c_chistics(const st_sp_chistics &chistics)
+{
+ // Set all chistics but preserve agg_type.
+ enum_sp_aggregate_type save_agg_type= agg_type();
+ set_chistics(chistics);
+ set_chistics_agg_type(save_agg_type);
+}
+
+
void
sp_head::set_info(longlong created, longlong modified,
const st_sp_chistics &chistics, sql_mode_t sql_mode)
@@ -3425,6 +3403,13 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
thd->lex->safe_to_cache_query= 0;
#endif
+ Opt_trace_start ots(thd, m_lex->query_tables,
+ SQLCOM_SELECT, &m_lex->var_list,
+ NULL, 0,
+ thd->variables.character_set_client);
+
+ Json_writer_object trace_command(thd);
+ Json_writer_array trace_command_steps(thd, "steps");
if (open_tables)
res= check_dependencies_in_with_clauses(m_lex->with_clauses_list) ||
instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
@@ -4637,8 +4622,8 @@ int
sp_instr_error::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_error::execute");
-
my_message(m_errcode, ER_THD(thd, m_errcode), MYF(0));
+ WSREP_DEBUG("sp_instr_error: %s %d", ER_THD(thd, m_errcode), thd->is_error());
*nextp= m_ip+1;
DBUG_RETURN(-1);
}
@@ -4686,7 +4671,7 @@ sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp)
thd->spcont->set_case_expr(thd, m_case_expr_id, &null_item))
{
/* If this also failed, we have to abort. */
- my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL));
}
}
else
@@ -5230,6 +5215,36 @@ bool sp_head::spvar_fill_table_rowtype_reference(THD *thd,
}
+bool sp_head::check_group_aggregate_instructions_forbid() const
+{
+ if (unlikely(m_flags & sp_head::HAS_AGGREGATE_INSTR))
+ {
+ my_error(ER_NOT_AGGREGATE_FUNCTION, MYF(0));
+ return true;
+ }
+ return false;
+}
+
+
+bool sp_head::check_group_aggregate_instructions_require() const
+{
+ if (unlikely(!(m_flags & HAS_AGGREGATE_INSTR)))
+ {
+ my_error(ER_INVALID_AGGREGATE_FUNCTION, MYF(0));
+ return true;
+ }
+ return false;
+}
+
+
+bool sp_head::check_group_aggregate_instructions_function() const
+{
+ return agg_type() == GROUP_AGGREGATE ?
+ check_group_aggregate_instructions_require() :
+ check_group_aggregate_instructions_forbid();
+}
+
+
/*
In Oracle mode stored routines have an optional name
at the end of a declaration:
@@ -5264,6 +5279,19 @@ err:
}
+bool
+sp_head::check_standalone_routine_end_name(const sp_name *end_name) const
+{
+ if (end_name && !end_name->eq(this))
+ {
+ my_error(ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0),
+ ErrConvDQName(end_name).ptr(), ErrConvDQName(this).ptr());
+ return true;
+ }
+ return false;
+}
+
+
ulong sp_head::sp_cache_version() const
{
return m_parent ? m_parent->sp_cache_version() : m_sp_cache_version;
diff --git a/sql/sp_head.h b/sql/sp_head.h
index e1cfbb484ad..500446c5e16 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -1,6 +1,7 @@
/* -*- C++ -*- */
/*
Copyright (c) 2002, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2020, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,9 +40,6 @@
@{
*/
-Item::Type
-sp_map_item_type(const Type_handler *handler);
-
uint
sp_get_flags_for_command(LEX *lex);
@@ -130,8 +128,8 @@ class sp_head :private Query_arena,
public Database_qualified_name,
public Sql_alloc
{
- sp_head(const sp_head &); /**< Prevent use of these */
- void operator=(sp_head &);
+ sp_head(const sp_head &)= delete;
+ void operator=(sp_head &)= delete;
protected:
MEM_ROOT main_mem_root;
@@ -187,6 +185,11 @@ private:
set_chistics() makes sure this.
*/
Sp_chistics m_chistics;
+ void set_chistics(const st_sp_chistics &chistics);
+ inline void set_chistics_agg_type(enum enum_sp_aggregate_type type)
+ {
+ m_chistics.agg_type= type;
+ }
public:
sql_mode_t m_sql_mode; ///< For SHOW CREATE and execution
bool m_explicit_name; /**< Prepend the db name? */
@@ -318,11 +321,13 @@ public:
SQL_I_List<Item_trigger_field> m_trg_table_fields;
protected:
- sp_head(MEM_ROOT *mem_root, sp_package *parent, const Sp_handler *handler);
+ sp_head(MEM_ROOT *mem_root, sp_package *parent, const Sp_handler *handler,
+ enum_sp_aggregate_type agg_type);
virtual ~sp_head();
public:
static void destroy(sp_head *sp);
- static sp_head *create(sp_package *parent, const Sp_handler *handler);
+ static sp_head *create(sp_package *parent, const Sp_handler *handler,
+ enum_sp_aggregate_type agg_type);
/// Initialize after we have reset mem_root
void
@@ -414,6 +419,10 @@ public:
const LEX_CSTRING *field_name,
Item *val, LEX *lex);
bool check_package_routine_end_name(const LEX_CSTRING &end_name) const;
+ bool check_standalone_routine_end_name(const sp_name *end_name) const;
+ bool check_group_aggregate_instructions_function() const;
+ bool check_group_aggregate_instructions_forbid() const;
+ bool check_group_aggregate_instructions_require() const;
private:
/**
Generate a code to set a single cursor parameter variable.
@@ -591,7 +600,8 @@ public:
if (!oldlex)
DBUG_RETURN(false); // Nothing to restore
LEX *sublex= thd->lex;
- if (thd->restore_from_local_lex_to_old_lex(oldlex))// This restores thd->lex
+ // This restores thd->lex and thd->stmt_lex
+ if (thd->restore_from_local_lex_to_old_lex(oldlex))
DBUG_RETURN(true);
if (!sublex->sp_lex_in_use)
{
@@ -730,11 +740,7 @@ public:
const LEX_CSTRING &db,
const LEX_CSTRING &table);
- void set_chistics(const st_sp_chistics &chistics);
- inline void set_chistics_agg_type(enum enum_sp_aggregate_type type)
- {
- m_chistics.agg_type= type;
- }
+ void set_c_chistics(const st_sp_chistics &chistics);
void set_info(longlong created, longlong modified,
const st_sp_chistics &chistics, sql_mode_t sql_mode);
@@ -2031,6 +2037,7 @@ private:
}; // class sp_instr_set_case_expr : public sp_instr_opt_meta
+bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bool
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index 4d36ca8b479..b1d77234f54 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -22,7 +22,6 @@
#endif
#include "sql_string.h" // LEX_STRING
-#include "mysql_com.h" // enum_field_types
#include "field.h" // Create_field
#include "sql_array.h" // Dynamic_array
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 61d7c708983..17b4c83b7bc 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -119,6 +119,19 @@ sp_rcontext *sp_rcontext::create(THD *thd,
}
+bool Row_definition_list::append_uniq(MEM_ROOT *mem_root, Spvar_definition *var)
+{
+ DBUG_ASSERT(elements);
+ uint unused;
+ if (unlikely(find_row_field_by_name(&var->field_name, &unused)))
+ {
+ my_error(ER_DUP_FIELDNAME, MYF(0), var->field_name.str);
+ return true;
+ }
+ return push_back(var, mem_root);
+}
+
+
bool Row_definition_list::
adjust_formal_params_to_actual_params(THD *thd, List<Item> *args)
{
@@ -228,9 +241,10 @@ bool Qualified_column_ident::resolve_type_ref(THD *thd, Column_definition *def)
// Make %TYPE variables see temporary tables that shadow permanent tables
thd->temporary_tables= open_tables_state_backup.temporary_tables;
- if ((table_list= lex.select_lex.add_table_to_list(thd, this, NULL, 0,
- TL_READ_NO_INSERT,
- MDL_SHARED_READ)) &&
+ if ((table_list=
+ lex.first_select_lex()->add_table_to_list(thd, this, NULL, 0,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_READ)) &&
!check_table_access(thd, SELECT_ACL, table_list, TRUE, UINT_MAX, FALSE) &&
!open_tables_only_view_structure(thd, table_list,
thd->mdl_context.has_locks()))
@@ -286,9 +300,10 @@ bool Table_ident::resolve_table_rowtype_ref(THD *thd,
// Make %ROWTYPE variables see temporary tables that shadow permanent tables
thd->temporary_tables= open_tables_state_backup.temporary_tables;
- if ((table_list= lex.select_lex.add_table_to_list(thd, this, NULL, 0,
- TL_READ_NO_INSERT,
- MDL_SHARED_READ)) &&
+ if ((table_list=
+ lex.first_select_lex()->add_table_to_list(thd, this, NULL, 0,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_READ)) &&
!check_table_access(thd, SELECT_ACL, table_list, TRUE, UINT_MAX, FALSE) &&
!open_tables_only_view_structure(thd, table_list,
thd->mdl_context.has_locks()))
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 1327d2943b5..70607a0cf8e 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -57,7 +57,18 @@
#include "sql_plugin_compat.h"
+#define MAX_SCRAMBLE_LENGTH 1024
+
bool mysql_user_table_is_in_short_password_format= false;
+bool using_global_priv_table= true;
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+// set that from field length in acl_load?
+const uint max_hostname_length= 60;
+const uint max_dbname_length= 64;
+
+#include "sql_acl_getsort.ic"
+#endif
static LEX_CSTRING native_password_plugin_name= {
STRING_WITH_LEN("mysql_native_password")
@@ -84,11 +95,20 @@ LEX_CSTRING current_user= { STRING_WITH_LEN("*current_user") };
LEX_CSTRING current_role= { STRING_WITH_LEN("*current_role") };
LEX_CSTRING current_user_and_current_role= { STRING_WITH_LEN("*current_user_and_current_role") };
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
+
static plugin_ref old_password_plugin;
-#endif
static plugin_ref native_password_plugin;
+static plugin_ref get_auth_plugin(THD *thd, const LEX_CSTRING &name, bool *locked)
+{
+ if (name.str == native_password_plugin_name.str)
+ return native_password_plugin;
+ else if (name.str == old_password_plugin_name.str)
+ return old_password_plugin;
+ *locked=true;
+ return my_plugin_lock_by_name(thd, &name, MYSQL_AUTHENTICATION_PLUGIN);
+}
+
/* Classes */
struct acl_host_and_ip
@@ -105,8 +125,11 @@ static bool compare_hostname(const acl_host_and_ip *, const char *, const char *
class ACL_ACCESS {
public:
- ulong sort;
+ ulonglong sort;
ulong access;
+ ACL_ACCESS()
+ :sort(0), access(0)
+ { }
};
/* ACL_HOST is used if no host is specified */
@@ -118,53 +141,88 @@ public:
char *db;
};
-class ACL_USER_BASE :public ACL_ACCESS
+class ACL_USER_BASE :public ACL_ACCESS, public Sql_alloc
{
public:
- static void *operator new(size_t size, MEM_ROOT *mem_root)
- { return (void*) alloc_root(mem_root, size); }
- static void operator delete(void *, MEM_ROOT *){}
+ ACL_USER_BASE()
+ :flags(0), user(null_clex_str)
+ {
+ bzero(&role_grants, sizeof(role_grants));
+ }
uchar flags; // field used to store various state information
LEX_CSTRING user;
/* list to hold references to granted roles (ACL_ROLE instances) */
DYNAMIC_ARRAY role_grants;
+ const char *get_username() { return user.str; }
};
-class ACL_USER :public ACL_USER_BASE
+class ACL_USER_PARAM
{
public:
+ ACL_USER_PARAM()
+ {
+ bzero(this, sizeof(*this));
+ }
acl_host_and_ip host;
size_t hostname_length;
USER_RESOURCES user_resource;
- uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form
- uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1
enum SSL_type ssl_type;
+ uint password_errors;
const char *ssl_cipher, *x509_issuer, *x509_subject;
- LEX_CSTRING plugin;
- LEX_CSTRING auth_string;
LEX_CSTRING default_rolename;
+ struct AUTH { LEX_CSTRING plugin, auth_string, salt; } *auth;
+ uint nauth;
+ bool account_locked;
+ bool password_expired;
+ my_time_t password_last_changed;
+ longlong password_lifetime;
+
+ bool alloc_auth(MEM_ROOT *root, uint n)
+ {
+ return !(auth= (AUTH*) alloc_root(root, (nauth= n)*sizeof(AUTH)));
+ }
+};
+
+
+class ACL_USER :public ACL_USER_BASE,
+ public ACL_USER_PARAM
+{
+public:
+
+ ACL_USER() { }
+ ACL_USER(THD *thd, const LEX_USER &combo,
+ const Account_options &options,
+ const ulong privileges);
ACL_USER *copy(MEM_ROOT *root)
{
- ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
- if (!dst)
+ ACL_USER *dst;
+ AUTH *dauth;
+ if (!multi_alloc_root(root, &dst, sizeof(ACL_USER),
+ &dauth, sizeof(AUTH)*nauth, NULL))
return 0;
*dst= *this;
- dst->user.str= safe_strdup_root(root, user.str);
- dst->user.length= user.length;
+ dst->user= safe_lexcstrdup_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->auth= dauth;
+ for (uint i=0; i < nauth; i++, dauth++)
+ {
+ if (auth[i].plugin.str == native_password_plugin_name.str ||
+ auth[i].plugin.str == old_password_plugin_name.str)
+ dauth->plugin= auth[i].plugin;
+ else
+ dauth->plugin= safe_lexcstrdup_root(root, auth[i].plugin);
+ dauth->auth_string= safe_lexcstrdup_root(root, auth[i].auth_string);
+ if (auth[i].salt.length == 0)
+ dauth->salt= auth[i].salt;
+ else
+ dauth->salt= safe_lexcstrdup_root(root, auth[i].salt);
+ }
dst->host.hostname= safe_strdup_root(root, host.hostname);
- dst->default_rolename.str= safe_strdup_root(root, default_rolename.str);
- dst->default_rolename.length= default_rolename.length;
+ dst->default_rolename= safe_lexcstrdup_root(root, default_rolename);
bzero(&dst->role_grants, sizeof(role_grants));
return dst;
}
@@ -173,7 +231,7 @@ public:
{
CHARSET_INFO *cs= system_charset_info;
int res;
- res= strcmp(safe_str(user.str), safe_str(user2));
+ res= strcmp(user.str, user2);
if (!res)
res= my_strcasecmp(cs, host.hostname, host2);
return res;
@@ -183,7 +241,7 @@ public:
bool wild_eq(const char *user2, const char *host2, const char *ip2)
{
- if (strcmp(safe_str(user.str), safe_str(user2)))
+ if (strcmp(user.str, user2))
return false;
return compare_hostname(&host, host2, ip2 ? ip2 : host2);
@@ -222,6 +280,8 @@ public:
acl_host_and_ip host;
const char *user,*db;
ulong initial_access; /* access bits present in the table */
+
+ const char *get_username() { return user; }
};
#ifndef DBUG_OFF
@@ -231,9 +291,7 @@ ulong role_global_merges= 0, role_db_merges= 0, role_table_merges= 0,
#endif
#ifndef NO_EMBEDDED_ACCESS_CHECKS
-static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd);
static void update_hostname(acl_host_and_ip *host, const char *hostname);
-static ulong get_sort(uint count,...);
static bool show_proxy_grants (THD *, const char *, const char *,
char *, size_t);
static bool show_role_grants(THD *, const char *,
@@ -275,15 +333,15 @@ public:
const char *proxied_host_arg, const char *proxied_user_arg,
bool with_grant_arg)
{
- user= (user_arg && *user_arg) ? user_arg : NULL;
+ user= user_arg;
update_hostname (&host, (host_arg && *host_arg) ? host_arg : NULL);
- proxied_user= (proxied_user_arg && *proxied_user_arg) ?
- proxied_user_arg : NULL;
+ proxied_user= proxied_user_arg;
update_hostname (&proxied_host,
(proxied_host_arg && *proxied_host_arg) ?
proxied_host_arg : NULL);
with_grant= with_grant_arg;
- sort= get_sort(4, host.hostname, user, proxied_host.hostname, proxied_user);
+ sort= get_magic_sort("huhu", host.hostname, user, proxied_host.hostname,
+ proxied_user);
}
void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
@@ -291,11 +349,10 @@ public:
bool with_grant_arg)
{
init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
- (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
+ strdup_root (mem, user_arg),
(proxied_host_arg && *proxied_host_arg) ?
strdup_root (mem, proxied_host_arg) : NULL,
- (proxied_user_arg && *proxied_user_arg) ?
- strdup_root (mem, proxied_user_arg) : NULL,
+ strdup_root (mem, proxied_user_arg),
with_grant_arg);
}
@@ -308,7 +365,7 @@ public:
const char *get_proxied_host() { return proxied_host.hostname; }
void set_user(MEM_ROOT *mem, const char *user_arg)
{
- user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
+ user= *user_arg ? strdup_root(mem, user_arg) : "";
}
void set_host(MEM_ROOT *mem, const char *host_arg)
{
@@ -323,9 +380,8 @@ public:
{
sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
"ignored in --skip-name-resolve mode.",
- safe_str(proxied_user),
- safe_str(proxied_host.hostname),
- safe_str(user),
+ proxied_user,
+ safe_str(proxied_host.hostname), user,
safe_str(host.hostname));
return TRUE;
}
@@ -345,11 +401,8 @@ public:
proxied_user_arg, proxied_user));
DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) &&
compare_hostname(&proxied_host, host_arg, ip_arg) &&
- (!user ||
- (user_arg && !wild_compare(user_arg, user, TRUE))) &&
- (!proxied_user ||
- (proxied_user && !wild_compare(proxied_user_arg,
- proxied_user, TRUE))));
+ (!*user || !strcmp(user_arg, user)) &&
+ (!*proxied_user || !strcmp(proxied_user_arg, proxied_user)));
}
@@ -381,8 +434,7 @@ public:
bool granted_on(const char *host_arg, const char *user_arg)
{
- return (((!user && (!user_arg || !user_arg[0])) ||
- (user && user_arg && !strcmp(user, user_arg))) &&
+ return (!strcmp(user, user_arg) &&
((!host.hostname && (!host_arg || !host_arg[0])) ||
(host.hostname && host_arg && !strcmp(host.hostname, host_arg))));
}
@@ -391,17 +443,15 @@ public:
void print_grant(String *str)
{
str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
- if (proxied_user)
- str->append(proxied_user, strlen(proxied_user));
+ str->append(proxied_user);
str->append(STRING_WITH_LEN("'@'"));
if (proxied_host.hostname)
str->append(proxied_host.hostname, strlen(proxied_host.hostname));
str->append(STRING_WITH_LEN("' TO '"));
- if (user)
- str->append(user, strlen(user));
+ str->append(user);
str->append(STRING_WITH_LEN("'@'"));
if (host.hostname)
- str->append(host.hostname, strlen(host.hostname));
+ str->append(host.hostname);
str->append(STRING_WITH_LEN("'"));
if (with_grant)
str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
@@ -614,8 +664,11 @@ static DYNAMIC_ARRAY acl_wild_hosts;
static Hash_filo<acl_entry> *acl_cache;
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
-static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
-static ulong get_sort(uint count,...);
+static int acl_compare(const ACL_ACCESS *a, const ACL_ACCESS *b);
+static int acl_user_compare(const ACL_USER *a, const ACL_USER *b);
+static void rebuild_acl_users();
+static int acl_db_compare(const ACL_DB *a, const ACL_DB *b);
+static void rebuild_acl_dbs();
static void init_check_host(void);
static void rebuild_check_host(void);
static void rebuild_role_grants(void);
@@ -624,8 +677,7 @@ static ACL_USER *find_user_wild(const char *host, const char *user, const char *
static ACL_ROLE *find_acl_role(const char *user);
static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, const LEX_CSTRING *h, const LEX_CSTRING *r);
static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host);
-static bool update_user_table(THD *, const User_table &, const char *, const char *, const
- char *, size_t new_password_len);
+static bool update_user_table_password(THD *, const User_table&, const ACL_USER&);
static bool acl_load(THD *thd, const Grant_tables& grant_tables);
static inline void get_grantor(THD *thd, char* grantor);
static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname);
@@ -670,7 +722,6 @@ HASH *Sp_handler_package_body::get_priv_hash() const
*/
enum enum_acl_tables
{
- USER_TABLE,
DB_TABLE,
TABLES_PRIV_TABLE,
COLUMNS_PRIV_TABLE,
@@ -679,7 +730,7 @@ enum enum_acl_tables
PROCS_PRIV_TABLE,
PROXIES_PRIV_TABLE,
ROLES_MAPPING_TABLE,
- TABLES_MAX // <== always the last
+ USER_TABLE // <== always the last
};
static const int Table_user= 1 << USER_TABLE;
@@ -691,6 +742,31 @@ static const int Table_procs_priv= 1 << PROCS_PRIV_TABLE;
static const int Table_proxies_priv= 1 << PROXIES_PRIV_TABLE;
static const int Table_roles_mapping= 1 << ROLES_MAPPING_TABLE;
+static LEX_CSTRING MYSQL_TABLE_NAME[USER_TABLE+1]= {
+ {STRING_WITH_LEN("db")},
+ {STRING_WITH_LEN("tables_priv")},
+ {STRING_WITH_LEN("columns_priv")},
+ {STRING_WITH_LEN("host")},
+ {STRING_WITH_LEN("procs_priv")},
+ {STRING_WITH_LEN("proxies_priv")},
+ {STRING_WITH_LEN("roles_mapping")},
+ {STRING_WITH_LEN("global_priv")}
+};
+static LEX_CSTRING MYSQL_TABLE_NAME_USER={STRING_WITH_LEN("user")};
+
+/**
+ Choose from either native or old password plugins when assigning a password
+*/
+
+static LEX_CSTRING &guess_auth_plugin(THD *thd, size_t password_len)
+{
+ if (thd->variables.old_passwords == 1 ||
+ password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
+ return old_password_plugin_name;
+ else
+ return native_password_plugin_name;
+}
+
/**
Base class representing a generic grant table from the mysql database.
@@ -705,104 +781,52 @@ class Grant_table_base
{
public:
/* Number of fields for this Grant Table. */
- uint num_fields() const { return tl.table->s->fields; }
+ uint num_fields() const { return m_table->s->fields; }
/* Check if the table exists after an attempt to open it was made.
Some tables, such as the host table in MySQL 5.6.7+ are missing. */
- bool table_exists() const { return tl.table; };
+ bool table_exists() const { return m_table; };
/* Initializes the READ_RECORD structure provided as a parameter
to read through the whole table, with all columns available. Cleaning up
is the caller's job. */
- bool init_read_record(READ_RECORD* info, THD* thd) const
+ bool init_read_record(READ_RECORD* info) const
{
- DBUG_ASSERT(tl.table);
- bool result= ::init_read_record(info, thd, tl.table, NULL, NULL, 1,
- true, false);
- if (!result)
- tl.table->use_all_columns();
- return result;
- }
+ DBUG_ASSERT(m_table);
- /* Return the number of privilege columns for this table. */
- uint num_privileges() const { return num_privilege_cols; }
- /* Return a privilege column by index. */
- Field* priv_field(uint privilege_idx) const
- {
- DBUG_ASSERT(privilege_idx < num_privileges());
- return tl.table->field[start_privilege_column + privilege_idx];
- }
+ if (num_fields() < min_columns)
+ {
+ my_printf_error(ER_UNKNOWN_ERROR, "Fatal error: mysql.%s table is "
+ "damaged or in unsupported 3.20 format",
+ MYF(ME_ERROR_LOG), m_table->s->table_name.str);
+ return 1;
+ }
- /* Fetch the privileges from the table as a set of bits. The first column
- is represented by the first bit in the result, the second column by the
- second bit, etc. */
- ulong get_access() const
- {
- return get_access(start_privilege_column,
- start_privilege_column + num_privileges() - 1);
+ bool result= ::init_read_record(info, m_table->in_use, m_table,
+ NULL, NULL, 1, true, false);
+ if (!result)
+ m_table->use_all_columns();
+ return result;
}
/* Return the underlying TABLE handle. */
- TABLE* table() const
- {
- return tl.table;
- }
-
- /** Check if the table was opened, issue an error otherwise. */
- int no_such_table() const
- {
- if (table_exists())
- return 0;
+ TABLE* table() const { return m_table; }
- my_error(ER_NO_SUCH_TABLE, MYF(0), tl.db.str, tl.alias.str);
- return 1;
- }
-
-
- protected:
- friend class Grant_tables;
-
- Grant_table_base() : start_privilege_column(0), num_privilege_cols(0)
- {
- tl.reset();
- };
-
- /* Initialization sequence common for all grant tables. This should be called
- after all table-specific initialization is performed. */
- void init(enum thr_lock_type lock_type, bool is_optional)
- {
- tl.open_type= OT_BASE_ONLY;
- tl.i_s_requested_object= OPEN_TABLE_ONLY;
- if (is_optional)
- tl.open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
- }
-
- /*
- Get all access bits from table between start_field and end_field indices.
-
- IMPLEMENTATION
- The record should be already read in table->record[0]. All privileges
- are specified as an ENUM(Y,N).
-
- SYNOPSIS
- get_access()
- start_field_idx The field index at which the first privilege
- specification begins.
- end_field_idx The field index at which the last privilege
- specification is located.
-
- RETURN VALUE
- privilege mask
- */
- ulong get_access(uint start_field_idx, uint end_field_idx) const
+ ulong get_access() const
{
ulong access_bits= 0, bit= 1;
- for (uint i = start_field_idx; i <= end_field_idx; i++, bit<<=1)
+ for (uint i = start_priv_columns; i < end_priv_columns; i++, bit<<=1)
{
- if (get_YN_as_bool(tl.table->field[i]))
+ if (get_YN_as_bool(m_table->field[i]))
access_bits|= bit;
}
return access_bits;
}
+ protected:
+ friend class Grant_tables;
+
+ Grant_table_base() : min_columns(3), start_priv_columns(0), end_priv_columns(0), m_table(0)
+ { }
+
/* Compute how many privilege columns this table has. This method
can only be called after the table has been opened.
@@ -810,102 +834,165 @@ class Grant_table_base
A privilege column is of type enum('Y', 'N'). Privilege columns are
expected to be one after another.
*/
- void compute_num_privilege_cols()
+ void set_table(TABLE *table)
{
- if (!table_exists()) // Table does not exist or not opened.
+ if (!(m_table= table)) // Table does not exist or not opened.
return;
- num_privilege_cols= 0;
- for (uint i= 0; i < num_fields(); i++)
+ for (end_priv_columns= 0; end_priv_columns < num_fields(); end_priv_columns++)
{
- Field *field= tl.table->field[i];
- if (num_privilege_cols > 0 && field->real_type() != MYSQL_TYPE_ENUM)
- return;
+ Field *field= m_table->field[end_priv_columns];
if (field->real_type() == MYSQL_TYPE_ENUM &&
static_cast<Field_enum*>(field)->typelib->count == 2)
{
- num_privilege_cols++;
- if (num_privilege_cols == 1)
- start_privilege_column= i;
+ if (!start_priv_columns)
+ start_priv_columns= end_priv_columns;
}
+ else if (start_priv_columns)
+ break;
}
}
+
+ /* the min number of columns a table should have */
+ uint min_columns;
/* The index at which privilege columns start. */
- uint start_privilege_column;
- /* The number of privilege columns in the table. */
- uint num_privilege_cols;
+ uint start_priv_columns;
+ /* The index after the last privilege column */
+ uint end_priv_columns;
- TABLE_LIST tl;
+ TABLE *m_table;
};
class User_table: public Grant_table_base
{
public:
- /* Field getters return NULL if the column is not present in the table.
- This is consistent only if the table is in a supported version. We do
- not guard against corrupt tables. (yet) */
- Field* host() const
- { return get_field(0); }
- Field* user() const
- { return get_field(1); }
- Field* password() const
- { return have_password() ? NULL : tl.table->field[2]; }
- /* Columns after privilege columns. */
- Field* ssl_type() const
- { return get_field(start_privilege_column + num_privileges()); }
- Field* ssl_cipher() const
- { return get_field(start_privilege_column + num_privileges() + 1); }
- Field* x509_issuer() const
- { return get_field(start_privilege_column + num_privileges() + 2); }
- Field* x509_subject() const
- { return get_field(start_privilege_column + num_privileges() + 3); }
- Field* max_questions() const
- { return get_field(start_privilege_column + num_privileges() + 4); }
- Field* max_updates() const
- { return get_field(start_privilege_column + num_privileges() + 5); }
- Field* max_connections() const
- { return get_field(start_privilege_column + num_privileges() + 6); }
- Field* max_user_connections() const
- { return get_field(start_privilege_column + num_privileges() + 7); }
- Field* plugin() const
- { return get_field(start_privilege_column + num_privileges() + 8); }
- Field* authentication_string() const
- { return get_field(start_privilege_column + num_privileges() + 9); }
- Field* password_expired() const
- { return get_field(start_privilege_column + num_privileges() + 10); }
- Field* is_role() const
- { return get_field(start_privilege_column + num_privileges() + 11); }
- Field* default_role() const
- { return get_field(start_privilege_column + num_privileges() + 12); }
- Field* max_statement_time() const
- { return get_field(start_privilege_column + num_privileges() + 13); }
-
- /*
- Check if a user entry in the user table is marked as being a role entry
+ bool init_read_record(READ_RECORD* info) const
+ {
+ return Grant_table_base::init_read_record(info) || setup_sysvars();
+ }
+
+ virtual LEX_CSTRING& name() const = 0;
+ virtual int get_auth(THD *, MEM_ROOT *, ACL_USER *u) const= 0;
+ virtual bool set_auth(const ACL_USER &u) const = 0;
+ virtual ulong get_access() const = 0;
+ virtual void set_access(ulong rights, bool revoke) const = 0;
+
+ char *get_host(MEM_ROOT *root) const
+ { return ::get_field(root, m_table->field[0]); }
+ int set_host(const char *s, size_t l) const
+ { return m_table->field[0]->store(s, l, system_charset_info); };
+ char *get_user(MEM_ROOT *root) const
+ { return ::get_field(root, m_table->field[1]); }
+ int set_user(const char *s, size_t l) const
+ { return m_table->field[1]->store(s, l, system_charset_info); };
+
+ virtual SSL_type get_ssl_type () const = 0;
+ virtual int set_ssl_type (SSL_type x) const = 0;
+ virtual const char* get_ssl_cipher (MEM_ROOT *root) const = 0;
+ virtual int set_ssl_cipher (const char *s, size_t l) const = 0;
+ virtual const char* get_x509_issuer (MEM_ROOT *root) const = 0;
+ virtual int set_x509_issuer (const char *s, size_t l) const = 0;
+ virtual const char* get_x509_subject (MEM_ROOT *root) const = 0;
+ virtual int set_x509_subject (const char *s, size_t l) const = 0;
+ virtual longlong get_max_questions () const = 0;
+ virtual int set_max_questions (longlong x) const = 0;
+ virtual longlong get_max_updates () const = 0;
+ virtual int set_max_updates (longlong x) const = 0;
+ virtual longlong get_max_connections () const = 0;
+ virtual int set_max_connections (longlong x) const = 0;
+ virtual longlong get_max_user_connections () const = 0;
+ virtual int set_max_user_connections (longlong x) const = 0;
+ virtual double get_max_statement_time () const = 0;
+ virtual int set_max_statement_time (double x) const = 0;
+ virtual bool get_is_role () const = 0;
+ virtual int set_is_role (bool x) const = 0;
+ virtual const char* get_default_role (MEM_ROOT *root) const = 0;
+ virtual int set_default_role (const char *s, size_t l) const = 0;
+ virtual bool get_account_locked () const = 0;
+ virtual int set_account_locked (bool x) const = 0;
+ virtual bool get_password_expired () const = 0;
+ virtual int set_password_expired (bool x) const = 0;
+ virtual my_time_t get_password_last_changed () const = 0;
+ virtual int set_password_last_changed (my_time_t x) const = 0;
+ virtual longlong get_password_lifetime () const = 0;
+ virtual int set_password_lifetime (longlong x) const = 0;
+
+ virtual ~User_table() {}
+ private:
+ friend class Grant_tables;
+ virtual int setup_sysvars() const = 0;
+};
- IMPLEMENTATION
- Access the coresponding column and check the coresponding ENUM of the form
- ENUM('N', 'Y')
+/* MySQL-3.23 to MariaDB 10.3 `user` table */
+class User_table_tabular: public User_table
+{
+ public:
- SYNOPSIS
- check_is_role()
- form an open table to read the entry from.
- The record should be already read in table->record[0]
+ LEX_CSTRING& name() const { return MYSQL_TABLE_NAME_USER; }
- RETURN VALUE
- TRUE if the user is marked as a role
- FALSE otherwise
- */
- bool check_is_role() const
+ int get_auth(THD *thd, MEM_ROOT *root, ACL_USER *u) const
{
- /* Table version does not support roles */
- if (!is_role())
- return false;
-
- return get_YN_as_bool(is_role());
+ u->alloc_auth(root, 1);
+ if (have_password())
+ {
+ const char *as= safe_str(::get_field(&acl_memroot, password()));
+ u->auth->auth_string.str= as;
+ u->auth->auth_string.length= strlen(as);
+ u->auth->plugin= guess_auth_plugin(thd, u->auth->auth_string.length);
+ }
+ else
+ {
+ u->auth->plugin= native_password_plugin_name;
+ u->auth->auth_string= empty_clex_str;
+ }
+ if (plugin() && authstr())
+ {
+ char *tmpstr= ::get_field(&acl_memroot, plugin());
+ if (tmpstr)
+ {
+ const char *pw= u->auth->auth_string.str;
+ const char *as= safe_str(::get_field(&acl_memroot, authstr()));
+ if (*pw)
+ {
+ if (*as && strcmp(as, pw))
+ {
+ sql_print_warning("'user' entry '%s@%s' has both a password and an "
+ "authentication plugin specified. The password will be ignored.",
+ safe_str(get_user(thd->mem_root)), safe_str(get_host(thd->mem_root)));
+ }
+ else
+ as= pw;
+ }
+ u->auth->plugin.str= tmpstr;
+ u->auth->plugin.length= strlen(tmpstr);
+ u->auth->auth_string.str= as;
+ u->auth->auth_string.length= strlen(as);
+ }
+ }
+ return 0;
}
+ bool set_auth(const ACL_USER &u) const
+ {
+ if (u.nauth != 1)
+ return 1;
+ if (plugin())
+ {
+ if (have_password())
+ password()->reset();
+ plugin()->store(u.auth->plugin.str, u.auth->plugin.length, system_charset_info);
+ authstr()->store(u.auth->auth_string.str, u.auth->auth_string.length, system_charset_info);
+ }
+ else
+ {
+ if (u.auth->plugin.str != native_password_plugin_name.str &&
+ u.auth->plugin.str != old_password_plugin_name.str)
+ return 1;
+ password()->store(u.auth->auth_string.str, u.auth->auth_string.length, system_charset_info);
+ }
+ return 0;
+ }
ulong get_access() const
{
@@ -921,16 +1008,10 @@ class User_table: public Grant_table_base
if (access & PROCESS_ACL)
access|= SUPER_ACL | EXECUTE_ACL;
}
- /*
- If it is pre 5.0.1 privilege table then map CREATE privilege on
- CREATE VIEW & SHOW VIEW privileges.
- */
+
if (num_fields() <= 31 && (access & CREATE_ACL))
access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
- /*
- If it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
- CREATE PROCEDURE & ALTER PROCEDURE privileges.
- */
+
if (num_fields() <= 33)
{
if (access & CREATE_ACL)
@@ -938,20 +1019,13 @@ class User_table: public Grant_table_base
if (access & ALTER_ACL)
access|= ALTER_PROC_ACL;
}
- /*
- Pre 5.0.3 did not have CREATE_USER_ACL.
- */
+
if (num_fields() <= 36 && (access & GRANT_ACL))
access|= CREATE_USER_ACL;
- /*
- If it is pre 5.1.6 privilege table then map CREATE privilege on
- CREATE|ALTER|DROP|EXECUTE EVENT.
- */
+
if (num_fields() <= 37 && (access & SUPER_ACL))
access|= EVENT_ACL;
- /*
- If it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
- */
+
if (num_fields() <= 38 && (access & SUPER_ACL))
access|= TRIGGER_ACL;
@@ -961,19 +1035,227 @@ class User_table: public Grant_table_base
return access & GLOBAL_ACLS;
}
- private:
- friend class Grant_tables;
+ void set_access(ulong rights, bool revoke) const
+ {
+ ulong priv= SELECT_ACL;
+ for (uint i= start_priv_columns; i < end_priv_columns; i++, priv <<= 1)
+ {
+ if (priv & rights)
+ m_table->field[i]->store(1 + !revoke, 0);
+ }
+ }
- /* Only Grant_tables can instantiate this class. */
- User_table() {};
+ SSL_type get_ssl_type () const
+ {
+ Field *f= get_field(end_priv_columns, MYSQL_TYPE_ENUM);
+ return (SSL_type)(f ? f->val_int()-1 : 0);
+ }
+ int set_ssl_type (SSL_type x) const
+ {
+ if (Field *f= get_field(end_priv_columns, MYSQL_TYPE_ENUM))
+ return f->store(x+1, 0);
+ else
+ return 1;
+ }
+ const char* get_ssl_cipher (MEM_ROOT *root) const
+ {
+ Field *f= get_field(end_priv_columns + 1, MYSQL_TYPE_BLOB);
+ return f ? ::get_field(root,f) : 0;
+ }
+ int set_ssl_cipher (const char *s, size_t l) const
+ {
+ if (Field *f= get_field(end_priv_columns + 1, MYSQL_TYPE_BLOB))
+ return f->store(s, l, &my_charset_latin1);
+ else
+ return 1;
+ }
+ const char* get_x509_issuer (MEM_ROOT *root) const
+ {
+ Field *f= get_field(end_priv_columns + 2, MYSQL_TYPE_BLOB);
+ return f ? ::get_field(root,f) : 0;
+ }
+ int set_x509_issuer (const char *s, size_t l) const
+ {
+ if (Field *f= get_field(end_priv_columns + 2, MYSQL_TYPE_BLOB))
+ return f->store(s, l, &my_charset_latin1);
+ else
+ return 1;
+ }
+ const char* get_x509_subject (MEM_ROOT *root) const
+ {
+ Field *f= get_field(end_priv_columns + 3, MYSQL_TYPE_BLOB);
+ return f ? ::get_field(root,f) : 0;
+ }
+ int set_x509_subject (const char *s, size_t l) const
+ {
+ if (Field *f= get_field(end_priv_columns + 3, MYSQL_TYPE_BLOB))
+ return f->store(s, l, &my_charset_latin1);
+ else
+ return 1;
+ }
+ longlong get_max_questions () const
+ {
+ Field *f= get_field(end_priv_columns + 4, MYSQL_TYPE_LONG);
+ return f ? f->val_int() : 0;
+ }
+ int set_max_questions (longlong x) const
+ {
+ if (Field *f= get_field(end_priv_columns + 4, MYSQL_TYPE_LONG))
+ return f->store(x, 0);
+ else
+ return 1;
+ }
+ longlong get_max_updates () const
+ {
+ Field *f= get_field(end_priv_columns + 5, MYSQL_TYPE_LONG);
+ return f ? f->val_int() : 0;
+ }
+ int set_max_updates (longlong x) const
+ {
+ if (Field *f= get_field(end_priv_columns + 5, MYSQL_TYPE_LONG))
+ return f->store(x, 0);
+ else
+ return 1;
+ }
+ longlong get_max_connections () const
+ {
+ Field *f= get_field(end_priv_columns + 6, MYSQL_TYPE_LONG);
+ return f ? f->val_int() : 0;
+ }
+ int set_max_connections (longlong x) const
+ {
+ if (Field *f= get_field(end_priv_columns + 6, MYSQL_TYPE_LONG))
+ return f->store(x, 0);
+ else
+ return 1;
+ }
+ longlong get_max_user_connections () const
+ {
+ Field *f= get_field(end_priv_columns + 7, MYSQL_TYPE_LONG);
+ return f ? f->val_int() : 0;
+ }
+ int set_max_user_connections (longlong x) const
+ {
+ if (Field *f= get_field(end_priv_columns + 7, MYSQL_TYPE_LONG))
+ return f->store(x, 0);
+ else
+ return 1;
+ }
+ double get_max_statement_time () const
+ {
+ Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_NEWDECIMAL);
+ return f ? f->val_real() : 0;
+ }
+ int set_max_statement_time (double x) const
+ {
+ if (Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_NEWDECIMAL))
+ return f->store(x);
+ else
+ return 1;
+ }
+ bool get_is_role () const
+ {
+ Field *f= get_field(end_priv_columns + 11, MYSQL_TYPE_ENUM);
+ return f ? f->val_int()-1 : 0;
+ }
+ int set_is_role (bool x) const
+ {
+ if (Field *f= get_field(end_priv_columns + 11, MYSQL_TYPE_ENUM))
+ return f->store(x+1, 0);
+ else
+ return 1;
+ }
+ const char* get_default_role (MEM_ROOT *root) const
+ {
+ Field *f= get_field(end_priv_columns + 12, MYSQL_TYPE_STRING);
+ return f ? ::get_field(root,f) : 0;
+ }
+ int set_default_role (const char *s, size_t l) const
+ {
+ if (Field *f= get_field(end_priv_columns + 12, MYSQL_TYPE_STRING))
+ return f->store(s, l, system_charset_info);
+ else
+ return 1;
+ }
+ /* On a MariaDB 10.3 user table, the account locking accessors will try to
+ get the content of the max_statement_time column, but they will fail due
+ to the typecheck in get_field. */
+ bool get_account_locked () const
+ {
+ Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_ENUM);
+ return f ? f->val_int()-1 : 0;
+ }
+ int set_account_locked (bool x) const
+ {
+ if (Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_ENUM))
+ return f->store(x+1, 0);
+
+ return 1;
+ }
+
+ bool get_password_expired () const
+ {
+ uint field_num= end_priv_columns + 10;
+
+ Field *f= get_field(field_num, MYSQL_TYPE_ENUM);
+ return f ? f->val_int()-1 : 0;
+ }
+ int set_password_expired (bool x) const
+ {
+ uint field_num= end_priv_columns + 10;
- void init(enum thr_lock_type lock_type)
+ if (Field *f= get_field(field_num, MYSQL_TYPE_ENUM))
+ return f->store(x+1, 0);
+ return 1;
+ }
+ my_time_t get_password_last_changed () const
+ {
+ ulong unused_dec;
+ if (Field *f= get_field(end_priv_columns + 11, MYSQL_TYPE_TIMESTAMP2))
+ return f->get_timestamp(&unused_dec);
+ return 0;
+ }
+ int set_password_last_changed (my_time_t x) const
+ {
+ if (Field *f= get_field(end_priv_columns + 11, MYSQL_TYPE_TIMESTAMP2))
+ {
+ f->set_notnull();
+ return f->store_timestamp(x, 0);
+ }
+ return 1;
+ }
+ longlong get_password_lifetime () const
+ {
+ if (Field *f= get_field(end_priv_columns + 12, MYSQL_TYPE_SHORT))
+ {
+ if (f->is_null())
+ return -1;
+ return f->val_int();
+ }
+ return 0;
+ }
+ int set_password_lifetime (longlong x) const
{
- /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, lock_type);
- Grant_table_base::init(lock_type, false);
+ if (Field *f= get_field(end_priv_columns + 12, MYSQL_TYPE_SHORT))
+ {
+ if (x < 0)
+ {
+ f->set_null();
+ return 0;
+ }
+ f->set_notnull();
+ return f->store(x, 0);
+ }
+ return 1;
}
+ virtual ~User_table_tabular() {}
+ private:
+ friend class Grant_tables;
+
+ /* Only Grant_tables can instantiate this class. */
+ User_table_tabular() { min_columns= 13; /* As in 3.20.13 */ }
+
/* The user table is a bit different compared to the other Grant tables.
Usually, we only add columns to the grant tables when adding functionality.
This makes it easy to test which version of the table we are using, by
@@ -984,183 +1266,544 @@ class User_table: public Grant_table_base
doesn't exist. This simplifies checking of table "version", as we don't
have to make use of num_fields() any more.
*/
- inline Field* get_field(uint field_num) const
+ inline Field* get_field(uint field_num, enum enum_field_types type) const
{
if (field_num >= num_fields())
return NULL;
+ Field *f= m_table->field[field_num];
+ return f->real_type() == type ? f : NULL;
+ }
+
+ int setup_sysvars() const
+ {
+ username_char_length= MY_MIN(m_table->field[1]->char_length(),
+ USERNAME_CHAR_LENGTH);
+ using_global_priv_table= false;
+
+ if (have_password()) // Password column might be missing. (MySQL 5.7.6+)
+ {
+ int password_length= password()->field_length /
+ password()->charset()->mbmaxlen;
+ if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
+ {
+ sql_print_error("Fatal error: mysql.user table is damaged or in "
+ "unsupported 3.20 format.");
+ return 1;
+ }
- return tl.table->field[field_num];
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
+ {
+ if (opt_secure_auth)
+ {
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ sql_print_error("Fatal error: mysql.user table is in old format, "
+ "but server started with --secure-auth option.");
+ return 1;
+ }
+ mysql_user_table_is_in_short_password_format= true;
+ if (global_system_variables.old_passwords)
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ else
+ {
+ extern sys_var *Sys_old_passwords_ptr;
+ Sys_old_passwords_ptr->value_origin= sys_var::AUTO;
+ global_system_variables.old_passwords= 1;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ sql_print_warning("mysql.user table is not updated to new password format; "
+ "Disabling new password usage until "
+ "mysql_fix_privilege_tables is run");
+ }
+ m_table->in_use->variables.old_passwords= 1;
+ }
+ else
+ {
+ mysql_user_table_is_in_short_password_format= false;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ }
+ }
+ return 0;
}
/* Normally password column is the third column in the table. If privileges
start on the third column instead, we are missing the password column.
This means we are using a MySQL 5.7.6+ data directory. */
- bool have_password() const { return start_privilege_column == 2; }
+ bool have_password() const { return start_priv_columns == 3; }
+
+ Field* password() const { return m_table->field[2]; }
+ Field* plugin() const { return get_field(end_priv_columns + 8, MYSQL_TYPE_STRING); }
+ Field* authstr() const { return get_field(end_priv_columns + 9, MYSQL_TYPE_BLOB); }
+};
+
+/*
+ MariaDB 10.4 and up `global_priv` table
+
+ TODO possible optimizations:
+ * update json in-place if the new value can fit
+ * don't repeat get_value for every key, but use a streaming parser
+ to convert json into in-memory object (ACL_USER?) in one json scan.
+ - this makes sense for acl_load(), but hardly for GRANT
+ * similarly, pack ACL_USER (?) into json in one go.
+ - doesn't make sense? GRANT rarely updates more than one field.
+*/
+class User_table_json: public User_table
+{
+ LEX_CSTRING& name() const { return MYSQL_TABLE_NAME[USER_TABLE]; }
+
+ int get_auth(THD *thd, MEM_ROOT *root, ACL_USER *u) const
+ {
+ size_t array_len;
+ const char *array;
+ int vl;
+ const char *v;
+
+ if (get_value("auth_or", JSV_ARRAY, &array, &array_len))
+ {
+ u->alloc_auth(root, 1);
+ return get_auth1(thd, root, u, 0);
+ }
+
+ if (json_get_array_item(array, array + array_len, (int)array_len,
+ &v, &vl) != JSV_NOTHING)
+ return 1;
+ u->alloc_auth(root, vl);
+ for (uint i=0; i < u->nauth; i++)
+ {
+ if (json_get_array_item(array, array + array_len, i, &v, &vl) != JSV_OBJECT)
+ return 1;
+
+ const char *p, *a;
+ int pl, al;
+ switch (json_get_object_key(v, v + vl, "plugin", &p, &pl)) {
+ case JSV_STRING: u->auth[i].plugin.str= strmake_root(root, p, pl);
+ u->auth[i].plugin.length= pl;
+ break;
+ case JSV_NOTHING: if (get_auth1(thd, root, u, i))
+ return 1;
+ else
+ continue;
+ default: return 1;
+ }
+ switch (json_get_object_key(v, v + vl, "authentication_string", &a, &al)) {
+ case JSV_NOTHING: u->auth[i].auth_string= empty_clex_str;
+ break;
+ case JSV_STRING: u->auth[i].auth_string.str= strmake_root(root, a, al);
+ u->auth[i].auth_string.length= al;
+ break;
+ default: return 1;
+ }
+ }
+ return 0;
+ }
+
+ int get_auth1(THD *thd, MEM_ROOT *root, ACL_USER *u, uint n) const
+ {
+ const char *authstr= get_str_value(root, "authentication_string");
+ const char *plugin= get_str_value(root, "plugin");
+ if (plugin && authstr)
+ {
+ if (plugin && *plugin)
+ {
+ u->auth[n].plugin.str= plugin;
+ u->auth[n].plugin.length= strlen(plugin);
+ }
+ else
+ u->auth[n].plugin= native_password_plugin_name;
+ u->auth[n].auth_string.str= authstr;
+ u->auth[n].auth_string.length= strlen(authstr);
+ return 0;
+ }
+ return 1;
+ }
+
+ bool append_str_value(String *to, const LEX_CSTRING &str) const
+ {
+ to->append('"');
+ to->reserve(str.length*2);
+ int len= json_escape(system_charset_info, (uchar*)str.str, (uchar*)str.str + str.length,
+ to->charset(), (uchar*)to->end(), (uchar*)to->end() + str.length*2);
+ if (len < 0)
+ return 1;
+ to->length(to->length() + len);
+ to->append('"');
+ return 0;
+ }
+
+ bool set_auth(const ACL_USER &u) const
+ {
+ size_t array_len;
+ const char *array;
+ if (u.nauth == 1 && get_value("auth_or", JSV_ARRAY, &array, &array_len))
+ return set_auth1(u, 0);
+
+ StringBuffer<JSON_SIZE> json(m_table->field[2]->charset());
+ bool top_done = false;
+ json.append('[');
+ for (uint i=0; i < u.nauth; i++)
+ {
+ ACL_USER::AUTH * const auth= u.auth + i;
+ if (i)
+ json.append(',');
+ json.append('{');
+ if (!top_done &&
+ (auth->plugin.str == native_password_plugin_name.str ||
+ auth->plugin.str == old_password_plugin_name.str ||
+ i == u.nauth - 1))
+ {
+ if (set_auth1(u, i))
+ return 1;
+ top_done= true;
+ }
+ else
+ {
+ json.append(STRING_WITH_LEN("\"plugin\":"));
+ if (append_str_value(&json, auth->plugin))
+ return 1;
+ if (auth->auth_string.length)
+ {
+ json.append(STRING_WITH_LEN(",\"authentication_string\":"));
+ if (append_str_value(&json, auth->auth_string))
+ return 1;
+ }
+ }
+ json.append('}');
+ }
+ json.append(']');
+ return set_value("auth_or", json.ptr(), json.length(), false) == JSV_BAD_JSON;
+ }
+ bool set_auth1(const ACL_USER &u, uint i) const
+ {
+ return set_str_value("plugin",
+ u.auth[i].plugin.str, u.auth[i].plugin.length) ||
+ set_str_value("authentication_string",
+ u.auth[i].auth_string.str, u.auth[i].auth_string.length);
+ }
+ ulong get_access() const
+ {
+ /*
+ when new privileges will be added, we'll start storing GLOBAL_ACLS
+ (or, for example, my_count_bits(GLOBAL_ACLS))
+ in the json too, and it'll allow us to do privilege upgrades
+ */
+ return get_int_value("access") & GLOBAL_ACLS;
+ }
+ void set_access(ulong rights, bool revoke) const
+ {
+ ulong access= get_access();
+ if (revoke)
+ access&= ~rights;
+ else
+ access|= rights;
+ set_int_value("access", access & GLOBAL_ACLS);
+ }
+ const char *unsafe_str(const char *s) const
+ { return s[0] ? s : NULL; }
+
+ SSL_type get_ssl_type () const
+ { return (SSL_type)get_int_value("ssl_type"); }
+ int set_ssl_type (SSL_type x) const
+ { return set_int_value("ssl_type", x); }
+ const char* get_ssl_cipher (MEM_ROOT *root) const
+ { return unsafe_str(get_str_value(root, "ssl_cipher")); }
+ int set_ssl_cipher (const char *s, size_t l) const
+ { return set_str_value("ssl_cipher", s, l); }
+ const char* get_x509_issuer (MEM_ROOT *root) const
+ { return unsafe_str(get_str_value(root, "x509_issuer")); }
+ int set_x509_issuer (const char *s, size_t l) const
+ { return set_str_value("x509_issuer", s, l); }
+ const char* get_x509_subject (MEM_ROOT *root) const
+ { return unsafe_str(get_str_value(root, "x509_subject")); }
+ int set_x509_subject (const char *s, size_t l) const
+ { return set_str_value("x509_subject", s, l); }
+ longlong get_max_questions () const
+ { return get_int_value("max_questions"); }
+ int set_max_questions (longlong x) const
+ { return set_int_value("max_questions", x); }
+ longlong get_max_updates () const
+ { return get_int_value("max_updates"); }
+ int set_max_updates (longlong x) const
+ { return set_int_value("max_updates", x); }
+ longlong get_max_connections () const
+ { return get_int_value("max_connections"); }
+ int set_max_connections (longlong x) const
+ { return set_int_value("max_connections", x); }
+ longlong get_max_user_connections () const
+ { return get_int_value("max_user_connections"); }
+ int set_max_user_connections (longlong x) const
+ { return set_int_value("max_user_connections", x); }
+ double get_max_statement_time () const
+ { return get_double_value("max_statement_time"); }
+ int set_max_statement_time (double x) const
+ { return set_double_value("max_statement_time", x); }
+ bool get_is_role () const
+ { return get_bool_value("is_role"); }
+ int set_is_role (bool x) const
+ { return set_bool_value("is_role", x); }
+ const char* get_default_role (MEM_ROOT *root) const
+ { return get_str_value(root, "default_role"); }
+ int set_default_role (const char *s, size_t l) const
+ { return set_str_value("default_role", s, l); }
+ bool get_account_locked () const
+ { return get_bool_value("account_locked"); }
+ int set_account_locked (bool x) const
+ { return set_bool_value("account_locked", x); }
+ my_time_t get_password_last_changed () const
+ { return static_cast<my_time_t>(get_int_value("password_last_changed")); }
+ int set_password_last_changed (my_time_t x) const
+ { return set_int_value("password_last_changed", static_cast<longlong>(x)); }
+ int set_password_lifetime (longlong x) const
+ { return set_int_value("password_lifetime", x); }
+ longlong get_password_lifetime () const
+ { return get_int_value("password_lifetime", -1); }
+ /*
+ password_last_changed=0 means the password is manually expired.
+ In MySQL 5.7+ this state is described using the password_expired column
+ in mysql.user
+ */
+ bool get_password_expired () const
+ { return get_int_value("password_last_changed", -1) == 0; }
+ int set_password_expired (bool x) const
+ { return x ? set_password_last_changed(0) : 0; }
+ ~User_table_json() {}
+ private:
+ friend class Grant_tables;
+ static const uint JSON_SIZE=1024;
+ int setup_sysvars() const
+ {
+ using_global_priv_table= true;
+ username_char_length= MY_MIN(m_table->field[1]->char_length(),
+ USERNAME_CHAR_LENGTH);
+ return 0;
+ }
+ bool get_value(const char *key,
+ enum json_types vt, const char **v, size_t *vl) const
+ {
+ enum json_types value_type;
+ int int_vl;
+ String str, *res= m_table->field[2]->val_str(&str);
+ if (!res ||
+ (value_type= json_get_object_key(res->ptr(), res->end(), key,
+ v, &int_vl)) == JSV_BAD_JSON)
+ return 1; // invalid
+ *vl= int_vl;
+ return value_type != vt;
+ }
+ const char *get_str_value(MEM_ROOT *root, const char *key) const
+ {
+ size_t value_len;
+ const char *value_start;
+ if (get_value(key, JSV_STRING, &value_start, &value_len))
+ return "";
+ char *ptr= (char*)alloca(value_len);
+ int len= json_unescape(m_table->field[2]->charset(),
+ (const uchar*)value_start,
+ (const uchar*)value_start + value_len,
+ system_charset_info,
+ (uchar*)ptr, (uchar*)ptr + value_len);
+ if (len < 0)
+ return NULL;
+ return strmake_root(root, ptr, len);
+ }
+ longlong get_int_value(const char *key, longlong def_val= 0) const
+ {
+ int err;
+ size_t value_len;
+ const char *value_start;
+ if (get_value(key, JSV_NUMBER, &value_start, &value_len))
+ return def_val;
+ const char *value_end= value_start + value_len;
+ return my_strtoll10(value_start, (char**)&value_end, &err);
+ }
+ double get_double_value(const char *key) const
+ {
+ int err;
+ size_t value_len;
+ const char *value_start;
+ if (get_value(key, JSV_NUMBER, &value_start, &value_len))
+ return 0;
+ const char *value_end= value_start + value_len;
+ return my_strtod(value_start, (char**)&value_end, &err);
+ }
+ bool get_bool_value(const char *key) const
+ {
+ size_t value_len;
+ const char *value_start;
+ if (get_value(key, JSV_TRUE, &value_start, &value_len))
+ return false;
+ return true;
+ }
+ enum json_types set_value(const char *key,
+ const char *val, size_t vlen, bool string) const
+ {
+ int value_len;
+ const char *value_start;
+ enum json_types value_type;
+ String str, *res= m_table->field[2]->val_str(&str);
+ if (!res || !res->length())
+ (res= &str)->set(STRING_WITH_LEN("{}"), m_table->field[2]->charset());
+ value_type= json_get_object_key(res->ptr(), res->end(), key,
+ &value_start, &value_len);
+ if (value_type == JSV_BAD_JSON)
+ return value_type; // invalid
+ StringBuffer<JSON_SIZE> json(res->charset());
+ json.copy(res->ptr(), value_start - res->ptr(), res->charset());
+ if (value_type == JSV_NOTHING)
+ {
+ if (value_len)
+ json.append(',');
+ json.append('"');
+ json.append(key);
+ json.append(STRING_WITH_LEN("\":"));
+ if (string)
+ json.append('"');
+ }
+ else
+ value_start+= value_len;
+ json.append(val, vlen);
+ if (!value_type && string)
+ json.append('"');
+ json.append(value_start, res->end() - value_start);
+ DBUG_ASSERT(json_valid(json.ptr(), json.length(), json.charset()));
+ m_table->field[2]->store(json.ptr(), json.length(), json.charset());
+ return value_type;
+ }
+ bool set_str_value(const char *key, const char *val, size_t vlen) const
+ {
+ char buf[JSON_SIZE];
+ int blen= json_escape(system_charset_info,
+ (const uchar*)val, (const uchar*)val + vlen,
+ m_table->field[2]->charset(),
+ (uchar*)buf, (uchar*)buf+sizeof(buf));
+ if (blen < 0)
+ return 1;
+ return set_value(key, buf, blen, true) == JSV_BAD_JSON;
+ }
+ bool set_int_value(const char *key, longlong val) const
+ {
+ char v[MY_INT64_NUM_DECIMAL_DIGITS+1];
+ size_t vlen= longlong10_to_str(val, v, -10) - v;
+ return set_value(key, v, vlen, false) == JSV_BAD_JSON;
+ }
+ bool set_double_value(const char *key, double val) const
+ {
+ char v[FLOATING_POINT_BUFFER+1];
+ size_t vlen= my_fcvt(val, TIME_SECOND_PART_DIGITS, v, NULL);
+ return set_value(key, v, vlen, false) == JSV_BAD_JSON;
+ }
+ bool set_bool_value(const char *key, bool val) const
+ {
+ return set_value(key, val ? "true" : "false", val ? 4 : 5, false) == JSV_BAD_JSON;
+ }
};
class Db_table: public Grant_table_base
{
public:
- Field* host() const { return tl.table->field[0]; }
- Field* db() const { return tl.table->field[1]; }
- Field* user() const { return tl.table->field[2]; }
+ Field* host() const { return m_table->field[0]; }
+ Field* db() const { return m_table->field[1]; }
+ Field* user() const { return m_table->field[2]; }
private:
friend class Grant_tables;
- Db_table() {};
-
- void init(enum thr_lock_type lock_type)
- {
- /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_DB_NAME, NULL, lock_type);
- Grant_table_base::init(lock_type, false);
- }
+ Db_table() { min_columns= 9; /* as in 3.20.13 */ }
};
class Tables_priv_table: public Grant_table_base
{
public:
- Field* host() const { return tl.table->field[0]; }
- Field* db() const { return tl.table->field[1]; }
- Field* user() const { return tl.table->field[2]; }
- Field* table_name() const { return tl.table->field[3]; }
- Field* grantor() const { return tl.table->field[4]; }
- Field* timestamp() const { return tl.table->field[5]; }
- Field* table_priv() const { return tl.table->field[6]; }
- Field* column_priv() const { return tl.table->field[7]; }
+ Field* host() const { return m_table->field[0]; }
+ Field* db() const { return m_table->field[1]; }
+ Field* user() const { return m_table->field[2]; }
+ Field* table_name() const { return m_table->field[3]; }
+ Field* grantor() const { return m_table->field[4]; }
+ Field* timestamp() const { return m_table->field[5]; }
+ Field* table_priv() const { return m_table->field[6]; }
+ Field* column_priv() const { return m_table->field[7]; }
private:
friend class Grant_tables;
- Tables_priv_table() {};
-
- void init(enum thr_lock_type lock_type, Grant_table_base *next_table= NULL)
- {
- /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- LEX_CSTRING MYSQL_TABLES_PRIV_NAME={STRING_WITH_LEN("tables_priv") };
- tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLES_PRIV_NAME, NULL, lock_type);
- Grant_table_base::init(lock_type, false);
- }
+ Tables_priv_table() { min_columns= 8; /* as in 3.22.26a */ }
};
class Columns_priv_table: public Grant_table_base
{
public:
- Field* host() const { return tl.table->field[0]; }
- Field* db() const { return tl.table->field[1]; }
- Field* user() const { return tl.table->field[2]; }
- Field* table_name() const { return tl.table->field[3]; }
- Field* column_name() const { return tl.table->field[4]; }
- Field* timestamp() const { return tl.table->field[5]; }
- Field* column_priv() const { return tl.table->field[6]; }
+ Field* host() const { return m_table->field[0]; }
+ Field* db() const { return m_table->field[1]; }
+ Field* user() const { return m_table->field[2]; }
+ Field* table_name() const { return m_table->field[3]; }
+ Field* column_name() const { return m_table->field[4]; }
+ Field* timestamp() const { return m_table->field[5]; }
+ Field* column_priv() const { return m_table->field[6]; }
private:
friend class Grant_tables;
- Columns_priv_table() {};
-
- void init(enum thr_lock_type lock_type)
- {
- /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- LEX_CSTRING MYSQL_COLUMNS_PRIV_NAME={ STRING_WITH_LEN("columns_priv") };
- tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_COLUMNS_PRIV_NAME, NULL, lock_type);
- Grant_table_base::init(lock_type, false);
- }
+ Columns_priv_table() { min_columns= 7; /* as in 3.22.26a */ }
};
class Host_table: public Grant_table_base
{
public:
- Field* host() const { return tl.table->field[0]; }
- Field* db() const { return tl.table->field[1]; }
+ Field* host() const { return m_table->field[0]; }
+ Field* db() const { return m_table->field[1]; }
private:
friend class Grant_tables;
- Host_table() {}
-
- void init(enum thr_lock_type lock_type)
- {
- /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- LEX_CSTRING MYSQL_HOST_NAME={STRING_WITH_LEN("host") };
- tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HOST_NAME, NULL, lock_type);
- Grant_table_base::init(lock_type, true);
- }
+ Host_table() { min_columns= 8; /* as in 3.20.13 */ }
};
class Procs_priv_table: public Grant_table_base
{
public:
- Field* host() const { return tl.table->field[0]; }
- Field* db() const { return tl.table->field[1]; }
- Field* user() const { return tl.table->field[2]; }
- Field* routine_name() const { return tl.table->field[3]; }
- Field* routine_type() const { return tl.table->field[4]; }
- Field* grantor() const { return tl.table->field[5]; }
- Field* proc_priv() const { return tl.table->field[6]; }
- Field* timestamp() const { return tl.table->field[7]; }
+ Field* host() const { return m_table->field[0]; }
+ Field* db() const { return m_table->field[1]; }
+ Field* user() const { return m_table->field[2]; }
+ Field* routine_name() const { return m_table->field[3]; }
+ Field* routine_type() const { return m_table->field[4]; }
+ Field* grantor() const { return m_table->field[5]; }
+ Field* proc_priv() const { return m_table->field[6]; }
+ Field* timestamp() const { return m_table->field[7]; }
private:
friend class Grant_tables;
- Procs_priv_table() {}
-
- void init(enum thr_lock_type lock_type)
- {
- /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- LEX_CSTRING MYSQL_PROCS_PRIV_NAME={STRING_WITH_LEN("procs_priv") };
- tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROCS_PRIV_NAME, NULL, lock_type);
- Grant_table_base::init(lock_type, true);
- }
+ Procs_priv_table() { min_columns=8; }
};
class Proxies_priv_table: public Grant_table_base
{
public:
- Field* host() const { return tl.table->field[0]; }
- Field* user() const { return tl.table->field[1]; }
- Field* proxied_host() const { return tl.table->field[2]; }
- Field* proxied_user() const { return tl.table->field[3]; }
- Field* with_grant() const { return tl.table->field[4]; }
- Field* grantor() const { return tl.table->field[5]; }
- Field* timestamp() const { return tl.table->field[6]; }
+ Field* host() const { return m_table->field[0]; }
+ Field* user() const { return m_table->field[1]; }
+ Field* proxied_host() const { return m_table->field[2]; }
+ Field* proxied_user() const { return m_table->field[3]; }
+ Field* with_grant() const { return m_table->field[4]; }
+ Field* grantor() const { return m_table->field[5]; }
+ Field* timestamp() const { return m_table->field[6]; }
private:
friend class Grant_tables;
- Proxies_priv_table() {}
-
- void init(enum thr_lock_type lock_type)
- {
- /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- LEX_CSTRING MYSQL_PROXIES_PRIV_NAME={STRING_WITH_LEN("proxies_priv") };
- tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROXIES_PRIV_NAME, NULL, lock_type);
- Grant_table_base::init(lock_type, true);
- }
+ Proxies_priv_table() { min_columns= 7; }
};
class Roles_mapping_table: public Grant_table_base
{
public:
- Field* host() const { return tl.table->field[0]; }
- Field* user() const { return tl.table->field[1]; }
- Field* role() const { return tl.table->field[2]; }
- Field* admin_option() const { return tl.table->field[3]; }
+ Field* host() const { return m_table->field[0]; }
+ Field* user() const { return m_table->field[1]; }
+ Field* role() const { return m_table->field[2]; }
+ Field* admin_option() const { return m_table->field[3]; }
private:
friend class Grant_tables;
- Roles_mapping_table() {}
-
- void init(enum thr_lock_type lock_type)
- {
- /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
- LEX_CSTRING MYSQL_ROLES_MAPPING_NAME={STRING_WITH_LEN("roles_mapping") };
- tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_ROLES_MAPPING_NAME, NULL, lock_type);
- Grant_table_base::init(lock_type, true);
- }
+ Roles_mapping_table() { min_columns= 4; }
};
/**
@@ -1169,170 +1812,143 @@ class Roles_mapping_table: public Grant_table_base
class Grant_tables
{
public:
- /* When constructing the Grant_tables object, we initialize only
- the tables which are going to be opened.
- @param which_tables Bitmap of which tables to open.
- @param lock_type Lock type to use when opening tables.
- */
- Grant_tables(int which_tables, enum thr_lock_type lock_type)
- {
- DBUG_ENTER("Grant_tables::Grant_tables");
- DBUG_PRINT("info", ("which_tables: %x, lock_type: %u",
- which_tables, lock_type));
- DBUG_ASSERT(which_tables); /* At least one table must be opened. */
- Grant_table_base* prev= NULL;
- /* We start from the last table, Table_roles_mapping, such that
- the first one in the linked list is Table_user. */
- if (which_tables & Table_roles_mapping)
- {
- m_roles_mapping_table.init(lock_type);
- prev= &m_roles_mapping_table;
- }
- if (which_tables & Table_proxies_priv)
- {
- m_proxies_priv_table.init(lock_type);
- link_tables(&m_proxies_priv_table, prev);
- prev= &m_proxies_priv_table;
- }
- if (which_tables & Table_procs_priv)
- {
- m_procs_priv_table.init(lock_type);
- link_tables(&m_procs_priv_table, prev);
- prev= &m_procs_priv_table;
- }
- if (which_tables & Table_host)
- {
- m_host_table.init(lock_type);
- link_tables(&m_host_table, prev);
- prev= &m_host_table;
- }
- if (which_tables & Table_columns_priv)
- {
- m_columns_priv_table.init(lock_type);
- link_tables(&m_columns_priv_table, prev);
- prev= &m_columns_priv_table;
- }
- if (which_tables & Table_tables_priv)
- {
- m_tables_priv_table.init(lock_type);
- link_tables(&m_tables_priv_table, prev);
- prev= &m_tables_priv_table;
- }
- if (which_tables & Table_db)
- {
- m_db_table.init(lock_type);
- link_tables(&m_db_table, prev);
- prev= &m_db_table;
- }
- if (which_tables & Table_user)
- {
- m_user_table.init(lock_type);
- link_tables(&m_user_table, prev);
- prev= &m_user_table;
- }
-
- first_table_in_list= prev;
- DBUG_VOID_RETURN;
- }
-
- /* Before any operation is possible on grant tables, they must be opened.
- This opens the tables according to the lock type specified during
- construction.
+ Grant_tables() : p_user_table(&m_user_table_json) { }
- @retval 1 replication filters matched. Abort the operation,
- but return OK (!)
- @retval 0 tables were opened successfully
- @retval -1 error, tables could not be opened
- */
- int open_and_lock(THD *thd)
+ int open_and_lock(THD *thd, int which_tables, enum thr_lock_type lock_type)
{
DBUG_ENTER("Grant_tables::open_and_lock");
- DBUG_ASSERT(first_table_in_list);
-#ifdef HAVE_REPLICATION
- if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE &&
- thd->slave_thread && !thd->spcont)
- {
- /*
- GRANT and REVOKE are applied the slave in/exclusion rules as they are
- some kind of updates to the mysql.% tables.
- */
- Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
- if (rpl_filter->is_on() &&
- !rpl_filter->tables_ok(0, &first_table_in_list->tl))
- DBUG_RETURN(1);
- }
-#endif
- if (open_and_lock_tables(thd, &first_table_in_list->tl, FALSE,
- MYSQL_LOCK_IGNORE_TIMEOUT))
- DBUG_RETURN(-1);
+ TABLE_LIST tables[USER_TABLE+1], *first= NULL;
+ DBUG_ASSERT(which_tables); /* At least one table must be opened. */
/*
We can read privilege tables even when !initialized.
This can be acl_load() - server startup or FLUSH PRIVILEGES
*/
- if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE &&
- !initialized)
+ if (lock_type >= TL_WRITE_ALLOW_WRITE && !initialized)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
DBUG_RETURN(-1);
}
- /* The privilge columns vary based on MariaDB version. Figure out
- how many we have after we've opened the table. */
- m_user_table.compute_num_privilege_cols();
- m_db_table.compute_num_privilege_cols();
- m_tables_priv_table.compute_num_privilege_cols();
- m_columns_priv_table.compute_num_privilege_cols();
- m_host_table.compute_num_privilege_cols();
- m_procs_priv_table.compute_num_privilege_cols();
- m_proxies_priv_table.compute_num_privilege_cols();
- m_roles_mapping_table.compute_num_privilege_cols();
+ for (int i=USER_TABLE; i >=0; i--)
+ {
+ TABLE_LIST *tl= tables + i;
+ if (which_tables & (1 << i))
+ {
+ tl->init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLE_NAME[i],
+ NULL, lock_type);
+ tl->open_type= OT_BASE_ONLY;
+ tl->i_s_requested_object= OPEN_TABLE_ONLY;
+ tl->updating= lock_type >= TL_WRITE_ALLOW_WRITE;
+ if (i >= FIRST_OPTIONAL_TABLE)
+ tl->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+ tl->next_global= tl->next_local= first;
+ first= tl;
+ }
+ else
+ tl->table= NULL;
+ }
+
+ uint counter;
+ int res= really_open(thd, first, &counter);
+
+ /* if User_table_json wasn't found, let's try User_table_tabular */
+ if (!res && (which_tables & Table_user) && !tables[USER_TABLE].table)
+ {
+ uint unused;
+ TABLE_LIST *tl= tables + USER_TABLE;
+ TABLE *backup_open_tables= thd->open_tables;
+ thd->set_open_tables(NULL);
+
+ tl->init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLE_NAME_USER,
+ NULL, lock_type);
+ tl->open_type= OT_BASE_ONLY;
+ tl->i_s_requested_object= OPEN_TABLE_ONLY;
+ tl->updating= lock_type >= TL_WRITE_ALLOW_WRITE;
+ p_user_table= &m_user_table_tabular;
+ counter++;
+ res= really_open(thd, tl, &unused);
+ thd->set_open_tables(backup_open_tables);
+ if (tables[USER_TABLE].table)
+ {
+ tables[USER_TABLE].table->next= backup_open_tables;
+ thd->set_open_tables(tables[USER_TABLE].table);
+ }
+ }
+ if (res)
+ DBUG_RETURN(res);
+
+ if (lock_tables(thd, first, counter, MYSQL_LOCK_IGNORE_TIMEOUT))
+ DBUG_RETURN(-1);
+
+ p_user_table->set_table(tables[USER_TABLE].table);
+ m_db_table.set_table(tables[DB_TABLE].table);
+ m_tables_priv_table.set_table(tables[TABLES_PRIV_TABLE].table);
+ m_columns_priv_table.set_table(tables[COLUMNS_PRIV_TABLE].table);
+ m_host_table.set_table(tables[HOST_TABLE].table);
+ m_procs_priv_table.set_table(tables[PROCS_PRIV_TABLE].table);
+ m_proxies_priv_table.set_table(tables[PROXIES_PRIV_TABLE].table);
+ m_roles_mapping_table.set_table(tables[ROLES_MAPPING_TABLE].table);
DBUG_RETURN(0);
}
inline const User_table& user_table() const
- {
- return m_user_table;
- }
+ { return *p_user_table; }
inline const Db_table& db_table() const
- {
- return m_db_table;
- }
-
+ { return m_db_table; }
inline const Tables_priv_table& tables_priv_table() const
- {
- return m_tables_priv_table;
- }
+ { return m_tables_priv_table; }
inline const Columns_priv_table& columns_priv_table() const
- {
- return m_columns_priv_table;
- }
+ { return m_columns_priv_table; }
inline const Host_table& host_table() const
- {
- return m_host_table;
- }
+ { return m_host_table; }
inline const Procs_priv_table& procs_priv_table() const
- {
- return m_procs_priv_table;
- }
+ { return m_procs_priv_table; }
inline const Proxies_priv_table& proxies_priv_table() const
- {
- return m_proxies_priv_table;
- }
+ { return m_proxies_priv_table; }
inline const Roles_mapping_table& roles_mapping_table() const
+ { return m_roles_mapping_table; }
+
+ private:
+
+ /* Before any operation is possible on grant tables, they must be opened.
+
+ @retval 1 replication filters matched. Abort the operation,
+ but return OK (!)
+ @retval 0 tables were opened successfully
+ @retval -1 error, tables could not be opened
+ */
+ int really_open(THD *thd, TABLE_LIST* tables, uint *counter)
{
- return m_roles_mapping_table;
+ DBUG_ENTER("Grant_tables::really_open:");
+#ifdef HAVE_REPLICATION
+ if (tables->lock_type >= TL_WRITE_ALLOW_WRITE &&
+ thd->slave_thread && !thd->spcont)
+ {
+ /*
+ GRANT and REVOKE are applied the slave in/exclusion rules as they are
+ some kind of updates to the mysql.% tables.
+ */
+ Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
+ if (rpl_filter->is_on() && !rpl_filter->tables_ok(0, tables))
+ DBUG_RETURN(1);
+ }
+#endif
+ if (open_tables(thd, &tables, counter, MYSQL_LOCK_IGNORE_TIMEOUT))
+ DBUG_RETURN(-1);
+ DBUG_RETURN(0);
}
- private:
- User_table m_user_table;
+ User_table *p_user_table;
+ User_table_json m_user_table_json;
+ User_table_tabular m_user_table_tabular;
Db_table m_db_table;
Tables_priv_table m_tables_priv_table;
Columns_priv_table m_columns_priv_table;
@@ -1340,20 +1956,6 @@ class Grant_tables
Procs_priv_table m_procs_priv_table;
Proxies_priv_table m_proxies_priv_table;
Roles_mapping_table m_roles_mapping_table;
-
- /* The grant tables are set-up in a linked list. We keep the head of it. */
- Grant_table_base *first_table_in_list;
- /**
- Chain two grant tables' TABLE_LIST members.
- */
- static void link_tables(Grant_table_base *from, Grant_table_base *to)
- {
- DBUG_ASSERT(from);
- if (to)
- from->tl.next_local= from->tl.next_global= &to->tl;
- else
- from->tl.next_local= from->tl.next_global= NULL;
- }
};
@@ -1361,14 +1963,13 @@ void ACL_PROXY_USER::init(const Proxies_priv_table& proxies_priv_table,
MEM_ROOT *mem)
{
init(get_field(mem, proxies_priv_table.host()),
- get_field(mem, proxies_priv_table.user()),
+ safe_str(get_field(mem, proxies_priv_table.user())),
get_field(mem, proxies_priv_table.proxied_host()),
- get_field(mem, proxies_priv_table.proxied_user()),
+ safe_str(get_field(mem, proxies_priv_table.proxied_user())),
proxies_priv_table.with_grant()->val_int() != 0);
}
-
/*
Enumeration of various ACL's and Hashes used in handle_grant_struct()
*/
@@ -1388,13 +1989,10 @@ enum enum_acl_lists
ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) : counter(0)
{
-
access= user->access;
/* set initial role access the same as the table row privileges */
initial_role_access= user->access;
- this->user.str= safe_strdup_root(root, user->user.str);
- this->user.length= user->user.length;
- bzero(&role_grants, sizeof(role_grants));
+ this->user= user->user;
bzero(&parent_grantee, sizeof(parent_grantee));
flags= IS_ROLE;
}
@@ -1405,7 +2003,6 @@ ACL_ROLE::ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *root) :
this->access= initial_role_access;
this->user.str= safe_strdup_root(root, rolename);
this->user.length= strlen(rolename);
- bzero(&role_grants, sizeof(role_grants));
bzero(&parent_grantee, sizeof(parent_grantee));
flags= IS_ROLE;
}
@@ -1443,7 +2040,7 @@ static bool has_validation_plugins()
MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL);
}
-struct validation_data { LEX_CSTRING *user, *password; };
+struct validation_data { const LEX_CSTRING *user, *password; };
static my_bool do_validate(THD *, plugin_ref plugin, void *arg)
{
@@ -1454,13 +2051,13 @@ static my_bool do_validate(THD *, plugin_ref plugin, void *arg)
}
-static bool validate_password(LEX_USER *user, THD *thd)
+static bool validate_password(THD *thd, const LEX_CSTRING &user,
+ const LEX_CSTRING &pwtext, bool has_hash)
{
- if (user->pwtext.length || !user->pwhash.length)
+ if (pwtext.length || !has_hash)
{
- struct validation_data data= { &user->user,
- user->pwtext.str ? &user->pwtext :
- const_cast<LEX_CSTRING *>(&empty_clex_str) };
+ struct validation_data data= { &user,
+ pwtext.str ? &pwtext : &empty_clex_str };
if (plugin_foreach(NULL, do_validate,
MariaDB_PASSWORD_VALIDATION_PLUGIN, &data))
{
@@ -1484,161 +2081,153 @@ static bool validate_password(LEX_USER *user, THD *thd)
return false;
}
-/**
- Convert scrambled password to binary form, according to scramble type,
- Binary form is stored in user.salt.
-
- @param acl_user The object where to store the salt
- @param password The password hash containing the salt
- @param password_len The length of the password hash
-
- Despite the name of the function it is used when loading ACLs from disk
- to store the password hash in the ACL_USER object.
-*/
-
-static void
-set_user_salt(ACL_USER *acl_user, const char *password, size_t password_len)
+static int set_user_salt(ACL_USER::AUTH *auth, plugin_ref plugin)
{
- if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
- {
- get_salt_from_password(acl_user->salt, password);
- acl_user->salt_len= SCRAMBLE_LENGTH;
- }
- else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
+ st_mysql_auth *info= (st_mysql_auth *) plugin_decl(plugin)->info;
+ if (info->interface_version >= 0x0202 && info->preprocess_hash &&
+ auth->auth_string.length)
{
- get_salt_from_password_323((ulong *) acl_user->salt, password);
- acl_user->salt_len= SCRAMBLE_LENGTH_323;
+ uchar buf[MAX_SCRAMBLE_LENGTH];
+ size_t len= sizeof(buf);
+ if (info->preprocess_hash(auth->auth_string.str,
+ auth->auth_string.length, buf, &len))
+ return 1;
+ auth->salt.str= (char*)memdup_root(&acl_memroot, buf, len);
+ auth->salt.length= len;
}
else
- acl_user->salt_len= 0;
-}
+ auth->salt= safe_lexcstrdup_root(&acl_memroot, auth->auth_string);
-static const char *fix_plugin_ptr(const 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;
+ return 0;
}
/**
- Fix ACL::plugin pointer to point to a hard-coded string, if appropriate
+ Fills in ACL_USER::auth_string and ACL_USER::salt fields, as needed
- 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.
+ hashes the plain-text password (if provided) to auth_string,
+ converts auth_string to salt.
- @retval 0 the pointers were fixed
- @retval 1 this ACL_USER uses a not built-in plugin
+ Fails if the plain-text password fails validation, if the plugin is
+ not loaded, if the auth_string is invalid, if the password is not applicable
*/
-static bool fix_user_plugin_ptr(ACL_USER *user)
+static int set_user_auth(THD *thd, const LEX_CSTRING &user,
+ ACL_USER::AUTH *auth, const LEX_CSTRING &pwtext)
{
- if (lex_string_eq(&user->plugin, &native_password_plugin_name))
- user->plugin= native_password_plugin_name;
- else
- if (lex_string_eq(&user->plugin, &old_password_plugin_name))
- user->plugin= old_password_plugin_name;
- else
- return true;
-
- set_user_salt(user, user->auth_string.str, user->auth_string.length);
- return false;
-}
-
+ const char *plugin_name= auth->plugin.str;
+ bool unlock_plugin= false;
+ plugin_ref plugin= get_auth_plugin(thd, auth->plugin, &unlock_plugin);
+ int res= 1;
-/*
- Validates the password, calculates password hash, transforms
- equivalent LEX_USER representations.
-
- Upon entering this function:
-
- - if user->plugin is specified, user->auth is the plugin auth data.
- - if user->plugin is mysql_native_password or mysql_old_password,
- user->auth is the password hash, and LEX_USER is transformed
- to match the next case (that is, user->plugin is cleared).
- - if user->plugin is NOT specified, built-in auth is assumed, that is
- mysql_native_password or mysql_old_password. In that case,
- user->pwhash is the password hash. And user->pwtext is the original
- plain-text password. Either one can be set or both.
-
- Upon exiting this function:
-
- - user->pwtext is left untouched
- - user->pwhash is the password hash, as the mysql.user.password column
- - user->plugin is the plugin name, as the mysql.user.plugin column
- - user->auth is the plugin auth data, as the mysql.user.authentication_string column
-*/
-static bool fix_lex_user(THD *thd, LEX_USER *user)
-{
- size_t check_length;
+ if (!plugin)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_PLUGIN_IS_NOT_LOADED,
+ ER_THD(thd, ER_PLUGIN_IS_NOT_LOADED), plugin_name);
+ return ER_PLUGIN_IS_NOT_LOADED;
+ }
- DBUG_ASSERT(user->plugin.length || !user->auth.length);
- DBUG_ASSERT(!(user->plugin.length && (user->pwtext.length || user->pwhash.length)));
+ auth->salt= auth->auth_string;
- if (lex_string_eq(&user->plugin, &native_password_plugin_name))
- check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
- else
- if (lex_string_eq(&user->plugin, &old_password_plugin_name))
- check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
- else
- if (user->plugin.length)
- return false; // nothing else to do
- else if (thd->variables.old_passwords == 1 ||
- user->pwhash.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
- check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
- else
- check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
-
- if (user->plugin.length)
+ st_mysql_auth *info= (st_mysql_auth *) plugin_decl(plugin)->info;
+ if (info->interface_version < 0x0202)
{
- user->pwhash= user->auth;
- user->plugin= empty_clex_str;
- user->auth= empty_clex_str;
+ res= pwtext.length ? ER_SET_PASSWORD_AUTH_PLUGIN : 0;
+ goto end;
}
- if (user->pwhash.length && user->pwhash.length != check_length)
+ if (info->hash_password &&
+ validate_password(thd, user, pwtext, auth->auth_string.length))
{
- my_error(ER_PASSWD_LENGTH, MYF(0), (int) check_length);
- return true;
+ res= ER_NOT_VALID_PASSWORD;
+ goto end;
}
-
- if (user->pwtext.length && !user->pwhash.length)
+ if (pwtext.length)
{
- size_t scramble_length;
- void (*make_scramble)(char *, const char *, size_t);
-
- if (thd->variables.old_passwords == 1)
+ if (info->hash_password)
{
- scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
- make_scramble= my_make_scrambled_password_323;
+ char buf[MAX_SCRAMBLE_LENGTH];
+ size_t len= sizeof(buf) - 1;
+ if (info->hash_password(pwtext.str, pwtext.length, buf, &len))
+ {
+ res= ER_OUTOFMEMORY;
+ goto end;
+ }
+ buf[len] = 0;
+ auth->auth_string.str= (char*)memdup_root(&acl_memroot, buf, len+1);
+ auth->auth_string.length= len;
}
else
{
- scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
- make_scramble= my_make_scrambled_password;
+ res= ER_SET_PASSWORD_AUTH_PLUGIN;
+ goto end;
}
+ }
+ if (set_user_salt(auth, plugin))
+ {
+ res= ER_PASSWD_LENGTH;
+ goto end;
+ }
- Query_arena *arena, backup;
- arena= thd->activate_stmt_arena_if_needed(&backup);
- char *buff= (char *) thd->alloc(scramble_length + 1);
- if (arena)
- thd->restore_active_arena(arena, &backup);
+ res= 0;
+end:
+ if (unlock_plugin)
+ plugin_unlock(thd, plugin);
+ return res;
+}
- if (buff == NULL)
- return true;
- make_scramble(buff, user->pwtext.str, user->pwtext.length);
- user->pwhash.str= buff;
- user->pwhash.length= scramble_length;
+
+/**
+ Lazily computes user's salt from the password hash
+*/
+static bool set_user_salt_if_needed(ACL_USER *user_copy, int curr_auth,
+ plugin_ref plugin)
+{
+ ACL_USER::AUTH *auth_copy= user_copy->auth + curr_auth;
+ DBUG_ASSERT(!strcasecmp(auth_copy->plugin.str, plugin_name(plugin)->str));
+
+ if (auth_copy->salt.str)
+ return 0; // already done
+
+ if (set_user_salt(auth_copy, plugin))
+ return 1;
+
+ mysql_mutex_lock(&acl_cache->lock);
+ ACL_USER *user= find_user_exact(user_copy->host.hostname, user_copy->user.str);
+ // make sure the user wasn't altered or dropped meanwhile
+ if (user)
+ {
+ ACL_USER::AUTH *auth= user->auth + curr_auth;
+ if (!auth->salt.str && auth->plugin.length == auth_copy->plugin.length &&
+ auth->auth_string.length == auth_copy->auth_string.length &&
+ !memcmp(auth->plugin.str, auth_copy->plugin.str, auth->plugin.length) &&
+ !memcmp(auth->auth_string.str, auth_copy->auth_string.str, auth->auth_string.length))
+ auth->salt= auth_copy->salt;
}
+ mysql_mutex_unlock(&acl_cache->lock);
+ return 0;
+}
+
+
+/**
+ 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.
+
+ @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::AUTH *auth)
+{
+ if (lex_string_eq(&auth->plugin, &native_password_plugin_name))
+ auth->plugin= native_password_plugin_name;
+ else
+ if (lex_string_eq(&auth->plugin, &old_password_plugin_name))
+ auth->plugin= old_password_plugin_name;
+ else
+ return true;
return false;
}
@@ -1715,27 +2304,12 @@ bool acl_init(bool dont_read_acl_tables)
DBUG_RETURN(return_val);
}
-/**
- Choose from either native or old password plugins when assigning a password
-*/
-
-static bool set_user_plugin (ACL_USER *user, size_t password_len)
+static void push_new_user(const ACL_USER &user)
{
- switch (password_len)
- {
- case 0: /* no password */
- case SCRAMBLED_PASSWORD_CHAR_LENGTH:
- user->plugin= native_password_plugin_name;
- return FALSE;
- case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
- user->plugin= old_password_plugin_name;
- return FALSE;
- default:
- sql_print_warning("Found invalid password for user: '%s@%s'; "
- "Ignoring user", safe_str(user->user.str),
- safe_str(user->host.hostname));
- return TRUE;
- }
+ push_dynamic(&acl_users, &user);
+ if (!user.host.hostname ||
+ (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
+ allow_all_hosts=1; // Anyone can connect
}
@@ -1760,7 +2334,6 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
READ_RECORD read_record_info;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
char tmp_name[SAFE_NAME_LEN+1];
- int password_length;
Sql_mode_save old_mode_save(thd);
DBUG_ENTER("acl_load");
@@ -1772,7 +2345,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
init_sql_alloc(&acl_memroot, "ACL", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
if (host_table.table_exists()) // "host" table may not exist (e.g. in MySQL 5.6.7+)
{
- if (host_table.init_read_record(&read_record_info, thd))
+ if (host_table.init_read_record(&read_record_info))
DBUG_RETURN(true);
while (!(read_record_info.read_record()))
{
@@ -1801,7 +2374,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
}
host.access= host_table.get_access();
host.access= fix_rights_for_db(host.access);
- host.sort= get_sort(2, host.host.hostname, host.db);
+ host.sort= get_magic_sort("hd", host.host.hostname, host.db);
if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
{
sql_print_warning("'host' entry '%s|%s' "
@@ -1826,252 +2399,100 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
freeze_size(&acl_hosts);
const User_table& user_table= tables.user_table();
- if (user_table.init_read_record(&read_record_info, thd))
+ if (user_table.init_read_record(&read_record_info))
DBUG_RETURN(true);
- if (user_table.num_fields() < 13) // number of columns in 3.21
- {
- sql_print_error("Fatal error: mysql.user table is damaged or in "
- "unsupported 3.20 format.");
- DBUG_RETURN(true);
- }
- username_char_length= MY_MIN(user_table.user()->char_length(),
- USERNAME_CHAR_LENGTH);
- if (user_table.password()) // Password column might be missing. (MySQL 5.7.6+)
- {
- password_length= user_table.password()->field_length /
- user_table.password()->charset()->mbmaxlen;
- if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
- {
- sql_print_error("Fatal error: mysql.user table is damaged or in "
- "unsupported 3.20 format.");
- DBUG_RETURN(TRUE);
- }
-
- DBUG_PRINT("info",("user table fields: %d, password length: %d",
- user_table.num_fields(), password_length));
-
- mysql_mutex_lock(&LOCK_global_system_variables);
- if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
- {
- if (opt_secure_auth)
- {
- mysql_mutex_unlock(&LOCK_global_system_variables);
- sql_print_error("Fatal error: mysql.user table is in old format, "
- "but server started with --secure-auth option.");
- DBUG_RETURN(TRUE);
- }
- mysql_user_table_is_in_short_password_format= true;
- if (global_system_variables.old_passwords)
- mysql_mutex_unlock(&LOCK_global_system_variables);
- else
- {
- extern sys_var *Sys_old_passwords_ptr;
- Sys_old_passwords_ptr->value_origin= sys_var::AUTO;
- global_system_variables.old_passwords= 1;
- mysql_mutex_unlock(&LOCK_global_system_variables);
- sql_print_warning("mysql.user table is not updated to new password format; "
- "Disabling new password usage until "
- "mysql_fix_privilege_tables is run");
- }
- thd->variables.old_passwords= 1;
- }
- else
- {
- mysql_user_table_is_in_short_password_format= false;
- mysql_mutex_unlock(&LOCK_global_system_variables);
- }
- }
-
allow_all_hosts=0;
while (!(read_record_info.read_record()))
{
ACL_USER user;
bool is_role= FALSE;
- bzero(&user, sizeof(user));
- update_hostname(&user.host, get_field(&acl_memroot, user_table.host()));
- char *username= get_field(&acl_memroot, user_table.user());
+ update_hostname(&user.host, user_table.get_host(&acl_memroot));
+ char *username= safe_str(user_table.get_user(&acl_memroot));
user.user.str= username;
- user.user.length= safe_strlen(username);
-
- /*
- If the user entry is a role, skip password and hostname checks
- A user can not log in with a role so some checks are not necessary
- */
- is_role= user_table.check_is_role();
+ user.user.length= strlen(username);
- if (is_role && is_invalid_role_name(username))
- {
- thd->clear_error(); // the warning is still issued
- continue;
- }
+ is_role= user_table.get_is_role();
- if (!is_role && check_no_resolve &&
- hostname_requires_resolving(user.host.hostname))
- {
- sql_print_warning("'user' entry '%s@%s' "
- "ignored in --skip-name-resolve mode.",
- safe_str(user.user.str),
- safe_str(user.host.hostname));
- continue;
- }
+ user.access= user_table.get_access();
- char *password= const_cast<char*>("");
- if (user_table.password())
- password= get_field(&acl_memroot, user_table.password());
- size_t password_len= safe_strlen(password);
- user.auth_string.str= safe_str(password);
- user.auth_string.length= password_len;
- set_user_salt(&user, password, password_len);
+ user.sort= get_magic_sort("hu", user.host.hostname, user.user.str);
+ user.hostname_length= safe_strlen(user.host.hostname);
- if (!is_role && set_user_plugin(&user, password_len))
- continue;
+ my_init_dynamic_array(&user.role_grants, sizeof(ACL_ROLE *), 0, 8, MYF(0));
- {
- user.access= user_table.get_access();
- user.sort= get_sort(2, user.host.hostname, user.user.str);
- user.hostname_length= safe_strlen(user.host.hostname);
- user.user_resource.user_conn= 0;
- user.user_resource.max_statement_time= 0.0;
-
- /* Starting from 4.0.2 we have more fields */
- if (user_table.ssl_type())
- {
- char *ssl_type=get_field(thd->mem_root, user_table.ssl_type());
- if (!ssl_type)
- user.ssl_type=SSL_TYPE_NONE;
- else if (!strcmp(ssl_type, "ANY"))
- user.ssl_type=SSL_TYPE_ANY;
- else if (!strcmp(ssl_type, "X509"))
- user.ssl_type=SSL_TYPE_X509;
- else /* !strcmp(ssl_type, "SPECIFIED") */
- user.ssl_type=SSL_TYPE_SPECIFIED;
-
- user.ssl_cipher= get_field(&acl_memroot, user_table.ssl_cipher());
- user.x509_issuer= get_field(&acl_memroot, user_table.x509_issuer());
- user.x509_subject= get_field(&acl_memroot, user_table.x509_subject());
-
- char *ptr = get_field(thd->mem_root, user_table.max_questions());
- user.user_resource.questions=ptr ? atoi(ptr) : 0;
- ptr = get_field(thd->mem_root, user_table.max_updates());
- user.user_resource.updates=ptr ? atoi(ptr) : 0;
- ptr = get_field(thd->mem_root, user_table.max_connections());
- user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
- if (user.user_resource.questions || user.user_resource.updates ||
- user.user_resource.conn_per_hour)
- mqh_used=1;
-
- if (user_table.max_user_connections())
- {
- /* Starting from 5.0.3 we have max_user_connections field */
- ptr= get_field(thd->mem_root, user_table.max_user_connections());
- user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
- }
-
- if (!is_role && user_table.plugin())
- {
- /* We may have plugin & auth_String fields */
- char *tmpstr= get_field(&acl_memroot, user_table.plugin());
- if (tmpstr)
- {
- user.plugin.str= tmpstr;
- user.plugin.length= strlen(user.plugin.str);
- user.auth_string.str=
- safe_str(get_field(&acl_memroot,
- user_table.authentication_string()));
- user.auth_string.length= strlen(user.auth_string.str);
-
- if (user.auth_string.length && password_len &&
- (user.auth_string.length != password_len ||
- memcmp(user.auth_string.str, password, password_len)))
- {
- sql_print_warning("'user' entry '%s@%s' has both a password "
- "and an authentication plugin specified. The "
- "password will be ignored.",
- safe_str(user.user.str),
- safe_str(user.host.hostname));
- }
- else if (password_len)
- {
- user.auth_string.str= password;
- user.auth_string.length= password_len;
- }
+ user.account_locked= user_table.get_account_locked();
- fix_user_plugin_ptr(&user);
- }
- }
+ user.password_expired= user_table.get_password_expired();
+ user.password_last_changed= user_table.get_password_last_changed();
+ user.password_lifetime= user_table.get_password_lifetime();
- if (user_table.max_statement_time())
- {
- /* Starting from 10.1.1 we can have max_statement_time */
- ptr= get_field(thd->mem_root,
- user_table.max_statement_time());
- user.user_resource.max_statement_time= ptr ? atof(ptr) : 0.0;
- }
- }
- else
+ if (is_role)
+ {
+ if (is_invalid_role_name(username))
{
- user.ssl_type=SSL_TYPE_NONE;
-#ifndef TO_BE_REMOVED
- if (user_table.num_fields() <= 13)
- { // Without grant
- if (user.access & CREATE_ACL)
- user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
- }
- /* Convert old privileges */
- user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
- if (user.access & FILE_ACL)
- user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
- if (user.access & PROCESS_ACL)
- user.access|= SUPER_ACL | EXECUTE_ACL;
-#endif
+ thd->clear_error(); // the warning is still issued
+ continue;
}
- (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_ROLE *),
- 8, 8, MYF(0));
+ ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot);
+ entry->role_grants = user.role_grants;
+ my_init_dynamic_array(&entry->parent_grantee,
+ sizeof(ACL_USER_BASE *), 0, 8, MYF(0));
+ my_hash_insert(&acl_roles, (uchar *)entry);
- /* check default role, if any */
- if (!is_role && user_table.default_role())
+ continue;
+ }
+ else
+ {
+ if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
{
- user.default_rolename.str=
- get_field(&acl_memroot, user_table.default_role());
- user.default_rolename.length= safe_strlen(user.default_rolename.str);
+ sql_print_warning("'user' entry '%s@%s' "
+ "ignored in --skip-name-resolve mode.", user.user.str,
+ safe_str(user.host.hostname));
+ continue;
}
- if (is_role)
- {
- DBUG_PRINT("info", ("Found role %s", user.user.str));
- ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot);
- entry->role_grants = user.role_grants;
- (void) my_init_dynamic_array(&entry->parent_grantee,
- sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
- my_hash_insert(&acl_roles, (uchar *)entry);
-
+ if (user_table.get_auth(thd, &acl_memroot, &user))
continue;
- }
- else
+ for (uint i= 0; i < user.nauth; i++)
{
- DBUG_PRINT("info", ("Found user %s", user.user.str));
- (void) push_dynamic(&acl_users,(uchar*) &user);
+ ACL_USER::AUTH *auth= user.auth + i;
+ auth->salt= null_clex_str;
+ fix_user_plugin_ptr(auth);
}
- if (!user.host.hostname ||
- (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
- allow_all_hosts=1; // Anyone can connect
+
+ user.ssl_type= user_table.get_ssl_type();
+ user.ssl_cipher= user_table.get_ssl_cipher(&acl_memroot);
+ user.x509_issuer= safe_str(user_table.get_x509_issuer(&acl_memroot));
+ user.x509_subject= safe_str(user_table.get_x509_subject(&acl_memroot));
+ user.user_resource.questions= (uint)user_table.get_max_questions();
+ user.user_resource.updates= (uint)user_table.get_max_updates();
+ user.user_resource.conn_per_hour= (uint)user_table.get_max_connections();
+ if (user.user_resource.questions || user.user_resource.updates ||
+ user.user_resource.conn_per_hour)
+ mqh_used=1;
+
+ user.user_resource.user_conn= (int)user_table.get_max_user_connections();
+ user.user_resource.max_statement_time= user_table.get_max_statement_time();
+
+ user.default_rolename.str= user_table.get_default_role(&acl_memroot);
+ user.default_rolename.length= safe_strlen(user.default_rolename.str);
}
+ push_new_user(user);
}
- my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
- sizeof(ACL_USER),(qsort_cmp) acl_compare);
+ rebuild_acl_users();
end_read_record(&read_record_info);
freeze_size(&acl_users);
const Db_table& db_table= tables.db_table();
- if (db_table.init_read_record(&read_record_info, thd))
+ if (db_table.init_read_record(&read_record_info))
DBUG_RETURN(TRUE);
while (!(read_record_info.read_record()))
{
ACL_DB db;
char *db_name;
- db.user=get_field(&acl_memroot, db_table.user());
+ db.user=safe_str(get_field(&acl_memroot, db_table.user()));
const char *hostname= get_field(&acl_memroot, db_table.host());
if (!hostname && find_acl_role(db.user))
hostname= "";
@@ -2086,7 +2507,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
{
sql_print_warning("'db' entry '%s %s@%s' "
"ignored in --skip-name-resolve mode.",
- db.db, safe_str(db.user), safe_str(db.host.hostname));
+ db.db, db.user, safe_str(db.host.hostname));
continue;
}
db.access= db_table.get_access();
@@ -2111,10 +2532,10 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
"case that has been forced to lowercase because "
"lower_case_table_names is set. It will not be "
"possible to remove this privilege using REVOKE.",
- db.db, safe_str(db.user), safe_str(db.host.hostname));
+ db.db, db.user, safe_str(db.host.hostname));
}
}
- db.sort=get_sort(3,db.host.hostname,db.db,db.user);
+ db.sort=get_magic_sort("hdu", db.host.hostname, db.db, db.user);
#ifndef TO_BE_REMOVED
if (db_table.num_fields() <= 9)
{ // Without grant
@@ -2125,13 +2546,13 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
acl_dbs.push(db);
}
end_read_record(&read_record_info);
- acl_dbs.sort((acl_dbs_cmp)acl_compare);
+ rebuild_acl_dbs();
acl_dbs.freeze();
const Proxies_priv_table& proxies_priv_table= tables.proxies_priv_table();
if (proxies_priv_table.table_exists())
{
- if (proxies_priv_table.init_read_record(&read_record_info, thd))
+ if (proxies_priv_table.init_read_record(&read_record_info))
DBUG_RETURN(TRUE);
while (!(read_record_info.read_record()))
{
@@ -2157,7 +2578,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
const Roles_mapping_table& roles_mapping_table= tables.roles_mapping_table();
if (roles_mapping_table.table_exists())
{
- if (roles_mapping_table.init_read_record(&read_record_info, thd))
+ if (roles_mapping_table.init_read_record(&read_record_info))
DBUG_RETURN(TRUE);
MEM_ROOT temp_root;
@@ -2194,6 +2615,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
init_check_host();
+ thd->bootstrap= !initialized; // keep FLUSH PRIVILEGES connection special
initialized=1;
DBUG_RETURN(FALSE);
}
@@ -2250,13 +2672,14 @@ bool acl_reload(THD *thd)
int result;
DBUG_ENTER("acl_reload");
- Grant_tables tables(Table_host | Table_user | Table_db | Table_proxies_priv |
- Table_roles_mapping, TL_READ);
+ Grant_tables tables;
/*
To avoid deadlocks we should obtain table locks before
obtaining acl_cache->lock mutex.
*/
- if (unlikely((result= tables.open_and_lock(thd))))
+ const uint tables_to_open= Table_host | Table_user | Table_db |
+ Table_proxies_priv | Table_roles_mapping;
+ if ((result= tables.open_and_lock(thd, tables_to_open, TL_READ)))
{
DBUG_ASSERT(result <= 0);
/*
@@ -2360,56 +2783,161 @@ static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
}
+static int acl_compare(const ACL_ACCESS *a, const ACL_ACCESS *b)
+{
+ if (a->sort > b->sort)
+ return -1;
+ if (a->sort < b->sort)
+ return 1;
+ return 0;
+}
+
+static int acl_user_compare(const ACL_USER *a, const ACL_USER *b)
+{
+ int res= strcmp(a->user.str, b->user.str);
+ if (res)
+ return res;
+
+ res= acl_compare(a, b);
+ if (res)
+ return res;
+
+ /*
+ For more deterministic results, resolve ambiguity between
+ "localhost" and "127.0.0.1"/"::1" by sorting "localhost" before
+ loopback addresses.
+ Test suite (on Windows) expects "root@localhost", even if
+ root@::1 would also match.
+ */
+ return -strcmp(a->host.hostname, b->host.hostname);
+}
+
+static int acl_db_compare(const ACL_DB *a, const ACL_DB *b)
+{
+ int res= strcmp(a->user, b->user);
+ if (res)
+ return res;
+
+ return acl_compare(a, b);
+}
+
+static void rebuild_acl_users()
+{
+ my_qsort((uchar*)dynamic_element(&acl_users, 0, ACL_USER*), acl_users.elements,
+ sizeof(ACL_USER), (qsort_cmp)acl_user_compare);
+}
+
+static void rebuild_acl_dbs()
+{
+ acl_dbs.sort(acl_db_compare);
+}
+
+
/*
- Return a number which, if sorted 'desc', puts strings in this order:
- no wildcards
- wildcards
- empty string
+ Return index of the first entry with given user in the array,
+ or SIZE_T_MAX if not found.
+
+ Assumes the array is sorted by get_username
*/
+template<typename T> size_t find_first_user(T* arr, size_t len, const char *user)
+{
+ size_t low= 0;
+ size_t high= len;
+ size_t mid;
+
+ bool found= false;
+ if(!len)
+ return SIZE_T_MAX;
-static ulong get_sort(uint count,...)
+#ifndef DBUG_OFF
+ for (uint i = 0; i < len - 1; i++)
+ DBUG_ASSERT(strcmp(arr[i].get_username(), arr[i + 1].get_username()) <= 0);
+#endif
+ while (low < high)
+ {
+ mid= low + (high - low) / 2;
+ int cmp= strcmp(arr[mid].get_username(),user);
+ if (cmp == 0)
+ found= true;
+
+ if (cmp >= 0 )
+ high= mid;
+ else
+ low= mid + 1;
+ }
+ return (!found || low == len || strcmp(arr[low].get_username(), user)!=0 )?SIZE_T_MAX:low;
+}
+
+static size_t acl_find_user_by_name(const char *user)
{
- va_list args;
- va_start(args,count);
- ulong sort=0;
+ return find_first_user<ACL_USER>((ACL_USER *)acl_users.buffer,acl_users.elements,user);
+}
+
+static size_t acl_find_db_by_username(const char *user)
+{
+ return find_first_user<ACL_DB>(acl_dbs.front(), acl_dbs.elements(), user);
+}
+
+static bool match_db(ACL_DB *acl_db, const char *db, my_bool db_is_pattern)
+{
+ return !acl_db->db || (db && !wild_compare(db, acl_db->db, db_is_pattern));
+}
+
+
+/*
+ Lookup in the acl_users or acl_dbs for the best matching entry corresponding to
+ given user, host and ip parameters (also db, in case of ACL_DB)
+
+ Historical note:
- /* Should not use this function with more than 4 arguments for compare. */
- DBUG_ASSERT(count <= 4);
+ In the past, both arrays were sorted just by ACL_ENTRY::sort field and were
+ searched linearly, until the first match of (username,host) pair was found.
- while (count--)
+ This function uses optimizations (binary search by username), yet preserves the
+ historical behavior, i.e the returns a match with highest ACL_ENTRY::sort.
+*/
+template <typename T> T* find_by_username_or_anon(T* arr, size_t len, const char *user,
+ const char *host, const char *ip,
+ const char *db, my_bool db_is_pattern, bool (*match_db_func)(T*,const char *,my_bool))
+{
+ size_t i;
+ T *ret = NULL;
+
+ // Check entries matching user name.
+ size_t start = find_first_user(arr, len, user);
+ for (i= start; i < len; i++)
{
- char *start, *str= va_arg(args,char*);
- uint chars= 0;
- uint wild_pos= 0; /* first wildcard position */
+ T *entry= &arr[i];
+ if (i > start && strcmp(user, entry->get_username()))
+ break;
- if ((start= str))
+ if (compare_hostname(&entry->host, host, ip) && (!match_db_func || match_db_func(entry, db, db_is_pattern)))
{
- for (; *str ; str++)
- {
- if (*str == wild_prefix && str[1])
- str++;
- else if (*str == wild_many || *str == wild_one)
- {
- wild_pos= (uint) (str - start) + 1;
- break;
- }
- chars= 128; // Marker that chars existed
- }
+ ret= entry;
+ break;
}
- sort= (sort << 8) + (wild_pos ? MY_MIN(wild_pos, 127U) : chars);
}
- va_end(args);
- return sort;
-}
+ // Look also for anonymous user (username is empty string)
+ // Due to sort by name, entries for anonymous user start at the start of array.
+ for (i= 0; i < len; i++)
+ {
+ T *entry = &arr[i];
+ if (*entry->get_username() || (ret && acl_compare(entry, ret) >= 0))
+ break;
+ if (compare_hostname(&entry->host, host, ip) && (!match_db_func || match_db_func(entry, db, db_is_pattern)))
+ {
+ ret= entry;
+ break;
+ }
+ }
+ return ret;
+}
-static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
+static ACL_DB *acl_db_find(const char *db, const char *user, const char *host, const char *ip, my_bool db_is_pattern)
{
- if (a->sort > b->sort)
- return -1;
- if (a->sort < b->sort)
- return 1;
- return 0;
+ return find_by_username_or_anon(acl_dbs.front(), acl_dbs.elements(),
+ user, host, ip, db, db_is_pattern, match_db);
}
@@ -2433,13 +2961,13 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host,
const char *ip, const char *db)
{
int res= 1;
- uint i;
ACL_USER *acl_user= 0;
DBUG_ENTER("acl_getroot");
DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
host, ip, user, db));
- sctx->user= user;
+ sctx->init();
+ sctx->user= *user ? user : NULL;
sctx->host= host;
sctx->ip= ip;
sctx->host_or_ip= host ? host : (safe_str(ip));
@@ -2455,9 +2983,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host,
mysql_mutex_lock(&acl_cache->lock);
- sctx->master_access= 0;
sctx->db_access= 0;
- *sctx->priv_user= *sctx->priv_host= *sctx->priv_role= 0;
if (host[0]) // User, not Role
{
@@ -2466,26 +2992,12 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host,
if (acl_user)
{
res= 0;
- for (i=0 ; i < acl_dbs.elements() ; i++)
- {
- ACL_DB *acl_db= &acl_dbs.at(i);
- if (!acl_db->user ||
- (user && user[0] && !strcmp(user, acl_db->user)))
- {
- if (compare_hostname(&acl_db->host, host, ip))
- {
- if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
- {
- sctx->db_access= acl_db->access;
- break;
- }
- }
- }
- }
+ if (ACL_DB *acl_db= acl_db_find(db, user, host, ip, FALSE))
+ sctx->db_access= acl_db->access;
+
sctx->master_access= acl_user->access;
- if (acl_user->user.str)
- strmake_buf(sctx->priv_user, user);
+ strmake_buf(sctx->priv_user, user);
if (acl_user->host.hostname)
strmake_buf(sctx->priv_host, acl_user->host.hostname);
@@ -2497,26 +3009,12 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host,
if (acl_role)
{
res= 0;
- for (i=0 ; i < acl_dbs.elements() ; i++)
- {
- ACL_DB *acl_db= &acl_dbs.at(i);
- if (!acl_db->user ||
- (user && user[0] && !strcmp(user, acl_db->user)))
- {
- if (compare_hostname(&acl_db->host, "", ""))
- {
- if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
- {
- sctx->db_access= acl_db->access;
- break;
- }
- }
- }
- }
+ if (ACL_DB *acl_db= acl_db_find(db, user, "", "", FALSE))
+ sctx->db_access = acl_db->access;
+
sctx->master_access= acl_role->access;
- if (acl_role->user.str)
- strmake_buf(sctx->priv_role, user);
+ strmake_buf(sctx->priv_role, user);
}
}
@@ -2542,20 +3040,9 @@ static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data)
*/
static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip)
{
- ACL_USER *result= NULL;
- mysql_mutex_assert_owner(&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.str ||
- !strcmp(user, acl_user_tmp->user.str)) &&
- compare_hostname(&acl_user_tmp->host, host, ip))
- {
- result= acl_user_tmp;
- break;
- }
- }
- return result;
+ return find_by_username_or_anon<ACL_USER>
+ (reinterpret_cast<ACL_USER*>(acl_users.buffer), acl_users.elements,
+ user, host, ip, NULL, FALSE, NULL);
}
static int check_user_can_set_role(THD *thd, const char *user, const char *host,
@@ -2716,7 +3203,6 @@ static uchar* check_get_key(ACL_USER *buff, size_t *length,
return (uchar*) buff->host.hostname;
}
-
static void acl_update_role(const char *rolename, ulong privileges)
{
ACL_ROLE *role= find_acl_role(rolename);
@@ -2725,148 +3211,106 @@ static void acl_update_role(const char *rolename, ulong privileges)
}
-static void acl_update_user(const char *user, const char *host,
- const char *password, size_t password_len,
- enum SSL_type ssl_type,
- const char *ssl_cipher,
- const char *x509_issuer,
- const char *x509_subject,
- USER_RESOURCES *mqh,
- ulong privileges,
- const LEX_CSTRING *plugin,
- const LEX_CSTRING *auth)
+ACL_USER::ACL_USER(THD *thd, const LEX_USER &combo,
+ const Account_options &options,
+ const ulong privileges)
{
- mysql_mutex_assert_owner(&acl_cache->lock);
+ user= safe_lexcstrdup_root(&acl_memroot, combo.user);
+ update_hostname(&host, safe_strdup_root(&acl_memroot, combo.host.str));
+ hostname_length= combo.host.length;
+ sort= get_magic_sort("hu", host.hostname, user.str);
+ password_last_changed= thd->query_start();
+ password_lifetime= -1;
+ my_init_dynamic_array(&role_grants, sizeof(ACL_USER *), 0, 8, MYF(0));
+}
+
- for (uint i=0 ; i < acl_users.elements ; i++)
+static int acl_user_update(THD *thd, ACL_USER *acl_user, uint nauth,
+ const LEX_USER &combo,
+ const Account_options &options,
+ const ulong privileges)
+{
+ if (nauth)
{
- ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
- if (acl_user->eq(user, host))
+ if (acl_user->nauth >= nauth)
+ acl_user->nauth= nauth;
+ else
+ acl_user->alloc_auth(&acl_memroot, nauth);
+
+ USER_AUTH *auth= combo.auth;
+ for (uint i= 0; i < nauth; i++, auth= auth->next)
{
- if (plugin->str[0])
- {
- acl_user->plugin= *plugin;
- acl_user->auth_string.str= auth->str ?
- strmake_root(&acl_memroot, 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(&acl_memroot, plugin->str, plugin->length);
- }
- else
- if (password[0])
- {
- acl_user->auth_string.str= strmake_root(&acl_memroot, password, password_len);
- acl_user->auth_string.length= password_len;
- set_user_salt(acl_user, password, password_len);
- set_user_plugin(acl_user, password_len);
- }
- acl_user->access=privileges;
- if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
- acl_user->user_resource.questions=mqh->questions;
- if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
- acl_user->user_resource.updates=mqh->updates;
- if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
- acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
- if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
- acl_user->user_resource.user_conn= mqh->user_conn;
- if (mqh->specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
- acl_user->user_resource.max_statement_time= mqh->max_statement_time;
- if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
- {
- acl_user->ssl_type= ssl_type;
- acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) :
- 0);
- acl_user->x509_issuer= (x509_issuer ? strdup_root(&acl_memroot,x509_issuer) :
- 0);
- acl_user->x509_subject= (x509_subject ?
- strdup_root(&acl_memroot,x509_subject) : 0);
- }
- /* search complete: */
- break;
+ acl_user->auth[i].plugin= auth->plugin;
+ acl_user->auth[i].auth_string= safe_lexcstrdup_root(&acl_memroot, auth->auth_str);
+ if (fix_user_plugin_ptr(acl_user->auth + i))
+ acl_user->auth[i].plugin= safe_lexcstrdup_root(&acl_memroot, auth->plugin);
+ if (set_user_auth(thd, acl_user->user, acl_user->auth + i, auth->pwtext))
+ return 1;
}
}
-}
+ acl_user->access= privileges;
+ if (options.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
+ acl_user->user_resource.questions= options.questions;
+ if (options.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
+ acl_user->user_resource.updates= options.updates;
+ if (options.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
+ acl_user->user_resource.conn_per_hour= options.conn_per_hour;
+ if (options.specified_limits & USER_RESOURCES::USER_CONNECTIONS)
+ acl_user->user_resource.user_conn= options.user_conn;
+ if (options.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
+ acl_user->user_resource.max_statement_time= options.max_statement_time;
+ if (options.ssl_type != SSL_TYPE_NOT_SPECIFIED)
+ {
+ acl_user->ssl_type= options.ssl_type;
+ acl_user->ssl_cipher= safe_strdup_root(&acl_memroot, options.ssl_cipher.str);
+ acl_user->x509_issuer= safe_strdup_root(&acl_memroot,
+ safe_str(options.x509_issuer.str));
+ acl_user->x509_subject= safe_strdup_root(&acl_memroot,
+ safe_str(options.x509_subject.str));
+ }
+ if (options.account_locked != ACCOUNTLOCK_UNSPECIFIED)
+ acl_user->account_locked= options.account_locked == ACCOUNTLOCK_LOCKED;
-static void acl_insert_role(const char *rolename, ulong privileges)
-{
- ACL_ROLE *entry;
+ /* Unexpire the user password */
+ if (nauth)
+ {
+ acl_user->password_expired= false;
+ acl_user->password_last_changed= thd->query_start();;
+ }
- mysql_mutex_assert_owner(&acl_cache->lock);
- entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot);
- (void) my_init_dynamic_array(&entry->parent_grantee,
- sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
- (void) my_init_dynamic_array(&entry->role_grants,sizeof(ACL_ROLE *),
- 8, 8, MYF(0));
+ switch (options.password_expire) {
+ case PASSWORD_EXPIRE_UNSPECIFIED:
+ break;
+ case PASSWORD_EXPIRE_NOW:
+ acl_user->password_expired= true;
+ break;
+ case PASSWORD_EXPIRE_NEVER:
+ acl_user->password_lifetime= 0;
+ break;
+ case PASSWORD_EXPIRE_DEFAULT:
+ acl_user->password_lifetime= -1;
+ break;
+ case PASSWORD_EXPIRE_INTERVAL:
+ acl_user->password_lifetime= options.num_expiration_days;
+ break;
+ }
- my_hash_insert(&acl_roles, (uchar *)entry);
+ return 0;
}
-static void acl_insert_user(const char *user, const char *host,
- const char *password, size_t password_len,
- enum SSL_type ssl_type,
- const char *ssl_cipher,
- const char *x509_issuer,
- const char *x509_subject,
- USER_RESOURCES *mqh,
- ulong privileges,
- const LEX_CSTRING *plugin,
- const LEX_CSTRING *auth)
+static void acl_insert_role(const char *rolename, ulong privileges)
{
- ACL_USER acl_user;
+ ACL_ROLE *entry;
mysql_mutex_assert_owner(&acl_cache->lock);
+ entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot);
+ my_init_dynamic_array(&entry->parent_grantee,
+ sizeof(ACL_USER_BASE *), 0, 8, MYF(0));
+ my_init_dynamic_array(&entry->role_grants, sizeof(ACL_ROLE *), 0, 8, MYF(0));
- bzero(&acl_user, sizeof(acl_user));
- acl_user.user.str=*user ? strdup_root(&acl_memroot,user) : 0;
- acl_user.user.length= strlen(user);
- update_hostname(&acl_user.host, safe_strdup_root(&acl_memroot, host));
- if (plugin->str[0])
- {
- acl_user.plugin= *plugin;
- acl_user.auth_string.str= auth->str ?
- strmake_root(&acl_memroot, 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(&acl_memroot, plugin->str, plugin->length);
- }
- else
- {
- acl_user.auth_string.str= strmake_root(&acl_memroot, password, password_len);
- acl_user.auth_string.length= password_len;
- set_user_salt(&acl_user, password, password_len);
- set_user_plugin(&acl_user, password_len);
- }
-
- acl_user.flags= 0;
- acl_user.access=privileges;
- acl_user.user_resource = *mqh;
- acl_user.sort=get_sort(2, acl_user.host.hostname, acl_user.user.str);
- acl_user.hostname_length=(uint) strlen(host);
- acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ?
- ssl_type : SSL_TYPE_NONE);
- acl_user.ssl_cipher= ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) : 0;
- acl_user.x509_issuer= x509_issuer ? strdup_root(&acl_memroot,x509_issuer) : 0;
- acl_user.x509_subject=x509_subject ? strdup_root(&acl_memroot,x509_subject) : 0;
- (void) my_init_dynamic_array(&acl_user.role_grants, sizeof(ACL_USER *),
- 8, 8, MYF(0));
-
- (void) push_dynamic(&acl_users,(uchar*) &acl_user);
- if (!acl_user.host.hostname ||
- (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
- allow_all_hosts=1; // Anyone can connect /* purecov: tested */
- my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
- sizeof(ACL_USER),(qsort_cmp) acl_compare);
-
- /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
- rebuild_check_host();
-
- /*
- Rebuild every user's role_grants since 'acl_users' has been sorted
- and old pointers to ACL_USER elements are no longer valid
- */
- rebuild_role_grants();
+ my_hash_insert(&acl_roles, (uchar *)entry);
}
@@ -2877,16 +3321,13 @@ static bool acl_update_db(const char *user, const char *host, const char *db,
bool updated= false;
- for (uint i=0 ; i < acl_dbs.elements() ; i++)
+ for (size_t i= acl_find_db_by_username(user); i < acl_dbs.elements(); i++)
{
ACL_DB *acl_db= &acl_dbs.at(i);
- if ((!acl_db->user && !user[0]) ||
- (acl_db->user &&
- !strcmp(user,acl_db->user)))
+ if (!strcmp(user,acl_db->user))
{
if ((!acl_db->host.hostname && !host[0]) ||
- (acl_db->host.hostname &&
- !strcmp(host, acl_db->host.hostname)))
+ (acl_db->host.hostname && !strcmp(host, acl_db->host.hostname)))
{
if ((!acl_db->db && !db[0]) ||
(acl_db->db && !strcmp(db,acl_db->db)))
@@ -2903,6 +3344,8 @@ static bool acl_update_db(const char *user, const char *host, const char *db,
}
}
}
+ else
+ break;
}
return updated;
@@ -2932,9 +3375,9 @@ static void acl_insert_db(const char *user, const char *host, const char *db,
update_hostname(&acl_db.host, safe_strdup_root(&acl_memroot, host));
acl_db.db=strdup_root(&acl_memroot,db);
acl_db.initial_access= acl_db.access= privileges;
- acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
+ acl_db.sort=get_magic_sort("hdu", acl_db.host.hostname, acl_db.db, acl_db.user);
acl_dbs.push(acl_db);
- acl_dbs.sort((acl_dbs_cmp)acl_compare);
+ rebuild_acl_dbs();
}
@@ -2980,26 +3423,16 @@ ulong acl_get(const char *host, const char *ip,
/*
Check if there are some access rights for database and user
*/
- for (i=0 ; i < acl_dbs.elements() ; i++)
+ if (ACL_DB *acl_db= acl_db_find(db,user, host, ip, db_is_pattern))
{
- ACL_DB *acl_db= &acl_dbs.at(i);
- if (!acl_db->user || !strcmp(user,acl_db->user))
- {
- if (compare_hostname(&acl_db->host,host,ip))
- {
- if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
- {
- db_access=acl_db->access;
- if (acl_db->host.hostname)
- goto exit; // Fully specified. Take it
- /* the host table is not used for roles */
- if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user))
- goto exit;
- break; /* purecov: tested */
- }
- }
- }
+ db_access= acl_db->access;
+ if (acl_db->host.hostname)
+ goto exit; // Fully specified. Take it
+ /* the host table is not used for roles */
+ if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user))
+ goto exit;
}
+
if (!db_access)
goto exit; // Can't be better
@@ -3297,7 +3730,7 @@ static int check_alter_user(THD *thd, const char *host, const char *user)
if (IF_WSREP((!WSREP(thd) || !thd->wsrep_applier), 1) &&
!thd->slave_thread && !thd->security_ctx->priv_user[0] &&
- !in_bootstrap)
+ !thd->bootstrap)
{
my_message(ER_PASSWORD_ANONYMOUS_USER,
ER_THD(thd, ER_PASSWORD_ANONYMOUS_USER),
@@ -3312,10 +3745,13 @@ static int check_alter_user(THD *thd, const char *host, const char *user)
if (!thd->slave_thread &&
IF_WSREP((!WSREP(thd) || !thd->wsrep_applier),1) &&
- (strcmp(thd->security_ctx->priv_user, user) ||
- my_strcasecmp(system_charset_info, host,
- thd->security_ctx->priv_host)))
+ !thd->security_ctx->is_priv_user(user, host))
{
+ if (thd->security_ctx->password_expired)
+ {
+ my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
+ goto end;
+ }
if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
goto end;
}
@@ -3339,13 +3775,8 @@ end:
bool check_change_password(THD *thd, LEX_USER *user)
{
LEX_USER *real_user= get_current_user(thd, user);
-
- if (fix_and_copy_user(real_user, user, thd) ||
- validate_password(real_user, thd))
- return true;
-
- *user= *real_user;
-
+ user->user= real_user->user;
+ user->host= real_user->host;
return check_alter_user(thd, user->host.str, user->user.str);
}
@@ -3362,17 +3793,20 @@ bool check_change_password(THD *thd, LEX_USER *user)
*/
bool change_password(THD *thd, LEX_USER *user)
{
- Grant_tables tables(Table_user, TL_WRITE);
+ Grant_tables tables;
/* Buffer should be extended when password length is extended. */
char buff[512];
ulong query_length= 0;
enum_binlog_format save_binlog_format;
- int result=0;
+ bool result= false, acl_cache_is_locked= false;
+ ACL_USER *acl_user;
+ ACL_USER::AUTH auth;
+ const char *password_plugin= 0;
const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
DBUG_ENTER("change_password");
DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
- user->host.str, user->user.str, user->pwhash.str));
- DBUG_ASSERT(user->host.str != 0); // Ensured by parent
+ user->host.str, user->user.str, user->auth->auth_str.str));
+ DBUG_ASSERT(user->host.str != 0); // Ensured by caller
/*
This statement will be replicated as a statement, even when using
@@ -3383,80 +3817,87 @@ bool change_password(THD *thd, LEX_USER *user)
*/
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
- if (mysql_bin_log.is_open() ||
- (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)))
- {
- query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
- safe_str(user->user.str), safe_str(user->host.str),
- safe_str(user->pwhash.str));
- }
-
if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
- {
- thd->set_query(buff, query_length, system_charset_info);
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL);
- }
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
- if ((result= tables.open_and_lock(thd)))
+ if ((result= tables.open_and_lock(thd, Table_user, TL_WRITE)))
DBUG_RETURN(result != 1);
- result= 1;
-
+ acl_cache_is_locked= 1;
mysql_mutex_lock(&acl_cache->lock);
- ACL_USER *acl_user;
+
if (!(acl_user= find_user_exact(user->host.str, user->user.str)))
{
- mysql_mutex_unlock(&acl_cache->lock);
- my_message(ER_PASSWORD_NO_MATCH,
- ER_THD(thd, ER_PASSWORD_NO_MATCH), MYF(0));
+ my_error(ER_PASSWORD_NO_MATCH, MYF(0));
goto end;
}
- /* update loaded acl entry: */
- if (acl_user->plugin.str == native_password_plugin_name.str ||
- acl_user->plugin.str == old_password_plugin_name.str)
+ if (acl_user->nauth == 1 &&
+ (acl_user->auth[0].plugin.str == native_password_plugin_name.str ||
+ acl_user->auth[0].plugin.str == old_password_plugin_name.str))
{
- acl_user->auth_string.str= strmake_root(&acl_memroot, user->pwhash.str, user->pwhash.length);
- acl_user->auth_string.length= user->pwhash.length;
- set_user_salt(acl_user, user->pwhash.str, user->pwhash.length);
-
- set_user_plugin(acl_user, user->pwhash.length);
+ /* historical hack of auto-changing the plugin */
+ acl_user->auth[0].plugin= guess_auth_plugin(thd, user->auth->auth_str.length);
}
- else
- push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_SET_PASSWORD_AUTH_PLUGIN,
- ER_THD(thd, ER_SET_PASSWORD_AUTH_PLUGIN));
- if (update_user_table(thd, tables.user_table(),
- safe_str(acl_user->host.hostname),
- safe_str(acl_user->user.str),
- user->pwhash.str, user->pwhash.length))
+ for (uint i=0; i < acl_user->nauth; i++)
{
- mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
+ auth= acl_user->auth[i];
+ auth.auth_string= safe_lexcstrdup_root(&acl_memroot, user->auth->auth_str);
+ int r= set_user_auth(thd, user->user, &auth, user->auth->pwtext);
+ if (r == ER_SET_PASSWORD_AUTH_PLUGIN)
+ password_plugin= auth.plugin.str;
+ else if (r)
+ goto end;
+ else
+ {
+ acl_user->auth[i]= auth;
+ password_plugin= 0;
+ break;
+ }
+ }
+ if (password_plugin)
+ {
+ my_error(ER_SET_PASSWORD_AUTH_PLUGIN, MYF(0), password_plugin);
goto end;
}
- acl_cache->clear(1); // Clear locked hostname cache
+ /* Update the acl password expired state of user */
+ acl_user->password_last_changed= thd->query_start();
+ acl_user->password_expired= false;
+
+ /* If user is the connected user, reset the password expired field on sctx
+ and allow the user to exit sandbox mode */
+ if (thd->security_ctx->is_priv_user(user->user.str, user->host.str))
+ thd->security_ctx->password_expired= false;
+
+ if (update_user_table_password(thd, tables.user_table(), *acl_user))
+ goto end;
+
+ acl_cache->clear(1); // Clear locked hostname cache
mysql_mutex_unlock(&acl_cache->lock);
- result= 0;
+ result= acl_cache_is_locked= 0;
if (mysql_bin_log.is_open())
{
+ query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
+ user->user.str, safe_str(user->host.str), auth.auth_string.str);
DBUG_ASSERT(query_length);
thd->clear_error();
result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
FALSE, FALSE, FALSE, 0) > 0;
}
end:
+ if (acl_cache_is_locked)
+ mysql_mutex_unlock(&acl_cache->lock);
close_mysql_tables(thd);
#ifdef WITH_WSREP
-WSREP_ERROR_LABEL:
+wsrep_error_label:
if (WSREP(thd) && !thd->wsrep_applier)
{
WSREP_TO_ISOLATION_END;
thd->set_query(query_save);
- thd->wsrep_exec_mode = LOCAL_STATE;
}
#endif /* WITH_WSREP */
thd->restore_stmt_binlog_format(save_binlog_format);
@@ -3475,20 +3916,19 @@ int acl_check_set_default_role(THD *thd, const char *host, const char *user,
int acl_set_default_role(THD *thd, const char *host, const char *user,
const char *rolename)
{
- Grant_tables tables(Table_user, TL_WRITE);
+ Grant_tables tables;
char user_key[MAX_KEY_LENGTH];
int result= 1;
int error;
ulong query_length= 0;
bool clear_role= FALSE;
char buff[512];
- enum_binlog_format save_binlog_format=
- thd->get_current_stmt_binlog_format();
+ enum_binlog_format save_binlog_format= thd->get_current_stmt_binlog_format();
const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
DBUG_ENTER("acl_set_default_role");
DBUG_PRINT("enter",("host: '%s' user: '%s' rolename: '%s'",
- safe_str(user), safe_str(host), safe_str(rolename)));
+ user, safe_str(host), safe_str(rolename)));
if (!strcasecmp(rolename, "NONE"))
clear_role= TRUE;
@@ -3498,7 +3938,7 @@ int acl_set_default_role(THD *thd, const char *host, const char *user,
{
query_length=
sprintf(buff,"SET DEFAULT ROLE '%-.120s' FOR '%-.120s'@'%-.120s'",
- safe_str(rolename), safe_str(user), safe_str(host));
+ safe_str(rolename), user, safe_str(host));
}
/*
@@ -3514,7 +3954,7 @@ int acl_set_default_role(THD *thd, const char *host, const char *user,
{
thd->set_query(buff, query_length, system_charset_info);
// Attention!!! here is implicit goto error;
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL);
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
}
/*
@@ -3522,7 +3962,7 @@ int acl_set_default_role(THD *thd, const char *host, const char *user,
TODO(cvicentiu) Should move this block out in a new function.
*/
{
- if ((result= tables.open_and_lock(thd)))
+ if ((result= tables.open_and_lock(thd, Table_user, TL_WRITE)))
DBUG_RETURN(result != 1);
const User_table& user_table= tables.user_table();
@@ -3555,17 +3995,8 @@ int acl_set_default_role(THD *thd, const char *host, const char *user,
/* update the mysql.user table with the new default role */
tables.user_table().table()->use_all_columns();
- if (!tables.user_table().default_role())
- {
- my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
- table->alias.c_ptr(), DEFAULT_ROLE_COLUMN_IDX + 1,
- tables.user_table().num_fields(),
- static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
- mysql_mutex_unlock(&acl_cache->lock);
- goto end;
- }
- user_table.host()->store(host,(uint) strlen(host), system_charset_info);
- user_table.user()->store(user,(uint) strlen(user), system_charset_info);
+ user_table.set_host(host, strlen(host));
+ user_table.set_user(user, strlen(user));
key_copy((uchar *) user_key, table->record[0], table->key_info,
table->key_info->key_length);
@@ -3579,9 +4010,8 @@ int acl_set_default_role(THD *thd, const char *host, const char *user,
goto end;
}
store_record(table, record[1]);
- user_table.default_role()->store(acl_user->default_rolename.str,
- acl_user->default_rolename.length,
- system_charset_info);
+ user_table.set_default_role(acl_user->default_rolename.str,
+ acl_user->default_rolename.length);
if (unlikely(error= table->file->ha_update_row(table->record[1],
table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
@@ -3606,13 +4036,12 @@ int acl_set_default_role(THD *thd, const char *host, const char *user,
}
#ifdef WITH_WSREP
-WSREP_ERROR_LABEL:
+wsrep_error_label:
if (WSREP(thd) && !thd->wsrep_applier)
{
WSREP_TO_ISOLATION_END;
thd->set_query(query_save);
- thd->wsrep_exec_mode = LOCAL_STATE;
}
#endif /* WITH_WSREP */
@@ -3654,17 +4083,22 @@ bool is_acl_user(const char *host, const char *user)
return res;
}
+
/*
Find first entry that matches the specified user@host pair
*/
-static ACL_USER * find_user_exact(const char *host, const char *user)
+static ACL_USER *find_user_exact(const char *host, const char *user)
{
mysql_mutex_assert_owner(&acl_cache->lock);
+ size_t start= acl_find_user_by_name(user);
- for (uint i=0 ; i < acl_users.elements ; i++)
+ for (size_t i= start; i < acl_users.elements; i++)
{
- ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
- if (acl_user->eq(user, host))
+ ACL_USER *acl_user= dynamic_element(&acl_users, i, ACL_USER*);
+ if (i > start && strcmp(acl_user->user.str, user))
+ return 0;
+
+ if (!my_strcasecmp(system_charset_info, acl_user->host.hostname, host))
return acl_user;
}
return 0;
@@ -3677,10 +4111,14 @@ static ACL_USER * find_user_wild(const char *host, const char *user, const char
{
mysql_mutex_assert_owner(&acl_cache->lock);
- for (uint i=0 ; i < acl_users.elements ; i++)
+ size_t start = acl_find_user_by_name(user);
+
+ for (size_t i= start; i < acl_users.elements; i++)
{
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
- if (acl_user->wild_eq(user, host, ip))
+ if (i > start && strcmp(acl_user->user.str, user))
+ break;
+ if (compare_hostname(&acl_user->host, host, ip ? ip : host))
return acl_user;
}
return 0;
@@ -3698,7 +4136,7 @@ static ACL_ROLE *find_acl_role(const char *role)
mysql_mutex_assert_owner(&acl_cache->lock);
ACL_ROLE *r= (ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)role,
- safe_strlen(role));
+ strlen(role));
DBUG_RETURN(r);
}
@@ -3850,53 +4288,23 @@ bool hostname_requires_resolving(const char *hostname)
}
-void set_authentication_plugin_from_password(const User_table& user_table,
- const char* password, size_t password_length)
-{
- if (password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH ||
- password_length == 0)
- {
- user_table.plugin()->store(native_password_plugin_name.str,
- native_password_plugin_name.length,
- system_charset_info);
- }
- else
- {
- DBUG_ASSERT(password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323);
- user_table.plugin()->store(old_password_plugin_name.str,
- old_password_plugin_name.length,
- system_charset_info);
- }
- user_table.authentication_string()->store(password,
- password_length,
- system_charset_info);
-}
/**
Update record for user in mysql.user privilege table with new password.
- @param thd THD
- @param table Pointer to TABLE object for open mysql.user table
- @param host Hostname
- @param user Username
- @param new_password New password hash
- @param new_password_len Length of new password hash
-
@see change_password
*/
-static bool update_user_table(THD *thd, const User_table& user_table,
- const char *host, const char *user,
- const char *new_password, size_t new_password_len)
+static bool update_user_table_password(THD *thd, const User_table& user_table,
+ const ACL_USER &user)
{
char user_key[MAX_KEY_LENGTH];
int error;
- DBUG_ENTER("update_user_table");
- DBUG_PRINT("enter",("user: %s host: %s",user,host));
+ DBUG_ENTER("update_user_table_password");
TABLE *table= user_table.table();
table->use_all_columns();
- user_table.host()->store(host,(uint) strlen(host), system_charset_info);
- user_table.user()->store(user,(uint) strlen(user), system_charset_info);
+ user_table.set_host(user.host.hostname, user.hostname_length);
+ user_table.set_user(user.user.str, user.user.length);
key_copy((uchar *) user_key, table->record[0], table->key_info,
table->key_info->key_length);
@@ -3905,28 +4313,30 @@ static bool update_user_table(THD *thd, const User_table& user_table,
HA_READ_KEY_EXACT))
{
my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
- MYF(0)); /* purecov: deadcode */
- DBUG_RETURN(1); /* purecov: deadcode */
+ MYF(0));
+ DBUG_RETURN(1);
}
- store_record(table,record[1]);
+ store_record(table, record[1]);
- if (user_table.plugin())
+ if (user_table.set_auth(user))
{
- set_authentication_plugin_from_password(user_table, new_password,
- new_password_len);
+ my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
+ user_table.name().str, 3, user_table.num_fields(),
+ static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
+ DBUG_RETURN(1);
}
- if (user_table.password())
- user_table.password()->store(new_password, new_password_len, system_charset_info);
-
+ user_table.set_password_expired(user.password_expired);
+ user_table.set_password_last_changed(user.password_last_changed);
if (unlikely(error= table->file->ha_update_row(table->record[1],
table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
{
- table->file->print_error(error,MYF(0)); /* purecov: deadcode */
+ table->file->print_error(error,MYF(0));
DBUG_RETURN(1);
}
+
DBUG_RETURN(0);
}
@@ -3948,7 +4358,8 @@ static bool test_if_create_new_users(THD *thd)
{
TABLE_LIST tl;
ulong db_access;
- tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, TL_WRITE);
+ tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLE_NAME[USER_TABLE],
+ NULL, TL_WRITE);
create_new_users= 1;
db_access=acl_get(sctx->host, sctx->ip,
@@ -3968,63 +4379,41 @@ static bool test_if_create_new_users(THD *thd)
/****************************************************************************
Handle GRANT commands
****************************************************************************/
+static USER_AUTH auth_no_password;
static int replace_user_table(THD *thd, const User_table &user_table,
- LEX_USER &combo,
- ulong rights, bool revoke_grant,
- bool can_create_user, bool no_auto_create)
+ LEX_USER * const combo, ulong rights,
+ const bool revoke_grant, const bool can_create_user,
+ const bool no_auto_create)
{
int error = -1;
+ uint nauth= 0;
bool old_row_exists=0;
- char what= (revoke_grant) ? 'N' : 'Y';
uchar user_key[MAX_KEY_LENGTH];
- bool handle_as_role= combo.is_role();
+ bool handle_as_role= combo->is_role();
LEX *lex= thd->lex;
TABLE *table= user_table.table();
+ ACL_USER new_acl_user, *old_acl_user= 0;
DBUG_ENTER("replace_user_table");
mysql_mutex_assert_owner(&acl_cache->lock);
- if (combo.pwhash.str && combo.pwhash.str[0])
- {
- if (combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
- combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
- {
- DBUG_ASSERT(0);
- my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
- DBUG_RETURN(-1);
- }
- }
- else
- combo.pwhash= empty_clex_str;
-
- /* if the user table is not up to date, we can't handle role updates */
- if (!user_table.is_role() && handle_as_role)
- {
- my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
- "user", ROLE_ASSIGN_COLUMN_IDX + 1, user_table.num_fields(),
- static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
- DBUG_RETURN(-1);
- }
-
table->use_all_columns();
- user_table.host()->store(combo.host.str,combo.host.length,
- system_charset_info);
- user_table.user()->store(combo.user.str,combo.user.length,
- system_charset_info);
+ user_table.set_host(combo->host.str,combo->host.length);
+ user_table.set_user(combo->user.str,combo->user.length);
key_copy(user_key, table->record[0], table->key_info,
table->key_info->key_length);
if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
HA_WHOLE_KEY, HA_READ_KEY_EXACT))
{
- /* what == 'N' means revoke */
- if (what == 'N')
+ if (revoke_grant)
{
- if (combo.host.length)
- my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
+ if (combo->host.length)
+ my_error(ER_NONEXISTING_GRANT, MYF(0), combo->user.str,
+ combo->host.str);
else
- my_error(ER_INVALID_ROLE, MYF(0), combo.user.str);
+ my_error(ER_INVALID_ROLE, MYF(0), combo->user.str);
goto end;
}
/*
@@ -4040,7 +4429,7 @@ static int replace_user_table(THD *thd, const User_table &user_table,
see also test_if_create_new_users()
*/
- else if (!combo.pwhash.length && !combo.plugin.length && no_auto_create)
+ else if (!combo->has_auth() && no_auto_create)
{
my_error(ER_PASSWORD_NO_MATCH, MYF(0));
goto end;
@@ -4050,21 +4439,14 @@ static int replace_user_table(THD *thd, const User_table &user_table,
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;
- }
- }
+
+ if (!combo->auth)
+ combo->auth= &auth_no_password;
old_row_exists = 0;
- restore_record(table,s->default_values);
- user_table.host()->store(combo.host.str,combo.host.length,
- system_charset_info);
- user_table.user()->store(combo.user.str,combo.user.length,
- system_charset_info);
+ restore_record(table, s->default_values);
+ user_table.set_host(combo->host.str, combo->host.length);
+ user_table.set_user(combo->user.str, combo->user.length);
}
else
{
@@ -4072,138 +4454,117 @@ static int replace_user_table(THD *thd, const User_table &user_table,
store_record(table,record[1]); // Save copy for update
}
- if (!old_row_exists || combo.pwtext.length || combo.pwhash.length)
- if (!handle_as_role && validate_password(&combo, thd))
- goto end;
+ for (USER_AUTH *auth= combo->auth; auth; auth= auth->next)
+ {
+ nauth++;
+ if (auth->plugin.length)
+ {
+ if (!plugin_is_ready(&auth->plugin, MYSQL_AUTHENTICATION_PLUGIN))
+ {
+ my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth->plugin.str);
+ goto end;
+ }
+ }
+ else
+ auth->plugin= guess_auth_plugin(thd, auth->auth_str.length);
+ }
/* Update table columns with new privileges */
+ user_table.set_access(rights, revoke_grant);
+ rights= user_table.get_access();
- ulong priv;
- priv = SELECT_ACL;
- for (uint i= 0; i < user_table.num_privileges(); i++, priv <<= 1)
+ if (handle_as_role)
{
- if (priv & rights)
- user_table.priv_field(i)->store(&what, 1, &my_charset_latin1);
+ if (old_row_exists && !user_table.get_is_role())
+ {
+ goto end;
+ }
+ if (user_table.set_is_role(true))
+ {
+ my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
+ user_table.name().str,
+ ROLE_ASSIGN_COLUMN_IDX + 1, user_table.num_fields(),
+ static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
+ goto end;
+ }
}
+ else
+ {
+ old_acl_user= find_user_exact(combo->host.str, combo->user.str);
+ if ((old_acl_user != NULL) != old_row_exists)
+ {
+ my_error(ER_PASSWORD_NO_MATCH, MYF(0));
+ goto end;
+ }
+ new_acl_user= old_row_exists ? *old_acl_user :
+ ACL_USER(thd, *combo, lex->account_options, rights);
+ if (acl_user_update(thd, &new_acl_user, nauth,
+ *combo, lex->account_options, rights))
+ goto end;
- rights= user_table.get_access();
+ if (user_table.set_auth(new_acl_user))
+ {
+ my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
+ user_table.name().str, 3, user_table.num_fields(),
+ static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
+ DBUG_RETURN(1);
+ }
- DBUG_PRINT("info",("table fields: %d", user_table.num_fields()));
- /* If we don't have a password column, we'll use the authentication_string
- column later. */
- if (combo.pwhash.str[0] && user_table.password())
- user_table.password()->store(combo.pwhash.str, combo.pwhash.length,
- system_charset_info);
- /* We either have the password column, the plugin column, or both. Otherwise
- we have a corrupt user table. */
- DBUG_ASSERT(user_table.password() || user_table.plugin());
- if (user_table.ssl_type()) /* From 4.0.0 we have more fields */
- {
- /* We write down SSL related ACL stuff */
- switch (lex->ssl_type) {
- case SSL_TYPE_ANY:
- user_table.ssl_type()->store(STRING_WITH_LEN("ANY"),
- &my_charset_latin1);
- user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
- user_table.x509_issuer()->store("", 0, &my_charset_latin1);
- user_table.x509_subject()->store("", 0, &my_charset_latin1);
+ switch (lex->account_options.ssl_type) {
+ case SSL_TYPE_NOT_SPECIFIED:
break;
+ case SSL_TYPE_NONE:
+ case SSL_TYPE_ANY:
case SSL_TYPE_X509:
- user_table.ssl_type()->store(STRING_WITH_LEN("X509"),
- &my_charset_latin1);
- user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
- user_table.x509_issuer()->store("", 0, &my_charset_latin1);
- user_table.x509_subject()->store("", 0, &my_charset_latin1);
+ user_table.set_ssl_type(lex->account_options.ssl_type);
+ user_table.set_ssl_cipher("", 0);
+ user_table.set_x509_issuer("", 0);
+ user_table.set_x509_subject("", 0);
break;
case SSL_TYPE_SPECIFIED:
- user_table.ssl_type()->store(STRING_WITH_LEN("SPECIFIED"),
- &my_charset_latin1);
- user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
- user_table.x509_issuer()->store("", 0, &my_charset_latin1);
- user_table.x509_subject()->store("", 0, &my_charset_latin1);
- if (lex->ssl_cipher)
- user_table.ssl_cipher()->store(lex->ssl_cipher,
- strlen(lex->ssl_cipher),
- system_charset_info);
- if (lex->x509_issuer)
- user_table.x509_issuer()->store(lex->x509_issuer,
- strlen(lex->x509_issuer),
- system_charset_info);
- if (lex->x509_subject)
- user_table.x509_subject()->store(lex->x509_subject,
- strlen(lex->x509_subject),
- system_charset_info);
- break;
- case SSL_TYPE_NOT_SPECIFIED:
- break;
- case SSL_TYPE_NONE:
- user_table.ssl_type()->store("", 0, &my_charset_latin1);
- user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
- user_table.x509_issuer()->store("", 0, &my_charset_latin1);
- user_table.x509_subject()->store("", 0, &my_charset_latin1);
+ user_table.set_ssl_type(lex->account_options.ssl_type);
+ if (lex->account_options.ssl_cipher.str)
+ user_table.set_ssl_cipher(lex->account_options.ssl_cipher.str,
+ lex->account_options.ssl_cipher.length);
+ else
+ user_table.set_ssl_cipher("", 0);
+ if (lex->account_options.x509_issuer.str)
+ user_table.set_x509_issuer(lex->account_options.x509_issuer.str,
+ lex->account_options.x509_issuer.length);
+ else
+ user_table.set_x509_issuer("", 0);
+ if (lex->account_options.x509_subject.str)
+ user_table.set_x509_subject(lex->account_options.x509_subject.str,
+ lex->account_options.x509_subject.length);
+ else
+ user_table.set_x509_subject("", 0);
break;
}
- USER_RESOURCES mqh= lex->mqh;
- if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
- user_table.max_questions()->store((longlong) mqh.questions, TRUE);
- if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
- user_table.max_updates()->store((longlong) mqh.updates, TRUE);
- if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
- user_table.max_connections()->store((longlong) mqh.conn_per_hour, TRUE);
- if (user_table.max_user_connections() &&
- (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
- user_table.max_user_connections()->store((longlong) mqh.user_conn, FALSE);
- if (user_table.plugin())
- {
- user_table.plugin()->set_notnull();
- user_table.authentication_string()->set_notnull();
- if (combo.plugin.str[0])
- {
- DBUG_ASSERT(combo.pwhash.str[0] == 0);
- if (user_table.password())
- user_table.password()->reset();
- user_table.plugin()->store(combo.plugin.str, combo.plugin.length,
- system_charset_info);
- user_table.authentication_string()->store(combo.auth.str, combo.auth.length,
- system_charset_info);
- }
- if (combo.pwhash.str[0])
- {
- DBUG_ASSERT(combo.plugin.str[0] == 0);
- /* We have Password column. */
- if (user_table.password())
- {
- user_table.plugin()->reset();
- user_table.authentication_string()->reset();
- }
- else
- {
- /* We do not have Password column. Use PLUGIN && Authentication_string
- columns instead. */
- set_authentication_plugin_from_password(user_table,
- combo.pwhash.str,
- combo.pwhash.length);
- }
- }
+ if (lex->account_options.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
+ user_table.set_max_questions(lex->account_options.questions);
+ if (lex->account_options.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
+ user_table.set_max_updates(lex->account_options.updates);
+ if (lex->account_options.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
+ user_table.set_max_connections(lex->account_options.conn_per_hour);
+ if (lex->account_options.specified_limits & USER_RESOURCES::USER_CONNECTIONS)
+ user_table.set_max_user_connections(lex->account_options.user_conn);
+ if (lex->account_options.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
+ user_table.set_max_statement_time(lex->account_options.max_statement_time);
- if (user_table.max_statement_time())
- {
- if (mqh.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
- user_table.max_statement_time()->store(mqh.max_statement_time);
- }
- }
- mqh_used= (mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour ||
- mqh.user_conn || mqh.max_statement_time != 0.0);
+ mqh_used= (mqh_used || lex->account_options.questions || lex->account_options.updates ||
+ lex->account_options.conn_per_hour || lex->account_options.user_conn ||
+ lex->account_options.max_statement_time != 0.0);
- /* table format checked earlier */
- if (handle_as_role)
+ if (lex->account_options.account_locked != ACCOUNTLOCK_UNSPECIFIED)
+ user_table.set_account_locked(new_acl_user.account_locked);
+
+ if (nauth)
+ user_table.set_password_last_changed(new_acl_user.password_last_changed);
+ if (lex->account_options.password_expire != PASSWORD_EXPIRE_UNSPECIFIED)
{
- if (old_row_exists && !user_table.check_is_role())
- {
- goto end;
- }
- user_table.is_role()->store("Y", 1, system_charset_info);
+ user_table.set_password_lifetime(new_acl_user.password_lifetime);
+ user_table.set_password_expired(new_acl_user.password_expired);
}
}
@@ -4243,37 +4604,31 @@ end:
if (likely(!error))
{
acl_cache->clear(1); // Clear privilege cache
- if (old_row_exists)
+ if (handle_as_role)
{
- if (handle_as_role)
- acl_update_role(combo.user.str, rights);
+ if (old_row_exists)
+ acl_update_role(combo->user.str, rights);
else
- acl_update_user(combo.user.str, combo.host.str,
- combo.pwhash.str, combo.pwhash.length,
- lex->ssl_type,
- lex->ssl_cipher,
- lex->x509_issuer,
- lex->x509_subject,
- &lex->mqh,
- rights,
- &combo.plugin,
- &combo.auth);
+ acl_insert_role(combo->user.str, rights);
}
else
{
- if (handle_as_role)
- acl_insert_role(combo.user.str, rights);
+ if (old_acl_user)
+ *old_acl_user= new_acl_user;
else
- acl_insert_user(combo.user.str, combo.host.str,
- combo.pwhash.str, combo.pwhash.length,
- lex->ssl_type,
- lex->ssl_cipher,
- lex->x509_issuer,
- lex->x509_subject,
- &lex->mqh,
- rights,
- &combo.plugin,
- &combo.auth);
+ {
+ push_new_user(new_acl_user);
+ rebuild_acl_users();
+
+ /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
+ rebuild_check_host();
+
+ /*
+ Rebuild every user's role_grants since 'acl_users' has been sorted
+ and old pointers to ACL_USER elements are no longer valid
+ */
+ rebuild_role_grants();
+ }
}
}
DBUG_RETURN(error);
@@ -4286,13 +4641,13 @@ end:
static int replace_db_table(TABLE *table, const char *db,
const LEX_USER &combo,
- ulong rights, bool revoke_grant)
+ ulong rights, const bool revoke_grant)
{
uint i;
ulong priv,store_rights;
bool old_row_exists=0;
int error;
- char what= (revoke_grant) ? 'N' : 'Y';
+ char what= revoke_grant ? 'N' : 'Y';
uchar user_key[MAX_KEY_LENGTH];
DBUG_ENTER("replace_db_table");
@@ -4321,7 +4676,7 @@ static int replace_db_table(TABLE *table, const char *db,
HA_WHOLE_KEY,
HA_READ_KEY_EXACT))
{
- if (what == 'N')
+ if (revoke_grant)
{ // no row, no revoke
my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
goto abort;
@@ -4581,6 +4936,13 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
DBUG_ENTER("replace_proxies_priv_table");
+ if (!table)
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), MYSQL_SCHEMA_NAME.str,
+ MYSQL_TABLE_NAME[PROXIES_PRIV_TABLE].str);
+ DBUG_RETURN(-1);
+ }
+
/* Check if there is such a user in user table in memory? */
if (!find_user_wild(user->host.str,user->user.str))
{
@@ -4716,7 +5078,7 @@ public:
char *db, *user, *tname, *hash_key;
ulong privs;
ulong init_privs; /* privileges found in physical table */
- ulong sort;
+ ulonglong sort;
size_t key_length;
GRANT_NAME(const char *h, const char *d,const char *u,
const char *t, ulong p, bool is_routine);
@@ -4762,7 +5124,7 @@ void GRANT_NAME::set_user_details(const char *h, const char *d,
my_casedn_str(files_charset_info, db);
}
user = strdup_root(&grant_memroot,u);
- sort= get_sort(3,host.hostname,db,user);
+ sort= get_magic_sort("hdu", host.hostname, db, user);
if (tname != t)
{
tname= strdup_root(&grant_memroot, t);
@@ -4804,7 +5166,6 @@ GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
update_hostname(&host, hostname);
db= get_field(&grant_memroot,form->field[1]);
- sort= get_sort(3, host.hostname, db, user);
tname= get_field(&grant_memroot,form->field[3]);
if (!db || !tname)
{
@@ -4812,6 +5173,7 @@ GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
privs= 0;
return; /* purecov: inspected */
}
+ sort= get_magic_sort("hdu", host.hostname, db, user);
if (lower_case_table_names)
{
my_casedn_str(files_charset_info, db);
@@ -4990,7 +5352,7 @@ routine_hash_search(const char *host, const char *ip, const char *db,
const char *user, const char *tname, const Sp_handler *sph,
bool exact)
{
- return (GRANT_TABLE*)
+ return (GRANT_NAME*)
name_hash_search(sph->get_priv_hash(),
host, ip, db, user, tname, exact, TRUE);
}
@@ -5004,6 +5366,10 @@ table_hash_search(const char *host, const char *ip, const char *db,
user, tname, exact, FALSE);
}
+static bool column_priv_insert(GRANT_TABLE *grant)
+{
+ return my_hash_insert(&column_priv_hash,(uchar*) grant);
+}
static GRANT_COLUMN *
column_hash_search(GRANT_TABLE *t, const char *cname, size_t length)
@@ -5233,6 +5599,15 @@ static inline void get_grantor(THD *thd, char *grantor)
strxmov(grantor, user, "@", host, NullS);
}
+
+/**
+ Revoke rights from a grant table entry.
+
+ @return 0 ok
+ @return 1 fatal error (error given)
+ @return -1 grant table was revoked
+*/
+
static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
TABLE *table, const LEX_USER &combo,
const char *db, const char *table_name,
@@ -5257,7 +5632,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
{
my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
MYF(0)); /* purecov: deadcode */
- DBUG_RETURN(-1); /* purecov: deadcode */
+ DBUG_RETURN(1); /* purecov: deadcode */
}
}
@@ -5288,7 +5663,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
combo.user.str, combo.host.str,
table_name); /* purecov: deadcode */
- DBUG_RETURN(-1); /* purecov: deadcode */
+ DBUG_RETURN(1); /* purecov: deadcode */
}
old_row_exists = 0;
restore_record(table,record[1]); // Get saved record
@@ -5351,13 +5726,14 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
else
{
my_hash_delete(&column_priv_hash,(uchar*) grant_table);
+ DBUG_RETURN(-1); // Entry revoked
}
DBUG_RETURN(0);
/* This should never happen */
table_error:
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
- DBUG_RETURN(-1); /* purecov: deadcode */
+ DBUG_RETURN(1); /* purecov: deadcode */
}
@@ -5378,6 +5754,13 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
HASH *hash= sph->get_priv_hash();
DBUG_ENTER("replace_routine_table");
+ if (!table)
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), MYSQL_SCHEMA_NAME.str,
+ MYSQL_TABLE_NAME[PROCS_PRIV_TABLE].str);
+ DBUG_RETURN(-1);
+ }
+
if (revoke_grant && !grant_name->init_privs) // only inherited role privs
{
my_hash_delete(hash, (uchar*) grant_name);
@@ -5892,7 +6275,7 @@ static int update_role_db(int merged, int first, ulong access,
acl_db.db= acl_dbs.at(first).db;
acl_db.access= access;
acl_db.initial_access= 0;
- acl_db.sort=get_sort(3, "", acl_db.db, role);
+ acl_db.sort= get_magic_sort("hdu", "", acl_db.db, role);
acl_dbs.push(acl_db);
return 2;
}
@@ -5975,22 +6358,29 @@ static bool merge_role_db_privileges(ACL_ROLE *grantee, const char *dbname,
}
update_flags|= update_role_db(merged, first, access, grantee->user.str);
- /*
- to make this code a bit simpler, we sort on deletes, to move
- deleted elements to the end of the array. strictly speaking it's
- unnecessary, it'd be faster to remove them in one O(N) array scan.
-
- on the other hand, qsort on almost sorted array is pretty fast anyway...
- */
- if (update_flags & (2|4))
- { // inserted or deleted, need to sort
- acl_dbs.sort((acl_dbs_cmp)acl_compare);
- }
if (update_flags & 4)
- { // deleted, trim the end
- while (acl_dbs.elements() && acl_dbs.back()->sort == 0)
- acl_dbs.pop();
+ {
+ // Remove elements marked for deletion.
+ uint count= 0;
+ for(uint i= 0; i < acl_dbs.elements(); i++)
+ {
+ ACL_DB *acl_db= &acl_dbs.at(i);
+ if (acl_db->sort)
+ {
+ if (i > count)
+ acl_dbs.set(count, *acl_db);
+ count++;
+ }
+ }
+ acl_dbs.elements(count);
}
+
+
+ if (update_flags & 2)
+ { // inserted, need to sort
+ rebuild_acl_dbs();
+ }
+
return update_flags;
}
@@ -6104,7 +6494,7 @@ static int update_role_table_columns(GRANT_TABLE *merged,
privs, cols);
merged->init_privs= merged->init_cols= 0;
update_role_columns(merged, first, last);
- my_hash_insert(&column_priv_hash,(uchar*) merged);
+ column_priv_insert(merged);
return 2;
}
else if ((privs | cols) == 0)
@@ -6376,33 +6766,17 @@ static bool merge_one_role_privileges(ACL_ROLE *grantee)
static bool has_auth(LEX_USER *user, LEX *lex)
{
- return user->pwtext.length || user->pwhash.length || user->plugin.length || user->auth.length ||
- lex->ssl_type != SSL_TYPE_NOT_SPECIFIED || lex->ssl_cipher ||
- lex->x509_issuer || lex->x509_subject ||
- lex->mqh.specified_limits;
-}
-
-static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd)
-{
- if (to != from)
- {
- /* preserve authentication information, if LEX_USER was reallocated */
- to->pwtext= from->pwtext;
- to->pwhash= from->pwhash;
- to->plugin= from->plugin;
- to->auth= from->auth;
- }
-
- if (fix_lex_user(thd, to))
- return true;
-
- return false;
+ return user->has_auth() ||
+ lex->account_options.ssl_type != SSL_TYPE_NOT_SPECIFIED ||
+ lex->account_options.ssl_cipher.str ||
+ lex->account_options.x509_issuer.str ||
+ lex->account_options.x509_subject.str ||
+ lex->account_options.specified_limits;
}
static bool copy_and_check_auth(LEX_USER *to, LEX_USER *from, THD *thd)
{
- if (fix_and_copy_user(to, from, thd))
- return true;
+ to->auth= from->auth;
// if changing auth for an existing user
if (has_auth(to, thd->lex) && find_user_exact(to->host.str, to->user.str))
@@ -6440,7 +6814,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
bool revoke_grant)
{
ulong column_priv= 0;
- int result;
+ int result, res;
List_iterator <LEX_USER> str_list (user_list);
LEX_USER *Str, *tmp_Str;
bool create_new_users=0;
@@ -6514,10 +6888,10 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
Open the mysql.user and mysql.tables_priv tables.
Don't open column table if we don't need it !
*/
- int maybe_columns_priv= 0;
+ int tables_to_open= Table_user | Table_tables_priv;
if (column_priv ||
(revoke_grant && ((rights & COL_ACLS) || columns.elements)))
- maybe_columns_priv= Table_columns_priv;
+ tables_to_open|= Table_columns_priv;
/*
The lock api is depending on the thd->lex variable which needs to be
@@ -6532,9 +6906,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
*/
thd->lex->sql_command= backup.sql_command;
- Grant_tables tables(Table_user | Table_tables_priv | maybe_columns_priv,
- TL_WRITE);
- if ((result= tables.open_and_lock(thd)))
+ Grant_tables tables;
+ if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
{
thd->lex->restore_backup_query_tables_list(&backup);
DBUG_RETURN(result != 1);
@@ -6559,7 +6932,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
}
/* Create user if needed */
error= copy_and_check_auth(Str, tmp_Str, thd) ||
- replace_user_table(thd, tables.user_table(), *Str,
+ replace_user_table(thd, tables.user_table(), Str,
0, revoke_grant, create_new_users,
MY_TEST(thd->variables.sql_mode &
MODE_NO_AUTO_CREATE_USER));
@@ -6584,12 +6957,12 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
result= TRUE;
continue;
}
- grant_table = new GRANT_TABLE (Str->host.str, db_name,
- Str->user.str, table_name,
- rights,
- column_priv);
+ grant_table= new (&grant_memroot) GRANT_TABLE(Str->host.str, db_name,
+ Str->user.str, table_name,
+ rights,
+ column_priv);
if (!grant_table ||
- my_hash_insert(&column_priv_hash,(uchar*) grant_table))
+ column_priv_insert(grant_table))
{
result= TRUE; /* purecov: deadcode */
continue; /* purecov: deadcode */
@@ -6632,22 +7005,24 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
/* TODO(cvicentiu) refactor replace_table_table to use Tables_priv_table
instead of TABLE directly. */
- if (replace_table_table(thd, grant_table, tables.tables_priv_table().table(),
- *Str, db_name, table_name,
- rights, column_priv, revoke_grant))
- {
- /* Should only happen if table is crashed */
- result= TRUE; /* purecov: deadcode */
- }
- else if (tables.columns_priv_table().table_exists())
+ if (tables.columns_priv_table().table_exists())
{
/* TODO(cvicentiu) refactor replace_column_table to use Columns_priv_table
instead of TABLE directly. */
if (replace_column_table(grant_table, tables.columns_priv_table().table(),
*Str, columns, db_name, table_name, rights,
revoke_grant))
- {
result= TRUE;
+ }
+ if ((res= replace_table_table(thd, grant_table,
+ tables.tables_priv_table().table(),
+ *Str, db_name, table_name,
+ rights, column_priv, revoke_grant)))
+ {
+ if (res > 0)
+ {
+ /* Should only happen if table is crashed */
+ result= TRUE; /* purecov: deadcode */
}
}
if (Str->is_role())
@@ -6659,9 +7034,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
mysql_mutex_unlock(&acl_cache->lock);
if (!result) /* success */
- {
result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- }
mysql_rwlock_unlock(&LOCK_grant);
@@ -6714,8 +7087,8 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list,
DBUG_RETURN(TRUE);
}
- Grant_tables tables(Table_user | Table_procs_priv, TL_WRITE);
- if ((result= tables.open_and_lock(thd)))
+ Grant_tables tables;
+ if ((result= tables.open_and_lock(thd, Table_user | Table_procs_priv, TL_WRITE)))
DBUG_RETURN(result != 1);
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
@@ -6739,7 +7112,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list,
}
/* Create user if needed */
if (copy_and_check_auth(Str, tmp_Str, thd) ||
- replace_user_table(thd, tables.user_table(), *Str,
+ replace_user_table(thd, tables.user_table(), Str,
0, revoke_grant, create_new_users,
MY_TEST(thd->variables.sql_mode &
MODE_NO_AUTO_CREATE_USER)))
@@ -6772,12 +7145,8 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list,
}
}
- /* TODO(cvicentiu) refactor replace_routine_table to use Tables_procs_priv
- instead of TABLE directly. */
- if (tables.procs_priv_table().no_such_table() ||
- replace_routine_table(thd, grant_name, tables.procs_priv_table().table(),
- *Str, db_name, table_name, sph, rights,
- revoke_grant) != 0)
+ if (replace_routine_table(thd, grant_name, tables.procs_priv_table().table(),
+ *Str, db_name, table_name, sph, rights, revoke_grant) != 0)
{
result= TRUE;
continue;
@@ -6915,8 +7284,8 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
no_auto_create_user= MY_TEST(thd->variables.sql_mode &
MODE_NO_AUTO_CREATE_USER);
- Grant_tables tables(Table_user | Table_roles_mapping, TL_WRITE);
- if ((result= tables.open_and_lock(thd)))
+ Grant_tables tables;
+ if ((result= tables.open_and_lock(thd, Table_user | Table_roles_mapping, TL_WRITE)))
DBUG_RETURN(result != 1);
mysql_rwlock_wrlock(&LOCK_grant);
@@ -7015,7 +7384,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
user_combo.user = username;
if (copy_and_check_auth(&user_combo, &user_combo, thd) ||
- replace_user_table(thd, tables.user_table(), user_combo, 0,
+ replace_user_table(thd, tables.user_table(), &user_combo, 0,
false, create_new_user,
no_auto_create_user))
{
@@ -7155,9 +7524,9 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
proxied_user= str_list++;
}
- Grant_tables tables(Table_user | (is_proxy ? Table_proxies_priv : Table_db),
- TL_WRITE);
- if ((result= tables.open_and_lock(thd)))
+ const uint tables_to_open= Table_user | (is_proxy ? Table_proxies_priv : Table_db);
+ Grant_tables tables;
+ if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
DBUG_RETURN(result != 1);
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
@@ -7186,7 +7555,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
}
if (copy_and_check_auth(Str, tmp_Str, thd) ||
- replace_user_table(thd, tables.user_table(), *Str,
+ replace_user_table(thd, tables.user_table(), Str,
(!db ? rights : 0), revoke_grant, create_new_users,
MY_TEST(thd->variables.sql_mode &
MODE_NO_AUTO_CREATE_USER)))
@@ -7208,13 +7577,8 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
}
else if (is_proxy)
{
- /* TODO(cvicentiu) refactor replace_proxies_priv_table to use
- Proxies_priv_table instead of TABLE directly. */
- if (tables.proxies_priv_table().no_such_table() ||
- replace_proxies_priv_table (thd, tables.proxies_priv_table().table(),
- Str, proxied_user,
- rights & GRANT_ACL ? TRUE : FALSE,
- revoke_grant))
+ if (replace_proxies_priv_table(thd, tables.proxies_priv_table().table(),
+ Str, proxied_user, rights & GRANT_ACL ? TRUE : FALSE, revoke_grant))
result= true;
}
if (Str->is_role())
@@ -7350,8 +7714,7 @@ static bool grant_load(THD *thd,
{
sql_print_warning("'tables_priv' entry '%s %s@%s' "
"ignored in --skip-name-resolve mode.",
- mem_check->tname,
- safe_str(mem_check->user),
+ mem_check->tname, mem_check->user,
safe_str(mem_check->host.hostname));
continue;
}
@@ -7359,7 +7722,7 @@ static bool grant_load(THD *thd,
if (! mem_check->ok())
delete mem_check;
- else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
+ else if (column_priv_insert(mem_check))
{
delete mem_check;
goto end_unlock;
@@ -7477,9 +7840,9 @@ bool grant_reload(THD *thd)
obtaining LOCK_grant rwlock.
*/
- Grant_tables tables(Table_tables_priv | Table_columns_priv| Table_procs_priv,
- TL_READ);
- if ((result= tables.open_and_lock(thd)))
+ Grant_tables tables;
+ const uint tables_to_open= Table_tables_priv | Table_columns_priv| Table_procs_priv;
+ if ((result= tables.open_and_lock(thd, tables_to_open, TL_READ)))
DBUG_RETURN(result != 1);
mysql_rwlock_wrlock(&LOCK_grant);
@@ -7995,7 +8358,8 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg,
grant_table= grant->grant_table_user;
grant_table_role= grant->grant_table_role;
- DBUG_ASSERT (grant_table || grant_table_role);
+ if (!grant_table && !grant_table_role)
+ goto err;
}
}
@@ -8417,26 +8781,31 @@ static void add_user_parameters(THD *thd, String *result, ACL_USER* acl_user,
append_identifier(thd, result, acl_user->host.hostname,
acl_user->hostname_length);
- if (acl_user->plugin.str == native_password_plugin_name.str ||
- acl_user->plugin.str == old_password_plugin_name.str)
+ if (acl_user->nauth == 1 &&
+ (acl_user->auth->plugin.str == native_password_plugin_name.str ||
+ acl_user->auth->plugin.str == old_password_plugin_name.str))
{
- if (acl_user->auth_string.length)
+ if (acl_user->auth->auth_string.length)
{
- DBUG_ASSERT(acl_user->salt_len);
result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
- result->append(&acl_user->auth_string);
+ result->append(&acl_user->auth->auth_string);
result->append('\'');
}
}
else
{
result->append(STRING_WITH_LEN(" IDENTIFIED VIA "));
- result->append(&acl_user->plugin);
- if (acl_user->auth_string.length)
+ for (uint i=0; i < acl_user->nauth; i++)
{
- result->append(STRING_WITH_LEN(" USING '"));
- result->append(&acl_user->auth_string);
- result->append('\'');
+ if (i)
+ result->append(STRING_WITH_LEN(" OR "));
+ result->append(&acl_user->auth[i].plugin);
+ if (acl_user->auth[i].auth_string.length)
+ {
+ result->append(STRING_WITH_LEN(" USING '"));
+ result->append(&acl_user->auth[i].auth_string);
+ result->append('\'');
+ }
}
}
/* "show grants" SSL related stuff */
@@ -8448,14 +8817,14 @@ static void add_user_parameters(THD *thd, String *result, ACL_USER* acl_user,
{
int ssl_options = 0;
result->append(STRING_WITH_LEN(" REQUIRE "));
- if (acl_user->x509_issuer)
+ if (acl_user->x509_issuer[0])
{
ssl_options++;
result->append(STRING_WITH_LEN("ISSUER \'"));
result->append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
result->append('\'');
}
- if (acl_user->x509_subject)
+ if (acl_user->x509_subject[0])
{
if (ssl_options++)
result->append(' ');
@@ -8551,6 +8920,16 @@ static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
}
+static void append_auto_expiration_policy(ACL_USER *acl_user, String *r) {
+ if (!acl_user->password_lifetime)
+ r->append(STRING_WITH_LEN(" PASSWORD EXPIRE NEVER"));
+ else if (acl_user->password_lifetime > 0)
+ {
+ r->append(STRING_WITH_LEN(" PASSWORD EXPIRE INTERVAL "));
+ r->append_longlong(acl_user->password_lifetime);
+ r->append(STRING_WITH_LEN(" DAY"));
+ }
+}
bool mysql_show_create_user(THD *thd, LEX_USER *lex_user)
{
@@ -8562,6 +8941,11 @@ bool mysql_show_create_user(THD *thd, LEX_USER *lex_user)
uint head_length;
DBUG_ENTER("mysql_show_create_user");
+ if (!initialized)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
+ DBUG_RETURN(TRUE);
+ }
if (get_show_user(thd, lex_user, &username, &hostname, NULL))
DBUG_RETURN(TRUE);
@@ -8600,12 +8984,42 @@ bool mysql_show_create_user(THD *thd, LEX_USER *lex_user)
append_identifier(thd, &result, username, strlen(username));
add_user_parameters(thd, &result, acl_user, false);
+ if (acl_user->account_locked)
+ result.append(STRING_WITH_LEN(" ACCOUNT LOCK"));
+
+ if (acl_user->password_expired)
+ result.append(STRING_WITH_LEN(" PASSWORD EXPIRE"));
+ else
+ append_auto_expiration_policy(acl_user, &result);
+
protocol->prepare_for_resend();
protocol->store(result.ptr(), result.length(), result.charset());
if (protocol->write())
{
error= true;
}
+
+ /* MDEV-24114 - PASSWORD EXPIRE and PASSWORD EXPIRE [NEVER | INTERVAL X DAY]
+ are two different mechanisms. To make sure a tool can restore the state
+ of a user account, including both the manual expiration state of the
+ account and the automatic expiration policy attached to it, we should
+ print two statements here, a CREATE USER (printed above) and an ALTER USER */
+ if (acl_user->password_expired && acl_user->password_lifetime > -1) {
+ result.length(0);
+ result.append("ALTER USER ");
+ append_identifier(thd, &result, username, strlen(username));
+ result.append('@');
+ append_identifier(thd, &result, acl_user->host.hostname,
+ acl_user->hostname_length);
+ append_auto_expiration_policy(acl_user, &result);
+ protocol->prepare_for_resend();
+ protocol->store(result.ptr(), result.length(), result.charset());
+ if (protocol->write())
+ {
+ error= true;
+ }
+ }
+
my_eof(thd);
end:
@@ -8986,7 +9400,7 @@ static bool show_database_privileges(THD *thd, const char *username,
const char *user, *host;
ACL_DB *acl_db= &acl_dbs.at(i);
- user= safe_str(acl_db->user);
+ user= acl_db->user;
host=acl_db->host.hostname;
/*
@@ -9064,7 +9478,7 @@ static bool show_table_and_column_privileges(THD *thd, const char *username,
GRANT_TABLE *grant_table= (GRANT_TABLE*)
my_hash_element(&column_priv_hash, index);
- user= safe_str(grant_table->user);
+ user= grant_table->user;
host= grant_table->host.hostname;
/*
@@ -9197,7 +9611,7 @@ static int show_routine_grants(THD* thd,
const char *user, *host;
GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
- user= safe_str(grant_proc->user);
+ user= grant_proc->user;
host= grant_proc->host.hostname;
/*
@@ -9525,7 +9939,7 @@ static int handle_grant_table(THD *thd, const Grant_table_base& grant_table,
if (!unlikely(error) && !*host_str)
{
// verify that we got a role or a user, as needed
- if (static_cast<const User_table&>(grant_table).check_is_role() !=
+ if (static_cast<const User_table&>(grant_table).get_is_role() !=
user_from->is_role())
error= HA_ERR_KEY_NOT_FOUND;
}
@@ -9689,8 +10103,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
my_hash_delete(&acl_roles, (uchar*) acl_role);
DBUG_RETURN(1);
}
- acl_role->user.str= strdup_root(&acl_memroot, user_to->user.str);
- acl_role->user.length= user_to->user.length;
+ acl_role->user= safe_lexcstrdup_root(&acl_memroot, user_to->user);
my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key,
old_key_length);
@@ -9786,8 +10199,6 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
default:
DBUG_ASSERT(0);
}
- if (! user)
- user= "";
if (! host)
host= "";
@@ -9881,8 +10292,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
{
switch ( struct_no ) {
case USER_ACL:
- acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str);
- acl_user->user.length= user_to->user.length;
+ acl_user->user= safe_lexcstrdup_root(&acl_memroot, user_to->user);
update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str));
acl_user->hostname_length= strlen(acl_user->host.hostname);
break;
@@ -10230,11 +10640,11 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
DBUG_RETURN(TRUE);
/* CREATE USER may be skipped on replication client. */
- Grant_tables tables(Table_user | Table_db |
- Table_tables_priv | Table_columns_priv |
- Table_procs_priv | Table_proxies_priv |
- Table_roles_mapping, TL_WRITE);
- if ((result= tables.open_and_lock(thd)))
+ Grant_tables tables;
+ const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
+ Table_columns_priv | Table_procs_priv |
+ Table_proxies_priv | Table_roles_mapping;
+ if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
DBUG_RETURN(result != 1);
mysql_rwlock_wrlock(&LOCK_grant);
@@ -10266,13 +10676,6 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
if (!user_name->host.str)
user_name->host= host_not_specified;
- if (fix_lex_user(thd, user_name))
- {
- append_user(thd, &wrong_users, user_name);
- result= TRUE;
- continue;
- }
-
/*
Search all in-memory structures and grant tables
for a mention of the new user/role name.
@@ -10317,7 +10720,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
}
}
- if (replace_user_table(thd, tables.user_table(), *user_name, 0, 0, 1, 0))
+ if (replace_user_table(thd, tables.user_table(), user_name, 0, 0, 1, 0))
{
append_user(thd, &wrong_users, user_name);
result= TRUE;
@@ -10407,11 +10810,11 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
/* DROP USER may be skipped on replication client. */
- Grant_tables tables(Table_user | Table_db |
- Table_tables_priv | Table_columns_priv |
- Table_procs_priv | Table_proxies_priv |
- Table_roles_mapping, TL_WRITE);
- if ((result= tables.open_and_lock(thd)))
+ Grant_tables tables;
+ const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
+ Table_columns_priv | Table_procs_priv |
+ Table_proxies_priv | Table_roles_mapping;
+ if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
DBUG_RETURN(result != 1);
thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
@@ -10517,11 +10920,11 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
DBUG_ENTER("mysql_rename_user");
/* RENAME USER may be skipped on replication client. */
- Grant_tables tables(Table_user | Table_db |
- Table_tables_priv | Table_columns_priv |
- Table_procs_priv | Table_proxies_priv |
- Table_roles_mapping, TL_WRITE);
- if ((result= tables.open_and_lock(thd)))
+ Grant_tables tables;
+ const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
+ Table_columns_priv | Table_procs_priv |
+ Table_proxies_priv | Table_roles_mapping;
+ if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
DBUG_RETURN(result != 1);
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
@@ -10560,8 +10963,12 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
continue;
}
some_users_renamed= TRUE;
+ rebuild_acl_users();
}
+ /* Rebuild 'acl_dbs' since 'acl_users' has been modified */
+ rebuild_acl_dbs();
+
/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
rebuild_check_host();
@@ -10603,8 +11010,8 @@ int mysql_alter_user(THD* thd, List<LEX_USER> &users_list)
bool some_users_altered= false;
/* The only table we're altering is the user table. */
- Grant_tables tables(Table_user, TL_WRITE);
- if ((result= tables.open_and_lock(thd)))
+ Grant_tables tables;
+ if ((result= tables.open_and_lock(thd, Table_user, TL_WRITE)))
DBUG_RETURN(result != 1);
/* Lock ACL data structures until we finish altering all users. */
@@ -10613,13 +11020,12 @@ int mysql_alter_user(THD* thd, List<LEX_USER> &users_list)
LEX_USER *tmp_lex_user;
List_iterator<LEX_USER> users_list_iterator(users_list);
+
while ((tmp_lex_user= users_list_iterator++))
{
LEX_USER* lex_user= get_current_user(thd, tmp_lex_user, false);
- if (!lex_user ||
- fix_lex_user(thd, lex_user) ||
- replace_user_table(thd, tables.user_table(), *lex_user, 0,
- false, false, true))
+ if (!lex_user || replace_user_table(thd, tables.user_table(), lex_user, 0,
+ false, false, true))
{
thd->clear_error();
append_user(thd, &wrong_users, tmp_lex_user);
@@ -10660,9 +11066,7 @@ int mysql_alter_user(THD* thd, List<LEX_USER> &users_list)
static bool
-mysql_revoke_sp_privs(THD *thd,
- Grant_tables *tables,
- const Sp_handler *sph,
+mysql_revoke_sp_privs(THD *thd, Grant_tables *tables, const Sp_handler *sph,
const LEX_USER *lex_user)
{
bool rc= false;
@@ -10673,7 +11077,7 @@ mysql_revoke_sp_privs(THD *thd,
{
const char *user,*host;
GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
- user= safe_str(grant_proc->user);
+ user= grant_proc->user;
host= safe_str(grant_proc->host.hostname);
if (!strcmp(lex_user->user.str, user) &&
@@ -10714,15 +11118,15 @@ mysql_revoke_sp_privs(THD *thd,
bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
{
uint counter, revoked;
- int result;
+ int result, res;
ACL_DB *acl_db;
DBUG_ENTER("mysql_revoke_all");
- Grant_tables tables(Table_user | Table_db |
- Table_tables_priv | Table_columns_priv |
- Table_procs_priv | Table_proxies_priv |
- Table_roles_mapping, TL_WRITE);
- if ((result= tables.open_and_lock(thd)))
+ Grant_tables tables;
+ const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
+ Table_columns_priv | Table_procs_priv |
+ Table_proxies_priv | Table_roles_mapping;
+ if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
DBUG_RETURN(result != 1);
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
@@ -10748,7 +11152,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
continue;
}
- if (replace_user_table(thd, tables.user_table(), *lex_user,
+ if (replace_user_table(thd, tables.user_table(), lex_user,
~(ulong)0, 1, 0, 0))
{
result= -1;
@@ -10765,11 +11169,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
{
for (counter= 0, revoked= 0 ; counter < acl_dbs.elements() ; )
{
- const char *user,*host;
+ const char *user, *host;
- acl_db=&acl_dbs.at(counter);
+ acl_db= &acl_dbs.at(counter);
- user= safe_str(acl_db->user);
+ user= acl_db->user;
host= safe_str(acl_db->host.hostname);
if (!strcmp(lex_user->user.str, user) &&
@@ -10801,42 +11205,41 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
const char *user,*host;
GRANT_TABLE *grant_table=
(GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
- user= safe_str(grant_table->user);
+ user= grant_table->user;
host= safe_str(grant_table->host.hostname);
if (!strcmp(lex_user->user.str,user) &&
!strcmp(lex_user->host.str, host))
{
- /* TODO(cvicentiu) refactor replace_db_table to use
- Db_table instead of TABLE directly. */
- if (replace_table_table(thd, grant_table,
- tables.tables_priv_table().table(),
- *lex_user, grant_table->db,
- grant_table->tname, ~(ulong)0, 0, 1))
- {
+ List<LEX_COLUMN> columns;
+ /* TODO(cvicentiu) refactor replace_db_table to use
+ Db_table instead of TABLE directly. */
+ if (replace_column_table(grant_table,
+ tables.columns_priv_table().table(),
+ *lex_user, columns, grant_table->db,
+ grant_table->tname, ~(ulong)0, 1))
result= -1;
- }
- else
+
+ /* TODO(cvicentiu) refactor replace_db_table to use
+ Db_table instead of TABLE directly. */
+ if ((res= replace_table_table(thd, grant_table,
+ tables.tables_priv_table().table(),
+ *lex_user, grant_table->db,
+ grant_table->tname, ~(ulong)0, 0, 1)))
{
- if (!grant_table->cols)
- {
- revoked= 1;
- continue;
- }
- List<LEX_COLUMN> columns;
- /* TODO(cvicentiu) refactor replace_db_table to use
- Db_table instead of TABLE directly. */
- if (!replace_column_table(grant_table,
- tables.columns_priv_table().table(),
- *lex_user, columns, grant_table->db,
- grant_table->tname, ~(ulong)0, 1))
- {
- revoked= 1;
- continue;
- }
- result= -1;
- }
- }
+ if (res > 0)
+ result= -1;
+ else
+ {
+ /*
+ Entry was deleted. We have to retry the loop as the
+ hash table has probably been reorganized.
+ */
+ revoked= 1;
+ continue;
+ }
+ }
+ }
counter++;
}
} while (revoked);
@@ -11006,11 +11409,11 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
Silence_routine_definer_errors error_handler;
DBUG_ENTER("sp_revoke_privileges");
- Grant_tables tables(Table_user | Table_db |
- Table_tables_priv | Table_columns_priv |
- Table_procs_priv | Table_proxies_priv |
- Table_roles_mapping, TL_WRITE);
- if ((result= tables.open_and_lock(thd)))
+ Grant_tables tables;
+ const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
+ Table_columns_priv | Table_procs_priv |
+ Table_proxies_priv | Table_roles_mapping;
+ if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
DBUG_RETURN(result != 1);
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
@@ -11109,20 +11512,12 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
thd->make_lex_string(&combo->user, combo->user.str, strlen(combo->user.str));
thd->make_lex_string(&combo->host, combo->host.str, strlen(combo->host.str));
- combo->reset_auth();
-
- if(au)
- {
- combo->plugin= au->plugin;
- combo->auth= au->auth_string;
- }
+ combo->auth= NULL;
if (user_list.push_back(combo, thd->mem_root))
DBUG_RETURN(TRUE);
- thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
- thd->lex->ssl_cipher= thd->lex->x509_subject= thd->lex->x509_issuer= 0;
- bzero((char*) &thd->lex->mqh, sizeof(thd->lex->mqh));
+ thd->lex->account_options.reset();
/*
Only care about whether the operation failed or succeeded
@@ -11209,9 +11604,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
or revoking proxy privilege, user is expected to provide entries mentioned
in mysql.user table.
*/
- if (!strcmp(thd->security_ctx->priv_user, user) &&
- !my_strcasecmp(system_charset_info, host,
- thd->security_ctx->priv_host))
+ if (thd->security_ctx->is_priv_user(user, host))
{
DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
thd->security_ctx->priv_user, user,
@@ -11380,10 +11773,10 @@ static int show_database_grants(THD *thd, SHOW_VAR *var, char *buff,
}
#else
+static bool set_user_salt_if_needed(ACL_USER *, int, plugin_ref)
+{ return 0; }
bool check_grant(THD *, ulong, TABLE_LIST *, bool, uint, bool)
-{
- return 0;
-}
+{ return 0; }
#endif /*NO_EMBEDDED_ACCESS_CHECKS */
SHOW_VAR acl_statistics[] = {
@@ -11419,7 +11812,7 @@ bool check_role_is_granted(const char *username,
ACL_USER_BASE *root;
mysql_mutex_lock(&acl_cache->lock);
if (hostname)
- root= find_user_exact(username, hostname);
+ root= find_user_exact(hostname, username);
else
root= find_acl_role(username);
@@ -11596,7 +11989,6 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
NULL, NULL, 1, 1);
- char *curr_host= thd->security_ctx->priv_host_name();
DBUG_ENTER("fill_schema_user_privileges");
if (!initialized)
@@ -11607,12 +11999,11 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
const char *user,*host, *is_grantable="YES";
acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
- user= safe_str(acl_user->user.str);
+ user= acl_user->user.str;
host= safe_str(acl_user->host.hostname);
if (no_global_access &&
- (strcmp(thd->security_ctx->priv_user, user) ||
- my_strcasecmp(system_charset_info, curr_host, host)))
+ !thd->security_ctx->is_priv_user(user, host))
continue;
want_access= acl_user->access;
@@ -11668,7 +12059,6 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
NULL, NULL, 1, 1);
- char *curr_host= thd->security_ctx->priv_host_name();
DBUG_ENTER("fill_schema_schema_privileges");
if (!initialized)
@@ -11680,12 +12070,11 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
const char *user, *host, *is_grantable="YES";
acl_db=&acl_dbs.at(counter);
- user= safe_str(acl_db->user);
+ user= acl_db->user;
host= safe_str(acl_db->host.hostname);
if (no_global_access &&
- (strcmp(thd->security_ctx->priv_user, user) ||
- my_strcasecmp(system_charset_info, curr_host, host)))
+ !thd->security_ctx->is_priv_user(user, host))
continue;
want_access=acl_db->access;
@@ -11742,7 +12131,6 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
NULL, NULL, 1, 1);
- char *curr_host= thd->security_ctx->priv_host_name();
DBUG_ENTER("fill_schema_table_privileges");
mysql_rwlock_rdlock(&LOCK_grant);
@@ -11752,12 +12140,11 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
const char *user, *host, *is_grantable= "YES";
GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
index);
- user= safe_str(grant_table->user);
+ user= grant_table->user;
host= safe_str(grant_table->host.hostname);
if (no_global_access &&
- (strcmp(thd->security_ctx->priv_user, user) ||
- my_strcasecmp(system_charset_info, curr_host, host)))
+ !thd->security_ctx->is_priv_user(user, host))
continue;
ulong table_access= grant_table->privs;
@@ -11825,7 +12212,6 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
NULL, NULL, 1, 1);
- char *curr_host= thd->security_ctx->priv_host_name();
DBUG_ENTER("fill_schema_table_privileges");
mysql_rwlock_rdlock(&LOCK_grant);
@@ -11835,12 +12221,11 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
const char *user, *host, *is_grantable= "YES";
GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
index);
- user= safe_str(grant_table->user);
+ user= grant_table->user;
host= safe_str(grant_table->host.hostname);
if (no_global_access &&
- (strcmp(thd->security_ctx->priv_user, user) ||
- my_strcasecmp(system_charset_info, curr_host, host)))
+ !thd->security_ctx->is_priv_user(user, host))
continue;
ulong table_access= grant_table->cols;
@@ -12180,6 +12565,7 @@ struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
char *pkt;
uint pkt_len;
} cached_server_packet;
+ uint curr_auth; ///< an index in acl_user->auth[]
int packets_read, packets_written; ///< counters for send/received packets
bool make_it_fail;
/** when plugin returns a failure this tells us what really happened */
@@ -12243,7 +12629,7 @@ static void login_failed_error(THD *thd)
static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
const char *data, uint data_len)
{
- DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
+ DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
DBUG_ASSERT(data_len <= 255);
THD *thd= mpvio->auth_info.thd;
@@ -12398,14 +12784,10 @@ static bool secure_auth(THD *thd)
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->auth_info.thd->net;
static uchar switch_plugin_request_buf[]= { 254 };
DBUG_ENTER("send_plugin_request_packet");
- mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
-
const char *client_auth_plugin=
((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
@@ -12423,8 +12805,9 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
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;
+ client_auth_plugin == old_password_plugin_name.str &&
+ my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
+ native_password_plugin_name.str) == 0;
if (switch_from_long_to_short_scramble)
DBUG_RETURN (secure_auth(mpvio->auth_info.thd) ||
@@ -12437,8 +12820,9 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
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;
+ client_auth_plugin == native_password_plugin_name.str &&
+ my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
+ old_password_plugin_name.str) == 0;
if (switch_from_short_to_long_scramble)
{
@@ -12457,6 +12841,23 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
+
+/**
+ Safeguard to avoid blocking the root, when max_password_errors
+ limit is reached.
+
+ Currently, we allow password errors for superuser on localhost.
+
+ @return true, if password errors should be ignored, and user should not be locked.
+*/
+static bool ignore_max_password_errors(const ACL_USER *acl_user)
+{
+ const char *host= acl_user->host.hostname;
+ return (acl_user->access & SUPER_ACL)
+ && (!strcasecmp(host, "localhost") ||
+ !strcmp(host, "127.0.0.1") ||
+ !strcmp(host, "::1"));
+}
/**
Finds acl entry in user database for authentication purposes.
@@ -12475,6 +12876,7 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
mysql_mutex_lock(&acl_cache->lock);
ACL_USER *user= find_user_or_anon(sctx->host, sctx->user, sctx->ip);
+
if (user)
mpvio->acl_user= user->copy(mpvio->auth_info.thd->mem_root);
@@ -12511,33 +12913,29 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
mpvio->make_it_fail= true;
}
+ if (mpvio->acl_user->password_errors >= max_password_errors &&
+ !ignore_max_password_errors(mpvio->acl_user))
+ {
+ my_error(ER_USER_IS_BLOCKED, MYF(0));
+ general_log_print(mpvio->auth_info.thd, COM_CONNECT,
+ ER_THD(mpvio->auth_info.thd, ER_USER_IS_BLOCKED));
+ DBUG_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 &&
+ if (mpvio->acl_user->auth->plugin.str != native_password_plugin_name.str &&
+ mpvio->acl_user->auth->plugin.str != old_password_plugin_name.str &&
!(mpvio->auth_info.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));
+ DBUG_ASSERT(my_strcasecmp(system_charset_info,
+ mpvio->acl_user->auth->plugin.str, native_password_plugin_name.str));
+ DBUG_ASSERT(my_strcasecmp(system_charset_info,
+ mpvio->acl_user->auth->plugin.str, old_password_plugin_name.str));
my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
general_log_print(mpvio->auth_info.thd, COM_CONNECT,
ER_THD(mpvio->auth_info.thd, ER_NOT_SUPPORTED_AUTH_MODE));
DBUG_RETURN (1);
}
-
- mpvio->auth_info.user_name= sctx->user;
- mpvio->auth_info.user_name_length= (uint)strlen(sctx->user);
- mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
- mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length;
- strmake_buf(mpvio->auth_info.authenticated_as, safe_str(mpvio->acl_user->user.str));
-
- DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
- "plugin=%s",
- mpvio->auth_info.user_name,
- mpvio->auth_info.auth_string,
- mpvio->auth_info.authenticated_as,
- mpvio->acl_user->plugin.str));
DBUG_RETURN(0);
}
@@ -12681,7 +13079,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
MYF(0));
DBUG_RETURN(1);
}
- client_plugin= fix_plugin_ptr(next_field);
+ client_plugin= next_field;
next_field+= strlen(next_field) + 1;
}
else
@@ -12690,21 +13088,18 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
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.
+ Normally old clients use old_password_plugin, but for
+ a passwordless accounts we use native_password_plugin.
+ See guess_auth_plugin().
*/
- if (mpvio->acl_user->auth_string.length == 0)
- mpvio->acl_user->plugin= old_password_plugin_name;
+ client_plugin= passwd_len ? old_password_plugin_name.str
+ : native_password_plugin_name.str;
}
}
if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
- read_client_connect_attrs(&next_field, end,
- thd->charset()))
+ read_client_connect_attrs(&next_field, end, thd->charset()))
{
my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
MYF(0));
@@ -12775,7 +13170,12 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
return packet_error;
DBUG_PRINT("info", ("IO layer change in progress..."));
- if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr))
+ mysql_rwlock_rdlock(&LOCK_ssl_refresh);
+ int ssl_ret = sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr);
+ mysql_rwlock_unlock(&LOCK_ssl_refresh);
+ ssl_acceptor_stats_update(ssl_ret);
+
+ if(ssl_ret)
{
DBUG_PRINT("error", ("Failed to accept new SSL connection"));
return packet_error;
@@ -12926,7 +13326,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) &&
(client_plugin < (char *)net->read_pos + pkt_len))
{
- client_plugin= fix_plugin_ptr(client_plugin);
next_field+= strlen(next_field) + 1;
}
else
@@ -12938,15 +13337,13 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
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.
+ Normally old clients use old_password_plugin, but for
+ a passwordless accounts we use native_password_plugin.
+ See guess_auth_plugin().
*/
- if (mpvio->acl_user->auth_string.length == 0)
- mpvio->acl_user->plugin= old_password_plugin_name;
+ client_plugin= passwd_len ? old_password_plugin_name.str
+ : native_password_plugin_name.str;
}
}
@@ -12964,7 +13361,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
restarted and a server auth plugin will read the data that the client
has just send. Cache them to return in the next server_mpvio_read_packet().
*/
- if (!lex_string_eq(&mpvio->acl_user->plugin, plugin_name(mpvio->plugin)))
+ if (!lex_string_eq(&mpvio->acl_user->auth->plugin, plugin_name(mpvio->plugin)))
{
mpvio->cached_client_reply.pkt= passwd;
mpvio->cached_client_reply.pkt_len= (uint)passwd_len;
@@ -13044,6 +13441,7 @@ static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
res= my_net_write(&mpvio->auth_info.thd->net, packet, packet_len) ||
net_flush(&mpvio->auth_info.thd->net);
}
+ mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
mpvio->packets_written++;
DBUG_RETURN(res);
}
@@ -13060,56 +13458,53 @@ static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
*/
static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
{
- MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
+ MPVIO_EXT * const mpvio= (MPVIO_EXT *) param;
+ MYSQL_SERVER_AUTH_INFO * const ai= &mpvio->auth_info;
ulong pkt_len;
DBUG_ENTER("server_mpvio_read_packet");
- if (mpvio->packets_written == 0)
+ if (mpvio->status == MPVIO_EXT::RESTART)
{
- /*
- 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->auth_info.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)
+ if (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++;
+ pkt_len= 0;
+ *buf= 0;
+ goto done;
+ }
- DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
+ if (mpvio->cached_client_reply.pkt)
+ {
+ 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.
+ */
+ if (my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
+ client_auth_plugin) == 0)
+ {
+ mpvio->status= MPVIO_EXT::FAILURE;
+ pkt_len= mpvio->cached_client_reply.pkt_len;
+ *buf= (uchar*) mpvio->cached_client_reply.pkt;
+ mpvio->packets_read++;
+ goto done;
+ }
}
/*
- 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.
+ 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->auth_info.thd->net);
+ pkt_len= my_net_read(&ai->thd->net);
}
else
- pkt_len= my_net_read(&mpvio->auth_info.thd->net);
+ pkt_len= my_net_read(&ai->thd->net);
if (unlikely(pkt_len == packet_error))
goto err;
@@ -13127,14 +13522,24 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
goto err;
}
else
- *buf= mpvio->auth_info.thd->net.read_pos;
+ *buf= ai->thd->net.read_pos;
+
+done:
+ if (set_user_salt_if_needed(mpvio->acl_user, mpvio->curr_auth, mpvio->plugin))
+ goto err;
+
+ ai->user_name= ai->thd->security_ctx->user;
+ ai->user_name_length= (uint) strlen(ai->user_name);
+ ai->auth_string= mpvio->acl_user->auth[mpvio->curr_auth].salt.str;
+ ai->auth_string_length= (ulong) mpvio->acl_user->auth[mpvio->curr_auth].salt.length;
+ strmake_buf(ai->authenticated_as, mpvio->acl_user->user.str);
DBUG_RETURN((int)pkt_len);
err:
if (mpvio->status == MPVIO_EXT::FAILURE)
{
- if (!mpvio->auth_info.thd->is_error())
+ if (!ai->thd->is_error())
my_error(ER_HANDSHAKE_ERROR, MYF(0));
}
DBUG_RETURN(-1);
@@ -13195,24 +13600,25 @@ static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
return 1;
if (acl_user->ssl_cipher)
{
+ const char *ssl_cipher= SSL_get_cipher(ssl);
DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
- acl_user->ssl_cipher, SSL_get_cipher(ssl)));
- if (strcmp(acl_user->ssl_cipher, SSL_get_cipher(ssl)))
+ acl_user->ssl_cipher, ssl_cipher));
+ if (strcmp(acl_user->ssl_cipher, ssl_cipher))
{
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));
+ acl_user->ssl_cipher, ssl_cipher);
return 1;
}
}
- if (!acl_user->x509_issuer && !acl_user->x509_subject)
+ if (!acl_user->x509_issuer[0] && !acl_user->x509_subject[0])
return 0; // all done
/* 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)
+ if (acl_user->x509_issuer[0])
{
char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
@@ -13229,7 +13635,7 @@ static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
free(ptr);
}
/* X509 subject is specified, we check it .. */
- if (acl_user->x509_subject)
+ if (acl_user->x509_subject[0])
{
char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
@@ -13263,35 +13669,25 @@ static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name,
MPVIO_EXT *mpvio)
{
- int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
+ int res= CR_OK;
bool unlock_plugin= false;
- plugin_ref plugin= NULL;
-
- if (auth_plugin_name->str == native_password_plugin_name.str)
- plugin= native_password_plugin;
-#ifndef EMBEDDED_LIBRARY
- else if (auth_plugin_name->str == old_password_plugin_name.str)
- plugin= old_password_plugin;
- else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
- MYSQL_AUTHENTICATION_PLUGIN)))
- unlock_plugin= true;
-#endif
+ plugin_ref plugin= get_auth_plugin(thd, *auth_plugin_name, &unlock_plugin);
mpvio->plugin= plugin;
- old_status= mpvio->status;
+ mpvio->auth_info.user_name= NULL;
if (plugin)
{
- st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
- switch (auth->interface_version >> 8) {
+ st_mysql_auth *info= (st_mysql_auth *) plugin_decl(plugin)->info;
+ switch (info->interface_version >> 8) {
case 0x02:
- res= auth->authenticate_user(mpvio, &mpvio->auth_info);
+ res= info->authenticate_user(mpvio, &mpvio->auth_info);
break;
case 0x01:
{
MYSQL_SERVER_AUTH_INFO_0x0100 compat;
compat.downgrade(&mpvio->auth_info);
- res= auth->authenticate_user(mpvio, (MYSQL_SERVER_AUTH_INFO *)&compat);
+ res= info->authenticate_user(mpvio, (MYSQL_SERVER_AUTH_INFO *)&compat);
compat.upgrade(&mpvio->auth_info);
}
break;
@@ -13311,20 +13707,64 @@ static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name,
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.
+ return res;
+}
- 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
+enum PASSWD_ERROR_ACTION
+{
+ PASSWD_ERROR_CLEAR,
+ PASSWD_ERROR_INCREMENT
+};
- return res;
+/* Increment, or clear password errors for a user. */
+static void handle_password_errors(const char *user, const char *hostname, PASSWD_ERROR_ACTION action)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ mysql_mutex_assert_not_owner(&acl_cache->lock);
+ mysql_mutex_lock(&acl_cache->lock);
+ ACL_USER *u = find_user_exact(hostname, user);
+ if (u)
+ {
+ switch(action)
+ {
+ case PASSWD_ERROR_INCREMENT:
+ u->password_errors++;
+ break;
+ case PASSWD_ERROR_CLEAR:
+ u->password_errors= 0;
+ break;
+ default:
+ DBUG_ASSERT(0);
+ break;
+ }
+ }
+ mysql_mutex_unlock(&acl_cache->lock);
+#endif
}
+static bool check_password_lifetime(THD *thd, const ACL_USER &acl_user)
+{
+ /* the password should never expire */
+ if (!acl_user.password_lifetime)
+ return false;
+
+ longlong interval= acl_user.password_lifetime;
+ if (interval < 0)
+ {
+ interval= default_password_lifetime;
+
+ /* default global policy applies, and that is password never expires */
+ if (!interval)
+ return false;
+ }
+
+ thd->set_time();
+
+ if ((thd->query_start() - acl_user.password_last_changed)/3600/24 >= interval)
+ return true;
+
+ return false;
+}
/**
Perform the handshake, authorize the client and update thd sctx variables.
@@ -13342,7 +13782,6 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
{
int res= CR_OK;
MPVIO_EXT mpvio;
- const LEX_CSTRING *auth_plugin_name= default_auth_plugin_name;
enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
: COM_CONNECT;
DBUG_ENTER("acl_authenticate");
@@ -13350,9 +13789,9 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
bzero(&mpvio, sizeof(mpvio));
mpvio.read_packet= server_mpvio_read_packet;
mpvio.write_packet= server_mpvio_write_packet;
+ mpvio.cached_client_reply.plugin= "";
mpvio.info= server_mpvio_info;
- mpvio.status= MPVIO_EXT::FAILURE;
- mpvio.make_it_fail= false;
+ mpvio.status= MPVIO_EXT::RESTART;
mpvio.auth_info.thd= thd;
mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip;
mpvio.auth_info.host_or_ip_length=
@@ -13368,6 +13807,8 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
DBUG_RETURN(1);
+ res= mpvio.status == MPVIO_EXT::SUCCESS ? CR_OK : CR_ERROR;
+
DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
mpvio.status == MPVIO_EXT::SUCCESS);
}
@@ -13383,29 +13824,35 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
the correct plugin.
*/
- res= do_auth_once(thd, auth_plugin_name, &mpvio);
+ res= do_auth_once(thd, default_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)
+ Security_context * const sctx= thd->security_ctx;
+ const ACL_USER * acl_user= mpvio.acl_user;
+ if (!acl_user)
+ statistic_increment(aborted_connects_preauth, &LOCK_status);
+
+ if (acl_user)
{
- DBUG_ASSERT(mpvio.acl_user);
- DBUG_ASSERT(command == COM_CHANGE_USER ||
- !lex_string_eq(auth_plugin_name, &mpvio.acl_user->plugin));
- auth_plugin_name= &mpvio.acl_user->plugin;
- res= do_auth_once(thd, auth_plugin_name, &mpvio);
+ /*
+ retry the authentication with curr_auth==0 if after receiving the user
+ name we found that we need to switch to a non-default plugin
+ */
+ for (mpvio.curr_auth= mpvio.status != MPVIO_EXT::RESTART;
+ res != CR_OK && mpvio.curr_auth < acl_user->nauth;
+ mpvio.curr_auth++)
+ {
+ thd->clear_error();
+ mpvio.status= MPVIO_EXT::RESTART;
+ res= do_auth_once(thd, &acl_user->auth[mpvio.curr_auth].plugin, &mpvio);
+ }
}
+
if (mpvio.make_it_fail && res == CR_OK)
{
mpvio.status= MPVIO_EXT::FAILURE;
res= CR_ERROR;
}
-
- Security_context *sctx= thd->security_ctx;
- const ACL_USER *acl_user= mpvio.acl_user;
thd->password= mpvio.auth_info.password_used; // remember for error messages
@@ -13433,7 +13880,6 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
{
Host_errors errors;
- DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
switch (res)
{
case CR_AUTH_PLUGIN_ERROR:
@@ -13444,6 +13890,8 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
break;
case CR_AUTH_USER_CREDENTIALS:
errors.m_authentication= 1;
+ if (thd->password && !mpvio.make_it_fail)
+ handle_password_errors(acl_user->user.str, acl_user->host.hostname, PASSWD_ERROR_INCREMENT);
break;
case CR_ERROR:
default:
@@ -13458,12 +13906,17 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
}
sctx->proxy_user[0]= 0;
+ if (thd->password && acl_user->password_errors)
+ {
+ /* Login succeeded, clear password errors.*/
+ handle_password_errors(acl_user->user.str, acl_user->host.hostname, PASSWD_ERROR_CLEAR);
+ }
if (initialized) // if not --skip-grant-tables
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bool is_proxy_user= FALSE;
- const char *auth_user = safe_str(acl_user->user.str);
+ const char *auth_user = acl_user->user.str;
ACL_PROXY_USER *proxy_user;
/* check if the user is allowed to proxy as another user */
proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
@@ -13509,10 +13962,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
#endif
sctx->master_access= acl_user->access;
- if (acl_user->user.str)
- strmake_buf(sctx->priv_user, acl_user->user.str);
- else
- *sctx->priv_user= 0;
+ strmake_buf(sctx->priv_user, acl_user->user.str);
if (acl_user->host.hostname)
strmake_buf(sctx->priv_host, acl_user->host.hostname);
@@ -13533,6 +13983,28 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
DBUG_RETURN(1);
}
+ if (acl_user->account_locked) {
+ status_var_increment(denied_connections);
+ my_error(ER_ACCOUNT_HAS_BEEN_LOCKED, MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ bool client_can_handle_exp_pass= thd->client_capabilities &
+ CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
+ bool password_expired= thd->password != PASSWORD_USED_NO_MENTION
+ && (acl_user->password_expired ||
+ check_password_lifetime(thd, *acl_user));
+
+ if (!client_can_handle_exp_pass && disconnect_on_expired_password &&
+ password_expired)
+ {
+ status_var_increment(denied_connections);
+ my_error(ER_MUST_CHANGE_PASSWORD_LOGIN, MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ sctx->password_expired= password_expired;
+
/*
Don't allow the user to connect if he has done too many queries.
As we are testing max_user_connections == 0 here, it means that we
@@ -13684,12 +14156,11 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
/* generate the scramble, or reuse the old one */
if (thd->scramble[SCRAMBLE_LENGTH])
- {
thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
- /* and send it to the client */
- if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
- DBUG_RETURN(CR_AUTH_HANDSHAKE);
- }
+
+ /* and send it to the client */
+ if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
+ DBUG_RETURN(CR_AUTH_HANDSHAKE);
/* reply and authenticate */
@@ -13740,15 +14211,16 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
DBUG_EXECUTE_IF("native_password_bad_reply", { pkt_len= 12; });
if (pkt_len == 0) /* no password */
- DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK);
+ DBUG_RETURN(info->auth_string_length != 0
+ ? CR_AUTH_USER_CREDENTIALS : CR_OK);
info->password_used= PASSWORD_USED_YES;
if (pkt_len == SCRAMBLE_LENGTH)
{
- if (!mpvio->acl_user->salt_len)
+ if (info->auth_string_length != SCRAMBLE_LENGTH)
DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
- if (check_scramble(pkt, thd->scramble, mpvio->acl_user->salt))
+ if (check_scramble(pkt, thd->scramble, (uchar*)info->auth_string))
DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
else
DBUG_RETURN(CR_OK);
@@ -13758,6 +14230,65 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
DBUG_RETURN(CR_AUTH_HANDSHAKE);
}
+static int native_password_make_scramble(const char *password,
+ size_t password_length, char *hash, size_t *hash_length)
+{
+ DBUG_ASSERT(*hash_length >= SCRAMBLED_PASSWORD_CHAR_LENGTH);
+ if (password_length == 0)
+ *hash_length= 0;
+ else
+ {
+ *hash_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
+ my_make_scrambled_password(hash, password, password_length);
+ }
+ return 0;
+}
+
+/* As this contains is a string of not a valid SCRAMBLE_LENGTH */
+static const char invalid_password[] = "*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE";
+
+static int native_password_get_salt(const char *hash, size_t hash_length,
+ unsigned char *out, size_t *out_length)
+{
+ DBUG_ASSERT(sizeof(invalid_password) > SCRAMBLE_LENGTH);
+ DBUG_ASSERT(*out_length >= SCRAMBLE_LENGTH);
+ DBUG_ASSERT(*out_length >= sizeof(invalid_password));
+ if (hash_length == 0)
+ {
+ *out_length= 0;
+ return 0;
+ }
+
+ if (hash_length != SCRAMBLED_PASSWORD_CHAR_LENGTH)
+ {
+ if (hash_length == 7 && strcmp(hash, "invalid") == 0)
+ {
+ memcpy(out, invalid_password, sizeof(invalid_password));
+ *out_length= sizeof(invalid_password);
+ return 0;
+ }
+ my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
+ return 1;
+ }
+
+ for (const char *c= hash + 1; c < (hash + hash_length); c++)
+ {
+ /* If any non-hex characters are found, mark the password as invalid. */
+ if (!(*c >= '0' && *c <= '9') &&
+ !(*c >= 'A' && *c <= 'F') &&
+ !(*c >= 'a' && *c <= 'f'))
+ {
+ memcpy(out, invalid_password, sizeof(invalid_password));
+ *out_length= sizeof(invalid_password);
+ return 0;
+ }
+ }
+
+ *out_length= SCRAMBLE_LENGTH;
+ get_salt_from_password(out, hash);
+ return 0;
+}
+
static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
MYSQL_SERVER_AUTH_INFO *info)
{
@@ -13768,12 +14299,10 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
/* generate the scramble, or reuse the old one */
if (thd->scramble[SCRAMBLE_LENGTH])
- {
thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
- /* and send it to the client */
- if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
- return CR_AUTH_HANDSHAKE;
- }
+ /* and send it to the client */
+ if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
+ return CR_AUTH_HANDSHAKE;
/* read the reply and authenticate */
if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
@@ -13792,7 +14321,7 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
pkt_len= (int)strnlen((char*)pkt, pkt_len);
if (pkt_len == 0) /* no password */
- return info->auth_string[0] ? CR_AUTH_USER_CREDENTIALS : CR_OK;
+ return info->auth_string_length ? CR_AUTH_USER_CREDENTIALS : CR_OK;
if (secure_auth(thd))
return CR_AUTH_HANDSHAKE;
@@ -13801,30 +14330,64 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
if (pkt_len == SCRAMBLE_LENGTH_323)
{
- if (!mpvio->acl_user->salt_len)
+ if (!info->auth_string_length)
return CR_AUTH_USER_CREDENTIALS;
- return check_scramble_323(pkt, thd->scramble,
- (ulong *) mpvio->acl_user->salt) ?
- CR_AUTH_USER_CREDENTIALS : CR_OK;
+ return check_scramble_323(pkt, thd->scramble, (ulong *) info->auth_string)
+ ? CR_AUTH_USER_CREDENTIALS : CR_OK;
}
my_error(ER_HANDSHAKE_ERROR, MYF(0));
return CR_AUTH_HANDSHAKE;
}
+static int old_password_make_scramble(const char *password,
+ size_t password_length, char *hash, size_t *hash_length)
+{
+ DBUG_ASSERT(*hash_length >= SCRAMBLED_PASSWORD_CHAR_LENGTH_323);
+ if (password_length == 0)
+ *hash_length= 0;
+ else
+ {
+ *hash_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+ my_make_scrambled_password_323(hash, password, password_length);
+ }
+ return 0;
+}
+
+#define SALT_LENGTH_323 (sizeof(ulong)*2)
+static int old_password_get_salt(const char *hash, size_t hash_length,
+ unsigned char *out, size_t *out_length)
+{
+ DBUG_ASSERT(*out_length >= SALT_LENGTH_323);
+
+ if (hash_length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
+ {
+ my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH_323);
+ return 1;
+ }
+
+ *out_length= SALT_LENGTH_323;
+ get_salt_from_password_323((ulong*)out, hash);
+ return 0;
+}
+
static struct st_mysql_auth native_password_handler=
{
MYSQL_AUTHENTICATION_INTERFACE_VERSION,
native_password_plugin_name.str,
- native_password_authenticate
+ native_password_authenticate,
+ native_password_make_scramble,
+ native_password_get_salt
};
static struct st_mysql_auth old_password_handler=
{
MYSQL_AUTHENTICATION_INTERFACE_VERSION,
old_password_plugin_name.str,
- old_password_authenticate
+ old_password_authenticate,
+ old_password_make_scramble,
+ old_password_get_salt
};
maria_declare_plugin(mysql_password)
diff --git a/sql/sql_acl_getsort.ic b/sql/sql_acl_getsort.ic
new file mode 100644
index 00000000000..df55c7c5f1e
--- /dev/null
+++ b/sql/sql_acl_getsort.ic
@@ -0,0 +1,205 @@
+/* Copyright (c) 2019, MariaDB Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/*
+ Returns a number which, if sorted in descending order, magically puts
+ patterns in the order from most specific (e.g. no wildcards) to most generic
+ (e.g. "%"). That is, the larger the number, the more specific the pattern is.
+
+ Takes a template that lists types of following patterns (by the first letter
+ of _h_ostname, _d_bname, _u_sername) and up to four patterns.
+ No more than two can be of 'h' or 'd' type (because one magic value takes 26
+ bits, see below).
+
+ ========================================================================
+
+ Here's how the magic is created:
+
+ Let's look at one iteration of the for() loop. That's one pattern. With
+ wildcards (usernames aren't interesting).
+
+ By definition a pattern A is "more specific" than pattern B if the set of
+ strings that match the pattern A is smaller than the set of strings that
+ match the pattern B. Strings are taken from the big superset of all valid
+ utf8 strings up to the maxlen.
+
+ Strings are matched character by character. For every non-wildcard
+ character there can be only one matching character in the matched string.
+
+ For a wild_one character ('_') any valid utf8 character will do. Below
+ numchars would mean a total number of vaid utf8 characters. It's a huge
+ number. A number of matching strings for wild_one will be numchars.
+
+ For a wild_many character ('%') any number of valid utf8 characters will do.
+ How many string will match it depends on the amount of non-wild_many
+ characters. Say, if a number of non-wildcard characters is N, and a number
+ of wild_one characters is M, and the number of wild_many characters is K,
+ then for K=1 its wild_many character will match any number of valid utf8
+ characters from 0 to L=maxlen-N-M. The number of matching strings will be
+
+ 1 + numchars + numchars^2 + numchars^3 + ... + numchars^L
+
+ Intermediate result: if M=K=0, the pattern will match only one string,
+ if M>0, K=0, the pattern will match numchars^M strings, if K=1, the
+ pattern will match
+
+ numchars^M + 1 + numchars + numchars^2 + ... + numchars^L
+
+ For a more visual notation, let's write these huge numbers not as
+ decimal or binary, but base numchars. Then the last number will be
+ a sum of two numbers: the first is one followed by M zeros, the second
+ constists of L+1 ones:
+
+ 1000{...M...}000 + 111{...L+1...}1111
+
+ This could produce any of the following
+
+ 111...112111...1111 if L > M, K = 1
+ 100...001111...1111 if M > L, K = 1
+ 2111111...111111111 if M = L, K = 1
+ 1111111...111111111 if M = 0, K = 1
+ 1000000...000000000 if K = 0, M > 0
+
+ There are two complications caused by multiple wild_many characters.
+ For, say, two wild_many characters, either can accept any number of utf8
+ characters, as long the the total amount of them is less then or equal to L.
+ Same logic applies to any number of non-consequent wild_many characters
+ (consequent wild_many characters count as one). This gives the number of
+ matching strings of
+
+ 1 + F(K,1)*numchars + F(K,2)*numchars^2 + ... + F(K,L)*numchars^L
+
+ where F(K,R) is the "number of ways one can put R balls into K boxes",
+ that is C^{K-1}_{R+K-1}.
+
+ In the "base numchars" notation, it means that besides 0, 1, and 2,
+ an R-th digit can be F(K,R). For the purpose of comparison, we only need
+ to know the most significant digit, F(K, L).
+ While it can be huge, we don't need the exact value, it's a
+ a monotonously increasing function of K, so if K1>K2, F(K1,L) > F(K2,L)
+ and we can simply compare values of K instead of complex F(K,L).
+
+ The second complication: F(K,R) gives only an upper boundary, the
+ actual number of matched strings can be smaller.
+ Example: pattern "a%b%c" can match "abbc" as a(b)b()c, and as a()b(b)c.
+ F(2,1) = 2, but it's only one string "abbc".
+ We'll ignore it here under assumption that it almost never happens
+ in practice and this simplification won't noticeably disrupt the ordering.
+
+ The last detail: old get_sort function sorted by the non-wildcard prefix
+ length, so in "abc_" and "a_bc" the former one was sorted first. Strictly
+ speaking they're both equally specific, but to preserve the backward
+ compatible sorting we'll use the P "prefix length or 0 if no wildcards"
+ to break ties.
+
+ Now, let's compare two long numbers. Numbers are easy to compare,
+ the longer number is larger. If they both have the same lengths,
+ the one with the larger first digit is larger, and so on.
+
+ But there is no need to actually calculate these numbers.
+ Three numbers L, K, M (and P to break ties) are enough to describe a pattern
+ for a purpose of comparison. L/K/M triplets can be compared like this:
+
+ * case 1: if for both patterns L>M: compare L, K, M, in that order
+ because:
+ - if L1 > L2, the first number is longer
+ - If L1 == L2, then the first digit is a monotonously increasing function
+ of K, so the first digit is larger when K is larger
+ - if K1 == K2, then all other digits in these numbers would be the
+ same too, with the exception of one digit in the middle that
+ got +1 because of +1000{...M...}000. So, whatever number has a
+ larger M will get this +1 first.
+ * case 2: if for both patterns L<M: compare M, L, K, in that order
+ * case 3: if for both patterns L=M: compare L (or M), K
+ * case 4: if one L1>M1, other L2=M2: compare L, K, M
+ * case 5: if one L1<M1, other L2=M2: compare M, L, K
+ * case 6: if one pattern L1>M1, the other M2>L2: first is more generic
+ unless (case 6a) K1=K2=1,M1=0,M2=L2+1 (in that case - equal)
+
+ note that in case 3 one can use a rule from the case either 1 or 2,
+ in the case 4 one can use the rule from the case 1,
+ in the case 5 one can use the rule from the case 2.
+
+ for the case 6 and ignoring the special case 6a, to compare patterns by a
+ magic number as a function z(a,b,c), we must ensure that z(L1,K1,M1) is
+ greater than z(M2,L2,K2) when L1=M2. This can be done by an extra bit,
+ which is 1 for K and 0 for L. Thus, the magic number could be
+
+ case 1: (((L*2 + 1)*(maxlen+1) + K)*(maxlen+1) + M)*(maxlen+1) + P
+ case 2: ((M*2*(maxlen+1) + L)*(maxlen+1) + K)*(maxlen+1) + P
+
+ upper bound: L<=maxlen, M<=maxlen, K<=maxlen/2, P<maxlen
+ for a current maxlen=64, the magic number needs 26 bits.
+*/
+
+static ulonglong get_magic_sort(const char *templ, ...)
+{
+ ulonglong sort=0;
+ va_list args;
+ va_start(args, templ);
+
+ IF_DBUG(uint bits_used= 0,);
+
+ for (; *templ; templ++)
+ {
+ char *pat= va_arg(args, char*);
+
+ if (*templ == 'u')
+ {
+ /* Username. Can be empty (= anybody) or a literal. Encoded in one bit */
+ sort= (sort << 1) + !*pat;
+ IF_DBUG(bits_used++,);
+ continue;
+ }
+
+ /* A wildcard pattern. Encoded in 26 bits. */
+ uint maxlen= *templ == 'd' ? max_dbname_length : max_hostname_length;
+ DBUG_ASSERT(maxlen <= 64);
+ DBUG_ASSERT(*templ == 'd' || *templ == 'h');
+
+ uint N= 0, M= 0, K= 0, P= 0;
+ for (uint i=0; pat[i]; i++)
+ {
+ if (pat[i] == wild_many)
+ {
+ if (!K && !M) P= N;
+ K++;
+ while (pat[i+1] == wild_many) i++;
+ continue;
+ }
+ if (pat[i] == wild_one)
+ {
+ if (!K && !M) P= N;
+ M++;
+ continue;
+ }
+ if (pat[i] == wild_prefix && pat[i+1]) i++;
+ N++;
+ }
+ uint L= K ? maxlen - N - M : 0, d= maxlen + 1, magic;
+ if (L > M)
+ magic= (((L * 2 + 1) * d + K) * d + M) * d + P;
+ else
+ magic= (((M * 2 + 0) * d + L) * d + K) * d + P;
+ DBUG_ASSERT(magic < 1<<26);
+ sort= (sort << 26) + magic;
+ IF_DBUG(bits_used+= 26,);
+ }
+ DBUG_ASSERT(bits_used < 8*sizeof(sort));
+ va_end(args);
+ return ~sort;
+}
+#endif
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index b5c8514c302..a96eb58809b 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -32,7 +32,7 @@
#include "strfunc.h"
#include "sql_admin.h"
#include "sql_statistics.h"
-
+#include "wsrep_mysqld.h"
/* Prepare, run and cleanup for mysql_recreate_table() */
static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list)
@@ -323,7 +323,7 @@ static bool open_only_one_table(THD* thd, TABLE_LIST* table,
bool is_view_operator_func)
{
LEX *lex= thd->lex;
- SELECT_LEX *select= &lex->select_lex;
+ SELECT_LEX *select= lex->first_select_lex();
TABLE_LIST *save_next_global, *save_next_local;
bool open_error;
save_next_global= table->next_global;
@@ -436,6 +436,48 @@ dbug_err:
return open_error;
}
+#ifdef WITH_WSREP
+ /*
+ OPTIMIZE, REPAIR and ALTER may take MDL locks not only for the affected table, but
+ also for the table referenced by foreign key constraint.
+ This wsrep_toi_replication() function handles TOI replication for OPTIMIZE and REPAIR
+ so that certification keys for potential FK parent tables are also appended in the
+ write set.
+ ALTER TABLE case is handled elsewhere.
+ */
+static bool wsrep_toi_replication(THD *thd, TABLE_LIST *tables)
+{
+ LEX *lex= thd->lex;
+ /* only handle OPTIMIZE and REPAIR here */
+ switch (lex->sql_command)
+ {
+ case SQLCOM_OPTIMIZE:
+ case SQLCOM_REPAIR:
+ break;
+ default:
+ return false;
+ }
+
+ close_thread_tables(thd);
+ wsrep::key_array keys;
+
+ wsrep_append_fk_parent_table(thd, tables, &keys);
+
+ /* now TOI replication, with no locks held */
+ if (keys.empty())
+ {
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, tables);
+ } else {
+ WSREP_TO_ISOLATION_BEGIN_FK_TABLES(NULL, NULL, tables, &keys) {
+ return true;
+ }
+ }
+ return false;
+
+ wsrep_error_label:
+ return true;
+}
+#endif /* WITH_WSREP */
/*
RETURN VALUES
@@ -504,6 +546,16 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
close_thread_tables(thd);
for (table= tables; table; table= table->next_local)
table->table= NULL;
+#ifdef WITH_WSREP
+ if (WSREP(thd))
+ {
+ if(wsrep_toi_replication(thd, tables))
+ {
+ WSREP_INFO("wsrep TOI replication of has failed.");
+ goto err;
+ }
+ }
+#endif /* WITH_WSREP */
for (table= tables; table; table= table->next_local)
{
@@ -797,7 +849,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
collect_eis=
(table->table->s->table_category == TABLE_CATEGORY_USER &&
!(lex->alter_info.flags & ALTER_PARTITION_ADMIN) &&
- (get_use_stat_tables_mode(thd) > NEVER ||
+ (check_eits_collection_allowed(thd) ||
lex->with_persistent_for_clause));
}
@@ -1324,7 +1376,7 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
bool Sql_cmd_analyze_table::execute(THD *thd)
{
LEX *m_lex= thd->lex;
- TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
+ TABLE_LIST *first_table= m_lex->first_select_lex()->table_list.first;
bool res= TRUE;
thr_lock_type lock_type = TL_READ_NO_INSERT;
DBUG_ENTER("Sql_cmd_analyze_table::execute");
@@ -1332,6 +1384,9 @@ bool Sql_cmd_analyze_table::execute(THD *thd)
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
FALSE, UINT_MAX, FALSE))
goto error;
+ if (thd->has_read_only_protection())
+ goto error;
+
WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
res= mysql_admin_table(thd, first_table, &m_lex->check_opt,
"analyze", lock_type, 1, 0, 0, 0,
@@ -1342,13 +1397,17 @@ bool Sql_cmd_analyze_table::execute(THD *thd)
/*
Presumably, ANALYZE and binlog writing doesn't require synchronization
*/
+ thd->get_stmt_da()->set_overwrite_status(true);
res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+ thd->get_stmt_da()->set_overwrite_status(false);
}
- m_lex->select_lex.table_list.first= first_table;
+ m_lex->first_select_lex()->table_list.first= first_table;
m_lex->query_tables= first_table;
+#ifdef WITH_WSREP
+ wsrep_error_label:
+#endif /* WITH_WSREP */
error:
-WSREP_ERROR_LABEL:
DBUG_RETURN(res);
}
@@ -1356,7 +1415,7 @@ WSREP_ERROR_LABEL:
bool Sql_cmd_check_table::execute(THD *thd)
{
LEX *m_lex= thd->lex;
- TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
+ TABLE_LIST *first_table= m_lex->first_select_lex()->table_list.first;
thr_lock_type lock_type = TL_READ_NO_INSERT;
bool res= TRUE;
DBUG_ENTER("Sql_cmd_check_table::execute");
@@ -1369,7 +1428,7 @@ bool Sql_cmd_check_table::execute(THD *thd)
lock_type, 0, 0, HA_OPEN_FOR_REPAIR, 0,
&handler::ha_check, &view_check);
- m_lex->select_lex.table_list.first= first_table;
+ m_lex->first_select_lex()->table_list.first= first_table;
m_lex->query_tables= first_table;
error:
@@ -1380,14 +1439,13 @@ error:
bool Sql_cmd_optimize_table::execute(THD *thd)
{
LEX *m_lex= thd->lex;
- TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
+ TABLE_LIST *first_table= m_lex->first_select_lex()->table_list.first;
bool res= TRUE;
DBUG_ENTER("Sql_cmd_optimize_table::execute");
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
FALSE, UINT_MAX, FALSE))
goto error; /* purecov: inspected */
- WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
res= (specialflag & SPECIAL_NO_NEW_FUNC) ?
mysql_recreate_table(thd, first_table, true) :
@@ -1400,13 +1458,14 @@ bool Sql_cmd_optimize_table::execute(THD *thd)
/*
Presumably, OPTIMIZE and binlog writing doesn't require synchronization
*/
+ thd->get_stmt_da()->set_overwrite_status(true);
res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+ thd->get_stmt_da()->set_overwrite_status(false);
}
- m_lex->select_lex.table_list.first= first_table;
+ m_lex->first_select_lex()->table_list.first= first_table;
m_lex->query_tables= first_table;
error:
-WSREP_ERROR_LABEL:
DBUG_RETURN(res);
}
@@ -1414,14 +1473,13 @@ WSREP_ERROR_LABEL:
bool Sql_cmd_repair_table::execute(THD *thd)
{
LEX *m_lex= thd->lex;
- TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
+ TABLE_LIST *first_table= m_lex->first_select_lex()->table_list.first;
bool res= TRUE;
DBUG_ENTER("Sql_cmd_repair_table::execute");
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
FALSE, UINT_MAX, FALSE))
goto error; /* purecov: inspected */
- WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "repair",
TL_WRITE, 1,
MY_TEST(m_lex->check_opt.sql_flags & TT_USEFRM),
@@ -1434,12 +1492,13 @@ bool Sql_cmd_repair_table::execute(THD *thd)
/*
Presumably, REPAIR and binlog writing doesn't require synchronization
*/
+ thd->get_stmt_da()->set_overwrite_status(true);
res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+ thd->get_stmt_da()->set_overwrite_status(false);
}
- m_lex->select_lex.table_list.first= first_table;
+ m_lex->first_select_lex()->table_list.first= first_table;
m_lex->query_tables= first_table;
error:
-WSREP_ERROR_LABEL:
DBUG_RETURN(res);
}
diff --git a/sql/sql_alloc.h b/sql/sql_alloc.h
index 153b0401e29..f475ecdff73 100644
--- a/sql/sql_alloc.h
+++ b/sql/sql_alloc.h
@@ -45,9 +45,6 @@ public:
#ifdef HAVE_valgrind
bool dummy_for_valgrind;
inline Sql_alloc() :dummy_for_valgrind(0) {}
-#else
- inline Sql_alloc() {}
#endif
- inline ~Sql_alloc() {}
};
#endif /* SQL_ALLOC_INCLUDED */
diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc
index 94003e328cc..f1a67e7d968 100644
--- a/sql/sql_alter.cc
+++ b/sql/sql_alter.cc
@@ -368,7 +368,7 @@ bool Sql_cmd_alter_table::execute(THD *thd)
{
LEX *lex= thd->lex;
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
/* first table of first SELECT_LEX */
TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first;
@@ -470,6 +470,26 @@ bool Sql_cmd_alter_table::execute(THD *thd)
if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE))
DBUG_RETURN(TRUE); /* purecov: inspected */
+#ifdef WITH_WSREP
+ if (WSREP(thd) &&
+ (!thd->is_current_stmt_binlog_format_row() ||
+ !thd->find_temporary_table(first_table)))
+ {
+ wsrep::key_array keys;
+ wsrep_append_fk_parent_table(thd, first_table, &keys);
+
+ WSREP_TO_ISOLATION_BEGIN_ALTER((lex->name.str ? select_lex->db.str : NULL),
+ (lex->name.str ? lex->name.str : NULL),
+ first_table, &alter_info, &keys)
+ {
+ WSREP_WARN("ALTER TABLE isolation failure");
+ DBUG_RETURN(TRUE);
+ }
+
+ thd->variables.auto_increment_offset = 1;
+ thd->variables.auto_increment_increment = 1;
+ }
+#endif
if (lex->name.str && !test_all_bits(priv, INSERT_ACL | CREATE_ACL))
{
@@ -497,18 +517,6 @@ bool Sql_cmd_alter_table::execute(THD *thd)
thd->work_part_info= 0;
#endif
- if (WSREP(thd) &&
- (!thd->is_current_stmt_binlog_format_row() ||
- !thd->find_temporary_table(first_table)))
- {
- WSREP_TO_ISOLATION_BEGIN_ALTER((lex->name.str ? select_lex->db.str : NULL),
- (lex->name.str ? lex->name.str : NULL),
- first_table, &alter_info);
-
- thd->variables.auto_increment_offset = 1;
- thd->variables.auto_increment_increment = 1;
- }
-
result= mysql_alter_table(thd, &select_lex->db, &lex->name,
&create_info,
first_table,
@@ -518,16 +526,12 @@ bool Sql_cmd_alter_table::execute(THD *thd)
lex->ignore);
DBUG_RETURN(result);
-
-WSREP_ERROR_LABEL:
- WSREP_WARN("ALTER TABLE isolation failure");
- DBUG_RETURN(TRUE);
}
bool Sql_cmd_discard_import_tablespace::execute(THD *thd)
{
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
- SELECT_LEX *select_lex= &thd->lex->select_lex;
+ SELECT_LEX *select_lex= thd->lex->first_select_lex();
/* first table of first SELECT_LEX */
TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first;
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc
index 65049348869..edcc8aeeda6 100644
--- a/sql/sql_analyse.cc
+++ b/sql/sql_analyse.cc
@@ -68,6 +68,25 @@ int compare_decimal2(int* len, const char *s, const char *t)
}
+static bool
+prepare_param(THD *thd, Item **item, const char *proc_name, uint pos)
+{
+ if ((*item)->fix_fields_if_needed(thd, item))
+ {
+ DBUG_PRINT("info", ("fix_fields() for the parameter %u failed", pos));
+ return true;
+ }
+ if ((*item)->type_handler()->result_type() != INT_RESULT ||
+ !(*item)->basic_const_item() ||
+ (*item)->val_real() < 0)
+ {
+ my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name);
+ return true;
+ }
+ return false;
+}
+
+
Procedure *
proc_analyse_init(THD *thd, ORDER *param, select_result *result,
List<Item> &field_list)
@@ -88,17 +107,8 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result,
else if (param->next)
{
// first parameter
- if ((*param->item)->fix_fields_if_needed(thd, param->item))
- {
- DBUG_PRINT("info", ("fix_fields() for the first parameter failed"));
- goto err;
- }
- if ((*param->item)->type() != Item::INT_ITEM ||
- (*param->item)->val_real() < 0)
- {
- my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name);
+ if (prepare_param(thd, param->item, proc_name, 0))
goto err;
- }
pc->max_tree_elements = (uint) (*param->item)->val_int();
param = param->next;
if (param->next) // no third parameter possible
@@ -107,25 +117,12 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result,
goto err;
}
// second parameter
- if ((*param->item)->fix_fields_if_needed(thd, param->item))
- {
- DBUG_PRINT("info", ("fix_fields() for the second parameter failed"));
- goto err;
- }
- if ((*param->item)->type() != Item::INT_ITEM ||
- (*param->item)->val_real() < 0)
- {
- my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name);
+ if (prepare_param(thd, param->item, proc_name, 1))
goto err;
- }
pc->max_treemem = (uint) (*param->item)->val_int();
}
- else if ((*param->item)->type() != Item::INT_ITEM ||
- (*param->item)->val_real() < 0)
- {
- my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name);
+ else if (prepare_param(thd, param->item, proc_name, 0))
goto err;
- }
// if only one parameter was given, it will be the value of max_tree_elements
else
{
@@ -481,30 +478,28 @@ void field_real::add()
void field_decimal::add()
{
/*TODO - remove rounding stuff after decimal_div returns proper frac */
- my_decimal dec_buf, *dec= item->val_decimal(&dec_buf);
- my_decimal rounded;
+ VDec vdec(item);
uint length;
TREE_ELEMENT *element;
- if (item->null_value)
+ if (vdec.is_null())
{
nulls++;
return;
}
- my_decimal_round(E_DEC_FATAL_ERROR, dec, item->decimals, FALSE,&rounded);
- dec= &rounded;
+ my_decimal dec;
+ vdec.round_to(&dec, item->decimals, HALF_UP);
- length= my_decimal_string_length(dec);
+ length= my_decimal_string_length(&dec);
- if (decimal_is_zero(dec))
+ if (decimal_is_zero(&dec))
empty++;
if (room_in_tree)
{
uchar buf[DECIMAL_MAX_FIELD_SIZE];
- my_decimal2binary(E_DEC_FATAL_ERROR, dec, buf,
- item->max_length, item->decimals);
+ dec.to_binary(buf, item->max_length, item->decimals);
if (!(element = tree_insert(&tree, (void*)buf, 0, tree.custom_arg)))
{
room_in_tree = 0; // Remove tree, out of RAM ?
@@ -524,18 +519,18 @@ void field_decimal::add()
if (!found)
{
found = 1;
- min_arg = max_arg = sum[0] = *dec;
- my_decimal_mul(E_DEC_FATAL_ERROR, sum_sqr, dec, dec);
+ min_arg = max_arg = sum[0] = dec;
+ my_decimal_mul(E_DEC_FATAL_ERROR, sum_sqr, &dec, &dec);
cur_sum= 0;
min_length = max_length = length;
}
- else if (!decimal_is_zero(dec))
+ else if (!decimal_is_zero(&dec))
{
int next_cur_sum= cur_sum ^ 1;
my_decimal sqr_buf;
- my_decimal_add(E_DEC_FATAL_ERROR, sum+next_cur_sum, sum+cur_sum, dec);
- my_decimal_mul(E_DEC_FATAL_ERROR, &sqr_buf, dec, dec);
+ my_decimal_add(E_DEC_FATAL_ERROR, sum+next_cur_sum, sum+cur_sum, &dec);
+ my_decimal_mul(E_DEC_FATAL_ERROR, &sqr_buf, &dec, &dec);
my_decimal_add(E_DEC_FATAL_ERROR,
sum_sqr+next_cur_sum, sum_sqr+cur_sum, &sqr_buf);
cur_sum= next_cur_sum;
@@ -543,13 +538,13 @@ void field_decimal::add()
min_length = length;
if (length > max_length)
max_length = length;
- if (my_decimal_cmp(dec, &min_arg) < 0)
+ if (dec.cmp(&min_arg) < 0)
{
- min_arg= *dec;
+ min_arg= dec;
}
- if (my_decimal_cmp(dec, &max_arg) > 0)
+ if (dec.cmp(&max_arg) > 0)
{
- max_arg= *dec;
+ max_arg= dec;
}
}
}
@@ -1003,7 +998,7 @@ void field_decimal::get_opt_type(String *answer,
uint length;
my_decimal_set_zero(&zero);
- my_bool is_unsigned= (my_decimal_cmp(&zero, &min_arg) >= 0);
+ my_bool is_unsigned= (zero.cmp(&min_arg) >= 0);
length= sprintf(buff, "DECIMAL(%d, %d)",
(int) (max_length - (item->decimals ? 1 : 0)),
@@ -1016,14 +1011,14 @@ void field_decimal::get_opt_type(String *answer,
String *field_decimal::get_min_arg(String *str)
{
- my_decimal2string(E_DEC_FATAL_ERROR, &min_arg, 0, 0, '0', str);
+ min_arg.to_string_native(str, 0, 0, '0');
return str;
}
String *field_decimal::get_max_arg(String *str)
{
- my_decimal2string(E_DEC_FATAL_ERROR, &max_arg, 0, 0, '0', str);
+ max_arg.to_string_native(str, 0, 0, '0');
return str;
}
@@ -1041,10 +1036,10 @@ String *field_decimal::avg(String *s, ha_rows rows)
int2my_decimal(E_DEC_FATAL_ERROR, rows - nulls, FALSE, &num);
my_decimal_div(E_DEC_FATAL_ERROR, &avg_val, sum+cur_sum, &num, prec_increment);
/* TODO remove this after decimal_div returns proper frac */
- my_decimal_round(E_DEC_FATAL_ERROR, &avg_val,
+ avg_val.round_to(&rounded_avg,
MY_MIN(sum[cur_sum].frac + prec_increment, DECIMAL_MAX_SCALE),
- FALSE,&rounded_avg);
- my_decimal2string(E_DEC_FATAL_ERROR, &rounded_avg, 0, 0, '0', s);
+ HALF_UP);
+ rounded_avg.to_string_native(s, 0, 0, '0');
return s;
}
@@ -1057,7 +1052,6 @@ String *field_decimal::std(String *s, ha_rows rows)
return s;
}
my_decimal num, tmp, sum2, sum2d;
- double std_sqr;
int prec_increment= current_thd->variables.div_precincrement;
int2my_decimal(E_DEC_FATAL_ERROR, rows - nulls, FALSE, &num);
@@ -1065,7 +1059,7 @@ String *field_decimal::std(String *s, ha_rows rows)
my_decimal_div(E_DEC_FATAL_ERROR, &tmp, &sum2, &num, prec_increment);
my_decimal_sub(E_DEC_FATAL_ERROR, &sum2, sum_sqr+cur_sum, &tmp);
my_decimal_div(E_DEC_FATAL_ERROR, &tmp, &sum2, &num, prec_increment);
- my_decimal2double(E_DEC_FATAL_ERROR, &tmp, &std_sqr);
+ double std_sqr= tmp.to_double();
s->set_real(((double) std_sqr <= 0.0 ? 0.0 : sqrt(std_sqr)),
MY_MIN(item->decimals + prec_increment, NOT_FIXED_DEC), my_thd_charset);
@@ -1117,12 +1111,9 @@ int collect_decimal(uchar *element, element_count count,
info->str->append(',');
else
info->found = 1;
- my_decimal dec;
- binary2my_decimal(E_DEC_FATAL_ERROR, element, &dec,
- info->item->max_length, info->item->decimals);
-
+ my_decimal dec(element, info->item->max_length, info->item->decimals);
info->str->append('\'');
- my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, '0', &s);
+ dec.to_string_native(&s, 0, 0, '0');
info->str->append(s);
info->str->append('\'');
return 0;
diff --git a/sql/sql_analyze_stmt.h b/sql/sql_analyze_stmt.h
index 3d294f50a49..eec52822ae5 100644
--- a/sql/sql_analyze_stmt.h
+++ b/sql/sql_analyze_stmt.h
@@ -284,3 +284,82 @@ private:
ulonglong sort_buffer_size;
};
+
+/**
+ A class to collect data about how rowid filter is executed.
+
+ It stores information about how rowid filter container is filled,
+ containers size and observed selectivity.
+
+ The observed selectivity is calculated in this way.
+ Some elements elem_set are checked if they belong to container.
+ Observed selectivity is calculated as the count of elem_set
+ elements that belong to container devided by all elem_set elements.
+*/
+
+class Rowid_filter_tracker : public Sql_alloc
+{
+private:
+ /* A member to track the time to fill the rowid filter */
+ Time_and_counter_tracker time_tracker;
+
+ /* Size of the rowid filter container buffer */
+ size_t container_buff_size;
+
+ /* Count of elements that were used to fill the rowid filter container */
+ uint container_elements;
+
+ /* Elements counts used for observed selectivity calculation */
+ uint n_checks;
+ uint n_positive_checks;
+public:
+ Rowid_filter_tracker(bool do_timing) :
+ time_tracker(do_timing), container_buff_size(0),
+ container_elements(0), n_checks(0), n_positive_checks(0)
+ {}
+
+ inline void start_tracking()
+ {
+ ANALYZE_START_TRACKING(&time_tracker);
+ }
+
+ inline void stop_tracking()
+ {
+ ANALYZE_STOP_TRACKING(&time_tracker);
+ }
+
+ /* Save container buffer size in bytes */
+ inline void report_container_buff_size(uint elem_size)
+ {
+ container_buff_size= container_elements * elem_size / 8;
+ }
+
+ Time_and_counter_tracker *get_time_tracker()
+ {
+ return &time_tracker;
+ }
+
+ double get_time_fill_container_ms()
+ {
+ return time_tracker.get_time_ms();
+ }
+
+ void increment_checked_elements_count(bool was_checked)
+ {
+ n_checks++;
+ if (was_checked)
+ n_positive_checks++;
+ }
+
+ inline void increment_container_elements_count() { container_elements++; }
+
+ uint get_container_elements() { return container_elements; }
+
+ double get_r_selectivity_pct()
+ {
+ return (double)n_positive_checks/(double)n_checks;
+ }
+
+ size_t get_container_buff_size() { return container_buff_size; }
+};
+
diff --git a/sql/sql_array.h b/sql/sql_array.h
index 56dba784d7d..bcfbb98ef19 100644
--- a/sql/sql_array.h
+++ b/sql/sql_array.h
@@ -165,6 +165,19 @@ public:
return ((const Elem*)array.buffer) + array.elements - 1;
}
+ /// @returns pointer to n-th element
+ Elem *get_pos(size_t idx)
+ {
+ return ((Elem*)array.buffer) + idx;
+ }
+
+ /// @returns pointer to n-th element
+ const Elem *get_pos(size_t idx) const
+ {
+ return ((const Elem*)array.buffer) + idx;
+ }
+
+
/**
@retval false ok
@retval true OOM, @c my_error() has been called.
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 280f9067df1..f4d9272eca1 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -62,7 +62,11 @@
#include <io.h>
#endif
#include "wsrep_mysqld.h"
+#ifdef WITH_WSREP
#include "wsrep_thd.h"
+#include "wsrep_trans_observer.h"
+#endif /* WITH_WSREP */
+
bool
No_such_table_error_handler::handle_condition(THD *,
@@ -306,97 +310,65 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
}
-/*
- Close all tables which aren't in use by any thread
-
- @param thd Thread context
- @param tables List of tables to remove from the cache
- @param wait_for_refresh Wait for a impending flush
- @param timeout Timeout for waiting for flush to be completed.
-
- @note THD can be NULL, but then wait_for_refresh must be FALSE
- and tables must be NULL.
-
- @note When called as part of FLUSH TABLES WITH READ LOCK this function
- ignores metadata locks held by other threads. In order to avoid
- situation when FLUSH TABLES WITH READ LOCK sneaks in at the moment
- when some write-locked table is being reopened (by FLUSH TABLES or
- ALTER TABLE) we have to rely on additional global shared metadata
- lock taken by thread trying to obtain global read lock.
-*/
+/**
+ Close all tables that are not in use in table definition cache
+ @param purge_flag Argument for tc_purge. true if we should force all
+ shares to be deleted. false if it's enough to just
+ evict those that are not in use.
+*/
-struct close_cached_tables_arg
+void purge_tables(bool purge_flag)
{
- tdc_version_t refresh_version;
- TDC_element *element;
-};
-
+ /*
+ Force close of all open tables.
-static my_bool close_cached_tables_callback(TDC_element *element,
- close_cached_tables_arg *arg)
-{
- mysql_mutex_lock(&element->LOCK_table_share);
- if (element->share && element->flushed &&
- element->version < arg->refresh_version)
- {
- /* wait_for_old_version() will unlock mutex and free share */
- arg->element= element;
- return TRUE;
- }
- mysql_mutex_unlock(&element->LOCK_table_share);
- return FALSE;
+ Note that code in TABLE_SHARE::wait_for_old_version() assumes that
+ incrementing of refresh_version is followed by purge of unused table
+ shares.
+ */
+ kill_delayed_threads();
+ /*
+ Get rid of all unused TABLE and TABLE_SHARE instances. By doing
+ this we automatically close all tables which were marked as "old".
+ */
+ tc_purge(purge_flag);
+ /* Free table shares which were not freed implicitly by loop above. */
+ tdc_purge(true);
}
+/**
+ close_cached_tables
+
+ This function has two separate usages:
+ 1) Close not used tables in the table cache to free memory
+ 2) Close a list of tables and wait until they are not used anymore. This
+ is used mainly when preparing a table for export.
+
+ If there are locked tables, they are closed and reopened before
+ function returns. This is done to ensure that table files will be closed
+ by all threads and thus external copyable when FLUSH TABLES returns.
+*/
+
bool close_cached_tables(THD *thd, TABLE_LIST *tables,
bool wait_for_refresh, ulong timeout)
{
- bool result= FALSE;
- struct timespec abstime;
- tdc_version_t refresh_version;
DBUG_ENTER("close_cached_tables");
DBUG_ASSERT(thd || (!wait_for_refresh && !tables));
-
- refresh_version= tdc_increment_refresh_version();
+ DBUG_ASSERT(wait_for_refresh || !tables);
if (!tables)
{
- /*
- Force close of all open tables.
-
- Note that code in TABLE_SHARE::wait_for_old_version() assumes that
- incrementing of refresh_version is followed by purge of unused table
- shares.
- */
- kill_delayed_threads();
- /*
- Get rid of all unused TABLE and TABLE_SHARE instances. By doing
- this we automatically close all tables which were marked as "old".
- */
- tc_purge(true);
- /* Free table shares which were not freed implicitly by loop above. */
- tdc_purge(true);
- }
- else
- {
- bool found=0;
- for (TABLE_LIST *table= tables; table; table= table->next_local)
- {
- /* tdc_remove_table() also sets TABLE_SHARE::version to 0. */
- found|= tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db.str,
- table->table_name.str, TRUE);
- }
- if (!found)
- wait_for_refresh=0; // Nothing to wait for
+ /* Free tables that are not used */
+ purge_tables(false);
+ if (!wait_for_refresh)
+ DBUG_RETURN(false);
}
DBUG_PRINT("info", ("open table definitions: %d",
(int) tdc_records()));
- if (!wait_for_refresh)
- DBUG_RETURN(result);
-
if (thd->locked_tables_mode)
{
/*
@@ -407,8 +379,9 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
*/
TABLE_LIST *tables_to_reopen= (tables ? tables :
thd->locked_tables_list.locked_tables());
+ bool result= false;
- /* Close open HANDLER instances to avoid self-deadlock. */
+ /* close open HANDLER for this thread to allow table to be closed */
mysql_ha_flush_tables(thd, tables_to_reopen);
for (TABLE_LIST *table_list= tables_to_reopen; table_list;
@@ -423,63 +396,15 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
if (! table)
continue;
- if (wait_while_table_is_used(thd, table,
- HA_EXTRA_PREPARE_FOR_FORCED_CLOSE))
+ if (thd->mdl_context.upgrade_shared_lock(table->mdl_ticket, MDL_EXCLUSIVE,
+ timeout))
{
- result= TRUE;
- goto err_with_reopen;
- }
- close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
- }
- }
-
- /* Wait until all threads have closed all the tables we are flushing. */
- DBUG_PRINT("info", ("Waiting for other threads to close their open tables"));
-
- /*
- To a self-deadlock or deadlocks with other FLUSH threads
- waiting on our open HANDLERs, we have to flush them.
- */
- mysql_ha_flush(thd);
- DEBUG_SYNC(thd, "after_flush_unlock");
-
- if (!tables)
- {
- int r= 0;
- close_cached_tables_arg argument;
- argument.refresh_version= refresh_version;
- set_timespec(abstime, timeout);
-
- while (!thd->killed &&
- (r= tdc_iterate(thd,
- (my_hash_walk_action) close_cached_tables_callback,
- &argument)) == 1 &&
- !argument.element->share->wait_for_old_version(thd, &abstime,
- MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
- /* no-op */;
-
- if (r)
- result= TRUE;
- }
- else
- {
- for (TABLE_LIST *table= tables; table; table= table->next_local)
- {
- if (thd->killed)
- break;
- if (tdc_wait_for_old_version(thd, table->db.str, table->table_name.str, timeout,
- MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL,
- refresh_version))
- {
- result= TRUE;
+ result= true;
break;
}
+ table->file->extra(HA_EXTRA_PREPARE_FOR_FORCED_CLOSE);
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
}
- }
-
-err_with_reopen:
- if (thd->locked_tables_mode)
- {
/*
No other thread has the locked tables open; reopen them and get the
old locks. This should always succeed (unless some external process
@@ -487,6 +412,7 @@ err_with_reopen:
*/
if (thd->locked_tables_list.reopen_tables(thd, false))
result= true;
+
/*
Since downgrade_lock() won't do anything with shared
metadata lock it is much simpler to go through all open tables rather
@@ -494,7 +420,220 @@ err_with_reopen:
*/
for (TABLE *tab= thd->open_tables; tab; tab= tab->next)
tab->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
+
+ DBUG_RETURN(result);
+ }
+ else if (tables)
+ {
+ /*
+ Get an explicit MDL lock for all requested tables to ensure they are
+ not used by any other thread
+ */
+ MDL_request_list mdl_requests;
+
+ DBUG_PRINT("info", ("Waiting for other threads to close their open tables"));
+ DEBUG_SYNC(thd, "after_flush_unlock");
+
+ /* close open HANDLER for this thread to allow table to be closed */
+ mysql_ha_flush_tables(thd, tables);
+
+ for (TABLE_LIST *table= tables; table; table= table->next_local)
+ {
+ MDL_request *mdl_request= new (thd->mem_root) MDL_request;
+ if (mdl_request == NULL)
+ DBUG_RETURN(true);
+ mdl_request->init(&table->mdl_request.key, MDL_EXCLUSIVE, MDL_STATEMENT);
+ mdl_requests.push_front(mdl_request);
+ }
+
+ if (thd->mdl_context.acquire_locks(&mdl_requests, timeout))
+ DBUG_RETURN(true);
+
+ for (TABLE_LIST *table= tables; table; table= table->next_local)
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db.str,
+ table->table_name.str, false);
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Collect all shares that has open tables
+*/
+
+struct tc_collect_arg
+{
+ DYNAMIC_ARRAY shares;
+ flush_tables_type flush_type;
+};
+
+static my_bool tc_collect_used_shares(TDC_element *element,
+ tc_collect_arg *arg)
+{
+ my_bool result= FALSE;
+
+ DYNAMIC_ARRAY *shares= &arg->shares;
+ mysql_mutex_lock(&element->LOCK_table_share);
+ if (element->ref_count > 0 && !element->share->is_view)
+ {
+ DBUG_ASSERT(element->share);
+ bool do_flush= 0;
+ switch (arg->flush_type) {
+ case FLUSH_ALL:
+ do_flush= 1;
+ break;
+ case FLUSH_NON_TRANS_TABLES:
+ if (!element->share->online_backup &&
+ element->share->table_category == TABLE_CATEGORY_USER)
+ do_flush= 1;
+ break;
+ case FLUSH_SYS_TABLES:
+ if (!element->share->online_backup &&
+ element->share->table_category != TABLE_CATEGORY_USER)
+ do_flush= 1;
+ }
+ if (do_flush)
+ {
+ element->ref_count++; // Protect against delete
+ if (push_dynamic(shares, (uchar*) &element->share))
+ result= TRUE;
+ }
+ }
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ return result;
+}
+
+
+/*
+ Ignore errors from opening read only tables
+*/
+
+class flush_tables_error_handler : public Internal_error_handler
+{
+public:
+ int handled_errors;
+ int unhandled_errors;
+ flush_tables_error_handler() : handled_errors(0), unhandled_errors(0)
+ {}
+
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ Sql_condition::enum_warning_level *level,
+ const char* msg,
+ Sql_condition ** cond_hdl)
+ {
+ *cond_hdl= NULL;
+ if (sql_errno == ER_OPEN_AS_READONLY)
+ {
+ handled_errors++;
+ return TRUE;
+ }
+ if (*level == Sql_condition::WARN_LEVEL_ERROR)
+ unhandled_errors++;
+ return FALSE;
+ }
+
+ bool got_fatal_error()
+ {
+ return unhandled_errors > 0;
}
+};
+
+
+/**
+ Flush cached table as part of global read lock
+
+ @param thd
+ @param flag What type of tables should be flushed
+
+ @return 0 ok
+ @return 1 error
+
+ After we get the list of table shares, we will call flush on all
+ possible tables, even if some flush fails.
+*/
+
+bool flush_tables(THD *thd, flush_tables_type flag)
+{
+ bool result= TRUE;
+ tc_collect_arg collect_arg;
+ TABLE *tmp_table;
+ flush_tables_error_handler error_handler;
+ DBUG_ENTER("flush_tables");
+
+ purge_tables(false); /* Flush unused tables and shares */
+
+ /*
+ Loop over all shares and collect shares that have open tables
+ TODO:
+ Optimize this to only collect shares that have been used for
+ write after last time all tables was closed.
+ */
+
+ if (!(tmp_table= (TABLE*) my_malloc(sizeof(*tmp_table),
+ MYF(MY_WME | MY_THREAD_SPECIFIC))))
+ DBUG_RETURN(1);
+
+ my_init_dynamic_array(&collect_arg.shares, sizeof(TABLE_SHARE*), 100, 100,
+ MYF(0));
+ collect_arg.flush_type= flag;
+ if (tdc_iterate(thd, (my_hash_walk_action) tc_collect_used_shares,
+ &collect_arg, true))
+ {
+ /* Release already collected shares */
+ for (uint i= 0 ; i < collect_arg.shares.elements ; i++)
+ {
+ TABLE_SHARE *share= *dynamic_element(&collect_arg.shares, i,
+ TABLE_SHARE**);
+ tdc_release_share(share);
+ }
+ goto err;
+ }
+
+ /* Call HA_EXTRA_FLUSH on all found shares */
+
+ thd->push_internal_handler(&error_handler);
+ for (uint i= 0 ; i < collect_arg.shares.elements ; i++)
+ {
+ TABLE_SHARE *share= *dynamic_element(&collect_arg.shares, i,
+ TABLE_SHARE**);
+ TABLE *table= tc_acquire_table(thd, share->tdc);
+ if (table)
+ {
+ (void) table->file->extra(HA_EXTRA_FLUSH);
+ tc_release_table(table);
+ }
+ else
+ {
+ /*
+ HA_OPEN_FOR_ALTER is used to allow us to open the table even if
+ TABLE_SHARE::incompatible_version is set.
+ */
+ if (!open_table_from_share(thd, share, &empty_clex_str,
+ HA_OPEN_KEYFILE, 0,
+ HA_OPEN_FOR_ALTER,
+ tmp_table, FALSE,
+ NULL))
+ {
+ (void) tmp_table->file->extra(HA_EXTRA_FLUSH);
+ /*
+ We don't put the table into the TDC as the table was not fully
+ opened (we didn't open triggers)
+ */
+ closefrm(tmp_table);
+ }
+ }
+ tdc_release_share(share);
+ }
+ thd->pop_internal_handler();
+ result= error_handler.got_fatal_error();
+ DBUG_PRINT("note", ("open_errors: %u %u",
+ error_handler.handled_errors,
+ error_handler.unhandled_errors));
+err:
+ my_free(tmp_table);
+ delete_dynamic(&collect_arg.shares);
DBUG_RETURN(result);
}
@@ -552,8 +691,17 @@ end:
}
+/**
+ Close cached connections
+
+ @return false ok
+ @return true If there was an error from closed_cached_connection_tables or
+ if there was any open connections that we had to force closed
+*/
+
bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connection)
{
+ bool res= false;
close_cached_connection_tables_arg argument;
DBUG_ENTER("close_cached_connections");
DBUG_ASSERT(thd);
@@ -567,9 +715,13 @@ bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connection)
&argument))
DBUG_RETURN(true);
- DBUG_RETURN(argument.tables ?
- close_cached_tables(thd, argument.tables, FALSE, LONG_TIMEOUT) :
- false);
+ for (TABLE_LIST *table= argument.tables; table; table= table->next_local)
+ res|= tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
+ table->db.str,
+ table->table_name.str, TRUE);
+
+ /* Return true if we found any open connections */
+ DBUG_RETURN(res);
}
@@ -604,6 +756,7 @@ bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connection)
static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
{
+ DBUG_ENTER("mark_used_tables_as_free_for_reuse");
for (; table ; table= table->next)
{
DBUG_ASSERT(table->pos_in_locked_tables == NULL ||
@@ -616,6 +769,7 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
else if (table->file->check_table_binlog_row_based_done)
table->file->clear_cached_table_binlog_row_based_flag();
}
+ DBUG_VOID_RETURN;
}
@@ -638,7 +792,7 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
- The table is marked as closed in the
locked_table_list but kept there so one can call
locked_table_list->reopen_tables() to put it back.
-
+
In case of drop/rename the documented behavior is to
implicitly remove the table from LOCK TABLES
list.
@@ -743,12 +897,16 @@ void close_thread_tables(THD *thd)
DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt ||
(thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
- /* Detach MERGE children after every statement. Even under LOCK TABLES. */
for (table= thd->open_tables; table; table= table->next)
{
+ if (table->update_handler)
+ table->delete_update_handler();
+
/* Table might be in use by some outer statement. */
DBUG_PRINT("tcache", ("table: '%s' query_id: %lu",
table->s->table_name.str, (ulong) table->query_id));
+
+ /* Detach MERGE children after every statement. Even under LOCK TABLES. */
if (thd->locked_tables_mode <= LTM_LOCK_TABLES ||
table->query_id == thd->query_id)
{
@@ -1754,59 +1912,6 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
{
- /*
- We are not under LOCK TABLES and going to acquire write-lock/
- modify the base table. We need to acquire protection against
- global read lock until end of this statement in order to have
- this statement blocked by active FLUSH TABLES WITH READ LOCK.
-
- We don't need to acquire this protection under LOCK TABLES as
- such protection already acquired at LOCK TABLES time and
- not released until UNLOCK TABLES.
-
- We don't block statements which modify only temporary tables
- as these tables are not preserved by any form of
- backup which uses FLUSH TABLES WITH READ LOCK.
-
- TODO: The fact that we sometimes acquire protection against
- GRL only when we encounter table to be write-locked
- slightly increases probability of deadlock.
- This problem will be solved once Alik pushes his
- temporary table refactoring patch and we can start
- pre-acquiring metadata locks at the beggining of
- open_tables() call.
- */
- if (table_list->mdl_request.is_write_lock_request() &&
- ! (flags & (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
- MYSQL_OPEN_FORCE_SHARED_MDL |
- MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
- MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) &&
- ! ot_ctx->has_protection_against_grl())
- {
- MDL_request protection_request;
- MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
-
- if (thd->global_read_lock.can_acquire_protection())
- DBUG_RETURN(TRUE);
-
- protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
- MDL_STATEMENT);
-
- /*
- Install error handler which if possible will convert deadlock error
- into request to back-off and restart process of opening tables.
- */
- thd->push_internal_handler(&mdl_deadlock_handler);
- bool result= thd->mdl_context.acquire_lock(&protection_request,
- ot_ctx->get_timeout());
- thd->pop_internal_handler();
-
- if (result)
- DBUG_RETURN(TRUE);
-
- ot_ctx->set_has_protection_against_grl();
- }
-
if (open_table_get_mdl_lock(thd, ot_ctx, &table_list->mdl_request,
flags, &mdl_ticket) ||
mdl_ticket == NULL)
@@ -1905,7 +2010,6 @@ retry_share:
if (mysql_make_view(thd, share, table_list, false))
goto err_lock;
-
/* TODO: Don't free this */
tdc_release_share(share);
@@ -1984,7 +2088,6 @@ retry_share:
else
{
enum open_frm_error error;
-
/* make a new table */
if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
goto err_lock;
@@ -2023,6 +2126,78 @@ retry_share:
tc_add_table(thd, table);
}
+ if (!(flags & MYSQL_OPEN_HAS_MDL_LOCK) &&
+ table->s->table_category < TABLE_CATEGORY_INFORMATION)
+ {
+ /*
+ We are not under LOCK TABLES and going to acquire write-lock/
+ modify the base table. We need to acquire protection against
+ global read lock until end of this statement in order to have
+ this statement blocked by active FLUSH TABLES WITH READ LOCK.
+
+ We don't need to acquire this protection under LOCK TABLES as
+ such protection already acquired at LOCK TABLES time and
+ not released until UNLOCK TABLES.
+
+ We don't block statements which modify only temporary tables
+ as these tables are not preserved by any form of
+ backup which uses FLUSH TABLES WITH READ LOCK.
+
+ TODO: The fact that we sometimes acquire protection against
+ GRL only when we encounter table to be write-locked
+ slightly increases probability of deadlock.
+ This problem will be solved once Alik pushes his
+ temporary table refactoring patch and we can start
+ pre-acquiring metadata locks at the beggining of
+ open_tables() call.
+ */
+ enum enum_mdl_type mdl_type= MDL_BACKUP_DML;
+
+ if (table->s->table_category != TABLE_CATEGORY_USER)
+ mdl_type= MDL_BACKUP_SYS_DML;
+ else if (table->s->online_backup)
+ mdl_type= MDL_BACKUP_TRANS_DML;
+
+ if (table_list->mdl_request.is_write_lock_request() &&
+ ! (flags & (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
+ MYSQL_OPEN_FORCE_SHARED_MDL |
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
+ MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) &&
+ ! ot_ctx->has_protection_against_grl(mdl_type))
+ {
+ MDL_request protection_request;
+ MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
+
+ if (thd->has_read_only_protection())
+ {
+ MYSQL_UNBIND_TABLE(table->file);
+ tc_release_table(table);
+ DBUG_RETURN(TRUE);
+ }
+
+ protection_request.init(MDL_key::BACKUP, "", "", mdl_type,
+ MDL_STATEMENT);
+
+ /*
+ Install error handler which if possible will convert deadlock error
+ into request to back-off and restart process of opening tables.
+ */
+ thd->push_internal_handler(&mdl_deadlock_handler);
+ bool result= thd->mdl_context.acquire_lock(&protection_request,
+ ot_ctx->get_timeout());
+ thd->pop_internal_handler();
+
+ if (result)
+ {
+ MYSQL_UNBIND_TABLE(table->file);
+ tc_release_table(table);
+ DBUG_RETURN(TRUE);
+ }
+
+ ot_ctx->set_has_protection_against_grl(mdl_type);
+ }
+ }
+
table->mdl_ticket= mdl_ticket;
table->reginfo.lock_type=TL_READ; /* Assume read */
@@ -2138,8 +2313,8 @@ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
cases don't take a global IX lock in order to be compatible with
global read lock.
*/
- if (unlikely(!thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
- MDL_INTENTION_EXCLUSIVE)))
+ if (unlikely(!thd->mdl_context.is_lock_owner(MDL_key::BACKUP, "", "",
+ MDL_BACKUP_DDL)))
{
error= ER_TABLE_NOT_LOCKED_FOR_WRITE;
goto err_exit;
@@ -2948,7 +3123,7 @@ Open_table_context::Open_table_context(THD *thd, uint flags)
m_flags(flags),
m_action(OT_NO_ACTION),
m_has_locks(thd->mdl_context.has_locks()),
- m_has_protection_against_grl(FALSE)
+ m_has_protection_against_grl(0)
{}
@@ -3164,7 +3339,7 @@ Open_table_context::recover_from_failed_open()
against GRL. It is no longer valid as the corresponding lock was
released by close_tables_for_reopen().
*/
- m_has_protection_against_grl= FALSE;
+ m_has_protection_against_grl= 0;
/* Prepare for possible another back-off. */
m_action= OT_NO_ACTION;
return result;
@@ -3834,6 +4009,40 @@ end:
}
+static bool upgrade_lock_if_not_exists(THD *thd,
+ const DDL_options_st &create_info,
+ TABLE_LIST *create_table,
+ ulong lock_wait_timeout)
+{
+ DBUG_ENTER("upgrade_lock_if_not_exists");
+
+ if (thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
+ thd->lex->sql_command == SQLCOM_CREATE_SEQUENCE)
+ {
+ DEBUG_SYNC(thd,"create_table_before_check_if_exists");
+ if (!create_info.or_replace() &&
+ ha_table_exists(thd, &create_table->db, &create_table->table_name))
+ {
+ if (create_info.if_not_exists())
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_TABLE_EXISTS_ERROR,
+ ER_THD(thd, ER_TABLE_EXISTS_ERROR),
+ create_table->table_name.str);
+ }
+ else
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_table->table_name.str);
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(thd->mdl_context.upgrade_shared_lock(
+ create_table->mdl_request.ticket,
+ MDL_EXCLUSIVE,
+ lock_wait_timeout));
+ }
+ DBUG_RETURN(false);
+}
+
+
/**
Acquire upgradable (SNW, SNRW) metadata locks on tables used by
LOCK TABLES or by a DDL statement. Under LOCK TABLES, we can't take
@@ -3871,10 +4080,7 @@ lock_table_names(THD *thd, const DDL_options_st &options,
MDL_request_list mdl_requests;
TABLE_LIST *table;
MDL_request global_request;
- ulong org_lock_wait_timeout= lock_wait_timeout;
- /* Check if we are using CREATE TABLE ... IF NOT EXISTS */
- bool create_table;
- Dummy_error_handler error_handler;
+ MDL_savepoint mdl_savepoint;
DBUG_ENTER("lock_table_names");
DBUG_ASSERT(!thd->locked_tables_mode);
@@ -3882,6 +4088,8 @@ lock_table_names(THD *thd, const DDL_options_st &options,
for (table= tables_start; table && table != tables_end;
table= table->next_global)
{
+ DBUG_PRINT("info", ("mdl_request.type: %d open_type: %d",
+ table->mdl_request.type, table->open_type));
if (table->mdl_request.type < MDL_SHARED_UPGRADABLE ||
table->mdl_request.type == MDL_SHARED_READ_ONLY ||
table->open_type == OT_TEMPORARY_ONLY ||
@@ -3915,74 +4123,48 @@ lock_table_names(THD *thd, const DDL_options_st &options,
if (mdl_requests.is_empty())
DBUG_RETURN(FALSE);
- /* Check if CREATE TABLE without REPLACE was used */
- create_table= ((thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
- thd->lex->sql_command == SQLCOM_CREATE_SEQUENCE) &&
- !options.or_replace());
-
- if (!(flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK))
+ if (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)
{
- /*
- Protect this statement against concurrent global read lock
- by acquiring global intention exclusive lock with statement
- duration.
- */
- if (thd->global_read_lock.can_acquire_protection())
- DBUG_RETURN(TRUE);
- global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
- MDL_STATEMENT);
- mdl_requests.push_front(&global_request);
-
- if (create_table)
-#ifdef WITH_WSREP
- if (!(thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
- thd->wsrep_exec_mode == REPL_RECV))
-#endif
- lock_wait_timeout= 0; // Don't wait for timeout
+ DBUG_RETURN(thd->mdl_context.acquire_locks(&mdl_requests,
+ lock_wait_timeout) ||
+ upgrade_lock_if_not_exists(thd, options, tables_start,
+ lock_wait_timeout));
}
- for (;;)
- {
- if (create_table)
- thd->push_internal_handler(&error_handler); // Avoid warnings & errors
- bool res= thd->mdl_context.acquire_locks(&mdl_requests, lock_wait_timeout);
- if (create_table)
- thd->pop_internal_handler();
-
- if (!res)
- DBUG_RETURN(FALSE); // Got locks
+ /* Protect this statement against concurrent BACKUP STAGE or FTWRL. */
+ if (thd->has_read_only_protection())
+ DBUG_RETURN(true);
- if (!create_table)
- DBUG_RETURN(TRUE); // Return original error
+ global_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_DDL, MDL_STATEMENT);
+ mdl_savepoint= thd->mdl_context.mdl_savepoint();
- /*
- We come here in the case of lock timeout when executing CREATE TABLE.
- Verify that table does exist (it usually does, as we got a lock conflict)
- */
- if (ha_table_exists(thd, &tables_start->db, &tables_start->table_name))
+ while (!thd->mdl_context.acquire_locks(&mdl_requests, lock_wait_timeout) &&
+ !upgrade_lock_if_not_exists(thd, options, tables_start,
+ lock_wait_timeout) &&
+ !thd->mdl_context.try_acquire_lock(&global_request))
+ {
+ if (global_request.ticket)
{
- if (options.if_not_exists())
- {
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_TABLE_EXISTS_ERROR,
- ER_THD(thd, ER_TABLE_EXISTS_ERROR),
- tables_start->table_name.str);
- }
- else
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), tables_start->table_name.str);
- DBUG_RETURN(TRUE);
+ thd->mdl_backup_ticket= global_request.ticket;
+ DBUG_RETURN(false);
}
+
/*
- We got error from acquire_locks, but the table didn't exists.
- This could happen if another connection runs a statement
- involving this non-existent table, and this statement took the mdl,
- but didn't error out with ER_NO_SUCH_TABLE yet (yes, a race condition).
- We play safe and restart the original acquire_locks with the
- original timeout.
+ There is ongoing or pending BACKUP STAGE or FTWRL.
+ Wait until it finishes and re-try.
*/
- create_table= 0;
- lock_wait_timeout= org_lock_wait_timeout;
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ if (thd->mdl_context.acquire_lock(&global_request, lock_wait_timeout))
+ break;
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+
+ /* Reset tickets for all acquired locks */
+ global_request.ticket= 0;
+ MDL_request_list::Iterator it(mdl_requests);
+ while (auto mdl_request= it++)
+ mdl_request->ticket= 0;
}
+ DBUG_RETURN(true);
}
@@ -4103,13 +4285,9 @@ bool open_tables(THD *thd, const DDL_options_st &options,
bool has_prelocking_list;
DBUG_ENTER("open_tables");
- /* Accessing data in XA_IDLE or XA_PREPARED is not allowed. */
- enum xa_states xa_state= thd->transaction.xid_state.xa_state;
- if (*start && (xa_state == XA_IDLE || xa_state == XA_PREPARED))
- {
- my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
+ /* Data access in XA transaction is only allowed when it is active. */
+ if (*start && thd->transaction.xid_state.check_has_uncommitted_xa())
DBUG_RETURN(true);
- }
thd->current_tablenr= 0;
restart:
@@ -4356,13 +4534,14 @@ restart:
}
}
+#ifdef WITH_WSREP
if (WSREP(thd) &&
wsrep_replicate_myisam &&
(*start) &&
(*start)->table &&
(*start)->table->file->ht == myisam_hton &&
- wsrep_thd_exec_mode(thd) == LOCAL_STATE &&
- !is_stat_table(&(*start)->db, &(*start)->alias) &&
+ wsrep_thd_is_local(thd) &&
+ !is_stat_table(&(*start)->db, &(*start)->alias) &&
thd->get_command() != COM_STMT_PREPARE &&
((thd->lex->sql_command == SQLCOM_INSERT ||
thd->lex->sql_command == SQLCOM_INSERT_SELECT ||
@@ -4373,11 +4552,17 @@ restart:
thd->lex->sql_command == SQLCOM_LOAD ||
thd->lex->sql_command == SQLCOM_DELETE)))
{
- WSREP_TO_ISOLATION_BEGIN(NULL, NULL, (*start));
+ wsrep_before_rollback(thd, true);
+ wsrep_after_rollback(thd, true);
+ wsrep_after_statement(thd);
+ WSREP_TO_ISOLATION_BEGIN(NULL, NULL, (*start));
}
+#endif /* WITH_WSREP */
error:
-WSREP_ERROR_LABEL:
+#ifdef WITH_WSREP
+wsrep_error_label:
+#endif
THD_STAGE_INFO(thd, stage_after_opening_tables);
thd_proc_info(thd, 0);
@@ -5357,8 +5542,7 @@ static bool fix_all_session_vcol_exprs(THD *thd, TABLE_LIST *tables)
@retval TRUE A lock wait timeout, deadlock or out of memory.
*/
-bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
- uint flags)
+bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, uint flags)
{
TABLE_LIST *table;
DBUG_ENTER("lock_tables");
@@ -5611,43 +5795,27 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
DBUG_ENTER("update_field_dependencies");
if (should_mark_column(thd->column_usage))
{
- MY_BITMAP *bitmap;
-
/*
We always want to register the used keys, as the column bitmap may have
been set for all fields (for example for view).
*/
-
table->covering_keys.intersect(field->part_of_key);
- if (field->vcol_info)
- table->mark_virtual_col(field);
-
if (thd->column_usage == MARK_COLUMNS_READ)
- bitmap= table->read_set;
+ {
+ if (table->mark_column_with_deps(field))
+ DBUG_VOID_RETURN; // Field was already marked
+ }
else
- bitmap= table->write_set;
-
- /*
- The test-and-set mechanism in the bitmap is not reliable during
- multi-UPDATE statements under MARK_COLUMNS_READ mode
- (thd->column_usage == MARK_COLUMNS_READ), as this bitmap contains
- only those columns that are used in the SET clause. I.e they are being
- set here. See multi_update::prepare()
- */
- if (bitmap_fast_test_and_set(bitmap, field->field_index))
{
- if (thd->column_usage == MARK_COLUMNS_WRITE)
+ if (bitmap_fast_test_and_set(table->write_set, field->field_index))
{
DBUG_PRINT("warning", ("Found duplicated field"));
thd->dup_field= field;
+ DBUG_VOID_RETURN;
}
- else
- {
- DBUG_PRINT("note", ("Field found before"));
- }
- DBUG_VOID_RETURN;
}
+
table->used_fields++;
}
if (table->get_fields_in_item_tree)
@@ -7577,7 +7745,7 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array,
Item_window_func::split_sum_func.
*/
if (sum_func_list &&
- ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) ||
+ ((item->with_sum_func() && item->type() != Item::SUM_FUNC_ITEM) ||
item->with_window_func))
{
item->split_sum_func(thd, ref_pointer_array, *sum_func_list,
@@ -7685,7 +7853,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
TABLE_LIST *first_select_table= (select_insert ?
tables->next_local:
0);
- SELECT_LEX *select_lex= select_insert ? &thd->lex->select_lex :
+ SELECT_LEX *select_lex= select_insert ? thd->lex->first_select_lex() :
thd->lex->current_select;
if (select_lex->first_cond_optimization)
{
@@ -7713,7 +7881,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
{
/* new counting for SELECT of INSERT ... SELECT command */
first_select_table= 0;
- thd->lex->select_lex.insert_tables= tablenr;
+ thd->lex->first_select_lex()->insert_tables= tablenr;
tablenr= 0;
}
if(table_list->jtbm_subselect)
@@ -7729,11 +7897,15 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
DBUG_RETURN(1);
}
tablenr++;
- }
- if (tablenr > MAX_TABLES)
- {
- my_error(ER_TOO_MANY_TABLES,MYF(0), static_cast<int>(MAX_TABLES));
- DBUG_RETURN(1);
+ /*
+ We test the max tables here as we setup_table_map() should not be called
+ with tablenr >= 64
+ */
+ if (tablenr > MAX_TABLES)
+ {
+ my_error(ER_TOO_MANY_TABLES,MYF(0), static_cast<int>(MAX_TABLES));
+ DBUG_RETURN(1);
+ }
}
}
else
@@ -8064,18 +8236,9 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
if ((field= field_iterator.field()))
{
- /* 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);
+ field->table->mark_column_with_deps(field);
if (table)
- {
table->covering_keys.intersect(field->part_of_key);
- }
if (tables->is_natural_join)
{
TABLE *field_table;
@@ -8253,7 +8416,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves,
from subquery of VIEW, because tables of subquery belongs to VIEW
(see condition before prepare_check_option() call)
*/
- bool it_is_update= (select_lex == &thd->lex->select_lex) &&
+ bool it_is_update= (select_lex == thd->lex->first_select_lex()) &&
thd->lex->which_check_option_applicable();
bool save_is_item_list_lookup= select_lex->is_item_list_lookup;
TABLE_LIST *derived= select_lex->master_unit()->derived;
@@ -8269,7 +8432,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves,
for (table= tables; table; table= table->next_local)
{
- if (select_lex == &thd->lex->select_lex &&
+ if (select_lex == thd->lex->first_select_lex() &&
select_lex->first_cond_optimization &&
table->merged_for_insert &&
table->prepare_where(thd, conds, FALSE))
@@ -8440,12 +8603,12 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
table_arg->update_default_fields(ignore_errors))
goto err;
+ if (table_arg->versioned() && !only_unvers_fields)
+ table_arg->vers_update_fields();
/* Update virtual fields */
if (table_arg->vfield &&
table_arg->update_virtual_fields(table_arg->file, VCOL_UPDATE_FOR_WRITE))
goto err;
- if (table_arg->versioned() && !only_unvers_fields)
- table_arg->vers_update_fields();
thd->abort_on_warning= save_abort_on_warning;
thd->no_errors= save_no_errors;
DBUG_RETURN(thd->is_error());
@@ -8686,14 +8849,17 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
goto err;
field->set_has_explicit_value();
}
- /* Update virtual fields */
- thd->abort_on_warning= FALSE;
- if (table->vfield &&
- table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE))
- goto err;
- if (table->versioned())
- table->vers_update_fields();
- thd->abort_on_warning= abort_on_warning_saved;
+ /* Update virtual fields if there wasn't any errors */
+ if (!thd->is_error())
+ {
+ thd->abort_on_warning= FALSE;
+ if (table->versioned())
+ table->vers_update_fields();
+ if (table->vfield &&
+ table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE))
+ goto err;
+ thd->abort_on_warning= abort_on_warning_saved;
+ }
DBUG_RETURN(thd->is_error());
err:
@@ -8865,7 +9031,7 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
Item_func_match *ifm;
while ((ifm=li++))
- if (unlikely(!ifm->fixed))
+ if (unlikely(!ifm->is_fixed()))
/*
it mean that clause where was FT function was removed, so we have
to remove the function from the list.
@@ -8919,7 +9085,6 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
{
Query_tables_list query_tables_list_backup;
LEX *lex= thd->lex;
-
DBUG_ENTER("open_system_tables_for_read");
/*
@@ -8933,9 +9098,15 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
thd->reset_n_backup_open_tables_state(backup);
thd->lex->sql_command= SQLCOM_SELECT;
+ /*
+ Only use MYSQL_LOCK_IGNORE_TIMEOUT for tables opened for read.
+ This is to ensure that lock_wait_timeout is honored when trying
+ to update stats tables.
+ */
if (open_and_lock_tables(thd, table_list, FALSE,
- MYSQL_OPEN_IGNORE_FLUSH |
- MYSQL_LOCK_IGNORE_TIMEOUT))
+ (MYSQL_OPEN_IGNORE_FLUSH |
+ (table_list->lock_type < TL_WRITE_ALLOW_WRITE ?
+ MYSQL_LOCK_IGNORE_TIMEOUT : 0))))
{
lex->restore_backup_query_tables_list(&query_tables_list_backup);
thd->restore_backup_open_tables_state(backup);
@@ -8967,6 +9138,13 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
void
close_system_tables(THD *thd, Open_tables_backup *backup)
{
+ /*
+ Inform the transaction handler that we are closing the
+ system tables and we don't need the read view anymore.
+ */
+ for (TABLE *table= thd->open_tables ; table ; table= table->next)
+ table->file->extra(HA_EXTRA_PREPARE_FOR_FORCED_CLOSE);
+
close_thread_tables(thd);
thd->restore_backup_open_tables_state(backup);
}
@@ -9100,7 +9278,7 @@ void unfix_fields(List<Item> &fields)
List_iterator<Item> li(fields);
Item *item;
while ((item= li++))
- item->fixed= 0;
+ item->unfix_fields();
}
diff --git a/sql/sql_base.h b/sql/sql_base.h
index c9fb9bc6a62..28a787c56dd 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -57,6 +57,13 @@ enum enum_resolution_type {
RESOLVED_AGAINST_ALIAS
};
+/* Argument to flush_tables() of what to flush */
+enum flush_tables_type {
+ FLUSH_ALL,
+ FLUSH_NON_TRANS_TABLES,
+ FLUSH_SYS_TABLES
+};
+
enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
IGNORE_ERRORS, REPORT_EXCEPT_NON_UNIQUE,
IGNORE_EXCEPT_NON_UNIQUE};
@@ -289,12 +296,10 @@ TABLE *open_system_table_for_update(THD *thd, TABLE_LIST *one_table);
TABLE *open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup);
void close_log_table(THD *thd, Open_tables_backup *backup);
-TABLE *open_performance_schema_table(THD *thd, TABLE_LIST *one_table,
- Open_tables_state *backup);
-void close_performance_schema_table(THD *thd, Open_tables_state *backup);
-
bool close_cached_tables(THD *thd, TABLE_LIST *tables,
bool wait_for_refresh, ulong timeout);
+void purge_tables(bool purge_flag);
+bool flush_tables(THD *thd, flush_tables_type flag);
bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connect_string);
void close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
ha_extra_function extra,
@@ -348,13 +353,6 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr)
table->force_index= table_list->force_index;
table->force_index_order= table->force_index_group= 0;
table->covering_keys= table->s->keys_for_keyread;
- TABLE_LIST *orig= table_list->select_lex ?
- table_list->select_lex->master_unit()->derived : 0;
- if (!orig || !orig->is_merged_derived())
- {
- /* Tables merged from derived were set up already.*/
- table->covering_keys= table->s->keys_for_keyread;
- }
}
inline TABLE_LIST *find_table_in_global_list(TABLE_LIST *table,
@@ -372,10 +370,12 @@ inline bool setup_fields_with_no_wrap(THD *thd, Ref_ptr_array ref_pointer_array,
bool allow_sum_func)
{
bool res;
- thd->lex->select_lex.no_wrap_view_item= TRUE;
+ SELECT_LEX *first= thd->lex->first_select_lex();
+ DBUG_ASSERT(thd->lex->current_select == first);
+ first->no_wrap_view_item= TRUE;
res= setup_fields(thd, ref_pointer_array, item, column_usage,
sum_func_list, NULL, allow_sum_func);
- thd->lex->select_lex.no_wrap_view_item= FALSE;
+ first->no_wrap_view_item= FALSE;
return res;
}
@@ -559,14 +559,14 @@ public:
Set flag indicating that we have already acquired metadata lock
protecting this statement against GRL while opening tables.
*/
- void set_has_protection_against_grl()
+ void set_has_protection_against_grl(enum_mdl_type mdl_type)
{
- m_has_protection_against_grl= TRUE;
+ m_has_protection_against_grl|= MDL_BIT(mdl_type);
}
- bool has_protection_against_grl() const
+ bool has_protection_against_grl(enum_mdl_type mdl_type) const
{
- return m_has_protection_against_grl;
+ return (bool) (m_has_protection_against_grl & MDL_BIT(mdl_type));
}
private:
@@ -598,7 +598,7 @@ private:
Indicates that in the process of opening tables we have acquired
protection against global read lock.
*/
- bool m_has_protection_against_grl;
+ mdl_bitmap_t m_has_protection_against_grl;
};
diff --git a/sql/sql_basic_types.h b/sql/sql_basic_types.h
index 6749b2251df..170e93741ef 100644
--- a/sql/sql_basic_types.h
+++ b/sql/sql_basic_types.h
@@ -22,4 +22,314 @@
typedef ulonglong sql_mode_t;
typedef int64 query_id_t;
+
+
+/*
+ "fuzzydate" with strict data type control.
+ Represents a mixture of *only* data type conversion flags, without rounding.
+ Please keep "explicit" in constructors and conversion methods.
+*/
+class date_conv_mode_t
+{
+public:
+ enum value_t
+ {
+ CONV_NONE= 0U,
+ /*
+ FUZZY_DATES is used for the result will only be used for comparison
+ purposes. Conversion is as relaxed as possible.
+ */
+ FUZZY_DATES= 1U,
+ TIME_ONLY= 4U,
+ INTERVAL_hhmmssff= 8U,
+ INTERVAL_DAY= 16U,
+ RANGE0_LAST= INTERVAL_DAY,
+ NO_ZERO_IN_DATE= (1UL << 23), // MODE_NO_ZERO_IN_DATE
+ NO_ZERO_DATE= (1UL << 24), // MODE_NO_ZERO_DATE
+ INVALID_DATES= (1UL << 25) // MODE_INVALID_DATES
+ };
+
+ /*
+ BIT-OR for all known values. Let's have a separate enum for it.
+ - We don't put this value "value_t", to avoid handling it in switch().
+ - We don't put this value as a static const inside the class,
+ because "gdb" would display it every time when we do "print"
+ for a time_round_mode_t value.
+ - We can't put into into a function returning this value, because
+ it's not allowed to use functions in static_assert.
+ */
+ enum known_values_t
+ {
+ KNOWN_MODES= FUZZY_DATES |
+ TIME_ONLY | INTERVAL_hhmmssff | INTERVAL_DAY |
+ NO_ZERO_IN_DATE | NO_ZERO_DATE | INVALID_DATES
+ };
+private:
+ value_t m_mode;
+public:
+
+ // Constructors
+ explicit date_conv_mode_t(ulonglong fuzzydate)
+ :m_mode((value_t) fuzzydate)
+ { }
+
+ // Conversion operators
+ explicit operator ulonglong() const
+ {
+ return m_mode;
+ }
+ explicit operator bool() const
+ {
+ return m_mode != 0;
+ }
+
+ // Unary operators
+ ulonglong operator~() const
+ {
+ return ~m_mode;
+ }
+
+ // Dyadic bitwise operators
+ date_conv_mode_t operator&(const date_conv_mode_t &other) const
+ {
+ return date_conv_mode_t(m_mode & other.m_mode);
+ }
+ date_conv_mode_t operator&(const ulonglong other) const
+ {
+ return date_conv_mode_t(m_mode & other);
+ }
+
+ date_conv_mode_t operator|(const date_conv_mode_t &other) const
+ {
+ return date_conv_mode_t(m_mode | other.m_mode);
+ }
+
+ // Dyadic bitwise assignment operators
+ date_conv_mode_t &operator&=(const date_conv_mode_t &other)
+ {
+ m_mode= value_t(m_mode & other.m_mode);
+ return *this;
+ }
+
+ date_conv_mode_t &operator|=(const date_conv_mode_t &other)
+ {
+ m_mode= value_t(m_mode | other.m_mode);
+ return *this;
+ }
+};
+
+
+/*
+ Fractional rounding mode for temporal data types.
+*/
+class time_round_mode_t
+{
+public:
+ enum value_t
+ {
+ /*
+ Use FRAC_NONE when the value needs no rounding nor truncation,
+ because it is already known not to haveany fractional digits outside
+ of the requested precision.
+ */
+ FRAC_NONE= 0,
+ FRAC_TRUNCATE= date_conv_mode_t::RANGE0_LAST << 1, // 32
+ FRAC_ROUND= date_conv_mode_t::RANGE0_LAST << 2 // 64
+ };
+ // BIT-OR for all known values. See comments in time_conv_mode_t.
+ enum known_values_t
+ {
+ KNOWN_MODES= FRAC_TRUNCATE | FRAC_ROUND
+ };
+private:
+ value_t m_mode;
+public:
+ // Constructors
+ explicit time_round_mode_t(ulonglong mode)
+ :m_mode((value_t) mode)
+ {
+ DBUG_ASSERT(mode == FRAC_NONE ||
+ mode == FRAC_TRUNCATE ||
+ mode == FRAC_ROUND);
+ }
+ // Conversion operators
+ explicit operator ulonglong() const
+ {
+ return m_mode;
+ }
+ value_t mode() const
+ {
+ return m_mode;
+ }
+ // Comparison operators
+ bool operator==(const time_round_mode_t &other)
+ {
+ return m_mode == other.m_mode;
+ }
+};
+
+
+/*
+ "fuzzydate" with strict data type control.
+ Used as a parameter to get_date() and represents a mixture of:
+ - data type conversion flags
+ - fractional second rounding flags
+ Please keep "explicit" in constructors and conversion methods.
+*/
+class date_mode_t
+{
+public:
+ enum value_t
+ {
+ CONV_NONE= date_conv_mode_t::CONV_NONE, // 0
+ FUZZY_DATES= date_conv_mode_t::FUZZY_DATES, // 1
+ TIME_ONLY= date_conv_mode_t::TIME_ONLY, // 4
+ INTERVAL_hhmmssff= date_conv_mode_t::INTERVAL_hhmmssff, // 8
+ INTERVAL_DAY= date_conv_mode_t::INTERVAL_DAY, // 16
+ FRAC_TRUNCATE= time_round_mode_t::FRAC_TRUNCATE, // 32
+ FRAC_ROUND= time_round_mode_t::FRAC_ROUND, // 64
+ NO_ZERO_IN_DATE= date_conv_mode_t::NO_ZERO_IN_DATE, // (1UL << 23)
+ NO_ZERO_DATE= date_conv_mode_t::NO_ZERO_DATE, // (1UL << 24)
+ INVALID_DATES= date_conv_mode_t::INVALID_DATES, // (1UL << 25)
+ };
+protected:
+ value_t m_mode;
+public:
+
+ // Constructors
+ explicit date_mode_t(ulonglong fuzzydate)
+ :m_mode((value_t) fuzzydate)
+ { }
+
+ // Conversion operators
+ explicit operator ulonglong() const
+ {
+ return m_mode;
+ }
+ explicit operator bool() const
+ {
+ return m_mode != 0;
+ }
+ explicit operator date_conv_mode_t() const
+ {
+ return date_conv_mode_t(ulonglong(m_mode) & date_conv_mode_t::KNOWN_MODES);
+ }
+ explicit operator time_round_mode_t() const
+ {
+ return time_round_mode_t(ulonglong(m_mode) & time_round_mode_t::KNOWN_MODES);
+ }
+ // Unary operators
+ ulonglong operator~() const
+ {
+ return ~m_mode;
+ }
+ bool operator!() const
+ {
+ return !m_mode;
+ }
+
+ // Dyadic bitwise operators
+ date_mode_t operator&(const date_mode_t &other) const
+ {
+ return date_mode_t(m_mode & other.m_mode);
+ }
+ date_mode_t operator&(ulonglong other) const
+ {
+ return date_mode_t(m_mode & other);
+ }
+
+ date_mode_t operator|(const date_mode_t &other) const
+ {
+ return date_mode_t(m_mode | other.m_mode);
+ }
+
+ // Dyadic bitwise assignment operators
+ date_mode_t &operator&=(const date_mode_t &other)
+ {
+ m_mode= value_t(m_mode & other.m_mode);
+ return *this;
+ }
+
+ date_mode_t &operator|=(const date_mode_t &other)
+ {
+ m_mode= value_t(m_mode | other.m_mode);
+ return *this;
+ }
+
+ date_mode_t &operator|=(const date_conv_mode_t &other)
+ {
+ m_mode= value_t(m_mode | ulonglong(other));
+ return *this;
+ }
+};
+
+
+// Bitwise OR out-of-class operators for data type mixtures
+static inline date_mode_t operator|(const date_mode_t &a,
+ const date_conv_mode_t &b)
+{
+ return date_mode_t(ulonglong(a) | ulonglong(b));
+}
+
+static inline date_mode_t operator|(const date_conv_mode_t &a,
+ const time_round_mode_t &b)
+{
+ return date_mode_t(ulonglong(a) | ulonglong(b));
+}
+
+
+static inline date_mode_t operator|(const date_conv_mode_t &a,
+ const date_mode_t &b)
+{
+ return date_mode_t(ulonglong(a) | ulonglong(b));
+}
+
+
+// Bitwise AND out-of-class operators for data type mixtures
+static inline date_conv_mode_t operator&(const date_mode_t &a,
+ const date_conv_mode_t &b)
+{
+ return date_conv_mode_t(ulonglong(a) & ulonglong(b));
+}
+
+static inline date_conv_mode_t operator&(const date_conv_mode_t &a,
+ const date_mode_t &b)
+{
+ return date_conv_mode_t(ulonglong(a) & ulonglong(b));
+}
+
+static inline date_conv_mode_t operator&(sql_mode_t &a,
+ const date_conv_mode_t &b)
+{
+ return date_conv_mode_t(a & ulonglong(b));
+}
+
+
+static const date_conv_mode_t
+ TIME_CONV_NONE (date_conv_mode_t::CONV_NONE),
+ TIME_FUZZY_DATES (date_conv_mode_t::FUZZY_DATES),
+ TIME_TIME_ONLY (date_conv_mode_t::TIME_ONLY),
+ TIME_INTERVAL_hhmmssff (date_conv_mode_t::INTERVAL_hhmmssff),
+ TIME_INTERVAL_DAY (date_conv_mode_t::INTERVAL_DAY),
+ TIME_NO_ZERO_IN_DATE (date_conv_mode_t::NO_ZERO_IN_DATE),
+ TIME_NO_ZERO_DATE (date_conv_mode_t::NO_ZERO_DATE),
+ TIME_INVALID_DATES (date_conv_mode_t::INVALID_DATES);
+
+// An often used combination
+static const date_conv_mode_t
+ TIME_NO_ZEROS (date_conv_mode_t::NO_ZERO_DATE|
+ date_conv_mode_t::NO_ZERO_IN_DATE);
+
+// Flags understood by str_to_xxx, number_to_xxx, check_date
+static const date_conv_mode_t
+ TIME_MODE_FOR_XXX_TO_DATE (date_mode_t::NO_ZERO_IN_DATE |
+ date_mode_t::NO_ZERO_DATE |
+ date_mode_t::INVALID_DATES);
+
+static const time_round_mode_t
+ TIME_FRAC_NONE (time_round_mode_t::FRAC_NONE),
+ TIME_FRAC_TRUNCATE (time_round_mode_t::FRAC_TRUNCATE),
+ TIME_FRAC_ROUND (time_round_mode_t::FRAC_ROUND);
+
+
#endif
diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc
index 1b8ba311a80..f1326d97a21 100644
--- a/sql/sql_binlog.cc
+++ b/sql/sql_binlog.cc
@@ -147,7 +147,7 @@ int binlog_defragment(THD *thd)
(char *) my_malloc(thd->lex->comment.length, MYF(MY_WME));
if (!thd->lex->comment.str)
{
- my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 1);
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 1);
return -1;
}
@@ -226,7 +226,7 @@ void mysql_client_binlog_statement(THD* thd)
*/
if (!(rli))
{
- my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 1); /* needed 1 bytes */
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 1); /* needed 1 bytes */
goto end;
}
@@ -245,7 +245,7 @@ void mysql_client_binlog_statement(THD* thd)
decoded_len= my_base64_needed_decoded_length((int)coded_len);
if (!(buf= (char *) my_malloc(decoded_len, MYF(MY_WME))))
{
- my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 1);
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 1);
goto end;
}
diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h
index ca9ba0b67f4..cce80ce2bc8 100644
--- a/sql/sql_bitmap.h
+++ b/sql/sql_bitmap.h
@@ -25,73 +25,211 @@
#include <my_sys.h>
#include <my_bitmap.h>
+#include <my_bit.h>
-template <uint default_width> class Bitmap
+
+template <uint width> class Bitmap
{
- MY_BITMAP map;
- uint32 buffer[(default_width+31)/32];
+
+/*
+ Workaround GCC optimizer bug (generating SSE instuctions on unaligned data)
+*/
+#if defined (__GNUC__) && defined(__x86_64__) && (__GNUC__ < 6) && !defined(__clang__)
+#define NEED_GCC_NO_SSE_WORKAROUND
+#endif
+
+#ifdef NEED_GCC_NO_SSE_WORKAROUND
+#pragma GCC push_options
+#pragma GCC target ("no-sse")
+#endif
+
+ uint32 buffer[(width + 31) / 32];
public:
- Bitmap() { init(); }
- Bitmap(const Bitmap& from) { *this=from; }
- explicit Bitmap(uint prefix_to_set) { init(prefix_to_set); }
- void init() { my_bitmap_init(&map, buffer, default_width, 0); }
- void init(uint prefix_to_set) { init(); set_prefix(prefix_to_set); }
- uint length() const { return default_width; }
- Bitmap& operator=(const Bitmap& map2)
- {
- init();
- memcpy(buffer, map2.buffer, sizeof(buffer));
- return *this;
- }
- void set_bit(uint n) { bitmap_set_bit(&map, n); }
- void clear_bit(uint n) { bitmap_clear_bit(&map, n); }
- void set_prefix(uint n) { bitmap_set_prefix(&map, n); }
- void set_all() { bitmap_set_all(&map); }
- void clear_all() { bitmap_clear_all(&map); }
- void intersect(Bitmap& map2) { bitmap_intersect(&map, &map2.map); }
- void intersect(ulonglong map2buff)
+ Bitmap()
{
- // Use a spearate temporary buffer, as bitmap_init() clears all the bits.
- ulonglong buf2;
- MY_BITMAP map2;
+ clear_all();
+ }
+ explicit Bitmap(uint prefix)
+ {
+ set_prefix(prefix);
+ }
+ void init(uint prefix)
+ {
+ set_prefix(prefix);
+ }
- my_bitmap_init(&map2, (uint32 *) &buf2, sizeof(ulonglong) * 8, 0);
+ uint length() const
+ {
+ return width;
+ }
+ void set_bit(uint n)
+ {
+ DBUG_ASSERT(n < width);
+ ((uchar*)buffer)[n / 8] |= (1 << (n & 7));
+ }
+ void clear_bit(uint n)
+ {
+ DBUG_ASSERT(n < width);
+ ((uchar*)buffer)[n / 8] &= ~(1 << (n & 7));
+ }
+ void set_prefix(uint prefix_size)
+ {
+ set_if_smaller(prefix_size, width);
+ uint prefix_bytes, prefix_bits, d;
+ uchar* m = (uchar*)buffer;
- // Store the original bits.
- if (sizeof(ulonglong) >= 8)
+ if ((prefix_bytes = prefix_size / 8))
+ memset(m, 0xff, prefix_bytes);
+ m += prefix_bytes;
+ if ((prefix_bits = prefix_size & 7))
{
- int8store(const_cast<uchar *>(static_cast<uchar *>
- (static_cast<void *>(&buf2))),
- map2buff);
+ *(m++) = (1 << prefix_bits) - 1;
+ // As the prefix bits are set, lets count this byte too as a prefix byte.
+ prefix_bytes++;
}
- else
+ if ((d = (width + 7) / 8 - prefix_bytes))
+ memset(m, 0, d);
+ }
+ void set_all()
+ {
+ set_prefix(width);
+ }
+ void clear_all()
+ {
+ memset(buffer, 0x00, sizeof(buffer));
+ }
+ void intersect(Bitmap & map2)
+ {
+ for (uint i = 0; i < array_elements(buffer); i++)
+ buffer[i] &= map2.buffer[i];
+ }
+
+private:
+ /*
+ Intersect with a bitmap represented as as longlong.
+ In addition, pad the rest of the bitmap with 0 or 1 bits
+ depending on pad_with_ones parameter.
+ */
+ void intersect_and_pad(ulonglong map2buff, bool pad_with_ones)
+ {
+ compile_time_assert(sizeof(ulonglong) == 8);
+ uint32 tmp[2];
+ int8store(tmp, map2buff);
+
+ buffer[0] &= tmp[0];
+ if (array_elements(buffer) > 1)
+ buffer[1] &= tmp[1];
+
+ if (array_elements(buffer) <= 2)
+ return;
+ if (pad_with_ones)
{
- DBUG_ASSERT(sizeof(buffer) >= 4);
- int4store(const_cast<uchar *>(static_cast<uchar *>
- (static_cast<void *>(&buf2))),
- static_cast<uint32>(map2buff));
+ memset((char*)buffer + 8, 0xff , sizeof(buffer) - 8);
+ if (width != sizeof(buffer) * 8)
+ {
+ ((uchar*)buffer)[sizeof(buffer)-1] = last_byte_mask(width);
+ }
}
+ else
+ memset((char*)buffer + 8, 0 , sizeof(buffer) - 8);
- bitmap_intersect(&map, &map2);
+ }
+
+public:
+ void intersect(ulonglong map2buff)
+ {
+ intersect_and_pad(map2buff, 0);
}
/* Use highest bit for all bits above sizeof(ulonglong)*8. */
void intersect_extended(ulonglong map2buff)
{
- intersect(map2buff);
- if (map.n_bits > sizeof(ulonglong) * 8)
- bitmap_set_above(&map, sizeof(ulonglong),
- MY_TEST(map2buff & (1LL << (sizeof(ulonglong) * 8 - 1))));
- }
- void subtract(Bitmap& map2) { bitmap_subtract(&map, &map2.map); }
- void merge(Bitmap& map2) { bitmap_union(&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); }
- bool operator!=(const Bitmap& map2) const { return !(*this == map2); }
+ intersect_and_pad(map2buff, (map2buff & (1ULL << 63)));
+ }
+ void subtract(Bitmap & map2)
+ {
+ for (size_t i = 0; i < array_elements(buffer); i++)
+ buffer[i] &= ~(map2.buffer[i]);
+ }
+ void merge(Bitmap & map2)
+ {
+ for (size_t i = 0; i < array_elements(buffer); i++)
+ buffer[i] |= map2.buffer[i];
+ }
+ bool is_set(uint n) const
+ {
+ DBUG_ASSERT(n < width);
+ return ((uchar*)buffer)[n / 8] & (1 << (n & 7));
+ }
+ bool is_prefix(uint prefix_size) const
+ {
+ uint prefix_mask = last_byte_mask(prefix_size);
+ uchar* m = (uchar*)buffer;
+ uchar* end_prefix = m + (prefix_size - 1) / 8;
+ uchar* end;
+ DBUG_ASSERT(prefix_size <= width);
+
+ /* Empty prefix is always true */
+ if (!prefix_size)
+ return true;
+
+ while (m < end_prefix)
+ if (*m++ != 0xff)
+ return false;
+
+ end = ((uchar*)buffer) + (width + 7) / 8 - 1;
+ if (m == end)
+ return ((*m & last_byte_mask(width)) == prefix_mask);
+
+ if (*m != prefix_mask)
+ return false;
+
+ while (++m < end)
+ if (*m != 0)
+ return false;
+ return ((*m & last_byte_mask(width)) == 0);
+ }
+ bool is_clear_all() const
+ {
+ for (size_t i= 0; i < array_elements(buffer); i++)
+ if (buffer[i])
+ return false;
+ return true;
+ }
+ bool is_set_all() const
+ {
+ if (width == sizeof(buffer) * 8)
+ {
+ for (size_t i = 0; i < array_elements(buffer); i++)
+ if (buffer[i] != 0xFFFFFFFFU)
+ return false;
+ return true;
+ }
+ else
+ return is_prefix(width);
+ }
+
+ bool is_subset(const Bitmap & map2) const
+ {
+ for (size_t i= 0; i < array_elements(buffer); i++)
+ if (buffer[i] & ~(map2.buffer[i]))
+ return false;
+ return true;
+ }
+ bool is_overlapping(const Bitmap & map2) const
+ {
+ for (size_t i = 0; i < array_elements(buffer); i++)
+ if (buffer[i] & map2.buffer[i])
+ return true;
+ return false;
+ }
+ bool operator==(const Bitmap & map2) const
+ {
+ return memcmp(buffer, map2.buffer, sizeof(buffer)) == 0;
+ }
+ bool operator!=(const Bitmap & map2) const
+ {
+ return !(*this == map2);
+ }
char *print(char *buf) const
{
char *s=buf;
@@ -111,36 +249,44 @@ public:
}
ulonglong to_ulonglong() const
{
- if (sizeof(buffer) >= 8)
- return uint8korr(static_cast<const uchar *>
- (static_cast<const void *>(buffer)));
DBUG_ASSERT(sizeof(buffer) >= 4);
- return (ulonglong)
- uint4korr(static_cast<const uchar *>
- (static_cast<const void *>(buffer)));
+ uchar *b=(uchar *)buffer;
+ if (sizeof(buffer) >= 8)
+ return uint8korr(b);
+ return (ulonglong) uint4korr(b);
}
uint bits_set()
{
- return bitmap_bits_set(&map);
+ uint res = 0;
+ for (size_t i = 0; i < array_elements(buffer); i++)
+ res += my_count_bits_uint32(buffer[i]);
+ return res;
}
class Iterator
{
Bitmap &map;
uint no;
public:
- Iterator(Bitmap<default_width> &map2): map(map2), no(0) {}
+ Iterator(Bitmap<width> &map2): map(map2), no(0) {}
int operator++(int) {
- if (no == default_width) return BITMAP_END;
+ if (no == width) return BITMAP_END;
while (!map.is_set(no))
{
- if ((++no) == default_width) return BITMAP_END;
+ if ((++no) == width) return BITMAP_END;
}
- return no ++;
+ return no++;
}
- enum { BITMAP_END= default_width };
+ enum { BITMAP_END = width };
};
+
+#ifdef NEED_GCC_NO_SSE_WORKAROUND
+#pragma GCC pop_options
+#undef NEED_GCC_NO_SSE_WORKAROUND
+#endif
+
};
+
/* An iterator to quickly walk over bits in ulonglong bitmap. */
class Table_map_iterator
{
@@ -175,7 +321,6 @@ template <> class Bitmap<64>
public:
Bitmap<64>() { }
explicit Bitmap<64>(uint prefix_to_set) { set_prefix(prefix_to_set); }
- void init() { }
void init(uint prefix_to_set) { set_prefix(prefix_to_set); }
uint length() const { return 64; }
void set_bit(uint n) { map|= ((ulonglong)1) << n; }
@@ -224,5 +369,12 @@ public:
}
};
+#if MAX_INDEXES <= 64
+typedef Bitmap<64> key_map; /* Used for finding keys */
+#elif MAX_INDEXES > 128
+#error "MAX_INDEXES values greater than 128 is not supported."
+#else
+typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */
+#endif
#endif /* SQL_BITMAP_INCLUDED */
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 497e51dfdce..bef3318f974 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -276,8 +276,8 @@ functions:
- Called before parsing and used to match a statement with the stored
queries hash.
If a match is found the cached result set is sent through repeated
- calls to net_real_write. (note: calling thread doesn't have a regis-
- tered result set writer: thd->net.query_cache_query=0)
+ calls to net_real_write. (note: calling thread does not have a
+ registered result set writer: thd->net.query_cache_query=0)
2. Query_cache::store_query
- Called just before handle_select() and is used to register a result
set writer to the statement currently being processed
@@ -477,8 +477,7 @@ static void make_base_query(String *new_query,
/* We do not support UCS2, UTF16, UTF32 as a client character set */
DBUG_ASSERT(current_thd->variables.character_set_client->mbminlen == 1);
- new_query->length(0); // Don't copy anything from old buffer
- if (new_query->realloc(query_length + additional_length))
+ if (new_query->alloc(query_length + additional_length))
{
/*
We could not allocate the query. Use original query for
@@ -4154,13 +4153,13 @@ Query_cache::is_cacheable(THD *thd, LEX *lex,
if (thd->lex->safe_to_cache_query &&
(thd->variables.query_cache_type == 1 ||
- (thd->variables.query_cache_type == 2 && (lex->select_lex.options &
- OPTION_TO_QUERY_CACHE))) &&
+ (thd->variables.query_cache_type == 2 &&
+ (lex->first_select_lex()->options & OPTION_TO_QUERY_CACHE))) &&
qc_is_able_to_intercept_result(thd))
{
DBUG_PRINT("qcache", ("options: %lx %lx type: %u",
(long) OPTION_TO_QUERY_CACHE,
- (long) lex->select_lex.options,
+ (long) lex->first_select_lex()->options,
(int) thd->variables.query_cache_type));
if (!(table_count= process_and_count_tables(thd, tables_used,
@@ -4181,7 +4180,7 @@ Query_cache::is_cacheable(THD *thd, LEX *lex,
("not interesting query: %d or not cacheable, options %lx %lx type: %u net->vio present: %u",
(int) lex->sql_command,
(long) OPTION_TO_QUERY_CACHE,
- (long) lex->select_lex.options,
+ (long) lex->first_select_lex()->options,
(int) thd->variables.query_cache_type,
(uint) MY_TEST(qc_is_able_to_intercept_result(thd))));
DBUG_RETURN(0);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index c4905fcabf6..0d61a38e918 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -49,9 +49,6 @@
#include <m_ctype.h>
#include <sys/stat.h>
#include <thr_alarm.h>
-#ifdef __WIN__0
-#include <io.h>
-#endif
#include <mysys_err.h>
#include <limits.h>
@@ -66,14 +63,18 @@
#include "sql_callback.h"
#include "lock.h"
#include "wsrep_mysqld.h"
-#include "wsrep_thd.h"
#include "sql_connect.h"
-#include "my_atomic.h"
+#ifdef WITH_WSREP
+#include "wsrep_thd.h"
+#include "wsrep_trans_observer.h"
+#else
+static inline bool wsrep_is_bf_aborted(THD* thd) { return false; }
+#endif /* WITH_WSREP */
+#include "opt_trace.h"
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
#endif
-#include "repl_failsafe.h"
/*
The following is used to initialise Table_ident with a internal
@@ -647,17 +648,50 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
tdc_hash_pins(0),
xid_hash_pins(0),
m_tmp_tables_locked(false)
+#ifdef HAVE_REPLICATION
+ ,
+ current_linfo(0),
+ slave_info(0)
+#endif
#ifdef WITH_WSREP
- ,
+ ,
wsrep_applier(is_wsrep_applier),
wsrep_applier_closing(false),
wsrep_client_thread(false),
- wsrep_apply_toi(false),
+ wsrep_retry_counter(0),
+ wsrep_PA_safe(true),
+ wsrep_retry_query(NULL),
+ wsrep_retry_query_len(0),
+ wsrep_retry_command(COM_CONNECT),
+ wsrep_consistency_check(NO_CONSISTENCY_CHECK),
+ wsrep_mysql_replicated(0),
+ wsrep_TOI_pre_query(NULL),
+ wsrep_TOI_pre_query_len(0),
wsrep_po_handle(WSREP_PO_INITIALIZER),
wsrep_po_cnt(0),
wsrep_apply_format(0),
- wsrep_ignore_table(false)
-#endif
+ wsrep_rbr_buf(NULL),
+ wsrep_sync_wait_gtid(WSREP_GTID_UNDEFINED),
+ wsrep_affected_rows(0),
+ wsrep_has_ignored_error(false),
+ wsrep_replicate_GTID(false),
+ wsrep_ignore_table(false),
+ wsrep_aborter(0),
+
+/* wsrep-lib */
+ m_wsrep_next_trx_id(WSREP_UNDEFINED_TRX_ID),
+ m_wsrep_mutex(LOCK_thd_data),
+ m_wsrep_cond(COND_wsrep_thd),
+ m_wsrep_client_service(this, m_wsrep_client_state),
+ m_wsrep_client_state(this,
+ m_wsrep_mutex,
+ m_wsrep_cond,
+ Wsrep_server_state::instance(),
+ m_wsrep_client_service,
+ wsrep::client_id(thread_id)),
+ wsrep_applier_service(NULL),
+ wsrep_wfc()
+#endif /*WITH_WSREP */
{
ulong tmp;
bzero(&variables, sizeof(variables));
@@ -676,6 +710,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
main_da.init();
mdl_context.init(this);
+ mdl_backup_lock= 0;
/*
Pass nominal parameters to init_alloc_root only to ensure that
@@ -725,7 +760,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
progress.arena= 0;
progress.report_to_client= 0;
progress.max_counter= 0;
- current_linfo = 0;
slave_thread = 0;
connection_name.str= 0;
connection_name.length= 0;
@@ -750,7 +784,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
net.reading_or_writing= 0;
client_capabilities= 0; // minimalistic client
system_thread= NON_SYSTEM_THREAD;
- cleanup_done= free_connection_done= abort_on_warning= 0;
+ cleanup_done= free_connection_done= abort_on_warning= got_warning= 0;
peer_port= 0; // For SHOW PROCESSLIST
transaction.m_pending_rows_event= 0;
transaction.on= 1;
@@ -765,11 +799,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
mysql_mutex_init(key_LOCK_wakeup_ready, &LOCK_wakeup_ready, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_thd_kill, &LOCK_thd_kill, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_COND_wakeup_ready, &COND_wakeup_ready, 0);
- /*
- LOCK_thread_count goes before LOCK_thd_data - the former is called around
- 'delete thd', the latter - in THD::~THD
- */
- mysql_mutex_record_order(&LOCK_thread_count, &LOCK_thd_data);
+ mysql_mutex_record_order(&LOCK_thd_kill, &LOCK_thd_data);
/* Variables with default values */
proc_info="login";
@@ -779,23 +809,8 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
*scramble= '\0';
#ifdef WITH_WSREP
- wsrep_ws_handle.trx_id = WSREP_UNDEFINED_TRX_ID;
- wsrep_ws_handle.opaque = NULL;
- wsrep_retry_counter = 0;
- wsrep_PA_safe = true;
- wsrep_retry_query = NULL;
- wsrep_retry_query_len = 0;
- wsrep_retry_command = COM_CONNECT;
- wsrep_consistency_check = NO_CONSISTENCY_CHECK;
- wsrep_mysql_replicated = 0;
- wsrep_TOI_pre_query = NULL;
- wsrep_TOI_pre_query_len = 0;
+ mysql_cond_init(key_COND_wsrep_thd, &COND_wsrep_thd, NULL);
wsrep_info[sizeof(wsrep_info) - 1] = '\0'; /* make sure it is 0-terminated */
- wsrep_sync_wait_gtid = WSREP_GTID_UNDEFINED;
- wsrep_affected_rows = 0;
- wsrep_replicate_GTID = false;
- wsrep_skip_wsrep_GTID = false;
- wsrep_split_flag = false;
#endif
/* Call to init() below requires fully initialized Open_tables_state. */
reset_open_tables_state(this);
@@ -859,7 +874,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
org_charset= 0;
/* Restore THR_THD */
set_current_thd(old_THR_THD);
- inc_thread_count();
}
@@ -1013,6 +1027,15 @@ Sql_condition* THD::raise_condition(uint sql_errno,
if (!(variables.option_bits & OPTION_SQL_NOTES) &&
(level == Sql_condition::WARN_LEVEL_NOTE))
DBUG_RETURN(NULL);
+#ifdef WITH_WSREP
+ /*
+ Suppress warnings/errors if the wsrep THD is going to replay. The
+ deadlock/interrupted errors may be transitient and should not be
+ reported to the client.
+ */
+ if (wsrep_must_replay(this))
+ DBUG_RETURN(NULL);
+#endif /* WITH_WSREP */
da->opt_clear_warning_info(query_id);
@@ -1038,7 +1061,8 @@ Sql_condition* THD::raise_condition(uint sql_errno,
level= Sql_condition::WARN_LEVEL_ERROR;
}
- if (handle_condition(sql_errno, sqlstate, &level, msg, &cond))
+ if (!is_fatal_error &&
+ handle_condition(sql_errno, sqlstate, &level, msg, &cond))
DBUG_RETURN(cond);
switch (level) {
@@ -1059,10 +1083,25 @@ Sql_condition* THD::raise_condition(uint sql_errno,
is_slave_error= 1; // needed to catch query errors during replication
- if (!da->is_error())
+#ifdef WITH_WSREP
+ /*
+ With wsrep we allow converting BF abort error to warning if
+ errors are ignored.
+ */
+ if (!is_fatal_error &&
+ no_errors &&
+ (wsrep_trx().bf_aborted() || wsrep_retry_counter))
+ {
+ WSREP_DEBUG("BF abort error converted to warning");
+ }
+ else
+#endif /* WITH_WSREP */
{
- set_row_count_func(-1);
- da->set_error_status(sql_errno, msg, sqlstate, ucid, cond);
+ if (!da->is_error())
+ {
+ set_row_count_func(-1);
+ da->set_error_status(sql_errno, msg, sqlstate, ucid, cond);
+ }
}
}
@@ -1123,7 +1162,16 @@ void *thd_memdup(MYSQL_THD thd, const void* str, size_t size)
extern "C"
void thd_get_xid(const MYSQL_THD thd, MYSQL_XID *xid)
{
- *xid = *(MYSQL_XID *) &thd->transaction.xid_state.xid;
+#ifdef WITH_WSREP
+ if (!thd->wsrep_xid.is_null())
+ {
+ *xid = *(MYSQL_XID *) &thd->wsrep_xid;
+ return;
+ }
+#endif /* WITH_WSREP */
+ *xid= thd->transaction.xid_state.is_explicit_XA() ?
+ *(MYSQL_XID *) thd->transaction.xid_state.get_xid() :
+ *(MYSQL_XID *) &thd->transaction.implicit_xid;
}
@@ -1225,13 +1273,12 @@ void THD::init()
first_successful_insert_id_in_prev_stmt= 0;
first_successful_insert_id_in_prev_stmt_for_binlog= 0;
first_successful_insert_id_in_cur_stmt= 0;
+ current_backup_stage= BACKUP_FINISHED;
+ backup_commit_lock= 0;
#ifdef WITH_WSREP
- wsrep_exec_mode= wsrep_applier ? REPL_RECV : LOCAL_STATE;
- wsrep_conflict_state= NO_CONFLICT;
- wsrep_thd_set_query_state(this, QUERY_IDLE);
wsrep_last_query_id= 0;
- wsrep_trx_meta.gtid= WSREP_GTID_UNDEFINED;
- wsrep_trx_meta.depends_on= WSREP_SEQNO_UNDEFINED;
+ wsrep_xid.null();
+ wsrep_skip_locking= FALSE;
wsrep_converted_lock_session= false;
wsrep_retry_counter= 0;
wsrep_rgi= NULL;
@@ -1240,11 +1287,11 @@ void THD::init()
wsrep_mysql_replicated = 0;
wsrep_TOI_pre_query = NULL;
wsrep_TOI_pre_query_len = 0;
- wsrep_sync_wait_gtid = WSREP_GTID_UNDEFINED;
+ wsrep_rbr_buf = NULL;
wsrep_affected_rows = 0;
+ m_wsrep_next_trx_id = WSREP_UNDEFINED_TRX_ID;
wsrep_replicate_GTID = false;
- wsrep_skip_wsrep_GTID = false;
- wsrep_split_flag = false;
+ wsrep_aborter = 0;
#endif /* WITH_WSREP */
if (variables.sql_log_bin)
@@ -1338,14 +1385,20 @@ void THD::update_all_stats()
void THD::init_for_queries()
{
set_time();
- ha_enable_transaction(this,TRUE);
+ /*
+ We don't need to call ha_enable_transaction() as we can't have
+ any active transactions that has to be committed
+ */
+ DBUG_ASSERT(transaction.is_empty());
+ transaction.on= TRUE;
reset_root_defaults(mem_root, variables.query_alloc_block_size,
variables.query_prealloc_size);
reset_root_defaults(&transaction.mem_root,
variables.trans_alloc_block_size,
variables.trans_prealloc_size);
- transaction.xid_state.xid.null();
+ DBUG_ASSERT(!transaction.xid_state.is_explicit_XA());
+ DBUG_ASSERT(transaction.implicit_xid.is_null());
}
@@ -1387,6 +1440,7 @@ void THD::change_user(void)
sp_cache_clear(&sp_func_cache);
sp_cache_clear(&sp_package_spec_cache);
sp_cache_clear(&sp_package_body_cache);
+ opt_trace.delete_traces();
}
/**
@@ -1418,7 +1472,7 @@ bool THD::set_db(const LEX_CSTRING *new_db)
const char *tmp= NULL;
if (new_db->str)
{
- if (!(tmp= my_strndup(new_db->str, new_db->length, MYF(MY_WME | ME_FATALERROR))))
+ if (!(tmp= my_strndup(new_db->str, new_db->length, MYF(MY_WME | ME_FATAL))))
result= 1;
}
@@ -1467,12 +1521,11 @@ void THD::cleanup(void)
DBUG_ASSERT(cleanup_done == 0);
set_killed(KILL_CONNECTION);
-#ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE
- if (transaction.xid_state.xa_state == XA_PREPARED)
- {
-#error xid_state in the cache should be replaced by the allocated value
- }
-#endif
+#ifdef WITH_WSREP
+ if (wsrep_cs().state() != wsrep::client_state::s_none)
+ wsrep_cs().cleanup();
+ wsrep_client_thread= false;
+#endif /* WITH_WSREP */
mysql_ha_cleanup(this);
locked_tables_list.unlock_locked_tables(this);
@@ -1480,10 +1533,10 @@ void THD::cleanup(void)
delete_dynamic(&user_var_events);
close_temporary_tables();
- transaction.xid_state.xa_state= XA_NOTR;
- transaction.xid_state.rm_error= 0;
- trans_rollback(this);
- xid_cache_delete(this, &transaction.xid_state);
+ if (transaction.xid_state.is_explicit_XA())
+ trans_xa_detach(this);
+ else
+ trans_rollback(this);
DBUG_ASSERT(open_tables == NULL);
/*
@@ -1492,7 +1545,10 @@ void THD::cleanup(void)
and left the mode a few lines above), there will be outstanding
metadata locks. Release them.
*/
- mdl_context.release_transactional_locks();
+ mdl_context.release_transactional_locks(this);
+
+ backup_end(this);
+ backup_unlock(this);
/* Release the global read lock, if acquired. */
if (global_read_lock.is_acquired())
@@ -1526,9 +1582,8 @@ void THD::cleanup(void)
apc_target.destroy();
#ifdef HAVE_REPLICATION
- unregister_slave(this, true, true);
+ unregister_slave();
#endif
-
cleanup_done=1;
DBUG_VOID_RETURN;
}
@@ -1549,7 +1604,7 @@ void THD::free_connection()
#ifndef EMBEDDED_LIBRARY
if (net.vio)
vio_delete(net.vio);
- net.vio= 0;
+ net.vio= nullptr;
net_end(&net);
#endif
if (!cleanup_done)
@@ -1588,12 +1643,16 @@ void THD::reset_for_reuse()
abort_on_warning= 0;
free_connection_done= 0;
m_command= COM_CONNECT;
+ transaction.on= 1;
#if defined(ENABLED_PROFILING)
profiling.reset();
#endif
#ifdef SIGNAL_WITH_VIO_CLOSE
active_vio = 0;
#endif
+#ifdef WITH_WSREP
+ wsrep_free_status(this);
+#endif /* WITH_WSREP */
}
@@ -1602,10 +1661,8 @@ THD::~THD()
THD *orig_thd= current_thd;
THD_CHECK_SENTRY(this);
DBUG_ENTER("~THD()");
- /* Check that we have already called thd->unlink() */
- DBUG_ASSERT(prev == 0 && next == 0);
- /* This takes a long time so we should not do this under LOCK_thread_count */
- mysql_mutex_assert_not_owner(&LOCK_thread_count);
+ /* Make sure threads are not available via server_threads. */
+ assert_not_linked();
/*
In error cases, thd may not be current thd. We have to fix this so
@@ -1629,6 +1686,9 @@ THD::~THD()
if (!free_connection_done)
free_connection();
+#ifdef WITH_WSREP
+ mysql_cond_destroy(&COND_wsrep_thd);
+#endif
mdl_context.destroy();
free_root(&transaction.mem_root,MYF(0));
@@ -1680,7 +1740,6 @@ THD::~THD()
}
update_global_memory_status(status_var.global_memory_used);
set_current_thd(orig_thd == this ? 0 : orig_thd);
- dec_thread_count();
DBUG_VOID_RETURN;
}
@@ -1810,6 +1869,7 @@ void THD::awake_no_mutex(killed_state state_to_set)
DBUG_PRINT("enter", ("this: %p current_thd: %p state: %d",
this, current_thd, (int) state_to_set));
THD_CHECK_SENTRY(this);
+ mysql_mutex_assert_owner(&LOCK_thd_data);
mysql_mutex_assert_owner(&LOCK_thd_kill);
print_aborted_warning(3, "KILLED");
@@ -1842,14 +1902,21 @@ void THD::awake_no_mutex(killed_state state_to_set)
}
/* Interrupt target waiting inside a storage engine. */
- if (state_to_set != NOT_KILLED)
+ if (state_to_set != NOT_KILLED && !wsrep_is_bf_aborted(this))
ha_kill_query(this, thd_kill_level(this));
- /* Broadcast a condition to kick the target if it is waiting on it. */
+ abort_current_cond_wait(false);
+ DBUG_VOID_RETURN;
+}
+
+/* Broadcast a condition to kick the target if it is waiting on it. */
+void THD::abort_current_cond_wait(bool force)
+{
+ mysql_mutex_assert_owner(&LOCK_thd_kill);
if (mysys_var)
{
mysql_mutex_lock(&mysys_var->mutex);
- if (!system_thread) // Don't abort locks
+ if (!system_thread || force) // Don't abort locks
mysys_var->abort=1;
/*
@@ -1907,7 +1974,6 @@ void THD::awake_no_mutex(killed_state state_to_set)
}
mysql_mutex_unlock(&mysys_var->mutex);
}
- DBUG_VOID_RETURN;
}
@@ -1961,16 +2027,7 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use,
mysql_mutex_lock(&in_use->LOCK_thd_kill);
if (in_use->killed < KILL_CONNECTION)
in_use->set_killed_no_mutex(KILL_CONNECTION);
- if (in_use->mysys_var)
- {
- mysql_mutex_lock(&in_use->mysys_var->mutex);
- if (in_use->mysys_var->current_cond)
- mysql_cond_broadcast(in_use->mysys_var->current_cond);
-
- /* Abort if about to wait in thr_upgrade_write_delay_lock */
- in_use->mysys_var->abort= 1;
- mysql_mutex_unlock(&in_use->mysys_var->mutex);
- }
+ in_use->abort_current_cond_wait(true);
mysql_mutex_unlock(&in_use->LOCK_thd_kill);
signalled= TRUE;
}
@@ -1995,12 +2052,6 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use,
if (!thd_table->needs_reopen())
{
signalled|= mysql_lock_abort_for_thread(this, thd_table);
- if (WSREP(this) && wsrep_thd_is_BF(this, FALSE))
- {
- WSREP_DEBUG("remove_table_from_cache: %llu",
- (unsigned long long) this->real_id);
- wsrep_abort_thd((void *)this, (void *)in_use, FALSE);
- }
}
}
}
@@ -2071,11 +2122,19 @@ void THD::reset_killed()
DBUG_ENTER("reset_killed");
if (killed != NOT_KILLED)
{
+ mysql_mutex_assert_not_owner(&LOCK_thd_kill);
mysql_mutex_lock(&LOCK_thd_kill);
killed= NOT_KILLED;
killed_err= 0;
mysql_mutex_unlock(&LOCK_thd_kill);
}
+#ifdef WITH_WSREP
+ mysql_mutex_assert_not_owner(&LOCK_thd_data);
+ mysql_mutex_lock(&LOCK_thd_data);
+ wsrep_aborter= 0;
+ mysql_mutex_unlock(&LOCK_thd_data);
+#endif /* WITH_WSREP */
+
DBUG_VOID_RETURN;
}
@@ -2232,12 +2291,6 @@ void THD::cleanup_after_query()
/* reset table map for multi-table update */
table_map_for_update= 0;
m_binlog_invoker= INVOKER_NONE;
-#ifdef WITH_WSREP
- if (TOTAL_ORDER == wsrep_exec_mode)
- {
- wsrep_exec_mode = LOCAL_STATE;
- }
-#endif /* WITH_WSREP */
#ifndef EMBEDDED_LIBRARY
if (rgi_slave)
@@ -2245,7 +2298,6 @@ void THD::cleanup_after_query()
#endif
#ifdef WITH_WSREP
- wsrep_sync_wait_gtid= WSREP_GTID_UNDEFINED;
if (!in_active_multi_stmt_transaction())
wsrep_affected_rows= 0;
#endif /* WITH_WSREP */
@@ -2505,6 +2557,16 @@ void THD::update_charset()
&not_used);
}
+void THD::give_protection_error()
+{
+ if (current_backup_stage != BACKUP_FINISHED)
+ my_error(ER_BACKUP_LOCK_IS_ACTIVE, MYF(0));
+ else
+ {
+ DBUG_ASSERT(global_read_lock.is_acquired() || mdl_backup_lock);
+ my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0));
+ }
+}
/* routings to adding tables to list of changed in transaction tables */
@@ -2581,7 +2643,7 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, size_t key_length)
key_length + 1);
if (!new_table)
{
- my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_FATALERROR),
+ my_error(EE_OUTOFMEMORY, MYF(ME_FATAL),
ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1);
set_killed(KILL_CONNECTION);
return 0;
@@ -2685,13 +2747,13 @@ void THD::make_explain_field_list(List<Item> &field_list, uint8 explain_flags,
NAME_CHAR_LEN*MAX_REF_PARTS, cs),
mem_root);
item->maybe_null=1;
- field_list.push_back(item= new (mem_root)
- Item_return_int(this, "rows", 10, MYSQL_TYPE_LONGLONG),
+ field_list.push_back(item=new (mem_root)
+ Item_empty_string(this, "rows", NAME_CHAR_LEN, cs),
mem_root);
if (is_analyze)
{
field_list.push_back(item= new (mem_root)
- Item_float(this, "r_rows", 0.1234, 2, 4),
+ Item_empty_string(this, "r_rows", NAME_CHAR_LEN, cs),
mem_root);
item->maybe_null=1;
}
@@ -2950,7 +3012,8 @@ int select_send::send_data(List<Item> &items)
thd->inc_sent_row_count(1);
- if (thd->vio_ok())
+ /* Don't return error if disconnected, only if write fails */
+ if (likely(thd->vio_ok()))
DBUG_RETURN(protocol->write());
DBUG_RETURN(0);
@@ -3233,9 +3296,9 @@ int select_export::send_data(List<Item> &items)
((uint64) res->length() / res->charset()->mbminlen + 1) *
write_cs->mbmaxlen + 1;
set_if_smaller(estimated_bytes, UINT_MAX32);
- if (cvt_str.realloc((uint32) estimated_bytes))
+ if (cvt_str.alloc((uint32) estimated_bytes))
{
- my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), (uint32) estimated_bytes);
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), (uint32) estimated_bytes);
goto err;
}
@@ -3502,7 +3565,7 @@ int select_singlerow_subselect::send_data(List<Item> &items)
if (it->assigned())
{
my_message(ER_SUBQUERY_NO_1_ROW, ER_THD(thd, ER_SUBQUERY_NO_1_ROW),
- MYF(current_thd->lex->ignore ? ME_JUST_WARNING : 0));
+ MYF(current_thd->lex->ignore ? ME_WARNING : 0));
DBUG_RETURN(1);
}
if (unit->offset_limit_cnt)
@@ -3609,18 +3672,15 @@ bool select_max_min_finder_subselect::cmp_int()
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);
+ VDec cvalue(cache), mvalue(maxmin);
/* 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)
+ if (cvalue.is_null())
+ return (is_all && !mvalue.is_null()) || (!is_all && mvalue.is_null());
+ if (mvalue.is_null())
return !is_all;
- if (fmax)
- return (my_decimal_cmp(cvalue, mvalue) > 0) ;
- return (my_decimal_cmp(cvalue,mvalue) < 0);
+ return fmax ? cvalue.cmp(mvalue) > 0 : cvalue.cmp(mvalue) < 0;
}
bool select_max_min_finder_subselect::cmp_str()
@@ -4253,6 +4313,7 @@ void Security_context::init()
host_or_ip= "connecting host";
priv_user[0]= priv_host[0]= proxy_user[0]= priv_role[0]= '\0';
master_access= 0;
+ password_expired= false;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
db_access= NO_ACCESS;
#endif
@@ -4291,6 +4352,7 @@ void Security_context::skip_grants()
host_or_ip= (char *)"";
master_access= ~NO_ACCESS;
*priv_user= *priv_host= '\0';
+ password_expired= false;
}
@@ -4301,6 +4363,13 @@ bool Security_context::set_user(char *user_arg)
return user == 0;
}
+bool Security_context::check_access(ulong want_access, bool match_any)
+{
+ DBUG_ENTER("Security_context::check_access");
+ DBUG_RETURN((match_any ? (master_access & want_access)
+ : ((master_access & want_access) == want_access)));
+}
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/**
Initialize this security context from the passed in credentials
@@ -4402,6 +4471,13 @@ bool Security_context::user_matches(Security_context *them)
!strcmp(user, them->user));
}
+bool Security_context::is_priv_user(const char *user, const char *host)
+{
+ return ((user != NULL) && (host != NULL) &&
+ !strcmp(user, priv_user) &&
+ !my_strcasecmp(system_charset_info, host,priv_host));
+}
+
/****************************************************************************
Handling of open and locked tables states.
@@ -4728,14 +4804,14 @@ MYSQL_THD create_thd()
thd->set_command(COM_DAEMON);
thd->system_thread= SYSTEM_THREAD_GENERIC;
thd->security_ctx->host_or_ip="";
- add_to_active_threads(thd);
+ server_threads.insert(thd);
return thd;
}
void destroy_thd(MYSQL_THD thd)
{
thd->add_status_to_global();
- unlink_not_visible_thd(thd);
+ server_threads.erase(thd);
delete thd;
}
@@ -4983,13 +5059,13 @@ thd_need_ordering_with(const MYSQL_THD thd, const MYSQL_THD other_thd)
#ifdef WITH_WSREP
/* wsrep applier, replayer and TOI processing threads are ordered
by replication provider, relaxed GAP locking protocol can be used
- between high priority wsrep threads. Note that this function
- is called while holding lock_sys mutex, therefore we can't
- use THD::LOCK_thd_data mutex below to follow mutex ordering rules.
+ between high priority wsrep threads.
+ Note that wsrep_thd_is_BF() doesn't take LOCK_thd_data for either thd,
+ the caller should guarantee that the BF state won't change.
+ (e.g. InnoDB does it by keeping lock_sys.mutex locked)
*/
- if (WSREP_ON &&
- wsrep_thd_is_BF(const_cast<THD *>(thd), false) &&
- wsrep_thd_is_BF(const_cast<THD *>(other_thd), false))
+ if (WSREP_ON && wsrep_thd_is_BF(thd, false) &&
+ wsrep_thd_is_BF(other_thd, false))
return 0;
#endif /* WITH_WSREP */
rgi= thd->rgi_slave;
@@ -5024,8 +5100,9 @@ extern "C" int thd_binlog_format(const MYSQL_THD thd)
if (WSREP(thd))
{
/* for wsrep binlog format is meaningful also when binlogging is off */
- return (int) thd->wsrep_binlog_format();
+ return (int) WSREP_BINLOG_FORMAT(thd->variables.binlog_format);
}
+
if (mysql_bin_log.is_open() && (thd->variables.option_bits & OPTION_BIN_LOG))
return (int) thd->variables.binlog_format;
return BINLOG_FORMAT_UNSPEC;
@@ -5508,6 +5585,10 @@ void THD::set_query_and_id(char *query_arg, uint32 query_length_arg,
set_query_inner(query_arg, query_length_arg, cs);
mysql_mutex_unlock(&LOCK_thd_data);
query_id= new_query_id;
+#ifdef WITH_WSREP
+ set_wsrep_next_trx_id(query_id);
+ WSREP_DEBUG("assigned new next query and trx id: %" PRIu64, wsrep_next_trx_id());
+#endif /* WITH_WSREP */
}
/** Assign a new value to thd->mysys_var. */
@@ -5527,6 +5608,7 @@ void THD::leave_locked_tables_mode()
{
if (locked_tables_mode == LTM_LOCK_TABLES)
{
+ DBUG_ASSERT(current_backup_stage == BACKUP_FINISHED);
/*
When leaving LOCK TABLES mode we have to change the duration of most
of the metadata locks being held, except for HANDLER and GRL locks,
@@ -5559,7 +5641,7 @@ void THD::get_definer(LEX_USER *definer, bool role)
{
definer->user= invoker.user;
definer->host= invoker.host;
- definer->reset_auth();
+ definer->auth= NULL;
}
else
#endif
@@ -5583,263 +5665,6 @@ void THD::mark_transaction_to_rollback(bool all)
is_fatal_sub_stmt_error= true;
transaction_rollback_request= all;
}
-/***************************************************************************
- Handling of XA id cacheing
-***************************************************************************/
-class XID_cache_element
-{
- /*
- m_state is used to prevent elements from being deleted while XA RECOVER
- iterates xid cache and to prevent recovered elments from being acquired by
- multiple threads.
-
- bits 1..29 are reference counter
- bit 30 is RECOVERED flag
- bit 31 is ACQUIRED flag (thread owns this xid)
- bit 32 is unused
-
- Newly allocated and deleted elements have m_state set to 0.
-
- On lock() m_state is atomically incremented. It also creates load-ACQUIRE
- memory barrier to make sure m_state is actually updated before furhter
- memory accesses. Attempting to lock an element that has neither ACQUIRED
- nor RECOVERED flag set returns failure and further accesses to element
- memory are forbidden.
-
- On unlock() m_state is decremented. It also creates store-RELEASE memory
- barrier to make sure m_state is actually updated after preceding memory
- accesses.
-
- ACQUIRED flag is set when thread registers it's xid or when thread acquires
- recovered xid.
-
- RECOVERED flag is set for elements found during crash recovery.
-
- ACQUIRED and RECOVERED flags are cleared before element is deleted from
- hash in a spin loop, after last reference is released.
- */
- int32 m_state;
-public:
- static const int32 ACQUIRED= 1 << 30;
- static const int32 RECOVERED= 1 << 29;
- XID_STATE *m_xid_state;
- bool is_set(int32 flag)
- { return my_atomic_load32_explicit(&m_state, MY_MEMORY_ORDER_RELAXED) & flag; }
- void set(int32 flag)
- {
- DBUG_ASSERT(!is_set(ACQUIRED | RECOVERED));
- my_atomic_add32_explicit(&m_state, flag, MY_MEMORY_ORDER_RELAXED);
- }
- bool lock()
- {
- int32 old= my_atomic_add32_explicit(&m_state, 1, MY_MEMORY_ORDER_ACQUIRE);
- if (old & (ACQUIRED | RECOVERED))
- return true;
- unlock();
- return false;
- }
- void unlock()
- { my_atomic_add32_explicit(&m_state, -1, MY_MEMORY_ORDER_RELEASE); }
- void mark_uninitialized()
- {
- int32 old= ACQUIRED;
- while (!my_atomic_cas32_weak_explicit(&m_state, &old, 0,
- MY_MEMORY_ORDER_RELAXED,
- MY_MEMORY_ORDER_RELAXED))
- {
- old&= ACQUIRED | RECOVERED;
- (void) LF_BACKOFF();
- }
- }
- bool acquire_recovered()
- {
- int32 old= RECOVERED;
- while (!my_atomic_cas32_weak_explicit(&m_state, &old, ACQUIRED | RECOVERED,
- MY_MEMORY_ORDER_RELAXED,
- MY_MEMORY_ORDER_RELAXED))
- {
- if (!(old & RECOVERED) || (old & ACQUIRED))
- return false;
- old= RECOVERED;
- (void) LF_BACKOFF();
- }
- return true;
- }
- static void lf_hash_initializer(LF_HASH *hash __attribute__((unused)),
- XID_cache_element *element,
- XID_STATE *xid_state)
- {
- DBUG_ASSERT(!element->is_set(ACQUIRED | RECOVERED));
- element->m_xid_state= xid_state;
- xid_state->xid_cache_element= element;
- }
- static void lf_alloc_constructor(uchar *ptr)
- {
- XID_cache_element *element= (XID_cache_element*) (ptr + LF_HASH_OVERHEAD);
- element->m_state= 0;
- }
- static void lf_alloc_destructor(uchar *ptr)
- {
- XID_cache_element *element= (XID_cache_element*) (ptr + LF_HASH_OVERHEAD);
- DBUG_ASSERT(!element->is_set(ACQUIRED));
- if (element->is_set(RECOVERED))
- my_free(element->m_xid_state);
- }
- static uchar *key(const XID_cache_element *element, size_t *length,
- my_bool not_used __attribute__((unused)))
- {
- *length= element->m_xid_state->xid.key_length();
- return element->m_xid_state->xid.key();
- }
-};
-
-
-static LF_HASH xid_cache;
-static bool xid_cache_inited;
-
-
-bool THD::fix_xid_hash_pins()
-{
- if (!xid_hash_pins)
- xid_hash_pins= lf_hash_get_pins(&xid_cache);
- return !xid_hash_pins;
-}
-
-
-void xid_cache_init()
-{
- xid_cache_inited= true;
- lf_hash_init(&xid_cache, sizeof(XID_cache_element), LF_HASH_UNIQUE, 0, 0,
- (my_hash_get_key) XID_cache_element::key, &my_charset_bin);
- xid_cache.alloc.constructor= XID_cache_element::lf_alloc_constructor;
- xid_cache.alloc.destructor= XID_cache_element::lf_alloc_destructor;
- xid_cache.initializer=
- (lf_hash_initializer) XID_cache_element::lf_hash_initializer;
-}
-
-
-void xid_cache_free()
-{
- if (xid_cache_inited)
- {
- lf_hash_destroy(&xid_cache);
- xid_cache_inited= false;
- }
-}
-
-
-/**
- Find recovered XA transaction by XID.
-*/
-
-XID_STATE *xid_cache_search(THD *thd, XID *xid)
-{
- XID_STATE *xs= 0;
- DBUG_ASSERT(thd->xid_hash_pins);
- XID_cache_element *element=
- (XID_cache_element*) lf_hash_search(&xid_cache, thd->xid_hash_pins,
- xid->key(), xid->key_length());
- if (element)
- {
- if (element->acquire_recovered())
- xs= element->m_xid_state;
- lf_hash_search_unpin(thd->xid_hash_pins);
- DEBUG_SYNC(thd, "xa_after_search");
- }
- return xs;
-}
-
-
-bool xid_cache_insert(XID *xid, enum xa_states xa_state)
-{
- XID_STATE *xs;
- LF_PINS *pins;
- int res= 1;
-
- if (!(pins= lf_hash_get_pins(&xid_cache)))
- return true;
-
- if ((xs= (XID_STATE*) my_malloc(sizeof(*xs), MYF(MY_WME))))
- {
- xs->xa_state=xa_state;
- xs->xid.set(xid);
- xs->rm_error=0;
-
- if ((res= lf_hash_insert(&xid_cache, pins, xs)))
- my_free(xs);
- else
- xs->xid_cache_element->set(XID_cache_element::RECOVERED);
- if (res == 1)
- res= 0;
- }
- lf_hash_put_pins(pins);
- return res;
-}
-
-
-bool xid_cache_insert(THD *thd, XID_STATE *xid_state)
-{
- if (thd->fix_xid_hash_pins())
- return true;
-
- int res= lf_hash_insert(&xid_cache, thd->xid_hash_pins, xid_state);
- switch (res)
- {
- case 0:
- xid_state->xid_cache_element->set(XID_cache_element::ACQUIRED);
- break;
- case 1:
- my_error(ER_XAER_DUPID, MYF(0));
- /* fall through */
- default:
- xid_state->xid_cache_element= 0;
- }
- return res;
-}
-
-
-void xid_cache_delete(THD *thd, XID_STATE *xid_state)
-{
- if (xid_state->xid_cache_element)
- {
- bool recovered= xid_state->xid_cache_element->is_set(XID_cache_element::RECOVERED);
- DBUG_ASSERT(thd->xid_hash_pins);
- xid_state->xid_cache_element->mark_uninitialized();
- lf_hash_delete(&xid_cache, thd->xid_hash_pins,
- xid_state->xid.key(), xid_state->xid.key_length());
- xid_state->xid_cache_element= 0;
- if (recovered)
- my_free(xid_state);
- }
-}
-
-
-struct xid_cache_iterate_arg
-{
- my_hash_walk_action action;
- void *argument;
-};
-
-static my_bool xid_cache_iterate_callback(XID_cache_element *element,
- xid_cache_iterate_arg *arg)
-{
- my_bool res= FALSE;
- if (element->lock())
- {
- res= arg->action(element->m_xid_state, arg->argument);
- element->unlock();
- }
- return res;
-}
-
-int xid_cache_iterate(THD *thd, my_hash_walk_action action, void *arg)
-{
- xid_cache_iterate_arg argument= { action, arg };
- return thd->fix_xid_hash_pins() ? -1 :
- lf_hash_iterate(&xid_cache, thd->xid_hash_pins,
- (my_hash_walk_action) xid_cache_iterate_callback,
- &argument);
-}
/**
@@ -5953,9 +5778,30 @@ int THD::decide_logging_format(TABLE_LIST *tables)
binlogging is off, or if the statement is filtered out from the
binlog by filtering rules.
*/
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT_NNULL(this) &&
+ wsrep_thd_is_local(this) &&
+ wsrep_is_active(this) &&
+ variables.wsrep_trx_fragment_size > 0)
+ {
+ if (!is_current_stmt_binlog_format_row())
+ {
+ my_message(ER_NOT_SUPPORTED_YET,
+ "Streaming replication not supported with "
+ "binlog_format=STATEMENT", MYF(0));
+ DBUG_RETURN(-1);
+ }
+ }
+
+ if ((WSREP_EMULATE_BINLOG_NNULL(this) ||
+ (mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG))) &&
+ !(wsrep_binlog_format() == BINLOG_FORMAT_STMT &&
+ !binlog_filter->db_ok(db.str)))
+#else
if (mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG) &&
!(wsrep_binlog_format() == BINLOG_FORMAT_STMT &&
!binlog_filter->db_ok(db.str)))
+#endif /* WITH_WSREP */
{
if (is_bulk_op())
@@ -6300,8 +6146,9 @@ int THD::decide_logging_format(TABLE_LIST *tables)
/* As all updated tables are temporary, nothing will be logged */
set_current_stmt_binlog_format_row();
}
- else if (IF_WSREP((!WSREP(this) ||
- wsrep_exec_mode == LOCAL_STATE),1))
+ else if (IF_WSREP((!WSREP_NNULL(this) ||
+ wsrep_cs().mode() ==
+ wsrep::client_state::m_local),1))
{
my_error((error= ER_BINLOG_STMT_MODE_AND_ROW_ENGINE), MYF(0), "");
}
@@ -6692,8 +6539,9 @@ int THD::binlog_write_row(TABLE* table, bool is_trans,
uchar const *record)
{
- DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
- ((WSREP(this) && wsrep_emulate_bin_log) || mysql_bin_log.is_open()));
+ DBUG_ASSERT(is_current_stmt_binlog_format_row());
+ DBUG_ASSERT((WSREP_NNULL(this) && wsrep_emulate_bin_log) ||
+ mysql_bin_log.is_open());
/*
Pack records into format for transfer. We are allocating more
memory than needed, but that doesn't matter.
@@ -6733,8 +6581,9 @@ int THD::binlog_update_row(TABLE* table, bool is_trans,
const uchar *before_record,
const uchar *after_record)
{
- DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
- ((WSREP(this) && wsrep_emulate_bin_log) || mysql_bin_log.is_open()));
+ DBUG_ASSERT(is_current_stmt_binlog_format_row());
+ DBUG_ASSERT((WSREP_NNULL(this) && wsrep_emulate_bin_log) ||
+ mysql_bin_log.is_open());
/**
Save a reference to the original read bitmaps
@@ -6812,8 +6661,9 @@ int THD::binlog_update_row(TABLE* table, bool is_trans,
int THD::binlog_delete_row(TABLE* table, bool is_trans,
uchar const *record)
{
- DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
- ((WSREP(this) && wsrep_emulate_bin_log) || mysql_bin_log.is_open()));
+ DBUG_ASSERT(is_current_stmt_binlog_format_row());
+ DBUG_ASSERT((WSREP_NNULL(this) && wsrep_emulate_bin_log) ||
+ mysql_bin_log.is_open());
/**
Save a reference to the original read bitmaps
We will need this to restore the bitmaps at the end as
@@ -6944,7 +6794,7 @@ int THD::binlog_remove_pending_rows_event(bool clear_maps,
{
DBUG_ENTER("THD::binlog_remove_pending_rows_event");
- if(!WSREP_EMULATE_BINLOG(this) && !mysql_bin_log.is_open())
+ if(!WSREP_EMULATE_BINLOG_NNULL(this) && !mysql_bin_log.is_open())
DBUG_RETURN(0);
/* Ensure that all events in a GTID group are in the same cache */
@@ -6967,7 +6817,7 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional)
mode: it might be the case that we left row-based mode before
flushing anything (e.g., if we have explicitly locked tables).
*/
- if(!WSREP_EMULATE_BINLOG(this) && !mysql_bin_log.is_open())
+ if (!WSREP_EMULATE_BINLOG_NNULL(this) && !mysql_bin_log.is_open())
DBUG_RETURN(0);
/* Ensure that all events in a GTID group are in the same cache */
@@ -7241,7 +7091,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
show_query_type(qtype), (int) query_len, query_arg));
DBUG_ASSERT(query_arg);
- DBUG_ASSERT(WSREP_EMULATE_BINLOG(this) || mysql_bin_log.is_open());
+ DBUG_ASSERT(WSREP_EMULATE_BINLOG_NNULL(this) || mysql_bin_log.is_open());
/* If this is withing a BEGIN ... COMMIT group, don't log it */
if (variables.option_bits & OPTION_GTID_BEGIN)
@@ -7409,7 +7259,7 @@ wait_for_commit::reinit()
{
subsequent_commits_list= NULL;
next_subsequent_commit= NULL;
- waitee= NULL;
+ waitee.store(NULL, std::memory_order_relaxed);
opaque_pointer= NULL;
wakeup_error= 0;
wakeup_subsequent_commits_running= false;
@@ -7470,7 +7320,6 @@ wait_for_commit::~wait_for_commit()
mysql_cond_destroy(&COND_wait_commit);
}
-
void
wait_for_commit::wakeup(int wakeup_error)
{
@@ -7487,8 +7336,9 @@ wait_for_commit::wakeup(int wakeup_error)
*/
mysql_mutex_lock(&LOCK_wait_commit);
- waitee= NULL;
this->wakeup_error= wakeup_error;
+ /* Memory barrier to make wakeup_error visible to the waiter thread. */
+ waitee.store(NULL, std::memory_order_release);
/*
Note that it is critical that the mysql_cond_signal() here is done while
still holding the mutex. As soon as we release the mutex, the waiter might
@@ -7519,9 +7369,10 @@ wait_for_commit::wakeup(int wakeup_error)
void
wait_for_commit::register_wait_for_prior_commit(wait_for_commit *waitee)
{
- DBUG_ASSERT(!this->waitee /* No prior registration allowed */);
+ DBUG_ASSERT(!this->waitee.load(std::memory_order_relaxed)
+ /* No prior registration allowed */);
wakeup_error= 0;
- this->waitee= waitee;
+ this->waitee.store(waitee, std::memory_order_relaxed);
mysql_mutex_lock(&waitee->LOCK_wait_commit);
/*
@@ -7530,7 +7381,7 @@ wait_for_commit::register_wait_for_prior_commit(wait_for_commit *waitee)
see comments on wakeup_subsequent_commits2() for details.
*/
if (waitee->wakeup_subsequent_commits_running)
- this->waitee= NULL;
+ this->waitee.store(NULL, std::memory_order_relaxed);
else
{
/*
@@ -7544,23 +7395,41 @@ wait_for_commit::register_wait_for_prior_commit(wait_for_commit *waitee)
}
-/*
- Wait for commit of another transaction to complete, as already registered
+/**
+ Waits for commit of another transaction to complete, as already registered
with register_wait_for_prior_commit(). If the commit already completed,
returns immediately.
+
+ If thd->backup_commit_lock is set, release it while waiting for other threads
*/
+
int
wait_for_commit::wait_for_prior_commit2(THD *thd)
{
PSI_stage_info old_stage;
wait_for_commit *loc_waitee;
+ bool backup_lock_released= 0;
+
+ /*
+ Release MDL_BACKUP_COMMIT LOCK while waiting for other threads to commit
+ This is needed to avoid deadlock between the other threads (which not
+ yet have the MDL_BACKUP_COMMIT_LOCK) and any threads using
+ BACKUP LOCK BLOCK_COMMIT.
+ */
+ if (thd->backup_commit_lock && thd->backup_commit_lock->ticket)
+ {
+ backup_lock_released= 1;
+ thd->mdl_context.release_lock(thd->backup_commit_lock->ticket);
+ thd->backup_commit_lock->ticket= 0;
+ }
mysql_mutex_lock(&LOCK_wait_commit);
DEBUG_SYNC(thd, "wait_for_prior_commit_waiting");
thd->ENTER_COND(&COND_wait_commit, &LOCK_wait_commit,
&stage_waiting_for_prior_transaction_to_commit,
&old_stage);
- while ((loc_waitee= this->waitee) && likely(!thd->check_killed(1)))
+ while ((loc_waitee= this->waitee.load(std::memory_order_relaxed)) &&
+ likely(!thd->check_killed(1)))
mysql_cond_wait(&COND_wait_commit, &LOCK_wait_commit);
if (!loc_waitee)
{
@@ -7583,14 +7452,14 @@ wait_for_commit::wait_for_prior_commit2(THD *thd)
do
{
mysql_cond_wait(&COND_wait_commit, &LOCK_wait_commit);
- } while (this->waitee);
+ } while (this->waitee.load(std::memory_order_relaxed));
if (wakeup_error)
my_error(ER_PRIOR_COMMIT_FAILED, MYF(0));
goto end;
}
remove_from_list(&loc_waitee->subsequent_commits_list);
mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit);
- this->waitee= NULL;
+ this->waitee.store(NULL, std::memory_order_relaxed);
wakeup_error= thd->killed_errno();
if (!wakeup_error)
@@ -7602,10 +7471,16 @@ wait_for_commit::wait_for_prior_commit2(THD *thd)
use within enter_cond/exit_cond.
*/
DEBUG_SYNC(thd, "wait_for_prior_commit_killed");
+ if (backup_lock_released)
+ thd->mdl_context.acquire_lock(thd->backup_commit_lock,
+ thd->variables.lock_wait_timeout);
return wakeup_error;
end:
thd->EXIT_COND(&old_stage);
+ if (backup_lock_released)
+ thd->mdl_context.acquire_lock(thd->backup_commit_lock,
+ thd->variables.lock_wait_timeout);
return wakeup_error;
}
@@ -7692,7 +7567,7 @@ wait_for_commit::unregister_wait_for_prior_commit2()
wait_for_commit *loc_waitee;
mysql_mutex_lock(&LOCK_wait_commit);
- if ((loc_waitee= this->waitee))
+ if ((loc_waitee= this->waitee.load(std::memory_order_relaxed)))
{
mysql_mutex_lock(&loc_waitee->LOCK_wait_commit);
if (loc_waitee->wakeup_subsequent_commits_running)
@@ -7705,7 +7580,7 @@ wait_for_commit::unregister_wait_for_prior_commit2()
See comments on wakeup_subsequent_commits2() for more details.
*/
mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit);
- while (this->waitee)
+ while (this->waitee.load(std::memory_order_relaxed))
mysql_cond_wait(&COND_wait_commit, &LOCK_wait_commit);
}
else
@@ -7713,7 +7588,7 @@ wait_for_commit::unregister_wait_for_prior_commit2()
/* Remove ourselves from the list in the waitee. */
remove_from_list(&loc_waitee->subsequent_commits_list);
mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit);
- this->waitee= NULL;
+ this->waitee.store(NULL, std::memory_order_relaxed);
}
}
wakeup_error= 0;
@@ -7836,7 +7711,7 @@ Query_arena_stmt::~Query_arena_stmt()
bool THD::timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts,
- ulong sec_part, ulonglong fuzzydate)
+ ulong sec_part, date_mode_t fuzzydate)
{
time_zone_used= 1;
if (ts == 0 && sec_part == 0)
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 86925a43d76..8edc6225d73 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -20,6 +20,7 @@
/* Classes in mysql */
+#include <atomic>
#include "dur_prop.h"
#include <waiting_threads.h>
#include "sql_const.h"
@@ -28,6 +29,7 @@
#include "rpl_tblmap.h"
#include "mdl.h"
#include "field.h" // Create_field
+#include "opt_trace_context.h"
#include "probes_mysql.h"
#include "sql_locale.h" /* my_locale_st */
#include "sql_profile.h" /* PROFILING */
@@ -39,15 +41,15 @@
#include "thr_malloc.h"
#include "log_slow.h" /* LOG_SLOW_DISABLE_... */
#include <my_tree.h>
-
#include "sql_digest_stream.h" // sql_digest_state
-
#include <mysql/psi/mysql_stage.h>
#include <mysql/psi/mysql_statement.h>
#include <mysql/psi/mysql_idle.h>
#include <mysql/psi/mysql_table.h>
#include <mysql_com_server.h>
#include "session_tracker.h"
+#include "backup.h"
+#include "xa.h"
extern "C"
void set_thd_stage_info(void *thd,
@@ -62,8 +64,19 @@ void set_thd_stage_info(void *thd,
#include "my_apc.h"
#include "rpl_gtid.h"
+
#include "wsrep_mysqld.h"
+#ifdef WITH_WSREP
+#include <inttypes.h>
+/* wsrep-lib */
+#include "wsrep_client_service.h"
+#include "wsrep_client_state.h"
+#include "wsrep_mutex.h"
+#include "wsrep_condition_variable.h"
+class Wsrep_applier_service;
+
+#endif /* WITH_WSREP */
class Reprepare_observer;
class Relay_log_info;
struct rpl_group_info;
@@ -80,6 +93,9 @@ class user_var_entry;
struct Trans_binlog_info;
class rpl_io_thread_info;
class rpl_sql_thread_info;
+#ifdef HAVE_REPLICATION
+struct Slave_info;
+#endif
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE };
@@ -157,8 +173,15 @@ enum enum_binlog_row_image {
#define MODE_HIGH_NOT_PRECEDENCE (1ULL << 29)
#define MODE_NO_ENGINE_SUBSTITUTION (1ULL << 30)
#define MODE_PAD_CHAR_TO_FULL_LENGTH (1ULL << 31)
+/* SQL mode bits defined above are common for MariaDB and MySQL */
+#define MODE_MASK_MYSQL_COMPATIBLE 0xFFFFFFFFULL
+/* The following modes are specific to MariaDB */
#define MODE_EMPTY_STRING_IS_NULL (1ULL << 32)
#define MODE_SIMULTANEOUS_ASSIGNMENT (1ULL << 33)
+#define MODE_TIME_ROUND_FRACTIONAL (1ULL << 34)
+/* The following modes are specific to MySQL */
+#define MODE_MYSQL80_TIME_TRUNCATE_FRACTIONAL (1ULL << 32)
+
/* Bits for different old style modes */
#define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE (1 << 0)
@@ -169,8 +192,6 @@ extern char internal_table_name[2];
extern char empty_c_string[1];
extern MYSQL_PLUGIN_IMPORT const char **errmesg;
-extern bool volatile shutdown_in_progress;
-
extern "C" LEX_STRING * thd_query_string (MYSQL_THD thd);
extern "C" size_t thd_query_safe(MYSQL_THD thd, char *buf, size_t buflen);
@@ -275,7 +296,7 @@ public:
class Alter_drop :public Sql_alloc {
public:
- enum drop_type {KEY, COLUMN, FOREIGN_KEY, CHECK_CONSTRAINT };
+ enum drop_type { KEY, COLUMN, FOREIGN_KEY, CHECK_CONSTRAINT, PERIOD };
const char *name;
enum drop_type type;
bool drop_if_exists;
@@ -294,6 +315,7 @@ public:
{
return type == COLUMN ? "COLUMN" :
type == CHECK_CONSTRAINT ? "CONSTRAINT" :
+ type == PERIOD ? "PERIOD" :
type == KEY ? "INDEX" : "FOREIGN KEY";
}
};
@@ -557,6 +579,8 @@ typedef struct system_variables
ulonglong long_query_time;
ulonglong max_statement_time;
ulonglong optimizer_switch;
+ ulonglong optimizer_trace;
+ ulong optimizer_trace_max_mem_size;
sql_mode_t sql_mode; ///< which non-standard SQL behaviour should be enabled
sql_mode_t old_behavior; ///< which old SQL behaviour should be enabled
ulonglong option_bits; ///< OPTION_xxx constants, e.g. OPTION_PROFILING
@@ -591,6 +615,7 @@ typedef struct system_variables
are based on the cluster size):
*/
ulong saved_auto_increment_increment, saved_auto_increment_offset;
+ ulong saved_lock_wait_timeout;
#endif /* WITH_WSREP */
uint eq_range_index_dive_limit;
ulong column_compression_zlib_strategy;
@@ -616,6 +641,7 @@ typedef struct system_variables
ulong optimizer_selectivity_sampling_limit;
ulong optimizer_use_condition_selectivity;
ulong use_stat_tables;
+ double sample_percentage;
ulong histogram_size;
ulong histogram_type;
ulong preload_buff_size;
@@ -719,10 +745,12 @@ typedef struct system_variables
my_bool wsrep_on;
my_bool wsrep_causal_reads;
+ uint wsrep_sync_wait;
+ ulong wsrep_retry_autocommit;
+ ulonglong wsrep_trx_fragment_size;
+ ulong wsrep_trx_fragment_unit;
+ ulong wsrep_OSU_method;
my_bool wsrep_dirty_reads;
- uint wsrep_sync_wait;
- ulong wsrep_retry_autocommit;
- ulong wsrep_OSU_method;
double long_query_time_double, max_statement_time_double;
my_bool pseudo_slave_mode;
@@ -731,6 +759,7 @@ typedef struct system_variables
ulong session_track_transaction_info;
my_bool session_track_schema;
my_bool session_track_state_change;
+ my_bool tcp_nodelay;
ulong threadpool_priority;
@@ -740,6 +769,7 @@ typedef struct system_variables
uint column_compression_threshold;
uint column_compression_zlib_level;
uint in_subquery_conversion_threshold;
+ ulonglong max_rowid_filter_size;
vers_asof_timestamp_t vers_asof_timestamp;
ulong vers_alter_history;
@@ -838,6 +868,8 @@ typedef struct system_status_var
ulong feature_locale; /* +1 when LOCALE is set */
ulong feature_subquery; /* +1 when subqueries are used */
ulong feature_system_versioning; /* +1 opening a table WITH SYSTEM VERSIONING */
+ ulong feature_application_time_periods;
+ /* +1 opening a table with application-time period */
ulong feature_timezone; /* +1 when XPATH is used */
ulong feature_trigger; /* +1 opening a table with triggers */
ulong feature_xml; /* +1 when XPATH is used */
@@ -1217,7 +1249,7 @@ public:
int insert(THD *thd, Statement *statement);
- Statement *find_by_name(LEX_CSTRING *name)
+ Statement *find_by_name(const LEX_CSTRING *name)
{
Statement *stmt;
stmt= (Statement*)my_hash_search(&names_hash, (uchar*)name->str,
@@ -1263,50 +1295,6 @@ struct st_savepoint {
MDL_savepoint mdl_savepoint;
};
-enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY};
-extern const char *xa_state_names[];
-class XID_cache_element;
-
-typedef struct st_xid_state {
- /* For now, this is only used to catch duplicated external xids */
- XID xid; // transaction identifier
- enum xa_states xa_state; // used by external XA only
- /* Error reported by the Resource Manager (RM) to the Transaction Manager. */
- uint rm_error;
- XID_cache_element *xid_cache_element;
-
- /**
- Check that XA transaction has an uncommitted work. Report an error
- to the user in case when there is an uncommitted work for XA transaction.
-
- @return result of check
- @retval false XA transaction is NOT in state IDLE, PREPARED
- or ROLLBACK_ONLY.
- @retval true XA transaction is in state IDLE or PREPARED
- or ROLLBACK_ONLY.
- */
-
- bool check_has_uncommitted_xa() const
- {
- if (xa_state == XA_IDLE ||
- xa_state == XA_PREPARED ||
- xa_state == XA_ROLLBACK_ONLY)
- {
- my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
- return true;
- }
- return false;
- }
-} XID_STATE;
-
-void xid_cache_init(void);
-void xid_cache_free(void);
-XID_STATE *xid_cache_search(THD *thd, XID *xid);
-bool xid_cache_insert(XID *xid, enum xa_states xa_state);
-bool xid_cache_insert(THD *thd, XID_STATE *xid_state);
-void xid_cache_delete(THD *thd, XID_STATE *xid_state);
-int xid_cache_iterate(THD *thd, my_hash_walk_action action, void *argument);
-
/**
@class Security_context
@brief A set of THD members describing the current authenticated user.
@@ -1337,6 +1325,8 @@ public:
ulong master_access; /* Global privileges from mysql.user */
ulong db_access; /* Privileges for current db */
+ bool password_expired;
+
void init();
void destroy();
void skip_grants();
@@ -1359,6 +1349,15 @@ public:
restore_security_context(THD *thd, Security_context *backup);
#endif
bool user_matches(Security_context *);
+ /**
+ Check global access
+ @param want_access The required privileges
+ @param match_any if the security context must match all or any of the req.
+ * privileges.
+ @return True if the security context fulfills the access requirements.
+ */
+ bool check_access(ulong want_access, bool match_any = false);
+ bool is_priv_user(const char *user, const char *host);
};
@@ -1979,42 +1978,22 @@ public:
Global_read_lock()
: m_state(GRL_NONE),
- m_mdl_global_shared_lock(NULL),
- m_mdl_blocks_commits_lock(NULL)
+ m_mdl_global_read_lock(NULL)
{}
bool lock_global_read_lock(THD *thd);
void unlock_global_read_lock(THD *thd);
- /**
- Check if this connection can acquire protection against GRL and
- emit error if otherwise.
- */
- bool can_acquire_protection() const
- {
- if (m_state)
- {
- my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0));
- return TRUE;
- }
- return FALSE;
- }
bool make_global_read_lock_block_commit(THD *thd);
bool is_acquired() const { return m_state != GRL_NONE; }
void set_explicit_lock_duration(THD *thd);
private:
enum_grl_state m_state;
/**
- In order to acquire the global read lock, the connection must
- acquire shared metadata lock in GLOBAL namespace, to prohibit
- all DDL.
- */
- MDL_ticket *m_mdl_global_shared_lock;
- /**
- Also in order to acquire the global read lock, the connection
- must acquire a shared metadata lock in COMMIT namespace, to
- prohibit commits.
+ Global read lock is acquired in two steps:
+ 1. acquire MDL_BACKUP_FTWRL1 in BACKUP namespace to prohibit DDL and DML
+ 2. upgrade to MDL_BACKUP_FTWRL2 to prohibit commits
*/
- MDL_ticket *m_mdl_blocks_commits_lock;
+ MDL_ticket *m_mdl_global_read_lock;
};
@@ -2053,7 +2032,7 @@ struct wait_for_commit
/*
The LOCK_wait_commit protects the fields subsequent_commits_list and
wakeup_subsequent_commits_running (for a waitee), and the pointer
- waiterr and associated COND_wait_commit (for a waiter).
+ waitee and associated COND_wait_commit (for a waiter).
*/
mysql_mutex_t LOCK_wait_commit;
mysql_cond_t COND_wait_commit;
@@ -2067,8 +2046,14 @@ struct wait_for_commit
When this is cleared for wakeup, the COND_wait_commit condition is
signalled.
+
+ This pointer is protected by LOCK_wait_commit. But there is also a "fast
+ path" where the waiter compares this to NULL without holding the lock.
+ Such read must be done with acquire semantics (and all corresponding
+ writes done with release semantics). This ensures that a wakeup with error
+ is reliably detected as (waitee==NULL && wakeup_error != 0).
*/
- wait_for_commit *waitee;
+ std::atomic<wait_for_commit *> waitee;
/*
Generic pointer for use by the transaction coordinator to optimise the
waiting for improved group commit.
@@ -2103,7 +2088,7 @@ struct wait_for_commit
Quick inline check, to avoid function call and locking in the common case
where no wakeup is registered, or a registered wait was already signalled.
*/
- if (waitee)
+ if (waitee.load(std::memory_order_acquire))
return wait_for_prior_commit2(thd);
else
{
@@ -2131,7 +2116,7 @@ struct wait_for_commit
}
void unregister_wait_for_prior_commit()
{
- if (waitee)
+ if (waitee.load(std::memory_order_relaxed))
unregister_wait_for_prior_commit2();
else
wakeup_error= 0;
@@ -2153,7 +2138,7 @@ struct wait_for_commit
}
next_ptr_ptr= &cur->next_subsequent_commit;
}
- waitee= NULL;
+ waitee.store(NULL, std::memory_order_relaxed);
}
void wakeup(int wakeup_error);
@@ -2169,13 +2154,31 @@ struct wait_for_commit
extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
+
+/**
+ A wrapper around thread_count.
+
+ It must be specified as a first base class of THD, so that increment is
+ done before any other THD constructors and decrement - after any other THD
+ destructors.
+
+ Destructor unblocks close_conneciton() if there are no more THD's left.
+*/
+struct THD_count
+{
+ THD_count() { thread_count++; }
+ ~THD_count() { thread_count--; }
+};
+
+
/**
@class THD
For each client connection we create a separate thread with THD serving as
a thread/connection descriptor
*/
-class THD :public Statement,
+class THD: public THD_count, /* this must be first */
+ public Statement,
/*
This is to track items changed during execution of a prepared
statement/stored procedure. It's created by
@@ -2200,19 +2203,6 @@ private:
inline bool is_conventional() const
{ DBUG_ASSERT(0); return Statement::is_conventional(); }
- void dec_thread_count(void)
- {
- DBUG_ASSERT(thread_count > 0);
- thread_safe_decrement32(&thread_count);
- signal_thd_deleted();
- }
-
-
- void inc_thread_count(void)
- {
- thread_safe_increment32(&thread_count);
- }
-
public:
MDL_context mdl_context;
@@ -2226,6 +2216,10 @@ public:
rpl_io_thread_info *rpl_io_info;
rpl_sql_thread_info *rpl_sql_info;
} system_thread_info;
+ /* Used for BACKUP LOCK */
+ MDL_ticket *mdl_backup_ticket, *mdl_backup_lock;
+ /* Used to register that thread has a MDL_BACKUP_WAIT_COMMIT lock */
+ MDL_request *backup_commit_lock;
void reset_for_next_command(bool do_clear_errors= 1);
/*
@@ -2281,14 +2275,14 @@ public:
- thd->db (used in SHOW PROCESSLIST)
Is locked when THD is deleted.
*/
- mysql_mutex_t LOCK_thd_data;
+ mutable mysql_mutex_t LOCK_thd_data;
/*
Protects:
- kill information
- mysys_var (used by KILL statement and shutdown).
- Also ensures that THD is not deleted while mutex is hold
*/
- mysql_mutex_t LOCK_thd_kill;
+ mutable mysql_mutex_t LOCK_thd_kill;
/* all prepared statements and cursors of this connection */
Statement_map stmt_map;
@@ -2325,6 +2319,8 @@ public:
Security_context main_security_ctx;
Security_context *security_ctx;
+ Security_context *security_context() const { return security_ctx; }
+ void set_security_context(Security_context *sctx) { security_ctx = sctx; }
/*
Points to info-string that we show in SHOW PROCESSLIST
@@ -2621,6 +2617,7 @@ public:
THD_TRANS stmt; // Trans for current statement
bool on; // see ha_enable_transaction()
XID_STATE xid_state;
+ XID implicit_xid;
WT_THD wt; ///< for deadlock detection
Rows_log_event *m_pending_rows_event;
@@ -2642,28 +2639,25 @@ public:
MEM_ROOT mem_root; // Transaction-life memory allocation pool
void cleanup()
{
- DBUG_ENTER("thd::cleanup");
+ DBUG_ENTER("THD::st_transactions::cleanup");
changed_tables= 0;
savepoints= 0;
- /*
- If rm_error is raised, it means that this piece of a distributed
- transaction has failed and must be rolled back. But the user must
- rollback it explicitly, so don't start a new distributed XA until
- then.
- */
- if (!xid_state.rm_error)
- xid_state.xid.null();
+ implicit_xid.null();
free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_VOID_RETURN;
}
- my_bool is_active()
+ bool is_active()
{
return (all.ha_list != NULL);
}
+ bool is_empty()
+ {
+ return all.is_empty() && stmt.is_empty();
+ }
st_transactions()
{
bzero((char*)this, sizeof(*this));
- xid_state.xid.null();
+ implicit_xid.null();
init_sql_alloc(&mem_root, "THD::transactions",
ALLOC_ROOT_MIN_BLOCK_SIZE, 0,
MYF(MY_THREAD_SPECIFIC));
@@ -3008,12 +3002,14 @@ public:
ulonglong bytes_sent_old;
ulonglong affected_rows; /* Number of changed rows */
+ Opt_trace_context opt_trace;
pthread_t real_id; /* For debugging */
my_thread_id thread_id, thread_dbug_id;
uint32 os_thread_id;
uint tmp_table, global_disable_checkpoint;
uint server_status,open_options;
enum enum_thread_type system_thread;
+ enum backup_stages current_backup_stage;
/*
Current or next transaction isolation level.
When a connection is established, the value is taken from
@@ -3160,6 +3156,9 @@ public:
it returned an error on master, and this is OK on the slave.
*/
bool is_slave_error;
+ /* True if we have printed something to the error log for this statement */
+ bool error_printed_to_log;
+
/*
True when a transaction is queued up for binlog group commit.
Used so that if another transaction needs to wait for a row lock held by
@@ -3188,12 +3187,6 @@ public:
/** number of name_const() substitutions, see sp_head.cc:subst_spvars() */
uint query_name_consts;
- /*
- If we do a purge of binary logs, log index info of the threads
- that are currently reading it needs to be adjusted. To do that
- each thread that is using LOG_INFO needs to adjust the pointer to it
- */
- LOG_INFO* current_linfo;
NET* slave_net; // network connection from slave -> m.
/*
@@ -3241,7 +3234,6 @@ public:
mysql_bin_log.start_union_events() call.
*/
bool unioned_events_trans;
-
/*
'queries' (actually SP statements) that run under inside this binlog
union have thd->query_id >= first_query_id.
@@ -3249,7 +3241,6 @@ public:
query_id_t first_query_id;
} binlog_evt_union;
- mysql_cond_t COND_wsrep_thd;
/**
Internal parser state.
Note that since the parser is not re-entrant, we keep only one parser
@@ -3310,6 +3301,10 @@ public:
void reset_for_reuse();
bool store_globals();
void reset_globals();
+ bool trace_started()
+ {
+ return opt_trace.is_started();
+ }
#ifdef SIGNAL_WITH_VIO_CLOSE
inline void set_active_vio(Vio* vio)
{
@@ -3329,9 +3324,12 @@ public:
void awake(killed_state state_to_set)
{
mysql_mutex_lock(&LOCK_thd_kill);
+ mysql_mutex_lock(&LOCK_thd_data);
awake_no_mutex(state_to_set);
+ mysql_mutex_unlock(&LOCK_thd_data);
mysql_mutex_unlock(&LOCK_thd_kill);
}
+ void abort_current_cond_wait(bool force);
/** Disconnect the associated communication endpoint. */
void disconnect();
@@ -3439,11 +3437,16 @@ public:
}
const Type_handler *type_handler_for_datetime() const;
bool timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts,
- ulong sec_part, ulonglong fuzzydate);
+ ulong sec_part, date_mode_t fuzzydate);
inline my_time_t query_start() { return start_time; }
inline ulong query_start_sec_part()
{ query_start_sec_part_used=1; return start_time_sec_part; }
MYSQL_TIME query_start_TIME();
+ time_round_mode_t temporal_round_mode() const
+ {
+ return variables.sql_mode & MODE_TIME_ROUND_FRACTIONAL ?
+ TIME_FRAC_ROUND : TIME_FRAC_TRUNCATE;
+ }
private:
struct {
@@ -3636,6 +3639,22 @@ public:
{
return server_status & SERVER_STATUS_IN_TRANS;
}
+ void give_protection_error();
+ /*
+ Give an error if any of the following is true for this connection
+ - BACKUP STAGE is active
+ - FLUSH TABLE WITH READ LOCK is active
+ - BACKUP LOCK table_name is active
+ */
+ inline bool has_read_only_protection()
+ {
+ if (current_backup_stage == BACKUP_FINISHED &&
+ !global_read_lock.is_acquired() &&
+ !mdl_backup_lock)
+ return FALSE;
+ give_protection_error();
+ return TRUE;
+ }
inline bool fill_information_schema_tables()
{
return !stmt_arena->is_stmt_prepare();
@@ -4046,8 +4065,7 @@ public:
mysql_mutex_lock(&LOCK_thd_kill);
int err= killed_errno();
if (err)
- my_message(err, killed_err ? killed_err->msg : ER_THD(this, err),
- MYF(0));
+ my_message(err, killed_err ? killed_err->msg : ER_THD(this, err), MYF(0));
mysql_mutex_unlock(&LOCK_thd_kill);
}
/* return TRUE if we will abort query if we make a warning now */
@@ -4410,6 +4428,80 @@ private:
return raised;
}
+private:
+ void push_warning_truncated_priv(Sql_condition::enum_warning_level level,
+ uint sql_errno,
+ const char *type_str, const char *val)
+ {
+ DBUG_ASSERT(sql_errno == ER_TRUNCATED_WRONG_VALUE ||
+ sql_errno == ER_WRONG_VALUE);
+ char buff[MYSQL_ERRMSG_SIZE];
+ CHARSET_INFO *cs= &my_charset_latin1;
+ cs->cset->snprintf(cs, buff, sizeof(buff),
+ ER_THD(this, sql_errno), type_str, val);
+ /*
+ Note: the format string can vary between ER_TRUNCATED_WRONG_VALUE
+ and ER_WRONG_VALUE, but the code passed to push_warning() is
+ always ER_TRUNCATED_WRONG_VALUE. This is intentional.
+ */
+ push_warning(this, level, ER_TRUNCATED_WRONG_VALUE, buff);
+ }
+public:
+ void push_warning_truncated_wrong_value(Sql_condition::enum_warning_level level,
+ const char *type_str, const char *val)
+ {
+ return push_warning_truncated_priv(level, ER_TRUNCATED_WRONG_VALUE,
+ type_str, val);
+ }
+ void push_warning_wrong_value(Sql_condition::enum_warning_level level,
+ const char *type_str, const char *val)
+ {
+ return push_warning_truncated_priv(level, ER_WRONG_VALUE, type_str, val);
+ }
+ void push_warning_truncated_wrong_value(const char *type_str, const char *val)
+ {
+ return push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_WARN,
+ type_str, val);
+ }
+ void push_warning_truncated_value_for_field(Sql_condition::enum_warning_level
+ level, const char *type_str,
+ const char *val,
+ const char *db_name,
+ const char *table_name,
+ const char *name)
+ {
+ DBUG_ASSERT(name);
+ char buff[MYSQL_ERRMSG_SIZE];
+ CHARSET_INFO *cs= &my_charset_latin1;
+
+ if (!db_name)
+ db_name= "";
+ if (!table_name)
+ table_name= "";
+ cs->cset->snprintf(cs, buff, sizeof(buff),
+ ER_THD(this, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
+ type_str, val, db_name, table_name, name,
+ (ulong) get_stmt_da()->current_row_for_warning());
+ push_warning(this, level, ER_TRUNCATED_WRONG_VALUE, buff);
+
+ }
+ void push_warning_wrong_or_truncated_value(Sql_condition::enum_warning_level level,
+ bool totally_useless_value,
+ const char *type_str,
+ const char *val,
+ const char *db_name,
+ const char *table_name,
+ const char *field_name)
+ {
+ if (field_name)
+ push_warning_truncated_value_for_field(level, type_str, val,
+ db_name, table_name, field_name);
+ else if (totally_useless_value)
+ push_warning_wrong_value(level, type_str, val);
+ else
+ push_warning_truncated_wrong_value(level, type_str, val);
+ }
+
public:
/** Overloaded to guard query/query_length fields */
virtual void set_statement(Statement *stmt);
@@ -4451,6 +4543,13 @@ public:
void set_query_id(query_id_t new_query_id)
{
query_id= new_query_id;
+#ifdef WITH_WSREP
+ if (WSREP_NNULL(this))
+ {
+ set_wsrep_next_trx_id(query_id);
+ WSREP_DEBUG("assigned new next trx id: %" PRIu64, wsrep_next_trx_id());
+ }
+#endif /* WITH_WSREP */
}
void set_open_tables(TABLE *open_tables_arg)
{
@@ -4484,7 +4583,7 @@ public:
{
if (!(server_status &
(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY)))
- mdl_context.release_transactional_locks();
+ mdl_context.release_transactional_locks(this);
}
int decide_logging_format(TABLE_LIST *tables);
/*
@@ -4652,12 +4751,10 @@ public:
};
bool has_thd_temporary_tables();
- TABLE *create_and_open_tmp_table(handlerton *hton,
- LEX_CUSTRING *frm,
+ TABLE *create_and_open_tmp_table(LEX_CUSTRING *frm,
const char *path,
const char *db,
const char *table_name,
- bool open_in_engine,
bool open_internal_tables);
TABLE *find_temporary_table(const char *db, const char *table_name,
@@ -4694,13 +4791,12 @@ private:
bool has_temporary_tables();
uint create_tmp_table_def_key(char *key, const char *db,
const char *table_name);
- TMP_TABLE_SHARE *create_temporary_table(handlerton *hton, LEX_CUSTRING *frm,
+ TMP_TABLE_SHARE *create_temporary_table(LEX_CUSTRING *frm,
const char *path, const char *db,
const char *table_name);
TABLE *find_temporary_table(const char *key, uint key_length,
Temporary_table_state state);
- TABLE *open_temporary_table(TMP_TABLE_SHARE *share, const char *alias,
- bool open_in_engine);
+ TABLE *open_temporary_table(TMP_TABLE_SHARE *share, const char *alias);
bool find_and_use_tmp_table(const TABLE_LIST *tl, TABLE **out_table);
bool use_temporary_table(TABLE *table, TABLE **out_table);
void close_temporary_table(TABLE *table);
@@ -4723,62 +4819,135 @@ private:
}
public:
+#ifdef HAVE_REPLICATION
+ /*
+ If we do a purge of binary logs, log index info of the threads
+ that are currently reading it needs to be adjusted. To do that
+ each thread that is using LOG_INFO needs to adjust the pointer to it
+ */
+ LOG_INFO *current_linfo;
+ Slave_info *slave_info;
+
+ void set_current_linfo(LOG_INFO *linfo);
+ void reset_current_linfo() { set_current_linfo(0); }
+
+ int register_slave(uchar *packet, size_t packet_length);
+ void unregister_slave();
+ bool is_binlog_dump_thread();
+#endif
+
inline ulong wsrep_binlog_format() const
{
- return WSREP_FORMAT(variables.binlog_format);
+ return WSREP_BINLOG_FORMAT(variables.binlog_format);
}
#ifdef WITH_WSREP
- const bool wsrep_applier; /* dedicated slave applier thread */
+ bool wsrep_applier; /* dedicated slave applier thread */
bool wsrep_applier_closing; /* applier marked to close */
bool wsrep_client_thread; /* to identify client threads*/
- bool wsrep_PA_safe;
- bool wsrep_converted_lock_session;
- bool wsrep_apply_toi; /* applier processing in TOI */
- enum wsrep_exec_mode wsrep_exec_mode;
query_id_t wsrep_last_query_id;
- enum wsrep_query_state wsrep_query_state;
- enum wsrep_conflict_state wsrep_conflict_state;
- wsrep_trx_meta_t wsrep_trx_meta;
+ XID wsrep_xid;
+
+ /** This flag denotes that record locking should be skipped during INSERT
+ and gap locking during SELECT. Only used by the streaming replication thread
+ that only modifies the wsrep_schema.SR table. */
+ my_bool wsrep_skip_locking;
+
+ mysql_cond_t COND_wsrep_thd;
+
+ // changed from wsrep_seqno_t to wsrep_trx_meta_t in wsrep API rev 75
uint32 wsrep_rand;
- Relay_log_info *wsrep_rli;
rpl_group_info *wsrep_rgi;
- wsrep_ws_handle_t wsrep_ws_handle;
+ bool wsrep_converted_lock_session;
+ char wsrep_info[128]; /* string for dynamic proc info */
ulong wsrep_retry_counter; // of autocommit
- char *wsrep_retry_query;
+ bool wsrep_PA_safe;
+ char* wsrep_retry_query;
size_t wsrep_retry_query_len;
enum enum_server_command wsrep_retry_command;
- enum wsrep_consistency_check_mode
+ enum wsrep_consistency_check_mode
wsrep_consistency_check;
+ std::vector<wsrep::provider::status_variable> wsrep_status_vars;
int wsrep_mysql_replicated;
- const char *wsrep_TOI_pre_query; /* a query to apply before
- the actual TOI query */
+ const char* wsrep_TOI_pre_query; /* a query to apply before
+ the actual TOI query */
size_t wsrep_TOI_pre_query_len;
wsrep_po_handle_t wsrep_po_handle;
size_t wsrep_po_cnt;
#ifdef GTID_SUPPORT
+ my_bool wsrep_po_in_trans;
rpl_sid wsrep_po_sid;
-#endif /* GTID_SUPPORT */
+#endif /* GTID_SUPPORT */
void *wsrep_apply_format;
- char wsrep_info[128]; /* string for dynamic proc info */
+ uchar* wsrep_rbr_buf;
+ wsrep_gtid_t wsrep_sync_wait_gtid;
+ // wsrep_gtid_t wsrep_last_written_gtid;
+ ulong wsrep_affected_rows;
+ bool wsrep_has_ignored_error;
+ bool wsrep_replicate_GTID;
+
/*
When enabled, do not replicate/binlog updates from the current table that's
being processed. At the moment, it is used to keep mysql.gtid_slave_pos
table updates from being replicated to other nodes via galera replication.
*/
bool wsrep_ignore_table;
- wsrep_gtid_t wsrep_sync_wait_gtid;
- ulong wsrep_affected_rows;
- bool wsrep_replicate_GTID;
- bool wsrep_skip_wsrep_GTID;
- /* This flag is set when innodb do an intermediate commit to
- processing the LOAD DATA INFILE statement by splitting it into 10K
- rows chunks. If flag is set, then binlog rotation is not performed
- while intermediate transaction try to commit, because in this case
- rotation causes unregistration of innodb handler. Later innodb handler
- registered again, but replication of last chunk of rows is skipped
- by the innodb engine: */
- bool wsrep_split_flag;
+ /* thread who has started kill for this THD protected by LOCK_thd_data*/
+ my_thread_id wsrep_aborter;
+
+ /*
+ Transaction id:
+ * m_wsrep_next_trx_id is assigned on the first query after
+ wsrep_next_trx_id() return WSREP_UNDEFINED_TRX_ID
+ * Each storage engine must assign value of wsrep_next_trx_id()
+ when the transaction starts.
+ * Effective transaction id is returned via wsrep_trx_id()
+ */
+ /*
+ Return effective transaction id
+ */
+ wsrep_trx_id_t wsrep_trx_id() const
+ {
+ return m_wsrep_client_state.transaction().id().get();
+ }
+
+
+ /*
+ Set next trx id
+ */
+ void set_wsrep_next_trx_id(query_id_t query_id)
+ {
+ m_wsrep_next_trx_id = (wsrep_trx_id_t) query_id;
+ }
+ /*
+ Return next trx id
+ */
+ wsrep_trx_id_t wsrep_next_trx_id() const
+ {
+ return m_wsrep_next_trx_id;
+ }
+
+private:
+ wsrep_trx_id_t m_wsrep_next_trx_id; /* cast from query_id_t */
+ /* wsrep-lib */
+ Wsrep_mutex m_wsrep_mutex;
+ Wsrep_condition_variable m_wsrep_cond;
+ Wsrep_client_service m_wsrep_client_service;
+ Wsrep_client_state m_wsrep_client_state;
+
+public:
+ Wsrep_client_state& wsrep_cs() { return m_wsrep_client_state; }
+ const Wsrep_client_state& wsrep_cs() const { return m_wsrep_client_state; }
+ const wsrep::transaction& wsrep_trx() const
+ { return m_wsrep_client_state.transaction(); }
+ const wsrep::streaming_context& wsrep_sr() const
+ { return m_wsrep_client_state.transaction().streaming_context(); }
+ /* Pointer to applier service for streaming THDs. This is needed to
+ be able to delete applier service object in case of background
+ rollback. */
+ Wsrep_applier_service* wsrep_applier_service;
+ /* wait_for_commit struct for binlog group commit */
+ wait_for_commit wsrep_wfc;
#endif /* WITH_WSREP */
/* Handling of timeouts for commands */
@@ -4825,18 +4994,6 @@ public:
(THD_TRANS::DID_WAIT | THD_TRANS::CREATED_TEMP_TABLE |
THD_TRANS::DROPPED_TEMP_TABLE | THD_TRANS::DID_DDL));
}
- /*
- Reset current_linfo
- Setting current_linfo to 0 needs to be done with LOCK_thread_count to
- ensure that adjust_linfo_offsets doesn't use a structure that may
- be deleted.
- */
- inline void reset_current_linfo()
- {
- mysql_mutex_lock(&LOCK_thread_count);
- current_linfo= 0;
- mysql_mutex_unlock(&LOCK_thread_count);
- }
uint get_net_wait_timeout()
@@ -4888,28 +5045,8 @@ public:
Item *sp_fix_func_item(Item **it_addr);
Item *sp_prepare_func_item(Item **it_addr, uint cols= 1);
bool sp_eval_expr(Field *result_field, Item **expr_item_ptr);
-};
-
-inline void add_to_active_threads(THD *thd)
-{
- mysql_mutex_lock(&LOCK_thread_count);
- threads.append(thd);
- mysql_mutex_unlock(&LOCK_thread_count);
-}
-
-/*
- This should be called when you want to delete a thd that was not
- running any queries.
- This function will assert that the THD is linked.
-*/
-inline void unlink_not_visible_thd(THD *thd)
-{
- thd->assert_linked();
- mysql_mutex_lock(&LOCK_thread_count);
- thd->unlink();
- mysql_mutex_unlock(&LOCK_thread_count);
-}
+};
/** A short cut for thd->get_stmt_da()->set_ok_status(). */
@@ -4944,10 +5081,18 @@ my_eof(THD *thd)
(A)->variables.sql_log_bin_off= 0;}
-inline sql_mode_t sql_mode_for_dates(THD *thd)
+inline date_conv_mode_t sql_mode_for_dates(THD *thd)
{
- return thd->variables.sql_mode &
- (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | MODE_INVALID_DATES);
+ static_assert((date_conv_mode_t::KNOWN_MODES &
+ time_round_mode_t::KNOWN_MODES) == 0,
+ "date_conv_mode_t and time_round_mode_t must use different "
+ "bit values");
+ static_assert(MODE_NO_ZERO_DATE == date_mode_t::NO_ZERO_DATE &&
+ MODE_NO_ZERO_IN_DATE == date_mode_t::NO_ZERO_IN_DATE &&
+ MODE_INVALID_DATES == date_mode_t::INVALID_DATES,
+ "sql_mode_t and date_mode_t values must be equal");
+ return date_conv_mode_t(thd->variables.sql_mode &
+ (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | MODE_INVALID_DATES));
}
/*
@@ -5900,11 +6045,13 @@ public:
- The sj-materialization temporary table
- Members needed to make index lookup or a full scan of the temptable.
*/
+class POSITION;
+
class SJ_MATERIALIZATION_INFO : public Sql_alloc
{
public:
/* Optimal join sub-order */
- struct st_position *positions;
+ POSITION *positions;
uint tables; /* Number of tables in the sj-nest */
@@ -6096,6 +6243,10 @@ class multi_delete :public select_result_interceptor
bool error_handled;
public:
+ // Methods used by ColumnStore
+ uint get_num_of_tables() const { return num_of_tables; }
+ TABLE_LIST* get_tables() const { return delete_tables; }
+public:
multi_delete(THD *thd_arg, TABLE_LIST *dt, uint num_of_tables);
~multi_delete();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
@@ -6277,7 +6428,7 @@ public:
be rolled back or that do not expect any previously metadata
locked tables.
*/
-#define CF_IMPLICT_COMMIT_BEGIN (1U << 6)
+#define CF_IMPLICIT_COMMIT_BEGIN (1U << 6)
/**
Implicitly commit after the SQL statement.
@@ -6295,7 +6446,7 @@ public:
before and after every DDL statement and any statement that
modifies our currently non-transactional system tables.
*/
-#define CF_AUTO_COMMIT_TRANS (CF_IMPLICT_COMMIT_BEGIN | CF_IMPLICIT_COMMIT_END)
+#define CF_AUTO_COMMIT_TRANS (CF_IMPLICIT_COMMIT_BEGIN | CF_IMPLICIT_COMMIT_END)
/**
Diagnostic statement.
@@ -6376,6 +6527,14 @@ public:
*/
#define CF_DB_CHANGE (1U << 23)
+#ifdef WITH_WSREP
+/**
+ DDL statement that may be subject to error filtering.
+*/
+#define CF_WSREP_MAY_IGNORE_ERRORS (1U << 24)
+#endif /* WITH_WSREP */
+
+
/* Bits in server_command_flags */
/**
@@ -6415,7 +6574,8 @@ public:
inline bool add_item_to_list(THD *thd, Item *item)
{
- return thd->lex->current_select->add_item_to_list(thd, item);
+ bool res= thd->lex->current_select->add_item_to_list(thd, item);
+ return res;
}
inline bool add_value_to_list(THD *thd, Item *value)
@@ -6871,5 +7031,85 @@ private:
THD *const thd;
};
+
+/** THD registry */
+class THD_list
+{
+ I_List<THD> threads;
+ mutable mysql_rwlock_t lock;
+
+public:
+ /**
+ Constructor replacement.
+
+ Unfortunately we can't use fair constructor to initialize mutex
+ for two reasons: PFS and embedded. The former can probably be fixed,
+ the latter can probably be dropped.
+ */
+ void init()
+ {
+ mysql_rwlock_init(key_rwlock_THD_list, &lock);
+ }
+
+ /** Destructor replacement. */
+ void destroy()
+ {
+ mysql_rwlock_destroy(&lock);
+ }
+
+ /**
+ Inserts thread to registry.
+
+ @param thd thread
+
+ Thread becomes accessible via server_threads.
+ */
+ void insert(THD *thd)
+ {
+ mysql_rwlock_wrlock(&lock);
+ threads.append(thd);
+ mysql_rwlock_unlock(&lock);
+ }
+
+ /**
+ Removes thread from registry.
+
+ @param thd thread
+
+ Thread becomes not accessible via server_threads.
+ */
+ void erase(THD *thd)
+ {
+ thd->assert_linked();
+ mysql_rwlock_wrlock(&lock);
+ thd->unlink();
+ mysql_rwlock_unlock(&lock);
+ }
+
+ /**
+ Iterates registered threads.
+
+ @param action called for every element
+ @param argument opque argument passed to action
+
+ @return
+ @retval 0 iteration completed successfully
+ @retval 1 iteration was interrupted (action returned 1)
+ */
+ template <typename T> int iterate(my_bool (*action)(THD *thd, T *arg), T *arg= 0)
+ {
+ int res= 0;
+ mysql_rwlock_rdlock(&lock);
+ I_List_iterator<THD> it(threads);
+ while (auto tmp= it++)
+ if ((res= action(tmp, arg)))
+ break;
+ mysql_rwlock_unlock(&lock);
+ return res;
+ }
+};
+
+extern THD_list server_threads;
+
#endif /* MYSQL_SERVER */
#endif /* SQL_CLASS_INCLUDED */
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index f5df73216d8..7f1fd06aa46 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -108,6 +108,7 @@ enum enum_sql_command {
SQLCOM_SHOW_STATUS_PACKAGE,
SQLCOM_SHOW_STATUS_PACKAGE_BODY,
SQLCOM_SHOW_PACKAGE_BODY_CODE,
+ SQLCOM_BACKUP, SQLCOM_BACKUP_LOCK,
/*
When a command is added here, be sure it's also added in mysqld.cc
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 4d9ef98d22b..4d09dab392b 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2007, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2016, MariaDB
+ Copyright (c) 2008, 2020, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -37,7 +37,11 @@
// reset_host_errors
#include "sql_acl.h" // acl_getroot, NO_ACCESS, SUPER_ACL
#include "sql_callback.h"
+
+#ifdef WITH_WSREP
+#include "wsrep_trans_observer.h" /* wsrep open/close */
#include "wsrep_mysqld.h"
+#endif /* WITH_WSREP */
#include "proxy_protocol.h"
HASH global_user_stats, global_client_stats, global_table_stats;
@@ -1032,12 +1036,16 @@ static int check_connection(THD *thd)
*/
statistic_increment(connection_errors_peer_addr, &LOCK_status);
my_error(ER_BAD_HOST_ERROR, MYF(0));
+ statistic_increment(aborted_connects_preauth, &LOCK_status);
return 1;
}
if (thd_set_peer_addr(thd, &net->vio->remote, ip, peer_port,
true, &connect_errors))
+ {
+ statistic_increment(aborted_connects_preauth, &LOCK_status);
return 1;
+ }
}
else /* Hostname given means that the connection was on a socket */
{
@@ -1065,6 +1073,7 @@ static int check_connection(THD *thd)
*/
statistic_increment(aborted_connects,&LOCK_status);
statistic_increment(connection_errors_internal, &LOCK_status);
+ statistic_increment(aborted_connects_preauth, &LOCK_status);
return 1; /* The error is set by alloc(). */
}
@@ -1178,18 +1187,17 @@ exit:
void end_connection(THD *thd)
{
NET *net= &thd->net;
+
#ifdef WITH_WSREP
- if (WSREP(thd) && wsrep)
+ if (thd->wsrep_cs().state() == wsrep::client_state::s_exec)
{
- wsrep_status_t rcode= wsrep->free_connection(wsrep, thd->thread_id);
- if (rcode) {
- WSREP_WARN("wsrep failed to free connection context: %lld code: %d",
- (longlong) thd->thread_id, rcode);
- }
+ /* Error happened after the thread acquired ownership to wsrep
+ client state, but before command was processed. Clean up the
+ state before wsrep_close(). */
+ wsrep_after_command_ignore_result(thd);
}
- thd->wsrep_client_thread= 0;
-#endif
-
+ wsrep_close(thd);
+#endif /* WITH_WSREP */
if (thd->user_connect)
{
/*
@@ -1322,7 +1330,8 @@ bool thd_prepare_connection(THD *thd)
prepare_new_connection_state(thd);
#ifdef WITH_WSREP
- thd->wsrep_client_thread= 1;
+ thd->wsrep_client_thread= true;
+ wsrep_open(thd);
#endif /* WITH_WSREP */
return FALSE;
}
@@ -1367,7 +1376,7 @@ void do_handle_one_connection(CONNECT *connect)
delete connect;
/* Make THD visible in show processlist */
- add_to_active_threads(thd);
+ server_threads.insert(thd);
thd->thr_create_utime= thr_create_utime;
/* We need to set this because of time_out_user_resource_limits */
@@ -1405,14 +1414,6 @@ void do_handle_one_connection(CONNECT *connect)
}
end_connection(thd);
-#ifdef WITH_WSREP
- if (WSREP(thd))
- {
- mysql_mutex_lock(&thd->LOCK_thd_data);
- wsrep_thd_set_query_state(thd, QUERY_EXITING);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- }
-#endif
end_thread:
close_connection(thd);
diff --git a/sql/sql_const.h b/sql/sql_const.h
index e33d8b73626..f7c820c727b 100644
--- a/sql/sql_const.h
+++ b/sql/sql_const.h
@@ -204,7 +204,11 @@
instead of reading with keys. The number says how many evaluation of the
WHERE clause is comparable to reading one extra row from a table.
*/
-#define TIME_FOR_COMPARE 5 // 5 compares == one read
+#define TIME_FOR_COMPARE 5 // 5 compares == one read
+#define TIME_FOR_COMPARE_IDX 20
+
+#define IDX_BLOCK_COPY_COST ((double) 1 / TIME_FOR_COMPARE)
+#define IDX_LOOKUP_COST ((double) 1 / 8)
/**
Number of comparisons of table rowids equivalent to reading one row from a
diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc
index b5946a6c31d..f3861ebd3e9 100644
--- a/sql/sql_cte.cc
+++ b/sql/sql_cte.cc
@@ -55,6 +55,14 @@ bool With_clause::add_with_element(With_element *elem)
}
+void st_select_lex_unit::set_with_clause(With_clause *with_cl)
+{
+ with_clause= with_cl;
+ if (with_clause)
+ with_clause->set_owner(this);
+}
+
+
/**
@brief
Check dependencies between tables defined in a list of with clauses
@@ -684,7 +692,7 @@ void With_element::move_anchors_ahead()
{
st_select_lex *next_sl;
st_select_lex *new_pos= spec->first_select();
- new_pos->linkage= UNION_TYPE;
+ new_pos->set_linkage(UNION_TYPE);
for (st_select_lex *sl= new_pos; sl; sl= next_sl)
{
next_sl= sl->next_select();
@@ -693,9 +701,9 @@ void With_element::move_anchors_ahead()
sl->move_node(new_pos);
if (new_pos == spec->first_select())
{
- enum sub_select_type type= new_pos->linkage;
- new_pos->linkage= sl->linkage;
- sl->linkage= type;
+ enum sub_select_type type= new_pos->get_linkage();
+ new_pos->set_linkage(sl->get_linkage());
+ sl->set_linkage(type);
new_pos->with_all_modifier= sl->with_all_modifier;
sl->with_all_modifier= false;
}
@@ -708,7 +716,7 @@ void With_element::move_anchors_ahead()
}
}
first_recursive= new_pos;
- spec->first_select()->linkage= DERIVED_TABLE_TYPE;
+ spec->first_select()->set_linkage(DERIVED_TABLE_TYPE);
}
@@ -761,7 +769,9 @@ bool With_clause::prepare_unreferenced_elements(THD *thd)
true on failure
*/
-bool With_element::set_unparsed_spec(THD *thd, char *spec_start, char *spec_end,
+bool With_element::set_unparsed_spec(THD *thd,
+ const char *spec_start,
+ const char *spec_end,
my_ptrdiff_t spec_offset)
{
stmt_prepare_mode= thd->m_parser_state->m_lip.stmt_prepare_mode;
@@ -775,7 +785,7 @@ bool With_element::set_unparsed_spec(THD *thd, char *spec_start, char *spec_end,
if (!unparsed_spec.str)
{
- my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR),
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATAL),
static_cast<int>(unparsed_spec.length));
return true;
}
@@ -858,10 +868,9 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd,
lex->sp_chistics= old_lex->sp_chistics;
lex->stmt_lex= old_lex;
- with_select= &lex->select_lex;
- with_select->select_number= ++thd->lex->stmt_lex->current_select_number;
parse_status= parse_sql(thd, &parser_state, 0);
((char*) &unparsed_spec.str[unparsed_spec.length])[0]= save_end;
+ with_select= lex->first_select_lex();
if (parse_status)
goto err;
@@ -1029,7 +1038,7 @@ bool With_element::prepare_unreferenced(THD *thd)
rename_columns_of_derived_unit(thd, spec) ||
check_duplicate_names(thd, first_sl->item_list, 1)))
rc= true;
-
+
thd->lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED;
return rc;
}
@@ -1142,7 +1151,8 @@ bool TABLE_LIST::set_as_with_table(THD *thd, With_element *with_elem)
if(!(derived= with_elem->clone_parsed_spec(thd, this)))
return true;
}
- derived->first_select()->linkage= DERIVED_TABLE_TYPE;
+ derived->first_select()->set_linkage(DERIVED_TABLE_TYPE);
+ select_lex->add_statistics(derived);
with_elem->inc_references();
return false;
}
diff --git a/sql/sql_cte.h b/sql/sql_cte.h
index bda62271649..80d56644d7e 100644
--- a/sql/sql_cte.h
+++ b/sql/sql_cte.h
@@ -197,7 +197,7 @@ public:
TABLE_LIST *find_first_sq_rec_ref_in_select(st_select_lex *sel);
- bool set_unparsed_spec(THD *thd, char *spec_start, char *spec_end,
+ bool set_unparsed_spec(THD *thd, const char *spec_start, const char *spec_end,
my_ptrdiff_t spec_offset);
st_select_lex_unit *clone_parsed_spec(THD *thd, TABLE_LIST *with_table);
@@ -292,8 +292,7 @@ private:
*/
With_clause *next_with_clause;
/* Set to true if dependencies between with elements have been checked */
- bool dependencies_are_checked;
-
+ bool dependencies_are_checked;
/*
The bitmap of all recursive with elements whose specifications
are not complied with restrictions imposed by the SQL standards
@@ -317,9 +316,8 @@ public:
bool with_recursive;
With_clause(bool recursive_fl, With_clause *emb_with_clause)
- : owner(NULL),
- embedding_with_clause(emb_with_clause), next_with_clause(NULL),
- dependencies_are_checked(false), unrestricted(0),
+ : owner(NULL), embedding_with_clause(emb_with_clause),
+ next_with_clause(NULL), dependencies_are_checked(false), unrestricted(0),
with_prepared_anchor(0), cleaned(0), stabilized(0),
with_recursive(recursive_fl)
{ }
@@ -333,8 +331,12 @@ public:
last_next= &this->next_with_clause;
}
+ st_select_lex_unit *get_owner() { return owner; }
+
void set_owner(st_select_lex_unit *unit) { owner= unit; }
+ void attach_to(st_select_lex *select_lex);
+
With_clause *pop() { return embedding_with_clause; }
bool check_dependencies();
@@ -367,7 +369,6 @@ bool With_element::is_unrestricted()
}
inline
-
bool With_element::is_with_prepared_anchor()
{
return owner->with_prepared_anchor & get_elem_map();
@@ -449,11 +450,14 @@ void With_element::prepare_for_next_iteration()
inline
-void st_select_lex_unit::set_with_clause(With_clause *with_cl)
-{
- with_clause= with_cl;
- if (with_clause)
- with_clause->set_owner(this);
+void With_clause::attach_to(st_select_lex *select_lex)
+{
+ for (With_element *with_elem= with_list.first;
+ with_elem;
+ with_elem= with_elem->next)
+ {
+ select_lex->register_unit(with_elem->spec, NULL);
+ }
}
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 7b1ca52239c..b2b591464f7 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -882,7 +882,7 @@ mysql_rm_db_internal(THD *thd, const LEX_CSTRING *db, bool if_exists, bool silen
lock_db_routines(thd, dbnorm))
goto exit;
- if (!in_bootstrap && !rm_mysql_schema)
+ if (!rm_mysql_schema)
{
for (table= tables; table; table= table->next_local)
{
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 4d6a2c64e3e..9fa1e015274 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2019, Oracle and/or its affiliates.
- Copyright (c) 2010, 2019, MariaDB
+ Copyright (c) 2010, 2021, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -237,6 +237,53 @@ static bool record_should_be_deleted(THD *thd, TABLE *table, SQL_SELECT *sel,
return false;
}
+static
+int update_portion_of_time(THD *thd, TABLE *table,
+ const vers_select_conds_t &period_conds,
+ bool *inside_period)
+{
+ bool lcond= period_conds.field_start->val_datetime_packed(thd)
+ < period_conds.start.item->val_datetime_packed(thd);
+ bool rcond= period_conds.field_end->val_datetime_packed(thd)
+ > period_conds.end.item->val_datetime_packed(thd);
+
+ *inside_period= !lcond && !rcond;
+ if (*inside_period)
+ return 0;
+
+ DBUG_ASSERT(!table->triggers
+ || !table->triggers->has_triggers(TRG_EVENT_INSERT,
+ TRG_ACTION_BEFORE));
+
+ int res= 0;
+ Item *src= lcond ? period_conds.start.item : period_conds.end.item;
+ uint dst_fieldno= lcond ? table->s->period.end_fieldno
+ : table->s->period.start_fieldno;
+
+ ulonglong prev_insert_id= table->file->next_insert_id;
+ store_record(table, record[1]);
+ if (likely(!res))
+ res= src->save_in_field(table->field[dst_fieldno], true);
+
+ if (likely(!res))
+ res= table->update_generated_fields();
+
+ if(likely(!res))
+ res= table->file->ha_update_row(table->record[1], table->record[0]);
+
+ if (likely(!res) && table->triggers)
+ res= table->triggers->process_triggers(thd, TRG_EVENT_INSERT,
+ TRG_ACTION_AFTER, true);
+ restore_record(table, record[1]);
+ if (res)
+ table->file->restore_auto_increment(prev_insert_id);
+
+ if (likely(!res) && lcond && rcond)
+ res= table->period_make_insert(period_conds.end.item,
+ table->field[table->s->period.start_fieldno]);
+
+ return res;
+}
inline
int TABLE::delete_row()
@@ -282,10 +329,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
bool return_error= 0;
ha_rows deleted= 0;
bool reverse= FALSE;
- bool has_triggers;
+ bool has_triggers= false;
ORDER *order= (ORDER *) ((order_list && order_list->elements) ?
order_list->first : NULL);
- SELECT_LEX *select_lex= &thd->lex->select_lex;
+ SELECT_LEX *select_lex= thd->lex->first_select_lex();
killed_state killed_status= NOT_KILLED;
THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE;
bool binlog_is_row;
@@ -293,7 +340,9 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
Explain_delete *explain;
Delete_plan query_plan(thd->mem_root);
Unique * deltempfile= NULL;
- bool delete_record, delete_while_scanning;
+ bool delete_record= false;
+ bool delete_while_scanning;
+ bool portion_of_time_through_update;
DBUG_ENTER("mysql_delete");
query_plan.index= MAX_KEY;
@@ -306,6 +355,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
THD_STAGE_INFO(thd, stage_init_update);
const bool delete_history= table_list->vers_conditions.delete_history;
+ DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set()));
if (thd->lex->handle_list_of_derived(table_list, DT_MERGE_FOR_INSERT))
DBUG_RETURN(TRUE);
@@ -324,7 +374,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
DBUG_RETURN(TRUE);
}
table->map=1;
- query_plan.select_lex= &thd->lex->select_lex;
+ query_plan.select_lex= thd->lex->first_select_lex();
query_plan.table= table;
query_plan.updating_a_view= MY_TEST(table_list->view);
@@ -359,7 +409,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
setup_order(thd, select_lex->ref_pointer_array, &tables,
fields, all_fields, order))
{
- free_underlaid_joins(thd, &thd->lex->select_lex);
+ free_underlaid_joins(thd, thd->lex->first_select_lex());
DBUG_RETURN(TRUE);
}
}
@@ -400,12 +450,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
- there should be no delete triggers associated with the table.
*/
- has_triggers= (table->triggers &&
- table->triggers->has_delete_triggers());
+ has_triggers= table->triggers && table->triggers->has_delete_triggers();
+
if (!with_select && !using_limit && const_cond_result &&
(!thd->is_current_stmt_binlog_format_row() &&
!has_triggers)
- && !table->versioned(VERS_TIMESTAMP))
+ && !table->versioned(VERS_TIMESTAMP) && !table_list->has_period())
{
/* Update the table->file->stats.records number */
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
@@ -575,7 +625,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
*/
if ((table->file->ha_table_flags() & HA_CAN_DIRECT_UPDATE_AND_DELETE) &&
- !has_triggers && !binlog_is_row && !with_select)
+ !has_triggers && !binlog_is_row && !with_select &&
+ !table_list->has_period())
{
table->mark_columns_needed_for_delete();
if (!table->check_virtual_columns_marked_for_read())
@@ -645,7 +696,15 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (unlikely(init_ftfuncs(thd, select_lex, 1)))
goto got_error;
- table->mark_columns_needed_for_delete();
+ if (table_list->has_period())
+ {
+ table->use_all_columns();
+ table->rpl_write_set= table->write_set;
+ }
+ else
+ {
+ table->mark_columns_needed_for_delete();
+ }
if ((table->file->ha_table_flags() & HA_CAN_FORCE_BULK_DELETE) &&
!table->prepare_triggers_for_delete_stmt_or_event())
@@ -702,6 +761,24 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
delete_record= true;
}
+ /*
+ From SQL2016, Part 2, 15.7 <Effect of deleting rows from base table>,
+ General Rules, 8), we can conclude that DELETE FOR PORTTION OF time performs
+ 0-2 INSERTS + DELETE. We can substitute INSERT+DELETE with one UPDATE, with
+ a condition of no side effects. The side effect is possible if there is a
+ BEFORE INSERT trigger, since it is the only one splitting DELETE and INSERT
+ operations.
+ Another possible side effect is related to tables of non-transactional
+ engines, since UPDATE is anyway atomic, and DELETE+INSERT is not.
+
+ This optimization is not possible for system-versioned table.
+ */
+ portion_of_time_through_update=
+ !(table->triggers && table->triggers->has_triggers(TRG_EVENT_INSERT,
+ TRG_ACTION_BEFORE))
+ && !table->versioned()
+ && table->file->has_transactions();
+
THD_STAGE_INFO(thd, stage_updating);
while (likely(!(error=info.read_record())) && likely(!thd->killed) &&
likely(!thd->is_error()))
@@ -725,7 +802,25 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
break;
}
- error= table->delete_row();
+ if (table_list->has_period() && portion_of_time_through_update)
+ {
+ bool need_delete= true;
+ error= update_portion_of_time(thd, table, table_list->period_conditions,
+ &need_delete);
+ if (likely(!error) && need_delete)
+ error= table->delete_row();
+ }
+ else
+ {
+ error= table->delete_row();
+
+ ha_rows rows_inserted;
+ if (likely(!error) && table_list->has_period()
+ && !portion_of_time_through_update)
+ error= table->insert_portion_of_time(thd, table_list->period_conditions,
+ &rows_inserted);
+ }
+
if (likely(!error))
{
deleted++;
@@ -745,7 +840,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
else
{
table->file->print_error(error,
- MYF(thd->lex->ignore ? ME_JUST_WARNING : 0));
+ MYF(thd->lex->ignore ? ME_WARNING : 0));
if (thd->is_error())
{
error= 1;
@@ -775,6 +870,8 @@ terminate_delete:
}
THD_STAGE_INFO(thd, stage_end);
end_read_record(&info);
+ if (table_list->has_period())
+ table->file->ha_release_auto_increment();
if (options & OPTION_QUICK)
(void) table->file->extra(HA_EXTRA_NORMAL);
ANALYZE_STOP_TRACKING(&explain->command_tracker);
@@ -906,14 +1003,16 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list,
bool *delete_while_scanning)
{
Item *fake_conds= 0;
- SELECT_LEX *select_lex= &thd->lex->select_lex;
+ SELECT_LEX *select_lex= thd->lex->first_select_lex();
DBUG_ENTER("mysql_prepare_delete");
List<Item> all_fields;
*delete_while_scanning= true;
thd->lex->allow_sum_func.clear_all();
- if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
- &thd->lex->select_lex.top_join_list,
+ if (setup_tables_and_check_access(thd,
+ &thd->lex->first_select_lex()->context,
+ &thd->lex->first_select_lex()->
+ top_join_list,
table_list,
select_lex->leaf_tables, FALSE,
DELETE_ACL, SELECT_ACL, TRUE))
@@ -925,6 +1024,18 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list,
DBUG_RETURN(true);
}
+ if (table_list->has_period())
+ {
+ if (table_list->is_view_or_derived())
+ {
+ my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str);
+ DBUG_RETURN(true);
+ }
+
+ if (select_lex->period_setup_conds(thd, table_list))
+ DBUG_RETURN(true);
+ }
+
DBUG_ASSERT(table_list->table);
// conds could be cached from previous SP call
DBUG_ASSERT(!table_list->vers_conditions.need_setup() ||
@@ -948,7 +1059,13 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list,
DBUG_RETURN(TRUE);
}
- if (unique_table(thd, table_list, table_list->next_global, 0))
+ /*
+ Application-time periods: if FOR PORTION OF ... syntax used, DELETE
+ statement could issue delete_row's mixed with write_row's. This causes
+ problems for myisam and corrupts table, if deleting while scanning.
+ */
+ if (table_list->has_period()
+ || unique_table(thd, table_list, table_list->next_global, 0))
*delete_while_scanning= false;
if (select_lex->inner_refs_list.elements &&
@@ -1002,18 +1119,20 @@ int mysql_multi_delete_prepare(THD *thd)
lex->query_tables also point on local list of DELETE SELECT_LEX
*/
- if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
- &thd->lex->select_lex.top_join_list,
+ if (setup_tables_and_check_access(thd,
+ &thd->lex->first_select_lex()->context,
+ &thd->lex->first_select_lex()->
+ top_join_list,
lex->query_tables,
- lex->select_lex.leaf_tables, FALSE,
- DELETE_ACL, SELECT_ACL, FALSE))
+ lex->first_select_lex()->leaf_tables,
+ FALSE, DELETE_ACL, SELECT_ACL, FALSE))
DBUG_RETURN(TRUE);
/*
Multi-delete can't be constructed over-union => we always have
single SELECT on top and have to check underlying SELECTs of it
*/
- lex->select_lex.set_unique_exclude();
+ lex->first_select_lex()->set_unique_exclude();
/* Fix tables-to-be-deleted-from list to point at opened tables */
for (target_tbl= (TABLE_LIST*) aux_tables;
target_tbl;
@@ -1061,8 +1180,8 @@ int mysql_multi_delete_prepare(THD *thd)
Reset the exclude flag to false so it doesn't interfare
with further calls to unique_table
*/
- lex->select_lex.exclude_from_table_unique_test= FALSE;
-
+ lex->first_select_lex()->exclude_from_table_unique_test= FALSE;
+
if (lex->save_prep_leaf_tables())
DBUG_RETURN(TRUE);
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index e2e30f35206..fc01dcdc750 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2002, 2011, Oracle and/or its affiliates.
- Copyright (c) 2010, 2020, MariaDB
+ Copyright (c) 2010, 2021, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -27,23 +27,26 @@
#include "unireg.h"
#include "sql_derived.h"
#include "sql_select.h"
+#include "derived_handler.h"
#include "sql_base.h"
#include "sql_view.h" // check_duplicate_names
#include "sql_acl.h" // SELECT_ACL
#include "sql_class.h"
#include "sql_cte.h"
+#include "my_json_writer.h"
+#include "opt_trace.h"
typedef bool (*dt_processor)(THD *thd, LEX *lex, TABLE_LIST *derived);
-bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived);
-bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived);
-bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived);
-bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived);
-bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived);
-bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived);
-bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived);
-bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived);
-
+static bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived);
+static bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived);
+static bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived);
+static bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived);
+static bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived);
+static bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived);
+static bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived);
+static bool mysql_derived_merge_for_insert(THD *thd, LEX *lex,
+ TABLE_LIST *derived);
dt_processor processors[]=
{
@@ -97,7 +100,8 @@ mysql_handle_derived(LEX *lex, uint phases)
processed normally.
*/
if (phases == DT_MERGE_FOR_INSERT &&
- cursor && cursor->top_table()->select_lex != &lex->select_lex)
+ cursor && (cursor->top_table()->select_lex !=
+ lex->first_select_lex()))
continue;
for (;
cursor && !res;
@@ -175,7 +179,10 @@ mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases)
if (!lex->derived_tables)
DBUG_RETURN(FALSE);
- derived->select_lex->changed_elements|= TOUCHED_SEL_DERIVED;
+ if (derived->select_lex)
+ derived->select_lex->changed_elements|= TOUCHED_SEL_DERIVED;
+ else
+ DBUG_ASSERT(derived->prelocking_placeholder);
lex->thd->derived_tables_processing= TRUE;
for (uint phase= 0; phase < DT_PHASES; phase++)
@@ -193,6 +200,7 @@ mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases)
if ((res= (*processors[phase])(lex->thd, lex, derived)))
break;
}
+
lex->thd->derived_tables_processing= FALSE;
DBUG_RETURN(res);
}
@@ -321,6 +329,7 @@ mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases)
@return TRUE if an error occur.
*/
+static
bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
{
bool res= FALSE;
@@ -333,6 +342,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
(derived->alias.str ? derived->alias.str : "<NULL>"),
derived->get_unit()));
+ const char *cause= NULL;
if (derived->merged)
{
@@ -344,6 +354,23 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
if (dt_select->uncacheable & UNCACHEABLE_RAND)
{
/* There is random function => fall back to materialization. */
+ cause= "Random function in the select";
+ if (unlikely(thd->trace_started()))
+ {
+ OPT_TRACE_VIEWS_TRANSFORM(thd, trace_wrapper, trace_derived,
+ derived->is_derived() ? "derived" : "view",
+ derived->alias.str ? derived->alias.str : "<NULL>",
+ derived->get_unit()->first_select()->select_number,
+ "materialized");
+ trace_derived.add("cause", cause);
+ }
+ derived->change_refs_to_fields();
+ derived->set_materialized_derived();
+ DBUG_RETURN(FALSE);
+ }
+
+ if (derived->dt_handler)
+ {
derived->change_refs_to_fields();
derived->set_materialized_derived();
DBUG_RETURN(FALSE);
@@ -362,15 +389,11 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
and small subqueries, and the bigger one can't be merged it wouldn't
block the smaller one.
*/
- if (parent_lex->get_free_table_map(&map, &tablenr))
- {
- /* There is no enough table bits, fall back to materialization. */
- goto unconditional_materialization;
- }
-
- if (dt_select->leaf_tables.elements + tablenr > MAX_TABLES)
+ if (parent_lex->get_free_table_map(&map, &tablenr) ||
+ dt_select->leaf_tables.elements + tablenr > MAX_TABLES)
{
/* There is no enough table bits, fall back to materialization. */
+ cause= "Not enough table bits to merge subquery";
goto unconditional_materialization;
}
@@ -448,6 +471,17 @@ exit_merge:
DBUG_RETURN(res);
unconditional_materialization:
+
+ if (unlikely(thd->trace_started()))
+ {
+ OPT_TRACE_VIEWS_TRANSFORM(thd,trace_wrapper, trace_derived,
+ derived->is_derived() ? "derived" : "view",
+ derived->alias.str ? derived->alias.str : "<NULL>",
+ derived->get_unit()->first_select()->select_number,
+ "materialized");
+ trace_derived.add("cause", cause);
+ }
+
derived->change_refs_to_fields();
derived->set_materialized_derived();
if (!derived->table || !derived->table->is_created())
@@ -475,6 +509,7 @@ unconditional_materialization:
@return TRUE if an error occur.
*/
+static
bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
{
DBUG_ENTER("mysql_derived_merge_for_insert");
@@ -531,7 +566,7 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
true Error
*/
-
+static
bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived)
{
SELECT_LEX_UNIT *unit= derived->get_unit();
@@ -552,7 +587,7 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived)
}
-/*
+/**
@brief
Prevent name resolution out of context of ON expressions in derived tables
@@ -634,7 +669,7 @@ static void nullify_outer_context_for_on_clauses(List<TABLE_LIST>& join_list)
true Error
*/
-
+static
bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
{
SELECT_LEX_UNIT *unit= derived->get_unit();
@@ -642,7 +677,6 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
DBUG_ENTER("mysql_derived_prepare");
DBUG_PRINT("enter", ("unit: %p table_list: %p alias: '%s'",
unit, derived, derived->alias.str));
-
if (!unit)
DBUG_RETURN(FALSE);
@@ -772,6 +806,18 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
}
}
+ if (unlikely(thd->trace_started()))
+ {
+ /*
+ Add to optimizer trace whether a derived table/view
+ is merged into the parent select or not.
+ */
+ OPT_TRACE_VIEWS_TRANSFORM(thd, trace_wrapper, trace_derived,
+ derived->is_derived() ? "derived" : "view",
+ derived->alias.str ? derived->alias.str : "<NULL>",
+ derived->get_unit()->first_select()->select_number,
+ derived->is_merged_derived() ? "merged" : "materialized");
+ }
/*
Above cascade call of prepare is important for PS protocol, but after it
is called we can check if we really need prepare for this derived
@@ -838,6 +884,24 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
if (derived->is_derived() && derived->is_merged_derived())
first_select->mark_as_belong_to_derived(derived);
+ derived->dt_handler= derived->find_derived_handler(thd);
+ if (derived->dt_handler)
+ {
+ char query_buff[4096];
+ String derived_query(query_buff, sizeof(query_buff), thd->charset());
+ derived_query.length(0);
+ derived->derived->print(&derived_query,
+ enum_query_type(QT_VIEW_INTERNAL |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF |
+ QT_PARSABLE));
+ if (!thd->make_lex_string(&derived->derived_spec,
+ derived_query.ptr(), derived_query.length()))
+ {
+ delete derived->dt_handler;
+ derived->dt_handler= NULL;
+ }
+ }
+
exit:
/* Hide "Unknown column" or "Unknown function" error */
if (derived->view)
@@ -914,6 +978,7 @@ exit:
@return TRUE if an error occur.
*/
+static
bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
{
SELECT_LEX_UNIT *unit= derived->get_unit();
@@ -930,6 +995,18 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
DBUG_RETURN(FALSE);
}
+ if (derived->is_materialized_derived() && derived->dt_handler)
+ {
+ /* Create an object for execution of the query specifying the table */
+ if (!(derived->pushdown_derived=
+ new (thd->mem_root) Pushdown_derived(derived, derived->dt_handler)))
+ {
+ delete derived->dt_handler;
+ derived->dt_handler= NULL;
+ DBUG_RETURN(TRUE);
+ }
+ }
+
lex->current_select= first_select;
if (unit->is_unit_op())
@@ -958,6 +1035,15 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
if (unit->optimized)
DBUG_RETURN(FALSE);
unit->optimized= TRUE;
+ if (!join)
+ {
+ /*
+ This happens when derived is used in SELECT for which
+ zer_result_cause != 0.
+ In this case join is already destroyed.
+ */
+ DBUG_RETURN(FALSE);
+ }
}
if ((res= join->optimize()))
goto err;
@@ -1004,6 +1090,7 @@ err:
@return TRUE if an error occur.
*/
+static
bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
{
DBUG_ENTER("mysql_derived_create");
@@ -1114,7 +1201,7 @@ bool TABLE_LIST::fill_recursive(THD *thd)
@return TRUE Error
*/
-
+static
bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
{
Field_iterator_table field_iterator;
@@ -1134,6 +1221,18 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
select_unit *derived_result= derived->derived_result;
SELECT_LEX *save_current_select= lex->current_select;
+ if (derived->pushdown_derived)
+ {
+ int res;
+ if (unit->executed)
+ DBUG_RETURN(FALSE);
+ /* Execute the query that specifies the derived table by a foreign engine */
+ res= derived->pushdown_derived->execute();
+ unit->executed= true;
+ delete derived->pushdown_derived;
+ DBUG_RETURN(res);
+ }
+
if (unit->executed && !derived_is_recursive &&
(unit->uncacheable & UNCACHEABLE_DEPENDENT))
{
@@ -1241,6 +1340,7 @@ err:
@return TRUE Error
*/
+static
bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
{
DBUG_ENTER("mysql_derived_reinit");
@@ -1254,11 +1354,6 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
unit->types.empty();
/* for derived tables & PS (which can't be reset by Item_subselect) */
unit->reinit_exec_mechanism();
- for (st_select_lex *sl= unit->first_select(); sl; sl= sl->next_select())
- {
- sl->cond_pushed_into_where= NULL;
- sl->cond_pushed_into_having= NULL;
- }
unit->set_thd(thd);
DBUG_RETURN(FALSE);
}
@@ -1266,25 +1361,61 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
/**
@brief
- Extract the condition depended on derived table/view and pushed it there
+ Extract condition that can be pushed into a derived table/view
- @param thd The thread handle
- @param cond The condition from which to extract the pushed condition
- @param derived The reference to the derived table/view
+ @param thd the thread handle
+ @param cond current condition
+ @param derived the reference to the derived table/view
@details
- This functiom builds the most restrictive condition depending only on
- the derived table/view that can be extracted from the condition cond.
- The built condition is pushed into the having clauses of the
- selects contained in the query specifying the derived table/view.
- The function also checks for each select whether any condition depending
- only on grouping fields can be extracted from the pushed condition.
- If so, it pushes the condition over grouping fields into the where
- clause of the select.
-
- @retval
- true if an error is reported
- false otherwise
+ This function builds the most restrictive condition depending only on
+ the derived table/view (directly or indirectly through equality) that
+ can be extracted from the given condition cond and pushes it into the
+ derived table/view.
+
+ Example of the transformation:
+
+ SELECT *
+ FROM t1,
+ (
+ SELECT x,MAX(y) AS max_y
+ FROM t2
+ GROUP BY x
+ ) AS d_tab
+ WHERE d_tab.x>1 AND d_tab.max_y<30;
+
+ =>
+
+ SELECT *
+ FROM t1,
+ (
+ SELECT x,z,MAX(y) AS max_y
+ FROM t2
+ WHERE x>1
+ HAVING max_y<30
+ GROUP BY x
+ ) AS d_tab
+ WHERE d_tab.x>1 AND d_tab.max_y<30;
+
+ In details:
+ 1. Check what pushable formula can be extracted from cond
+ 2. Build a clone PC of the formula that can be extracted
+ (the clone is built only if the extracted formula is a AND subformula
+ of cond or conjunction of such subformulas)
+ Do for every select specifying derived table/view:
+ 3. If there is no HAVING clause prepare PC to be conjuncted with
+ WHERE clause of the select. Otherwise do 4-7.
+ 4. Check what formula PC_where can be extracted from PC to be pushed
+ into the WHERE clause of the select
+ 5. Build PC_where and if PC_where is a conjunct(s) of PC remove it from PC
+ getting PC_having
+ 6. Prepare PC_where to be conjuncted with the WHERE clause of the select
+ 7. Prepare PC_having to be conjuncted with the HAVING clause of the select
+ @note
+ This method is similar to pushdown_cond_for_in_subquery()
+
+ @retval TRUE if an error occurs
+ @retval FALSE otherwise
*/
bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
@@ -1325,63 +1456,27 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
if (!some_select_allows_cond_pushdown)
DBUG_RETURN(false);
- /*
- Build the most restrictive condition extractable from 'cond'
- that can be pushed into the derived table 'derived'.
- All subexpressions of this condition are cloned from the
- subexpressions of 'cond'.
- This condition has to be fixed yet.
- */
+ /* 1. Check what pushable formula can be extracted from cond */
Item *extracted_cond;
- derived->check_pushable_cond_for_table(cond);
- extracted_cond= derived->build_pushable_cond_for_table(thd, cond);
+ cond->check_pushable_cond(&Item::pushable_cond_checker_for_derived,
+ (uchar *)(&derived->table->map));
+ /* 2. Build a clone PC of the formula that can be extracted */
+ extracted_cond=
+ cond->build_pushable_cond(thd,
+ &Item::pushable_equality_checker_for_derived,
+ ((uchar *)&derived->table->map));
if (!extracted_cond)
{
/* Nothing can be pushed into the derived table */
DBUG_RETURN(false);
}
- /* Push extracted_cond into every select of the unit specifying 'derived' */
+
st_select_lex *save_curr_select= thd->lex->current_select;
for (; sl; sl= sl->next_select())
{
Item *extracted_cond_copy;
if (!sl->cond_pushdown_is_allowed())
continue;
- thd->lex->current_select= sl;
- if (sl->have_window_funcs())
- {
- if (sl->join->group_list || sl->join->implicit_grouping)
- continue;
- ORDER *common_partition_fields=
- sl->find_common_window_func_partition_fields(thd);
- if (!common_partition_fields)
- continue;
- extracted_cond_copy= !sl->next_select() ?
- extracted_cond :
- extracted_cond->build_clone(thd);
- if (!extracted_cond_copy)
- continue;
-
- Item *cond_over_partition_fields;;
- sl->collect_grouping_fields(thd, common_partition_fields);
- sl->check_cond_extraction_for_grouping_fields(extracted_cond_copy,
- derived);
- cond_over_partition_fields=
- sl->build_cond_for_grouping_fields(thd, extracted_cond_copy, true);
- if (cond_over_partition_fields)
- cond_over_partition_fields= cond_over_partition_fields->transform(thd,
- &Item::derived_grouping_field_transformer_for_where,
- (uchar*) sl);
- if (cond_over_partition_fields)
- {
- cond_over_partition_fields->walk(
- &Item::cleanup_excluding_const_fields_processor, 0, 0);
- sl->cond_pushed_into_where= cond_over_partition_fields;
- }
-
- continue;
- }
-
/*
For each select of the unit except the last one
create a clone of extracted_cond
@@ -1392,58 +1487,6 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
if (!extracted_cond_copy)
continue;
- if (!sl->join->group_list && !sl->with_sum_func)
- {
- /* extracted_cond_copy is pushed into where of sl */
- extracted_cond_copy= extracted_cond_copy->transform(thd,
- &Item::derived_field_transformer_for_where,
- (uchar*) sl);
- if (extracted_cond_copy)
- {
- extracted_cond_copy->walk(
- &Item::cleanup_excluding_const_fields_processor, 0, 0);
- sl->cond_pushed_into_where= extracted_cond_copy;
- }
-
- continue;
- }
-
- /*
- Figure out what can be extracted from the pushed condition
- that could be pushed into the where clause of sl
- */
- Item *cond_over_grouping_fields;
- sl->collect_grouping_fields(thd, sl->join->group_list);
- sl->check_cond_extraction_for_grouping_fields(extracted_cond_copy,
- derived);
- cond_over_grouping_fields=
- sl->build_cond_for_grouping_fields(thd, extracted_cond_copy, true);
-
- /*
- Transform the references to the 'derived' columns from the condition
- pushed into the where clause of sl to make them usable in the new context
- */
- if (cond_over_grouping_fields)
- cond_over_grouping_fields= cond_over_grouping_fields->transform(thd,
- &Item::derived_grouping_field_transformer_for_where,
- (uchar*) sl);
-
- if (cond_over_grouping_fields)
- {
- /*
- In extracted_cond_copy remove top conjuncts that
- has been pushed into the where clause of sl
- */
- extracted_cond_copy= remove_pushed_top_conjuncts(thd, extracted_cond_copy);
-
- cond_over_grouping_fields->walk(
- &Item::cleanup_excluding_const_fields_processor, 0, 0);
- sl->cond_pushed_into_where= cond_over_grouping_fields;
-
- if (!extracted_cond_copy)
- continue;
- }
-
/*
Rename the columns of all non-first selects of a union to be compatible
by names with the columns of the first select. It will allow to use copies
@@ -1454,27 +1497,115 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
DBUG_ASSERT(sl->item_list.elements == first_sl->item_list.elements);
List_iterator_fast<Item> it(sl->item_list);
List_iterator_fast<Item> nm_it(unit->types);
- Item * item;
- while((item= it++))
- {
+ while (Item *item= it++)
item->share_name_with(nm_it++);
- }
}
+ /* Collect fields that are used in the GROUP BY of sl */
+ if (sl->have_window_funcs())
+ {
+ if (sl->group_list.first || sl->join->implicit_grouping)
+ continue;
+ ORDER *common_partition_fields=
+ sl->find_common_window_func_partition_fields(thd);
+ if (!common_partition_fields)
+ continue;
+ sl->collect_grouping_fields_for_derived(thd, common_partition_fields);
+ }
+ else
+ sl->collect_grouping_fields_for_derived(thd, sl->group_list.first);
+
+ Item *remaining_cond= NULL;
+ /* Do 4-6 */
+ sl->pushdown_cond_into_where_clause(thd, extracted_cond_copy,
+ &remaining_cond,
+ &Item::derived_field_transformer_for_where,
+ (uchar *) sl);
+
+ if (!remaining_cond)
+ continue;
/*
- Transform the references to the 'derived' columns from the condition
- pushed into the having clause of sl to make them usable in the new context
+ 7. Prepare PC_having to be conjuncted with the HAVING clause of
+ the select
*/
- extracted_cond_copy= extracted_cond_copy->transform(thd,
- &Item::derived_field_transformer_for_having,
- (uchar*) sl);
- if (!extracted_cond_copy)
+ remaining_cond=
+ remaining_cond->transform(thd,
+ &Item::derived_field_transformer_for_having,
+ (uchar *) sl);
+ if (!remaining_cond)
continue;
- extracted_cond_copy->walk(&Item::cleanup_excluding_const_fields_processor,
- 0, 0);
- sl->cond_pushed_into_having= extracted_cond_copy;
+ if (remaining_cond->walk(&Item::cleanup_excluding_const_fields_processor,
+ 0, 0))
+ continue;
+
+ mark_or_conds_to_avoid_pushdown(remaining_cond);
+
+ sl->cond_pushed_into_having= remaining_cond;
}
thd->lex->current_select= save_curr_select;
DBUG_RETURN(false);
}
+
+
+/**
+ @brief
+ Look for provision of the derived_handler interface by a foreign engine
+
+ @param thd The thread handler
+
+ @details
+ The function looks through its tables of the query that specifies this
+ derived table searching for a table whose handlerton owns a
+ create_derived call-back function. If the call of this function returns
+ a derived_handler interface object then the server will push the query
+ specifying the derived table into this engine.
+ This is a responsibility of the create_derived call-back function to
+ check whether the engine can execute the query.
+
+ @retval the found derived_handler if the search is successful
+ 0 otherwise
+*/
+
+derived_handler *TABLE_LIST::find_derived_handler(THD *thd)
+{
+ if (!derived || is_recursive_with_table())
+ return 0;
+ for (SELECT_LEX *sl= derived->first_select(); sl; sl= sl->next_select())
+ {
+ if (!(sl->join))
+ continue;
+ for (TABLE_LIST *tbl= sl->join->tables_list; tbl; tbl= tbl->next_local)
+ {
+ if (!tbl->table)
+ continue;
+ handlerton *ht= tbl->table->file->partition_ht();
+ if (!ht->create_derived)
+ continue;
+ derived_handler *dh= ht->create_derived(thd, this);
+ if (dh)
+ {
+ dh->set_derived(this);
+ return dh;
+ }
+ }
+ }
+ return 0;
+}
+
+
+TABLE_LIST *TABLE_LIST::get_first_table()
+{
+ for (SELECT_LEX *sl= derived->first_select(); sl; sl= sl->next_select())
+ {
+ if (!(sl->join))
+ continue;
+ for (TABLE_LIST *tbl= sl->join->tables_list; tbl; tbl= tbl->next_local)
+ {
+ if (!tbl->table)
+ continue;
+ return tbl;
+ }
+ }
+ return 0;
+}
diff --git a/sql/sql_derived.h b/sql/sql_derived.h
index 093191e62a7..403277d65c9 100644
--- a/sql/sql_derived.h
+++ b/sql/sql_derived.h
@@ -22,7 +22,6 @@ struct LEX;
bool mysql_handle_derived(LEX *lex, uint phases);
bool mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases);
-bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived);
bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived);
diff --git a/sql/sql_do.cc b/sql/sql_do.cc
index eab0e57b1d2..5a7bca2774c 100644
--- a/sql/sql_do.cc
+++ b/sql/sql_do.cc
@@ -33,7 +33,7 @@ bool mysql_do(THD *thd, List<Item> &values)
DBUG_RETURN(TRUE);
while ((value = li++))
(void) value->is_null();
- free_underlaid_joins(thd, &thd->lex->select_lex);
+ free_underlaid_joins(thd, thd->lex->first_select_lex());
if (unlikely(thd->is_error()))
{
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index da6a12137f7..a11a0f454a2 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -781,7 +781,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
List<Item> field_list;
MEM_ROOT *mem_root= thd->mem_root;
const Sql_condition *err;
- SELECT_LEX *sel= &thd->lex->select_lex;
+ SELECT_LEX *sel= thd->lex->first_select_lex();
SELECT_LEX_UNIT *unit= &thd->lex->unit;
ulonglong idx= 0;
Protocol *protocol=thd->protocol;
diff --git a/sql/sql_error.h b/sql/sql_error.h
index 3b29f7aba06..bb83d8af800 100644
--- a/sql/sql_error.h
+++ b/sql/sql_error.h
@@ -814,11 +814,48 @@ private:
extern char *err_conv(char *buff, uint to_length, const char *from,
uint from_length, CHARSET_INFO *from_cs);
-class ErrConv
+class ErrBuff
{
protected:
mutable char err_buffer[MYSQL_ERRMSG_SIZE];
public:
+ ErrBuff()
+ {
+ err_buffer[0]= '\0';
+ }
+ const char *ptr() const { return err_buffer; }
+ const char *set_longlong(const Longlong_hybrid &nr) const
+ {
+ return nr.is_unsigned() ? ullstr(nr.value(), err_buffer) :
+ llstr(nr.value(), err_buffer);
+ }
+ const char *set_double(double nr) const
+ {
+ my_gcvt(nr, MY_GCVT_ARG_DOUBLE, sizeof(err_buffer), err_buffer, 0);
+ return err_buffer;
+ }
+ const char *set_decimal(const decimal_t *d) const
+ {
+ int len= sizeof(err_buffer);
+ decimal2string(d, err_buffer, &len, 0, 0, ' ');
+ return err_buffer;
+ }
+ const char *set_str(const char *str, size_t len, CHARSET_INFO *cs) const
+ {
+ DBUG_ASSERT(len < UINT_MAX32);
+ return err_conv(err_buffer, (uint) sizeof(err_buffer), str, (uint) len, cs);
+ }
+ const char *set_mysql_time(const MYSQL_TIME *ltime) const
+ {
+ my_TIME_to_str(ltime, err_buffer, AUTO_SEC_PART_DIGITS);
+ return err_buffer;
+ }
+};
+
+
+class ErrConv: public ErrBuff
+{
+public:
ErrConv() {}
virtual ~ErrConv() {}
virtual const char *ptr() const = 0;
@@ -838,20 +875,18 @@ public:
: ErrConv(), str(s->ptr()), len(s->length()), cs(s->charset()) {}
const char *ptr() const
{
- DBUG_ASSERT(len < UINT_MAX32);
- return err_conv(err_buffer, (uint) sizeof(err_buffer), str, (uint) len, cs);
+ return set_str(str, len, cs);
}
};
class ErrConvInteger : public ErrConv, public Longlong_hybrid
{
public:
- ErrConvInteger(longlong num_arg, bool unsigned_flag= false) :
- ErrConv(), Longlong_hybrid(num_arg, unsigned_flag) {}
+ ErrConvInteger(const Longlong_hybrid &nr)
+ : ErrConv(), Longlong_hybrid(nr) { }
const char *ptr() const
{
- return m_unsigned ? ullstr(m_value, err_buffer) :
- llstr(m_value, err_buffer);
+ return set_longlong(static_cast<Longlong_hybrid>(*this));
}
};
@@ -862,8 +897,7 @@ public:
ErrConvDouble(double num_arg) : ErrConv(), num(num_arg) {}
const char *ptr() const
{
- my_gcvt(num, MY_GCVT_ARG_DOUBLE, sizeof(err_buffer), err_buffer, 0);
- return err_buffer;
+ return set_double(num);
}
};
@@ -874,8 +908,7 @@ public:
ErrConvTime(const MYSQL_TIME *ltime_arg) : ErrConv(), ltime(ltime_arg) {}
const char *ptr() const
{
- my_TIME_to_str(ltime, err_buffer, AUTO_SEC_PART_DIGITS);
- return err_buffer;
+ return set_mysql_time(ltime);
}
};
@@ -886,9 +919,7 @@ public:
ErrConvDecimal(const decimal_t *d_arg) : ErrConv(), d(d_arg) {}
const char *ptr() const
{
- int len= sizeof(err_buffer);
- decimal2string(d, err_buffer, &len, 0, 0, ' ');
- return err_buffer;
+ return set_decimal(d);
}
};
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index 95ff94273b4..645c1618a8a 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -34,6 +34,9 @@ const char *unit_operation_text[4]=
"UNIT RESULT","UNION RESULT","INTERSECT RESULT","EXCEPT RESULT"
};
+const char *pushed_derived_text= "PUSHED DERIVED";
+const char *pushed_select_text= "PUSHED SELECT";
+
static void write_item(Json_writer *writer, Item *item);
static void append_item_to_str(String *out, Item *item);
@@ -233,7 +236,7 @@ void Explain_query::print_explain_json(select_result_sink *output,
CHARSET_INFO *cs= system_charset_info;
List<Item> item_list;
- String *buf= &writer.output;
+ const String *buf= writer.output.get_string();
item_list.push_back(new (thd->mem_root)
Item_string(thd, buf->ptr(), buf->length(), cs),
thd->mem_root);
@@ -334,6 +337,9 @@ int print_explain_row(select_result_sink *result,
List<Item> item_list;
Item *item;
+ if (!select_type[0])
+ return 0;
+
item_list.push_back(new (mem_root) Item_int(thd, (int32) select_number),
mem_root);
item_list.push_back(new (mem_root) Item_string_sys(thd, select_type),
@@ -380,21 +386,31 @@ int print_explain_row(select_result_sink *result,
item_list.push_back(item, mem_root);
/* 'rows' */
+ StringBuffer<64> rows_str;
if (rows)
{
+ rows_str.append_ulonglong((ulonglong)(*rows));
item_list.push_back(new (mem_root)
- Item_int(thd, *rows, MY_INT64_NUM_DECIMAL_DIGITS),
- mem_root);
+ Item_string_sys(thd, rows_str.ptr(),
+ rows_str.length()), mem_root);
}
else
item_list.push_back(item_null, mem_root);
/* 'r_rows' */
+ StringBuffer<64> r_rows_str;
if (is_analyze)
{
if (r_rows)
- item_list.push_back(new (mem_root) Item_float(thd, *r_rows, 2),
- mem_root);
+ {
+ Item_float *fl= new (mem_root) Item_float(thd, *r_rows, 2);
+ String tmp;
+ String *res= fl->val_str(&tmp);
+ r_rows_str.append(res->ptr());
+ item_list.push_back(new (mem_root)
+ Item_string_sys(thd, r_rows_str.ptr(),
+ r_rows_str.length()), mem_root);
+ }
else
item_list.push_back(item_null, mem_root);
}
@@ -529,10 +545,17 @@ int Explain_union::print_explain(Explain_query *query,
item_list.push_back(item_null, mem_root);
/* `r_rows` */
+ StringBuffer<64> r_rows_str;
if (is_analyze)
{
double avg_rows= fake_select_lex_tracker.get_avg_rows();
- item_list.push_back(new (mem_root) Item_float(thd, avg_rows, 2), mem_root);
+ Item_float *fl= new (mem_root) Item_float(thd, avg_rows, 2);
+ String tmp;
+ String *res= fl->val_str(&tmp);
+ r_rows_str.append(res->ptr());
+ item_list.push_back(new (mem_root)
+ Item_string_sys(thd, r_rows_str.ptr(),
+ r_rows_str.length()), mem_root);
}
/* `filtered` */
@@ -748,7 +771,15 @@ int Explain_select::print_explain(Explain_query *query,
THD *thd= output->thd;
MEM_ROOT *mem_root= thd->mem_root;
- if (message)
+ if (select_type == pushed_derived_text || select_type == pushed_select_text)
+ {
+ print_explain_message_line(output, explain_flags, is_analyze,
+ select_id /*select number*/,
+ select_type,
+ NULL, /* rows */
+ NULL);
+ }
+ else if (message)
{
List<Item> item_list;
Item *item_null= new (mem_root) Item_null(thd);
@@ -871,14 +902,20 @@ void Explain_select::print_explain_json(Explain_query *query,
bool started_cache= print_explain_json_cache(writer, is_analyze);
- if (message)
+ if (message ||
+ select_type == pushed_derived_text ||
+ select_type == pushed_select_text)
{
writer->add_member("query_block").start_object();
writer->add_member("select_id").add_ll(select_id);
add_linkage(writer);
writer->add_member("table").start_object();
- writer->add_member("message").add_str(message);
+ writer->add_member("message").add_str(select_type == pushed_derived_text ?
+ "Pushed derived" :
+ select_type == pushed_select_text ?
+ "Pushed select" :
+ message);
writer->end_object();
print_explain_json_for_children(query, writer, is_analyze);
@@ -1120,12 +1157,14 @@ void Explain_table_access::fill_key_str(String *key_str, bool is_json) const
- this is just used key length for ref/range
- for index_merge, it is a comma-separated list of lengths.
- for hash join, it is key_len:pseudo_key_len
+ - [tabular form only] rowid filter length is added after "|".
- The column looks identical in tabular and json forms. In JSON, we consider
- the column legacy, it is superceded by used_key_parts.
+ In JSON, we consider this column to be legacy, it is superceded by
+ used_key_parts.
*/
-void Explain_table_access::fill_key_len_str(String *key_len_str) const
+void Explain_table_access::fill_key_len_str(String *key_len_str,
+ bool is_json) const
{
bool is_hj= (type == JT_HASH || type == JT_HASH_NEXT ||
type == JT_HASH_RANGE || type == JT_HASH_INDEX_MERGE);
@@ -1153,6 +1192,14 @@ void Explain_table_access::fill_key_len_str(String *key_len_str) const
length= longlong10_to_str(hash_next_key.get_key_len(), buf, 10) - buf;
key_len_str->append(buf, length);
}
+
+ if (!is_json && rowid_filter)
+ {
+ key_len_str->append('|');
+ StringBuffer<64> filter_key_len;
+ rowid_filter->quick->print_key_len(&filter_key_len);
+ key_len_str->append(filter_key_len);
+ }
}
@@ -1238,7 +1285,18 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
}
/* `type` column */
- push_str(thd, &item_list, join_type_str[type]);
+ StringBuffer<64> join_type_buf;
+ if (rowid_filter == NULL)
+ push_str(thd, &item_list, join_type_str[type]);
+ else
+ {
+ join_type_buf.append(join_type_str[type]);
+ join_type_buf.append("|filter");
+ item_list.push_back(new (mem_root)
+ Item_string_sys(thd, join_type_buf.ptr(),
+ join_type_buf.length()),
+ mem_root);
+ }
/* `possible_keys` column */
StringBuffer<64> possible_keys_buf;
@@ -1250,6 +1308,14 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
/* `key` */
StringBuffer<64> key_str;
fill_key_str(&key_str, false);
+
+ if (rowid_filter)
+ {
+ key_str.append("|");
+ StringBuffer<64> rowid_key_str;
+ rowid_filter->quick->print_key(&rowid_key_str);
+ key_str.append(rowid_key_str);
+ }
if (key_str.length() > 0)
push_string(thd, &item_list, &key_str);
@@ -1258,7 +1324,7 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
/* `key_len` */
StringBuffer<64> key_len_str;
- fill_key_len_str(&key_len_str);
+ fill_key_len_str(&key_len_str, false);
if (key_len_str.length() > 0)
push_string(thd, &item_list, &key_len_str);
@@ -1281,17 +1347,27 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
push_string_list(thd, &item_list, ref_list, &ref_list_buf);
/* `rows` */
+ StringBuffer<64> rows_str;
if (rows_set)
{
+ rows_str.append_ulonglong((ulonglong)rows);
+
+ if (rowid_filter)
+ {
+ rows_str.append(" (");
+ rows_str.append_ulonglong((ulonglong) (round(rowid_filter->selectivity *
+ 100.0)));
+ rows_str.append("%)");
+ }
item_list.push_back(new (mem_root)
- Item_int(thd, (ulonglong) rows,
- MY_INT64_NUM_DECIMAL_DIGITS),
- mem_root);
+ Item_string_sys(thd, rows_str.ptr(),
+ rows_str.length()), mem_root);
}
else
item_list.push_back(item_null, mem_root);
/* `r_rows` */
+ StringBuffer<64> r_rows_str;
if (is_analyze)
{
if (!tracker.has_scans())
@@ -1301,8 +1377,20 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
else
{
double avg_rows= tracker.get_avg_rows();
- item_list.push_back(new (mem_root) Item_float(thd, avg_rows, 2),
- mem_root);
+ Item_float *fl= new (mem_root) Item_float(thd, avg_rows, 2);
+ String tmp;
+ String *res= fl->val_str(&tmp);
+ r_rows_str.append(res->ptr());
+ if (rowid_filter)
+ {
+ r_rows_str.append(" (");
+ r_rows_str.append_ulonglong(
+ (ulonglong) (rowid_filter->tracker->get_r_selectivity_pct() * 100.0));
+ r_rows_str.append("%)");
+ }
+ item_list.push_back(new (mem_root)
+ Item_string_sys(thd, r_rows_str.ptr(),
+ r_rows_str.length()), mem_root);
}
}
@@ -1366,6 +1454,15 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
extra_buf.append(STRING_WITH_LEN("Using filesort"));
}
+ if (rowid_filter)
+ {
+ if (first)
+ first= false;
+ else
+ extra_buf.append(STRING_WITH_LEN("; "));
+ extra_buf.append(STRING_WITH_LEN("Using rowid filter"));
+ }
+
item_list.push_back(new (mem_root)
Item_string_sys(thd, extra_buf.ptr(),
extra_buf.length()),
@@ -1554,6 +1651,29 @@ void add_json_keyset(Json_writer *writer, const char *elem_name,
}
+void Explain_rowid_filter::print_explain_json(Explain_query *query,
+ Json_writer *writer,
+ bool is_analyze)
+{
+ Json_writer_nesting_guard guard(writer);
+ writer->add_member("rowid_filter").start_object();
+ quick->print_json(writer);
+ writer->add_member("rows").add_ll(rows);
+ writer->add_member("selectivity_pct").add_double(selectivity * 100.0);
+ if (is_analyze)
+ {
+ writer->add_member("r_rows").add_double(tracker->get_container_elements());
+ writer->add_member("r_selectivity_pct").
+ add_double(tracker->get_r_selectivity_pct() * 100.0);
+ writer->add_member("r_buffer_size").
+ add_double((double) (tracker->get_container_buff_size()));
+ writer->add_member("r_filling_time_ms").
+ add_double(tracker->get_time_fill_container_ms());
+ }
+ writer->end_object(); // rowid_filter
+}
+
+
void Explain_table_access::print_explain_json(Explain_query *query,
Json_writer *writer,
bool is_analyze)
@@ -1626,7 +1746,7 @@ void Explain_table_access::print_explain_json(Explain_query *query,
/* `key_length` */
StringBuffer<64> key_len_str;
- fill_key_len_str(&key_len_str);
+ fill_key_len_str(&key_len_str, true);
if (key_len_str.length())
writer->add_member("key_length").add_str(key_len_str);
@@ -1651,6 +1771,11 @@ void Explain_table_access::print_explain_json(Explain_query *query,
if (!ref_list.is_empty())
print_json_array(writer, "ref", ref_list);
+ if (rowid_filter)
+ {
+ rowid_filter->print_explain_json(query, writer, is_analyze);
+ }
+
/* r_loops (not present in tabular output) */
if (is_analyze)
{
@@ -1683,8 +1808,10 @@ void Explain_table_access::print_explain_json(Explain_query *query,
if (op_tracker.get_loops())
{
- writer->add_member("r_total_time_ms").
- add_double(op_tracker.get_time_ms());
+ double total_time= op_tracker.get_time_ms();
+ if (rowid_filter)
+ total_time+= rowid_filter->tracker->get_time_fill_container_ms();
+ writer->add_member("r_total_time_ms").add_double(total_time);
}
}
diff --git a/sql/sql_explain.h b/sql/sql_explain.h
index 97fe07572cf..ce3f3ef06e1 100644
--- a/sql/sql_explain.h
+++ b/sql/sql_explain.h
@@ -331,6 +331,8 @@ public:
/////////////////////////////////////////////////////////////////////////////
extern const char *unit_operation_text[4];
+extern const char *pushed_derived_text;
+extern const char *pushed_select_text;
/*
Explain structure for a UNION.
@@ -586,6 +588,8 @@ class Explain_index_use : public Sql_alloc
{
char *key_name;
uint key_len;
+ char *filter_name;
+ uint filter_len;
public:
String_list key_parts_list;
@@ -598,12 +602,46 @@ public:
{
key_name= NULL;
key_len= (uint)-1;
+ filter_name= NULL;
+ filter_len= (uint)-1;
}
bool set(MEM_ROOT *root, KEY *key_name, uint key_len_arg);
bool set_pseudo_key(MEM_ROOT *root, const char *key_name);
inline const char *get_key_name() const { return key_name; }
inline uint get_key_len() const { return key_len; }
+ //inline const char *get_filter_name() const { return filter_name; }
+};
+
+
+/*
+ Query Plan data structure for Rowid filter.
+*/
+class Explain_rowid_filter : public Sql_alloc
+{
+public:
+ /* Quick select used to collect the rowids into filter */
+ Explain_quick_select *quick;
+
+ /* How many rows the above quick select is expected to return */
+ ha_rows rows;
+
+ /* Expected selectivity for the filter */
+ double selectivity;
+
+ /* Tracker with the information about how rowid filter is executed */
+ Rowid_filter_tracker *tracker;
+
+ void print_explain_json(Explain_query *query, Json_writer *writer,
+ bool is_analyze);
+
+ /*
+ TODO:
+ Here should be ANALYZE members:
+ - r_rows for the quick select
+ - An object that tracked the table access time
+ - real selectivity of the filter.
+ */
};
@@ -673,6 +711,7 @@ public:
void print_json(Json_writer *writer, bool is_analyze);
};
+
/*
EXPLAIN data structure for a single JOIN_TAB.
*/
@@ -692,7 +731,8 @@ public:
cache_cond(NULL),
pushed_index_cond(NULL),
sjm_nest(NULL),
- pre_join_sort(NULL)
+ pre_join_sort(NULL),
+ rowid_filter(NULL)
{}
~Explain_table_access() { delete sjm_nest; }
@@ -799,6 +839,8 @@ public:
Exec_time_tracker op_tracker;
Table_access_tracker jbuf_tracker;
+ Explain_rowid_filter *rowid_filter;
+
int print_explain(select_result_sink *output, uint8 explain_flags,
bool is_analyze,
uint select_id, const char *select_type,
@@ -809,7 +851,7 @@ public:
private:
void append_tag_name(String *str, enum explain_extra_tag tag);
void fill_key_str(String *key_str, bool is_json) const;
- void fill_key_len_str(String *key_len_str) const;
+ void fill_key_len_str(String *key_len_str, bool is_json) const;
double get_r_filtered();
void tag_to_json(Json_writer *writer, enum explain_extra_tag tag);
};
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 967b85496ac..8447d5bea7d 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -433,8 +433,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
/* Always read all columns */
table->read_set= &table->s->all_set;
- if (table->vcol_set)
- table->vcol_set= &table->s->all_set;
/* Restore the state. */
thd->set_open_tables(backup_open_tables);
@@ -1194,10 +1192,10 @@ void mysql_ha_flush(THD *thd)
@note Broadcasts refresh if it closed a table with old version.
*/
-void mysql_ha_cleanup(THD *thd)
+void mysql_ha_cleanup_no_free(THD *thd)
{
SQL_HANDLER *hash_tables;
- DBUG_ENTER("mysql_ha_cleanup");
+ DBUG_ENTER("mysql_ha_cleanup_no_free");
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
@@ -1205,9 +1203,15 @@ void mysql_ha_cleanup(THD *thd)
if (hash_tables->table)
mysql_ha_close_table(hash_tables);
}
+ DBUG_VOID_RETURN;
+}
- my_hash_free(&thd->handler_tables_hash);
+void mysql_ha_cleanup(THD *thd)
+{
+ DBUG_ENTER("mysql_ha_cleanup");
+ mysql_ha_cleanup_no_free(thd);
+ my_hash_free(&thd->handler_tables_hash);
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_handler.h b/sql/sql_handler.h
index 8d9dc5a9af2..4ac0d09e7a1 100644
--- a/sql/sql_handler.h
+++ b/sql/sql_handler.h
@@ -73,6 +73,7 @@ bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes, const char *,
void mysql_ha_flush(THD *thd);
void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables);
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables);
+void mysql_ha_cleanup_no_free(THD *thd);
void mysql_ha_cleanup(THD *thd);
void mysql_ha_set_explicit_lock_duration(THD *thd);
void mysql_ha_rm_temporary_tables(THD *thd);
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index 5e0420652e4..81e5ad48e7e 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -87,7 +87,7 @@ enum enum_used_fields
static bool init_fields(THD *thd, TABLE_LIST *tables,
struct st_find_field *find_fields, uint count)
{
- Name_resolution_context *context= &thd->lex->select_lex.context;
+ Name_resolution_context *context= &thd->lex->first_select_lex()->context;
DBUG_ENTER("init_fields");
context->resolve_in_table_list_only(tables);
for (; count-- ; find_fields++)
@@ -719,10 +719,11 @@ static bool mysqld_help_internal(THD *thd, const char *mask)
Init tables and fields to be usable from items
tables do not contain VIEWs => we can pass 0 as conds
*/
- thd->lex->select_lex.context.table_list=
- thd->lex->select_lex.context.first_name_resolution_table= &tables[0];
- if (setup_tables(thd, &thd->lex->select_lex.context,
- &thd->lex->select_lex.top_join_list,
+ thd->lex->first_select_lex()->context.table_list=
+ thd->lex->first_select_lex()->context.first_name_resolution_table=
+ &tables[0];
+ if (setup_tables(thd, &thd->lex->first_select_lex()->context,
+ &thd->lex->first_select_lex()->top_join_list,
tables, leaves, FALSE, FALSE))
goto error;
memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields));
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 09d15133e6d..820a305059f 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -82,6 +82,10 @@
#include "debug_sync.h"
+#ifdef WITH_WSREP
+#include "wsrep_trans_observer.h" /* wsrep_start_transction() */
+#endif /* WITH_WSREP */
+
#ifndef EMBEDDED_LIBRARY
static bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
TABLE_LIST *table_list);
@@ -241,7 +245,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
}
else
{ // Part field list
- SELECT_LEX *select_lex= &thd->lex->select_lex;
+ SELECT_LEX *select_lex= thd->lex->first_select_lex();
Name_resolution_context *context= &select_lex->context;
Name_resolution_context_state ctx_state;
int res;
@@ -273,7 +277,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
/* Restore the current context. */
ctx_state.restore_state(context, table_list);
- thd->lex->select_lex.no_wrap_view_item= FALSE;
+ thd->lex->first_select_lex()->no_wrap_view_item= FALSE;
if (res)
DBUG_RETURN(-1);
@@ -547,10 +551,10 @@ bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
If this goes ok, the tickets are cloned and added to the list of granted
locks held by the handler thread.
*/
- if (thd->global_read_lock.can_acquire_protection())
+ if (thd->has_read_only_protection())
DBUG_RETURN(TRUE);
- protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ protection_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_DML,
MDL_STATEMENT);
if (thd->mdl_context.acquire_lock(&protection_request,
@@ -640,7 +644,7 @@ create_insert_stmt_from_insert_delayed(THD *thd, String *buf)
if (buf->append(thd->query()) ||
buf->replace(thd->lex->keyword_delayed_begin_offset,
thd->lex->keyword_delayed_end_offset -
- thd->lex->keyword_delayed_begin_offset, 0))
+ thd->lex->keyword_delayed_begin_offset, NULL, 0))
return 1;
return 0;
}
@@ -657,7 +661,7 @@ static void save_insert_query_plan(THD* thd, TABLE_LIST *table_list)
bool skip= MY_TEST(table_list->view);
/* Save subquery children */
- for (SELECT_LEX_UNIT *unit= thd->lex->select_lex.first_inner_unit();
+ for (SELECT_LEX_UNIT *unit= thd->lex->first_select_lex()->first_inner_unit();
unit;
unit= unit->next_unit())
{
@@ -777,7 +781,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
/* mysql_prepare_insert sets table_list->table if it was not set */
table= table_list->table;
- context= &thd->lex->select_lex.context;
+ context= &thd->lex->first_select_lex()->context;
/*
These three asserts test the hypothesis that the resetting of the name
resolution context below is not necessary at all since the list of local
@@ -900,6 +904,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
using_bulk_insert= 1;
table->file->ha_start_bulk_insert(values_list.elements);
}
+ else
+ table->file->ha_reset_copy_info();
}
thd->abort_on_warning= !ignore && thd->is_strict_mode();
@@ -1079,7 +1085,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
} while (bulk_parameters_iterations(thd));
values_loop_end:
- free_underlaid_joins(thd, &thd->lex->select_lex);
+ free_underlaid_joins(thd, thd->lex->first_select_lex());
joins_freed= TRUE;
/*
@@ -1103,11 +1109,23 @@ values_loop_end:
auto_inc values from the delayed_insert thread as they share TABLE.
*/
table->file->ha_release_auto_increment();
- if (using_bulk_insert && unlikely(table->file->ha_end_bulk_insert()) &&
- !error)
+ if (using_bulk_insert)
+ {
+ if (unlikely(table->file->ha_end_bulk_insert()) &&
+ !error)
+ {
+ table->file->print_error(my_errno,MYF(0));
+ error=1;
+ }
+ }
+ /* Get better status from handler if handler supports it */
+ if (table->file->copy_info.records)
{
- table->file->print_error(my_errno,MYF(0));
- error=1;
+ DBUG_ASSERT(info.copied >= table->file->copy_info.copied);
+ info.touched= table->file->copy_info.touched;
+ info.copied= table->file->copy_info.copied;
+ info.deleted= table->file->copy_info.deleted;
+ info.updated= table->file->copy_info.updated;
}
if (duplic != DUP_ERROR || ignore)
{
@@ -1230,8 +1248,12 @@ values_loop_end:
retval= thd->lex->explain->send_explain(thd);
goto abort;
}
- if ((iteration * values_list.elements) == 1 && (!(thd->variables.option_bits & OPTION_WARNINGS) ||
- !thd->cuted_fields))
+ DBUG_PRINT("info", ("touched: %llu copied: %llu updated: %llu deleted: %llu",
+ (ulonglong) info.touched, (ulonglong) info.copied,
+ (ulonglong) info.updated, (ulonglong) info.deleted));
+
+ if ((iteration * values_list.elements) == 1 &&
+ (!(thd->variables.option_bits & OPTION_WARNINGS) || !thd->cuted_fields))
{
my_ok(thd, info.copied + info.deleted +
((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
@@ -1272,7 +1294,7 @@ abort:
table->file->ha_release_auto_increment();
if (!joins_freed)
- free_underlaid_joins(thd, &thd->lex->select_lex);
+ free_underlaid_joins(thd, thd->lex->first_select_lex());
thd->abort_on_warning= 0;
DBUG_RETURN(retval);
}
@@ -1302,7 +1324,7 @@ abort:
static bool check_view_insertability(THD * thd, TABLE_LIST *view)
{
- uint num= view->view->select_lex.item_list.elements;
+ uint num= view->view->first_select_lex()->item_list.elements;
TABLE *table= view->table;
Field_translator *trans_start= view->field_translation,
*trans_end= trans_start + num;
@@ -1402,10 +1424,12 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
than INSERT.
*/
- if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
- &thd->lex->select_lex.top_join_list,
+ if (setup_tables_and_check_access(thd,
+ &thd->lex->first_select_lex()->context,
+ &thd->lex->first_select_lex()->
+ top_join_list,
table_list,
- thd->lex->select_lex.leaf_tables,
+ thd->lex->first_select_lex()->leaf_tables,
select_insert, INSERT_ACL, SELECT_ACL,
TRUE))
DBUG_RETURN(TRUE);
@@ -1413,7 +1437,7 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
if (insert_into_view && !fields.elements)
{
thd->lex->empty_field_list_on_rset= 1;
- if (!thd->lex->select_lex.leaf_tables.head()->table ||
+ if (!thd->lex->first_select_lex()->leaf_tables.head()->table ||
table_list->is_multitable())
{
my_error(ER_VIEW_NO_INSERT_FIELD_LIST, MYF(0),
@@ -1487,7 +1511,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
enum_duplicates duplic, COND **where,
bool select_insert)
{
- SELECT_LEX *select_lex= &thd->lex->select_lex;
+ SELECT_LEX *select_lex= thd->lex->first_select_lex();
Name_resolution_context *context= &select_lex->context;
Name_resolution_context_state ctx_state;
bool insert_into_view= (table_list->view != 0);
@@ -1737,7 +1761,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
*/
if (info->ignore)
{
- table->file->print_error(error, MYF(ME_JUST_WARNING));
+ table->file->print_error(error, MYF(ME_WARNING));
goto ok_or_after_trg_err; /* Ignoring a not fatal error, return 0 */
}
goto err;
@@ -1756,10 +1780,8 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
was used. This ensures that we don't get a problem when the
whole range of the key has been used.
*/
- if (info->handle_duplicates == DUP_REPLACE &&
- table->next_number_field &&
- key_nr == table->s->next_number_index &&
- (insert_id_for_cur_row > 0))
+ if (info->handle_duplicates == DUP_REPLACE && table->next_number_field &&
+ key_nr == table->s->next_number_index && insert_id_for_cur_row > 0)
goto err;
if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
{
@@ -1862,7 +1884,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
{
if (!(thd->variables.old_behavior &
OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE))
- table->file->print_error(error, MYF(ME_JUST_WARNING));
+ table->file->print_error(error, MYF(ME_WARNING));
goto ok_or_after_trg_err;
}
goto err;
@@ -2044,7 +2066,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
goto err;
if (!(thd->variables.old_behavior &
OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE))
- table->file->print_error(error, MYF(ME_JUST_WARNING));
+ table->file->print_error(error, MYF(ME_WARNING));
table->file->restore_auto_increment(prev_insert_id);
goto ok_or_after_trg_err;
}
@@ -2206,11 +2228,11 @@ public:
mysql_mutex_init(key_delayed_insert_mutex, &mutex, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_delayed_insert_cond, &cond, NULL);
mysql_cond_init(key_delayed_insert_cond_client, &cond_client, NULL);
- mysql_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_delayed_insert);
delayed_insert_threads++;
+ mysql_mutex_unlock(&LOCK_delayed_insert);
delayed_lock= global_system_variables.low_priority_updates ?
TL_WRITE_LOW_PRIORITY : TL_WRITE;
- mysql_mutex_unlock(&LOCK_thread_count);
DBUG_VOID_RETURN;
}
~Delayed_insert()
@@ -2222,21 +2244,15 @@ public:
if (table)
{
close_thread_tables(&thd);
- thd.mdl_context.release_transactional_locks();
+ thd.mdl_context.release_transactional_locks(&thd);
}
mysql_mutex_destroy(&mutex);
mysql_cond_destroy(&cond);
mysql_cond_destroy(&cond_client);
- /*
- We could use unlink_not_visible_threads() here, but as
- delayed_insert_threads also needs to be protected by
- the LOCK_thread_count mutex, we open code this.
- */
- mysql_mutex_lock(&LOCK_thread_count);
- thd.unlink(); // Must be unlinked under lock
+ server_threads.erase(&thd);
+ mysql_mutex_assert_owner(&LOCK_delayed_insert);
delayed_insert_threads--;
- mysql_mutex_unlock(&LOCK_thread_count);
my_free(thd.query());
thd.security_ctx->user= 0;
@@ -2383,7 +2399,7 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
di->thd.set_db(&table_list->db);
di->thd.set_query(my_strndup(table_list->table_name.str,
table_list->table_name.length,
- MYF(MY_WME | ME_FATALERROR)),
+ MYF(MY_WME | ME_FATAL)),
table_list->table_name.length, system_charset_info);
if (di->thd.db.str == NULL || di->thd.query() == NULL)
{
@@ -2396,9 +2412,12 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
di->table_list.alias.str= di->table_list.table_name.str= di->thd.query();
di->table_list.alias.length= di->table_list.table_name.length= di->thd.query_length();
di->table_list.db= di->thd.db;
- /* We need the tickets so that they can be cloned in handle_delayed_insert */
- di->grl_protection.init(MDL_key::GLOBAL, "", "",
- MDL_INTENTION_EXCLUSIVE, MDL_STATEMENT);
+ /*
+ We need the tickets so that they can be cloned in
+ handle_delayed_insert
+ */
+ di->grl_protection.init(MDL_key::BACKUP, "", "",
+ MDL_BACKUP_DML, MDL_STATEMENT);
di->grl_protection.ticket= grl_protection_request->ticket;
init_mdl_requests(&di->table_list);
di->table_list.mdl_request.ticket= table_list->mdl_request.ticket;
@@ -2415,7 +2434,7 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
mysql_mutex_unlock(&di->mutex);
di->unlock();
delete di;
- my_error(ER_CANT_CREATE_THREAD, MYF(ME_FATALERROR), error);
+ my_error(ER_CANT_CREATE_THREAD, MYF(ME_FATAL), error);
goto end_create;
}
@@ -2473,10 +2492,12 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
}
/* Unlock the delayed insert object after its last access. */
di->unlock();
- DBUG_RETURN((table_list->table == NULL));
+ DBUG_PRINT("exit", ("table_list->table: %p", table_list->table));
+ DBUG_RETURN(thd->is_error());
end_create:
mysql_mutex_unlock(&LOCK_delayed_create);
+ DBUG_PRINT("exit", ("is_error: %d", thd->is_error()));
DBUG_RETURN(thd->is_error());
}
@@ -2531,24 +2552,27 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
if (thd.killed)
{
/*
- Copy the error message. Note that we don't treat fatal
- errors in the delayed thread as fatal errors in the
- main thread. If delayed thread was killed, we don't
- want to send "Server shutdown in progress" in the
- INSERT THREAD.
-
- The thread could be killed with an error message if
- di->handle_inserts() or di->open_and_lock_table() fails.
- The thread could be killed without an error message if
- killed using THD::notify_shared_lock() or
- kill_delayed_threads_for_table().
+ Check how the insert thread was killed. If it was killed
+ by FLUSH TABLES which calls kill_delayed_threads_for_table(),
+ then is_error is not set.
+ In this case, return without setting an error,
+ which means that the insert will be converted to a normal insert.
*/
- if (!thd.is_error())
- my_message(ER_QUERY_INTERRUPTED, ER_THD(&thd, ER_QUERY_INTERRUPTED),
- MYF(0));
- else
+ if (thd.is_error())
+ {
+ /*
+ Copy the error message. Note that we don't treat fatal
+ errors in the delayed thread as fatal errors in the
+ main thread. If delayed thread was killed, we don't
+ want to send "Server shutdown in progress" in the
+ INSERT THREAD.
+
+ The thread could be killed with an error message if
+ di->handle_inserts() or di->open_and_lock_table() fails.
+ */
my_message(thd.get_stmt_da()->sql_errno(),
thd.get_stmt_da()->message(), MYF(0));
+ }
goto error;
}
}
@@ -2630,10 +2654,6 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
share->default_fields)
{
bool error_reported= FALSE;
- if (unlikely(!(copy->def_vcol_set=
- (MY_BITMAP*) alloc_root(client_thd->mem_root,
- sizeof(MY_BITMAP)))))
- goto error;
if (unlikely(parse_vcol_defs(client_thd, client_thd->mem_root, copy,
&error_reported,
VCOL_INIT_DEPENDENCY_FAILURE_IS_WARNING)))
@@ -2653,15 +2673,6 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
copy->def_write_set.bitmap= ((my_bitmap_map*)
(bitmap + share->column_bitmap_size));
bitmaps_used= 2;
- if (share->virtual_fields)
- {
- my_bitmap_init(copy->def_vcol_set,
- (my_bitmap_map*) (bitmap +
- bitmaps_used*share->column_bitmap_size),
- share->fields, FALSE);
- bitmaps_used++;
- copy->vcol_set= copy->def_vcol_set;
- }
if (share->default_fields || share->default_expressions)
{
my_bitmap_init(&copy->has_value_set,
@@ -2859,23 +2870,7 @@ void kill_delayed_threads(void)
mysql_mutex_lock(&di->thd.LOCK_thd_kill);
if (di->thd.killed < KILL_CONNECTION)
di->thd.set_killed_no_mutex(KILL_CONNECTION);
- if (di->thd.mysys_var)
- {
- mysql_mutex_lock(&di->thd.mysys_var->mutex);
- if (di->thd.mysys_var->current_cond)
- {
- /*
- We need the following test because the main mutex may be locked
- in handle_delayed_insert()
- */
- if (&di->mutex != di->thd.mysys_var->current_mutex)
- mysql_mutex_lock(di->thd.mysys_var->current_mutex);
- mysql_cond_broadcast(di->thd.mysys_var->current_cond);
- if (&di->mutex != di->thd.mysys_var->current_mutex)
- mysql_mutex_unlock(di->thd.mysys_var->current_mutex);
- }
- mysql_mutex_unlock(&di->thd.mysys_var->mutex);
- }
+ di->thd.abort_current_cond_wait(false);
mysql_mutex_unlock(&di->thd.LOCK_thd_kill);
}
mysql_mutex_unlock(&LOCK_delayed_insert); // For unlink from list
@@ -3000,7 +2995,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
pthread_detach_this_thread();
/* Add thread to THD list so that's it's visible in 'show processlist' */
thd->set_start_time();
- add_to_active_threads(thd);
+ server_threads.insert(thd);
if (abort_loop)
thd->set_killed(KILL_CONNECTION);
else
@@ -3158,11 +3153,30 @@ pthread_handler_t handle_delayed_insert(void *arg)
mysql_mutex_unlock(&di->thd.mysys_var->mutex);
mysql_mutex_lock(&di->mutex);
}
+
+ /*
+ The code depends on that the following ASSERT always hold.
+ I don't want to accidently introduce and bugs in the following code
+ in this commit, so I leave the small cleaning up of the code to
+ a future commit
+ */
+ DBUG_ASSERT(thd->lock || di->stacked_inserts == 0);
+
DBUG_PRINT("delayed",
- ("thd->killed: %d di->tables_in_use: %d thd->lock: %d",
- thd->killed, di->tables_in_use, thd->lock != 0));
+ ("thd->killed: %d di->status: %d di->stacked_insert: %d di->tables_in_use: %d thd->lock: %d",
+ thd->killed, di->status, di->stacked_inserts, di->tables_in_use, thd->lock != 0));
- if (di->tables_in_use && ! thd->lock && !thd->killed)
+ /*
+ This is used to test see what happens if killed is sent before
+ we have time to handle the insert requests.
+ */
+ DBUG_EXECUTE_IF("write_delay_wakeup",
+ if (!thd->killed && di->stacked_inserts)
+ my_sleep(500000);
+ );
+
+ if (di->tables_in_use && ! thd->lock &&
+ (!thd->killed || di->stacked_inserts))
{
/*
Request for new delayed insert.
@@ -3336,7 +3350,7 @@ bool Delayed_insert::handle_inserts(void)
or if another thread is removing the current table definition
from the table cache.
*/
- my_error(ER_DELAYED_CANT_CHANGE_LOCK, MYF(ME_FATALERROR | ME_NOREFRESH),
+ my_error(ER_DELAYED_CANT_CHANGE_LOCK, MYF(ME_FATAL | ME_ERROR_LOG),
table->s->table_name.str);
goto err;
}
@@ -3512,7 +3526,7 @@ bool Delayed_insert::handle_inserts(void)
{
/* This is not known to happen. */
my_error(ER_DELAYED_CANT_CHANGE_LOCK,
- MYF(ME_FATALERROR | ME_NOREFRESH),
+ MYF(ME_FATAL | ME_ERROR_LOG),
table->s->table_name.str);
goto err;
}
@@ -3610,7 +3624,7 @@ bool Delayed_insert::handle_inserts(void)
bool mysql_insert_select_prepare(THD *thd)
{
LEX *lex= thd->lex;
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
DBUG_ENTER("mysql_insert_select_prepare");
@@ -3699,7 +3713,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
select, LEX::current_select should point to the first select while
we are fixing fields from insert list.
*/
- lex->current_select= &lex->select_lex;
+ lex->current_select= lex->first_select_lex();
res= (setup_fields(thd, Ref_ptr_array(),
values, MARK_COLUMNS_READ, 0, NULL, 0) ||
@@ -3716,7 +3730,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
if (info.handle_duplicates == DUP_UPDATE && !res)
{
- Name_resolution_context *context= &lex->select_lex.context;
+ Name_resolution_context *context= &lex->first_select_lex()->context;
Name_resolution_context_state ctx_state;
/* Save the state of the current name resolution context. */
@@ -3726,7 +3740,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
table_list->next_local= 0;
context->resolve_in_table_list_only(table_list);
- lex->select_lex.no_wrap_view_item= TRUE;
+ lex->first_select_lex()->no_wrap_view_item= TRUE;
res= res ||
check_update_fields(thd, context->table_list,
*info.update_fields, *info.update_values,
@@ -3737,22 +3751,26 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
*/
true,
&map);
- lex->select_lex.no_wrap_view_item= FALSE;
+ lex->first_select_lex()->no_wrap_view_item= FALSE;
/*
- When we are not using GROUP BY and there are no ungrouped aggregate functions
- we can refer to other tables in the ON DUPLICATE KEY part.
- We use next_name_resolution_table descructively, so check it first (views?)
+ When we are not using GROUP BY and there are no ungrouped
+ aggregate functions we can refer to other tables in the ON
+ DUPLICATE KEY part. We use next_name_resolution_table
+ descructively, so check it first (views?)
*/
DBUG_ASSERT (!table_list->next_name_resolution_table);
- if (lex->select_lex.group_list.elements == 0 &&
- !lex->select_lex.with_sum_func)
+ if (lex->first_select_lex()->group_list.elements == 0 &&
+ !lex->first_select_lex()->with_sum_func)
+ {
/*
- We must make a single context out of the two separate name resolution contexts :
- the INSERT table and the tables in the SELECT part of INSERT ... SELECT.
- To do that we must concatenate the two lists
+ We must make a single context out of the two separate name
+ resolution contexts : the INSERT table and the tables in the
+ SELECT part of INSERT ... SELECT. To do that we must
+ concatenate the two lists
*/
table_list->next_name_resolution_table=
ctx_state.get_first_name_resolution_table();
+ }
res= res || setup_fields(thd, Ref_ptr_array(), *info.update_values,
MARK_COLUMNS_READ, 0, NULL, 0);
@@ -3858,9 +3876,9 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
void
DESCRIPTION
- If the result table is the same as one of the source tables (INSERT SELECT),
- the result table is not finally prepared at the join prepair phase.
- Do the final preparation now.
+ If the result table is the same as one of the source tables
+ (INSERT SELECT), the result table is not finally prepared at the
+ join prepair phase. Do the final preparation now.
RETURN
0 OK
@@ -4000,10 +4018,13 @@ bool select_insert::prepare_eof()
DBUG_PRINT("enter", ("trans_table=%d, table_type='%s'",
trans_table, table->file->table_type()));
- error= (IF_WSREP((thd->wsrep_conflict_state == MUST_ABORT ||
- thd->wsrep_conflict_state == CERT_FAILURE) ? -1 :, )
- (thd->locked_tables_mode <= LTM_LOCK_TABLES ?
- table->file->ha_end_bulk_insert() : 0));
+#ifdef WITH_WSREP
+ error= (thd->wsrep_cs().current_error()) ? -1 :
+ (thd->locked_tables_mode <= LTM_LOCK_TABLES) ?
+#else
+ error= (thd->locked_tables_mode <= LTM_LOCK_TABLES) ?
+#endif /* WITH_WSREP */
+ table->file->ha_end_bulk_insert() : 0;
if (likely(!error) && unlikely(thd->is_error()))
error= thd->get_stmt_da()->sql_errno();
@@ -4188,9 +4209,9 @@ void select_insert::abort_result_set()
Field *Item::create_field_for_create_select(TABLE *table)
{
- Field *def_field, *tmp_field;
- return ::create_tmp_field(table->in_use, table, this, type(),
- (Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0);
+ static Tmp_field_param param(false, false, false, false);
+ Tmp_field_src src;
+ return create_tmp_field_ex(table, &src, &param);
}
@@ -4233,10 +4254,8 @@ Field *Item::create_field_for_create_select(TABLE *table)
@retval 0 Error
*/
-TABLE *select_create::create_table_from_items(THD *thd,
- List<Item> *items,
- MYSQL_LOCK **lock,
- TABLEOP_HOOKS *hooks)
+TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items,
+ MYSQL_LOCK **lock, TABLEOP_HOOKS *hooks)
{
TABLE tmp_table; // Used during 'Create_field()'
TABLE_SHARE share;
@@ -4259,7 +4278,7 @@ TABLE *select_create::create_table_from_items(THD *thd,
if (!opt_explicit_defaults_for_timestamp)
promote_first_timestamp_column(&alter_info->create_list);
- if (create_info->vers_fix_system_fields(thd, alter_info, *create_table))
+ if (create_info->fix_create_fields(thd, alter_info, *create_table))
DBUG_RETURN(NULL);
while ((item=it++))
@@ -4298,10 +4317,10 @@ TABLE *select_create::create_table_from_items(THD *thd,
alter_info->create_list.push_back(cr_field, thd->mem_root);
}
- if (create_info->vers_check_system_fields(thd, alter_info,
- create_table->table_name,
- create_table->db,
- select_field_count))
+ if (create_info->check_fields(thd, alter_info,
+ create_table->table_name,
+ create_table->db,
+ select_field_count))
DBUG_RETURN(NULL);
DEBUG_SYNC(thd,"create_table_select_before_create");
@@ -4522,8 +4541,6 @@ select_create::prepare(List<Item> &_values, SELECT_LEX_UNIT *u)
thd->binlog_start_trans_and_stmt();
}
- DEBUG_SYNC(thd,"create_table_select_before_check_if_exists");
-
if (!(table= create_table_from_items(thd, &values, &extra_lock, hook_ptr)))
/* abort() deletes table */
DBUG_RETURN(-1);
@@ -4641,9 +4658,16 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
/* suppress_use */ FALSE,
errcode) > 0;
}
-
- ha_fake_trx_id(thd);
-
+#ifdef WITH_WSREP
+ if (thd->wsrep_trx().active())
+ {
+ WSREP_DEBUG("transaction already started for CTAS");
+ }
+ else
+ {
+ wsrep_start_transaction(thd, thd->wsrep_next_trx_id());
+ }
+#endif
return result;
}
@@ -4701,10 +4725,19 @@ bool select_create::send_eof()
if (!table->s->tmp_table)
{
#ifdef WITH_WSREP
- if (WSREP_ON)
+ if (WSREP(thd) &&
+ table->file->ht->db_type == DB_TYPE_INNODB)
{
+ if (thd->wsrep_trx_id() == WSREP_UNDEFINED_TRX_ID)
+ {
+ wsrep_start_transaction(thd, thd->wsrep_next_trx_id());
+ }
+ DBUG_ASSERT(thd->wsrep_trx_id() != WSREP_UNDEFINED_TRX_ID);
+ WSREP_DEBUG("CTAS key append for trx: %" PRIu64 " thd %llu query %lld ",
+ thd->wsrep_trx_id(), thd->thread_id, thd->query_id);
+
/*
- append table level exclusive key for CTAS
+ append table level exclusive key for CTAS
*/
wsrep_key_arr_t key_arr= {0, 0};
wsrep_prepare_keys_for_isolation(thd,
@@ -4712,15 +4745,11 @@ bool select_create::send_eof()
create_table->table_name.str,
table_list,
&key_arr);
- int rcode = wsrep->append_key(
- wsrep,
- &thd->wsrep_ws_handle,
- key_arr.keys, //&wkey,
- key_arr.keys_len,
- WSREP_KEY_EXCLUSIVE,
- false);
+ int rcode= wsrep_thd_append_key(thd, key_arr.keys, key_arr.keys_len,
+ WSREP_SERVICE_KEY_EXCLUSIVE);
wsrep_keys_free(&key_arr);
- if (rcode) {
+ if (rcode)
+ {
DBUG_PRINT("wsrep", ("row key failed: %d", rcode));
WSREP_ERROR("Appending table key for CTAS failed: %s, %d",
(wsrep_thd_query(thd)) ?
@@ -4729,22 +4758,22 @@ bool select_create::send_eof()
DBUG_RETURN(true);
}
/* If commit fails, we should be able to reset the OK status. */
- thd->get_stmt_da()->set_overwrite_status(TRUE);
+ thd->get_stmt_da()->set_overwrite_status(true);
}
#endif /* WITH_WSREP */
trans_commit_stmt(thd);
if (!(thd->variables.option_bits & OPTION_GTID_BEGIN))
trans_commit_implicit(thd);
#ifdef WITH_WSREP
- if (WSREP_ON)
+ if (WSREP(thd))
{
thd->get_stmt_da()->set_overwrite_status(FALSE);
mysql_mutex_lock(&thd->LOCK_thd_data);
- if (thd->wsrep_conflict_state != NO_CONFLICT)
+ if (wsrep_current_error(thd))
{
- WSREP_DEBUG("select_create commit failed, thd: %lld err: %d %s",
- (longlong) thd->thread_id, thd->wsrep_conflict_state,
- thd->query());
+ WSREP_DEBUG("select_create commit failed, thd: %llu err: %s %s",
+ thd->thread_id,
+ wsrep_thd_transaction_state_str(thd), WSREP_QUERY(thd));
mysql_mutex_unlock(&thd->LOCK_thd_data);
abort_result_set();
DBUG_RETURN(true);
diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc
index 86df1d32bd9..f072a675e31 100644
--- a/sql/sql_join_cache.cc
+++ b/sql/sql_join_cache.cc
@@ -2317,6 +2317,8 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
if ((rc= join_tab_execution_startup(join_tab)) < 0)
goto finish2;
+ join_tab->build_range_rowid_filter_if_needed();
+
/* Prepare to retrieve all records of the joined table */
if (unlikely((error= join_tab_scan->open())))
{
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 4f90fb28b45..386d179322e 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -31,8 +31,11 @@
#include "sql_select.h"
#include "sql_cte.h"
#include "sql_signal.h"
+#include "sql_truncate.h" // Sql_cmd_truncate_table
+#include "sql_admin.h" // Sql_cmd_analyze/Check..._table
#include "sql_partition.h"
-
+#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part
+#include "event_parse_data.h"
void LEX::parse_error(uint err_number)
{
@@ -174,7 +177,7 @@ init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex)
{
TABLE_LIST *table_list;
Table_ident *table_ident;
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
Name_resolution_context *context= &select_lex->context;
/*
We will call the parser to create a part_info struct based on the
@@ -637,6 +640,30 @@ void Lex_input_stream::reduce_digest_token(uint token_left, uint token_right)
}
}
+/**
+ lex starting operations for builtin select collected together
+*/
+
+void SELECT_LEX::lex_start(LEX *plex)
+{
+ SELECT_LEX_UNIT *unit= &plex->unit;
+ /* 'parent_lex' is used in init_query() so it must be before it. */
+ parent_lex= plex;
+ init_query();
+ master= unit;
+ prev= &unit->slave;
+ link_next= slave= next= 0;
+ link_prev= (st_select_lex_node**)&(plex->all_selects_list);
+ DBUG_ASSERT(!group_list_ptrs);
+ select_number= 1;
+ in_sum_expr=0;
+ ftfunc_list_alloc.empty();
+ ftfunc_list= &ftfunc_list_alloc;
+ group_list.empty();
+ order_list.empty();
+ gorder_list.empty();
+}
+
void lex_start(THD *thd)
{
DBUG_ENTER("lex_start");
@@ -661,18 +688,19 @@ void LEX::start(THD *thd_arg)
DBUG_ASSERT(!explain);
+ builtin_select.lex_start(this);
+ lex_options= 0;
context_stack.empty();
+ //empty select_stack
+ select_stack_top= 0;
unit.init_query();
- current_select_number= 1;
- select_lex.linkage= UNSPECIFIED_TYPE;
- /* 'parent_lex' is used in init_query() so it must be before it. */
- select_lex.parent_lex= this;
- select_lex.init_query();
+ current_select_number= 0;
curr_with_clause= 0;
with_clauses_list= 0;
with_clauses_list_last_next= &with_clauses_list;
clone_spec_offset= 0;
create_view= NULL;
+ field_list.empty();
value_list.empty();
update_list.empty();
set_var_list.empty();
@@ -682,21 +710,12 @@ void LEX::start(THD *thd_arg)
with_persistent_for_clause= FALSE;
column_list= NULL;
index_list= NULL;
- prepared_stmt_params.empty();
+ prepared_stmt.lex_start();
auxiliary_table_list.empty();
unit.next= unit.master= unit.link_next= unit.return_to= 0;
unit.prev= unit.link_prev= 0;
- unit.slave= current_select= all_selects_list= &select_lex;
- select_lex.master= &unit;
- select_lex.prev= &unit.slave;
- select_lex.link_next= select_lex.slave= select_lex.next= 0;
- select_lex.link_prev= (st_select_lex_node**)&(all_selects_list);
- select_lex.options= 0;
- select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
- select_lex.init_order();
- select_lex.group_list.empty();
- if (select_lex.group_list_ptrs)
- select_lex.group_list_ptrs->clear();
+ unit.slave= current_select= all_selects_list= &builtin_select;
+ sql_cache= LEX::SQL_CACHE_UNSPECIFIED;
describe= 0;
analyze_stmt= 0;
explain_json= false;
@@ -705,14 +724,7 @@ void LEX::start(THD *thd_arg)
safe_to_cache_query= 1;
parsing_options.reset();
empty_field_list_on_rset= 0;
- select_lex.select_number= 1;
part_info= 0;
- select_lex.in_sum_expr=0;
- select_lex.ftfunc_list_alloc.empty();
- select_lex.ftfunc_list= &select_lex.ftfunc_list_alloc;
- select_lex.group_list.empty();
- select_lex.order_list.empty();
- select_lex.gorder_list.empty();
m_sql_cmd= NULL;
duplicates= DUP_ERROR;
ignore= 0;
@@ -723,7 +735,9 @@ void LEX::start(THD *thd_arg)
default_used= FALSE;
query_tables= 0;
reset_query_tables_list(FALSE);
- expr_allows_subselect= TRUE;
+ clause_that_disallows_subselect= NULL;
+ selects_allow_into= FALSE;
+ selects_allow_procedure= FALSE;
use_only_table_context= FALSE;
parse_vcol_expr= FALSE;
check_exists= FALSE;
@@ -733,8 +747,8 @@ void LEX::start(THD *thd_arg)
name= null_clex_str;
event_parse_data= NULL;
profile_options= PROFILE_NONE;
- nest_level=0 ;
- select_lex.nest_level_base= &unit;
+ nest_level= 0;
+ builtin_select.nest_level_base= &unit;
allow_sum_func.clear_all();
in_sum_func= NULL;
@@ -756,8 +770,16 @@ void LEX::start(THD *thd_arg)
win_spec= NULL;
vers_conditions.empty();
+ period_conditions.empty();
is_lex_started= TRUE;
+
+ next_is_main= FALSE;
+ next_is_down= FALSE;
+
+ wild= 0;
+ exchange= 0;
+
DBUG_VOID_RETURN;
}
@@ -1300,7 +1322,8 @@ int ORAlex(YYSTYPE *yylval, THD *thd)
int Lex_input_stream::lex_token(YYSTYPE *yylval, THD *thd)
{
int token;
-
+ const int left_paren= (int) '(';
+
if (lookahead_token >= 0)
{
/*
@@ -1317,6 +1340,8 @@ int Lex_input_stream::lex_token(YYSTYPE *yylval, THD *thd)
token= lex_one_token(yylval, thd);
add_digest_token(token, yylval);
+ SELECT_LEX *curr_sel= thd->lex->current_select;
+
switch(token) {
case WITH:
/*
@@ -1365,8 +1390,16 @@ int Lex_input_stream::lex_token(YYSTYPE *yylval, THD *thd)
}
break;
case VALUES:
- if (thd->lex->current_select->parsing_place == IN_UPDATE_ON_DUP_KEY ||
- thd->lex->current_select->parsing_place == IN_PART_FUNC)
+ if (curr_sel &&
+ (curr_sel->parsing_place == BEFORE_OPT_LIST ||
+ curr_sel->parsing_place == AFTER_LIST))
+ {
+ curr_sel->parsing_place= NO_MATTER;
+ break;
+ }
+ if (curr_sel &&
+ (curr_sel->parsing_place == IN_UPDATE_ON_DUP_KEY ||
+ curr_sel->parsing_place == IN_PART_FUNC))
return VALUE_SYM;
token= lex_one_token(yylval, thd);
add_digest_token(token, yylval);
@@ -1380,6 +1413,43 @@ int Lex_input_stream::lex_token(YYSTYPE *yylval, THD *thd)
lookahead_token= token;
return VALUES;
}
+ case VALUE_SYM:
+ if (curr_sel &&
+ (curr_sel->parsing_place == BEFORE_OPT_LIST ||
+ curr_sel->parsing_place == AFTER_LIST))
+ {
+ curr_sel->parsing_place= NO_MATTER;
+ return VALUES;
+ }
+ break;
+ case PARTITION_SYM:
+ case SELECT_SYM:
+ case UNION_SYM:
+ if (curr_sel &&
+ (curr_sel->parsing_place == BEFORE_OPT_LIST ||
+ curr_sel->parsing_place == AFTER_LIST))
+ {
+ curr_sel->parsing_place= NO_MATTER;
+ }
+ break;
+ case left_paren:
+ if (!curr_sel ||
+ curr_sel->parsing_place != BEFORE_OPT_LIST)
+ return token;
+ token= lex_one_token(yylval, thd);
+ add_digest_token(token, yylval);
+ lookahead_yylval= yylval;
+ yylval= NULL;
+ lookahead_token= token;
+ curr_sel->parsing_place= NO_MATTER;
+ if (token == LIKE)
+ return LEFT_PAREN_LIKE;
+ if (token == WITH)
+ return LEFT_PAREN_WITH;
+ if (token != left_paren && token != SELECT_SYM && token != VALUES)
+ return LEFT_PAREN_ALT;
+ else
+ return left_paren;
break;
default:
break;
@@ -1436,10 +1506,13 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
next_state= MY_LEX_START;
return PERCENT_ORACLE_SYM;
}
+ if (c == '[' && (m_thd->variables.sql_mode & MODE_MSSQL))
+ return scan_ident_delimited(thd, &yylval->ident_cli, ']');
/* Fall through */
case MY_LEX_SKIP: // This should not happen
if (c != ')')
next_state= MY_LEX_START; // Allow signed numbers
+ yylval->kwd.set_keyword(m_tok_start, 1);
return((int) c);
case MY_LEX_MINUS_OR_COMMENT:
@@ -1617,7 +1690,7 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
return scan_ident_start(thd, &yylval->ident_cli);
case MY_LEX_USER_VARIABLE_DELIMITER: // Found quote char
- return scan_ident_delimited(thd, &yylval->ident_cli);
+ return scan_ident_delimited(thd, &yylval->ident_cli, m_tok_start[0]);
case MY_LEX_INT_OR_REAL: // Complete int or incomplete real
if (c != '.' || yyPeek() == '.')
@@ -1743,7 +1816,7 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
return(TEXT_STRING);
}
case MY_LEX_COMMENT: // Comment
- lex->select_lex.options|= OPTION_FOUND_COMMENT;
+ lex->lex_options|= OPTION_LEX_FOUND_COMMENT;
while ((c= yyGet()) != '\n' && c) ;
yyUnget(); // Safety against eof
state= MY_LEX_START; // Try again
@@ -1754,7 +1827,7 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
state= MY_LEX_CHAR; // Probable division
break;
}
- lex->select_lex.options|= OPTION_FOUND_COMMENT;
+ lex->lex_options|= OPTION_LEX_FOUND_COMMENT;
/* Reject '/' '*', since we might need to turn off the echo */
yyUnget();
@@ -1816,7 +1889,7 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
else
{
#ifdef WITH_WSREP
- if (WSREP(thd) && version == 99997 && thd->wsrep_exec_mode == LOCAL_STATE)
+ if (WSREP(thd) && version == 99997 && wsrep_thd_is_local(thd))
{
WSREP_DEBUG("consistency check: %s", thd->query());
thd->wsrep_consistency_check= CONSISTENCY_CHECK_DECLARED;
@@ -2183,11 +2256,12 @@ int Lex_input_stream::scan_ident_middle(THD *thd, Lex_ident_cli_st *str,
int Lex_input_stream::scan_ident_delimited(THD *thd,
- Lex_ident_cli_st *str)
+ Lex_ident_cli_st *str,
+ uchar quote_char)
{
CHARSET_INFO *const cs= thd->charset();
uint double_quotes= 0;
- uchar c, quote_char= m_tok_start[0];
+ uchar c;
DBUG_ASSERT(m_ptr == m_tok_start + 1);
for ( ; ; )
@@ -2272,8 +2346,8 @@ void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, size_t * prefix_length)
void st_select_lex_node::init_query_common()
{
options= 0;
- sql_cache= SQL_CACHE_UNSPECIFIED;
- linkage= UNSPECIFIED_TYPE;
+ set_linkage(UNSPECIFIED_TYPE);
+ distinct= TRUE;
no_table_names_allowed= 0;
uncacheable= 0;
}
@@ -2281,7 +2355,7 @@ void st_select_lex_node::init_query_common()
void st_select_lex_unit::init_query()
{
init_query_common();
- linkage= GLOBAL_OPTIONS_TYPE;
+ set_linkage(GLOBAL_OPTIONS_TYPE);
select_limit_cnt= HA_POS_ERROR;
offset_limit_cnt= 0;
union_distinct= 0;
@@ -2319,21 +2393,12 @@ void st_select_lex::init_query()
join= 0;
having= prep_having= where= prep_where= 0;
cond_pushed_into_where= cond_pushed_into_having= 0;
+ attach_to_conds.empty();
olap= UNSPECIFIED_OLAP_TYPE;
having_fix_field= 0;
having_fix_field_for_pushed_cond= 0;
context.select_lex= this;
context.init();
- /*
- Add the name resolution context of the current (sub)query to the
- stack of contexts for the whole query.
- TODO:
- push_context may return an error if there is no memory for a new
- element in the stack, however this method has no return value,
- thus push_context should be moved to a place where query
- initialization is checked for failure.
- */
- parent_lex->push_context(&context, parent_lex->thd->mem_root);
cond_count= between_count= with_wild= 0;
max_equal_elems= 0;
ref_pointer_array.reset();
@@ -2349,7 +2414,10 @@ void st_select_lex::init_query()
changed_elements= 0;
first_natural_join_processing= 1;
first_cond_optimization= 1;
+ is_service_select= 0;
parsing_place= NO_MATTER;
+ save_parsing_place= NO_MATTER;
+ context_analysis_place= NO_MATTER;
exclude_from_table_unique_test= no_wrap_view_item= FALSE;
nest_level= 0;
link_next= 0;
@@ -2365,6 +2433,7 @@ void st_select_lex::init_query()
tvc= 0;
in_tvc= false;
versioned_tables= 0;
+ pushdown_select= 0;
}
void st_select_lex::init_select()
@@ -2380,16 +2449,14 @@ void st_select_lex::init_select()
table_join_options= 0;
in_sum_expr= with_wild= 0;
options= 0;
- sql_cache= SQL_CACHE_UNSPECIFIED;
ftfunc_list_alloc.empty();
inner_sum_func_list= 0;
ftfunc_list= &ftfunc_list_alloc;
- order_list.elements= 0;
- order_list.first= 0;
- order_list.next= &order_list.first;
+ order_list.empty();
/* Set limit and offset to default values */
select_limit= 0; /* denotes the default limit = HA_POS_ERROR */
offset_limit= 0; /* denotes the default offset = 0 */
+ is_set_query_expr_tail= false;
with_sum_func= 0;
with_all_modifier= 0;
is_correlated= 0;
@@ -2455,6 +2522,24 @@ void st_select_lex_node::add_slave(st_select_lex_node *slave_arg)
}
}
+void st_select_lex_node::link_chain_down(st_select_lex_node *first)
+{
+ st_select_lex_node *last_node;
+ st_select_lex_node *node= first;
+ do
+ {
+ last_node= node;
+ node->master= this;
+ node= node->next;
+ } while (node);
+ if ((last_node->next= slave))
+ {
+ slave->prev= &last_node->next;
+ }
+ first->prev= &slave;
+ slave= first;
+}
+
/*
@brief
Substitute this node in select tree for a newly creates node
@@ -2476,7 +2561,6 @@ void st_select_lex_node::substitute_in_tree(st_select_lex_node *subst)
subst->master= master;
}
-
/*
include on level down (but do not link)
@@ -2525,7 +2609,7 @@ void st_select_lex_node::fast_exclude()
// Remove slave structure
for (; slave; slave= slave->next)
slave->fast_exclude();
-
+
}
@@ -3005,8 +3089,7 @@ void st_select_lex::print_order(String *str,
else
{
/* replace numeric reference with equivalent for ORDER constant */
- if (order->item[0]->type() == Item::INT_ITEM &&
- order->item[0]->basic_const_item())
+ if (order->item[0]->is_order_clause_position())
{
/* make it expression instead of integer constant */
str->append(STRING_WITH_LEN("''"));
@@ -3200,6 +3283,7 @@ LEX::LEX()
gtid_domain_static_buffer,
initial_gtid_domain_buffer_size,
initial_gtid_domain_buffer_size, 0);
+ unit.slave= &builtin_select;
}
@@ -3226,12 +3310,12 @@ bool LEX::can_be_merged()
// TODO: do not forget implement case when select_lex.table_list.elements==0
/* find non VIEW subqueries/unions */
- bool selects_allow_merge= (select_lex.next_select() == 0 &&
- !(select_lex.uncacheable &
+ bool selects_allow_merge= (first_select_lex()->next_select() == 0 &&
+ !(first_select_lex()->uncacheable &
UNCACHEABLE_RAND));
if (selects_allow_merge)
{
- for (SELECT_LEX_UNIT *tmp_unit= select_lex.first_inner_unit();
+ for (SELECT_LEX_UNIT *tmp_unit= first_select_lex()->first_inner_unit();
tmp_unit;
tmp_unit= tmp_unit->next_unit())
{
@@ -3248,12 +3332,12 @@ bool LEX::can_be_merged()
}
return (selects_allow_merge &&
- select_lex.group_list.elements == 0 &&
- select_lex.having == 0 &&
- select_lex.with_sum_func == 0 &&
- select_lex.table_list.elements >= 1 &&
- !(select_lex.options & SELECT_DISTINCT) &&
- select_lex.select_limit == 0);
+ first_select_lex()->group_list.elements == 0 &&
+ first_select_lex()->having == 0 &&
+ first_select_lex()->with_sum_func == 0 &&
+ first_select_lex()->table_list.elements >= 1 &&
+ !(first_select_lex()->options & SELECT_DISTINCT) &&
+ first_select_lex()->select_limit == 0);
}
@@ -3607,12 +3691,25 @@ void LEX::set_trg_event_type_for_tables()
break;
}
+ if (period_conditions.is_set())
+ {
+ switch (sql_command)
+ {
+ case SQLCOM_DELETE:
+ case SQLCOM_UPDATE:
+ case SQLCOM_REPLACE:
+ new_trg_event_map |= trg2bit(TRG_EVENT_INSERT);
+ default:
+ break;
+ }
+ }
+
/*
Do not iterate over sub-selects, only the tables in the outermost
SELECT_LEX can be modified, if any.
*/
- TABLE_LIST *tables= select_lex.get_table_list();
+ TABLE_LIST *tables= first_select_lex()->get_table_list();
while (tables)
{
@@ -3668,12 +3765,13 @@ TABLE_LIST *LEX::unlink_first_table(bool *link_to_local)
/*
and from local list if it is not empty
*/
- if ((*link_to_local= MY_TEST(select_lex.table_list.first)))
+ if ((*link_to_local= MY_TEST(first_select_lex()->table_list.first)))
{
- select_lex.context.table_list=
- select_lex.context.first_name_resolution_table= first->next_local;
- select_lex.table_list.first= first->next_local;
- select_lex.table_list.elements--; //safety
+ first_select_lex()->context.table_list=
+ first_select_lex()->context.first_name_resolution_table=
+ first->next_local;
+ first_select_lex()->table_list.first= first->next_local;
+ first_select_lex()->table_list.elements--; //safety
first->next_local= 0;
/*
Ensure that the global list has the same first table as the local
@@ -3704,7 +3802,7 @@ TABLE_LIST *LEX::unlink_first_table(bool *link_to_local)
void LEX::first_lists_tables_same()
{
- TABLE_LIST *first_table= select_lex.table_list.first;
+ TABLE_LIST *first_table= first_select_lex()->table_list.first;
if (query_tables != first_table && first_table != 0)
{
TABLE_LIST *next;
@@ -3729,6 +3827,23 @@ void LEX::first_lists_tables_same()
}
}
+void LEX::fix_first_select_number()
+{
+ SELECT_LEX *first= first_select_lex();
+ if (first && first->select_number != 1)
+ {
+ uint num= first->select_number;
+ for (SELECT_LEX *sel= all_selects_list;
+ sel;
+ sel= sel->next_select_in_list())
+ {
+ if (sel->select_number < num)
+ sel->select_number++;
+ }
+ first->select_number= 1;
+ }
+}
+
/*
Link table back that was unlinked with unlink_first_table()
@@ -3754,10 +3869,10 @@ void LEX::link_first_table_back(TABLE_LIST *first,
if (link_to_local)
{
- first->next_local= select_lex.table_list.first;
- select_lex.context.table_list= first;
- select_lex.table_list.first= first;
- select_lex.table_list.elements++; //safety
+ first->next_local= first_select_lex()->table_list.first;
+ first_select_lex()->context.table_list= first;
+ first_select_lex()->table_list.first= first;
+ first_select_lex()->table_list.elements++; //safety
}
}
}
@@ -3786,19 +3901,19 @@ void LEX::cleanup_after_one_table_open()
NOTE: all units will be connected to thd->lex->select_lex, because we
have not UNION on most upper level.
*/
- if (all_selects_list != &select_lex)
+ if (all_selects_list != first_select_lex())
{
derived_tables= 0;
- select_lex.exclude_from_table_unique_test= false;
+ first_select_lex()->exclude_from_table_unique_test= false;
/* cleunup underlying units (units of VIEW) */
- for (SELECT_LEX_UNIT *un= select_lex.first_inner_unit();
+ for (SELECT_LEX_UNIT *un= first_select_lex()->first_inner_unit();
un;
un= un->next_unit())
un->cleanup();
/* reduce all selects list to default state */
- all_selects_list= &select_lex;
+ all_selects_list= first_select_lex();
/* remove underlying units (units of VIEW) subtree */
- select_lex.cut_subtree();
+ first_select_lex()->cut_subtree();
}
}
@@ -4450,7 +4565,7 @@ void SELECT_LEX::update_used_tables()
tab->covering_keys= tab->s->keys_for_keyread;
tab->covering_keys.intersect(tab->keys_in_use_for_query);
/*
- View/derived was merged. Need to recalculate read_set/vcol_set
+ View/derived was merged. Need to recalculate read_set
bitmaps here. For example:
CREATE VIEW v1 AS SELECT f1,f2,f3 FROM t1;
SELECT f1 FROM v1;
@@ -4459,8 +4574,6 @@ void SELECT_LEX::update_used_tables()
be in the read_set.
*/
bitmap_clear_all(tab->read_set);
- if (tab->vcol_set)
- bitmap_clear_all(tab->vcol_set);
break;
}
}
@@ -4661,9 +4774,12 @@ void st_select_lex::set_explain_type(bool on_the_fly)
using_materialization= TRUE;
}
- if (&master_unit()->thd->lex->select_lex == this)
+ if (master_unit()->thd->lex->first_select_lex() == this)
{
- type= is_primary ? "PRIMARY" : "SIMPLE";
+ if (pushdown_select)
+ type= pushed_select_text;
+ else
+ type= is_primary ? "PRIMARY" : "SIMPLE";
}
else
{
@@ -4672,7 +4788,11 @@ void st_select_lex::set_explain_type(bool on_the_fly)
/* If we're a direct child of a UNION, we're the first sibling there */
if (linkage == DERIVED_TABLE_TYPE)
{
- if (is_uncacheable & UNCACHEABLE_DEPENDENT)
+ bool is_pushed_master_unit= master_unit()->derived &&
+ master_unit()->derived->pushdown_derived;
+ if (is_pushed_master_unit)
+ type= pushed_derived_text;
+ else if (is_uncacheable & UNCACHEABLE_DEPENDENT)
type= "LATERAL DERIVED";
else
type= "DERIVED";
@@ -4861,8 +4981,8 @@ bool LEX::save_prep_leaf_tables()
Query_arena *arena= thd->stmt_arena, backup;
arena= thd->activate_stmt_arena_if_needed(&backup);
//It is used for DETETE/UPDATE so top level has only one SELECT
- DBUG_ASSERT(select_lex.next_select() == NULL);
- bool res= select_lex.save_prep_leaf_tables(thd);
+ DBUG_ASSERT(first_select_lex()->next_select() == NULL);
+ bool res= first_select_lex()->save_prep_leaf_tables(thd);
if (arena)
thd->restore_active_arena(arena, &backup);
@@ -5217,8 +5337,13 @@ bool LEX::is_partition_management() const
SELECT_LEX *LEX::exclude_last_select()
{
- DBUG_ENTER("SELECT_LEX::exclude_last_select");
- SELECT_LEX *exclude= current_select;
+ return exclude_not_first_select(current_select);
+}
+
+SELECT_LEX *LEX::exclude_not_first_select(SELECT_LEX *exclude)
+{
+ DBUG_ENTER("LEX::exclude_not_first_select");
+ DBUG_PRINT("enter", ("exclude %p #%u", exclude, exclude->select_number));
SELECT_LEX_UNIT *unit= exclude->master_unit();
SELECT_LEX *sl;
DBUG_ASSERT(unit->first_select() != exclude);
@@ -5229,89 +5354,271 @@ SELECT_LEX *LEX::exclude_last_select()
DBUG_PRINT("info", ("excl: %p unit: %p prev: %p", exclude, unit, sl));
if (!sl)
DBUG_RETURN(NULL);
- DBUG_ASSERT(exclude->next_select() == NULL);
- exclude->exclude_from_tree();
+ DBUG_ASSERT(&sl->next == exclude->prev);
+
+ exclude->prev= NULL;
+
current_select= sl;
DBUG_RETURN(exclude);
}
-/**
- Put given (new) SELECT_LEX level below after currect (last) SELECT
+SELECT_LEX_UNIT *LEX::alloc_unit()
+{
+ SELECT_LEX_UNIT *unit;
+ DBUG_ENTER("LEX::alloc_unit");
+ if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT()))
+ DBUG_RETURN(NULL);
- LAST SELECT -> DUMMY SELECT
- |
- V
- NEW UNIT
- |
- V
- NEW SELECT
+ unit->init_query();
+ /* TODO: reentrant problem */
+ unit->thd= thd;
+ unit->link_next= 0;
+ unit->link_prev= 0;
+ /* TODO: remove return_to */
+ unit->return_to= NULL;
+ DBUG_RETURN(unit);
+}
- SELECT (*LAST*) ... FROM (SELECT (*NEW*) ... )
- @param nselect Select to put one level below
+SELECT_LEX *LEX::alloc_select(bool select)
+{
+ SELECT_LEX *select_lex;
+ DBUG_ENTER("LEX::alloc_select");
+ if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
+ DBUG_RETURN(NULL);
+ DBUG_PRINT("info", ("Allocate select: %p #%u statement lex: %p",
+ select_lex, thd->lex->stmt_lex->current_select_number,
+ thd->lex->stmt_lex));
+ /*
+ TODO: move following init to constructor when we get rid of builtin
+ select
+ */
+ select_lex->select_number= ++thd->lex->stmt_lex->current_select_number;
+ select_lex->parent_lex= this; /* Used in init_query. */
+ select_lex->init_query();
+ if (select)
+ select_lex->init_select();
+ select_lex->nest_level_base= &this->unit;
+ select_lex->include_global((st_select_lex_node**)&all_selects_list);
+ select_lex->context.resolve_in_select_list= TRUE;
+ DBUG_RETURN(select_lex);
+}
- @retval TRUE Error
- @retval FALSE OK
-*/
+SELECT_LEX_UNIT *
+LEX::create_unit(SELECT_LEX *first_sel)
+{
+ SELECT_LEX_UNIT *unit;
+ DBUG_ENTER("LEX::create_unit");
+
+ unit = first_sel->master_unit();
+
+ if (!unit && !(unit= alloc_unit()))
+ DBUG_RETURN(NULL);
+
+ unit->register_select_chain(first_sel);
+ if (first_sel->next_select())
+ {
+ unit->reset_distinct();
+ DBUG_ASSERT(!unit->fake_select_lex);
+ if (unit->add_fake_select_lex(thd))
+ DBUG_RETURN(NULL);
+ }
+ DBUG_RETURN(unit);
+}
-bool LEX::add_unit_in_brackets(SELECT_LEX *nselect)
+SELECT_LEX_UNIT *
+SELECT_LEX::attach_selects_chain(SELECT_LEX *first_sel,
+ Name_resolution_context *context)
{
- DBUG_ENTER("LEX::add_unit_in_brackets");
- bool distinct= nselect->master_unit()->union_distinct == nselect;
- bool rc= add_select_to_union_list(distinct, nselect->linkage, 0);
- if (rc)
- DBUG_RETURN(TRUE);
- SELECT_LEX* dummy_select= current_select;
- dummy_select->automatic_brackets= TRUE;
- dummy_select->linkage= nselect->linkage;
+ SELECT_LEX_UNIT *unit;
+ DBUG_ENTER("SELECT_LEX::attach_select_chain");
+
+ if (!(unit= parent_lex->alloc_unit()))
+ DBUG_RETURN(NULL);
+
+ unit->register_select_chain(first_sel);
+ register_unit(unit, context);
+ if (first_sel->next_select())
+ {
+ unit->reset_distinct();
+ DBUG_ASSERT(!unit->fake_select_lex);
+ if (unit->add_fake_select_lex(parent_lex->thd))
+ DBUG_RETURN(NULL);
+ }
+
+ DBUG_RETURN(unit);
+}
+
+SELECT_LEX *
+LEX::wrap_unit_into_derived(SELECT_LEX_UNIT *unit)
+{
+ SELECT_LEX *wrapping_sel;
+ Table_ident *ti;
+ DBUG_ENTER("LEX::wrap_unit_into_derived");
+
+ if (!(wrapping_sel= alloc_select(TRUE)))
+ DBUG_RETURN(NULL);
+ Name_resolution_context *context= &wrapping_sel->context;
+ context->init();
+ wrapping_sel->automatic_brackets= FALSE;
+
+ wrapping_sel->register_unit(unit, context);
/* stuff dummy SELECT * FROM (...) */
+
+ if (push_select(wrapping_sel)) // for Items & TABLE_LIST
+ DBUG_RETURN(NULL);
+
+ /* add SELECT list*/
+ {
+ Item *item= new (thd->mem_root)
+ Item_field(thd, context, NULL, NULL, &star_clex_str);
+ if (item == NULL)
+ goto err;
+ if (add_item_to_list(thd, item))
+ goto err;
+ (wrapping_sel->with_wild)++;
+ }
+
+ unit->first_select()->set_linkage(DERIVED_TABLE_TYPE);
+
+ ti= new (thd->mem_root) Table_ident(unit);
+ if (ti == NULL)
+ goto err;
+ {
+ TABLE_LIST *table_list;
+ LEX_CSTRING alias;
+ if (wrapping_sel->make_unique_derived_name(thd, &alias))
+ goto err;
+
+ if (!(table_list= wrapping_sel->add_table_to_list(thd, ti, &alias,
+ 0, TL_READ,
+ MDL_SHARED_READ)))
+ goto err;
+
+ context->resolve_in_table_list_only(table_list);
+ wrapping_sel->add_joined_table(table_list);
+ }
+
+ pop_select();
+
+ derived_tables|= DERIVED_SUBQUERY;
+
+ DBUG_RETURN(wrapping_sel);
+
+err:
+ pop_select();
+ DBUG_RETURN(NULL);
+}
+
+SELECT_LEX *LEX::wrap_select_chain_into_derived(SELECT_LEX *sel)
+{
+ SELECT_LEX *dummy_select;
+ SELECT_LEX_UNIT *unit;
+ Table_ident *ti;
+ DBUG_ENTER("LEX::wrap_select_chain_into_derived");
+
+ if (!(dummy_select= alloc_select(TRUE)))
+ DBUG_RETURN(NULL);
Name_resolution_context *context= &dummy_select->context;
- context->init();
+ dummy_select->automatic_brackets= FALSE;
+ sel->distinct= TRUE; // First select has not this attribute (safety)
+
+ if (!(unit= dummy_select->attach_selects_chain(sel, context)))
+ DBUG_RETURN(NULL);
+
+ /* stuff dummy SELECT * FROM (...) */
+
+ if (push_select(dummy_select)) // for Items & TABLE_LIST
+ DBUG_RETURN(NULL);
/* add SELECT list*/
- Item *item= new (thd->mem_root)
- Item_field(thd, context, NULL, NULL, &star_clex_str);
- if (unlikely(item == NULL))
- DBUG_RETURN(TRUE);
- if (unlikely(add_item_to_list(thd, item)))
- DBUG_RETURN(TRUE);
- (dummy_select->with_wild)++;
+ {
+ Item *item= new (thd->mem_root)
+ Item_field(thd, context, NULL, NULL, &star_clex_str);
+ if (item == NULL)
+ goto err;
+ if (add_item_to_list(thd, item))
+ goto err;
+ (dummy_select->with_wild)++;
+ }
- rc= mysql_new_select(this, 1, nselect);
- nselect->linkage= DERIVED_TABLE_TYPE;
- DBUG_ASSERT(nselect->outer_select() == dummy_select);
+ sel->set_linkage(DERIVED_TABLE_TYPE);
- current_select= dummy_select;
- current_select->nest_level--;
+ ti= new (thd->mem_root) Table_ident(unit);
+ if (ti == NULL)
+ goto err;
+ {
+ TABLE_LIST *table_list;
+ LEX_CSTRING alias;
+ if (dummy_select->make_unique_derived_name(thd, &alias))
+ goto err;
- SELECT_LEX_UNIT *unit= nselect->master_unit();
- Table_ident *ti= new (thd->mem_root) Table_ident(unit);
- if (unlikely(ti == NULL))
- DBUG_RETURN(TRUE);
- char buff[10];
- LEX_CSTRING alias;
- alias.length= my_snprintf(buff, sizeof(buff),
- "__%u", dummy_select->select_number);
- alias.str= thd->strmake(buff, alias.length);
- if (unlikely(!alias.str))
- DBUG_RETURN(TRUE);
+ if (!(table_list= dummy_select->add_table_to_list(thd, ti, &alias,
+ 0, TL_READ,
+ MDL_SHARED_READ)))
+ goto err;
- TABLE_LIST *table_list;
- if (unlikely(!(table_list=
- dummy_select->add_table_to_list(thd, ti, &alias,
- 0, TL_READ,
- MDL_SHARED_READ))))
- DBUG_RETURN(TRUE);
- context->resolve_in_table_list_only(table_list);
- dummy_select->add_joined_table(table_list);
+ context->resolve_in_table_list_only(table_list);
+ dummy_select->add_joined_table(table_list);
+ }
+
+ pop_select();
derived_tables|= DERIVED_SUBQUERY;
- current_select= nselect;
- current_select->nest_level++;
- DBUG_RETURN(rc);
+ DBUG_RETURN(dummy_select);
+
+err:
+ pop_select();
+ DBUG_RETURN(NULL);
+}
+
+bool LEX::push_context(Name_resolution_context *context)
+{
+ DBUG_ENTER("LEX::push_context");
+ DBUG_PRINT("info", ("Context: %p Select: %p (%d)",
+ context, context->select_lex,
+ (context->select_lex ?
+ context->select_lex->select_number:
+ 0)));
+ bool res= context_stack.push_front(context, thd->mem_root);
+ DBUG_RETURN(res);
+}
+
+
+Name_resolution_context *LEX::pop_context()
+{
+ DBUG_ENTER("LEX::pop_context");
+ Name_resolution_context *context= context_stack.pop();
+ DBUG_PRINT("info", ("Context: %p Select: %p (%d)",
+ context, context->select_lex,
+ (context->select_lex ?
+ context->select_lex->select_number:
+ 0)));
+ DBUG_RETURN(context);
+}
+
+
+SELECT_LEX *LEX::create_priority_nest(SELECT_LEX *first_in_nest)
+{
+ DBUG_ENTER("LEX::create_priority_nest");
+ DBUG_ASSERT(first_in_nest->first_nested);
+ enum sub_select_type wr_unit_type= first_in_nest->get_linkage();
+ bool wr_distinct= first_in_nest->distinct;
+ SELECT_LEX *attach_to= first_in_nest->first_nested;
+ attach_to->cut_next();
+ SELECT_LEX *wrapper= wrap_select_chain_into_derived(first_in_nest);
+ if (wrapper)
+ {
+ first_in_nest->first_nested= NULL;
+ wrapper->set_linkage_and_distinct(wr_unit_type, wr_distinct);
+ wrapper->first_nested= attach_to->first_nested;
+ wrapper->set_master_unit(attach_to->master_unit());
+ attach_to->link_neighbour(wrapper);
+ }
+ DBUG_RETURN(wrapper);
}
@@ -5326,7 +5633,7 @@ bool LEX::add_unit_in_brackets(SELECT_LEX *nselect)
void LEX::check_automatic_up(enum sub_select_type type)
{
if (type != INTERSECT_TYPE &&
- current_select->linkage == INTERSECT_TYPE &&
+ current_select->get_linkage() == INTERSECT_TYPE &&
current_select->outer_select() &&
current_select->outer_select()->automatic_brackets)
{
@@ -5796,10 +6103,17 @@ bool LEX::sp_for_loop_implicit_cursor_statement(THD *thd,
bounds->m_index->sp_lex_in_use= true;
sphead->reset_lex(thd, bounds->m_index);
DBUG_ASSERT(thd->lex != this);
- if (unlikely(!(item=
- new (thd->mem_root) Item_field(thd,
- thd->lex->current_context(),
- NullS, NullS, &name))))
+ /*
+ We pass NULL as Name_resolution_context here.
+ It's OK, fix_fields() will not be called for this Item_field created.
+ Item_field is only needed for LEX::sp_for_loop_cursor_declarations()
+ and is used to transfer the loop index variable name, "rec" in this example:
+ FOR rec IN (SELECT * FROM t1)
+ DO
+ SELECT rec.a, rec.b;
+ END FOR;
+ */
+ if (!(item= new (thd->mem_root) Item_field(thd, NULL, NullS, NullS, &name)))
return true;
bounds->m_index->set_item_and_free_list(item, NULL);
if (thd->lex->sphead->restore_lex(thd))
@@ -5906,10 +6220,22 @@ bool LEX::sp_for_loop_intrange_declarations(THD *thd, Lex_for_loop_st *loop,
const LEX_CSTRING *index,
const Lex_for_loop_bounds_st &bounds)
{
- if (unlikely(!(loop->m_index=
- bounds.m_index->
- sp_add_for_loop_variable(thd, index,
- bounds.m_index->get_item()))))
+ Item *item;
+ if ((item= bounds.m_index->get_item())->type() == Item::FIELD_ITEM)
+ {
+ // We're here is the lower bound is unknown identifier
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), item->full_name());
+ return true;
+ }
+ if ((item= bounds.m_target_bound->get_item())->type() == Item::FIELD_ITEM)
+ {
+ // We're here is the upper bound is unknown identifier
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), item->full_name());
+ return true;
+ }
+ if (!(loop->m_index=
+ bounds.m_index->sp_add_for_loop_variable(thd, index,
+ bounds.m_index->get_item())))
return true;
if (unlikely(!(loop->m_target_bound=
bounds.m_target_bound->
@@ -5935,6 +6261,7 @@ bool LEX::sp_for_loop_cursor_declarations(THD *thd,
LEX_CSTRING name;
uint coffs, param_count= 0;
const sp_pcursor *pcursor;
+ DBUG_ENTER("LEX::sp_for_loop_cursor_declarations");
if ((item_splocal= item->get_item_splocal()))
name= item_splocal->m_name;
@@ -5966,23 +6293,23 @@ bool LEX::sp_for_loop_cursor_declarations(THD *thd,
else
{
thd->parse_error();
- return true;
+ DBUG_RETURN(true);
}
if (unlikely(!(pcursor= spcont->find_cursor_with_error(&name, &coffs,
false)) ||
pcursor->check_param_count_with_error(param_count)))
- return true;
+ DBUG_RETURN(true);
if (!(loop->m_index= sp_add_for_loop_cursor_variable(thd, index,
pcursor, coffs,
bounds.m_index,
item_func_sp)))
- return true;
+ DBUG_RETURN(true);
loop->m_target_bound= NULL;
loop->m_direction= bounds.m_direction;
loop->m_cursor_offset= coffs;
loop->m_implicit_cursor= bounds.m_implicit_cursor;
- return false;
+ DBUG_RETURN(false);
}
@@ -6274,13 +6601,14 @@ sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name1,
sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
- const Sp_handler *sph)
+ const Sp_handler *sph,
+ enum_sp_aggregate_type agg_type)
{
sp_package *package= get_sp_package();
sp_head *sp;
/* Order is important here: new - reset - init */
- if (likely((sp= sp_head::create(package, sph))))
+ if (likely((sp= sp_head::create(package, sph, agg_type))))
{
sp->reset_thd_mem_root(thd);
sp->init(this);
@@ -6303,7 +6631,8 @@ sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
sp_head *LEX::make_sp_head_no_recursive(THD *thd, const sp_name *name,
- const Sp_handler *sph)
+ const Sp_handler *sph,
+ enum_sp_aggregate_type agg_type)
{
sp_package *package= thd->lex->get_sp_package();
/*
@@ -6321,13 +6650,13 @@ sp_head *LEX::make_sp_head_no_recursive(THD *thd, const sp_name *name,
(package &&
(sph == &sp_handler_package_procedure ||
sph == &sp_handler_package_function)))
- return make_sp_head(thd, name, sph);
+ return make_sp_head(thd, name, sph, agg_type);
my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str());
return NULL;
}
-bool LEX::sp_body_finalize_procedure(THD *thd)
+bool LEX::sp_body_finalize_routine(THD *thd)
{
if (sphead->check_unresolved_goto())
return true;
@@ -6337,22 +6666,61 @@ bool LEX::sp_body_finalize_procedure(THD *thd)
}
+bool LEX::sp_body_finalize_procedure(THD *thd)
+{
+ return sphead->check_group_aggregate_instructions_forbid() ||
+ sp_body_finalize_routine(thd);
+}
+
+
+bool LEX::sp_body_finalize_procedure_standalone(THD *thd,
+ const sp_name *end_name)
+{
+ return sp_body_finalize_procedure(thd) ||
+ sphead->check_standalone_routine_end_name(end_name);
+}
+
+
bool LEX::sp_body_finalize_function(THD *thd)
{
- if (sphead->is_not_allowed_in_function("function"))
+ if (sphead->is_not_allowed_in_function("function") ||
+ sphead->check_group_aggregate_instructions_function())
return true;
if (!(sphead->m_flags & sp_head::HAS_RETURN))
{
my_error(ER_SP_NORETURN, MYF(0), ErrConvDQName(sphead).ptr());
return true;
}
- if (sp_body_finalize_procedure(thd))
+ if (sp_body_finalize_routine(thd))
return true;
(void) is_native_function_with_warn(thd, &sphead->m_name);
return false;
}
+bool LEX::sp_body_finalize_trigger(THD *thd)
+{
+ return sphead->is_not_allowed_in_function("trigger") ||
+ sp_body_finalize_procedure(thd);
+}
+
+
+bool LEX::sp_body_finalize_event(THD *thd)
+{
+ event_parse_data->body_changed= true;
+ return sp_body_finalize_procedure(thd);
+}
+
+
+bool LEX::stmt_create_stored_function_finalize_standalone(const sp_name *end_name)
+{
+ if (sphead->check_standalone_routine_end_name(end_name))
+ return true;
+ stmt_create_routine_finalize();
+ return false;
+}
+
+
bool LEX::sp_block_with_exceptions_finalize_declarations(THD *thd)
{
/*
@@ -6644,7 +7012,7 @@ bool LEX::maybe_start_compound_statement(THD *thd)
{
if (!sphead)
{
- if (!make_sp_head(thd, NULL, &sp_handler_procedure))
+ if (!make_sp_head(thd, NULL, &sp_handler_procedure, DEFAULT_AGGREGATE))
return true;
sphead->set_suid(SP_IS_NOT_SUID);
sphead->set_body_start(thd, thd->m_parser_state->m_lip.get_cpp_ptr());
@@ -6854,7 +7222,6 @@ Item_param *LEX::add_placeholder(THD *thd, const LEX_CSTRING *name,
my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
return NULL;
}
-
Query_fragment pos(thd, sphead, start, end);
Item_param *item= new (thd->mem_root) Item_param(thd, name,
pos.pos(), pos.length());
@@ -6885,6 +7252,38 @@ bool LEX::add_resignal_statement(THD *thd, const sp_condition_value *v)
}
+/*
+ Make an Item when an identifier is found in the FOR loop bounds:
+ FOR rec IN cursor
+ FOR var IN var1 .. xxx
+ FOR var IN row1.field1 .. xxx
+ When we parse the first expression after the "IN" keyword,
+ we don't know yet if it's a cursor name, or a scalar SP variable name,
+ or a field of a ROW SP variable. Here we create Item_field to remember
+ the fully qualified name. Later sp_for_loop_cursor_declarations()
+ detects how to treat this name properly.
+*/
+Item *LEX::create_item_for_loop_bound(THD *thd,
+ const LEX_CSTRING *a,
+ const LEX_CSTRING *b,
+ const LEX_CSTRING *c)
+{
+ /*
+ Pass NULL as the name resolution context.
+ This is OK, fix_fields() won't be called for this Item_field.
+ */
+ return new (thd->mem_root) Item_field(thd, NULL, a->str, b->str, c);
+}
+
+
+bool LEX::check_expr_allows_fields_or_error(THD *thd, const char *name) const
+{
+ if (select_stack_top > 0)
+ return false; // OK, fields are allowed
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), name, thd->where);
+ return true; // Error, fields are not allowed
+}
+
Item *LEX::create_item_ident_nospvar(THD *thd,
const Lex_ident_sys_st *a,
const Lex_ident_sys_st *b)
@@ -6907,12 +7306,11 @@ Item *LEX::create_item_ident_nospvar(THD *thd,
my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), a->str, thd->where);
return NULL;
}
- if ((current_select->parsing_place != IN_HAVING) ||
- (current_select->get_in_sum_expr() > 0))
- return new (thd->mem_root) Item_field(thd, current_context(),
- NullS, a->str, b);
- return new (thd->mem_root) Item_ref(thd, current_context(),
- NullS, a->str, b);
+
+ if (current_select->parsing_place == FOR_LOOP_BOUND)
+ return create_item_for_loop_bound(thd, &null_clex_str, a, b);
+
+ return create_item_ident_field(thd, NullS, a->str, b);
}
@@ -7124,12 +7522,11 @@ Item *LEX::create_item_ident(THD *thd,
my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), b->str, thd->where);
return NULL;
}
- if (current_select->parsing_place != IN_HAVING ||
- current_select->get_in_sum_expr() > 0)
- return new (thd->mem_root) Item_field(thd, current_context(),
- schema, b->str, c);
- return new (thd->mem_root) Item_ref(thd, current_context(),
- schema, b->str, c);
+
+ if (current_select->parsing_place == FOR_LOOP_BOUND)
+ return create_item_for_loop_bound(thd, &null_clex_str, b, c);
+
+ return create_item_ident_field(thd, schema, b->str, c);
}
@@ -7163,11 +7560,9 @@ Item *LEX::create_item_limit(THD *thd, const Lex_ident_cli_st *ca)
#endif
safe_to_cache_query= 0;
- if (unlikely(item->type() != Item::INT_ITEM))
- {
- my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0));
+ if (!item->is_valid_limit_clause_variable_with_error())
return NULL;
- }
+
item->limit_clause_param= true;
return item;
}
@@ -7197,11 +7592,8 @@ Item *LEX::create_item_limit(THD *thd,
if (unlikely(!(item= create_item_spvar_row_field(thd, rh, &sa, &sb, spv,
ca->pos(), cb->end()))))
return NULL;
- if (unlikely(item->type() != Item::INT_ITEM))
- {
- my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0));
+ if (!item->is_valid_limit_clause_variable_with_error())
return NULL;
- }
item->limit_clause_param= true;
return item;
}
@@ -7221,15 +7613,20 @@ bool LEX::set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val)
}
-Item *LEX::create_item_ident_nosp(THD *thd, Lex_ident_sys_st *name)
+Item *LEX::create_item_ident_field(THD *thd, const char *db,
+ const char *table,
+ const Lex_ident_sys_st *name)
{
+ if (check_expr_allows_fields_or_error(thd, name->str))
+ return NULL;
+
if (current_select->parsing_place != IN_HAVING ||
current_select->get_in_sum_expr() > 0)
return new (thd->mem_root) Item_field(thd, current_context(),
- NullS, NullS, name);
+ db, table, name);
return new (thd->mem_root) Item_ref(thd, current_context(),
- NullS, NullS, name);
+ db, table, name);
}
@@ -7243,6 +7640,7 @@ Item *LEX::create_item_ident_sp(THD *thd, Lex_ident_sys_st *name,
const Sp_rcontext_handler *rh;
sp_variable *spv;
+ uint unused_off;
DBUG_ASSERT(spcont);
DBUG_ASSERT(sphead);
if ((spv= find_variable(name, &rh)))
@@ -7280,6 +7678,20 @@ Item *LEX::create_item_ident_sp(THD *thd, Lex_ident_sys_st *name,
if (lex_string_eq(name, STRING_WITH_LEN("SQLERRM")))
return new (thd->mem_root) Item_func_sqlerrm(thd);
}
+
+ if (fields_are_impossible() &&
+ (current_select->parsing_place != FOR_LOOP_BOUND ||
+ spcont->find_cursor(name, &unused_off, false) == NULL))
+ {
+ // we are out of SELECT or FOR so it is syntax error
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), name->str);
+ return NULL;
+ }
+
+ if (current_select->parsing_place == FOR_LOOP_BOUND)
+ return create_item_for_loop_bound(thd, &null_clex_str, &null_clex_str,
+ name);
+
return create_item_ident_nosp(thd, name);
}
@@ -7551,46 +7963,80 @@ void binlog_unsafe_map_init()
/**
@brief
- Finding fiels that are used in the GROUP BY of this st_select_lex
+ Collect fiels that are used in the GROUP BY of this st_select_lex
@param thd The thread handle
@details
- This method looks through the fields which are used in the GROUP BY of this
- st_select_lex and saves this fields.
+ This method looks through the fields that are used in the GROUP BY of this
+ st_select_lex and saves info on these fields.
*/
-void st_select_lex::collect_grouping_fields(THD *thd,
- ORDER *grouping_list)
+void st_select_lex::collect_grouping_fields_for_derived(THD *thd,
+ ORDER *grouping_list)
{
grouping_tmp_fields.empty();
List_iterator<Item> li(join->fields_list);
Item *item= li++;
- for (uint i= 0; i < master_unit()->derived->table->s->fields; i++, (item=li++))
+ for (uint i= 0; i < master_unit()->derived->table->s->fields;
+ i++, (item=li++))
{
for (ORDER *ord= grouping_list; ord; ord= ord->next)
{
if ((*ord->item)->eq((Item*)item, 0))
{
- Grouping_tmp_field *grouping_tmp_field=
- new Grouping_tmp_field(master_unit()->derived->table->field[i], item);
+ Field_pair *grouping_tmp_field=
+ new Field_pair(master_unit()->derived->table->field[i], item);
grouping_tmp_fields.push_back(grouping_tmp_field);
}
}
}
}
+
+/**
+ Collect fields that are used in the GROUP BY of this SELECT
+*/
+
+bool st_select_lex::collect_grouping_fields(THD *thd)
+{
+ grouping_tmp_fields.empty();
+
+ for (ORDER *ord= group_list.first; ord; ord= ord->next)
+ {
+ Item *item= *ord->item;
+ if (item->type() != Item::FIELD_ITEM &&
+ !(item->type() == Item::REF_ITEM &&
+ item->real_type() == Item::FIELD_ITEM &&
+ ((((Item_ref *) item)->ref_type() == Item_ref::VIEW_REF) ||
+ (((Item_ref *) item)->ref_type() == Item_ref::REF))))
+ continue;
+
+ Field_pair *grouping_tmp_field=
+ new Field_pair(((Item_field *)item->real_item())->field, item);
+ if (grouping_tmp_fields.push_back(grouping_tmp_field, thd->mem_root))
+ return false;
+ }
+ if (grouping_tmp_fields.elements)
+ return false;
+ return true;
+}
+
+
/**
@brief
For a condition check possibility of exraction a formula over grouping fields
-
- @param cond The condition whose subformulas are to be analyzed
+
+ @param thd The thread handle
+ @param cond The condition whose subformulas are to be analyzed
+ @param checker The checker callback function to be applied to the nodes
+ of the tree of the object
@details
This method traverses the AND-OR condition cond and for each subformula of
the condition it checks whether it can be usable for the extraction of a
condition over the grouping fields of this select. The method uses
- the call-back parameter check_processor to ckeck whether a primary formula
+ the call-back parameter checker to check whether a primary formula
depends only on grouping fields.
The subformulas that are not usable are marked with the flag NO_EXTRACTION_FL.
The subformulas that can be entierly extracted are marked with the flag
@@ -7604,13 +8050,17 @@ void st_select_lex::collect_grouping_fields(THD *thd,
*/
void
-st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond,
- TABLE_LIST *derived)
+st_select_lex::check_cond_extraction_for_grouping_fields(THD *thd, Item *cond)
{
+ if (cond->get_extraction_flag() == NO_EXTRACTION_FL)
+ return;
cond->clear_extraction_flag();
if (cond->type() == Item::COND_ITEM)
{
- bool and_cond= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC;
+ Item_cond_and *and_cond=
+ (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) ?
+ ((Item_cond_and*) cond) : 0;
+
List<Item> *arg_list= ((Item_cond*) cond)->argument_list();
List_iterator<Item> li(*arg_list);
uint count= 0; // to count items not containing NO_EXTRACTION_FL
@@ -7618,7 +8068,7 @@ st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond,
Item *item;
while ((item=li++))
{
- check_cond_extraction_for_grouping_fields(item, derived);
+ check_cond_extraction_for_grouping_fields(thd, item);
if (item->get_extraction_flag() != NO_EXTRACTION_FL)
{
count++;
@@ -7631,7 +8081,9 @@ st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond,
if ((and_cond && count == 0) || item)
cond->set_extraction_flag(NO_EXTRACTION_FL);
if (count_full == arg_list->elements)
+ {
cond->set_extraction_flag(FULL_EXTRACTION_FL);
+ }
if (cond->get_extraction_flag() != 0)
{
li.rewind();
@@ -7641,7 +8093,7 @@ st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond,
}
else
{
- int fl= cond->excl_dep_on_grouping_fields(this) ?
+ int fl= cond->excl_dep_on_grouping_fields(this) && !cond->is_expensive() ?
FULL_EXTRACTION_FL : NO_EXTRACTION_FL;
cond->set_extraction_flag(fl);
}
@@ -7667,7 +8119,7 @@ st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond,
to figure out whether a subformula depends only on these fields or not.
@note
The built condition C is always implied by the condition cond
- (cond => C). The method tries to build the most restictive such
+ (cond => C). The method tries to build the least restictive such
condition (i.e. for any other condition C' such that cond => C'
we have C => C').
@note
@@ -7743,6 +8195,140 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond,
}
+bool st_select_lex::set_nest_level(int new_nest_level)
+{
+ DBUG_ENTER("st_select_lex::set_nest_level");
+ DBUG_PRINT("enter", ("select #%d %p nest level: %d",
+ select_number, this, new_nest_level));
+ if (new_nest_level > (int) MAX_SELECT_NESTING)
+ {
+ my_error(ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ nest_level= new_nest_level;
+ new_nest_level++;
+ for (SELECT_LEX_UNIT *u= first_inner_unit(); u; u= u->next_unit())
+ {
+ if (u->set_nest_level(new_nest_level))
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+bool st_select_lex_unit::set_nest_level(int new_nest_level)
+{
+ DBUG_ENTER("st_select_lex_unit::set_nest_level");
+ for(SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
+ {
+ if (sl->set_nest_level(new_nest_level))
+ DBUG_RETURN(TRUE);
+ }
+ if (fake_select_lex &&
+ fake_select_lex->set_nest_level(new_nest_level))
+ DBUG_RETURN(TRUE);
+ DBUG_RETURN(FALSE);
+}
+
+
+bool st_select_lex::check_parameters(SELECT_LEX *main_select)
+{
+ DBUG_ENTER("st_select_lex::check_parameters");
+ DBUG_PRINT("enter", ("select #%d %p nest level: %d",
+ select_number, this, nest_level));
+
+
+ if ((options & OPTION_PROCEDURE_CLAUSE) &&
+ (!parent_lex->selects_allow_procedure ||
+ next_select() != NULL ||
+ this != master_unit()->first_select() ||
+ nest_level != 0))
+ {
+ my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "PROCEDURE");
+ DBUG_RETURN(TRUE);
+ }
+
+ if ((options & SELECT_HIGH_PRIORITY) && this != main_select)
+ {
+ my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "HIGH_PRIORITY");
+ DBUG_RETURN(TRUE);
+ }
+ if ((options & OPTION_BUFFER_RESULT) && this != main_select)
+ {
+ my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_BUFFER_RESULT");
+ DBUG_RETURN(TRUE);
+ }
+ if ((options & OPTION_FOUND_ROWS) && this != main_select)
+ {
+ my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CALC_FOUND_ROWS");
+ DBUG_RETURN(TRUE);
+ }
+ if (options & OPTION_NO_QUERY_CACHE)
+ {
+ /*
+ Allow this flag only on the first top-level SELECT statement, if
+ SQL_CACHE wasn't specified.
+ */
+ if (this != main_select)
+ {
+ my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_NO_CACHE");
+ DBUG_RETURN(TRUE);
+ }
+ if (parent_lex->sql_cache == LEX::SQL_CACHE)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "SQL_CACHE", "SQL_NO_CACHE");
+ DBUG_RETURN(TRUE);
+ }
+ parent_lex->safe_to_cache_query=0;
+ parent_lex->sql_cache= LEX::SQL_NO_CACHE;
+ }
+ if (options & OPTION_TO_QUERY_CACHE)
+ {
+ /*
+ Allow this flag only on the first top-level SELECT statement, if
+ SQL_NO_CACHE wasn't specified.
+ */
+ if (this != main_select)
+ {
+ my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CACHE");
+ DBUG_RETURN(TRUE);
+ }
+ if (parent_lex->sql_cache == LEX::SQL_NO_CACHE)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "SQL_NO_CACHE", "SQL_CACHE");
+ DBUG_RETURN(TRUE);
+ }
+ parent_lex->safe_to_cache_query=1;
+ parent_lex->sql_cache= LEX::SQL_CACHE;
+ }
+
+ for (SELECT_LEX_UNIT *u= first_inner_unit(); u; u= u->next_unit())
+ {
+ if (u->check_parameters(main_select))
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+bool st_select_lex_unit::check_parameters(SELECT_LEX *main_select)
+{
+ for(SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
+ {
+ if (sl->check_parameters(main_select))
+ return TRUE;
+ }
+ return fake_select_lex && fake_select_lex->check_parameters(main_select);
+}
+
+
+bool LEX::check_main_unit_semantics()
+{
+ if (unit.set_nest_level(0) ||
+ unit.check_parameters(first_select_lex()))
+ return TRUE;
+ return FALSE;
+}
+
int set_statement_var_if_exists(THD *thd, const char *var_name,
size_t var_name_length, ulonglong value)
{
@@ -7793,14 +8379,23 @@ bool LEX::sp_add_cfetch(THD *thd, const LEX_CSTRING *name)
}
+bool LEX::sp_add_agg_cfetch()
+{
+ sphead->m_flags|= sp_head::HAS_AGGREGATE_INSTR;
+ sp_instr_agg_cfetch *i=
+ new (thd->mem_root) sp_instr_agg_cfetch(sphead->instructions(), spcont);
+ return i == NULL || sphead->add_instr(i);
+}
+
+
bool LEX::create_or_alter_view_finalize(THD *thd, Table_ident *table_ident)
{
sql_command= SQLCOM_CREATE_VIEW;
/* first table in list is target VIEW name */
- if (unlikely(!select_lex.add_table_to_list(thd, table_ident, NULL,
+ if (!first_select_lex()->add_table_to_list(thd, table_ident, NULL,
TL_OPTION_UPDATING,
TL_IGNORE,
- MDL_EXCLUSIVE)))
+ MDL_EXCLUSIVE))
return true;
query_tables->open_strategy= TABLE_LIST::OPEN_STUB;
return false;
@@ -7948,6 +8543,7 @@ bool LEX::create_package_finalize(THD *thd,
exp ? ErrConvDQName(name).ptr() : name->m_name.str);
return true;
}
+ // TODO: reuse code in LEX::create_package_finalize and sp_head::set_stmt_end
sphead->m_body.length= body_end - body_start;
if (unlikely(!(sphead->m_body.str= thd->strmake(body_start,
sphead->m_body.length))))
@@ -7962,7 +8558,8 @@ bool LEX::create_package_finalize(THD *thd,
sphead->restore_thd_mem_root(thd);
sp_package *pkg= sphead->get_package();
DBUG_ASSERT(pkg);
- return pkg->validate_after_parser(thd);
+ return sphead->check_group_aggregate_instructions_forbid() ||
+ pkg->validate_after_parser(thd);
}
@@ -8332,6 +8929,20 @@ bool LEX::last_field_generated_always_as_row_end()
VERS_SYS_END_FLAG);
}
+void st_select_lex_unit::reset_distinct()
+{
+ union_distinct= NULL;
+ for(SELECT_LEX *sl= first_select()->next_select();
+ sl;
+ sl= sl->next_select())
+ {
+ if (sl->distinct)
+ {
+ union_distinct= sl;
+ }
+ }
+}
+
void LEX::save_values_list_state()
{
@@ -8347,54 +8958,1559 @@ void LEX::restore_values_list_state()
}
-void LEX::tvc_start()
+void st_select_lex_unit::fix_distinct()
+{
+ if (union_distinct && this != union_distinct->master_unit())
+ reset_distinct();
+}
+
+
+void st_select_lex_unit::register_select_chain(SELECT_LEX *first_sel)
+{
+ DBUG_ASSERT(first_sel != 0);
+ slave= first_sel;
+ first_sel->prev= &slave;
+ for(SELECT_LEX *sel=first_sel; sel; sel= sel->next_select())
+ {
+ sel->master= (st_select_lex_node *)this;
+ uncacheable|= sel->uncacheable;
+ }
+}
+
+
+void st_select_lex::register_unit(SELECT_LEX_UNIT *unit,
+ Name_resolution_context *outer_context)
{
- if (current_select == &select_lex)
- mysql_init_select(this);
+ if ((unit->next= slave))
+ slave->prev= &unit->next;
+ unit->prev= &slave;
+ slave= unit;
+ unit->master= this;
+ uncacheable|= unit->uncacheable;
+
+ for(SELECT_LEX *sel= unit->first_select();sel; sel= sel->next_select())
+ {
+ sel->context.outer_context= outer_context;
+ }
+}
+
+
+void st_select_lex::add_statistics(SELECT_LEX_UNIT *unit)
+{
+ for (;
+ unit;
+ unit= unit->next_unit())
+ for(SELECT_LEX *child= unit->first_select();
+ child;
+ child= child->next_select())
+ {
+ /*
+ A subselect can add fields to an outer select.
+ Reserve space for them.
+ */
+ select_n_where_fields+= child->select_n_where_fields;
+ /*
+ Aggregate functions in having clause may add fields
+ to an outer select. Count them also.
+ */
+ select_n_having_items+= child->select_n_having_items;
+ }
+}
+
+
+bool LEX::main_select_push(bool service)
+{
+ DBUG_ENTER("LEX::main_select_push");
+ current_select_number= 1;
+ builtin_select.select_number= 1;
+ builtin_select.is_service_select= service;
+ if (push_select(&builtin_select))
+ DBUG_RETURN(TRUE);
+ DBUG_RETURN(FALSE);
+}
+
+void Lex_select_lock::set_to(SELECT_LEX *sel)
+{
+ if (defined_lock)
+ {
+ if (sel->master_unit() &&
+ sel == sel->master_unit()->fake_select_lex)
+ sel->master_unit()->set_lock_to_the_last_select(*this);
+ else
+ {
+ sel->parent_lex->safe_to_cache_query= 0;
+ if (update_lock)
+ {
+ sel->lock_type= TL_WRITE;
+ sel->set_lock_for_tables(TL_WRITE, false);
+ }
+ else
+ {
+ sel->lock_type= TL_READ_WITH_SHARED_LOCKS;
+ sel->set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS, false);
+ }
+ }
+ }
+}
+
+bool Lex_order_limit_lock::set_to(SELECT_LEX *sel)
+{
+ /*TODO: lock */
+ //if (lock.defined_lock && sel == sel->master_unit()->fake_select_lex)
+ // return TRUE;
+ if (lock.defined_timeout)
+ {
+ THD *thd= sel->parent_lex->thd;
+ if (set_statement_var_if_exists(thd,
+ C_STRING_WITH_LEN("lock_wait_timeout"),
+ lock.timeout) ||
+ set_statement_var_if_exists(thd,
+ C_STRING_WITH_LEN("innodb_lock_wait_timeout"),
+ lock.timeout))
+ return TRUE;
+ }
+ lock.set_to(sel);
+ sel->explicit_limit= limit.explicit_limit;
+ sel->select_limit= limit.select_limit;
+ sel->offset_limit= limit.offset_limit;
+ if (order_list)
+ {
+ if (sel->get_linkage() != GLOBAL_OPTIONS_TYPE &&
+ sel->olap != UNSPECIFIED_OLAP_TYPE &&
+ (sel->get_linkage() != UNION_TYPE || sel->braces))
+ {
+ my_error(ER_WRONG_USAGE, MYF(0),
+ "CUBE/ROLLUP", "ORDER BY");
+ return TRUE;
+ }
+ sel->order_list= *(order_list);
+ }
+ sel->is_set_query_expr_tail= true;
+ return FALSE;
+}
+
+
+static void change_item_list_context(List<Item> *list,
+ Name_resolution_context *context)
+{
+ List_iterator_fast<Item> it (*list);
+ Item *item;
+ while((item= it++))
+ {
+ item->walk(&Item::change_context_processor, FALSE, (void *)context);
+ }
+}
+
+
+bool LEX::insert_select_hack(SELECT_LEX *sel)
+{
+ DBUG_ENTER("LEX::insert_select_hack");
+
+ DBUG_ASSERT(first_select_lex() == &builtin_select);
+ DBUG_ASSERT(sel != NULL);
+
+ DBUG_ASSERT(builtin_select.first_inner_unit() == NULL);
+
+ if (builtin_select.link_prev)
+ {
+ if ((*builtin_select.link_prev= builtin_select.link_next))
+ ((st_select_lex *)builtin_select.link_next)->link_prev=
+ builtin_select.link_prev;
+ builtin_select.link_prev= NULL; // indicator of removal
+ }
+
+ if (set_main_unit(sel->master_unit()))
+ return true;
+
+ DBUG_ASSERT(builtin_select.table_list.elements == 1);
+ TABLE_LIST *insert_table= builtin_select.table_list.first;
+
+ if (!(insert_table->next_local= sel->table_list.first))
+ {
+ sel->table_list.next= &insert_table->next_local;
+ }
+ sel->table_list.first= insert_table;
+ sel->table_list.elements++;
+ insert_table->select_lex= sel;
+
+ sel->context.first_name_resolution_table= insert_table;
+ builtin_select.context= sel->context;
+ change_item_list_context(&field_list, &sel->context);
+
+ if (sel->tvc && !sel->next_select() &&
+ (sql_command == SQLCOM_INSERT_SELECT ||
+ sql_command == SQLCOM_REPLACE_SELECT))
+ {
+ DBUG_PRINT("info", ("'Usual' INSERT detected"));
+ many_values= sel->tvc->lists_of_values;
+ sel->options= sel->tvc->select_options;
+ sel->tvc= NULL;
+ if (sql_command == SQLCOM_INSERT_SELECT)
+ sql_command= SQLCOM_INSERT;
+ else
+ sql_command= SQLCOM_REPLACE;
+ }
+
+
+ for (SELECT_LEX *sel= all_selects_list;
+ sel;
+ sel= sel->next_select_in_list())
+ {
+ if (sel->select_number != 1)
+ sel->select_number--;
+ };
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Create an Item_singlerow_subselect for a query expression.
+*/
+
+Item *LEX::create_item_query_expression(THD *thd,
+ st_select_lex_unit *unit)
+{
+ if (clause_that_disallows_subselect)
+ {
+ my_error(ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
+ clause_that_disallows_subselect);
+ return NULL;
+ }
+
+ // Add the subtree of subquery to the current SELECT_LEX
+ SELECT_LEX *curr_sel= select_stack_head();
+ DBUG_ASSERT(current_select == curr_sel ||
+ (curr_sel == NULL && current_select == &builtin_select));
+ if (!curr_sel)
+ {
+ curr_sel= &builtin_select;
+ curr_sel->register_unit(unit, &curr_sel->context);
+ curr_sel->add_statistics(unit);
+ }
+
+ return new (thd->mem_root)
+ Item_singlerow_subselect(thd, unit->first_select());
+}
+
+
+SELECT_LEX_UNIT *LEX::parsed_select_expr_start(SELECT_LEX *s1, SELECT_LEX *s2,
+ enum sub_select_type unit_type,
+ bool distinct)
+{
+ SELECT_LEX_UNIT *res;
+ SELECT_LEX *sel1;
+ SELECT_LEX *sel2;
+ if (!s1->next_select())
+ sel1= s1;
else
- save_values_list_state();
- many_values.empty();
- insert_list= 0;
+ {
+ sel1= wrap_unit_into_derived(s1->master_unit());
+ if (!sel1)
+ return NULL;
+ }
+ if (!s2->next_select())
+ sel2= s2;
+ else
+ {
+ sel2= wrap_unit_into_derived(s2->master_unit());
+ if (!sel2)
+ return NULL;
+ }
+ sel1->link_neighbour(sel2);
+ sel2->set_linkage_and_distinct(unit_type, distinct);
+ sel2->first_nested= sel1->first_nested= sel1;
+ res= create_unit(sel1);
+ if (res == NULL)
+ return NULL;
+ res->pre_last_parse= sel1;
+ push_select(res->fake_select_lex);
+ return res;
}
-bool LEX::tvc_start_derived()
+SELECT_LEX_UNIT *LEX::parsed_select_expr_cont(SELECT_LEX_UNIT *unit,
+ SELECT_LEX *s2,
+ enum sub_select_type unit_type,
+ bool distinct, bool oracle)
{
- if (current_select->linkage == GLOBAL_OPTIONS_TYPE ||
- unlikely(mysql_new_select(this, 1, NULL)))
+ DBUG_ASSERT(!s2->next_select());
+ SELECT_LEX *sel1= s2;
+ SELECT_LEX *last= unit->pre_last_parse->next_select();
+
+ int cmp= oracle? 0 : cmp_unit_op(unit_type, last->get_linkage());
+ if (cmp == 0)
+ {
+ sel1->first_nested= last->first_nested;
+ }
+ else if (cmp > 0)
+ {
+ last->first_nested= unit->pre_last_parse;
+ sel1->first_nested= last;
+ }
+ else /* cmp < 0 */
+ {
+ SELECT_LEX *first_in_nest= last->first_nested;
+ if (first_in_nest->first_nested != first_in_nest)
+ {
+ /* There is a priority jump starting from first_in_nest */
+ if ((last= create_priority_nest(first_in_nest)) == NULL)
+ return NULL;
+ unit->fix_distinct();
+ }
+ sel1->first_nested= last->first_nested;
+ }
+ last->link_neighbour(sel1);
+ sel1->set_master_unit(unit);
+ sel1->set_linkage_and_distinct(unit_type, distinct);
+ unit->pre_last_parse= last;
+ return unit;
+}
+
+
+/**
+ Add primary expression as the next term in a given query expression body
+ pruducing a new query expression body
+*/
+
+SELECT_LEX_UNIT *
+LEX::add_primary_to_query_expression_body(SELECT_LEX_UNIT *unit,
+ SELECT_LEX *sel,
+ enum sub_select_type unit_type,
+ bool distinct,
+ bool oracle)
+{
+ SELECT_LEX *sel2= sel;
+ if (sel->master_unit() && sel->master_unit()->first_select()->next_select())
+ {
+ sel2= wrap_unit_into_derived(sel->master_unit());
+ if (!sel2)
+ return NULL;
+ }
+ SELECT_LEX *sel1= unit->first_select();
+ if (!sel1->next_select())
+ unit= parsed_select_expr_start(sel1, sel2, unit_type, distinct);
+ else
+ unit= parsed_select_expr_cont(unit, sel2, unit_type, distinct, oracle);
+ return unit;
+}
+
+
+/**
+ Add query primary to a parenthesized query primary
+ pruducing a new query expression body
+*/
+
+SELECT_LEX_UNIT *
+LEX::add_primary_to_query_expression_body_ext_parens(
+ SELECT_LEX_UNIT *unit,
+ SELECT_LEX *sel,
+ enum sub_select_type unit_type,
+ bool distinct)
+{
+ SELECT_LEX *sel1= unit->first_select();
+ if (unit->first_select()->next_select())
+ {
+ sel1= wrap_unit_into_derived(unit);
+ if (!sel1)
+ return NULL;
+ if (!create_unit(sel1))
+ return NULL;
+ }
+ SELECT_LEX *sel2= sel;
+ if (sel->master_unit() && sel->master_unit()->first_select()->next_select())
+ {
+ sel2= wrap_unit_into_derived(sel->master_unit());
+ if (!sel2)
+ return NULL;
+ }
+ unit= parsed_select_expr_start(sel1, sel2, unit_type, distinct);
+ return unit;
+}
+
+
+/**
+ Process multi-operand query expression body
+*/
+
+bool LEX::parsed_multi_operand_query_expression_body(SELECT_LEX_UNIT *unit)
+{
+ SELECT_LEX *first_in_nest=
+ unit->pre_last_parse->next_select()->first_nested;
+ if (first_in_nest->first_nested != first_in_nest)
+ {
+ /* There is a priority jump starting from first_in_nest */
+ if (create_priority_nest(first_in_nest) == NULL)
+ return true;
+ unit->fix_distinct();
+ }
+ return false;
+}
+
+
+/**
+ Add non-empty tail to a query expression body
+*/
+
+SELECT_LEX_UNIT *LEX::add_tail_to_query_expression_body(SELECT_LEX_UNIT *unit,
+ Lex_order_limit_lock *l)
+{
+ DBUG_ASSERT(l != NULL);
+ pop_select();
+ SELECT_LEX *sel= unit->first_select()->next_select() ? unit->fake_select_lex :
+ unit->first_select();
+ l->set_to(sel);
+ return unit;
+}
+
+
+/**
+ Add non-empty tail to a parenthesized query primary
+*/
+
+SELECT_LEX_UNIT *
+LEX::add_tail_to_query_expression_body_ext_parens(SELECT_LEX_UNIT *unit,
+ Lex_order_limit_lock *l)
+{
+ SELECT_LEX *sel= unit->first_select()->next_select() ? unit->fake_select_lex :
+ unit->first_select();
+
+ DBUG_ASSERT(l != NULL);
+
+ pop_select();
+ if (sel->is_set_query_expr_tail)
+ {
+ if (!l->order_list && !sel->explicit_limit)
+ l->order_list= &sel->order_list;
+ else
+ {
+ if (!unit)
+ return NULL;
+ sel= wrap_unit_into_derived(unit);
+ if (!sel)
+ return NULL;
+ if (!create_unit(sel))
+ return NULL;
+ }
+ }
+ l->set_to(sel);
+ return sel->master_unit();
+}
+
+
+/**
+ Process subselect parsing
+*/
+
+SELECT_LEX *LEX::parsed_subselect(SELECT_LEX_UNIT *unit)
+{
+ if (clause_that_disallows_subselect)
+ {
+ my_error(ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
+ clause_that_disallows_subselect);
+ return NULL;
+ }
+
+ // Add the subtree of subquery to the current SELECT_LEX
+ SELECT_LEX *curr_sel= select_stack_head();
+ DBUG_ASSERT(current_select == curr_sel ||
+ (curr_sel == NULL && current_select == &builtin_select));
+ if (curr_sel)
+ {
+ curr_sel->register_unit(unit, context_stack.head());
+ curr_sel->add_statistics(unit);
+ }
+
+ return unit->first_select();
+}
+
+
+/**
+ Process INSERT-like select
+*/
+
+bool LEX::parsed_insert_select(SELECT_LEX *first_select)
+{
+ if (sql_command == SQLCOM_INSERT ||
+ sql_command == SQLCOM_REPLACE)
+ {
+ if (sql_command == SQLCOM_INSERT)
+ sql_command= SQLCOM_INSERT_SELECT;
+ else
+ sql_command= SQLCOM_REPLACE_SELECT;
+ }
+ insert_select_hack(first_select);
+ if (check_main_unit_semantics())
return true;
+
+ // fix "main" select
+ SELECT_LEX *blt __attribute__((unused))= pop_select();
+ DBUG_ASSERT(blt == &builtin_select);
+ push_select(first_select);
+ return false;
+}
+
+
+bool LEX::parsed_TVC_start()
+{
+ SELECT_LEX *sel;
save_values_list_state();
many_values.empty();
insert_list= 0;
+ if (!(sel= alloc_select(TRUE)) ||
+ push_select(sel))
+ return true;
+ sel->init_select();
+ sel->braces= FALSE; // just initialisation
return false;
}
-bool LEX::tvc_finalize()
+SELECT_LEX *LEX::parsed_TVC_end()
{
- if (unlikely(!(current_select->tvc=
- new (thd->mem_root)
- table_value_constr(many_values,
- current_select,
- current_select->options))))
- return true;
+ SELECT_LEX *res= pop_select(); // above TVC select
+ if (!(res->tvc=
+ new (thd->mem_root) table_value_constr(many_values,
+ res,
+ res->options)))
+ return NULL;
restore_values_list_state();
- if (!current_select->master_unit()->fake_select_lex)
- current_select->master_unit()->add_fake_select_lex(thd);
- return false;
+ return res;
}
-bool LEX::tvc_finalize_derived()
+
+TABLE_LIST *LEX::parsed_derived_table(SELECT_LEX_UNIT *unit,
+ int for_system_time,
+ LEX_CSTRING *alias)
{
+ TABLE_LIST *res;
derived_tables|= DERIVED_SUBQUERY;
- if (unlikely(!expr_allows_subselect || sql_command == (int)SQLCOM_PURGE))
+ unit->first_select()->set_linkage(DERIVED_TABLE_TYPE);
+
+ // Add the subtree of subquery to the current SELECT_LEX
+ SELECT_LEX *curr_sel= select_stack_head();
+ DBUG_ASSERT(current_select == curr_sel ||
+ (curr_sel == NULL && current_select == &builtin_select));
+
+ Table_ident *ti= new (thd->mem_root) Table_ident(unit);
+ if (ti == NULL)
+ return NULL;
+ if (!(res= curr_sel->add_table_to_list(thd, ti, alias, 0,
+ TL_READ, MDL_SHARED_READ)))
+ return NULL;
+ if (for_system_time)
{
- thd->parse_error();
+ res->vers_conditions= vers_conditions;
+ }
+ return res;
+}
+
+bool LEX::parsed_create_view(SELECT_LEX_UNIT *unit, int check)
+{
+ SQL_I_List<TABLE_LIST> *save= &first_select_lex()->table_list;
+ if (set_main_unit(unit))
+ return true;
+ if (check_main_unit_semantics())
+ return true;
+ first_select_lex()->table_list.push_front(save);
+ current_select= first_select_lex();
+ size_t len= thd->m_parser_state->m_lip.get_cpp_ptr() -
+ create_view->select.str;
+ void *create_view_select= thd->memdup(create_view->select.str, len);
+ create_view->select.length= len;
+ create_view->select.str= (char *) create_view_select;
+ size_t not_used;
+ trim_whitespace(thd->charset(),
+ &create_view->select, &not_used);
+ create_view->check= check;
+ parsing_options.allows_variable= TRUE;
+ return false;
+}
+
+bool LEX::select_finalize(st_select_lex_unit *expr)
+{
+ sql_command= SQLCOM_SELECT;
+ selects_allow_into= TRUE;
+ selects_allow_procedure= TRUE;
+ if (set_main_unit(expr))
+ return true;
+ return check_main_unit_semantics();
+}
+
+
+bool LEX::select_finalize(st_select_lex_unit *expr, Lex_select_lock l)
+{
+ return expr->set_lock_to_the_last_select(l) ||
+ select_finalize(expr);
+}
+
+
+/*
+ "IN" and "EXISTS" subselect can appear in two statement types:
+
+ 1. Statements that can have table columns, such as SELECT, DELETE, UPDATE
+ 2. Statements that cannot have table columns, e.g:
+ RETURN ((1) IN (SELECT * FROM t1))
+ IF ((1) IN (SELECT * FROM t1))
+
+ Statements of the first type call master_select_push() in the beginning.
+ In such case everything is properly linked.
+
+ Statements of the second type do not call mastr_select_push().
+ Here we catch the second case and relink thd->lex->builtin_select and
+ select_lex to properly point to each other.
+
+ QQ: Shouldn't subselects of other type also call relink_hack()?
+ QQ: Can we do it at constructor time instead?
+*/
+
+void LEX::relink_hack(st_select_lex *select_lex)
+{
+ if (!select_stack_top) // Statements of the second type
+ {
+ if (!select_lex->get_master()->get_master())
+ ((st_select_lex *) select_lex->get_master())->
+ set_master(&builtin_select);
+ if (!builtin_select.get_slave())
+ builtin_select.set_slave(select_lex->get_master());
+ }
+}
+
+
+bool SELECT_LEX_UNIT::set_lock_to_the_last_select(Lex_select_lock l)
+{
+ if (l.defined_lock)
+ {
+ SELECT_LEX *sel= first_select();
+ while (sel->next_select())
+ sel= sel->next_select();
+ if (sel->braces)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "lock options",
+ "SELECT in brackets");
+ return TRUE;
+ }
+ l.set_to(sel);
+ }
+ return FALSE;
+}
+
+/**
+ Generate unique name for generated derived table for this SELECT
+*/
+
+bool SELECT_LEX::make_unique_derived_name(THD *thd, LEX_CSTRING *alias)
+{
+ // uint32 digits + two underscores + trailing '\0'
+ char buff[MAX_INT_WIDTH + 2 + 1];
+ alias->length= my_snprintf(buff, sizeof(buff), "__%u", select_number);
+ alias->str= thd->strmake(buff, alias->length);
+ return !alias->str;
+}
+
+
+/*
+ Make a new sp_instr_stmt and set its m_query to a concatenation
+ of two strings.
+*/
+bool LEX::new_sp_instr_stmt(THD *thd,
+ const LEX_CSTRING &prefix,
+ const LEX_CSTRING &suffix)
+{
+ LEX_STRING qbuff;
+ sp_instr_stmt *i;
+
+ if (!(i= new (thd->mem_root) sp_instr_stmt(sphead->instructions(),
+ spcont, this)))
+ return true;
+
+ qbuff.length= prefix.length + suffix.length;
+ if (!(qbuff.str= (char*) alloc_root(thd->mem_root, qbuff.length + 1)))
+ return true;
+ if (prefix.length)
+ memcpy(qbuff.str, prefix.str, prefix.length);
+ strmake(qbuff.str + prefix.length, suffix.str, suffix.length);
+ i->m_query= qbuff;
+ return sphead->add_instr(i);
+}
+
+
+bool LEX::sp_proc_stmt_statement_finalize_buf(THD *thd, const LEX_CSTRING &qbuf)
+{
+ sphead->m_flags|= sp_get_flags_for_command(this);
+ /* "USE db" doesn't work in a procedure */
+ if (unlikely(sql_command == SQLCOM_CHANGE_DB))
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "USE");
+ return true;
+ }
+ /*
+ Don't add an instruction for SET statements, since all
+ instructions for them were already added during processing
+ of "set" rule.
+ */
+ DBUG_ASSERT(sql_command != SQLCOM_SET_OPTION || var_list.is_empty());
+ if (sql_command != SQLCOM_SET_OPTION)
+ return new_sp_instr_stmt(thd, empty_clex_str, qbuf);
+ return false;
+}
+
+
+bool LEX::sp_proc_stmt_statement_finalize(THD *thd, bool no_lookahead)
+{
+ // Extract the query statement from the tokenizer
+ Lex_input_stream *lip= &thd->m_parser_state->m_lip;
+ Lex_cstring qbuf(sphead->m_tmp_query, no_lookahead ? lip->get_ptr() :
+ lip->get_tok_start());
+ return LEX::sp_proc_stmt_statement_finalize_buf(thd, qbuf);
+}
+
+
+/**
+ @brief
+ Extract the condition that can be pushed into WHERE clause
+
+ @param thd the thread handle
+ @param cond the condition from which to extract a pushed condition
+ @param remaining_cond IN/OUT the condition that will remain of cond after
+ the extraction
+ @param transformer the transformer callback function to be
+ applied to the fields of the condition so it
+ can be pushed`
+ @param arg parameter to be passed to the transformer
+
+ @details
+ This function builds the most restrictive condition depending only on
+ the fields used in the GROUP BY of this SELECT. These fields were
+ collected before in grouping_tmp_fields list of this SELECT.
+
+ First this method checks if this SELECT doesn't have any aggregation
+ functions and has no GROUP BY clause. If so cond can be entirely pushed
+ into WHERE.
+
+ Otherwise the method checks if there is a condition depending only on
+ grouping fields that can be extracted from cond.
+
+ The condition that can be pushed into WHERE should be transformed.
+ It is done by transformer.
+
+ The extracted condition is saved in cond_pushed_into_where of this select.
+ cond can remain un empty after the extraction of the condition that can be
+ pushed into WHERE. It is saved in remaining_cond.
+
+ @note
+ This method is called for pushdown conditions into materialized
+ derived tables/views optimization.
+ Item::derived_field_transformer_for_where is passed as the actual
+ callback function.
+ Also it is called for pushdown into materialized IN subqueries.
+ Item::in_subq_field_transformer_for_where is passed as the actual
+ callback function.
+*/
+
+void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond,
+ Item **remaining_cond,
+ Item_transformer transformer,
+ uchar *arg)
+{
+ if (!cond_pushdown_is_allowed())
+ return;
+ thd->lex->current_select= this;
+ if (have_window_funcs())
+ {
+ Item *cond_over_partition_fields;
+ check_cond_extraction_for_grouping_fields(thd, cond);
+ cond_over_partition_fields=
+ build_cond_for_grouping_fields(thd, cond, true);
+ if (cond_over_partition_fields)
+ cond_over_partition_fields= cond_over_partition_fields->transform(thd,
+ &Item::grouping_field_transformer_for_where,
+ (uchar*) this);
+ if (cond_over_partition_fields)
+ {
+ cond_over_partition_fields->walk(
+ &Item::cleanup_excluding_const_fields_processor, 0, 0);
+ cond_pushed_into_where= cond_over_partition_fields;
+ }
+
+ return;
+ }
+
+ if (!join->group_list && !with_sum_func)
+ {
+ cond=
+ cond->transform(thd, transformer, arg);
+ if (cond)
+ {
+ cond->walk(
+ &Item::cleanup_excluding_const_fields_processor, 0, 0);
+ cond_pushed_into_where= cond;
+ }
+
+ return;
+ }
+
+ /*
+ Figure out what can be extracted from cond and pushed into
+ the WHERE clause of this select.
+ */
+ Item *cond_over_grouping_fields;
+ check_cond_extraction_for_grouping_fields(thd, cond);
+ cond_over_grouping_fields=
+ build_cond_for_grouping_fields(thd, cond, true);
+
+ /*
+ Transform references to the columns of condition that can be pushed
+ into WHERE so it can be pushed.
+ */
+ if (cond_over_grouping_fields)
+ cond_over_grouping_fields= cond_over_grouping_fields->transform(thd,
+ &Item::grouping_field_transformer_for_where,
+ (uchar*) this);
+
+ if (cond_over_grouping_fields)
+ {
+
+ /*
+ Remove top conjuncts in cond that has been pushed into the WHERE
+ clause of this select
+ */
+ cond= remove_pushed_top_conjuncts(thd, cond);
+
+ cond_over_grouping_fields->walk(
+ &Item::cleanup_excluding_const_fields_processor, 0, 0);
+ cond_pushed_into_where= cond_over_grouping_fields;
+ }
+
+ *remaining_cond= cond;
+}
+
+
+/**
+ @brief
+ Mark OR-conditions as non-pushable to avoid repeatable pushdown
+
+ @param cond the processed condition
+
+ @details
+ Consider pushdown into the materialized derived table/view.
+ Consider OR condition that can be pushed into HAVING and some
+ parts of this OR condition that can be pushed into WHERE.
+
+ On example:
+
+ SELECT *
+ FROM t1,
+ (
+ SELECT a,MAX(c) AS m_c
+ GROUP BY a
+ ) AS dt
+ WHERE ((dt.m_c>10) AND (dt.a>2)) OR ((dt.m_c<7) and (dt.a<3)) AND
+ (t1.a=v1.a);
+
+
+ Here ((dt.m_c>10) AND (dt.a>2)) OR ((dt.m_c<7) and (dt.a<3)) or1
+ can be pushed down into the HAVING of the materialized
+ derived table dt.
+
+ (dt.a>2) OR (dt.a<3) part of or1 depends only on grouping fields
+ of dt and can be pushed into WHERE.
+
+ As a result:
+
+ SELECT *
+ FROM t1,
+ (
+ SELECT a,MAX(c) AS m_c
+ WHERE (dt.a>2) OR (dt.a<3)
+ GROUP BY a
+ HAVING ((dt.m_c>10) AND (dt.a>2)) OR ((dt.m_c<7) and (dt.a<3))
+ ) AS dt
+ WHERE ((dt.m_c>10) AND (dt.a>2)) OR ((dt.m_c<7) and (dt.a<3)) AND
+ (t1.a=v1.a);
+
+
+ Here (dt.a>2) OR (dt.a<3) also remains in HAVING of dt.
+ When SELECT that defines df is processed HAVING pushdown optimization
+ is made. In HAVING pushdown optimization it will extract
+ (dt.a>2) OR (dt.a<3) condition from or1 again and push it into WHERE.
+ This will cause duplicate conditions in WHERE of dt.
+
+ To avoid repeatable pushdown such OR conditions as or1 describen
+ above are marked with NO_EXTRACTION_FL.
+
+ @note
+ This method is called for pushdown into materialized
+ derived tables/views/IN subqueries optimization.
+*/
+
+void mark_or_conds_to_avoid_pushdown(Item *cond)
+{
+ if (cond->type() == Item::COND_ITEM &&
+ ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ if (item->type() == Item::COND_ITEM &&
+ ((Item_cond*) item)->functype() == Item_func::COND_OR_FUNC)
+ item->set_extraction_flag(NO_EXTRACTION_FL);
+ }
+ }
+ else if (cond->type() == Item::COND_ITEM &&
+ ((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC)
+ cond->set_extraction_flag(NO_EXTRACTION_FL);
+}
+
+/**
+ @brief
+ Get condition that can be pushed from HAVING into WHERE
+
+ @param thd the thread handle
+ @param cond the condition from which to extract the condition
+
+ @details
+ The method collects in attach_to_conds list conditions from cond
+ that can be pushed from HAVING into WHERE.
+
+ Conditions that can be pushed were marked with FULL_EXTRACTION_FL in
+ check_cond_extraction_for_grouping_fields() method.
+ Conditions that can't be pushed were marked with NO_EXTRACTION_FL.
+ Conditions which parts can be pushed weren't marked.
+
+ There are two types of conditions that can be pushed:
+ 1. Condition that can be simply moved from HAVING
+ (if cond is marked with FULL_EXTRACTION_FL or
+ cond is an AND condition and some of its parts are marked with
+ FULL_EXTRACTION_FL)
+ In this case condition is transformed and pushed into attach_to_conds
+ list.
+ 2. Part of some other condition c1 that can't be entirely pushed
+ (if с1 isn't marked with any flag).
+
+ For example:
+
+ SELECT t1.a,MAX(t1.b),t1.c
+ FROM t1
+ GROUP BY t1.a
+ HAVING ((t1.a > 5) AND (t1.c < 3)) OR (t1.a = 3);
+
+ Here (t1.a > 5) OR (t1.a = 3) from HAVING can be pushed into WHERE.
+
+ In this case build_pushable_cond() is called for c1.
+ This method builds a clone of the c1 part that can be pushed.
+
+ Transformation mentioned above is made with multiple_equality_transformer
+ transformer. It transforms all multiple equalities in the extracted
+ condition into the set of equalities.
+
+ @note
+ Conditions that can be pushed are collected in attach_to_conds in this way:
+ 1. if cond is an AND condition its parts that can be pushed into WHERE
+ are added to attach_to_conds list separately.
+ 2. in all other cases conditions are pushed into the list entirely.
+
+ @retval
+ true - if an error occurs
+ false - otherwise
+*/
+
+bool
+st_select_lex::build_pushable_cond_for_having_pushdown(THD *thd, Item *cond)
+{
+ List<Item> equalities;
+
+ /* Condition can't be pushed */
+ if (cond->get_extraction_flag() == NO_EXTRACTION_FL)
+ return false;
+
+ /**
+ Condition can be pushed entirely.
+ Transform its multiple equalities and add to attach_to_conds list.
+ */
+ if (cond->get_extraction_flag() == FULL_EXTRACTION_FL)
+ {
+ Item *result= cond->transform(thd,
+ &Item::multiple_equality_transformer,
+ (uchar *)this);
+ if (!result)
+ return true;
+ if (result->type() == Item::COND_ITEM &&
+ ((Item_cond*) result)->functype() == Item_func::COND_AND_FUNC)
+ {
+ List_iterator<Item> li(*((Item_cond*) result)->argument_list());
+ Item *item;
+ while ((item= li++))
+ {
+ if (attach_to_conds.push_back(item, thd->mem_root))
+ return true;
+ }
+ }
+ else
+ {
+ if (attach_to_conds.push_back(result, thd->mem_root))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ There is no flag set for this condition. It means that some
+ part of this condition can be pushed.
+ */
+ if (cond->type() != Item::COND_ITEM)
+ return false;
+
+ if (((Item_cond *)cond)->functype() != Item_cond::COND_AND_FUNC)
+ {
+ /*
+ cond is not a conjunctive formula and it cannot be pushed into WHERE.
+ Try to extract a formula that can be pushed.
+ */
+ Item *fix= cond->build_pushable_cond(thd, 0, 0);
+ if (!fix)
+ return false;
+ if (attach_to_conds.push_back(fix, thd->mem_root))
+ return true;
+ }
+ else
+ {
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ if (item->get_extraction_flag() == NO_EXTRACTION_FL)
+ continue;
+ else if (item->get_extraction_flag() == FULL_EXTRACTION_FL)
+ {
+ Item *result= item->transform(thd,
+ &Item::multiple_equality_transformer,
+ (uchar *)item);
+ if (!result)
+ return true;
+ if (result->type() == Item::COND_ITEM &&
+ ((Item_cond*) result)->functype() == Item_func::COND_AND_FUNC)
+ {
+ List_iterator<Item> li(*((Item_cond*) result)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ if (attach_to_conds.push_back(item, thd->mem_root))
+ return true;
+ }
+ }
+ else
+ {
+ if (attach_to_conds.push_back(result, thd->mem_root))
+ return true;
+ }
+ }
+ else
+ {
+ Item *fix= item->build_pushable_cond(thd, 0, 0);
+ if (!fix)
+ continue;
+ if (attach_to_conds.push_back(fix, thd->mem_root))
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+/**
+ Check if item is equal to some field in Field_pair 'field_pair'
+ from 'pair_list' and return found 'field_pair' if it exists.
+*/
+
+Field_pair *get_corresponding_field_pair(Item *item,
+ List<Field_pair> pair_list)
+{
+ DBUG_ASSERT(item->type() == Item::FIELD_ITEM ||
+ (item->type() == Item::REF_ITEM &&
+ ((((Item_ref *) item)->ref_type() == Item_ref::VIEW_REF) ||
+ (((Item_ref *) item)->ref_type() == Item_ref::REF))));
+
+ List_iterator<Field_pair> it(pair_list);
+ Field_pair *field_pair;
+ Item_field *field_item= (Item_field *) (item->real_item());
+ while ((field_pair= it++))
+ {
+ if (field_item->field == field_pair->field)
+ return field_pair;
+ }
+ return NULL;
+}
+
+
+/**
+ @brief
+ Collect fields from multiple equalities which are equal to grouping
+
+ @param thd the thread handle
+
+ @details
+ This method checks if multiple equalities of the WHERE clause contain
+ fields from GROUP BY of this SELECT. If so all fields of such multiple
+ equalities are collected in grouping_tmp_fields list without repetitions.
+
+ @retval
+ true - if an error occurs
+ false - otherwise
+*/
+
+bool st_select_lex::collect_fields_equal_to_grouping(THD *thd)
+{
+ if (!join->cond_equal || join->cond_equal->is_empty())
+ return false;
+
+ List_iterator_fast<Item_equal> li(join->cond_equal->current_level);
+ Item_equal *item_equal;
+
+ while ((item_equal= li++))
+ {
+ Item_equal_fields_iterator it(*item_equal);
+ Item *item;
+ while ((item= it++))
+ {
+ if (get_corresponding_field_pair(item, grouping_tmp_fields))
+ break;
+ }
+ if (!item)
+ break;
+
+ it.rewind();
+ while ((item= it++))
+ {
+ if (get_corresponding_field_pair(item, grouping_tmp_fields))
+ continue;
+ Field_pair *grouping_tmp_field=
+ new Field_pair(((Item_field *)item->real_item())->field, item);
+ if (grouping_tmp_fields.push_back(grouping_tmp_field, thd->mem_root))
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
+ @brief
+ Remove marked top conjuncts of HAVING for having pushdown
+
+ @param thd the thread handle
+ @param cond the condition which subformulas are to be removed
+
+ @details
+ This method removes from cond all subformulas that can be moved from HAVING
+ into WHERE.
+
+ @retval
+ condition without removed subformulas
+ 0 if the whole 'cond' is removed
+*/
+
+Item *remove_pushed_top_conjuncts_for_having(THD *thd, Item *cond)
+{
+ /* Nothing to extract */
+ if (cond->get_extraction_flag() == NO_EXTRACTION_FL)
+ {
+ cond->clear_extraction_flag();
+ return cond;
+ }
+ /* cond can be pushed in WHERE entirely */
+ if (cond->get_extraction_flag() == FULL_EXTRACTION_FL)
+ {
+ cond->clear_extraction_flag();
+ return 0;
+ }
+
+ /* Some parts of cond can be pushed */
+ if (cond->type() == Item::COND_ITEM &&
+ ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ if (item->get_extraction_flag() == NO_EXTRACTION_FL)
+ item->clear_extraction_flag();
+ else if (item->get_extraction_flag() == FULL_EXTRACTION_FL)
+ {
+ if (item->type() == Item::FUNC_ITEM &&
+ ((Item_func*) item)->functype() == Item_func::MULT_EQUAL_FUNC)
+ item->set_extraction_flag(DELETION_FL);
+ else
+ {
+ item->clear_extraction_flag();
+ li.remove();
+ }
+ }
+ }
+ switch (((Item_cond*) cond)->argument_list()->elements)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return (((Item_cond*) cond)->argument_list()->head());
+ default:
+ return cond;
+ }
+ }
+ return cond;
+}
+
+
+/**
+ @brief
+ Extract condition that can be pushed from HAVING into WHERE
+
+ @param thd the thread handle
+ @param having the HAVING clause of this select
+ @param having_equal multiple equalities of HAVING
+
+ @details
+ This method builds a set of conditions dependent only on
+ fields used in the GROUP BY of this select (directly or indirectly
+ through equalities). These conditions are extracted from the HAVING
+ clause of this select.
+ The method saves these conditions into attach_to_conds list and removes
+ from HAVING conditions that can be entirely pushed into WHERE.
+
+ Example of the HAVING pushdown transformation:
+
+ SELECT t1.a,MAX(t1.b)
+ FROM t1
+ GROUP BY t1.a
+ HAVING (t1.a>2) AND (MAX(c)>12);
+
+ =>
+
+ SELECT t1.a,MAX(t1.b)
+ FROM t1
+ WHERE (t1.a>2)
+ GROUP BY t1.a
+ HAVING (MAX(c)>12);
+
+ In this method (t1.a>2) is not attached to the WHERE clause.
+ It is pushed into the attach_to_conds list to be attached to
+ the WHERE clause later.
+
+ In details:
+ 1. Collect fields used in the GROUP BY grouping_fields of this SELECT
+ 2. Collect fields equal to grouping_fields from the WHERE clause
+ of this SELECT and add them to the grouping_fields list.
+ 3. Extract the most restrictive condition from the HAVING clause of this
+ select that depends only on the grouping fields (directly or indirectly
+ through equality).
+ If the extracted condition is an AND condition it is transformed into a
+ list of all its conjuncts saved in attach_to_conds. Otherwise,
+ the condition is put into attach_to_conds as the only its element.
+ 4. Remove conditions from HAVING clause that can be entirely pushed
+ into WHERE.
+ Multiple equalities are not removed but marked with DELETION_FL flag.
+ They will be deleted later in substitite_for_best_equal_field() called
+ for the HAVING condition.
+ 5. Unwrap fields wrapped in Item_ref wrappers contained in the condition
+ of attach_to_conds so the condition could be pushed into WHERE.
+
+ @note
+ This method is similar to st_select_lex::pushdown_cond_into_where_clause().
+
+ @retval TRUE if an error occurs
+ @retval FALSE otherwise
+*/
+
+Item *st_select_lex::pushdown_from_having_into_where(THD *thd, Item *having)
+{
+ if (!having || !group_list.first)
+ return having;
+ if (!cond_pushdown_is_allowed())
+ return having;
+
+ st_select_lex *save_curr_select= thd->lex->current_select;
+ thd->lex->current_select= this;
+
+ /*
+ 1. Collect fields used in the GROUP BY grouping fields of this SELECT
+ 2. Collect fields equal to grouping_fields from the WHERE clause
+ of this SELECT and add them to the grouping fields list.
+ */
+ if (collect_grouping_fields(thd) ||
+ collect_fields_equal_to_grouping(thd))
+ return having;
+
+ /*
+ 3. Extract the most restrictive condition from the HAVING clause of this
+ select that depends only on the grouping fields (directly or indirectly
+ through equality).
+ If the extracted condition is an AND condition it is transformed into a
+ list of all its conjuncts saved in attach_to_conds. Otherwise,
+ the condition is put into attach_to_conds as the only its element.
+ */
+ List_iterator_fast<Item> it(attach_to_conds);
+ Item *item;
+ check_cond_extraction_for_grouping_fields(thd, having);
+ if (build_pushable_cond_for_having_pushdown(thd, having))
+ {
+ attach_to_conds.empty();
+ goto exit;
+ }
+ if (!attach_to_conds.elements)
+ goto exit;
+
+ /*
+ 4. Remove conditions from HAVING clause that can be entirely pushed
+ into WHERE.
+ Multiple equalities are not removed but marked with DELETION_FL flag.
+ They will be deleted later in substitite_for_best_equal_field() called
+ for the HAVING condition.
+ */
+ having= remove_pushed_top_conjuncts_for_having(thd, having);
+
+ /*
+ Change join->cond_equal which points to the multiple equalities of
+ the top level of HAVING.
+ Removal of AND conditions may leave only one conjunct in HAVING.
+
+ Example 1:
+ SELECT *
+ FROM t1
+ GROUP BY t1.a
+ (t1.a < 2) AND (t1.b = 2)
+
+ (t1.a < 2) is pushed into WHERE.
+ join->cond_equal should point on (t1.b = 2) multiple equality now.
+
+ Example 2:
+ SELECT *
+ FROM t1
+ GROUP BY t1.a
+ (t1.a = 2) AND (t1.b < 2)
+
+ (t1.a = 2) is pushed into WHERE.
+ join->cond_equal should be NULL now.
+ */
+ if (having &&
+ having->type() == Item::FUNC_ITEM &&
+ ((Item_func*) having)->functype() == Item_func::MULT_EQUAL_FUNC)
+ join->having_equal= new (thd->mem_root) COND_EQUAL((Item_equal *)having,
+ thd->mem_root);
+ else if (!having ||
+ having->type() != Item::COND_ITEM ||
+ ((Item_cond *)having)->functype() != Item_cond::COND_AND_FUNC)
+ join->having_equal= 0;
+
+ /*
+ 5. Unwrap fields wrapped in Item_ref wrappers contained in the condition
+ of attach_to_conds so the condition could be pushed into WHERE.
+ */
+ it.rewind();
+ while ((item=it++))
+ {
+ item= item->transform(thd,
+ &Item::field_transformer_for_having_pushdown,
+ (uchar *)this);
+
+ if (item->walk(&Item::cleanup_excluding_immutables_processor, 0, STOP_PTR)
+ || item->fix_fields(thd, NULL))
+ {
+ attach_to_conds.empty();
+ goto exit;
+ }
+ }
+exit:
+ thd->lex->current_select= save_curr_select;
+ return having;
+}
+
+
+bool LEX::stmt_install_plugin(const DDL_options_st &opt,
+ const Lex_ident_sys_st &name,
+ const LEX_CSTRING &soname)
+{
+ create_info.init();
+ if (add_create_options_with_check(opt))
+ return true;
+ sql_command= SQLCOM_INSTALL_PLUGIN;
+ comment= name;
+ ident= soname;
+ return false;
+}
+
+
+void LEX::stmt_install_plugin(const LEX_CSTRING &soname)
+{
+ sql_command= SQLCOM_INSTALL_PLUGIN;
+ comment= null_clex_str;
+ ident= soname;
+}
+
+
+bool LEX::stmt_uninstall_plugin_by_name(const DDL_options_st &opt,
+ const Lex_ident_sys_st &name)
+{
+ check_opt.init();
+ if (add_create_options_with_check(opt))
+ return true;
+ sql_command= SQLCOM_UNINSTALL_PLUGIN;
+ comment= name;
+ ident= null_clex_str;
+ return false;
+}
+
+
+bool LEX::stmt_uninstall_plugin_by_soname(const DDL_options_st &opt,
+ const LEX_CSTRING &soname)
+{
+ check_opt.init();
+ if (add_create_options_with_check(opt))
+ return true;
+ sql_command= SQLCOM_UNINSTALL_PLUGIN;
+ comment= null_clex_str;
+ ident= soname;
+ return false;
+}
+
+
+bool LEX::stmt_prepare_validate(const char *stmt_type)
+{
+ if (unlikely(table_or_sp_used()))
+ {
+ my_error(ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), stmt_type);
return true;
}
- current_select->linkage= DERIVED_TABLE_TYPE;
- return tvc_finalize();
+ return check_main_unit_semantics();
+}
+
+
+bool LEX::stmt_prepare(const Lex_ident_sys_st &ident, Item *code)
+{
+ sql_command= SQLCOM_PREPARE;
+ if (stmt_prepare_validate("PREPARE..FROM"))
+ return true;
+ prepared_stmt.set(ident, code, NULL);
+ return false;
+}
+
+
+bool LEX::stmt_execute_immediate(Item *code, List<Item> *params)
+{
+ sql_command= SQLCOM_EXECUTE_IMMEDIATE;
+ if (stmt_prepare_validate("EXECUTE IMMEDIATE"))
+ return true;
+ static const Lex_ident_sys immediate(STRING_WITH_LEN("IMMEDIATE"));
+ prepared_stmt.set(immediate, code, params);
+ return false;
+}
+
+
+bool LEX::stmt_execute(const Lex_ident_sys_st &ident, List<Item> *params)
+{
+ sql_command= SQLCOM_EXECUTE;
+ prepared_stmt.set(ident, NULL, params);
+ return stmt_prepare_validate("EXECUTE..USING");
+}
+
+
+void LEX::stmt_deallocate_prepare(const Lex_ident_sys_st &ident)
+{
+ sql_command= SQLCOM_DEALLOCATE_PREPARE;
+ prepared_stmt.set(ident, NULL, NULL);
+}
+
+
+bool LEX::stmt_alter_table_exchange_partition(Table_ident *table)
+{
+ DBUG_ASSERT(sql_command == SQLCOM_ALTER_TABLE);
+ first_select_lex()->db= table->db;
+ if (first_select_lex()->db.str == NULL &&
+ copy_db_to(&first_select_lex()->db))
+ return true;
+ name= table->table;
+ alter_info.partition_flags|= ALTER_PARTITION_EXCHANGE;
+ if (!first_select_lex()->add_table_to_list(thd, table, NULL,
+ TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE))
+ return true;
+ DBUG_ASSERT(!m_sql_cmd);
+ m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_table_exchange_partition();
+ return m_sql_cmd == NULL;
+}
+
+
+void LEX::stmt_purge_to(const LEX_CSTRING &to)
+{
+ type= 0;
+ sql_command= SQLCOM_PURGE;
+ to_log= to.str;
+}
+
+
+bool LEX::stmt_purge_before(Item *item)
+{
+ type= 0;
+ sql_command= SQLCOM_PURGE_BEFORE;
+ value_list.empty();
+ value_list.push_front(item, thd->mem_root);
+ return check_main_unit_semantics();
+}
+
+
+bool LEX::stmt_create_udf_function(const DDL_options_st &options,
+ enum_sp_aggregate_type agg_type,
+ const Lex_ident_sys_st &name,
+ Item_result return_type,
+ const LEX_CSTRING &soname)
+{
+ if (stmt_create_function_start(options))
+ return true;
+
+ if (unlikely(is_native_function(thd, &name)))
+ {
+ my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0), name.str);
+ return true;
+ }
+ sql_command= SQLCOM_CREATE_FUNCTION;
+ udf.name= name;
+ udf.returns= return_type;
+ udf.dl= soname.str;
+ udf.type= agg_type == GROUP_AGGREGATE ? UDFTYPE_AGGREGATE :
+ UDFTYPE_FUNCTION;
+ stmt_create_routine_finalize();
+ return false;
+}
+
+
+bool LEX::stmt_create_stored_function_start(const DDL_options_st &options,
+ enum_sp_aggregate_type agg_type,
+ const sp_name *spname)
+{
+ if (stmt_create_function_start(options) ||
+ unlikely(!make_sp_head_no_recursive(thd, spname,
+ &sp_handler_function, agg_type)))
+ return true;
+ return false;
+}
+
+
+Spvar_definition *LEX::row_field_name(THD *thd, const Lex_ident_sys_st &name)
+{
+ Spvar_definition *res;
+ if (unlikely(check_string_char_length(&name, 0, NAME_CHAR_LEN,
+ system_charset_info, 1)))
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), name.str);
+ return NULL;
+ }
+ if (unlikely(!(res= new (thd->mem_root) Spvar_definition())))
+ return NULL;
+ init_last_field(res, &name, thd->variables.collation_database);
+ return res;
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 748e6180642..2389b23e08e 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -149,6 +149,12 @@ public:
bool copy_or_convert(THD *thd, const Lex_ident_cli_st *str, CHARSET_INFO *cs);
bool is_null() const { return str == NULL; }
bool to_size_number(ulonglong *to) const;
+ void set_valid_utf8(const LEX_CSTRING *name)
+ {
+ DBUG_ASSERT(Well_formed_prefix(system_charset_info, name->str,
+ name->length).length() == name->length);
+ str= name->str ; length= name->length;
+ }
};
@@ -164,6 +170,33 @@ public:
{
((LEX_CSTRING &) *this)= null_clex_str;
}
+ Lex_ident_sys(const char *name, size_t length)
+ {
+ LEX_CSTRING tmp= {name, length};
+ set_valid_utf8(&tmp);
+ }
+ Lex_ident_sys & operator=(const Lex_ident_sys_st &name)
+ {
+ Lex_ident_sys_st::operator=(name);
+ return *this;
+ }
+};
+
+
+/**
+ ORDER BY ... LIMIT parameters;
+*/
+class Lex_order_limit_lock: public Sql_alloc
+{
+public:
+ SQL_I_List<st_order> *order_list; /* ORDER clause */
+ Lex_select_lock lock;
+ Lex_select_limit limit;
+
+ Lex_order_limit_lock() :order_list(NULL)
+ {}
+
+ bool set_to(st_select_lex *sel);
};
@@ -174,6 +207,14 @@ enum sub_select_type
UNION_TYPE, INTERSECT_TYPE, EXCEPT_TYPE,
GLOBAL_OPTIONS_TYPE, DERIVED_TABLE_TYPE, OLAP_TYPE
};
+
+inline int cmp_unit_op(enum sub_select_type op1, enum sub_select_type op2)
+{
+ DBUG_ASSERT(op1 >= UNION_TYPE && op1 <= EXCEPT_TYPE);
+ DBUG_ASSERT(op2 >= UNION_TYPE && op2 <= EXCEPT_TYPE);
+ return (op1 == INTERSECT_TYPE ? 1 : 0) - (op2 == INTERSECT_TYPE ? 1 : 0);
+}
+
enum unit_common_op {OP_MIX, OP_UNION, OP_INTERSECT, OP_EXCEPT};
enum enum_view_suid
@@ -193,6 +234,22 @@ enum plsql_cursor_attr_t
};
+enum enum_sp_suid_behaviour
+{
+ SP_IS_DEFAULT_SUID= 0,
+ SP_IS_NOT_SUID,
+ SP_IS_SUID
+};
+
+
+enum enum_sp_aggregate_type
+{
+ DEFAULT_AGGREGATE= 0,
+ NOT_AGGREGATE,
+ GROUP_AGGREGATE
+};
+
+
/* These may not be declared yet */
class Table_ident;
class sql_exchange;
@@ -215,6 +272,8 @@ class Item_window_func;
struct sql_digest_state;
class With_clause;
class my_var;
+class select_handler;
+class Pushdown_select;
#define ALLOC_ROOT_SET 1024
@@ -265,15 +324,6 @@ extern uint binlog_unsafe_map[256];
void binlog_unsafe_map_init();
#endif
-struct LEX_TYPE
-{
- enum enum_field_types type;
- char *length, *dec;
- CHARSET_INFO *charset;
- void set(int t, char *l, char *d, CHARSET_INFO *cs)
- { type= (enum_field_types)t; length= l; dec= d; charset= cs; }
-};
-
#ifdef MYSQL_SERVER
/*
The following hack is needed because mysql_yacc.cc does not define
@@ -302,7 +352,8 @@ struct LEX_TYPE
This is not within #ifdef because we want "EXPLAIN PARTITIONS ..." to produce
additional "partitions" column even if partitioning is not compiled in.
*/
-#define DESCRIBE_PARTITIONS 4
+#define DESCRIBE_PARTITIONS 4
+#define DESCRIBE_EXTENDED2 8
#ifdef MYSQL_SERVER
@@ -311,13 +362,6 @@ extern MYSQL_PLUGIN_IMPORT const LEX_CSTRING empty_clex_str;
extern const LEX_CSTRING star_clex_str;
extern const LEX_CSTRING param_clex_str;
-enum enum_sp_suid_behaviour
-{
- SP_IS_DEFAULT_SUID= 0,
- SP_IS_NOT_SUID,
- SP_IS_SUID
-};
-
enum enum_sp_data_access
{
SP_DEFAULT_ACCESS= 0,
@@ -327,13 +371,6 @@ enum enum_sp_data_access
SP_MODIFIES_SQL_DATA
};
-enum enum_sp_aggregate_type
-{
- DEFAULT_AGGREGATE= 0,
- NOT_AGGREGATE,
- GROUP_AGGREGATE
-};
-
const LEX_CSTRING sp_data_access_name[]=
{
{ STRING_WITH_LEN("") },
@@ -541,7 +578,7 @@ public:
unit is container of either
- One SELECT
- UNION of selects
- select_lex and unit are both inherited form select_lex_node
+ select_lex and unit are both inherited form st_select_lex_node
neighbors are two select_lex or units on the same level
All select describing structures linked with following pointers:
@@ -666,13 +703,6 @@ public:
ulonglong options;
/*
- In sql_cache we store SQL_CACHE flag as specified by user to be
- able to restore SELECT statement from internal structures.
- */
- enum e_sql_cache { SQL_CACHE_UNSPECIFIED, SQL_NO_CACHE, SQL_CACHE };
- e_sql_cache sql_cache;
-
- /*
result of this query can't be cached, bit field, can be :
UNCACHEABLE_DEPENDENT_GENERATED
UNCACHEABLE_DEPENDENT_INJECTED
@@ -682,11 +712,15 @@ public:
UNCACHEABLE_PREPARE
*/
uint8 uncacheable;
+private:
enum sub_select_type linkage;
+public:
bool is_linkage_set() const
{
return linkage == UNION_TYPE || linkage == INTERSECT_TYPE || linkage == EXCEPT_TYPE;
}
+ enum sub_select_type get_linkage() { return linkage; }
+ bool distinct;
bool no_table_names_allowed; /* used for global order by */
static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
@@ -704,13 +738,32 @@ public:
}
inline st_select_lex_node* get_master() { return master; }
+ inline st_select_lex_node* get_slave() { return slave; }
void include_down(st_select_lex_node *upper);
void add_slave(st_select_lex_node *slave_arg);
void include_neighbour(st_select_lex_node *before);
+ void link_chain_down(st_select_lex_node *first);
+ void link_neighbour(st_select_lex_node *neighbour)
+ {
+ DBUG_ASSERT(next == NULL);
+ DBUG_ASSERT(neighbour != NULL);
+ next= neighbour;
+ neighbour->prev= &next;
+ }
+ void cut_next() { next= NULL; }
void include_standalone(st_select_lex_node *sel, st_select_lex_node **ref);
void include_global(st_select_lex_node **plink);
void exclude();
void exclude_from_tree();
+ void exclude_from_global()
+ {
+ if (!link_prev)
+ return;
+ if (((*link_prev)= link_next))
+ link_next->link_prev= link_prev;
+ link_next= NULL;
+ link_prev= NULL;
+ }
void substitute_in_tree(st_select_lex_node *subst);
void set_slave(st_select_lex_node *slave_arg) { slave= slave_arg; }
@@ -727,10 +780,27 @@ public:
st_select_lex_node *insert_chain_before(st_select_lex_node **ptr_pos_to_insert,
st_select_lex_node *end_chain_node);
void move_as_slave(st_select_lex_node *new_master);
+ void set_linkage(enum sub_select_type l)
+ {
+ DBUG_ENTER("st_select_lex_node::set_linkage");
+ DBUG_PRINT("info", ("node: %p linkage: %d->%d", this, linkage, l));
+ linkage= l;
+ DBUG_VOID_RETURN;
+ }
+ /*
+ This method created for reiniting LEX in mysql_admin_table() and can be
+ used only if you are going remove all SELECT_LEX & units except belonger
+ to LEX (LEX::unit & LEX::select, for other purposes there are
+ SELECT_LEX_UNIT::exclude_level & SELECT_LEX_UNIT::exclude_tree.
+
+ It is also used in parsing to detach builtin select.
+ */
+ void cut_subtree() { slave= 0; }
friend class st_select_lex_unit;
friend bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel);
friend bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
bool open_view_no_parse);
+ friend class st_select_lex;
private:
void fast_exclude();
};
@@ -762,12 +832,13 @@ protected:
bool prepare_join(THD *thd, SELECT_LEX *sl, select_result *result,
ulong additional_options,
bool is_union_select);
- bool join_union_item_types(THD *thd, List<Item> &types, uint count);
bool join_union_type_handlers(THD *thd,
class Type_holder *holders, uint count);
bool join_union_type_attributes(THD *thd,
class Type_holder *holders, uint count);
public:
+ bool join_union_item_types(THD *thd, List<Item> &types, uint count);
+public:
// Ensures that at least all members used during cleanup() are initialized.
st_select_lex_unit()
: union_result(NULL), table(NULL), result(NULL),
@@ -776,9 +847,9 @@ public:
{
}
-
TABLE *table; /* temporary table using for appending UNION results */
select_result *result;
+ st_select_lex *pre_last_parse;
bool prepared, // prepare phase already performed for UNION (unit)
optimized, // optimize phase already performed for UNION (unit)
optimized_2,
@@ -867,11 +938,15 @@ public:
void init_query();
st_select_lex* outer_select();
+ const st_select_lex* first_select() const
+ {
+ return reinterpret_cast<const st_select_lex*>(slave);
+ }
st_select_lex* first_select()
{
return reinterpret_cast<st_select_lex*>(slave);
}
- inline void set_with_clause(With_clause *with_cl);
+ void set_with_clause(With_clause *with_cl);
st_select_lex_unit* next_unit()
{
return reinterpret_cast<st_select_lex_unit*>(next);
@@ -915,36 +990,70 @@ public:
int save_union_explain(Explain_query *output);
int save_union_explain_part2(Explain_query *output);
unit_common_op common_op();
+
+ void reset_distinct();
+ void fix_distinct();
+
+ void register_select_chain(SELECT_LEX *first_sel);
+
+ bool set_nest_level(int new_nest_level);
+ bool check_parameters(SELECT_LEX *main_select);
+
+ bool set_lock_to_the_last_select(Lex_select_lock l);
+
+ friend class st_select_lex;
};
typedef class st_select_lex_unit SELECT_LEX_UNIT;
typedef Bounds_checked_array<Item*> Ref_ptr_array;
-/*
- Structure which consists of the field and the item which
- produces this field.
+/**
+ Structure which consists of the field and the item that
+ corresponds to this field.
*/
-class Grouping_tmp_field :public Sql_alloc
+class Field_pair :public Sql_alloc
{
public:
- Field *tmp_field;
- Item *producing_item;
- Grouping_tmp_field(Field *fld, Item *item)
- :tmp_field(fld), producing_item(item) {}
+ Field *field;
+ Item *corresponding_item;
+ Field_pair(Field *fld, Item *item)
+ :field(fld), corresponding_item(item) {}
};
+Field_pair *get_corresponding_field_pair(Item *item,
+ List<Field_pair> pair_list);
+Field_pair *find_matching_field_pair(Item *item, List<Field_pair> pair_list);
+
#define TOUCHED_SEL_COND 1/* WHERE/HAVING/ON should be reinited before use */
#define TOUCHED_SEL_DERIVED (1<<1)/* derived should be reinited before use */
+
/*
SELECT_LEX - store information of parsed SELECT statment
*/
class st_select_lex: public st_select_lex_node
{
public:
+ /*
+ Currently the field first_nested is used only by parser.
+ It containa either a reference to the first select
+ of the nest of selects to which 'this' belongs to, or
+ in the case of priority jump it contains a reference to
+ the select to which the priority nest has to be attached to.
+ If there is no priority jump then the first select of the
+ nest contains the reference to itself in first_nested.
+ Example:
+ select1 union select2 intersect select
+ Here we have a priority jump at select2.
+ So select2->first_nested points to select1,
+ while select3->first_nested points to select2 and
+ select1->first_nested points to select1.
+ */
+ st_select_lex *first_nested;
+
Name_resolution_context context;
LEX_CSTRING db;
Item *where, *having; /* WHERE & HAVING clauses */
@@ -952,6 +1061,7 @@ public:
Item *prep_having;/* saved HAVING clause for prepared statement processing */
Item *cond_pushed_into_where; /* condition pushed into the select's WHERE */
Item *cond_pushed_into_having; /* condition pushed into the select's HAVING */
+ List<Item> attach_to_conds;
/* Saved values of the WHERE and HAVING clauses*/
Item::cond_result cond_value, having_value;
/*
@@ -1040,6 +1150,7 @@ public:
SQL_I_List<ORDER> order_list; /* ORDER clause */
SQL_I_List<ORDER> gorder_list;
Item *select_limit, *offset_limit; /* LIMIT clause parameters */
+ bool is_set_query_expr_tail;
/// Array of pointers to top elements of all_fields list
Ref_ptr_array ref_pointer_array;
@@ -1074,6 +1185,7 @@ public:
*/
uint fields_in_window_functions;
enum_parsing_place parsing_place; /* where we are parsing expression */
+ enum_parsing_place save_parsing_place;
enum_parsing_place context_analysis_place; /* where we are in prepare */
bool with_sum_func; /* sum function indicator */
@@ -1141,6 +1253,8 @@ public:
bool no_wrap_view_item;
/* exclude this select from check of unique_table() */
bool exclude_from_table_unique_test;
+ /* the select is "service-select" and can not have tables*/
+ bool is_service_select;
/* index in the select list of the expression currently being fixed */
int cur_pos_in_select_list;
@@ -1171,7 +1285,8 @@ public:
nesting_map name_visibility_map;
table_map with_dep;
- List<Grouping_tmp_field> grouping_tmp_fields;
+ /* the structure to store fields that are used in the GROUP BY of this select */
+ List<Field_pair> grouping_tmp_fields;
/* it is for correct printing SELECT options */
thr_lock_type lock_type;
@@ -1181,6 +1296,11 @@ public:
table_value_constr *tvc;
bool in_tvc;
+ /* The interface employed to execute the select query by a foreign engine */
+ select_handler *select_h;
+ /* The object used to organize execution of the query by a foreign engine */
+ Pushdown_select *pushdown_select;
+
/** System Versioning */
public:
uint versioned_tables;
@@ -1188,9 +1308,18 @@ public:
/* push new Item_field into item_list */
bool vers_push_field(THD *thd, TABLE_LIST *table, const LEX_CSTRING field_name);
+ int period_setup_conds(THD *thd, TABLE_LIST *table);
void init_query();
void init_select();
st_select_lex_unit* master_unit() { return (st_select_lex_unit*) master; }
+ inline void set_master_unit(st_select_lex_unit *master_unit)
+ {
+ master= (st_select_lex_node *)master_unit;
+ }
+ void set_master(st_select_lex *master_arg)
+ {
+ master= master_arg;
+ }
st_select_lex_unit* first_inner_unit()
{
return (st_select_lex_unit*) slave;
@@ -1245,12 +1374,6 @@ public:
List<Item>* get_item_list();
ulong get_table_join_options();
void set_lock_for_tables(thr_lock_type lock_type, bool for_update);
- inline void init_order()
- {
- order_list.elements= 0;
- order_list.first= 0;
- order_list.next= &order_list.first;
- }
/*
This method created for reiniting LEX in mysql_admin_table() and can be
used only if you are going remove all SELECT_LEX & units except belonger
@@ -1383,9 +1506,10 @@ public:
With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
bool check_unrestricted_recursive(bool only_standard_compliant);
bool check_subqueries_with_recursive_references();
- void collect_grouping_fields(THD *thd, ORDER *grouping_list);
- void check_cond_extraction_for_grouping_fields(Item *cond,
- TABLE_LIST *derived);
+ void collect_grouping_fields_for_derived(THD *thd, ORDER *grouping_list);
+ bool collect_grouping_fields(THD *thd);
+ bool collect_fields_equal_to_grouping(THD *thd);
+ void check_cond_extraction_for_grouping_fields(THD *thd, Item *cond);
Item *build_cond_for_grouping_fields(THD *thd, Item *cond,
bool no_to_clones);
@@ -1408,6 +1532,15 @@ public:
bool cond_pushdown_is_allowed() const
{ return !olap && !explicit_limit && !tvc; }
+ bool build_pushable_cond_for_having_pushdown(THD *thd, Item *cond);
+ void pushdown_cond_into_where_clause(THD *thd, Item *extracted_cond,
+ Item **remaining_cond,
+ Item_transformer transformer,
+ uchar *arg);
+ Item *pushdown_from_having_into_where(THD *thd, Item *having);
+
+ select_handler *find_select_handler(THD *thd);
+
private:
bool m_non_agg_field_used;
bool m_agg_func_used;
@@ -1425,6 +1558,35 @@ public:
DBUG_ASSERT(this != sel);
select_n_where_fields+= sel->select_n_where_fields;
}
+ inline void set_linkage_and_distinct(enum sub_select_type l, bool d)
+ {
+ DBUG_ENTER("SELECT_LEX::set_linkage_and_distinct");
+ DBUG_PRINT("info", ("select: %p distinct %d", this, d));
+ set_linkage(l);
+ DBUG_ASSERT(l == UNION_TYPE ||
+ l == INTERSECT_TYPE ||
+ l == EXCEPT_TYPE);
+ if (d && master_unit() && master_unit()->union_distinct != this)
+ master_unit()->union_distinct= this;
+ distinct= d;
+ with_all_modifier= !distinct;
+ DBUG_VOID_RETURN;
+ }
+ bool set_nest_level(int new_nest_level);
+ bool check_parameters(SELECT_LEX *main_select);
+ void mark_select()
+ {
+ DBUG_ENTER("st_select_lex::mark_select()");
+ DBUG_PRINT("info", ("Select #%d", select_number));
+ DBUG_VOID_RETURN;
+ }
+ void register_unit(SELECT_LEX_UNIT *unit,
+ Name_resolution_context *outer_context);
+ SELECT_LEX_UNIT *attach_selects_chain(SELECT_LEX *sel,
+ Name_resolution_context *context);
+ void add_statistics(SELECT_LEX_UNIT *unit);
+ bool make_unique_derived_name(THD *thd, LEX_CSTRING *alias);
+ void lex_start(LEX *plex);
};
typedef class st_select_lex SELECT_LEX;
@@ -2561,7 +2723,7 @@ private:
int scan_ident_start(THD *thd, Lex_ident_cli_st *str);
int scan_ident_middle(THD *thd, Lex_ident_cli_st *str,
CHARSET_INFO **cs, my_lex_states *);
- int scan_ident_delimited(THD *thd, Lex_ident_cli_st *str);
+ int scan_ident_delimited(THD *thd, Lex_ident_cli_st *str, uchar quote_char);
bool get_7bit_or_8bit_ident(THD *thd, uchar *last_char);
/** Current thread. */
@@ -2836,15 +2998,103 @@ public:
Explain_delete* save_explain_delete_data(MEM_ROOT *mem_root, THD *thd);
};
+enum account_lock_type
+{
+ ACCOUNTLOCK_UNSPECIFIED= 0,
+ ACCOUNTLOCK_LOCKED,
+ ACCOUNTLOCK_UNLOCKED
+};
+
+enum password_exp_type
+{
+ PASSWORD_EXPIRE_UNSPECIFIED= 0,
+ PASSWORD_EXPIRE_NOW,
+ PASSWORD_EXPIRE_NEVER,
+ PASSWORD_EXPIRE_DEFAULT,
+ PASSWORD_EXPIRE_INTERVAL
+};
+
+struct Account_options: public USER_RESOURCES
+{
+ Account_options() { }
+
+ void reset()
+ {
+ bzero(this, sizeof(*this));
+ ssl_type= SSL_TYPE_NOT_SPECIFIED;
+ }
+
+ enum SSL_type ssl_type; // defined in violite.h
+ LEX_CSTRING x509_subject, x509_issuer, ssl_cipher;
+ account_lock_type account_locked;
+ password_exp_type password_expire;
+ longlong num_expiration_days;
+};
class Query_arena_memroot;
/* The state of the lex parsing. This is saved in the THD struct */
+
+class Lex_prepared_stmt
+{
+ Lex_ident_sys m_name; // Statement name (in all queries)
+ Item *m_code; // PREPARE or EXECUTE IMMEDIATE source expression
+ List<Item> m_params; // List of parameters for EXECUTE [IMMEDIATE]
+public:
+
+ Lex_prepared_stmt()
+ :m_code(NULL)
+ { }
+ const Lex_ident_sys &name() const
+ {
+ return m_name;
+ }
+ uint param_count() const
+ {
+ return m_params.elements;
+ }
+ List<Item> &params()
+ {
+ return m_params;
+ }
+ void set(const Lex_ident_sys_st &ident, Item *code, List<Item> *params)
+ {
+ DBUG_ASSERT(m_params.elements == 0);
+ m_name= ident;
+ m_code= code;
+ if (params)
+ m_params= *params;
+ }
+ bool params_fix_fields(THD *thd)
+ {
+ // Fix Items in the EXECUTE..USING list
+ List_iterator_fast<Item> param_it(m_params);
+ while (Item *param= param_it++)
+ {
+ if (param->fix_fields_if_needed_for_scalar(thd, 0))
+ return true;
+ }
+ return false;
+ }
+ bool get_dynamic_sql_string(THD *thd, LEX_CSTRING *dst, String *buffer);
+ void lex_start()
+ {
+ m_params.empty();
+ }
+};
+
+
struct LEX: public Query_tables_list
{
SELECT_LEX_UNIT unit; /* most upper unit */
- SELECT_LEX select_lex; /* first SELECT_LEX */
+ SELECT_LEX *first_select_lex() { return unit.first_select(); }
+ const SELECT_LEX *first_select_lex() const { return unit.first_select(); }
+
+private:
+ SELECT_LEX builtin_select;
/* current SELECT_LEX in parsing */
+
+public:
SELECT_LEX *current_select;
/* list of all SELECT_LEX */
SELECT_LEX *all_selects_list;
@@ -2890,7 +3140,6 @@ struct LEX: public Query_tables_list
const char *help_arg;
const char *backup_dir; /* For RESTORE/BACKUP */
const char* to_log; /* For PURGE MASTER LOGS TO */
- const char* x509_subject,*x509_issuer,*ssl_cipher;
String *wild; /* Wildcard in SHOW {something} LIKE 'wild'*/
sql_exchange *exchange;
select_result *result;
@@ -2926,6 +3175,9 @@ struct LEX: public Query_tables_list
*/
LEX_USER *definer;
+ /* Used in ALTER/CREATE user to store account locking options */
+ Account_options account_options;
+
Table_type table_type; /* Used for SHOW CREATE */
List<Key_part_spec> ref_list;
List<LEX_USER> users_list;
@@ -2950,6 +3202,12 @@ private:
bool sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop);
bool sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop);
+ /*
+ Check if Item_field and Item_ref are allowed in the current statement.
+ @retval false OK (fields are allowed)
+ @retval true ERROR (fields are not allowed). Error is raised.
+ */
+ bool check_expr_allows_fields_or_error(THD *thd, const char *name) const;
public:
void parse_error(uint err_number= ER_SYNTAX_ERROR);
inline bool is_arena_for_set_stmt() {return arena_for_set_stmt != 0;}
@@ -2977,6 +3235,8 @@ public:
required a local context, the parser pops the top-most context.
*/
List<Name_resolution_context> context_stack;
+ SELECT_LEX *select_stack[MAX_SELECT_NESTING + 1];
+ uint select_stack_top;
SQL_I_List<ORDER> proc_list;
SQL_I_List<TABLE_LIST> auxiliary_table_list, save_list;
@@ -2989,13 +3249,13 @@ public:
LEX_MASTER_INFO mi; // used by CHANGE MASTER
LEX_SERVER_OPTIONS server_options;
LEX_CSTRING relay_log_connection_name;
- USER_RESOURCES mqh;
LEX_RESET_SLAVE reset_slave_info;
ulonglong type;
ulong next_binlog_file_number;
/* The following is used by KILL */
killed_state kill_signal;
killed_type kill_type;
+ bool is_shutdown_wait_for_slaves;
/*
This variable is used in post-parse stage to declare that sum-functions,
or functions which have sense only if GROUP BY is present, are allowed.
@@ -3012,10 +3272,12 @@ public:
/*
Usually `expr` rule of yacc is quite reused but some commands better
not support subqueries which comes standard with this rule, like
- KILL, HA_READ, CREATE/ALTER EVENT etc. Set this to `false` to get
- syntax error back.
+ KILL, HA_READ, CREATE/ALTER EVENT etc. Set this to a non-NULL
+ clause name to get an error.
*/
- bool expr_allows_subselect;
+ const char *clause_that_disallows_subselect;
+ bool selects_allow_into;
+ bool selects_allow_procedure;
/*
A special command "PARSE_VCOL_EXPR" is defined for the parser
to translate a defining expression of a virtual column into an
@@ -3025,7 +3287,6 @@ public:
*/
bool parse_vcol_expr;
- enum SSL_type ssl_type; // defined in violite.h
enum enum_duplicates duplicates;
enum enum_tx_isolation tx_isolation;
enum enum_ha_read_modes ha_read_mode;
@@ -3041,6 +3302,7 @@ public:
uint profile_query_id;
uint profile_options;
uint grant, grant_tot_col, which_columns;
+ enum backup_stages backup_stage;
enum Foreign_key::fk_match_opt fk_match_option;
enum_fk_option fk_update_opt;
enum_fk_option fk_delete_opt;
@@ -3070,7 +3332,17 @@ public:
enum enum_yes_no_unknown tx_chain, tx_release;
bool safe_to_cache_query;
bool ignore;
+ bool next_is_main; // use "main" SELECT_LEX for nrxt allocation;
+ bool next_is_down; // use "main" SELECT_LEX for nrxt allocation;
st_parsing_options parsing_options;
+ uint8 lex_options; // see OPTION_LEX_*
+ /*
+ In sql_cache we store SQL_CACHE flag as specified by user to be
+ able to restore SELECT statement from internal structures.
+ */
+ enum e_sql_cache { SQL_CACHE_UNSPECIFIED, SQL_NO_CACHE, SQL_CACHE };
+ e_sql_cache sql_cache;
+
Alter_info alter_info;
/*
For CREATE TABLE statement last element of table list which is not
@@ -3078,12 +3350,7 @@ public:
creating or last of tables referenced by foreign keys).
*/
TABLE_LIST *create_last_non_select_table;
- /* Prepared statements SQL syntax:*/
- LEX_CSTRING prepared_stmt_name; /* Statement name (in all queries) */
- /* PREPARE or EXECUTE IMMEDIATE source expression */
- Item *prepared_stmt_code;
- /* Names of user variables holding parameters (in EXECUTE) */
- List<Item> prepared_stmt_params;
+ Lex_prepared_stmt prepared_stmt;
sp_head *sphead;
sp_name *spname;
bool sp_lex_in_use; // Keep track on lex usage in SPs for error handling
@@ -3211,6 +3478,7 @@ public:
/* System Versioning */
vers_select_conds_t vers_conditions;
+ vers_select_conds_t period_conditions;
inline void free_set_stmt_mem_root()
{
@@ -3268,20 +3536,24 @@ public:
SELECT_LEX *sl;
SELECT_LEX_UNIT *un;
for (sl= current_select, un= sl->master_unit();
- un != &unit;
- sl= sl->outer_select(), un= sl->master_unit())
+ un && un != &unit;
+ sl= sl->outer_select(), un= (sl ? sl->master_unit() : NULL))
{
- sl->uncacheable|= cause;
- un->uncacheable|= cause;
+ sl->uncacheable|= cause;
+ un->uncacheable|= cause;
}
- select_lex.uncacheable|= cause;
+ if (sl)
+ sl->uncacheable|= cause;
}
+ if (first_select_lex())
+ first_select_lex()->uncacheable|= cause;
}
void set_trg_event_type_for_tables();
TABLE_LIST *unlink_first_table(bool *link_to_local);
void link_first_table_back(TABLE_LIST *first, bool link_to_local);
void first_lists_tables_same();
+ void fix_first_select_number();
bool can_be_merged();
bool can_use_merged();
@@ -3319,14 +3591,74 @@ public:
void cleanup_after_one_table_open();
- bool push_context(Name_resolution_context *context, MEM_ROOT *mem_root)
+ bool push_context(Name_resolution_context *context);
+
+ Name_resolution_context *pop_context();
+
+ SELECT_LEX *select_stack_head()
{
- return context_stack.push_front(context, mem_root);
+ if (likely(select_stack_top))
+ return select_stack[select_stack_top - 1];
+ return NULL;
+ }
+
+ bool push_select(SELECT_LEX *select_lex)
+ {
+ DBUG_ENTER("LEX::push_select");
+ DBUG_PRINT("info", ("Top Select was %p (%d) depth: %u pushed: %p (%d)",
+ select_stack_head(),
+ select_stack_top,
+ (select_stack_top ?
+ select_stack_head()->select_number :
+ 0),
+ select_lex, select_lex->select_number));
+ if (unlikely(select_stack_top > MAX_SELECT_NESTING))
+ {
+ my_error(ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (push_context(&select_lex->context))
+ DBUG_RETURN(TRUE);
+ select_stack[select_stack_top++]= select_lex;
+ current_select= select_lex;
+ DBUG_RETURN(FALSE);
+ }
+
+ SELECT_LEX *pop_select()
+ {
+ DBUG_ENTER("LEX::pop_select");
+ SELECT_LEX *select_lex;
+ if (likely(select_stack_top))
+ select_lex= select_stack[--select_stack_top];
+ else
+ select_lex= 0;
+ DBUG_PRINT("info", ("Top Select is %p (%d) depth: %u poped: %p (%d)",
+ select_stack_head(),
+ select_stack_top,
+ (select_stack_top ?
+ select_stack_head()->select_number :
+ 0),
+ select_lex,
+ (select_lex ? select_lex->select_number : 0)));
+ DBUG_ASSERT(select_lex);
+
+ pop_context();
+
+ if (unlikely(!select_stack_top))
+ {
+ current_select= &builtin_select;
+ DBUG_PRINT("info", ("Top Select is empty -> sel builtin: %p",
+ current_select));
+ }
+ else
+ current_select= select_stack[select_stack_top - 1];
+
+ DBUG_RETURN(select_lex);
}
- Name_resolution_context *pop_context()
+ SELECT_LEX *current_select_or_default()
{
- return context_stack.pop();
+ return current_select ? current_select : &builtin_select;
}
bool copy_db_to(LEX_CSTRING *to);
@@ -3335,6 +3667,7 @@ public:
{
return context_stack.head();
}
+
/*
Restore the LEX and THD in case of a parse error.
*/
@@ -3363,9 +3696,8 @@ public:
on its top. So select_lex (as the first added) will be at the tail
of the list.
*/
- if (&select_lex == all_selects_list && !sroutines.records)
+ if (first_select_lex() == all_selects_list && !sroutines.records)
{
- DBUG_ASSERT(!all_selects_list->next_select_in_list());
return TRUE;
}
return FALSE;
@@ -3386,27 +3718,17 @@ public:
bool last_field_generated_always_as_row_end();
bool set_bincmp(CHARSET_INFO *cs, bool bin);
- bool get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer);
- bool prepared_stmt_params_fix_fields(THD *thd)
- {
- // Fix Items in the EXECUTE..USING list
- List_iterator_fast<Item> param_it(prepared_stmt_params);
- while (Item *param= param_it++)
- {
- if (param->fix_fields_if_needed_for_scalar(thd, 0))
- return true;
- }
- return false;
- }
+ bool new_sp_instr_stmt(THD *, const LEX_CSTRING &prefix,
+ const LEX_CSTRING &suffix);
+ bool sp_proc_stmt_statement_finalize_buf(THD *, const LEX_CSTRING &qbuf);
+ bool sp_proc_stmt_statement_finalize(THD *, bool no_lookahead);
+
sp_variable *sp_param_init(LEX_CSTRING *name);
bool sp_param_fill_definition(sp_variable *spvar);
int case_stmt_action_expr(Item* expr);
int case_stmt_action_when(Item *when, bool simple);
int case_stmt_action_then();
- bool add_select_to_union_list(bool is_union_distinct,
- enum sub_select_type type,
- bool is_top_level);
bool setup_select_in_parentheses();
bool set_trigger_new_row(const LEX_CSTRING *name, Item *val);
bool set_trigger_field(const LEX_CSTRING *name1, const LEX_CSTRING *name2,
@@ -3428,19 +3750,17 @@ public:
sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name1,
const LEX_CSTRING *name2);
sp_name *make_sp_name_package_routine(THD *thd, const LEX_CSTRING *name);
- sp_head *make_sp_head(THD *thd, const sp_name *name, const Sp_handler *sph);
+ sp_head *make_sp_head(THD *thd, const sp_name *name, const Sp_handler *sph,
+ enum_sp_aggregate_type agg_type);
sp_head *make_sp_head_no_recursive(THD *thd, const sp_name *name,
- const Sp_handler *sph);
- sp_head *make_sp_head_no_recursive(THD *thd,
- DDL_options_st options, sp_name *name,
- const Sp_handler *sph)
- {
- if (add_create_options_with_check(options))
- return NULL;
- return make_sp_head_no_recursive(thd, name, sph);
- }
+ const Sp_handler *sph,
+ enum_sp_aggregate_type agg_type);
+ bool sp_body_finalize_routine(THD *);
+ bool sp_body_finalize_trigger(THD *);
+ bool sp_body_finalize_event(THD *);
bool sp_body_finalize_function(THD *);
bool sp_body_finalize_procedure(THD *);
+ bool sp_body_finalize_procedure_standalone(THD *, const sp_name *end_name);
sp_package *create_package_start(THD *thd,
enum_sql_command command,
const Sp_handler *sph,
@@ -3531,7 +3851,12 @@ public:
return create_item_qualified_asterisk(thd, &a, &b);
}
- Item *create_item_ident_nosp(THD *thd, Lex_ident_sys_st *name);
+ Item *create_item_ident_field(THD *thd, const char *db, const char *table,
+ const Lex_ident_sys_st *name);
+ Item *create_item_ident_nosp(THD *thd, Lex_ident_sys_st *name)
+ {
+ return create_item_ident_field(thd, NullS, NullS, name);
+ }
Item *create_item_ident_sp(THD *thd, Lex_ident_sys_st *name,
const char *start, const char *end);
Item *create_item_ident(THD *thd, Lex_ident_cli_st *cname)
@@ -3669,6 +3994,8 @@ public:
const Lex_ident_cli_st *var_name,
const Lex_ident_cli_st *field_name);
+ Item *create_item_query_expression(THD *thd, st_select_lex_unit *unit);
+
Item *make_item_func_replace(THD *thd, Item *org, Item *find, Item *replace);
Item *make_item_func_substr(THD *thd, Item *a, Item *b, Item *c);
Item *make_item_func_substr(THD *thd, Item *a, Item *b);
@@ -3867,6 +4194,17 @@ public:
sp_for_loop_intrange_finalize(thd, loop);
}
bool sp_for_loop_outer_block_finalize(THD *thd, const Lex_for_loop_st &loop);
+
+ /*
+ Make an Item when an identifier is found in the FOR loop bounds:
+ FOR rec IN cursor
+ FOR rec IN var1 .. var2
+ FOR rec IN row1.field1 .. xxx
+ */
+ Item *create_item_for_loop_bound(THD *thd,
+ const LEX_CSTRING *a,
+ const LEX_CSTRING *b,
+ const LEX_CSTRING *c);
/* End of FOR LOOP methods */
bool add_signal_statement(THD *thd, const class sp_condition_value *value);
@@ -3922,10 +4260,10 @@ public:
void add_key_to_list(LEX_CSTRING *field_name,
enum Key::Keytype type, bool check_exists);
// Add a constraint as a part of CREATE TABLE or ALTER TABLE
- bool add_constraint(LEX_CSTRING *name, Virtual_column_info *constr,
+ bool add_constraint(const LEX_CSTRING &name, Virtual_column_info *constr,
bool if_not_exists)
{
- constr->name= *name;
+ constr->name= name;
constr->flags= if_not_exists ?
Alter_info::CHECK_CONSTRAINT_IF_NOT_EXISTS : 0;
alter_info.check_constraint_list.push_back(constr);
@@ -3966,6 +4304,7 @@ public:
return check_create_options(create_info);
}
bool sp_add_cfetch(THD *thd, const LEX_CSTRING *name);
+ bool sp_add_agg_cfetch();
bool set_command_with_check(enum_sql_command command,
uint scope,
@@ -4015,14 +4354,16 @@ public:
bool create_like() const
{
- DBUG_ASSERT(!create_info.like() || !select_lex.item_list.elements);
+ DBUG_ASSERT(!create_info.like() ||
+ !first_select_lex()->item_list.elements);
return create_info.like();
}
bool create_select() const
{
- DBUG_ASSERT(!create_info.like() || !select_lex.item_list.elements);
- return select_lex.item_list.elements;
+ DBUG_ASSERT(!create_info.like() ||
+ !first_select_lex()->item_list.elements);
+ return first_select_lex()->item_list.elements;
}
bool create_simple() const
@@ -4031,7 +4372,7 @@ public:
}
SELECT_LEX *exclude_last_select();
- bool add_unit_in_brackets(SELECT_LEX *nselect);
+ SELECT_LEX *exclude_not_first_select(SELECT_LEX *exclude);
void check_automatic_up(enum sub_select_type type);
bool create_or_alter_view_finalize(THD *thd, Table_ident *table_ident);
bool add_alter_view(THD *thd, uint16 algorithm, enum_view_suid suid,
@@ -4039,7 +4380,6 @@ public:
bool add_create_view(THD *thd, DDL_options_st ddl,
uint16 algorithm, enum_view_suid suid,
Table_ident *table_ident);
-
bool add_grant_command(THD *thd, enum_sql_command sql_command_arg,
stored_procedure_type type_arg);
@@ -4047,6 +4387,36 @@ public:
{
return create_info.vers_info;
}
+
+ int add_period(Lex_ident name, Lex_ident_sys_st start, Lex_ident_sys_st end)
+ {
+ if (lex_string_cmp(system_charset_info, &start, &end) == 0)
+ {
+ my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), start.str);
+ return 1;
+ }
+
+ Table_period_info &info= create_info.period_info;
+
+ if (check_exists && info.name.streq(name))
+ return 0;
+
+ if (info.is_set())
+ {
+ my_error(ER_MORE_THAN_ONE_PERIOD, MYF(0));
+ return 1;
+ }
+ info.set_period(start, end);
+ info.name= name;
+
+ info.constr= new Virtual_column_info();
+ info.constr->expr= lt_creator.create(thd,
+ create_item_ident_nosp(thd, &start),
+ create_item_ident_nosp(thd, &end));
+ add_constraint(null_clex_str, info.constr, false);
+ return 0;
+ }
+
sp_package *get_sp_package() const;
/**
@@ -4058,7 +4428,7 @@ public:
*/
bool check_simple_select(const LEX_CSTRING *option)
{
- if (current_select != &select_lex)
+ if (current_select != &builtin_select)
{
char command[80];
strmake(command, option->str, MY_MIN(option->length, sizeof(command)-1));
@@ -4068,17 +4438,140 @@ public:
return false;
}
+ SELECT_LEX_UNIT *alloc_unit();
+ SELECT_LEX *alloc_select(bool is_select);
+ SELECT_LEX_UNIT *create_unit(SELECT_LEX*);
+ SELECT_LEX *wrap_unit_into_derived(SELECT_LEX_UNIT *unit);
+ SELECT_LEX *wrap_select_chain_into_derived(SELECT_LEX *sel);
+ bool main_select_push(bool service= false);
+ bool insert_select_hack(SELECT_LEX *sel);
+ SELECT_LEX *create_priority_nest(SELECT_LEX *first_in_nest);
+
+ bool set_main_unit(st_select_lex_unit *u)
+ {
+ unit.options= u->options;
+ unit.uncacheable= u->uncacheable;
+ unit.register_select_chain(u->first_select());
+ unit.first_select()->options|= builtin_select.options;
+ unit.fake_select_lex= u->fake_select_lex;
+ unit.union_distinct= u->union_distinct;
+ unit.set_with_clause(u->with_clause);
+ builtin_select.exclude_from_global();
+ return false;
+ }
+ bool check_main_unit_semantics();
+
+ SELECT_LEX_UNIT *parsed_select_expr_start(SELECT_LEX *s1, SELECT_LEX *s2,
+ enum sub_select_type unit_type,
+ bool distinct);
+ SELECT_LEX_UNIT *parsed_select_expr_cont(SELECT_LEX_UNIT *unit,
+ SELECT_LEX *s2,
+ enum sub_select_type unit_type,
+ bool distinct, bool oracle);
+ bool parsed_multi_operand_query_expression_body(SELECT_LEX_UNIT *unit);
+ SELECT_LEX_UNIT *add_tail_to_query_expression_body(SELECT_LEX_UNIT *unit,
+ Lex_order_limit_lock *l);
+ SELECT_LEX_UNIT *
+ add_tail_to_query_expression_body_ext_parens(SELECT_LEX_UNIT *unit,
+ Lex_order_limit_lock *l);
+ SELECT_LEX_UNIT *parsed_body_ext_parens_primary(SELECT_LEX_UNIT *unit,
+ SELECT_LEX *primary,
+ enum sub_select_type unit_type,
+ bool distinct);
+ SELECT_LEX_UNIT *
+ add_primary_to_query_expression_body(SELECT_LEX_UNIT *unit,
+ SELECT_LEX *sel,
+ enum sub_select_type unit_type,
+ bool distinct,
+ bool oracle);
+ SELECT_LEX_UNIT *
+ add_primary_to_query_expression_body_ext_parens(
+ SELECT_LEX_UNIT *unit,
+ SELECT_LEX *sel,
+ enum sub_select_type unit_type,
+ bool distinct);
+ SELECT_LEX *parsed_subselect(SELECT_LEX_UNIT *unit);
+ bool parsed_insert_select(SELECT_LEX *firs_select);
void save_values_list_state();
void restore_values_list_state();
- void tvc_start();
- bool tvc_start_derived();
- bool tvc_finalize();
- bool tvc_finalize_derived();
+ bool parsed_TVC_start();
+ SELECT_LEX *parsed_TVC_end();
+ TABLE_LIST *parsed_derived_table(SELECT_LEX_UNIT *unit,
+ int for_system_time,
+ LEX_CSTRING *alias);
+ bool parsed_create_view(SELECT_LEX_UNIT *unit, int check);
+ bool select_finalize(st_select_lex_unit *expr);
+ bool select_finalize(st_select_lex_unit *expr, Lex_select_lock l);
+ void relink_hack(st_select_lex *select_lex);
+
+ bool stmt_install_plugin(const DDL_options_st &opt,
+ const Lex_ident_sys_st &name,
+ const LEX_CSTRING &soname);
+ void stmt_install_plugin(const LEX_CSTRING &soname);
+
+ bool stmt_uninstall_plugin_by_name(const DDL_options_st &opt,
+ const Lex_ident_sys_st &name);
+ bool stmt_uninstall_plugin_by_soname(const DDL_options_st &opt,
+ const LEX_CSTRING &soname);
+ bool stmt_prepare_validate(const char *stmt_type);
+ bool stmt_prepare(const Lex_ident_sys_st &ident, Item *code);
+ bool stmt_execute(const Lex_ident_sys_st &ident, List<Item> *params);
+ bool stmt_execute_immediate(Item *code, List<Item> *params);
+ void stmt_deallocate_prepare(const Lex_ident_sys_st &ident);
+
+ bool stmt_alter_table_exchange_partition(Table_ident *table);
+
+ void stmt_purge_to(const LEX_CSTRING &to);
+ bool stmt_purge_before(Item *item);
+
+private:
+ bool stmt_create_routine_start(const DDL_options_st &options)
+ {
+ create_info.set(options);
+ return main_select_push() || check_create_options(options);
+ }
+public:
+ bool stmt_create_function_start(const DDL_options_st &options)
+ {
+ sql_command= SQLCOM_CREATE_SPFUNCTION;
+ return stmt_create_routine_start(options);
+ }
+ bool stmt_create_procedure_start(const DDL_options_st &options)
+ {
+ sql_command= SQLCOM_CREATE_PROCEDURE;
+ return stmt_create_routine_start(options);
+ }
+ void stmt_create_routine_finalize()
+ {
+ pop_select(); // main select
+ }
+
+ bool stmt_create_stored_function_start(const DDL_options_st &options,
+ enum_sp_aggregate_type,
+ const sp_name *name);
+ bool stmt_create_stored_function_finalize_standalone(const sp_name *end_name);
+
+ bool stmt_create_udf_function(const DDL_options_st &options,
+ enum_sp_aggregate_type agg_type,
+ const Lex_ident_sys_st &name,
+ Item_result return_type,
+ const LEX_CSTRING &soname);
+ Spvar_definition *row_field_name(THD *thd, const Lex_ident_sys_st &name);
bool map_data_type(const Lex_ident_sys_st &schema,
Lex_field_type_st *type) const;
void mark_first_table_as_inserting();
+
+ bool fields_are_impossible()
+ {
+ // no select or it is last select with no tables (service select)
+ return !select_stack_head() ||
+ (select_stack_top == 1 &&
+ select_stack[0]->is_service_select);
+ }
+
+
};
@@ -4366,8 +4859,10 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr);
Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
Item *expr);
-void sp_create_assignment_lex(THD *thd, bool no_lookahead);
+bool sp_create_assignment_lex(THD *thd, bool no_lookahead);
bool sp_create_assignment_instr(THD *thd, bool no_lookahead);
+void mark_or_conds_to_avoid_pushdown(Item *cond);
+
#endif /* MYSQL_SERVER */
#endif /* SQL_LEX_INCLUDED */
diff --git a/sql/sql_list.h b/sql/sql_list.h
index a47acf57b54..9d1c01a484d 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -21,6 +21,7 @@
#endif
#include "sql_alloc.h"
+#include <iterator>
/**
Simple intrusive linked list.
@@ -526,6 +527,63 @@ public:
empty();
}
T *elem(uint n) { return (T*) base_list::elem(n); }
+ // Create a new list with one element
+ static List<T> *make(MEM_ROOT *mem_root, T *first)
+ {
+ List<T> *res= new (mem_root) List<T>;
+ return res == NULL || res->push_back(first, mem_root) ? NULL : res;
+ }
+
+ class Iterator;
+ using value_type= T;
+ using iterator= Iterator;
+ using const_iterator= const Iterator;
+
+ Iterator begin() const { return Iterator(first); }
+ Iterator end() const { return Iterator(); }
+
+ class Iterator
+ {
+ public:
+ using iterator_category= std::forward_iterator_tag;
+ using value_type= T;
+ using difference_type= std::ptrdiff_t;
+ using pointer= T *;
+ using reference= T &;
+
+ Iterator(list_node *p= &end_of_list) : node{p} {}
+
+ Iterator &operator++()
+ {
+ DBUG_ASSERT(node != &end_of_list);
+
+ node= node->next;
+ return *this;
+ }
+
+ T operator++(int)
+ {
+ Iterator tmp(*this);
+ operator++();
+ return tmp;
+ }
+
+ T &operator*() { return *static_cast<T *>(node->info); }
+ T *operator->() { return static_cast<T *>(node->info); }
+
+ bool operator==(const typename List<T>::iterator &rhs)
+ {
+ return node == rhs.node;
+ }
+
+ bool operator!=(const typename List<T>::iterator &rhs)
+ {
+ return node != rhs.node;
+ }
+
+ private:
+ list_node *node{&end_of_list};
+ };
};
@@ -619,7 +677,7 @@ struct ilink
struct ilink **prev,*next;
static void *operator new(size_t size) throw ()
{
- return (void*)my_malloc((uint)size, MYF(MY_WME | MY_FAE | ME_FATALERROR));
+ return (void*)my_malloc((uint)size, MYF(MY_WME | MY_FAE | ME_FATAL));
}
static void operator delete(void* ptr_arg, size_t)
{
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 09af120e6cf..afc2f121167 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -41,7 +41,8 @@
#include "sql_trigger.h"
#include "sql_derived.h"
#include "sql_show.h"
-#include "debug_sync.h"
+
+#include "wsrep_mysqld.h"
extern "C" int _my_b_net_read(IO_CACHE *info, uchar *Buffer, size_t Count);
@@ -99,64 +100,41 @@ public:
#define PUSH(A) *(stack_pos++)=(A)
#ifdef WITH_WSREP
-/** If requested by wsrep_load_data_splitting, commit and restart
-the transaction after every 10,000 inserted rows. */
-
-static bool wsrep_load_data_split(THD *thd, const TABLE *table,
- const COPY_INFO &info)
+/** If requested by wsrep_load_data_splitting and streaming replication is
+ not enabled, replicate a streaming fragment every 10,000 rows.*/
+class Wsrep_load_data_split
{
- DBUG_ENTER("wsrep_load_data_split");
-
- if (!wsrep_load_data_splitting || !wsrep_on(thd)
- || !info.records || (info.records % 10000)
- || !thd->transaction.stmt.ha_list
- || thd->transaction.stmt.ha_list->ht() != binlog_hton
- || !thd->transaction.stmt.ha_list->next()
- || thd->transaction.stmt.ha_list->next()->next())
- DBUG_RETURN(false);
-
- if (handlerton* hton= thd->transaction.stmt.ha_list->next()->ht())
+public:
+ Wsrep_load_data_split(THD *thd)
+ : m_thd(thd)
+ , m_load_data_splitting(wsrep_load_data_splitting)
+ , m_fragment_unit(thd->wsrep_trx().streaming_context().fragment_unit())
+ , m_fragment_size(thd->wsrep_trx().streaming_context().fragment_size())
{
- if (hton->db_type != DB_TYPE_INNODB)
- DBUG_RETURN(false);
- WSREP_DEBUG("intermediate transaction commit in LOAD DATA");
- wsrep_set_load_multi_commit(thd, true);
- if (wsrep_run_wsrep_commit(thd, true) != WSREP_TRX_OK) DBUG_RETURN(true);
- if (binlog_hton->commit(binlog_hton, thd, true)) DBUG_RETURN(true);
- wsrep_post_commit(thd, true);
- hton->commit(hton, thd, true);
- wsrep_set_load_multi_commit(thd, false);
- DEBUG_SYNC(thd, "intermediate_transaction_commit");
- table->file->extra(HA_EXTRA_FAKE_START_STMT);
+ if (WSREP(m_thd) && m_load_data_splitting)
+ {
+ /* Override streaming settings with backward compatible values for
+ load data splitting */
+ m_thd->wsrep_cs().streaming_params(wsrep::streaming_context::row, 10000);
+ }
}
- DBUG_RETURN(false);
-}
-/*
- If the commit fails, then an early return from
- the function occurs there and therefore we need
- to reset the table->auto_increment_field_not_null
- flag, which is usually reset after calling
- the write_record():
-*/
-#define WSREP_LOAD_DATA_SPLIT(thd,table,info) \
- if (wsrep_load_data_split(thd,table,info)) \
- { \
- table->auto_increment_field_not_null= FALSE; \
- DBUG_RETURN(1); \
+ ~Wsrep_load_data_split()
+ {
+ if (WSREP(m_thd) && m_load_data_splitting)
+ {
+ /* Restore original settings */
+ m_thd->wsrep_cs().streaming_params(m_fragment_unit, m_fragment_size);
+ }
}
-#else /* WITH_WSREP */
-#define WSREP_LOAD_DATA_SPLIT(thd,table,info) /* empty */
+private:
+ THD *m_thd;
+ my_bool m_load_data_splitting;
+ enum wsrep::streaming_context::fragment_unit m_fragment_unit;
+ size_t m_fragment_size;
+};
#endif /* WITH_WSREP */
-#define WRITE_RECORD(thd,table,info) \
- do { \
- int err_= write_record(thd, table, &info); \
- table->auto_increment_field_not_null= FALSE; \
- if (err_) \
- DBUG_RETURN(1); \
- } while (0)
-
class READ_INFO: public Load_data_param
{
File file;
@@ -374,6 +352,9 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list,
bool transactional_table __attribute__((unused));
DBUG_ENTER("mysql_load");
+#ifdef WITH_WSREP
+ Wsrep_load_data_split wsrep_load_data_split(thd);
+#endif /* WITH_WSREP */
/*
Bug #34283
mysqlbinlog leaves tmpfile after termination if binlog contains
@@ -410,10 +391,13 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list,
DBUG_RETURN(TRUE);
if (thd->lex->handle_list_of_derived(table_list, DT_PREPARE))
DBUG_RETURN(TRUE);
- if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
- &thd->lex->select_lex.top_join_list,
+ if (setup_tables_and_check_access(thd,
+ &thd->lex->first_select_lex()->context,
+ &thd->lex->first_select_lex()->
+ top_join_list,
table_list,
- thd->lex->select_lex.leaf_tables, FALSE,
+ thd->lex->first_select_lex()->leaf_tables,
+ FALSE,
INSERT_ACL | UPDATE_ACL,
INSERT_ACL | UPDATE_ACL, FALSE))
DBUG_RETURN(-1);
@@ -944,7 +928,7 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
List_iterator_fast<Item> it(fields_vars);
Item *item;
TABLE *table= table_list->table;
- bool progress_reports;
+ bool err, progress_reports;
ulonglong counter, time_to_report_progress;
DBUG_ENTER("read_fixed_length");
@@ -1035,9 +1019,11 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
DBUG_RETURN(-1);
}
- WSREP_LOAD_DATA_SPLIT(thd, table, info);
- WRITE_RECORD(thd, table, info);
-
+ err= write_record(thd, table, &info);
+ table->auto_increment_field_not_null= FALSE;
+ if (err)
+ DBUG_RETURN(1);
+
/*
We don't need to reset auto-increment field since we are restoring
its default value at the beginning of each loop iteration.
@@ -1070,7 +1056,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
Item *item;
TABLE *table= table_list->table;
uint enclosed_length;
- bool progress_reports;
+ bool err, progress_reports;
ulonglong counter, time_to_report_progress;
DBUG_ENTER("read_sep_field");
@@ -1175,9 +1161,10 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
DBUG_RETURN(-1);
}
- WSREP_LOAD_DATA_SPLIT(thd, table, info);
- WRITE_RECORD(thd, table, info);
-
+ err= write_record(thd, table, &info);
+ table->auto_increment_field_not_null= FALSE;
+ if (err)
+ DBUG_RETURN(1);
/*
We don't need to reset auto-increment field since we are restoring
its default value at the beginning of each loop iteration.
@@ -1221,6 +1208,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
for ( ; ; it.rewind())
{
+ bool err;
if (thd->killed)
{
thd->send_kill_message();
@@ -1294,10 +1282,12 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
case VIEW_CHECK_ERROR:
DBUG_RETURN(-1);
}
-
- WSREP_LOAD_DATA_SPLIT(thd, table, info);
- WRITE_RECORD(thd, table, info);
-
+
+ err= write_record(thd, table, &info);
+ table->auto_increment_field_not_null= false;
+ if (err)
+ DBUG_RETURN(1);
+
/*
We don't need to reset auto-increment field since we are restoring
its default value at the beginning of each loop iteration.
diff --git a/sql/sql_mode.cc b/sql/sql_mode.cc
index 32d19cecbdb..6e62fa8fa24 100644
--- a/sql/sql_mode.cc
+++ b/sql/sql_mode.cc
@@ -17,7 +17,7 @@
#include "mariadb.h"
#include "set_var.h"
-void Sql_mode_dependency::push_dependency_warnings(THD *thd)
+void Sql_mode_dependency::push_dependency_warnings(THD *thd) const
{
sql_mode_t all= m_hard | m_soft;
for (uint i= 0; all ; i++, all >>= 1)
diff --git a/sql/sql_mode.h b/sql/sql_mode.h
index e92848fb6d1..fb2b7cefa0a 100644
--- a/sql/sql_mode.h
+++ b/sql/sql_mode.h
@@ -155,7 +155,7 @@ public:
m_soft= 0;
return *this;
}
- void push_dependency_warnings(THD *thd);
+ void push_dependency_warnings(THD *thd) const;
};
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index b16091dbab4..3ae7c7c7df3 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -100,6 +100,7 @@
#include "set_var.h"
#include "sql_bootstrap.h"
#include "sql_sequence.h"
+#include "opt_trace.h"
#include "my_json_writer.h"
@@ -111,14 +112,18 @@
#include "../storage/maria/ha_maria.h"
#endif
+#include "wsrep.h"
#include "wsrep_mysqld.h"
+#ifdef WITH_WSREP
#include "wsrep_thd.h"
+#include "wsrep_trans_observer.h" /* wsrep transaction hooks */
-static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
+static bool wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
Parser_state *parser_state,
bool is_com_multi,
bool is_next_command);
+#endif /* WITH_WSREP */
/**
@defgroup Runtime_Environment Runtime Environment
@{
@@ -392,10 +397,6 @@ const LEX_CSTRING command_name[257]={
{ STRING_WITH_LEN("Error") } // Last command number 255
};
-const char *xa_state_names[]={
- "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY"
-};
-
#ifdef HAVE_REPLICATION
/**
Returns true if all tables should be ignored.
@@ -784,6 +785,8 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CREATE_SERVER]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_SERVER]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_SERVER]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_BACKUP]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_BACKUP_LOCK]= CF_AUTO_COMMIT_TRANS;
/*
The following statements can deal with temporary tables,
@@ -893,6 +896,16 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_REVOKE_ALL]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_INSTALL_PLUGIN]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]|= CF_DISALLOW_IN_RO_TRANS;
+#ifdef WITH_WSREP
+ /*
+ Statements for which some errors are ignored when
+ wsrep_ignore_apply_errors = WSREP_IGNORE_ERRORS_ON_RECONCILING_DDL
+ */
+ sql_command_flags[SQLCOM_DROP_DB]|= CF_WSREP_MAY_IGNORE_ERRORS;
+ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_WSREP_MAY_IGNORE_ERRORS;
+ sql_command_flags[SQLCOM_DROP_INDEX]|= CF_WSREP_MAY_IGNORE_ERRORS;
+ sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_WSREP_MAY_IGNORE_ERRORS;
+#endif /* WITH_WSREP */
}
bool sqlcom_can_generate_row_events(const THD *thd)
@@ -967,15 +980,29 @@ static char *fgets_fn(char *buffer, size_t size, fgets_input_t input, int *error
}
-static void handle_bootstrap_impl(THD *thd)
+int bootstrap(MYSQL_FILE *file)
{
- MYSQL_FILE *file= bootstrap_file;
- DBUG_ENTER("handle_bootstrap_impl");
+ int bootstrap_error= 0;
+ DBUG_ENTER("handle_bootstrap");
+
+ THD *thd= new THD(next_thread_id());
+#ifdef WITH_WSREP
+ thd->variables.wsrep_on= 0;
+#endif
+ thd->bootstrap=1;
+ my_net_init(&thd->net,(st_vio*) 0, thd, MYF(0));
+ thd->max_client_packet_length= thd->net.max_packet;
+ thd->security_ctx->master_access= ~(ulong)0;
#ifndef EMBEDDED_LIBRARY
- pthread_detach_this_thread();
+ mysql_thread_set_psi_id(thd->thread_id);
+#else
+ thd->mysql= 0;
+#endif
+
+ /* The following must be called before DBUG_ENTER */
thd->thread_stack= (char*) &thd;
-#endif /* EMBEDDED_LIBRARY */
+ thd->store_globals();
thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=
@@ -1052,10 +1079,6 @@ static void handle_bootstrap_impl(THD *thd)
thd->profiling.set_query_source(thd->query(), length);
#endif
- /*
- We don't need to obtain LOCK_thread_count here because in bootstrap
- mode we have only one thread.
- */
thd->set_time();
Parser_state parser_state;
if (parser_state.init(thd, thd->query(), length))
@@ -1082,56 +1105,8 @@ static void handle_bootstrap_impl(THD *thd)
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
thd->lex->restore_set_statement_var();
}
-
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Execute commands from bootstrap_file.
-
- Used when creating the initial grant tables.
-*/
-
-pthread_handler_t handle_bootstrap(void *arg)
-{
- THD *thd=(THD*) arg;
-
- mysql_thread_set_psi_id(thd->thread_id);
-
- do_handle_bootstrap(thd);
- return 0;
-}
-
-void do_handle_bootstrap(THD *thd)
-{
- /* The following must be called before DBUG_ENTER */
- thd->thread_stack= (char*) &thd;
- if (my_thread_init() || thd->store_globals())
- {
-#ifndef EMBEDDED_LIBRARY
- close_connection(thd, ER_OUT_OF_RESOURCES);
-#endif
- thd->fatal_error();
- goto end;
- }
-
- handle_bootstrap_impl(thd);
-
-end:
delete thd;
-
- mysql_mutex_lock(&LOCK_thread_count);
- in_bootstrap = FALSE;
- mysql_cond_broadcast(&COND_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
-
-#ifndef EMBEDDED_LIBRARY
- my_thread_end();
- pthread_exit(0);
-#endif
-
- return;
+ DBUG_RETURN(bootstrap_error);
}
@@ -1183,14 +1158,20 @@ static bool wsrep_tables_accessible_when_detached(const TABLE_LIST *tables)
{
for (const TABLE_LIST *table= tables; table; table= table->next_global)
{
- TABLE_CATEGORY c;
LEX_CSTRING db= table->db, tn= table->table_name;
- c= get_table_category(&db, &tn);
- if (c != TABLE_CATEGORY_INFORMATION && c != TABLE_CATEGORY_PERFORMANCE)
+ if (get_table_category(&db, &tn) < TABLE_CATEGORY_INFORMATION)
return false;
}
return true;
}
+
+static bool wsrep_command_no_result(char command)
+{
+ return (command == COM_STMT_PREPARE ||
+ command == COM_STMT_FETCH ||
+ command == COM_STMT_SEND_LONG_DATA ||
+ command == COM_STMT_CLOSE);
+}
#endif /* WITH_WSREP */
#ifndef EMBEDDED_LIBRARY
@@ -1210,28 +1191,11 @@ bool do_command(THD *thd)
{
bool return_value;
char *packet= 0;
-#ifdef WITH_WSREP
- ulong packet_length= 0; // just to avoid (false positive) compiler warning
-#else
ulong packet_length;
-#endif /* WITH_WSREP */
NET *net= &thd->net;
enum enum_server_command command;
DBUG_ENTER("do_command");
-#ifdef WITH_WSREP
- if (WSREP(thd))
- {
- mysql_mutex_lock(&thd->LOCK_thd_data);
- wsrep_thd_set_query_state(thd, QUERY_IDLE);
- if (thd->wsrep_conflict_state==MUST_ABORT)
- {
- wsrep_client_rollback(thd);
- }
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- }
-#endif /* WITH_WSREP */
-
/*
indicator of uninitialized lex => normal flow of errors handling
(see my_message_sql)
@@ -1272,29 +1236,6 @@ bool do_command(THD *thd)
DEBUG_SYNC(thd, "before_do_command_net_read");
packet_length= my_net_read_packet(net, 1);
-#ifdef WITH_WSREP
- if (WSREP(thd)) {
- mysql_mutex_lock(&thd->LOCK_thd_data);
-
- /* these THD's are aborted or are aborting during being idle */
- if (thd->wsrep_conflict_state == ABORTING)
- {
- while (thd->wsrep_conflict_state == ABORTING) {
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- my_sleep(1000);
- mysql_mutex_lock(&thd->LOCK_thd_data);
- }
- thd->store_globals();
- }
- else if (thd->wsrep_conflict_state == ABORTED)
- {
- thd->store_globals();
- }
-
- wsrep_thd_set_query_state(thd, QUERY_EXEC);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- }
-#endif /* WITH_WSREP */
if (unlikely(packet_length == packet_error))
{
@@ -1302,20 +1243,6 @@ bool do_command(THD *thd)
net->error,
vio_description(net->vio)));
-#ifdef WITH_WSREP
- if (WSREP(thd))
- {
- mysql_mutex_lock(&thd->LOCK_thd_data);
- if (thd->wsrep_conflict_state == MUST_ABORT)
- {
- DBUG_PRINT("wsrep",("aborted for wsrep rollback: %lu",
- (ulong) thd->real_id));
- wsrep_client_rollback(thd);
- }
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- }
-#endif /* WITH_WSREP */
-
/* Instrument this broken statement as "statement/com/error" */
thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
com_statement_info[COM_END].
@@ -1366,13 +1293,61 @@ bool do_command(THD *thd)
command= fetch_command(thd, packet);
#ifdef WITH_WSREP
- if (WSREP(thd))
+ DEBUG_SYNC(thd, "wsrep_before_before_command");
+ /*
+ If this command does not return a result, then we
+ instruct wsrep_before_command() to skip result handling.
+ This causes BF aborted transaction to roll back but keep
+ the error state until next command which is able to return
+ a result to the client.
+ */
+ if (wsrep_before_command(thd, wsrep_command_no_result(command)))
{
/*
- Bail out if DB snapshot has not been installed.
+ Aborted by background rollbacker thread.
+ Handle error here and jump straight to out.
+ Notice that thd->store_globals() is called
+ in wsrep_before_command().
*/
- if (!thd->wsrep_applier &&
- (!wsrep_ready || wsrep_reject_queries != WSREP_REJECT_NONE) &&
+ WSREP_LOG_THD(thd, "enter found BF aborted");
+ DBUG_ASSERT(!thd->mdl_context.has_locks());
+ DBUG_ASSERT(!thd->get_stmt_da()->is_set());
+ /* We let COM_QUIT and COM_STMT_CLOSE to execute even if wsrep aborted. */
+ if (command != COM_STMT_CLOSE &&
+ command != COM_QUIT)
+ {
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ WSREP_DEBUG("Deadlock error for: %s", thd->query());
+ thd->reset_killed();
+ thd->mysys_var->abort = 0;
+ thd->wsrep_retry_counter = 0;
+
+ /* Instrument this broken statement as "statement/com/error" */
+ thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ com_statement_info[COM_END].
+ m_key);
+
+ thd->protocol->end_statement();
+
+ /* Mark the statement completed. */
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
+ return_value= FALSE;
+
+ wsrep_after_command_before_result(thd);
+ goto out;
+ }
+ }
+
+ if (WSREP(thd))
+ {
+ /*
+ * bail out if DB snapshot has not been installed. We however,
+ * allow queries "SET" and "SHOW", they are trapped later in execute_command
+ */
+ if (!(thd->wsrep_applier) &&
+ (!wsrep_ready_get() || wsrep_reject_queries != WSREP_REJECT_NONE) &&
(server_command_flags[command] & CF_SKIP_WSREP_CHECK) == 0)
{
my_message(ER_UNKNOWN_COM_ERROR,
@@ -1385,11 +1360,11 @@ bool do_command(THD *thd)
thd->m_digest= NULL;
return_value= FALSE;
+ wsrep_after_command_before_result(thd);
goto out;
}
}
-#endif
-
+#endif /* WITH_WSREP */
/* Restore read timeout value */
my_net_set_read_timeout(net, thd->variables.net_read_timeout);
@@ -1397,37 +1372,6 @@ bool do_command(THD *thd)
DBUG_ASSERT(!thd->apc_target.is_enabled());
return_value= dispatch_command(command, thd, packet+1,
(uint) (packet_length-1), FALSE, FALSE);
-#ifdef WITH_WSREP
- if (WSREP(thd))
- {
- while (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT)
- {
- WSREP_DEBUG("Retry autocommit for: %s\n", thd->wsrep_retry_query);
- CHARSET_INFO *current_charset = thd->variables.character_set_client;
- if (!is_supported_parser_charset(current_charset))
- {
- /* Do not use non-supported parser character sets */
- WSREP_WARN("Current client character set is non-supported parser "
- "character set: %s", current_charset->csname);
- thd->variables.character_set_client = &my_charset_latin1;
- WSREP_WARN("For retry temporally setting character set to : %s",
- my_charset_latin1.csname);
- }
- thd->clear_error();
- return_value= dispatch_command(command, thd, thd->wsrep_retry_query,
- thd->wsrep_retry_query_len, FALSE, FALSE);
- thd->variables.character_set_client = current_charset;
- }
-
- if (thd->wsrep_retry_query && thd->wsrep_conflict_state != REPLAYING)
- {
- my_free(thd->wsrep_retry_query);
- thd->wsrep_retry_query = NULL;
- thd->wsrep_retry_query_len = 0;
- thd->wsrep_retry_command = COM_CONNECT;
- }
- }
-#endif /* WITH_WSREP */
DBUG_ASSERT(!thd->apc_target.is_enabled());
out:
@@ -1435,6 +1379,13 @@ out:
/* The statement instrumentation must be closed in all cases. */
DBUG_ASSERT(thd->m_digest == NULL);
DBUG_ASSERT(thd->m_statement_psi == NULL);
+#ifdef WITH_WSREP
+ if (packet_length != packet_error)
+ {
+ /* there was a command to process, and before_command() has been called */
+ wsrep_after_command_after_result(thd);
+ }
+#endif /* WITH_WSREP */
DBUG_RETURN(return_value);
}
#endif /* EMBEDDED_LIBRARY */
@@ -1500,6 +1451,36 @@ static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables)
DBUG_RETURN(FALSE);
}
+#ifdef WITH_WSREP
+static my_bool wsrep_read_only_option(THD *thd, TABLE_LIST *all_tables)
+{
+ int opt_readonly_saved = opt_readonly;
+ ulong flag_saved = (ulong)(thd->security_ctx->master_access & SUPER_ACL);
+
+ opt_readonly = 0;
+ thd->security_ctx->master_access &= ~SUPER_ACL;
+
+ my_bool ret = !deny_updates_if_read_only_option(thd, all_tables);
+
+ opt_readonly = opt_readonly_saved;
+ thd->security_ctx->master_access |= flag_saved;
+
+ return ret;
+}
+
+static void wsrep_copy_query(THD *thd)
+{
+ thd->wsrep_retry_command = thd->get_command();
+ thd->wsrep_retry_query_len = thd->query_length();
+ if (thd->wsrep_retry_query) {
+ my_free(thd->wsrep_retry_query);
+ }
+ thd->wsrep_retry_query = (char *)my_malloc(
+ thd->wsrep_retry_query_len + 1, MYF(0));
+ strncpy(thd->wsrep_retry_query, thd->query(), thd->wsrep_retry_query_len);
+ thd->wsrep_retry_query[thd->wsrep_retry_query_len] = '\0';
+}
+#endif /* WITH_WSREP */
/**
check COM_MULTI packet
@@ -1582,41 +1563,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* keep it withing 1 byte */
compile_time_assert(COM_END == 255);
-#ifdef WITH_WSREP
- if (WSREP(thd))
- {
- if (!thd->in_multi_stmt_transaction_mode())
- {
- thd->wsrep_PA_safe= true;
- }
-
- mysql_mutex_lock(&thd->LOCK_thd_data);
- wsrep_thd_set_query_state(thd, QUERY_EXEC);
- if (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT)
- {
- thd->wsrep_conflict_state= NO_CONFLICT;
- }
- if (thd->wsrep_conflict_state== MUST_ABORT)
- {
- wsrep_client_rollback(thd);
- }
- /* We let COM_QUIT and COM_STMT_CLOSE to execute even if wsrep aborted. */
- if (thd->wsrep_conflict_state == ABORTED &&
- command != COM_STMT_CLOSE && command != COM_QUIT)
- {
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- my_message(ER_LOCK_DEADLOCK, "Deadlock: wsrep aborted transaction",
- MYF(0));
- WSREP_DEBUG("Deadlock error for: %s", thd->query());
- thd->reset_killed();
- thd->mysys_var->abort = 0;
- thd->wsrep_conflict_state = NO_CONFLICT;
- thd->wsrep_retry_counter = 0;
- goto dispatch_end;
- }
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- }
-#endif /* WITH_WSREP */
#if defined(ENABLED_PROFILING)
thd->profiling.start_new_query();
#endif
@@ -1659,6 +1605,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
*/
thd->set_query_id(get_query_id());
}
+#ifdef WITH_WSREP
+ if (WSREP(thd) && thd->wsrep_next_trx_id() == WSREP_UNDEFINED_TRX_ID)
+ {
+ thd->set_wsrep_next_trx_id(thd->query_id);
+ WSREP_DEBUG("assigned new next trx id: %" PRIu64, thd->wsrep_next_trx_id());
+ }
+#endif /* WITH_WSREP */
if (!(server_command_flags[command] & CF_SKIP_QUESTIONS))
statistic_increment(thd->status_var.questions, &LOCK_status);
@@ -1685,6 +1638,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->get_stmt_da()->set_skip_flush();
}
+ if (unlikely(thd->security_ctx->password_expired &&
+ command != COM_QUERY &&
+ command != COM_PING &&
+ command != COM_QUIT &&
+ command != COM_STMT_PREPARE &&
+ command != COM_STMT_EXECUTE))
+ {
+ my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
+ goto dispatch_end;
+ }
+
switch (command) {
case COM_INIT_DB:
{
@@ -1704,7 +1668,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_REGISTER_SLAVE:
{
status_var_increment(thd->status_var.com_register_slave);
- if (!register_slave(thd, (uchar*)packet, packet_length))
+ if (!thd->register_slave((uchar*) packet, packet_length))
my_ok(thd);
break;
}
@@ -1712,8 +1676,16 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_RESET_CONNECTION:
{
thd->status_var.com_other++;
+#ifdef WITH_WSREP
+ wsrep_after_command_ignore_result(thd);
+ wsrep_close(thd);
+#endif /* WITH_WSREP */
thd->change_user();
thd->clear_error(); // if errors from rollback
+#ifdef WITH_WSREP
+ wsrep_open(thd);
+ wsrep_before_command(thd);
+#endif /* WITH_WSREP */
/* Restore original charset from client authentication packet.*/
if(thd->org_charset)
thd->update_charset(thd->org_charset,thd->org_charset,thd->org_charset);
@@ -1725,7 +1697,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
int auth_rc;
status_var_increment(thd->status_var.com_other);
+#ifdef WITH_WSREP
+ wsrep_after_command_ignore_result(thd);
+ wsrep_close(thd);
+#endif /* WITH_WSREP */
thd->change_user();
+#ifdef WITH_WSREP
+ wsrep_open(thd);
+ wsrep_before_command(thd);
+#endif /* WITH_WSREP */
thd->clear_error(); // if errors from rollback
/* acl_authenticate() takes the data from net->read_pos */
@@ -1788,11 +1768,23 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_STMT_BULK_EXECUTE:
{
mysqld_stmt_bulk_execute(thd, packet, packet_length);
+#ifdef WITH_WSREP
+ if (WSREP(thd))
+ {
+ (void)wsrep_after_statement(thd);
+ }
+#endif /* WITH_WSREP */
break;
}
case COM_STMT_EXECUTE:
{
mysqld_stmt_execute(thd, packet, packet_length);
+#ifdef WITH_WSREP
+ if (WSREP(thd))
+ {
+ (void)wsrep_after_statement(thd);
+ }
+#endif /* WITH_WSREP */
break;
}
case COM_STMT_FETCH:
@@ -1845,10 +1837,23 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (unlikely(parser_state.init(thd, thd->query(), thd->query_length())))
break;
+#ifdef WITH_WSREP
if (WSREP(thd))
- wsrep_mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
- is_com_multi, is_next_command);
+ {
+ if (wsrep_mysql_parse(thd, thd->query(), thd->query_length(),
+ &parser_state,
+ is_com_multi, is_next_command))
+ {
+ WSREP_DEBUG("Deadlock error for: %s", thd->query());
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ thd->reset_kill_query();
+ thd->wsrep_retry_counter = 0;
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ goto dispatch_end;
+ }
+ }
else
+#endif /* WITH_WSREP */
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
is_com_multi, is_next_command);
@@ -1860,9 +1865,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
*/
char *beginning_of_next_stmt= (char*) parser_state.m_lip.found_semicolon;
-#ifdef WITH_ARIA_STORAGE_ENGINE
- ha_maria::implicit_commit(thd, FALSE);
-#endif
+ ha_maria_implicit_commit(thd, FALSE);
/* Finalize server status flags after executing a statement. */
thd->update_server_status();
@@ -1930,16 +1933,30 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
statistic_increment(thd->status_var.questions, &LOCK_status);
if (!WSREP(thd))
- thd->set_time(); /* Reset the query start time. */
+ thd->set_time(); /* Reset the query start time. */
parser_state.reset(beginning_of_next_stmt, length);
+#ifdef WITH_WSREP
if (WSREP(thd))
- wsrep_mysql_parse(thd, beginning_of_next_stmt, length, &parser_state,
- is_com_multi, is_next_command);
+ {
+ if (wsrep_mysql_parse(thd, beginning_of_next_stmt,
+ length, &parser_state,
+ is_com_multi, is_next_command))
+ {
+ WSREP_DEBUG("Deadlock error for: %s", thd->query());
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ thd->reset_kill_query();
+ thd->wsrep_retry_counter = 0;
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+
+ goto dispatch_end;
+ }
+ }
else
- mysql_parse(thd, beginning_of_next_stmt, length, &parser_state,
- is_com_multi, is_next_command);
+#endif /* WITH_WSREP */
+ mysql_parse(thd, beginning_of_next_stmt, length, &parser_state,
+ is_com_multi, is_next_command);
}
@@ -2010,10 +2027,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
Init TABLE_LIST members necessary when the undelrying
table is view.
*/
- table_list.select_lex= &(thd->lex->select_lex);
+ table_list.select_lex= thd->lex->first_select_lex();
thd->lex->
- select_lex.table_list.link_in_list(&table_list,
- &table_list.next_local);
+ first_select_lex()->table_list.link_in_list(&table_list,
+ &table_list.next_local);
thd->lex->add_to_query_tables(&table_list);
if (is_infoschema_db(&table_list.db))
@@ -2102,7 +2119,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
general_log_print(thd, command, "Log: '%s' Pos: %lu", name, pos);
if (nlen < FN_REFLEN)
mysql_binlog_send(thd, thd->strmake(name, nlen), (my_off_t)pos, flags);
- unregister_slave(thd, true, true); // todo: can be extraneous
+ thd->unregister_slave(); // todo: can be extraneous
/* fake COM_QUIT -- if we get here, the thread needs to terminate */
error = TRUE;
break;
@@ -2132,6 +2149,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
DBUG_EXECUTE_IF("simulate_detached_thread_refresh", debug_simulate= TRUE;);
if (debug_simulate)
{
+ /* This code doesn't work under FTWRL */
+ DBUG_ASSERT(! (options & REFRESH_READ_LOCK));
/*
Simulate a reload without a attached thread session.
Provides a environment similar to that of when the
@@ -2174,6 +2193,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
*/
enum mysql_enum_shutdown_level level;
level= (enum mysql_enum_shutdown_level) (uchar) packet[0];
+ thd->lex->is_shutdown_wait_for_slaves= false; // "deferred" cleanup
if (level == SHUTDOWN_DEFAULT)
level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable
else if (level != SHUTDOWN_WAIT_ALL_BUFFERS)
@@ -2373,9 +2393,23 @@ com_multi_end:
break;
}
+dispatch_end:
#ifdef WITH_WSREP
- dispatch_end:
-
+ /*
+ BF aborted before sending response back to client
+ */
+ if (thd->killed == KILL_QUERY)
+ {
+ WSREP_DEBUG("THD is killed at dispatch_end");
+ }
+ wsrep_after_command_before_result(thd);
+ if (wsrep_current_error(thd) && !wsrep_command_no_result(command))
+ {
+ /* todo: Pass wsrep client state current error to override */
+ wsrep_override_error(thd, wsrep_current_error(thd),
+ wsrep_current_error_status(thd));
+ WSREP_LOG_THD(thd, "leave");
+ }
if (WSREP(thd))
{
/*
@@ -2384,12 +2418,11 @@ com_multi_end:
*/
DBUG_ASSERT((command != COM_QUIT && command != COM_STMT_CLOSE)
|| thd->get_stmt_da()->is_disabled());
+ DBUG_ASSERT(thd->wsrep_trx().state() != wsrep::transaction::s_replaying);
/* wsrep BF abort in query exec phase */
- mysql_mutex_lock(&thd->LOCK_thd_data);
- do_end_of_statement= thd->wsrep_conflict_state != REPLAYING &&
- thd->wsrep_conflict_state != RETRY_AUTOCOMMIT &&
- !thd->killed;
- mysql_mutex_unlock(&thd->LOCK_thd_data);
+ mysql_mutex_lock(&thd->LOCK_thd_kill);
+ do_end_of_statement= thd_is_connection_alive(thd);
+ mysql_mutex_unlock(&thd->LOCK_thd_kill);
}
else
do_end_of_statement= true;
@@ -2616,23 +2649,24 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
DBUG_RETURN(1);
#else
{
- if (lex->select_lex.db.str == NULL &&
- lex->copy_db_to(&lex->select_lex.db))
+ if (lex->first_select_lex()->db.str == NULL &&
+ lex->copy_db_to(&lex->first_select_lex()->db))
{
DBUG_RETURN(1);
}
schema_select_lex= new (thd->mem_root) SELECT_LEX();
schema_select_lex->table_list.first= NULL;
if (lower_case_table_names == 1)
- lex->select_lex.db.str= thd->strdup(lex->select_lex.db.str);
- schema_select_lex->db= lex->select_lex.db;
+ lex->first_select_lex()->db.str=
+ thd->strdup(lex->first_select_lex()->db.str);
+ schema_select_lex->db= lex->first_select_lex()->db;
/*
check_db_name() may change db.str if lower_case_table_names == 1,
but that's ok as the db is allocted above in this case.
*/
- if (check_db_name((LEX_STRING*) &lex->select_lex.db))
+ if (check_db_name((LEX_STRING*) &lex->first_select_lex()->db))
{
- my_error(ER_WRONG_DB_NAME, MYF(0), lex->select_lex.db.str);
+ my_error(ER_WRONG_DB_NAME, MYF(0), lex->first_select_lex()->db.str);
DBUG_RETURN(1);
}
break;
@@ -2671,7 +2705,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
default:
break;
}
-
+ if (schema_select_lex)
+ schema_select_lex->set_master_unit(&lex->unit);
SELECT_LEX *select_lex= lex->current_select;
if (make_schema_select(thd, select_lex, get_schema_table(schema_table_idx)))
DBUG_RETURN(1);
@@ -3078,7 +3113,7 @@ static int mysql_create_routine(THD *thd, LEX *lex)
if (sp_process_definer(thd))
return true;
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
if (!lex->sphead->m_handler->sp_create_routine(thd, lex->sphead))
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -3147,7 +3182,9 @@ static int mysql_create_routine(THD *thd, LEX *lex)
#endif
return false;
}
-WSREP_ERROR_LABEL:
+#ifdef WITH_WSREP
+wsrep_error_label:
+#endif
return true;
}
@@ -3435,7 +3472,7 @@ mysql_execute_command(THD *thd)
int up_result= 0;
LEX *lex= thd->lex;
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
/* first table of first SELECT_LEX */
TABLE_LIST *first_table= select_lex->table_list.first;
/* list of all tables in query */
@@ -3454,6 +3491,16 @@ mysql_execute_command(THD *thd)
DBUG_ASSERT(!(sql_command_flags[lex->sql_command] & CF_INSERTS_DATA) ||
first_table->for_insert_data);
+ if (thd->security_ctx->password_expired &&
+ lex->sql_command != SQLCOM_SET_OPTION &&
+ lex->sql_command != SQLCOM_PREPARE &&
+ lex->sql_command != SQLCOM_EXECUTE &&
+ lex->sql_command != SQLCOM_DEALLOCATE_PREPARE)
+ {
+ my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
+ DBUG_RETURN(1);
+ }
+
DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt);
/*
Each statement or replication event which might produce deadlock
@@ -3478,6 +3525,7 @@ mysql_execute_command(THD *thd)
DBUG_ASSERT(first_table == all_tables && first_table != 0);
*/
lex->first_lists_tables_same();
+ lex->fix_first_select_number();
/* should be assigned after making first tables same */
all_tables= lex->query_tables;
/* set context for commands which do not use setup_tables */
@@ -3623,13 +3671,20 @@ mysql_execute_command(THD *thd)
#ifdef HAVE_REPLICATION
} /* endif unlikely slave */
#endif
+ Opt_trace_start ots(thd, all_tables, lex->sql_command, &lex->var_list,
+ thd->query(), thd->query_length(),
+ thd->variables.character_set_client);
+
+ Json_writer_object trace_command(thd);
+ Json_writer_array trace_command_steps(thd, "steps");
+
/* store old value of binlog format */
enum_binlog_format orig_binlog_format,orig_current_stmt_binlog_format;
thd->get_binlog_format(&orig_binlog_format,
&orig_current_stmt_binlog_format);
#ifdef WITH_WSREP
- if (wsrep && WSREP(thd))
+ if (WSREP(thd))
{
/*
change LOCK TABLE WRITE to transaction
@@ -3659,8 +3714,8 @@ mysql_execute_command(THD *thd)
* allow SET and SHOW queries and reads from information schema
* and dirty reads (if configured)
*/
- if (!thd->wsrep_applier &&
- !(wsrep_ready && wsrep_reject_queries == WSREP_REJECT_NONE) &&
+ if (!(thd->wsrep_applier) &&
+ !(wsrep_ready_get() && wsrep_reject_queries == WSREP_REJECT_NONE) &&
!(thd->variables.wsrep_dirty_reads &&
(sql_command_flags[lex->sql_command] & CF_CHANGES_DATA) == 0) &&
!wsrep_tables_accessible_when_detached(all_tables) &&
@@ -3706,7 +3761,7 @@ mysql_execute_command(THD *thd)
not run in it's own transaction it may simply never appear on
the slave in case the outside transaction rolls back.
*/
- if (stmt_causes_implicit_commit(thd, CF_IMPLICT_COMMIT_BEGIN))
+ if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_BEGIN))
{
/*
Note that this should never happen inside of stored functions
@@ -3729,6 +3784,14 @@ mysql_execute_command(THD *thd)
}
}
thd->transaction.stmt.mark_trans_did_ddl();
+#ifdef WITH_WSREP
+ /* Clean up the previous transaction on implicit commit */
+ if (WSREP_NNULL(thd) && wsrep_thd_is_local(thd) &&
+ wsrep_after_statement(thd))
+ {
+ goto error;
+ }
+#endif /* WITH_WSREP */
}
#ifndef DBUG_OFF
@@ -3782,6 +3845,34 @@ mysql_execute_command(THD *thd)
/* Start timeouts */
thd->set_query_timer();
+#ifdef WITH_WSREP
+ /*
+ Always start a new transaction for a wsrep THD unless the
+ current command is DDL or explicit BEGIN. This will guarantee that
+ the THD is BF abortable even if it does not generate any
+ changes and takes only read locks. If the statement does not
+ start a multi STMT transaction, the wsrep_transaction is
+ committed as empty at the end of this function.
+
+ Transaction is started for BEGIN in trans_begin(), for DDL the
+ implicit commit took care of committing previous transaction
+ above and a new transaction should not be started.
+
+ Do not start transaction for stored procedures, it will be handled
+ internally in SP processing.
+ */
+ if (WSREP_NNULL(thd) &&
+ wsrep_thd_is_local(thd) &&
+ lex->sql_command != SQLCOM_BEGIN &&
+ lex->sql_command != SQLCOM_CALL &&
+ lex->sql_command != SQLCOM_EXECUTE &&
+ lex->sql_command != SQLCOM_EXECUTE_IMMEDIATE &&
+ !(sql_command_flags[lex->sql_command] & CF_AUTO_COMMIT_TRANS))
+ {
+ wsrep_start_trx_if_not_started(thd);
+ }
+#endif /* WITH_WSREP */
+
switch (lex->sql_command) {
case SQLCOM_SHOW_EVENTS:
@@ -3841,17 +3932,21 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_STORAGE_ENGINES:
case SQLCOM_SHOW_PROFILE:
case SQLCOM_SELECT:
- {
- if (lex->sql_command == SQLCOM_SELECT)
- WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_READ);
- else
- {
- WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
-#ifdef ENABLED_PROFILING
- if (lex->sql_command == SQLCOM_SHOW_PROFILE)
- thd->profiling.discard_current_query();
-#endif
- }
+ {
+#ifdef WITH_WSREP
+ if (lex->sql_command == SQLCOM_SELECT)
+ {
+ WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_READ);
+ }
+ else
+ {
+ WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
+# ifdef ENABLED_PROFILING
+ if (lex->sql_command == SQLCOM_SHOW_PROFILE)
+ thd->profiling.discard_current_query();
+# endif
+ }
+#endif /* WITH_WSREP */
thd->status_var.last_query_cost= 0.0;
@@ -4324,9 +4419,7 @@ mysql_execute_command(THD *thd)
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE);
ha_rows found= 0, updated= 0;
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (WSREP_CLIENT(thd) &&
- wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE))
- goto error;
+ WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE);
if (update_precheck(thd, all_tables))
break;
@@ -4355,6 +4448,11 @@ mysql_execute_command(THD *thd)
/* mysql_update return 2 if we need to switch to multi-update */
if (up_result != 2)
break;
+ if (thd->lex->period_conditions.is_set())
+ {
+ DBUG_ASSERT(0); // Should never happen
+ goto error;
+ }
}
/* fall through */
case SQLCOM_UPDATE_MULTI:
@@ -4484,9 +4582,7 @@ mysql_execute_command(THD *thd)
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE);
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (WSREP_CLIENT(thd) &&
- wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE))
- goto error;
+ WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE);
/*
Since INSERT DELAYED doesn't support temporary tables, we could
@@ -4544,9 +4640,7 @@ mysql_execute_command(THD *thd)
select_insert *sel_result;
bool explain= MY_TEST(lex->describe);
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (WSREP_CLIENT(thd) &&
- wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE))
- goto error;
+ WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE);
if ((res= insert_precheck(thd, all_tables)))
break;
@@ -4673,9 +4767,7 @@ mysql_execute_command(THD *thd)
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE);
select_result *sel_result= NULL;
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (WSREP_CLIENT(thd) &&
- wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE))
- goto error;
+ WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE);
if ((res= delete_precheck(thd, all_tables)))
break;
@@ -4734,9 +4826,7 @@ mysql_execute_command(THD *thd)
DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
multi_delete *result;
- if (WSREP_CLIENT(thd) &&
- wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE))
- goto error;
+ WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE);
if ((res= multi_delete_precheck(thd, all_tables)))
break;
@@ -4967,7 +5057,8 @@ mysql_execute_command(THD *thd)
thd->release_transactional_locks();
thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
}
- if (thd->global_read_lock.is_acquired())
+ if (thd->global_read_lock.is_acquired() &&
+ thd->current_backup_stage == BACKUP_FINISHED)
thd->global_read_lock.unlock_global_read_lock(thd);
if (res)
goto error;
@@ -4982,6 +5073,26 @@ mysql_execute_command(THD *thd)
if (res)
goto error;
+#ifdef WITH_WSREP
+ /* Clean up the previous transaction on implicit commit. */
+ if (wsrep_on(thd) && !wsrep_not_committed(thd) && wsrep_after_statement(thd))
+ goto error;
+#endif
+
+ /* We can't have any kind of table locks while backup is active */
+ if (thd->current_backup_stage != BACKUP_FINISHED)
+ {
+ my_error(ER_BACKUP_LOCK_IS_ACTIVE, MYF(0));
+ goto error;
+ }
+
+ /* Should not lock tables while BACKUP LOCK is active */
+ if (thd->mdl_backup_lock)
+ {
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ goto error;
+ }
+
/*
Here we have to pre-open temporary tables for LOCK TABLES.
@@ -5014,6 +5125,23 @@ mysql_execute_command(THD *thd)
my_ok(thd);
}
break;
+ case SQLCOM_BACKUP:
+ if (check_global_access(thd, RELOAD_ACL))
+ goto error;
+ if (!(res= run_backup_stage(thd, lex->backup_stage)))
+ my_ok(thd);
+ break;
+ case SQLCOM_BACKUP_LOCK:
+ if (check_global_access(thd, RELOAD_ACL))
+ goto error;
+ /* first table is set for lock. For unlock the list is empty */
+ if (first_table)
+ res= backup_lock(thd, first_table);
+ else
+ backup_unlock(thd);
+ if (!res)
+ my_ok(thd);
+ break;
case SQLCOM_CREATE_DB:
{
if (prepare_db_action(thd, lex->create_info.or_replace() ?
@@ -5562,6 +5690,7 @@ mysql_execute_command(THD *thd)
thd->release_transactional_locks();
WSREP_DEBUG("BEGIN failed, MDL released: %lld",
(longlong) thd->thread_id);
+ WSREP_DEBUG("stmt_da, sql_errno: %d", (thd->get_stmt_da()->is_error()) ? thd->get_stmt_da()->sql_errno() : 0);
goto error;
}
my_ok(thd);
@@ -5601,20 +5730,7 @@ mysql_execute_command(THD *thd)
thd->set_killed(KILL_CONNECTION);
thd->print_aborted_warning(3, "RELEASE");
}
-#ifdef WITH_WSREP
- if (WSREP(thd)) {
-
- if (thd->wsrep_conflict_state == NO_CONFLICT ||
- thd->wsrep_conflict_state == REPLAYING)
- {
- my_ok(thd);
- }
- } else {
-#endif /* WITH_WSREP */
- my_ok(thd);
-#ifdef WITH_WSREP
- }
-#endif /* WITH_WSREP */
+ my_ok(thd);
break;
}
case SQLCOM_ROLLBACK:
@@ -5650,17 +5766,7 @@ mysql_execute_command(THD *thd)
/* Disconnect the current client connection. */
if (tx_release)
thd->set_killed(KILL_CONNECTION);
-#ifdef WITH_WSREP
- if (WSREP(thd)) {
- if (thd->wsrep_conflict_state == NO_CONFLICT) {
- my_ok(thd);
- }
- } else {
-#endif /* WITH_WSREP */
- my_ok(thd);
-#ifdef WITH_WSREP
- }
-#endif /* WITH_WSREP */
+ my_ok(thd);
break;
}
case SQLCOM_RELEASE_SAVEPOINT:
@@ -5924,6 +6030,14 @@ mysql_execute_command(THD *thd)
break;
}
case SQLCOM_XA_START:
+#ifdef WITH_WSREP
+ if (WSREP(thd))
+ {
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0),
+ "XA transactions with Galera replication");
+ break;
+ }
+#endif /* WITH_WSREP */
if (trans_xa_start(thd))
goto error;
my_ok(thd);
@@ -6091,8 +6205,10 @@ mysql_execute_command(THD *thd)
goto finish;
error:
-WSREP_ERROR_LABEL:
- res= TRUE;
+#ifdef WITH_WSREP
+wsrep_error_label:
+#endif
+ res= true;
finish:
@@ -6100,7 +6216,6 @@ finish:
DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() ||
thd->in_multi_stmt_transaction_mode());
-
lex->unit.cleanup();
/* close/reopen tables that were marked to need reopen under LOCK TABLES */
@@ -6127,25 +6242,6 @@ finish:
THD_STAGE_INFO(thd, stage_rollback);
trans_rollback_stmt(thd);
}
-#ifdef WITH_WSREP
- else if (thd->spcont &&
- (thd->wsrep_conflict_state == MUST_ABORT ||
- thd->wsrep_conflict_state == ABORTED ||
- thd->wsrep_conflict_state == CERT_FAILURE))
- {
- /*
- The error was cleared, but THD was aborted by wsrep and
- wsrep_conflict_state is still set accordingly. This
- situation is expected if we are running a stored procedure
- that declares a handler that catches ER_LOCK_DEADLOCK error.
- In which case the error may have been cleared in method
- sp_rcontext::handle_sql_condition().
- */
- trans_rollback_stmt(thd);
- thd->wsrep_conflict_state= NO_CONFLICT;
- thd->killed= NOT_KILLED;
- }
-#endif /* WITH_WSREP */
else
{
/* If commit fails, we should be able to reset the OK status. */
@@ -6154,16 +6250,11 @@ finish:
trans_commit_stmt(thd);
thd->get_stmt_da()->set_overwrite_status(false);
}
-#ifdef WITH_ARIA_STORAGE_ENGINE
- ha_maria::implicit_commit(thd, FALSE);
-#endif
+ ha_maria_implicit_commit(thd, FALSE);
}
/* Free tables. Set stage 'closing tables' */
close_thread_tables(thd);
-#ifdef WITH_WSREP
- thd->wsrep_consistency_check= NO_CONSISTENCY_CHECK;
-#endif /* WITH_WSREP */
#ifndef DBUG_OFF
@@ -6225,9 +6316,10 @@ finish:
TRANSACT_TRACKER(add_trx_state_from_thd(thd));
- WSREP_TO_ISOLATION_END;
-
#ifdef WITH_WSREP
+ thd->wsrep_consistency_check= NO_CONSISTENCY_CHECK;
+
+ WSREP_TO_ISOLATION_END;
/*
Force release of transactional locks if not in active MST and wsrep is on.
*/
@@ -6240,11 +6332,26 @@ finish:
(longlong) thd->thread_id);
thd->release_transactional_locks();
}
+
+ /*
+ Current command did not start multi STMT transaction and the command
+ did not cause commit to happen (e.g. read only). Commit the wsrep
+ transaction as empty.
+ */
+ if (!thd->in_active_multi_stmt_transaction() &&
+ !thd->in_sub_stmt &&
+ thd->wsrep_trx().active() &&
+ thd->wsrep_trx().state() == wsrep::transaction::s_executing)
+ {
+ wsrep_commit_empty(thd, true);
+ }
+
+ /* assume PA safety for next transaction */
+ thd->wsrep_PA_safe= true;
#endif /* WITH_WSREP */
DBUG_RETURN(res || thd->is_error());
-}
-
+ }
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
{
@@ -6370,6 +6477,7 @@ static bool execute_show_status(THD *thd, TABLE_LIST *all_tables)
bool res;
system_status_var old_status_var= thd->status_var;
thd->initial_status_var= &old_status_var;
+ WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
UINT_MAX, FALSE)))
res= execute_sqlcom_select(thd, all_tables);
@@ -6388,6 +6496,10 @@ static bool execute_show_status(THD *thd, TABLE_LIST *all_tables)
offsetof(STATUS_VAR, last_cleared_system_status_var));
mysql_mutex_unlock(&LOCK_status);
return res;
+#ifdef WITH_WSREP
+wsrep_error_label: /* see WSREP_SYNC_WAIT() macro above */
+ return true;
+#endif /* WITH_WSREP */
}
@@ -7297,7 +7409,7 @@ bool check_stack_overrun(THD *thd, long margin,
if (ebuff) {
my_snprintf(ebuff, MYSQL_ERRMSG_SIZE, ER_THD(thd, ER_STACK_OVERRUN_NEED_MORE),
stack_used, my_thread_stack_size, margin);
- my_message(ER_STACK_OVERRUN_NEED_MORE, ebuff, MYF(ME_FATALERROR));
+ my_message(ER_STACK_OVERRUN_NEED_MORE, ebuff, MYF(ME_FATAL));
delete [] ebuff;
}
return 1;
@@ -7367,16 +7479,21 @@ void THD::reset_for_next_command(bool do_clear_error)
DBUG_ASSERT(!in_sub_stmt);
if (likely(do_clear_error))
+ {
clear_error(1);
-
+ /*
+ The following variable can't be reset in clear_error() as
+ clear_error() is called during auto_repair of table
+ */
+ error_printed_to_log= 0;
+ }
free_list= 0;
/*
We also assign stmt_lex in lex_start(), but during bootstrap this
code is executed first.
*/
DBUG_ASSERT(lex == &main_lex);
- main_lex.stmt_lex= &main_lex; main_lex.current_select_number= 1;
- DBUG_PRINT("info", ("Lex and stmt_lex: %p", &main_lex));
+ main_lex.stmt_lex= &main_lex; main_lex.current_select_number= 0;
/*
Those two lines below are theoretically unneeded as
THD::cleanup_after_query() should take care of this already.
@@ -7393,7 +7510,7 @@ void THD::reset_for_next_command(bool do_clear_error)
use autoinc values passed in binlog events, not the values forced by
the cluster.
*/
- if (WSREP(this) && wsrep_exec_mode == LOCAL_STATE &&
+ if (WSREP_NNULL(this) && wsrep_thd_is_local(this) &&
!slave_thread && wsrep_auto_increment_control)
{
variables.auto_increment_offset=
@@ -7447,7 +7564,7 @@ void THD::reset_for_next_command(bool do_clear_error)
#endif
DBUG_PRINT("debug",
("is_current_stmt_binlog_format_row(): %d",
- is_current_stmt_binlog_format_row()));
+ is_current_stmt_binlog_format_row()));
DBUG_VOID_RETURN;
}
@@ -7467,11 +7584,7 @@ mysql_init_select(LEX *lex)
SELECT_LEX *select_lex= lex->current_select;
select_lex->init_select();
lex->wild= 0;
- if (select_lex == &lex->select_lex)
- {
- DBUG_ASSERT(lex->result == 0);
- lex->exchange= 0;
- }
+ lex->exchange= 0;
}
@@ -7492,7 +7605,7 @@ mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *select_lex)
{
THD *thd= lex->thd;
bool new_select= select_lex == NULL;
- Name_resolution_context *curr_context= lex->context_stack.head();
+ int old_nest_level= lex->current_select->nest_level;
DBUG_ENTER("mysql_new_select");
if (new_select)
@@ -7504,34 +7617,25 @@ mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *select_lex)
select_lex->init_query();
select_lex->init_select();
}
- lex->nest_level++;
- if (lex->nest_level > (int) MAX_SELECT_NESTING)
- {
- my_error(ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT, MYF(0));
- DBUG_RETURN(1);
- }
- select_lex->nest_level= lex->nest_level;
select_lex->nest_level_base= &thd->lex->unit;
if (move_down)
{
+ lex->nest_level++;
+ if (select_lex->set_nest_level(old_nest_level + 1))
+ DBUG_RETURN(1);
SELECT_LEX_UNIT *unit;
/* first select_lex of subselect or derived table */
- if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT()))
+ if (!(unit= lex->alloc_unit()))
DBUG_RETURN(1);
- unit->init_query();
- unit->thd= thd;
unit->include_down(lex->current_select);
- unit->link_next= 0;
- unit->link_prev= 0;
unit->return_to= lex->current_select;
select_lex->include_down(unit);
/*
By default we assume that it is usual subselect and we have outer name
resolution context, if no we will assign it to 0 later
*/
-
- select_lex->context.outer_context= curr_context;
+ select_lex->context.outer_context= &select_lex->outer_select()->context;
}
else
{
@@ -7558,15 +7662,13 @@ mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *select_lex)
"SELECT ... PROCEDURE ANALYSE()");
DBUG_RETURN(TRUE);
}
- // SELECT 1 FROM t1 ORDER BY 1 UNION SELECT 1 FROM t1 -- not possible
- DBUG_ASSERT(!lex->current_select->order_list.first ||
- lex->current_select->braces);
- // SELECT 1 FROM t1 LIMIT 1 UNION SELECT 1 FROM t1; -- not possible
- DBUG_ASSERT(!lex->current_select->explicit_limit ||
- lex->current_select->braces);
+ SELECT_LEX_NODE *save_slave= select_lex->slave;
select_lex->include_neighbour(lex->current_select);
- SELECT_LEX_UNIT *unit= select_lex->master_unit();
+ select_lex->slave= save_slave;
+ SELECT_LEX_UNIT *unit= select_lex->master_unit();
+ if (select_lex->set_nest_level(old_nest_level))
+ DBUG_RETURN(1);
if (!unit->fake_select_lex && unit->add_fake_select_lex(lex->thd))
DBUG_RETURN(1);
select_lex->context.outer_context=
@@ -7622,151 +7724,171 @@ void mysql_init_multi_delete(LEX *lex)
{
lex->sql_command= SQLCOM_DELETE_MULTI;
mysql_init_select(lex);
- lex->select_lex.select_limit= 0;
+ lex->first_select_lex()->select_limit= 0;
lex->unit.select_limit_cnt= HA_POS_ERROR;
- lex->select_lex.table_list.save_and_clear(&lex->auxiliary_table_list);
+ lex->first_select_lex()->table_list.
+ save_and_clear(&lex->auxiliary_table_list);
lex->query_tables= 0;
lex->query_tables_last= &lex->query_tables;
}
-static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
+#ifdef WITH_WSREP
+static void wsrep_prepare_for_autocommit_retry(THD* thd,
+ char* rawbuf,
+ uint length,
+ Parser_state* parser_state)
+{
+ thd->clear_error();
+ close_thread_tables(thd);
+ thd->wsrep_retry_counter++; // grow
+ wsrep_copy_query(thd);
+ thd->set_time();
+ parser_state->reset(rawbuf, length);
+
+ /* PSI end */
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
+
+ /* DTRACE end */
+ if (MYSQL_QUERY_DONE_ENABLED())
+ {
+ MYSQL_QUERY_DONE(thd->is_error());
+ }
+
+ /* SHOW PROFILE end */
+#if defined(ENABLED_PROFILING)
+ thd->profiling.finish_current_query();
+#endif
+
+ /* SHOW PROFILE begin */
+#if defined(ENABLED_PROFILING)
+ thd->profiling.start_new_query("continuing");
+ thd->profiling.set_query_source(rawbuf, length);
+#endif
+
+ /* DTRACE begin */
+ MYSQL_QUERY_START(rawbuf, thd->thread_id,
+ thd->get_db(),
+ &thd->security_ctx->priv_user[0],
+ (char *) thd->security_ctx->host_or_ip);
+
+ /* Performance Schema Interface instrumentation, begin */
+ thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ com_statement_info[thd->get_command()].m_key);
+ MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, thd->query(),
+ thd->query_length());
+
+ DBUG_ASSERT(thd->wsrep_trx().active() == false);
+ thd->wsrep_cs().reset_error();
+ thd->set_query_id(next_query_id());
+}
+
+static bool wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
Parser_state *parser_state,
bool is_com_multi,
bool is_next_command)
{
-#ifdef WITH_WSREP
bool is_autocommit=
!thd->in_multi_stmt_transaction_mode() &&
- thd->wsrep_conflict_state == NO_CONFLICT &&
- !thd->wsrep_applier;
-
+ wsrep_read_only_option(thd, thd->lex->query_tables);
+ bool retry_autocommit;
do
{
- if (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT)
+ retry_autocommit= false;
+ mysql_parse(thd, rawbuf, length, parser_state, is_com_multi, is_next_command);
+
+ /*
+ Convert all ER_QUERY_INTERRUPTED errors to ER_LOCK_DEADLOCK
+ if the transaction was BF aborted. This can happen when the
+ transaction is being BF aborted via thd->awake() while it is
+ still executing.
+
+ Note that this must be done before wsrep_after_statement() call
+ since it clears the transaction for autocommit queries.
+ */
+ if (((thd->get_stmt_da()->is_error() &&
+ thd->get_stmt_da()->sql_errno() == ER_QUERY_INTERRUPTED) ||
+ !thd->get_stmt_da()->is_set()) &&
+ thd->wsrep_trx().bf_aborted())
{
- thd->wsrep_conflict_state= NO_CONFLICT;
- /* Performance Schema Interface instrumentation, begin */
- thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
- com_statement_info[thd->get_command()].m_key);
- MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, thd->query(),
- thd->query_length());
-
- DBUG_EXECUTE_IF("sync.wsrep_retry_autocommit",
- {
- const char act[]=
- "now "
- "SIGNAL wsrep_retry_autocommit_reached "
- "WAIT_FOR wsrep_retry_autocommit_continue";
- DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(act)));
- });
- WSREP_DEBUG("Retry autocommit query: %s", thd->query());
- }
-
- mysql_parse(thd, rawbuf, length, parser_state, is_com_multi,
- is_next_command);
-
- if (WSREP(thd)) {
- /* wsrep BF abort in query exec phase */
- mysql_mutex_lock(&thd->LOCK_thd_data);
- if (thd->wsrep_conflict_state == MUST_ABORT) {
- wsrep_client_rollback(thd);
-
- WSREP_DEBUG("abort in exec query state, avoiding autocommit");
- }
+ WSREP_DEBUG("overriding error: %d with DEADLOCK",
+ (thd->get_stmt_da()->is_error()) ?
+ thd->get_stmt_da()->sql_errno() : 0);
- if (thd->wsrep_conflict_state == MUST_REPLAY)
- {
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- if (thd->lex->explain)
- delete_explain_query(thd->lex);
- mysql_mutex_lock(&thd->LOCK_thd_data);
+ thd->reset_kill_query();
+ wsrep_override_error(thd, ER_LOCK_DEADLOCK);
+ }
- wsrep_replay_transaction(thd);
- }
+#ifdef ENABLED_DEBUG_SYNC
+ /* we need the test otherwise we get stuck in the "SET DEBUG_SYNC" itself */
+ if (thd->lex->sql_command != SQLCOM_SET_OPTION)
+ DEBUG_SYNC(thd, "wsrep_after_statement_enter");
+#endif
- /* setting error code for BF aborted trxs */
- if (thd->wsrep_conflict_state == ABORTED ||
- thd->wsrep_conflict_state == CERT_FAILURE)
+ if (wsrep_after_statement(thd) &&
+ is_autocommit &&
+ thd_is_connection_alive(thd))
+ {
+ thd->reset_for_next_command();
+ thd->reset_kill_query();
+ if (is_autocommit &&
+ thd->lex->sql_command != SQLCOM_SELECT &&
+ thd->wsrep_retry_counter < thd->variables.wsrep_retry_autocommit)
{
- thd->reset_for_next_command();
- if (is_autocommit &&
- thd->lex->sql_command != SQLCOM_SELECT &&
- (thd->wsrep_retry_counter < thd->variables.wsrep_retry_autocommit))
- {
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- WSREP_DEBUG("wsrep retrying AC query: %s",
- (thd->query()) ? thd->query() : "void");
-
- /* Performance Schema Interface instrumentation, end */
- MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
- thd->m_statement_psi= NULL;
- thd->m_digest= NULL;
- // Released thd->LOCK_thd_data above as below could end up
- // close_thread_tables()/close_open_tables()/close_thread_table()/mysql_mutex_lock(&thd->LOCK_thd_data)
- close_thread_tables(thd);
-
- mysql_mutex_lock(&thd->LOCK_thd_data);
- thd->wsrep_conflict_state= RETRY_AUTOCOMMIT;
- thd->wsrep_retry_counter++; // grow
- wsrep_copy_query(thd);
- thd->set_time();
- parser_state->reset(rawbuf, length);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- }
- else
- {
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- // This does dirty read to wsrep variables but it is only a debug code
- WSREP_DEBUG("%s, thd: %lld is_AC: %d, retry: %lu - %lu SQL: %s",
- (thd->wsrep_conflict_state == ABORTED) ?
- "BF Aborted" : "cert failure",
- (longlong) thd->thread_id, is_autocommit,
- thd->wsrep_retry_counter,
- thd->variables.wsrep_retry_autocommit, thd->query());
- my_message(ER_LOCK_DEADLOCK, "Deadlock: wsrep aborted transaction",
- MYF(0));
-
- mysql_mutex_lock(&thd->LOCK_thd_data);
- thd->wsrep_conflict_state= NO_CONFLICT;
- if (thd->wsrep_conflict_state != REPLAYING)
- thd->wsrep_retry_counter= 0; // reset
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- }
-
- thd->reset_killed();
+ DBUG_EXECUTE_IF("sync.wsrep_retry_autocommit",
+ {
+ const char act[]=
+ "now "
+ "SIGNAL wsrep_retry_autocommit_reached "
+ "WAIT_FOR wsrep_retry_autocommit_continue";
+ DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(act)));
+ });
+ WSREP_DEBUG("wsrep retrying AC query: %lu %s",
+ thd->wsrep_retry_counter, WSREP_QUERY(thd));
+ wsrep_prepare_for_autocommit_retry(thd, rawbuf, length, parser_state);
+ if (thd->lex->explain)
+ delete_explain_query(thd->lex);
+ retry_autocommit= true;
}
else
{
- set_if_smaller(thd->wsrep_retry_counter, 0); // reset; eventually ok
- mysql_mutex_unlock(&thd->LOCK_thd_data);
+ WSREP_DEBUG("%s, thd: %llu is_AC: %d, retry: %lu - %lu SQL: %s",
+ wsrep_thd_transaction_state_str(thd),
+ thd->thread_id,
+ is_autocommit,
+ thd->wsrep_retry_counter,
+ thd->variables.wsrep_retry_autocommit,
+ WSREP_QUERY(thd));
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ thd->reset_kill_query();
+ thd->wsrep_retry_counter= 0; // reset
}
}
-
- /* If retry is requested clean up explain structure */
- if ((thd->wsrep_conflict_state == RETRY_AUTOCOMMIT ||
- thd->wsrep_conflict_state == MUST_REPLAY )
- && thd->lex->explain)
+ else
{
- delete_explain_query(thd->lex);
+ set_if_smaller(thd->wsrep_retry_counter, 0); // reset; eventually ok
}
-
- } while (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT);
+ } while (retry_autocommit);
if (thd->wsrep_retry_query)
{
- WSREP_DEBUG("releasing retry_query: conf %d sent %d kill %d errno %d SQL %s",
- thd->wsrep_conflict_state,
- thd->get_stmt_da()->is_sent(),
+ WSREP_DEBUG("releasing retry_query: "
+ "conf %s sent %d kill %d errno %d SQL %s",
+ wsrep_thd_transaction_state_str(thd),
+ thd->get_stmt_da()->is_sent(),
thd->killed,
- thd->get_stmt_da()->is_error() ? thd->get_stmt_da()->sql_errno() : 0,
+ thd->get_stmt_da()->is_error() ?
+ thd->get_stmt_da()->sql_errno() : 0,
thd->wsrep_retry_query);
my_free(thd->wsrep_retry_query);
thd->wsrep_retry_query = NULL;
thd->wsrep_retry_query_len = 0;
thd->wsrep_retry_command = COM_CONNECT;
}
-#endif /* WITH_WSREP */
+ return false;
}
+#endif /* WITH_WSREP */
/*
@@ -7940,7 +8062,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *rawbuf, uint length)
thd->reset_for_next_command();
if (!parse_sql(thd, & parser_state, NULL, true) &&
- all_tables_not_ok(thd, lex->select_lex.table_list.first))
+ all_tables_not_ok(thd, lex->first_select_lex()->table_list.first))
error= 1; /* Ignore question */
thd->end_statement();
}
@@ -8022,6 +8144,11 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
LEX_CSTRING alias_str;
LEX *lex= thd->lex;
DBUG_ENTER("add_table_to_list");
+ DBUG_PRINT("enter", ("Table '%s' (%p) Select %p (%u)",
+ (alias ? alias->str : table->table.str),
+ table,
+ this, select_number));
+ DBUG_ASSERT(!is_service_select || (table_options & TL_OPTION_SEQUENCE));
if (unlikely(!table))
DBUG_RETURN(0); // End of memory
@@ -8102,7 +8229,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->schema_table_name= ptr->table_name;
ptr->schema_table= schema_table;
}
- ptr->select_lex= lex->current_select;
+ ptr->select_lex= this;
/*
We can't cache internal temporary tables between prepares as the
table may be deleted before next exection.
@@ -8209,8 +8336,6 @@ bool st_select_lex::init_nested_join(THD *thd)
nested_join= ptr->nested_join=
((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
- if (unlikely(join_list->push_front(ptr, thd->mem_root)))
- DBUG_RETURN(1);
ptr->embedding= embedding;
ptr->join_list= join_list;
ptr->alias.str="(nested_join)";
@@ -8247,7 +8372,6 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
join_list= ptr->join_list;
embedding= ptr->embedding;
nested_join= ptr->nested_join;
- nested_join->nest_type= 0;
if (nested_join->join_list.elements == 1)
{
TABLE_LIST *embedded= nested_join->join_list.head();
@@ -8257,8 +8381,6 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
join_list->push_front(embedded, thd->mem_root);
ptr= embedded;
embedded->lifted= 1;
- if (embedded->nested_join)
- embedded->nested_join->nest_type= 0;
}
else if (nested_join->join_list.elements == 0)
{
@@ -8290,10 +8412,12 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
DBUG_ENTER("nest_last_join");
TABLE_LIST *head= join_list->head();
- if (head->nested_join && head->nested_join->nest_type & REBALANCED_NEST)
+ if (head->nested_join && (head->nested_join->nest_type & REBALANCED_NEST))
{
+ head= join_list->pop();
DBUG_RETURN(head);
}
+
if (unlikely(!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
sizeof(NESTED_JOIN)))))
DBUG_RETURN(0);
@@ -8327,7 +8451,6 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
ptr->join_using_fields= prev_join_using;
}
}
- join_list->push_front(ptr, thd->mem_root);
nested_join->used_tables= nested_join->not_null_tables= (table_map) 0;
DBUG_RETURN(ptr);
}
@@ -8524,16 +8647,14 @@ bool st_select_lex::add_cross_joined_table(TABLE_LIST *left_op,
SELECT * FROM t1 JOIN t2;
SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a JOIN t3
*/
+ add_joined_table(left_op);
+ add_joined_table(right_op);
right_op->straight= straight_fl;
DBUG_RETURN(false);
}
TABLE_LIST *tbl;
List<TABLE_LIST> *right_op_jl= right_op->join_list;
- IF_DBUG(const TABLE_LIST *r_tbl=,) right_op_jl->pop();
- DBUG_ASSERT(right_op == r_tbl);
- IF_DBUG(const TABLE_LIST *l_tbl=,) right_op_jl->pop();
- DBUG_ASSERT(left_op == l_tbl);
TABLE_LIST *cj_nest;
/*
@@ -8586,7 +8707,7 @@ bool st_select_lex::add_cross_joined_table(TABLE_LIST *left_op,
cj_nest->embedding= tbl->embedding;
cj_nest->join_list= jl;
cj_nest->alias.str= "(nest_last_join)";
- cj_nest->alias.length= sizeof("(nest_last_join)");
+ cj_nest->alias.length= sizeof("(nest_last_join)")-1;
li.replace(cj_nest);
/*
@@ -8801,7 +8922,7 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type, bool for_update
bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
{
SELECT_LEX *first_sl= first_select();
- DBUG_ENTER("add_fake_select_lex");
+ DBUG_ENTER("st_select_lex_unit::add_fake_select_lex");
DBUG_ASSERT(!fake_select_lex);
if (!(fake_select_lex= new (thd_arg->mem_root) SELECT_LEX()))
@@ -8811,16 +8932,19 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
fake_select_lex->select_number= INT_MAX;
fake_select_lex->parent_lex= thd_arg->lex; /* Used in init_query. */
fake_select_lex->make_empty_select();
- fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE;
+ fake_select_lex->set_linkage(GLOBAL_OPTIONS_TYPE);
fake_select_lex->select_limit= 0;
+ fake_select_lex->no_table_names_allowed= 1;
+
fake_select_lex->context.outer_context=first_sl->context.outer_context;
/* allow item list resolving in fake select for ORDER BY */
fake_select_lex->context.resolve_in_select_list= TRUE;
fake_select_lex->context.select_lex= fake_select_lex;
fake_select_lex->nest_level_base= first_select()->nest_level_base;
- fake_select_lex->nest_level=first_select()->nest_level;
+ if (fake_select_lex->set_nest_level(first_select()->nest_level))
+ DBUG_RETURN(1);
if (!is_unit_op())
{
@@ -8833,7 +8957,7 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
fake_select_lex->no_table_names_allowed= 1;
thd_arg->lex->current_select= fake_select_lex;
}
- thd_arg->lex->pop_context();
+ //thd_arg->lex->pop_context("add fake");
DBUG_RETURN(0);
}
@@ -8869,9 +8993,13 @@ push_new_name_resolution_context(THD *thd,
left_op->first_leaf_for_name_resolution();
on_context->last_name_resolution_table=
right_op->last_leaf_for_name_resolution();
- on_context->select_lex = thd->lex->current_select;
- on_context->outer_context = thd->lex->current_context()->outer_context;
- return thd->lex->push_context(on_context, thd->mem_root);
+ LEX *lex= thd->lex;
+ on_context->select_lex = lex->current_select;
+ st_select_lex *curr_select= lex->pop_select();
+ st_select_lex *outer_sel= lex->select_stack_head();
+ lex->push_select(curr_select);
+ on_context->outer_context = outer_sel ? &outer_sel->context : 0;
+ return lex->push_context(on_context);
}
@@ -8960,7 +9088,7 @@ void add_join_on(THD *thd, TABLE_LIST *b, Item *expr)
SELECT * FROM t1, t2 WHERE (t1.j=t2.j and <some_cond>)
@endverbatim
- @param a Left join argument
+ @param a Left join argumentex
@param b Right join argument
@param using_fields Field names from USING clause
*/
@@ -8983,26 +9111,35 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
pointer - thread found, and its LOCK_thd_kill is locked.
*/
-THD *find_thread_by_id(longlong id, bool query_id)
+struct find_thread_callback_arg
{
- THD *tmp;
- mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
- I_List_iterator<THD> it(threads);
- while ((tmp=it++))
+ find_thread_callback_arg(longlong id_arg, bool query_id_arg):
+ thd(0), id(id_arg), query_id(query_id_arg) {}
+ THD *thd;
+ longlong id;
+ bool query_id;
+};
+
+
+static my_bool find_thread_callback(THD *thd, find_thread_callback_arg *arg)
+{
+ if (arg->id == (arg->query_id ? thd->query_id : (longlong) thd->thread_id))
{
- if (tmp->get_command() == COM_DAEMON)
- continue;
- if (id == (query_id ? tmp->query_id : (longlong) tmp->thread_id))
- {
- mysql_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete
- break;
- }
+ mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete
+ arg->thd= thd;
+ return 1;
}
- mysql_mutex_unlock(&LOCK_thread_count);
- return tmp;
+ return 0;
}
+THD *find_thread_by_id(longlong id, bool query_id)
+{
+ find_thread_callback_arg arg(id, query_id);
+ server_threads.iterate(find_thread_callback, &arg);
+ return arg.thd;
+}
+
/**
kill one thread.
@@ -9010,9 +9147,6 @@ THD *find_thread_by_id(longlong id, bool query_id)
@param id Thread id or query id
@param kill_signal Should it kill the query or the connection
@param type Type of id: thread id or query id
-
- @note
- This is written such that we have a short lock on LOCK_thread_count
*/
uint
@@ -9022,8 +9156,11 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ
uint error= (type == KILL_TYPE_QUERY ? ER_NO_SUCH_QUERY : ER_NO_SUCH_THREAD);
DBUG_ENTER("kill_one_thread");
DBUG_PRINT("enter", ("id: %lld signal: %u", id, (uint) kill_signal));
+ tmp= find_thread_by_id(id, type == KILL_TYPE_QUERY);
+ if (!tmp)
+ DBUG_RETURN(error);
- if (id && (tmp= find_thread_by_id(id, type == KILL_TYPE_QUERY)))
+ if (tmp->get_command() != COM_DAEMON)
{
/*
If we're SUPER, we can KILL anything, including system-threads.
@@ -9046,18 +9183,41 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ
faster and do a harder kill than KILL_SYSTEM_THREAD;
*/
+ mysql_mutex_lock(&tmp->LOCK_thd_data); // for various wsrep* checks below
+#ifdef WITH_WSREP
if (((thd->security_ctx->master_access & SUPER_ACL) ||
thd->security_ctx->user_matches(tmp->security_ctx)) &&
- !wsrep_thd_is_BF(tmp, false))
+ !wsrep_thd_is_BF(tmp, false) && !tmp->wsrep_applier)
+#else
+ if ((thd->security_ctx->master_access & SUPER_ACL) ||
+ thd->security_ctx->user_matches(tmp->security_ctx))
+#endif /* WITH_WSREP */
{
- tmp->awake_no_mutex(kill_signal);
- error=0;
+#ifdef WITH_WSREP
+ DEBUG_SYNC(thd, "before_awake_no_mutex");
+ if (tmp->wsrep_aborter && tmp->wsrep_aborter != thd->thread_id)
+ {
+ /* victim is in hit list already, bail out */
+ WSREP_DEBUG("victim has wsrep aborter: %lu, skipping awake()",
+ tmp->wsrep_aborter);
+ error= 0;
+ }
+ else
+#endif /* WITH_WSREP */
+ {
+ WSREP_DEBUG("kill_one_thread %llu, victim: %llu wsrep_aborter %llu by signal %d",
+ thd->thread_id, id, tmp->wsrep_aborter, kill_signal);
+ tmp->awake_no_mutex(kill_signal);
+ WSREP_DEBUG("victim: %llu taken care of", id);
+ error= 0;
+ }
}
else
error= (type == KILL_TYPE_QUERY ? ER_KILL_QUERY_DENIED_ERROR :
ER_KILL_DENIED_ERROR);
- mysql_mutex_unlock(&tmp->LOCK_thd_kill);
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
}
+ mysql_mutex_unlock(&tmp->LOCK_thd_kill);
DBUG_PRINT("exit", ("%d", error));
DBUG_RETURN(error);
}
@@ -9071,56 +9231,67 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ
@param only_kill_query Should it kill the query or the connection
@note
- This is written such that we have a short lock on LOCK_thread_count
-
If we can't kill all threads because of security issues, no threads
are killed.
*/
-static uint kill_threads_for_user(THD *thd, LEX_USER *user,
- killed_state kill_signal, ha_rows *rows)
+struct kill_threads_callback_arg
{
- THD *tmp;
+ kill_threads_callback_arg(THD *thd_arg, LEX_USER *user_arg):
+ thd(thd_arg), user(user_arg) {}
+ THD *thd;
+ LEX_USER *user;
List<THD> threads_to_kill;
- DBUG_ENTER("kill_threads_for_user");
-
- *rows= 0;
-
- if (unlikely(thd->is_fatal_error)) // If we run out of memory
- DBUG_RETURN(ER_OUT_OF_RESOURCES);
+};
- DBUG_PRINT("enter", ("user: %s signal: %u", user->user.str,
- (uint) kill_signal));
- mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
- I_List_iterator<THD> it(threads);
- while ((tmp=it++))
+static my_bool kill_threads_callback(THD *thd, kill_threads_callback_arg *arg)
+{
+ if (thd->security_ctx->user)
{
- if (!tmp->security_ctx->user)
- continue;
/*
Check that hostname (if given) and user name matches.
host.str[0] == '%' means that host name was not given. See sql_yacc.yy
*/
- if (((user->host.str[0] == '%' && !user->host.str[1]) ||
- !strcmp(tmp->security_ctx->host_or_ip, user->host.str)) &&
- !strcmp(tmp->security_ctx->user, user->user.str))
+ if (((arg->user->host.str[0] == '%' && !arg->user->host.str[1]) ||
+ !strcmp(thd->security_ctx->host_or_ip, arg->user->host.str)) &&
+ !strcmp(thd->security_ctx->user, arg->user->user.str))
{
- if (!(thd->security_ctx->master_access & SUPER_ACL) &&
- !thd->security_ctx->user_matches(tmp->security_ctx))
+ if (!(arg->thd->security_ctx->master_access & SUPER_ACL) &&
+ !arg->thd->security_ctx->user_matches(thd->security_ctx))
+ return 1;
+ if (!arg->threads_to_kill.push_back(thd, arg->thd->mem_root))
{
- mysql_mutex_unlock(&LOCK_thread_count);
- DBUG_RETURN(ER_KILL_DENIED_ERROR);
+ mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete
+ mysql_mutex_lock(&thd->LOCK_thd_data);
}
- if (!threads_to_kill.push_back(tmp, thd->mem_root))
- mysql_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete
}
}
- mysql_mutex_unlock(&LOCK_thread_count);
- if (!threads_to_kill.is_empty())
+ return 0;
+}
+
+
+static uint kill_threads_for_user(THD *thd, LEX_USER *user,
+ killed_state kill_signal, ha_rows *rows)
+{
+ kill_threads_callback_arg arg(thd, user);
+ DBUG_ENTER("kill_threads_for_user");
+
+ *rows= 0;
+
+ if (unlikely(thd->is_fatal_error)) // If we run out of memory
+ DBUG_RETURN(ER_OUT_OF_RESOURCES);
+
+ DBUG_PRINT("enter", ("user: %s signal: %u", user->user.str,
+ (uint) kill_signal));
+
+ if (server_threads.iterate(kill_threads_callback, &arg))
+ DBUG_RETURN(ER_KILL_DENIED_ERROR);
+
+ if (!arg.threads_to_kill.is_empty())
{
- List_iterator_fast<THD> it2(threads_to_kill);
+ List_iterator_fast<THD> it2(arg.threads_to_kill);
THD *next_ptr;
THD *ptr= it2++;
do
@@ -9136,6 +9307,7 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
*/
next_ptr= it2++;
mysql_mutex_unlock(&ptr->LOCK_thd_kill);
+ mysql_mutex_unlock(&ptr->LOCK_thd_data);
(*rows)++;
} while ((ptr= next_ptr));
}
@@ -9300,7 +9472,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
{
TABLE_LIST *table;
LEX *lex= thd->lex;
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
DBUG_ENTER("multi_update_precheck");
if (select_lex->item_list.elements != lex->value_list.elements)
@@ -9336,7 +9508,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
/*
Is there tables of subqueries?
*/
- if (&lex->select_lex != lex->all_selects_list)
+ if (lex->first_select_lex() != lex->all_selects_list)
{
DBUG_PRINT("info",("Checking sub query list"));
for (table= tables; table; table= table->next_global)
@@ -9370,7 +9542,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
{
- SELECT_LEX *select_lex= &thd->lex->select_lex;
+ SELECT_LEX *select_lex= thd->lex->first_select_lex();
TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
DBUG_ENTER("multi_delete_precheck");
@@ -9487,7 +9659,7 @@ static TABLE_LIST *multi_delete_table_match(LEX *lex, TABLE_LIST *tbl,
bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
{
- TABLE_LIST *tables= lex->select_lex.table_list.first;
+ TABLE_LIST *tables= lex->first_select_lex()->table_list.first;
TABLE_LIST *target_tbl;
DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables");
@@ -9529,7 +9701,8 @@ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
bool update_precheck(THD *thd, TABLE_LIST *tables)
{
DBUG_ENTER("update_precheck");
- if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements)
+ if (thd->lex->first_select_lex()->item_list.elements !=
+ thd->lex->value_list.elements)
{
my_message(ER_WRONG_VALUE_COUNT, ER_THD(thd, ER_WRONG_VALUE_COUNT), MYF(0));
DBUG_RETURN(TRUE);
@@ -9620,7 +9793,7 @@ void create_table_set_open_action_and_adjust_tables(LEX *lex)
else
create_table->open_type= OT_BASE_ONLY;
- if (!lex->select_lex.item_list.elements)
+ if (!lex->first_select_lex()->item_list.elements)
{
/*
Avoid opening and locking target table for ordinary CREATE TABLE
@@ -9651,7 +9824,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
TABLE_LIST *create_table)
{
LEX *lex= thd->lex;
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
ulong want_priv;
bool error= TRUE; // Error message is given
DBUG_ENTER("create_table_precheck");
@@ -9792,8 +9965,9 @@ Item *negate_expression(THD *thd, Item *expr)
{
/* it is NOT(NOT( ... )) */
Item *arg= ((Item_func *) expr)->arguments()[0];
+ const Type_handler *fh= arg->fixed_type_handler();
enum_parsing_place place= thd->lex->current_select->parsing_place;
- if (arg->is_bool_type() || place == IN_WHERE || place == IN_HAVING)
+ if ((fh && fh->is_bool_type()) || place == IN_WHERE || place == IN_HAVING)
return arg;
/*
if it is not boolean function then we have to emulate value of
@@ -9831,8 +10005,7 @@ void get_default_definer(THD *thd, LEX_USER *definer, bool role)
definer->host.length= strlen(definer->host.str);
}
definer->user.length= strlen(definer->user.str);
-
- definer->reset_auth();
+ definer->auth= NULL;
}
@@ -9891,7 +10064,7 @@ LEX_USER *create_definer(THD *thd, LEX_CSTRING *user_name,
definer->user= *user_name;
definer->host= *host_name;
- definer->reset_auth();
+ definer->auth= NULL;
return definer;
}
@@ -10160,6 +10333,9 @@ bool parse_sql(THD *thd, Parser_state *parser_state,
((thd->variables.sql_mode & MODE_ORACLE) ?
ORAparse(thd) :
MYSQLparse(thd)) != 0;
+ DBUG_ASSERT(opt_bootstrap || mysql_parse_status ||
+ thd->lex->select_stack_top == 0);
+ thd->lex->current_select= thd->lex->first_select_lex();
/*
Check that if MYSQLparse() failed either thd->is_error() is set, or an
@@ -10259,7 +10435,7 @@ CHARSET_INFO *find_bin_collation(CHARSET_INFO *cs)
void LEX::mark_first_table_as_inserting()
{
- TABLE_LIST *t= select_lex.table_list.first;
+ TABLE_LIST *t= first_select_lex()->table_list.first;
DBUG_ENTER("Query_tables_list::mark_tables_with_important_flags");
DBUG_ASSERT(sql_command_flags[sql_command] & CF_INSERTS_DATA);
t->for_insert_data= TRUE;
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index b726bb667b2..be37e3f6bb3 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -99,11 +99,10 @@ void create_table_set_open_action_and_adjust_tables(LEX *lex);
void mysql_init_multi_delete(LEX *lex);
bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
void create_table_set_open_action_and_adjust_tables(LEX *lex);
-pthread_handler_t handle_bootstrap(void *arg);
+int bootstrap(MYSQL_FILE *file);
bool run_set_statement_if_requested(THD *thd, LEX *lex);
int mysql_execute_command(THD *thd);
bool do_command(THD *thd);
-void do_handle_bootstrap(THD *thd);
bool dispatch_command(enum enum_server_command command, THD *thd,
char* packet, uint packet_length,
bool is_com_multi, bool is_next_command);
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index 972684df378..43264e3e508 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -533,15 +533,10 @@ static bool create_full_part_field_array(THD *thd, TABLE *table,
full_part_field_array may be NULL if storage engine supports native
partitioning.
*/
- table->vcol_set= table->read_set= &part_info->full_part_field_set;
+ table->read_set= &part_info->full_part_field_set;
if ((ptr= part_info->full_part_field_array))
for (; *ptr; ptr++)
- {
- if ((*ptr)->vcol_info)
- table->mark_virtual_col(*ptr);
- else
- bitmap_fast_test_and_set(table->read_set, (*ptr)->field_index);
- }
+ table->mark_column_with_deps(*ptr);
table->default_column_bitmaps();
end:
@@ -843,7 +838,8 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
goto end;
table->get_fields_in_item_tree= true;
- func_expr->walk(&Item::change_context_processor, 0, &lex.select_lex.context);
+ func_expr->walk(&Item::change_context_processor, 0,
+ &lex.first_select_lex()->context);
thd->where= "partition function";
/*
In execution we must avoid the use of thd->change_item_tree since
@@ -1509,7 +1505,7 @@ static bool check_list_constants(THD *thd, partition_info *part_info)
List_iterator<part_elem_value> list_val_it2(part_def->list_val_list);
while ((list_value= list_val_it2++))
{
- calc_value= list_value->value - type_add;
+ calc_value= list_value->value ^ type_add;
part_info->list_array[list_index].list_value= calc_value;
part_info->list_array[list_index++].partition_id= i;
}
@@ -1571,7 +1567,7 @@ static bool check_vers_constants(THD *thd, partition_info *part_info)
my_tz_OFFSET0->gmt_sec_to_TIME(&ltime, vers_info->interval.start);
while ((el= it++)->id < hist_parts)
{
- if (date_add_interval(&ltime, vers_info->interval.type,
+ if (date_add_interval(thd, &ltime, vers_info->interval.type,
vers_info->interval.step))
goto err;
uint error= 0;
@@ -2392,6 +2388,8 @@ static int add_column_list_values(String *str, partition_info *part_info,
*/
if (create_info)
{
+ const Column_derived_attributes
+ derived_attr(create_info->default_table_charset);
Create_field *sql_field;
if (!(sql_field= get_sql_field(field_name,
@@ -2406,7 +2404,7 @@ static int add_column_list_values(String *str, partition_info *part_info,
&need_cs_check))
return 1;
if (need_cs_check)
- field_cs= get_sql_field_charset(sql_field, create_info);
+ field_cs= sql_field->explicit_or_derived_charset(&derived_attr);
else
field_cs= NULL;
}
@@ -2667,7 +2665,7 @@ char *generate_partition_syntax(THD *thd, partition_info *part_info,
default:
DBUG_ASSERT(0);
/* We really shouldn't get here, no use in continuing from here */
- my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL));
DBUG_RETURN(NULL);
}
if (part_info->part_type == VERSIONING_PARTITION)
@@ -6132,7 +6130,7 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
lpt->pack_frm_data,
lpt->pack_frm_len))))
{
- file->print_error(error, MYF(error != ER_OUTOFMEMORY ? 0 : ME_FATALERROR));
+ file->print_error(error, MYF(error != ER_OUTOFMEMORY ? 0 : ME_FATAL));
}
if (mysql_trans_commit_alter_copy_data(thd))
@@ -6885,9 +6883,9 @@ static void release_log_entries(partition_info *part_info)
alter_partition_lock_handling()
lpt Struct carrying parameters
RETURN VALUES
- NONE
+ true on error
*/
-static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
+static bool alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
{
THD *thd= lpt->thd;
@@ -6901,23 +6899,9 @@ static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
lpt->table= 0;
lpt->table_list->table= 0;
if (thd->locked_tables_mode)
- {
- Diagnostics_area *stmt_da= NULL;
- Diagnostics_area tmp_stmt_da(true);
-
- if (unlikely(thd->is_error()))
- {
- /* reopen might fail if we have a previous error, use a temporary da. */
- stmt_da= thd->get_stmt_da();
- thd->set_stmt_da(&tmp_stmt_da);
- }
-
- if (unlikely(thd->locked_tables_list.reopen_tables(thd, false)))
- sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
+ return thd->locked_tables_list.reopen_tables(thd, false);
- if (stmt_da)
- thd->set_stmt_da(stmt_da);
- }
+ return false;
}
@@ -7118,6 +7102,8 @@ err_exclusive_lock:
thd->set_stmt_da(&tmp_stmt_da);
}
+ /* NB: error status is not needed here, the statement fails with
+ the original error. */
if (unlikely(thd->locked_tables_list.reopen_tables(thd, false)))
sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
@@ -7341,13 +7327,14 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_ERROR("fail_drop_partition_8") ||
(write_log_completed(lpt, FALSE), FALSE) ||
ERROR_INJECT_CRASH("crash_drop_partition_9") ||
- ERROR_INJECT_ERROR("fail_drop_partition_9") ||
- (alter_partition_lock_handling(lpt), FALSE))
+ ERROR_INJECT_ERROR("fail_drop_partition_9"))
{
handle_alter_part_error(lpt, action_completed, TRUE, frm_install,
close_table_on_failure);
goto err;
}
+ if (alter_partition_lock_handling(lpt))
+ goto err;
}
else if ((alter_info->partition_flags & ALTER_PARTITION_ADD) &&
(part_info->part_type == RANGE_PARTITION ||
@@ -7418,13 +7405,14 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_ERROR("fail_add_partition_9") ||
(write_log_completed(lpt, FALSE), FALSE) ||
ERROR_INJECT_CRASH("crash_add_partition_10") ||
- ERROR_INJECT_ERROR("fail_add_partition_10") ||
- (alter_partition_lock_handling(lpt), FALSE))
+ ERROR_INJECT_ERROR("fail_add_partition_10"))
{
handle_alter_part_error(lpt, action_completed, FALSE, frm_install,
close_table_on_failure);
goto err;
}
+ if (alter_partition_lock_handling(lpt))
+ goto err;
}
else
{
@@ -7523,13 +7511,14 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_ERROR("fail_change_partition_11") ||
(write_log_completed(lpt, FALSE), FALSE) ||
ERROR_INJECT_CRASH("crash_change_partition_12") ||
- ERROR_INJECT_ERROR("fail_change_partition_12") ||
- (alter_partition_lock_handling(lpt), FALSE))
+ ERROR_INJECT_ERROR("fail_change_partition_12"))
{
handle_alter_part_error(lpt, action_completed, FALSE, frm_install,
close_table_on_failure);
goto err;
}
+ if (alter_partition_lock_handling(lpt))
+ goto err;
}
downgrade_mdl_if_lock_tables_mode(thd, mdl_ticket, MDL_SHARED_NO_READ_WRITE);
/*
@@ -8355,7 +8344,8 @@ static int get_part_iter_for_interval_via_mapping(partition_info *part_info,
field->type() == MYSQL_TYPE_DATETIME))
{
/* Monotonic, but return NULL for dates with zeros in month/day. */
- zero_in_start_date= field->get_date(&start_date, 0);
+ DBUG_ASSERT(field->cmp_type() == TIME_RESULT); // No rounding/truncation
+ zero_in_start_date= field->get_date(&start_date, date_mode_t(0));
DBUG_PRINT("info", ("zero start %u %04d-%02d-%02d",
zero_in_start_date, start_date.year,
start_date.month, start_date.day));
@@ -8379,7 +8369,8 @@ static int get_part_iter_for_interval_via_mapping(partition_info *part_info,
!part_info->part_expr->null_value)
{
MYSQL_TIME end_date;
- bool zero_in_end_date= field->get_date(&end_date, 0);
+ DBUG_ASSERT(field->cmp_type() == TIME_RESULT); // No rounding/truncation
+ bool zero_in_end_date= field->get_date(&end_date, date_mode_t(0));
/*
This is an optimization for TO_DAYS()/TO_SECONDS() to avoid scanning
the NULL partition for ranges that cannot include a date with 0 as
diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc
index 90156a76dec..54484846609 100644
--- a/sql/sql_partition_admin.cc
+++ b/sql/sql_partition_admin.cc
@@ -51,7 +51,7 @@ bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd)
/* Moved from mysql_execute_command */
LEX *lex= thd->lex;
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
/* first table of first SELECT_LEX */
TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first;
/*
@@ -742,7 +742,7 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
int error;
ha_partition *partition;
ulong timeout= thd->variables.lock_wait_timeout;
- TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
+ TABLE_LIST *first_table= thd->lex->first_select_lex()->table_list.first;
Alter_info *alter_info= &thd->lex->alter_info;
uint table_counter, i;
List<String> partition_names_list;
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index ffb7d747537..981420f03ae 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2005, 2018, Oracle and/or its affiliates.
- Copyright (c) 2010, 2018, MariaDB Corporation
+ Copyright (c) 2010, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -281,6 +281,7 @@ struct st_mysql_sys_var
MYSQL_PLUGIN_VAR_HEADER;
};
+enum install_status { INSTALL_GOOD, INSTALL_FAIL_WARN_OK, INSTALL_FAIL_NOT_OK };
/*
sys_var class for access to all plugin variables visible to the user
*/
@@ -1078,7 +1079,7 @@ static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin)
NOTE
Requires that a write-lock is held on LOCK_system_variables_hash
*/
-static bool plugin_add(MEM_ROOT *tmp_root,
+static enum install_status plugin_add(MEM_ROOT *tmp_root, bool if_not_exists,
const LEX_CSTRING *name, LEX_CSTRING *dl, myf MyFlags)
{
struct st_plugin_int tmp, *maybe_dupe;
@@ -1089,14 +1090,16 @@ static bool plugin_add(MEM_ROOT *tmp_root,
if (name->str && plugin_find_internal(name, MYSQL_ANY_PLUGIN))
{
+ if (if_not_exists)
+ MyFlags|= ME_NOTE;
my_error(ER_PLUGIN_INSTALLED, MyFlags, name->str);
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(if_not_exists ? INSTALL_FAIL_WARN_OK : INSTALL_FAIL_NOT_OK);
}
/* Clear the whole struct to catch future extensions. */
bzero((char*) &tmp, sizeof(tmp));
fix_dl_name(tmp_root, dl);
if (! (tmp.plugin_dl= plugin_dl_add(dl, MyFlags)))
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(INSTALL_FAIL_NOT_OK);
/* Find plugin by name */
for (plugin= tmp.plugin_dl->plugins; plugin->info; plugin++)
{
@@ -1122,7 +1125,7 @@ static bool plugin_add(MEM_ROOT *tmp_root,
if (plugin->name != maybe_dupe->plugin->name)
{
my_error(ER_UDF_EXISTS, MyFlags, plugin->name);
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(INSTALL_FAIL_NOT_OK);
}
dupes++;
continue; // already installed
@@ -1174,7 +1177,7 @@ static bool plugin_add(MEM_ROOT *tmp_root,
init_alloc_root(&tmp_plugin_ptr->mem_root, "plugin", 4096, 4096, MYF(0));
if (name->str)
- DBUG_RETURN(FALSE); // all done
+ DBUG_RETURN(INSTALL_GOOD); // all done
oks++;
tmp.plugin_dl->ref_count++;
@@ -1192,7 +1195,9 @@ err:
my_error(ER_CANT_FIND_DL_ENTRY, MyFlags, name->str);
plugin_dl_del(tmp.plugin_dl);
- DBUG_RETURN(errs > 0 || oks + dupes == 0);
+ if (errs > 0 || oks + dupes == 0)
+ DBUG_RETURN(INSTALL_FAIL_NOT_OK);
+ DBUG_RETURN(INSTALL_GOOD);
}
static void plugin_variables_deinit(struct st_plugin_int *plugin)
@@ -1796,6 +1801,8 @@ static void plugin_load(MEM_ROOT *tmp_root)
int error;
THD *new_thd= new THD(0);
bool result;
+ unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
+ { MYSQL_AUDIT_GENERAL_CLASSMASK };
DBUG_ENTER("plugin_load");
if (global_system_variables.log_warnings >= 9)
@@ -1845,13 +1852,38 @@ static void plugin_load(MEM_ROOT *tmp_root)
continue;
/*
+ Pre-acquire audit plugins for events that may potentially occur
+ during [UN]INSTALL PLUGIN.
+
+ When audit event is triggered, audit subsystem acquires interested
+ plugins by walking through plugin list. Evidently plugin list
+ iterator protects plugin list by acquiring LOCK_plugin, see
+ plugin_foreach_with_mask().
+
+ On the other hand plugin_load is acquiring LOCK_plugin
+ rather for a long time.
+
+ When audit event is triggered during plugin_load plugin
+ list iterator acquires the same lock (within the same thread)
+ second time.
+
+ This hack should be removed when LOCK_plugin is fixed so it
+ protects only what it supposed to protect.
+
+ See also mysql_install_plugin(), mysql_uninstall_plugin() and
+ initialize_audit_plugin()
+ */
+ if (mysql_audit_general_enabled())
+ mysql_audit_acquire_plugins(new_thd, event_class_mask);
+
+ /*
there're no other threads running yet, so we don't need a mutex.
but plugin_add() before is designed to work in multi-threaded
environment, and it uses mysql_mutex_assert_owner(), so we lock
the mutex here to satisfy the assert
*/
mysql_mutex_lock(&LOCK_plugin);
- plugin_add(tmp_root, &name, &dl, MYF(ME_ERROR_LOG));
+ plugin_add(tmp_root, false, &name, &dl, MYF(ME_ERROR_LOG));
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
mysql_mutex_unlock(&LOCK_plugin);
}
@@ -1874,7 +1906,7 @@ end:
static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list)
{
char buffer[FN_REFLEN];
- LEX_STRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name;
+ LEX_CSTRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name;
char *p= buffer;
DBUG_ENTER("plugin_load_list");
while (list)
@@ -1893,7 +1925,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list)
#ifndef __WIN__
case ':': /* can't use this as delimiter as it may be drive letter */
#endif
- str->str[str->length]= '\0';
+ p[-1]= 0;
if (str == &name) // load all plugins in named module
{
if (!name.length)
@@ -1906,16 +1938,16 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list)
mysql_mutex_lock(&LOCK_plugin);
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
name.str= 0; // load everything
- if (plugin_add(tmp_root, (LEX_CSTRING*) &name, (LEX_CSTRING*) &dl,
- MYF(ME_ERROR_LOG)))
+ if (plugin_add(tmp_root, false, &name, &dl,
+ MYF(ME_ERROR_LOG)) != INSTALL_GOOD)
goto error;
}
else
{
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
mysql_mutex_lock(&LOCK_plugin);
- if (plugin_add(tmp_root, (LEX_CSTRING*) &name, (LEX_CSTRING*) &dl,
- MYF(ME_ERROR_LOG)))
+ if (plugin_add(tmp_root, false, &name, &dl,
+ MYF(ME_ERROR_LOG)) != INSTALL_GOOD)
goto error;
}
mysql_mutex_unlock(&LOCK_plugin);
@@ -1927,7 +1959,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list)
case '#':
if (str == &name)
{
- name.str[name.length]= '\0';
+ p[-1]= 0;
str= &dl;
str->str= p;
continue;
@@ -2150,7 +2182,7 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name,
TABLE_LIST tables;
TABLE *table;
LEX_CSTRING dl= *dl_arg;
- bool error;
+ enum install_status error;
int argc=orig_argc;
char **argv=orig_argv;
unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
@@ -2198,12 +2230,14 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name,
mysql_audit_acquire_plugins(thd, event_class_mask);
mysql_mutex_lock(&LOCK_plugin);
- error= plugin_add(thd->mem_root, name, &dl, MYF(0));
- if (unlikely(error))
+ error= plugin_add(thd->mem_root, thd->lex->create_info.if_not_exists(),
+ name, &dl, MYF(0));
+ if (unlikely(error != INSTALL_GOOD))
goto err;
if (name->str)
- error= finalize_install(thd, table, name, &argc, argv);
+ error= finalize_install(thd, table, name, &argc, argv)
+ ? INSTALL_FAIL_NOT_OK : INSTALL_GOOD;
else
{
st_plugin_dl *plugin_dl= plugin_dl_find(&dl);
@@ -2211,11 +2245,12 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name,
for (plugin= plugin_dl->plugins; plugin->info; plugin++)
{
LEX_CSTRING str= { plugin->name, strlen(plugin->name) };
- error|= finalize_install(thd, table, &str, &argc, argv);
+ if (finalize_install(thd, table, &str, &argc, argv))
+ error= INSTALL_FAIL_NOT_OK;
}
}
- if (unlikely(error))
+ if (unlikely(error != INSTALL_GOOD))
{
reap_needed= true;
reap_plugins();
@@ -2225,10 +2260,11 @@ err:
mysql_mutex_unlock(&LOCK_plugin);
if (argv)
free_defaults(argv);
- DBUG_RETURN(error);
-
-WSREP_ERROR_LABEL:
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(error == INSTALL_FAIL_NOT_OK);
+#ifdef WITH_WSREP
+wsrep_error_label:
+ DBUG_RETURN(true);
+#endif
}
@@ -2240,8 +2276,8 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_CSTRING *name)
if (!(plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN)) ||
plugin->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_DYING))
{
- // maybe plugin is in mysql.plugin present so postpond the error
- plugin= NULL;
+ // maybe plugin is present in mysql.plugin; postpone the error
+ plugin= nullptr;
}
if (plugin)
@@ -2290,8 +2326,9 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_CSTRING *name)
}
else if (!plugin)
{
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str);
- return 1;
+ const myf MyFlags= thd->lex->if_exists() ? ME_NOTE : 0;
+ my_error(ER_SP_DOES_NOT_EXIST, MyFlags, "PLUGIN", name->str);
+ return !MyFlags;
}
return 0;
}
@@ -2313,7 +2350,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name,
if (!opt_noacl && check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE))
DBUG_RETURN(TRUE);
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
/* need to open before acquiring LOCK_plugin or it will deadlock */
if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
@@ -2372,8 +2409,9 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name,
}
else
{
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SONAME", dl.str);
- error= true;
+ myf MyFlags= thd->lex->if_exists() ? ME_NOTE : 0;
+ my_error(ER_SP_DOES_NOT_EXIST, MyFlags, "SONAME", dl.str);
+ error|= !MyFlags;
}
}
reap_plugins();
@@ -2381,9 +2419,10 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name,
global_plugin_version++;
mysql_mutex_unlock(&LOCK_plugin);
DBUG_RETURN(error);
-
-WSREP_ERROR_LABEL:
- DBUG_RETURN(TRUE);
+#ifdef WITH_WSREP
+wsrep_error_label:
+ DBUG_RETURN(true);
+#endif
}
@@ -4353,33 +4392,31 @@ void wsrep_plugins_pre_init()
members of wsrep startup threads with correct values, as these value
were not available at the time these threads were created.
*/
-void wsrep_plugins_post_init()
-{
- THD *thd;
- I_List_iterator<THD> it(threads);
+my_bool post_init_callback(THD *thd, void *)
+{
DBUG_ASSERT(!current_thd);
-
- mysql_mutex_lock(&LOCK_global_system_variables);
-
- while ((thd= it++))
+ if (thd->wsrep_applier)
{
- if (thd->wsrep_applier)
- {
- // Save options_bits as it will get overwritten in
- // plugin_thdvar_init() (verified)
- ulonglong option_bits_saved= thd->variables.option_bits;
+ // Save options_bits as it will get overwritten in
+ // plugin_thdvar_init() (verified)
+ ulonglong option_bits_saved= thd->variables.option_bits;
- set_current_thd(thd);
- plugin_thdvar_init(thd);
+ set_current_thd(thd);
+ plugin_thdvar_init(thd);
- // Restore option_bits
- thd->variables.option_bits= option_bits_saved;
- }
+ // Restore option_bits
+ thd->variables.option_bits= option_bits_saved;
}
+ set_current_thd(0);
+ return 0;
+}
+
+void wsrep_plugins_post_init()
+{
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ server_threads.iterate(post_init_callback);
mysql_mutex_unlock(&LOCK_global_system_variables);
- set_current_thd(0);
- return;
}
#endif /* WITH_WSREP */
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index b266cdb8a9a..eb5532f5d59 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -23,11 +23,11 @@
*/
#define SHOW_always_last SHOW_KEY_CACHE_LONG, \
SHOW_HAVE, SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, \
- SHOW_LONG_NOFLUSH, SHOW_LEX_STRING, \
+ SHOW_LONG_NOFLUSH, SHOW_LEX_STRING, SHOW_ATOMIC_COUNTER_UINT32_T, \
/* SHOW_*_STATUS must be at the end, SHOW_LONG_STATUS being first */ \
SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, SHOW_LONGLONG_STATUS, \
SHOW_UINT32_STATUS
-#include <my_global.h>
+#include "mariadb.h"
#undef SHOW_always_last
#include "m_string.h" /* LEX_STRING */
@@ -205,4 +205,3 @@ extern void sync_dynamic_session_variables(THD* thd, bool global_lock);
extern void wsrep_plugins_pre_init();
extern void wsrep_plugins_post_init();
#endif /* WITH_WSREP */
-
diff --git a/sql/sql_plugin_services.ic b/sql/sql_plugin_services.ic
index 4153873c64f..740569dc76e 100644
--- a/sql/sql_plugin_services.ic
+++ b/sql/sql_plugin_services.ic
@@ -142,50 +142,38 @@ static struct thd_error_context_service_st thd_error_context_handler= {
};
static struct wsrep_service_st wsrep_handler = {
- get_wsrep,
- get_wsrep_certify_nonPK,
- get_wsrep_debug,
- get_wsrep_drupal_282555_workaround,
get_wsrep_recovery,
- get_wsrep_load_data_splitting,
- get_wsrep_log_conflicts,
- get_wsrep_protocol_version,
- wsrep_aborting_thd_contains,
- wsrep_aborting_thd_enqueue,
wsrep_consistency_check,
wsrep_is_wsrep_xid,
wsrep_xid_seqno,
wsrep_xid_uuid,
- wsrep_lock_rollback,
wsrep_on,
- wsrep_post_commit,
- wsrep_prepare_key,
- wsrep_run_wsrep_commit,
+ wsrep_prepare_key_for_innodb,
wsrep_thd_LOCK,
wsrep_thd_UNLOCK,
- wsrep_thd_awake,
- wsrep_thd_conflict_state,
- wsrep_thd_conflict_state_str,
- wsrep_thd_exec_mode,
- wsrep_thd_exec_mode_str,
- wsrep_thd_get_conflict_state,
- wsrep_thd_is_BF,
- wsrep_thd_is_wsrep,
wsrep_thd_query,
- wsrep_thd_query_state,
- wsrep_thd_query_state_str,
wsrep_thd_retry_counter,
- wsrep_thd_set_conflict_state,
wsrep_thd_ignore_table,
wsrep_thd_trx_seqno,
- wsrep_thd_ws_handle,
- wsrep_set_load_multi_commit,
- wsrep_is_load_multi_commit,
- wsrep_trx_is_aborting,
- wsrep_trx_order_before,
- wsrep_unlock_rollback,
+ wsrep_thd_is_aborting,
wsrep_set_data_home_dir,
- wsrep_thd_is_applier,
+ wsrep_thd_is_BF,
+ wsrep_thd_is_local,
+ wsrep_thd_self_abort,
+ wsrep_thd_append_key,
+ wsrep_thd_client_state_str,
+ wsrep_thd_client_mode_str,
+ wsrep_thd_transaction_state_str,
+ wsrep_thd_transaction_id,
+ wsrep_thd_bf_abort,
+ wsrep_thd_order_before,
+ wsrep_handle_SR_rollback,
+ wsrep_thd_skip_locking,
+ wsrep_get_sr_table_name,
+ wsrep_get_debug,
+ wsrep_commit_ordered,
+ wsrep_thd_is_applying,
+ wsrep_thd_set_wsrep_aborter,
wsrep_report_bf_lock_wait,
wsrep_thd_kill_LOCK,
wsrep_thd_kill_UNLOCK
@@ -223,6 +211,16 @@ static struct my_print_error_service_st my_print_error_handler=
my_printv_error
};
+struct json_service_st json_handler=
+{
+ json_type,
+ json_get_array_item,
+ json_get_object_key,
+ json_get_object_nkey,
+ json_escape_string,
+ json_unescape_json
+};
+
static struct st_service_ref list_of_services[]=
{
{ "base64_service", VERSION_base64, &base64_handler },
@@ -245,6 +243,6 @@ static struct st_service_ref list_of_services[]=
{ "thd_specifics_service", VERSION_thd_specifics, &thd_specifics_handler },
{ "thd_timezone_service", VERSION_thd_timezone, &thd_timezone_handler },
{ "thd_wait_service", VERSION_thd_wait, &thd_wait_handler },
- { "wsrep_service", VERSION_wsrep, &wsrep_handler }
+ { "wsrep_service", VERSION_wsrep, &wsrep_handler },
+ { "json_service", VERSION_json, &json_handler }
};
-
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 4ddb50a651e..8d094a09165 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -112,6 +112,7 @@ When one supplies long data for a placeholder:
#include "sp_cache.h"
#include "sql_handler.h" // mysql_ha_rm_tables
#include "probes_mysql.h"
+#include "opt_trace.h"
#ifdef EMBEDDED_LIBRARY
/* include MYSQL_BIND headers */
#include <mysql.h>
@@ -124,7 +125,10 @@ static const uint PARAMETER_FLAG_UNSIGNED= 128U << 8;
#include "log_event.h" // class Log_event
#include "sql_handler.h"
#include "transaction.h" // trans_rollback_implicit
+#ifdef WITH_WSREP
#include "wsrep_mysqld.h"
+#include "wsrep_trans_observer.h"
+#endif /* WITH_WSREP */
/**
A result class used to send cursor rows using the binary protocol.
@@ -192,7 +196,7 @@ public:
void setup_set_params();
virtual Query_arena::Type type() const;
virtual void cleanup_stmt();
- bool set_name(LEX_CSTRING *name);
+ bool set_name(const LEX_CSTRING *name);
inline void close_cursor() { delete cursor; cursor= 0; }
inline bool is_in_use() { return flags & (uint) IS_IN_USE; }
inline bool is_sql_prepare() const { return flags & (uint) IS_SQL_PREPARE; }
@@ -1426,7 +1430,7 @@ static int mysql_test_update(Prepared_statement *stmt,
THD *thd= stmt->thd;
uint table_count= 0;
TABLE_LIST *update_source_table;
- SELECT_LEX *select= &stmt->lex->select_lex;
+ SELECT_LEX *select= stmt->lex->first_select_lex();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint want_privilege;
#endif
@@ -1482,10 +1486,10 @@ static int mysql_test_update(Prepared_statement *stmt,
table_list->table->grant.want_privilege= want_privilege;
table_list->register_want_access(want_privilege);
#endif
- thd->lex->select_lex.no_wrap_view_item= TRUE;
+ thd->lex->first_select_lex()->no_wrap_view_item= TRUE;
res= setup_fields(thd, Ref_ptr_array(),
select->item_list, MARK_COLUMNS_READ, 0, NULL, 0);
- thd->lex->select_lex.no_wrap_view_item= FALSE;
+ thd->lex->first_select_lex()->no_wrap_view_item= FALSE;
if (res)
goto error;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -1550,10 +1554,10 @@ static bool mysql_test_delete(Prepared_statement *stmt,
goto error;
}
- DBUG_RETURN(mysql_prepare_delete(thd, table_list,
- lex->select_lex.with_wild,
- lex->select_lex.item_list,
- &lex->select_lex.where,
+ DBUG_RETURN(mysql_prepare_delete(thd, table_list,
+ lex->first_select_lex()->with_wild,
+ lex->first_select_lex()->item_list,
+ &lex->first_select_lex()->where,
&delete_while_scanning));
error:
DBUG_RETURN(TRUE);
@@ -1585,7 +1589,7 @@ static int mysql_test_select(Prepared_statement *stmt,
SELECT_LEX_UNIT *unit= &lex->unit;
DBUG_ENTER("mysql_test_select");
- lex->select_lex.context.resolve_in_select_list= TRUE;
+ lex->first_select_lex()->context.resolve_in_select_list= TRUE;
ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
if (tables)
@@ -1598,7 +1602,7 @@ static int mysql_test_select(Prepared_statement *stmt,
if (!lex->result && !(lex->result= new (stmt->mem_root) select_send(thd)))
{
- my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR),
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATAL),
static_cast<int>(sizeof(select_send)));
goto error;
}
@@ -1619,7 +1623,7 @@ static int mysql_test_select(Prepared_statement *stmt,
if (!lex->describe && !thd->lex->analyze_stmt && !stmt->is_sql_prepare())
{
/* Make copy of item list, as change_columns may change it */
- List<Item> fields(lex->select_lex.item_list);
+ List<Item> fields(lex->first_select_lex()->item_list);
/* Change columns if a procedure like analyse() */
if (unit->last_procedure && unit->last_procedure->change_columns(thd, fields))
@@ -1777,7 +1781,7 @@ static bool select_like_stmt_test(Prepared_statement *stmt,
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
- lex->select_lex.context.resolve_in_select_list= TRUE;
+ lex->first_select_lex()->context.resolve_in_select_list= TRUE;
if (specific_prepare && (*specific_prepare)(thd))
DBUG_RETURN(TRUE);
@@ -1845,7 +1849,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
DBUG_ENTER("mysql_test_create_table");
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
bool res= FALSE;
bool link_to_local;
TABLE_LIST *create_table= lex->query_tables;
@@ -2177,11 +2181,11 @@ static bool mysql_test_multidelete(Prepared_statement *stmt,
{
THD *thd= stmt->thd;
- thd->lex->current_select= &thd->lex->select_lex;
+ thd->lex->current_select= thd->lex->first_select_lex();
if (add_item_to_list(thd, new (thd->mem_root)
Item_null(thd)))
{
- my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 0);
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 0);
goto error;
}
@@ -2216,13 +2220,14 @@ error:
static int mysql_insert_select_prepare_tester(THD *thd)
{
- SELECT_LEX *first_select= &thd->lex->select_lex;
+ SELECT_LEX *first_select= thd->lex->first_select_lex();
TABLE_LIST *second_table= first_select->table_list.first->next_local;
/* Skip first table, which is the table we are inserting in */
first_select->table_list.first= second_table;
- thd->lex->select_lex.context.table_list=
- thd->lex->select_lex.context.first_name_resolution_table= second_table;
+ thd->lex->first_select_lex()->context.table_list=
+ thd->lex->first_select_lex()->context.first_name_resolution_table=
+ second_table;
return mysql_insert_select_prepare(thd);
}
@@ -2257,7 +2262,7 @@ static bool mysql_test_insert_select(Prepared_statement *stmt,
return 1;
/* store it, because mysql_insert_select_prepare_tester change it */
- first_local_table= lex->select_lex.table_list.first;
+ first_local_table= lex->first_select_lex()->table_list.first;
DBUG_ASSERT(first_local_table != 0);
res=
@@ -2265,7 +2270,7 @@ static bool mysql_test_insert_select(Prepared_statement *stmt,
&mysql_insert_select_prepare_tester,
OPTION_SETUP_TABLES_DONE);
/* revert changes made by mysql_insert_select_prepare_tester */
- lex->select_lex.table_list.first= first_local_table;
+ lex->first_select_lex()->table_list.first= first_local_table;
return res;
}
@@ -2291,7 +2296,7 @@ static int mysql_test_handler_read(Prepared_statement *stmt,
SQL_HANDLER *ha_table;
DBUG_ENTER("mysql_test_handler_read");
- lex->select_lex.context.resolve_in_select_list= TRUE;
+ lex->first_select_lex()->context.resolve_in_select_list= TRUE;
/*
We don't have to test for permissions as this is already done during
@@ -2301,7 +2306,7 @@ static int mysql_test_handler_read(Prepared_statement *stmt,
lex->ident.str,
lex->insert_list,
lex->ha_rkey_mode,
- lex->select_lex.where)))
+ lex->first_select_lex()->where)))
DBUG_RETURN(1);
if (!stmt->is_sql_prepare())
@@ -2340,7 +2345,7 @@ static bool check_prepared_statement(Prepared_statement *stmt)
{
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
TABLE_LIST *tables;
enum enum_sql_command sql_command= lex->sql_command;
int res= 0;
@@ -2349,12 +2354,24 @@ static bool check_prepared_statement(Prepared_statement *stmt)
sql_command, stmt->param_count));
lex->first_lists_tables_same();
+ lex->fix_first_select_number();
tables= lex->query_tables;
/* set context for commands which do not use setup_tables */
- lex->select_lex.context.resolve_in_table_list_only(select_lex->
+ lex->first_select_lex()->context.resolve_in_table_list_only(select_lex->
get_table_list());
+ /*
+ For the optimizer trace, this is the symmetric, for statement preparation,
+ of what is done at statement execution (in mysql_execute_command()).
+ */
+ Opt_trace_start ots(thd, tables, lex->sql_command, &lex->var_list,
+ thd->query(), thd->query_length(),
+ thd->variables.character_set_client);
+
+ Json_writer_object trace_command(thd);
+ Json_writer_array trace_command_steps(thd, "steps");
+
/* Reset warning count for each query that uses tables */
if (tables)
thd->get_stmt_da()->opt_clear_warning_info(thd->query_id);
@@ -2745,7 +2762,7 @@ end:
}
/**
- Get an SQL statement from an item in lex->prepared_stmt_code.
+ Get an SQL statement from an item in m_code.
This function can return pointers to very different memory classes:
- a static string "NULL", if the item returned NULL
@@ -2770,13 +2787,15 @@ end:
@retval true on error (out of memory)
*/
-bool LEX::get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer)
+bool Lex_prepared_stmt::get_dynamic_sql_string(THD *thd,
+ LEX_CSTRING *dst,
+ String *buffer)
{
- if (prepared_stmt_code->fix_fields_if_needed_for_scalar(thd, NULL))
+ if (m_code->fix_fields_if_needed_for_scalar(thd, NULL))
return true;
- const String *str= prepared_stmt_code->val_str(buffer);
- if (prepared_stmt_code->null_value)
+ const String *str= m_code->val_str(buffer);
+ if (m_code->null_value)
{
/*
Prepare source was NULL, so we need to set "str" to
@@ -2857,7 +2876,7 @@ bool LEX::get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer)
void mysql_sql_stmt_prepare(THD *thd)
{
LEX *lex= thd->lex;
- LEX_CSTRING *name= &lex->prepared_stmt_name;
+ const LEX_CSTRING *name= &lex->prepared_stmt.name();
Prepared_statement *stmt;
LEX_CSTRING query;
DBUG_ENTER("mysql_sql_stmt_prepare");
@@ -2882,7 +2901,7 @@ void mysql_sql_stmt_prepare(THD *thd)
See comments in get_dynamic_sql_string().
*/
StringBuffer<256> buffer;
- if (lex->get_dynamic_sql_string(&query, &buffer) ||
+ if (lex->prepared_stmt.get_dynamic_sql_string(thd, &query, &buffer) ||
! (stmt= new Prepared_statement(thd)))
{
DBUG_VOID_RETURN; /* out of memory */
@@ -2945,7 +2964,7 @@ void mysql_sql_stmt_execute_immediate(THD *thd)
LEX_CSTRING query;
DBUG_ENTER("mysql_sql_stmt_execute_immediate");
- if (lex->prepared_stmt_params_fix_fields(thd))
+ if (lex->prepared_stmt.params_fix_fields(thd))
DBUG_VOID_RETURN;
/*
@@ -2957,7 +2976,7 @@ void mysql_sql_stmt_execute_immediate(THD *thd)
See comments in get_dynamic_sql_string().
*/
StringBuffer<256> buffer;
- if (lex->get_dynamic_sql_string(&query, &buffer) ||
+ if (lex->prepared_stmt.get_dynamic_sql_string(thd, &query, &buffer) ||
!(stmt= new Prepared_statement(thd)))
DBUG_VOID_RETURN; // out of memory
@@ -3092,6 +3111,10 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
for (order= win_spec->order_list->first; order; order= order->next)
order->item= &order->item_ptr;
}
+
+ // Reinit Pushdown
+ sl->cond_pushed_into_where= NULL;
+ sl->cond_pushed_into_having= NULL;
}
if (sl->changed_elements & TOUCHED_SEL_DERIVED)
{
@@ -3146,7 +3169,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
{
tables->reinit_before_use(thd);
}
- lex->current_select= &lex->select_lex;
+ lex->current_select= lex->first_select_lex();
if (lex->result)
@@ -3470,7 +3493,7 @@ void mysql_sql_stmt_execute(THD *thd)
{
LEX *lex= thd->lex;
Prepared_statement *stmt;
- LEX_CSTRING *name= &lex->prepared_stmt_name;
+ const LEX_CSTRING *name= &lex->prepared_stmt.name();
/* Query text for binary, general or slow log, if any of them is open */
String expanded_query;
DBUG_ENTER("mysql_sql_stmt_execute");
@@ -3483,7 +3506,7 @@ void mysql_sql_stmt_execute(THD *thd)
DBUG_VOID_RETURN;
}
- if (stmt->param_count != lex->prepared_stmt_params.elements)
+ if (stmt->param_count != lex->prepared_stmt.param_count())
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
DBUG_VOID_RETURN;
@@ -3491,7 +3514,7 @@ void mysql_sql_stmt_execute(THD *thd)
DBUG_PRINT("info",("stmt: %p", stmt));
- if (lex->prepared_stmt_params_fix_fields(thd))
+ if (lex->prepared_stmt.params_fix_fields(thd))
DBUG_VOID_RETURN;
/*
@@ -3711,7 +3734,7 @@ void mysqld_stmt_close(THD *thd, char *packet)
void mysql_sql_stmt_close(THD *thd)
{
Prepared_statement* stmt;
- LEX_CSTRING *name= &thd->lex->prepared_stmt_name;
+ const LEX_CSTRING *name= &thd->lex->prepared_stmt.name();
DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s", (int) name->length,
name->str));
@@ -4076,7 +4099,7 @@ void Prepared_statement::cleanup_stmt()
}
-bool Prepared_statement::set_name(LEX_CSTRING *name_arg)
+bool Prepared_statement::set_name(const LEX_CSTRING *name_arg)
{
name.length= name_arg->length;
name.str= (char*) memdup_root(mem_root, name_arg->str, name_arg->length);
@@ -4194,6 +4217,15 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
thd->is_error() ||
init_param_array(this));
+ if (thd->security_ctx->password_expired &&
+ lex->sql_command != SQLCOM_SET_OPTION)
+ {
+ thd->restore_backup_statement(this, &stmt_backup);
+ thd->restore_active_arena(this, &stmt_backup);
+ thd->stmt_arena= old_stmt_arena;
+ my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
+ DBUG_RETURN(true);
+ }
lex->set_trg_event_type_for_tables();
/*
@@ -4343,7 +4375,7 @@ Prepared_statement::set_parameters(String *expanded_query,
if (is_sql_ps)
{
/* SQL prepared statement */
- res= set_params_from_actual_params(this, thd->lex->prepared_stmt_params,
+ res= set_params_from_actual_params(this, thd->lex->prepared_stmt.params(),
expanded_query);
}
else if (param_count)
@@ -4423,15 +4455,6 @@ Prepared_statement::execute_loop(String *expanded_query,
if (set_parameters(expanded_query, packet, packet_end))
return TRUE;
-#ifdef NOT_YET_FROM_MYSQL_5_6
- if (unlikely(thd->security_ctx->password_expired &&
- !lex->is_change_password))
- {
- my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
- return true;
- }
-#endif
-
reexecute:
// Make sure that reprepare() did not create any new Items.
DBUG_ASSERT(thd->free_list == NULL);
@@ -4453,30 +4476,6 @@ reexecute:
error= execute(expanded_query, open_cursor) || thd->is_error();
thd->m_reprepare_observer= NULL;
-#ifdef WITH_WSREP
-
- if (WSREP_ON)
- {
- mysql_mutex_lock(&thd->LOCK_thd_data);
- switch (thd->wsrep_conflict_state)
- {
- case CERT_FAILURE:
- WSREP_DEBUG("PS execute fail for CERT_FAILURE: thd: %lld err: %d",
- (longlong) thd->thread_id,
- thd->get_stmt_da()->sql_errno() );
- thd->wsrep_conflict_state = NO_CONFLICT;
- break;
-
- case MUST_REPLAY:
- (void) wsrep_replay_transaction(thd);
- break;
-
- default:
- break;
- }
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- }
-#endif /* WITH_WSREP */
if (unlikely(error) &&
(sql_command_flags[lex->sql_command] & CF_REEXECUTION_FRAGILE) &&
@@ -4596,16 +4595,6 @@ Prepared_statement::execute_bulk_loop(String *expanded_query,
}
read_types= FALSE;
-#ifdef NOT_YET_FROM_MYSQL_5_6
- if (unlikely(thd->security_ctx->password_expired &&
- !lex->is_change_password))
- {
- my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
- thd->set_bulk_execution(0);
- return true;
- }
-#endif
-
// iterations changed by set_bulk_parameters
while ((iterations || start_param) && !error && !thd->is_error())
{
@@ -4649,31 +4638,24 @@ reexecute:
error= execute(expanded_query, open_cursor) || thd->is_error();
thd->m_reprepare_observer= NULL;
-#ifdef WITH_WSREP
- if (WSREP_ON)
+#ifdef WITH_WSREP
+ if (!(sql_command_flags[lex->sql_command] & CF_PS_ARRAY_BINDING_OPTIMIZED) &&
+ WSREP(thd))
{
- mysql_mutex_lock(&thd->LOCK_thd_data);
- switch (thd->wsrep_conflict_state)
+ if (wsrep_after_statement(thd))
{
- case CERT_FAILURE:
- WSREP_DEBUG("PS execute fail for CERT_FAILURE: thd: %lld err: %d",
- (longlong) thd->thread_id,
- thd->get_stmt_da()->sql_errno() );
- thd->wsrep_conflict_state = NO_CONFLICT;
- break;
-
- case MUST_REPLAY:
- (void) wsrep_replay_transaction(thd);
- break;
-
- default:
- break;
+ /*
+ Re-execution success is unlikely after an error from
+ wsrep_after_statement(), so retrun error immediately.
+ */
+ thd->get_stmt_da()->reset_diagnostics_area();
+ wsrep_override_error(thd, thd->wsrep_cs().current_error(),
+ thd->wsrep_cs().current_error_status());
}
- mysql_mutex_unlock(&thd->LOCK_thd_data);
}
+ else
#endif /* WITH_WSREP */
-
if (unlikely(error) &&
(sql_command_flags[lex->sql_command] & CF_REEXECUTION_FRAGILE) &&
!thd->is_fatal_error && !thd->killed &&
@@ -4820,8 +4802,8 @@ bool Prepared_statement::validate_metadata(Prepared_statement *copy)
if (is_sql_prepare() || lex->describe)
return FALSE;
- if (lex->select_lex.item_list.elements !=
- copy->lex->select_lex.item_list.elements)
+ if (lex->first_select_lex()->item_list.elements !=
+ copy->lex->first_select_lex()->item_list.elements)
{
/** Column counts mismatch, update the client */
thd->server_status|= SERVER_STATUS_METADATA_CHANGED;
@@ -4978,7 +4960,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
alloc_query(thd, (char*) expanded_query->ptr(),
expanded_query->length()))
{
- my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), expanded_query->length());
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), expanded_query->length());
goto error;
}
/*
@@ -5139,7 +5121,7 @@ bool Prepared_statement::execute_immediate(const char *query, uint query_len)
if (unlikely(prepare(query, query_len)))
DBUG_RETURN(true);
- if (param_count != thd->lex->prepared_stmt_params.elements)
+ if (param_count != thd->lex->prepared_stmt.param_count())
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
deallocate_immediate();
@@ -5558,16 +5540,9 @@ bool Protocol_local::store_longlong(longlong value, bool unsigned_flag)
bool Protocol_local::store_decimal(const my_decimal *value)
{
- char buf[DECIMAL_MAX_STR_LENGTH];
- String str(buf, sizeof (buf), &my_charset_bin);
- int rc;
-
- rc= my_decimal2string(E_DEC_FATAL_ERROR, value, 0, 0, 0, &str);
-
- if (rc)
- return TRUE;
-
- return store_column(str.ptr(), str.length());
+ DBUG_ASSERT(0); // This method is not used yet
+ StringBuffer<DECIMAL_MAX_STR_LENGTH> str;
+ return value->to_string(&str) ? store_column(str.ptr(), str.length()) : true;
}
@@ -5597,7 +5572,7 @@ bool Protocol_local::store(const char *str, size_t length,
bool Protocol_local::store(MYSQL_TIME *time, int decimals)
{
if (decimals != AUTO_SEC_PART_DIGITS)
- my_time_trunc(time, decimals);
+ my_datetime_trunc(time, decimals);
return store_column(time, sizeof(MYSQL_TIME));
}
diff --git a/sql/sql_priv.h b/sql/sql_priv.h
index 2f37e1106f0..0d1c9881c17 100644
--- a/sql/sql_priv.h
+++ b/sql/sql_priv.h
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2018, Oracle and/or its affiliates.
- Copyright (c) 2010, 2018, Monty Program Ab.
+ Copyright (c) 2010, 2019, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -177,7 +177,11 @@
#define OPTION_SKIP_REPLICATION (1ULL << 37) // THD, user
#define OPTION_RPL_SKIP_PARALLEL (1ULL << 38)
-#define OPTION_FOUND_COMMENT (1ULL << 39) // SELECT, intern, parser
+#define OPTION_NO_QUERY_CACHE (1ULL << 39) // SELECT, user
+#define OPTION_PROCEDURE_CLAUSE (1ULL << 40) // Internal usage
+
+
+#define OPTION_LEX_FOUND_COMMENT (1ULL << 0) // intern, parser
/* The rest of the file is included in the server only */
#ifndef MYSQL_CLIENT
@@ -222,6 +226,9 @@
#define OPTIMIZER_SWITCH_ORDERBY_EQ_PROP (1ULL << 29)
#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED (1ULL << 30)
#define OPTIMIZER_SWITCH_SPLIT_MATERIALIZED (1ULL << 31)
+#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_SUBQUERY (1ULL << 32)
+#define OPTIMIZER_SWITCH_USE_ROWID_FILTER (1ULL << 33)
+#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FROM_HAVING (1ULL << 34)
#define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \
OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \
@@ -248,7 +255,11 @@
OPTIMIZER_SWITCH_EXISTS_TO_IN | \
OPTIMIZER_SWITCH_ORDERBY_EQ_PROP | \
OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED | \
- OPTIMIZER_SWITCH_SPLIT_MATERIALIZED)
+ OPTIMIZER_SWITCH_SPLIT_MATERIALIZED | \
+ OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_SUBQUERY | \
+ OPTIMIZER_SWITCH_USE_ROWID_FILTER | \
+ OPTIMIZER_SWITCH_COND_PUSHDOWN_FROM_HAVING | \
+ OPTIMIZER_SWITCH_OPTIMIZE_JOIN_BUFFER_SIZE)
/*
Replication uses 8 bytes to store SQL_MODE in the binary log. The day you
@@ -333,10 +344,15 @@
#ifndef MYSQL_CLIENT
/*
- Some defines for exit codes for ::is_equal class functions.
+ Field::is_equal() return codes.
*/
#define IS_EQUAL_NO 0
#define IS_EQUAL_YES 1
+/**
+ new_field has compatible packed representation with old type,
+ so it is theoretically possible to perform change by only updating
+ data dictionary without changing table rows
+*/
#define IS_EQUAL_PACK_LENGTH 2
enum enum_parsing_place
@@ -350,6 +366,9 @@ enum enum_parsing_place
IN_ORDER_BY,
IN_UPDATE_ON_DUP_KEY,
IN_PART_FUNC,
+ BEFORE_OPT_LIST,
+ AFTER_LIST,
+ FOR_LOOP_BOUND,
PARSING_PLACE_SIZE /* always should be the last */
};
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
index 4d54ab32691..f36805012b2 100644
--- a/sql/sql_profile.cc
+++ b/sql/sql_profile.cc
@@ -110,7 +110,7 @@ int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table)
};
ST_FIELD_INFO *field_info;
- Name_resolution_context *context= &thd->lex->select_lex.context;
+ Name_resolution_context *context= &thd->lex->first_select_lex()->context;
int i;
for (i= 0; schema_table->fields_info[i].field_name != NULL; i++)
@@ -402,7 +402,7 @@ bool PROFILING::show_profiles()
QUERY_PROFILE *prof;
List<Item> field_list;
MEM_ROOT *mem_root= thd->mem_root;
- SELECT_LEX *sel= &thd->lex->select_lex;
+ SELECT_LEX *sel= thd->lex->first_select_lex();
SELECT_LEX_UNIT *unit= &thd->lex->unit;
ha_rows idx= 0;
Protocol *protocol= thd->protocol;
diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc
index 930c00fac35..76fb9819fd5 100644
--- a/sql/sql_reload.cc
+++ b/sql/sql_reload.cc
@@ -153,6 +153,8 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
if (mysql_bin_log.rotate_and_purge(true, drop_gtid_domain))
*write_to_binlog= -1;
+ /* Note that WSREP(thd) might not be true here e.g. during
+ SST. */
if (WSREP_ON)
{
/* Wait for last binlog checkpoint event to be logged. */
@@ -214,7 +216,10 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
!thd->mdl_context.has_locks() ||
thd->handler_tables_hash.records ||
thd->ull_hash.records ||
- thd->global_read_lock.is_acquired());
+ thd->global_read_lock.is_acquired() ||
+ thd->mdl_backup_lock ||
+ thd->current_backup_stage != BACKUP_FINISHED
+ );
/*
Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too
@@ -224,6 +229,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
{
if ((options & REFRESH_READ_LOCK) && thd)
{
+ DBUG_ASSERT(!(options & REFRESH_FAST) && !tables);
/*
On the first hand we need write lock on the tables to be flushed,
on the other hand we must not try to aspire a global read lock
@@ -235,6 +241,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
return 1;
}
+
/*
Writing to the binlog could cause deadlocks, as we don't log
UNLOCK TABLES
@@ -242,9 +249,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
tmp_write_to_binlog= 0;
if (thd->global_read_lock.lock_global_read_lock(thd))
return 1; // Killed
- if (close_cached_tables(thd, tables,
- ((options & REFRESH_FAST) ? FALSE : TRUE),
- thd->variables.lock_wait_timeout))
+ if (flush_tables(thd, FLUSH_ALL))
{
/*
NOTE: my_error() has been already called by reopen_tables() within
@@ -267,11 +272,9 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
make_global_read_lock_block_commit(thd) above since they could have
modified the tables too.
*/
- if (WSREP(thd) &&
- close_cached_tables(thd, tables, (options & REFRESH_FAST) ?
- FALSE : TRUE, TRUE))
- result= 1;
- }
+ if (WSREP(thd) && flush_tables(thd, FLUSH_ALL))
+ result= 1;
+ }
else
{
if (thd && thd->locked_tables_mode)
@@ -304,8 +307,8 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
with global read lock.
*/
if (thd->open_tables &&
- !thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
- MDL_INTENTION_EXCLUSIVE))
+ !thd->mdl_context.is_lock_owner(MDL_key::BACKUP, "", "",
+ MDL_BACKUP_DDL))
{
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),
thd->open_tables->s->table_name.str);
@@ -325,25 +328,21 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
}
#ifdef WITH_WSREP
- if (thd && thd->wsrep_applier)
- {
- /*
- In case of applier thread, do not wait for table share(s) to be
- removed from table definition cache.
- */
- options|= REFRESH_FAST;
- }
-#endif
- if (close_cached_tables(thd, tables,
- ((options & REFRESH_FAST) ? FALSE : TRUE),
- (thd ? thd->variables.lock_wait_timeout :
- LONG_TIMEOUT)))
+ /* In case of applier thread, do not call flush tables */
+ if (!thd || !thd->wsrep_applier)
+#endif /* WITH_WSREP */
{
- /*
- NOTE: my_error() has been already called by reopen_tables() within
- close_cached_tables().
- */
- result= 1;
+ if (close_cached_tables(thd, tables,
+ ((options & REFRESH_FAST) ? FALSE : TRUE),
+ (thd ? thd->variables.lock_wait_timeout :
+ LONG_TIMEOUT)))
+ {
+ /*
+ NOTE: my_error() has been already called by reopen_tables() within
+ close_cached_tables().
+ */
+ result= 1;
+ }
}
}
my_dbopt_cleanup();
@@ -413,6 +412,19 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
#endif
if (options & REFRESH_USER_RESOURCES)
reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
+ if (options & REFRESH_SSL)
+ {
+ if (reinit_ssl())
+ result= 1;
+#ifdef WITH_WSREP
+ if (!result &&
+ WSREP_ON && wsrep_reload_ssl())
+ {
+ my_message(ER_UNKNOWN_ERROR, "Failed to refresh WSREP SSL.", MYF(0));
+ result= 1;
+ }
+#endif
+ }
if (options & REFRESH_GENERIC)
{
List_iterator_fast<LEX_CSTRING> li(thd->lex->view_list);
@@ -524,6 +536,19 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
goto error;
}
+ if (thd->current_backup_stage != BACKUP_FINISHED)
+ {
+ my_error(ER_BACKUP_LOCK_IS_ACTIVE, MYF(0));
+ goto error;
+ }
+
+ /* Should not flush tables while BACKUP LOCK is active */
+ if (thd->mdl_backup_lock)
+ {
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ goto error;
+ }
+
if (thd->lex->type & REFRESH_READ_LOCK)
{
/*
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 389346e389d..841ff561502 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2018, Oracle and/or its affiliates.
- Copyright (c) 2008, 2019, MariaDB Corporation
+ Copyright (c) 2008, 2020, MariaDB Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -31,6 +31,7 @@
#include "debug_sync.h"
#include "semisync_master.h"
#include "semisync_slave.h"
+#include "mysys_err.h"
enum enum_gtid_until_state {
@@ -522,6 +523,22 @@ static enum enum_binlog_checksum_alg get_binlog_checksum_value_at_connect(THD *
DBUG_RETURN(ret);
}
+
+/**
+ Set current_linfo
+
+ Setting current_linfo needs to be done with LOCK_thd_data to ensure that
+ adjust_linfo_offsets doesn't use a structure that may be deleted.
+*/
+
+void THD::set_current_linfo(LOG_INFO *linfo)
+{
+ mysql_mutex_lock(&LOCK_thd_data);
+ current_linfo= linfo;
+ mysql_mutex_unlock(&LOCK_thd_data);
+}
+
+
/*
Adjust the position pointer in the binary log file for all running slaves
@@ -543,61 +560,48 @@ static enum enum_binlog_checksum_alg get_binlog_checksum_value_at_connect(THD *
Now they sync is done for next read.
*/
-void adjust_linfo_offsets(my_off_t purge_offset)
+static my_bool adjust_callback(THD *thd, my_off_t *purge_offset)
{
- THD *tmp;
-
- mysql_mutex_lock(&LOCK_thread_count);
- I_List_iterator<THD> it(threads);
-
- while ((tmp=it++))
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ if (auto linfo= thd->current_linfo)
{
- LOG_INFO* linfo;
- if ((linfo = tmp->current_linfo))
- {
- mysql_mutex_lock(&linfo->lock);
- /*
- Index file offset can be less that purge offset only if
- we just started reading the index file. In that case
- we have nothing to adjust
- */
- if (linfo->index_file_offset < purge_offset)
- linfo->fatal = (linfo->index_file_offset != 0);
- else
- linfo->index_file_offset -= purge_offset;
- mysql_mutex_unlock(&linfo->lock);
- }
+ /*
+ Index file offset can be less that purge offset only if
+ we just started reading the index file. In that case
+ we have nothing to adjust
+ */
+ if (linfo->index_file_offset < *purge_offset)
+ linfo->fatal= (linfo->index_file_offset != 0);
+ else
+ linfo->index_file_offset-= *purge_offset;
}
- mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ return 0;
}
-bool log_in_use(const char* log_name)
+void adjust_linfo_offsets(my_off_t purge_offset)
{
- size_t log_name_len = strlen(log_name) + 1;
- THD *tmp;
- bool result = 0;
-
- mysql_mutex_lock(&LOCK_thread_count);
- I_List_iterator<THD> it(threads);
+ server_threads.iterate(adjust_callback, &purge_offset);
+}
- while ((tmp=it++))
- {
- LOG_INFO* linfo;
- if ((linfo = tmp->current_linfo))
- {
- mysql_mutex_lock(&linfo->lock);
- result = !strncmp(log_name, linfo->log_file_name, log_name_len);
- mysql_mutex_unlock(&linfo->lock);
- if (result)
- break;
- }
- }
- mysql_mutex_unlock(&LOCK_thread_count);
+static my_bool log_in_use_callback(THD *thd, const char *log_name)
+{
+ my_bool result= 0;
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ if (auto linfo= thd->current_linfo)
+ result= !strcmp(log_name, linfo->log_file_name);
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
return result;
}
+
+bool log_in_use(const char* log_name)
+{
+ return server_threads.iterate(log_in_use_callback, log_name);
+}
+
bool purge_error_message(THD* thd, int res)
{
uint errcode;
@@ -901,44 +905,88 @@ static int send_heartbeat_event(binlog_send_info *info,
struct binlog_file_entry
{
binlog_file_entry *next;
- char *name;
+ LEX_CSTRING name;
+ my_off_t size;
};
+/**
+ Read all binary logs and return as a list
+
+ @param memroot Use this for mem_root calls
+ @param reverse If set filenames returned in latest first order (reverse
+ order than in the index file)
+ @param already_locked If set, index file is already locked.
+
+ @return 0 error
+ # pointer to list
+
+ @notes
+ index_file is always unlocked at return
+*/
+
static binlog_file_entry *
-get_binlog_list(MEM_ROOT *memroot)
+get_binlog_list(MEM_ROOT *memroot, bool reverse= true,
+ bool already_locked= false)
{
IO_CACHE *index_file;
- char fname[FN_REFLEN];
- size_t length;
- binlog_file_entry *current_list= NULL, *e;
+ char *fname, *buff, *end_pos;
+ binlog_file_entry *current_list= NULL, *current_link= NULL, *e;
DBUG_ENTER("get_binlog_list");
if (!mysql_bin_log.is_open())
{
+ if (already_locked)
+ mysql_bin_log.unlock_index();
my_error(ER_NO_BINARY_LOGGING, MYF(0));
DBUG_RETURN(NULL);
}
-
- mysql_bin_log.lock_index();
+ if (!already_locked)
+ mysql_bin_log.lock_index();
index_file=mysql_bin_log.get_index_file();
reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 0);
+ if (!(buff= (char*) alloc_root(memroot,
+ (size_t) (index_file->end_of_file+1))))
+ goto err;
+ if (my_b_read(index_file, (uchar*) buff, (size_t) index_file->end_of_file))
+ {
+ my_error(EE_READ, MYF(ME_ERROR_LOG), my_filename(index_file->file),
+ my_errno);
+ goto err;
+ }
+ buff[index_file->end_of_file]= 0; // For strchr
+ mysql_bin_log.unlock_index();
+
/* The file ends with EOF or empty line */
- while ((length=my_b_gets(index_file, fname, sizeof(fname))) > 1)
+ for (fname= buff;
+ (end_pos= strchr(fname, '\n')) && (end_pos - fname) > 1;
+ fname= end_pos+1)
{
- --length; /* Remove the newline */
- if (!(e= (binlog_file_entry *)alloc_root(memroot, sizeof(*e))) ||
- !(e->name= strmake_root(memroot, fname, length)))
- {
- mysql_bin_log.unlock_index();
+ end_pos[0]= '\0'; // remove the newline
+ if (!(e= (binlog_file_entry *) alloc_root(memroot, sizeof(*e))))
DBUG_RETURN(NULL);
+ if (reverse)
+ {
+ e->next= current_list;
+ current_list= e;
+ }
+ else
+ {
+ e->next= NULL;
+ if (!current_link)
+ current_list= e;
+ else
+ current_link->next= e;
+ current_link= e;
}
- e->next= current_list;
- current_list= e;
+ e->name.str= fname;
+ e->name.length= (size_t) (end_pos - fname);
}
- mysql_bin_log.unlock_index();
-
DBUG_RETURN(current_list);
+
+err:
+ mysql_bin_log.unlock_index();
+ DBUG_RETURN(0);
}
@@ -1263,8 +1311,7 @@ gtid_find_binlog_file(slave_connection_state *state, char *out_name,
char buf[FN_REFLEN];
init_alloc_root(&memroot, "gtid_find_binlog_file",
- 10*(FN_REFLEN+sizeof(binlog_file_entry)),
- 0, MYF(MY_THREAD_SPECIFIC));
+ 8192, 0, MYF(MY_THREAD_SPECIFIC));
if (!(list= get_binlog_list(&memroot)))
{
errormsg= "Out of memory while looking for GTID position in binlog";
@@ -1290,7 +1337,7 @@ gtid_find_binlog_file(slave_connection_state *state, char *out_name,
Read the Gtid_list_log_event at the start of the binlog file to
get the binlog state.
*/
- if (normalize_binlog_name(buf, list->name, false))
+ if (normalize_binlog_name(buf, list->name.str, false))
{
errormsg= "Failed to determine binlog file name while looking for "
"GTID position in binlog";
@@ -2181,9 +2228,8 @@ static int init_binlog_sender(binlog_send_info *info,
// set current pos too
linfo->pos= *pos;
-
// note: publish that we use file, before we open it
- thd->current_linfo= linfo;
+ thd->set_current_linfo(linfo);
if (check_start_offset(info, linfo->log_file_name, *pos))
return 1;
@@ -2435,14 +2481,15 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
DBUG_RETURN(0);
}
-static bool should_stop(binlog_send_info *info)
+static bool should_stop(binlog_send_info *info, bool kill_server_check= false)
{
return
- info->net->error ||
- info->net->vio == NULL ||
- info->thd->killed ||
- info->error != 0 ||
- info->should_stop;
+ info->net->error ||
+ info->net->vio == NULL ||
+ (info->thd->killed &&
+ (info->thd->killed != KILL_SERVER || kill_server_check)) ||
+ info->error != 0 ||
+ info->should_stop;
}
/**
@@ -2463,7 +2510,7 @@ static int wait_new_events(binlog_send_info *info, /* in */
&stage_master_has_sent_all_binlog_to_slave,
&old_stage);
- while (!should_stop(info))
+ while (!should_stop(info, true))
{
*end_pos_ptr= mysql_bin_log.get_binlog_end_pos(binlog_end_pos_filename);
if (strcmp(linfo->log_file_name, binlog_end_pos_filename) != 0)
@@ -2818,6 +2865,14 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
info->error= ER_UNKNOWN_ERROR;
goto err;
}
+ DBUG_EXECUTE_IF("simulate_delay_at_shutdown",
+ {
+ const char act[]=
+ "now "
+ "WAIT_FOR greetings_from_kill_mysql";
+ DBUG_ASSERT(!debug_sync_set_action(thd,
+ STRING_WITH_LEN(act)));
+ };);
/*
heartbeat_period from @master_heartbeat_period user variable
@@ -3429,31 +3484,42 @@ err:
slave_server_id the slave's server id
*/
-void kill_zombie_dump_threads(uint32 slave_server_id)
+struct kill_callback_arg
{
- mysql_mutex_lock(&LOCK_thread_count);
- I_List_iterator<THD> it(threads);
- THD *tmp;
+ kill_callback_arg(uint32 id): slave_server_id(id), thd(0) {}
+ uint32 slave_server_id;
+ THD *thd;
+};
- while ((tmp=it++))
+static my_bool kill_callback(THD *thd, kill_callback_arg *arg)
+{
+ if (thd->get_command() == COM_BINLOG_DUMP &&
+ thd->variables.server_id == arg->slave_server_id)
{
- if (tmp->get_command() == COM_BINLOG_DUMP &&
- tmp->variables.server_id == slave_server_id)
- {
- mysql_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete
- break;
- }
+ arg->thd= thd;
+ mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ return 1;
}
- mysql_mutex_unlock(&LOCK_thread_count);
- if (tmp)
+ return 0;
+}
+
+
+void kill_zombie_dump_threads(uint32 slave_server_id)
+{
+ kill_callback_arg arg(slave_server_id);
+ server_threads.iterate(kill_callback, &arg);
+
+ if (arg.thd)
{
/*
Here we do not call kill_one_thread() as
it will be slow because it will iterate through the list
again. We just to do kill the thread ourselves.
*/
- tmp->awake_no_mutex(KILL_SLAVE_SAME_ID);
- mysql_mutex_unlock(&tmp->LOCK_thd_kill);
+ arg.thd->awake_no_mutex(KILL_SLAVE_SAME_ID);
+ mysql_mutex_unlock(&arg.thd->LOCK_thd_kill);
+ mysql_mutex_unlock(&arg.thd->LOCK_thd_data);
}
}
@@ -3901,11 +3967,21 @@ int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len,
if (!mysql_bin_log.is_open())
{
my_message(ER_FLUSH_MASTER_BINLOG_CLOSED,
- ER_THD(thd, ER_FLUSH_MASTER_BINLOG_CLOSED),
- MYF(ME_BELL+ME_WAITTANG));
+ ER_THD(thd, ER_FLUSH_MASTER_BINLOG_CLOSED), MYF(0));
return 1;
}
+#ifdef WITH_WSREP
+ if (WSREP(thd))
+ {
+ /* RESET MASTER will initialize GTID sequence, and that would happen locally
+ in this node, so better reject it
+ */
+ my_message(ER_NOT_ALLOWED_COMMAND,
+ "RESET MASTER not allowed when node is in cluster", MYF(0));
+ return 1;
+ }
+#endif /* WITH_WSREP */
bool ret= 0;
/* Temporarily disable master semisync before resetting master. */
repl_semisync_master.before_reset_master();
@@ -4013,7 +4089,7 @@ bool mysql_show_binlog_events(THD* thd)
goto err;
}
- thd->current_linfo= &linfo;
+ thd->set_current_linfo(&linfo);
if ((file=open_binlog(&log, linfo.log_file_name, &errmsg)) < 0)
goto err;
@@ -4279,17 +4355,25 @@ void show_binlogs_get_fields(THD *thd, List<Item> *field_list)
@retval FALSE success
@retval TRUE failure
+
+ @notes
+ We only keep the index locked while reading all file names as
+ if there are 1000+ binary logs, there can be a serious impact
+ as getting the file sizes can take some notable time (up to 20 seconds
+ has been reported) and we don't want to block log rotations for that long.
*/
+
+#define BINLOG_INDEX_RETRY_COUNT 5
+
bool show_binlogs(THD* thd)
{
- IO_CACHE *index_file;
LOG_INFO cur;
- File file;
- char fname[FN_REFLEN];
+ MEM_ROOT mem_root;
+ binlog_file_entry *list;
List<Item> field_list;
- size_t length;
- size_t cur_dir_len;
Protocol *protocol= thd->protocol;
+ uint retry_count= 0;
+ size_t cur_dir_len;
DBUG_ENTER("show_binlogs");
if (!mysql_bin_log.is_open())
@@ -4303,55 +4387,71 @@ bool show_binlogs(THD* thd)
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
-
+
+ init_alloc_root(&mem_root, "binlog_file_list", 8192, 0,
+ MYF(MY_THREAD_SPECIFIC));
+retry:
+ /*
+ The current mutex handling here is to ensure we get the current log position
+ and all the log files from the index in sync without any index rotation
+ in between.
+ */
mysql_mutex_lock(mysql_bin_log.get_log_lock());
mysql_bin_log.lock_index();
- index_file=mysql_bin_log.get_index_file();
+ mysql_bin_log.raw_get_current_log(&cur);
+ mysql_mutex_unlock(mysql_bin_log.get_log_lock());
- mysql_bin_log.raw_get_current_log(&cur); // dont take mutex
- mysql_mutex_unlock(mysql_bin_log.get_log_lock()); // lockdep, OK
-
- cur_dir_len= dirname_length(cur.log_file_name);
+ /* The following call unlocks lock_index */
+ if ((!(list= get_binlog_list(&mem_root, false, true))))
+ goto err;
- reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 0);
+ DEBUG_SYNC(thd, "at_after_lock_index");
- /* The file ends with EOF or empty line */
- while ((length=my_b_gets(index_file, fname, sizeof(fname))) > 1)
+ // the 1st loop computes the sizes; If stat() fails, then retry
+ cur_dir_len= dirname_length(cur.log_file_name);
+ for (binlog_file_entry *cur_link= list; cur_link; cur_link= cur_link->next)
{
- size_t dir_len;
- ulonglong file_length= 0; // Length if open fails
- fname[--length] = '\0'; // remove the newline
+ const char *fname= cur_link->name.str;
+ size_t dir_len= dirname_length(fname);
+ size_t length= cur_link->name.length- dir_len;
- protocol->prepare_for_resend();
- dir_len= dirname_length(fname);
- length-= dir_len;
- protocol->store(fname + dir_len, length, &my_charset_bin);
+ /* Skip directory name as we shouldn't include this in the result */
+ cur_link->name.str+= dir_len;
+ cur_link->name.length-= dir_len;
if (!(strncmp(fname+dir_len, cur.log_file_name+cur_dir_len, length)))
- file_length= cur.pos; /* The active log, use the active position */
+ cur_link->size= cur.pos; /* The active log, use the active position */
else
{
- /* this is an old log, open it and find the size */
- if ((file= mysql_file_open(key_file_binlog,
- fname, O_RDONLY | O_SHARE | O_BINARY,
- MYF(0))) >= 0)
+ MY_STAT stat_info;
+ if (mysql_file_stat(key_file_binlog, fname, &stat_info, MYF(0)))
+ cur_link->size= stat_info.st_size;
+ else
{
- file_length= (ulonglong) mysql_file_seek(file, 0L, MY_SEEK_END, MYF(0));
- mysql_file_close(file, MYF(0));
+ if (retry_count++ < BINLOG_INDEX_RETRY_COUNT)
+ {
+ free_root(&mem_root, MYF(MY_MARK_BLOCKS_FREE));
+ goto retry;
+ }
+ cur_link->size= 0;
}
}
- protocol->store(file_length);
+ }
+
+ for (binlog_file_entry *cur_link= list; cur_link; cur_link= cur_link->next)
+ {
+ protocol->prepare_for_resend();
+ protocol->store(cur_link->name.str, cur_link->name.length, &my_charset_bin);
+ protocol->store((ulonglong) cur_link->size);
if (protocol->write())
goto err;
}
- if (unlikely(index_file->error == -1))
- goto err;
- mysql_bin_log.unlock_index();
+ free_root(&mem_root, MYF(0));
my_eof(thd);
DBUG_RETURN(FALSE);
err:
- mysql_bin_log.unlock_index();
+ free_root(&mem_root, MYF(0));
DBUG_RETURN(TRUE);
}
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index 9129aaeed5e..95916e31abf 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -21,17 +21,6 @@
#ifdef HAVE_REPLICATION
#include "slave.h"
-typedef struct st_slave_info
-{
- uint32 server_id;
- uint32 rpl_recovery_rank, master_id;
- char host[HOSTNAME_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
- char user[USERNAME_LENGTH+1];
- char password[MAX_PASSWORD_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
- uint16 port;
- THD* thd;
-} SLAVE_INFO;
-
struct slave_connection_state;
extern my_bool opt_show_slave_auth_info;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 6c090ea5352..12d0a9e824e 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -64,6 +64,10 @@
#include "sys_vars_shared.h"
#include "sp_head.h"
#include "sp_rcontext.h"
+#include "rowid_filter.h"
+#include "select_handler.h"
+#include "my_json_writer.h"
+#include "opt_trace.h"
/*
A key part number that means we're using a fulltext scan.
@@ -100,6 +104,9 @@ static int sort_keyuse(KEYUSE *a,KEYUSE *b);
static bool are_tables_local(JOIN_TAB *jtab, table_map used_tables);
static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
bool allow_full_scan, table_map used_tables);
+static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select,
+ TABLE *table,
+ const key_map *keys,ha_rows limit);
static void optimize_straight_join(JOIN *join, table_map join_tables);
static bool greedy_search(JOIN *join, table_map remaining_tables,
uint depth, uint prune_level,
@@ -147,7 +154,8 @@ static COND *build_equal_items(JOIN *join, COND *cond,
static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
COND *cond,
COND_EQUAL *cond_equal,
- void *table_join_idx);
+ void *table_join_idx,
+ bool do_substitution);
static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list,
COND *conds, bool top, bool in_sj);
static bool check_interleaving_with_nj(JOIN_TAB *next);
@@ -342,7 +350,63 @@ bool dbug_user_var_equals_int(THD *thd, const char *name, int value)
}
return FALSE;
}
-#endif
+#endif /* DBUG_OFF */
+
+/*
+ Intialize POSITION structure.
+*/
+
+POSITION::POSITION()
+{
+ table= 0;
+ records_read= cond_selectivity= read_time= 0.0;
+ prefix_record_count= 0.0;
+ key= 0;
+ use_join_buffer= 0;
+ sj_strategy= SJ_OPT_NONE;
+ n_sj_tables= 0;
+ spl_plan= 0;
+ range_rowid_filter_info= 0;
+ ref_depend_map= dups_producing_tables= 0;
+ inner_tables_handled_with_other_sjs= 0;
+ dups_weedout_picker.set_empty();
+ firstmatch_picker.set_empty();
+ loosescan_picker.set_empty();
+ sjmat_picker.set_empty();
+}
+
+
+static void trace_table_dependencies(THD *thd,
+ JOIN_TAB *join_tabs, uint table_count)
+{
+ DBUG_ASSERT(thd->trace_started());
+ Json_writer_object trace_wrapper(thd);
+ Json_writer_array trace_dep(thd, "table_dependencies");
+
+ for (uint i= 0; i < table_count; i++)
+ {
+ TABLE_LIST *table_ref= join_tabs[i].tab_list;
+ Json_writer_object trace_one_table(thd);
+ trace_one_table.add_table_name(&join_tabs[i]);
+ trace_one_table.add("row_may_be_null",
+ (bool)table_ref->table->maybe_null);
+ const table_map map= table_ref->get_map();
+ DBUG_ASSERT(map < (1ULL << table_count));
+ for (uint j= 0; j < table_count; j++)
+ {
+ if (map & (1ULL << j))
+ {
+ trace_one_table.add("map_bit", static_cast<longlong>(j));
+ break;
+ }
+ }
+ Json_writer_array depends_on(thd, "depends_on_map_bits");
+ Table_map_iterator it(join_tabs[i].dependent);
+ uint dep_bit;
+ while ((dep_bit= it++) != Table_map_iterator::BITMAP_END)
+ depends_on.add(static_cast<longlong>(dep_bit));
+ }
+}
/**
@@ -353,7 +417,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
ulong setup_tables_done_option)
{
bool res;
- SELECT_LEX *select_lex = &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
DBUG_ENTER("handle_select");
MYSQL_SELECT_START(thd->query());
@@ -696,8 +760,9 @@ bool vers_select_conds_t::init_from_sysvar(THD *thd)
if (type != SYSTEM_TIME_UNSPECIFIED && type != SYSTEM_TIME_ALL)
{
DBUG_ASSERT(type == SYSTEM_TIME_AS_OF);
+ Datetime dt(&in.ltime);
start.item= new (thd->mem_root)
- Item_datetime_literal(thd, &in.ltime, TIME_SECOND_PART_DIGITS);
+ Item_datetime_literal(thd, &dt, TIME_SECOND_PART_DIGITS);
if (!start.item)
return true;
}
@@ -734,6 +799,130 @@ void vers_select_conds_t::print(String *str, enum_query_type query_type) const
}
static
+Item* period_get_condition(THD *thd, TABLE_LIST *table, SELECT_LEX *select,
+ vers_select_conds_t *conds, bool timestamp)
+{
+ DBUG_ASSERT(table);
+ DBUG_ASSERT(table->table);
+#define newx new (thd->mem_root)
+ TABLE_SHARE *share= table->table->s;
+ const TABLE_SHARE::period_info_t *period= conds->period;
+
+ const LEX_CSTRING &fstart= period->start_field(share)->field_name;
+ const LEX_CSTRING &fend= period->end_field(share)->field_name;
+
+ conds->field_start= newx Item_field(thd, &select->context,
+ table->db.str, table->alias.str,
+ thd->make_clex_string(fstart));
+ conds->field_end= newx Item_field(thd, &select->context,
+ table->db.str, table->alias.str,
+ thd->make_clex_string(fend));
+
+ Item *cond1= NULL, *cond2= NULL, *cond3= NULL, *curr= NULL;
+ if (timestamp)
+ {
+ MYSQL_TIME max_time;
+ switch (conds->type)
+ {
+ case SYSTEM_TIME_UNSPECIFIED:
+ case SYSTEM_TIME_HISTORY:
+ {
+ thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE);
+ max_time.second_part= TIME_MAX_SECOND_PART;
+ Datetime dt(&max_time);
+ curr= newx Item_datetime_literal(thd, &dt, TIME_SECOND_PART_DIGITS);
+ if (conds->type == SYSTEM_TIME_UNSPECIFIED)
+ cond1= newx Item_func_eq(thd, conds->field_end, curr);
+ else
+ cond1= newx Item_func_lt(thd, conds->field_end, curr);
+ break;
+ }
+ case SYSTEM_TIME_AS_OF:
+ cond1= newx Item_func_le(thd, conds->field_start, conds->start.item);
+ cond2= newx Item_func_gt(thd, conds->field_end, conds->start.item);
+ break;
+ case SYSTEM_TIME_FROM_TO:
+ cond1= newx Item_func_lt(thd, conds->field_start, conds->end.item);
+ cond2= newx Item_func_gt(thd, conds->field_end, conds->start.item);
+ cond3= newx Item_func_lt(thd, conds->start.item, conds->end.item);
+ break;
+ case SYSTEM_TIME_BETWEEN:
+ cond1= newx Item_func_le(thd, conds->field_start, conds->end.item);
+ cond2= newx Item_func_gt(thd, conds->field_end, conds->start.item);
+ cond3= newx Item_func_le(thd, conds->start.item, conds->end.item);
+ break;
+ case SYSTEM_TIME_BEFORE:
+ cond1= newx Item_func_history(thd, conds->field_end);
+ cond2= newx Item_func_lt(thd, conds->field_end, conds->start.item);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ }
+ else
+ {
+ DBUG_ASSERT(table->table->s && table->table->s->db_plugin);
+
+ Item *trx_id0= conds->start.item;
+ Item *trx_id1= conds->end.item;
+ if (conds->start.item && conds->start.unit == VERS_TIMESTAMP)
+ {
+ bool backwards= conds->type != SYSTEM_TIME_AS_OF;
+ trx_id0= newx Item_func_trt_id(thd, conds->start.item,
+ TR_table::FLD_TRX_ID, backwards);
+ }
+ if (conds->end.item && conds->end.unit == VERS_TIMESTAMP)
+ {
+ trx_id1= newx Item_func_trt_id(thd, conds->end.item,
+ TR_table::FLD_TRX_ID, false);
+ }
+
+ switch (conds->type)
+ {
+ case SYSTEM_TIME_UNSPECIFIED:
+ case SYSTEM_TIME_HISTORY:
+ curr= newx Item_int(thd, ULONGLONG_MAX);
+ if (conds->type == SYSTEM_TIME_UNSPECIFIED)
+ cond1= newx Item_func_eq(thd, conds->field_end, curr);
+ else
+ cond1= newx Item_func_lt(thd, conds->field_end, curr);
+ break;
+ DBUG_ASSERT(!conds->start.item);
+ DBUG_ASSERT(!conds->end.item);
+ break;
+ case SYSTEM_TIME_AS_OF:
+ cond1= newx Item_func_trt_trx_sees_eq(thd, trx_id0, conds->field_start);
+ cond2= newx Item_func_trt_trx_sees(thd, conds->field_end, trx_id0);
+ DBUG_ASSERT(!conds->end.item);
+ break;
+ case SYSTEM_TIME_FROM_TO:
+ cond1= newx Item_func_trt_trx_sees(thd, trx_id1, conds->field_start);
+ cond2= newx Item_func_trt_trx_sees_eq(thd, conds->field_end, trx_id0);
+ cond3= newx Item_func_lt(thd, conds->start.item, conds->end.item);
+ break;
+ case SYSTEM_TIME_BETWEEN:
+ cond1= newx Item_func_trt_trx_sees_eq(thd, trx_id1, conds->field_start);
+ cond2= newx Item_func_trt_trx_sees_eq(thd, conds->field_end, trx_id0);
+ cond3= newx Item_func_le(thd, conds->start.item, conds->end.item);
+ break;
+ case SYSTEM_TIME_BEFORE:
+ cond1= newx Item_func_history(thd, conds->field_end);
+ cond2= newx Item_func_trt_trx_sees(thd, trx_id0, conds->field_end);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ }
+
+ if (cond1)
+ {
+ cond1= and_items(thd, cond2, cond1);
+ cond1= and_items(thd, cond3, cond1);
+ }
+ return cond1;
+}
+
+static
bool skip_setup_conds(THD *thd)
{
return (!thd->stmt_arena->is_conventional()
@@ -741,17 +930,54 @@ bool skip_setup_conds(THD *thd)
|| thd->lex->is_view_context_analysis();
}
-int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
+int SELECT_LEX::period_setup_conds(THD *thd, TABLE_LIST *tables)
{
- DBUG_ENTER("SELECT_LEX::vers_setup_cond");
-#define newx new (thd->mem_root)
+ DBUG_ENTER("SELECT_LEX::period_setup_conds");
+ const bool update_conds= !skip_setup_conds(thd);
+
+ Query_arena backup;
+ Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
+
+ DBUG_ASSERT(!tables->next_local && tables->table);
+
+ Item *result= NULL;
+ for (TABLE_LIST *table= tables; table; table= table->next_local)
+ {
+ if (!table->table)
+ continue;
+ vers_select_conds_t &conds= table->period_conditions;
+ if (!table->table->s->period.name.streq(conds.name))
+ {
+ my_error(ER_PERIOD_NOT_FOUND, MYF(0), conds.name.str);
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ DBUG_RETURN(-1);
+ }
+
+ if (update_conds)
+ {
+ conds.period= &table->table->s->period;
+ result= and_items(thd, result,
+ period_get_condition(thd, table, this, &conds, true));
+ }
+ }
+ if (update_conds)
+ where= and_items(thd, where, result);
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+
+ DBUG_RETURN(0);
+}
+
+int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
+{
+ DBUG_ENTER("SELECT_LEX::vers_setup_conds");
const bool update_conds= !skip_setup_conds(thd);
- TABLE_LIST *table;
if (!versioned_tables)
{
- for (table= tables; table; table= table->next_local)
+ for (TABLE_LIST *table= tables; table; table= table->next_local)
{
if (table->table && table->table->versioned())
versioned_tables++;
@@ -808,7 +1034,7 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
break;
}
- for (table= tables; table; table= table->next_local)
+ for (TABLE_LIST *table= tables; table; table= table->next_local)
{
if (!table->table || table->is_view() || !table->table->versioned())
continue;
@@ -830,7 +1056,7 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
DBUG_RETURN(-1);
}
else if (!vers_conditions.is_set())
- vers_conditions.type= SYSTEM_TIME_ALL;
+ vers_conditions.set_all();
}
#endif
@@ -862,16 +1088,6 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
continue;
}
- const LEX_CSTRING *fstart=
- thd->make_clex_string(table->table->vers_start_field()->field_name);
- const LEX_CSTRING *fend=
- thd->make_clex_string(table->table->vers_end_field()->field_name);
-
- Item *row_start=
- newx Item_field(thd, &this->context, table->db.str, table->alias.str, fstart);
- Item *row_end=
- newx Item_field(thd, &this->context, table->db.str, table->alias.str, fend);
-
bool timestamps_only= table->table->versioned(VERS_TIMESTAMP);
if (vers_conditions.is_set() && vers_conditions.type != SYSTEM_TIME_HISTORY)
@@ -891,126 +1107,30 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
}
}
- if (!update_conds)
- continue;
-
- Item *cond1= NULL, *cond2= NULL, *cond3= NULL, *curr= NULL;
- Item *point_in_time1= vers_conditions.start.item;
- Item *point_in_time2= vers_conditions.end.item;
- TABLE *t= table->table;
- if (t->versioned(VERS_TIMESTAMP))
- {
- MYSQL_TIME max_time;
- switch (vers_conditions.type)
- {
- case SYSTEM_TIME_UNSPECIFIED:
- case SYSTEM_TIME_HISTORY:
- thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE);
- max_time.second_part= TIME_MAX_SECOND_PART;
- curr= newx Item_datetime_literal(thd, &max_time, TIME_SECOND_PART_DIGITS);
- if (vers_conditions.type == SYSTEM_TIME_UNSPECIFIED)
- cond1= newx Item_func_eq(thd, row_end, curr);
- else
- cond1= newx Item_func_lt(thd, row_end, curr);
- break;
- case SYSTEM_TIME_AS_OF:
- cond1= newx Item_func_le(thd, row_start, point_in_time1);
- cond2= newx Item_func_gt(thd, row_end, point_in_time1);
- break;
- case SYSTEM_TIME_FROM_TO:
- cond1= newx Item_func_lt(thd, row_start, point_in_time2);
- cond2= newx Item_func_gt(thd, row_end, point_in_time1);
- cond3= newx Item_func_lt(thd, point_in_time1, point_in_time2);
- break;
- case SYSTEM_TIME_BETWEEN:
- cond1= newx Item_func_le(thd, row_start, point_in_time2);
- cond2= newx Item_func_gt(thd, row_end, point_in_time1);
- cond3= newx Item_func_le(thd, point_in_time1, point_in_time2);
- break;
- case SYSTEM_TIME_BEFORE:
- cond1= newx Item_func_history(thd, row_end);
- cond2= newx Item_func_lt(thd, row_end, point_in_time1);
- break;
- default:
- DBUG_ASSERT(0);
- }
- }
- else
+ if (update_conds)
{
- DBUG_ASSERT(table->table->s && table->table->s->db_plugin);
-
- Item *trx_id0, *trx_id1;
-
- switch (vers_conditions.type)
- {
- case SYSTEM_TIME_UNSPECIFIED:
- case SYSTEM_TIME_HISTORY:
- curr= newx Item_int(thd, ULONGLONG_MAX);
- if (vers_conditions.type == SYSTEM_TIME_UNSPECIFIED)
- cond1= newx Item_func_eq(thd, row_end, curr);
- else
- cond1= newx Item_func_lt(thd, row_end, curr);
- break;
- case SYSTEM_TIME_AS_OF:
- trx_id0= vers_conditions.start.unit == VERS_TIMESTAMP
- ? newx Item_func_trt_id(thd, point_in_time1, TR_table::FLD_TRX_ID)
- : point_in_time1;
- cond1= newx Item_func_trt_trx_sees_eq(thd, trx_id0, row_start);
- cond2= newx Item_func_trt_trx_sees(thd, row_end, trx_id0);
- break;
- case SYSTEM_TIME_FROM_TO:
- cond3= newx Item_func_lt(thd, point_in_time1, point_in_time2);
- /* fall through */
- case SYSTEM_TIME_BETWEEN:
- trx_id0= vers_conditions.start.unit == VERS_TIMESTAMP
- ? newx Item_func_trt_id(thd, point_in_time1, TR_table::FLD_TRX_ID, true)
- : point_in_time1;
- trx_id1= vers_conditions.end.unit == VERS_TIMESTAMP
- ? newx Item_func_trt_id(thd, point_in_time2, TR_table::FLD_TRX_ID, false)
- : point_in_time2;
- cond1= vers_conditions.type == SYSTEM_TIME_FROM_TO
- ? newx Item_func_trt_trx_sees(thd, trx_id1, row_start)
- : newx Item_func_trt_trx_sees_eq(thd, trx_id1, row_start);
- cond2= newx Item_func_trt_trx_sees_eq(thd, row_end, trx_id0);
- if (!cond3)
- cond3= newx Item_func_le(thd, point_in_time1, point_in_time2);
- break;
- case SYSTEM_TIME_BEFORE:
- trx_id0= vers_conditions.start.unit == VERS_TIMESTAMP
- ? newx Item_func_trt_id(thd, point_in_time1, TR_table::FLD_TRX_ID, true)
- : point_in_time1;
- cond1= newx Item_func_history(thd, row_end);
- cond2= newx Item_func_trt_trx_sees(thd, trx_id0, row_end);
- break;
- default:
- DBUG_ASSERT(0);
- }
- }
-
- if (cond1)
- {
- cond1= and_items(thd, cond2, cond1);
- cond1= and_items(thd, cond3, cond1);
+ vers_conditions.period = &table->table->s->vers;
+ Item *cond= period_get_condition(thd, table, this, &vers_conditions,
+ timestamps_only);
if (is_select)
- table->on_expr= and_items(thd, table->on_expr, cond1);
+ table->on_expr= and_items(thd, table->on_expr, cond);
else
{
if (join)
{
- where= and_items(thd, join->conds, cond1);
+ where= and_items(thd, join->conds, cond);
join->conds= where;
}
else
- where= and_items(thd, where, cond1);
- table->where= and_items(thd, table->where, cond1);
+ where= and_items(thd, where, cond);
+ table->where= and_items(thd, table->where, cond);
}
- }
- table->vers_conditions.type= SYSTEM_TIME_ALL;
+ table->vers_conditions.set_all();
+ }
} // for (table= tables; ...)
DBUG_RETURN(0);
-#undef newx
}
/*****************************************************************************
@@ -1052,10 +1172,17 @@ JOIN::prepare(TABLE_LIST *tables_init,
proc_param= proc_param_init;
tables_list= tables_init;
select_lex= select_lex_arg;
+ DBUG_PRINT("info", ("select %p (%u) = JOIN %p",
+ select_lex, select_lex->select_number, this));
select_lex->join= this;
join_list= &select_lex->top_join_list;
union_part= unit_arg->is_unit_op();
+ Json_writer_object trace_wrapper(thd);
+ Json_writer_object trace_prepare(thd, "join_preparation");
+ trace_prepare.add_select_number(select_lex->select_number);
+ Json_writer_array trace_steps(thd, "steps");
+
// simple check that we got usable conds
dbug_print_item(conds);
@@ -1097,7 +1224,7 @@ JOIN::prepare(TABLE_LIST *tables_init,
while ((select_el= select_it++))
{
- if (select_el->with_sum_func)
+ if (select_el->with_sum_func())
found_sum_func_elem= true;
if (select_el->with_field)
found_field_elem= true;
@@ -1282,7 +1409,7 @@ JOIN::prepare(TABLE_LIST *tables_init,
item->max_length)))
real_order= TRUE;
- if ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) ||
+ if ((item->with_sum_func() && item->type() != Item::SUM_FUNC_ITEM) ||
item->with_window_func)
item->split_sum_func(thd, ref_ptrs, all_fields, SPLIT_SUM_SELECT);
}
@@ -1290,7 +1417,7 @@ JOIN::prepare(TABLE_LIST *tables_init,
order= NULL;
}
- if (having && having->with_sum_func)
+ if (having && having->with_sum_func())
having->split_sum_func2(thd, ref_ptrs, all_fields,
&having, SPLIT_SUM_SKIP_REGISTERED);
if (select_lex->inner_sum_func_list)
@@ -1400,6 +1527,12 @@ JOIN::prepare(TABLE_LIST *tables_init,
}
}
+ if (thd->trace_started())
+ {
+ Json_writer_object trace_wrapper(thd);
+ opt_trace_print_expanded_query(thd, select_lex, &trace_wrapper);
+ }
+
if (!procedure && result && result->prepare(fields_list, unit_arg))
goto err; /* purecov: inspected */
@@ -1460,6 +1593,7 @@ err:
bool JOIN::build_explain()
{
+ DBUG_ENTER("JOIN::build_explain");
have_query_plan= QEP_AVAILABLE;
/*
@@ -1476,8 +1610,7 @@ bool JOIN::build_explain()
thd->mem_root= old_mem_root;
DBUG_ASSERT(thd->free_list == old_free_list); // no Items were created
if (res)
- return 1;
-
+ DBUG_RETURN(1);
uint select_nr= select_lex->select_number;
JOIN_TAB *curr_tab= join_tab + exec_join_tab_cnt();
for (uint i= 0; i < aggr_tables; i++, curr_tab++)
@@ -1489,13 +1622,14 @@ bool JOIN::build_explain()
curr_tab->tracker= thd->lex->explain->get_union(select_nr)->
get_tmptable_read_tracker();
}
- else
+ else if (select_nr < INT_MAX)
{
- curr_tab->tracker= thd->lex->explain->get_select(select_nr)->
- get_using_temporary_read_tracker();
+ Explain_select *tmp= thd->lex->explain->get_select(select_nr);
+ if (tmp)
+ curr_tab->tracker= tmp->get_using_temporary_read_tracker();
}
}
- return 0;
+ DBUG_RETURN(0);
}
@@ -1504,7 +1638,16 @@ int JOIN::optimize()
int res= 0;
create_explain_query_if_not_exists(thd->lex, thd->mem_root);
join_optimization_state init_state= optimization_state;
- if (optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE)
+ if (select_lex->pushdown_select)
+ {
+ if (!(select_options & SELECT_DESCRIBE))
+ {
+ /* Prepare to execute the query pushed into a foreign engine */
+ res= select_lex->pushdown_select->init();
+ }
+ with_two_phase_optimization= false;
+ }
+ else if (optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE)
res= optimize_stage2();
else
{
@@ -1525,6 +1668,139 @@ int JOIN::optimize()
}
+/**
+ @brief
+ Create range filters objects needed in execution for all join tables
+
+ @details
+ For each join table from the chosen execution plan such that a range filter
+ is used when joining this table the function creates a Rowid_filter object
+ for this range filter. In order to do this the function first constructs
+ a quick select to scan the range for this range filter. Then it creates
+ a container for the range filter and finally constructs a Range_rowid_filter
+ object a pointer to which is set in the field JOIN_TAB::rowid_filter of
+ the joined table.
+
+ @retval false always
+*/
+
+bool JOIN::make_range_rowid_filters()
+{
+ DBUG_ENTER("make_range_rowid_filters");
+
+ /*
+ Do not build range filters with detected impossible WHERE.
+ Anyway conditions cannot be used anymore to extract ranges for filters.
+ */
+ if (const_table_map != found_const_table_map)
+ DBUG_RETURN(0);
+
+ JOIN_TAB *tab;
+
+ for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
+ tab;
+ tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
+ {
+ if (!tab->range_rowid_filter_info)
+ continue;
+
+ DBUG_ASSERT(!(tab->ref.key >= 0 &&
+ tab->ref.key == (int) tab->range_rowid_filter_info->key_no));
+ DBUG_ASSERT(!(tab->ref.key == -1 && tab->quick &&
+ tab->quick->index == tab->range_rowid_filter_info->key_no));
+
+ int err;
+ SQL_SELECT *sel= NULL;
+ Rowid_filter_container *filter_container= NULL;
+ Item **sargable_cond= get_sargable_cond(this, tab->table);
+ sel= make_select(tab->table, const_table_map, const_table_map,
+ *sargable_cond, (SORT_INFO*) 0, 1, &err);
+ if (!sel)
+ continue;
+
+ key_map filter_map;
+ filter_map.clear_all();
+ filter_map.set_bit(tab->range_rowid_filter_info->key_no);
+ filter_map.merge(tab->table->with_impossible_ranges);
+ bool force_index_save= tab->table->force_index;
+ tab->table->force_index= true;
+ int rc= sel->test_quick_select(thd, filter_map, (table_map) 0,
+ (ha_rows) HA_POS_ERROR,
+ true, false, true, true);
+ tab->table->force_index= force_index_save;
+ if (thd->is_error())
+ goto no_filter;
+ /*
+ If SUBS_IN_TO_EXISTS strtrategy is chosen for the subquery then
+ additional conditions are injected into WHERE/ON/HAVING and it may
+ happen that the call of test_quick_select() discovers impossible range.
+ */
+ if (rc == -1)
+ {
+ const_table_map|= tab->table->map;
+ goto no_filter;
+ }
+ DBUG_ASSERT(sel->quick);
+ filter_container=
+ tab->range_rowid_filter_info->create_container();
+ if (filter_container)
+ {
+ tab->rowid_filter=
+ new (thd->mem_root) Range_rowid_filter(tab->table,
+ tab->range_rowid_filter_info,
+ filter_container, sel);
+ if (tab->rowid_filter)
+ continue;
+ }
+ no_filter:
+ if (sel->quick)
+ delete sel->quick;
+ delete sel;
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief
+ Allocate memory the rowid containers of the used the range filters
+
+ @details
+ For each join table from the chosen execution plan such that a range filter
+ is used when joining this table the function allocate memory for the
+ rowid container employed by the filter. On success it lets the table engine
+ know that what rowid filter will be used when accessing the table rows.
+
+ @retval false always
+*/
+
+bool
+JOIN::init_range_rowid_filters()
+{
+ DBUG_ENTER("init_range_rowid_filters");
+
+ JOIN_TAB *tab;
+
+ for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
+ tab;
+ tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
+ {
+ if (!tab->rowid_filter)
+ continue;
+ if (tab->rowid_filter->get_container()->alloc())
+ {
+ delete tab->rowid_filter;
+ tab->rowid_filter= 0;
+ continue;
+ }
+ tab->table->file->rowid_filter_push(tab->rowid_filter);
+ tab->is_rowid_filter_built= false;
+ }
+ DBUG_RETURN(0);
+}
+
+
int JOIN::init_join_caches()
{
JOIN_TAB *tab;
@@ -1581,6 +1857,11 @@ JOIN::optimize_inner()
set_allowed_join_cache_types();
need_distinct= TRUE;
+ Json_writer_object trace_wrapper(thd);
+ Json_writer_object trace_prepare(thd, "join_optimization");
+ trace_prepare.add_select_number(select_lex->select_number);
+ Json_writer_array trace_steps(thd, "steps");
+
/*
Needed in case optimizer short-cuts,
set properly in make_aggr_tables_info()
@@ -1664,7 +1945,7 @@ JOIN::optimize_inner()
{
/*
Item_cond_and can't be fixed after creation, so we do not check
- conds->fixed
+ conds->is_fixed()
*/
conds->fix_fields(thd, &conds);
conds->change_ref_to_fields(thd, tables_list);
@@ -1710,7 +1991,7 @@ JOIN::optimize_inner()
if (arena)
thd->restore_active_arena(arena, &backup);
}
-
+
if (optimize_constant_subqueries())
DBUG_RETURN(1);
@@ -1721,9 +2002,28 @@ JOIN::optimize_inner()
(void) having->walk(&Item::cleanup_is_expensive_cache_processor,
0, (void *) 0);
- if (setup_jtbm_semi_joins(this, join_list, &conds))
+ List<Item> eq_list;
+
+ if (setup_degenerate_jtbm_semi_joins(this, join_list, eq_list))
DBUG_RETURN(1);
+ if (eq_list.elements != 0)
+ {
+ Item *new_cond;
+
+ if (eq_list.elements == 1)
+ new_cond= eq_list.pop();
+ else
+ new_cond= new (thd->mem_root) Item_cond_and(thd, eq_list);
+
+ if (new_cond &&
+ ((new_cond->fix_fields(thd, &new_cond) ||
+ !(conds= and_items(thd, conds, new_cond)) ||
+ conds->fix_fields(thd, &conds))))
+ DBUG_RETURN(TRUE);
+ }
+ eq_list.empty();
+
if (select_lex->cond_pushed_into_where)
{
conds= and_conds(thd, conds, select_lex->cond_pushed_into_where);
@@ -1743,7 +2043,7 @@ JOIN::optimize_inner()
select_lex->having_fix_field_for_pushed_cond= 0;
}
}
-
+
bool ignore_on_expr= false;
/*
PS/SP note: on_expr of versioned table can not be reallocated
@@ -1759,7 +2059,7 @@ JOIN::optimize_inner()
}
conds= optimize_cond(this, conds, join_list, ignore_on_expr,
&cond_value, &cond_equal, OPT_LINK_EQUAL_FIELDS);
-
+
if (thd->is_error())
{
error= 1;
@@ -1767,6 +2067,58 @@ JOIN::optimize_inner()
DBUG_RETURN(1);
}
+ having= optimize_cond(this, having, join_list, TRUE,
+ &having_value, &having_equal);
+
+ if (thd->is_error())
+ {
+ error= 1;
+ DBUG_PRINT("error",("Error from optimize_cond"));
+ DBUG_RETURN(1);
+ }
+
+ /* Do not push into WHERE from HAVING if cond_value == Item::COND_FALSE */
+
+ if (thd->lex->sql_command == SQLCOM_SELECT &&
+ optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FROM_HAVING) &&
+ cond_value != Item::COND_FALSE)
+ {
+ having=
+ select_lex->pushdown_from_having_into_where(thd, having);
+ if (select_lex->attach_to_conds.elements != 0)
+ {
+ conds= and_new_conditions_to_optimized_cond(thd, conds, &cond_equal,
+ select_lex->attach_to_conds,
+ &cond_value);
+ sel->attach_to_conds.empty();
+ }
+ }
+
+ if (optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_SUBQUERY))
+ {
+ TABLE_LIST *tbl;
+ List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables);
+ while ((tbl= li++))
+ if (tbl->jtbm_subselect)
+ {
+ if (tbl->jtbm_subselect->pushdown_cond_for_in_subquery(thd, conds))
+ DBUG_RETURN(1);
+ }
+ }
+
+ if (setup_jtbm_semi_joins(this, join_list, eq_list))
+ DBUG_RETURN(1);
+
+ if (eq_list.elements != 0)
+ {
+ conds= and_new_conditions_to_optimized_cond(thd, conds, &cond_equal,
+ eq_list, &cond_value);
+
+ if (!conds &&
+ cond_value != Item::COND_FALSE && cond_value != Item::COND_TRUE)
+ DBUG_RETURN(TRUE);
+ }
+
if (optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED))
{
TABLE_LIST *tbl;
@@ -1804,32 +2156,22 @@ JOIN::optimize_inner()
if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE))
DBUG_RETURN(1);
}
-
{
- having= optimize_cond(this, having, join_list, TRUE,
- &having_value, &having_equal);
-
- if (unlikely(thd->is_error()))
- {
- error= 1;
- DBUG_PRINT("error",("Error from optimize_cond"));
- DBUG_RETURN(1);
- }
if (select_lex->where)
{
select_lex->cond_value= cond_value;
if (sel->where != conds && cond_value == Item::COND_OK)
thd->change_item_tree(&sel->where, conds);
- }
+ }
if (select_lex->having)
{
select_lex->having_value= having_value;
if (sel->having != having && having_value == Item::COND_OK)
- thd->change_item_tree(&sel->having, having);
+ thd->change_item_tree(&sel->having, having);
}
- if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE ||
+ if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE ||
(!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS)))
- { /* Impossible cond */
+ { /* Impossible cond */
if (unit->select_limit_cnt)
{
DBUG_PRINT("info", (having_value == Item::COND_FALSE ?
@@ -2019,6 +2361,9 @@ int JOIN::optimize_stage2()
/* Generate an execution plan from the found optimal join order. */
if (get_best_combination())
DBUG_RETURN(1);
+
+ if (make_range_rowid_filters())
+ DBUG_RETURN(1);
if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE))
DBUG_RETURN(1);
@@ -2077,7 +2422,7 @@ int JOIN::optimize_stage2()
if (!conds && outer_join)
{
/* Handle the case where we have an OUTER JOIN without a WHERE */
- conds= new (thd->mem_root) Item_int(thd, (longlong) 1,1); // Always true
+ conds= new (thd->mem_root) Item_bool(thd, true); // Always true
}
if (impossible_where)
@@ -2113,7 +2458,7 @@ int JOIN::optimize_stage2()
if (conds)
{
conds= substitute_for_best_equal_field(thd, NO_PARTICULAR_TAB, conds,
- cond_equal, map2table);
+ cond_equal, map2table, true);
if (unlikely(thd->is_error()))
{
error= 1;
@@ -2126,6 +2471,23 @@ int JOIN::optimize_stage2()
"after substitute_best_equal",
QT_ORDINARY););
}
+ if (having)
+ {
+ having= substitute_for_best_equal_field(thd, NO_PARTICULAR_TAB, having,
+ having_equal, map2table, false);
+ if (thd->is_error())
+ {
+ error= 1;
+ DBUG_PRINT("error",("Error from substitute_for_best_equal"));
+ DBUG_RETURN(1);
+ }
+ if (having)
+ having->update_used_tables();
+ DBUG_EXECUTE("having",
+ print_where(having,
+ "after substitute_best_equal",
+ QT_ORDINARY););
+ }
/*
Perform the optimization on fields evaluation mentioned above
@@ -2139,7 +2501,7 @@ int JOIN::optimize_stage2()
*tab->on_expr_ref= substitute_for_best_equal_field(thd, NO_PARTICULAR_TAB,
*tab->on_expr_ref,
tab->cond_equal,
- map2table);
+ map2table, true);
if (unlikely(thd->is_error()))
{
error= 1;
@@ -2169,7 +2531,7 @@ int JOIN::optimize_stage2()
while (equals)
{
ref_item= substitute_for_best_equal_field(thd, tab, ref_item,
- equals, map2table);
+ equals, map2table, true);
if (unlikely(thd->is_fatal_error))
DBUG_RETURN(1);
@@ -2217,7 +2579,7 @@ int JOIN::optimize_stage2()
if (conds && const_table_map != found_const_table_map &&
(select_options & SELECT_DESCRIBE))
{
- conds=new (thd->mem_root) Item_int(thd, (longlong) 0, 1); // Always false
+ conds=new (thd->mem_root) Item_bool(thd, false); // Always false
}
/* Cache constant expressions in WHERE, HAVING, ON clauses. */
@@ -2510,13 +2872,13 @@ int JOIN::optimize_stage2()
elements may be lost during further having
condition transformation in JOIN::exec.
*/
- if (having && const_table_map && !having->with_sum_func)
+ if (having && const_table_map && !having->with_sum_func())
{
having->update_used_tables();
having= having->remove_eq_conds(thd, &select_lex->having_value, true);
if (select_lex->having_value == Item::COND_FALSE)
{
- having= new (thd->mem_root) Item_int(thd, (longlong) 0,1);
+ having= new (thd->mem_root) Item_bool(thd, false);
zero_result_cause= "Impossible HAVING noticed after reading const tables";
error= 0;
select_lex->mark_const_derived(zero_result_cause);
@@ -2554,7 +2916,7 @@ int JOIN::optimize_stage2()
{
JOIN_TAB *tab= &join_tab[const_tables];
- if (order)
+ if (order && !need_tmp)
{
/*
Force using of tmp table if sorting by a SP or UDF function due to
@@ -2696,6 +3058,9 @@ int JOIN::optimize_stage2()
if (init_join_caches())
DBUG_RETURN(1);
+ if (init_range_rowid_filters())
+ DBUG_RETURN(1);
+
error= 0;
if (select_options & SELECT_DESCRIBE)
@@ -3259,7 +3624,7 @@ bool JOIN::make_aggr_tables_info()
or end_write_group()) if JOIN::group is set to false.
*/
// the temporary table was explicitly requested
- DBUG_ASSERT(MY_TEST(select_options & OPTION_BUFFER_RESULT));
+ DBUG_ASSERT(select_options & OPTION_BUFFER_RESULT);
// the temporary table does not have a grouping expression
DBUG_ASSERT(!curr_tab->table->group);
}
@@ -3863,6 +4228,15 @@ bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
bool need_tmp_table, bool need_order,
bool distinct)
{
+ DBUG_ENTER("JOIN::save_explain_data");
+ DBUG_PRINT("enter", ("Save explain Select_lex: %u (%p) parent lex: %p stmt_lex: %p present select: %u (%p)",
+ select_lex->select_number, select_lex,
+ select_lex->parent_lex, thd->lex->stmt_lex,
+ (output->get_select(select_lex->select_number) ?
+ select_lex->select_number : 0),
+ (output->get_select(select_lex->select_number) ?
+ output->get_select(select_lex->select_number)
+ ->select_lex : NULL)));
/*
If there is SELECT in this statement with the same number it must be the
same SELECT
@@ -3889,8 +4263,9 @@ bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
/* It's a degenerate join */
message= zero_result_cause ? zero_result_cause : "No tables used";
}
- return save_explain_data_intern(thd->lex->explain, need_tmp_table, need_order,
- distinct, message);
+ bool rc= save_explain_data_intern(thd->lex->explain, need_tmp_table,
+ need_order, distinct, message);
+ DBUG_RETURN(rc);
}
/*
@@ -3912,11 +4287,11 @@ bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
{
if (!(join_tab[i].filesort->tracker=
new Filesort_tracker(thd->lex->analyze_stmt)))
- return 1;
+ DBUG_RETURN(1);
}
}
}
- return 0;
+ DBUG_RETURN(0);
}
@@ -3958,6 +4333,12 @@ void JOIN::exec_inner()
limit in order to produce the partial query result stored in the
UNION temp table.
*/
+
+ Json_writer_object trace_wrapper(thd);
+ Json_writer_object trace_exec(thd, "join_execution");
+ trace_exec.add_select_number(select_lex->select_number);
+ Json_writer_array trace_steps(thd, "steps");
+
if (!select_lex->outer_select() && // (1)
select_lex != select_lex->master_unit()->fake_select_lex) // (2)
thd->lex->set_limit_rows_examined();
@@ -3983,7 +4364,6 @@ void JOIN::exec_inner()
if (select_options & SELECT_DESCRIBE)
select_describe(this, FALSE, FALSE, FALSE,
(zero_result_cause?zero_result_cause:"No tables used"));
-
else
{
if (result->send_result_set_metadata(*columns_list,
@@ -4082,7 +4462,8 @@ void JOIN::exec_inner()
not the case.
*/
if (exec_const_order_group_cond.elements &&
- !(select_options & SELECT_DESCRIBE))
+ !(select_options & SELECT_DESCRIBE) &&
+ !select_lex->pushdown_select)
{
List_iterator_fast<Item> const_item_it(exec_const_order_group_cond);
Item *cur_const_item;
@@ -4109,6 +4490,12 @@ void JOIN::exec_inner()
!table_count ? "No tables used" : NullS);
DBUG_VOID_RETURN;
}
+ else if (select_lex->pushdown_select)
+ {
+ /* Execute the query pushed into a foreign engine */
+ error= select_lex->pushdown_select->execute();
+ DBUG_VOID_RETURN;
+ }
else
{
/* it's a const select, materialize it. */
@@ -4156,6 +4543,9 @@ int
JOIN::destroy()
{
DBUG_ENTER("JOIN::destroy");
+
+ DBUG_PRINT("info", ("select %p (%u) <> JOIN %p",
+ select_lex, select_lex->select_number, this));
select_lex->join= 0;
cond_equal= 0;
@@ -4273,10 +4663,10 @@ mysql_select(THD *thd,
is it single SELECT in derived table, called in derived table
creation
*/
- if (select_lex->linkage != DERIVED_TABLE_TYPE ||
+ if (select_lex->get_linkage() != DERIVED_TABLE_TYPE ||
(select_options & SELECT_DESCRIBE))
{
- if (select_lex->linkage != GLOBAL_OPTIONS_TYPE)
+ if (select_lex->get_linkage() != GLOBAL_OPTIONS_TYPE)
{
/*
Original join tabs might be overwritten at first
@@ -4323,6 +4713,21 @@ mysql_select(THD *thd,
}
}
+ /* Look for a table owned by an engine with the select_handler interface */
+ select_lex->select_h= select_lex->find_select_handler(thd);
+ if (select_lex->select_h)
+ {
+ /* Create a Pushdown_select object for later execution of the query */
+ if (!(select_lex->pushdown_select=
+ new (thd->mem_root) Pushdown_select(select_lex,
+ select_lex->select_h)))
+ {
+ delete select_lex->select_h;
+ select_lex->select_h= NULL;
+ DBUG_RETURN(TRUE);
+ }
+ }
+
if ((err= join->optimize()))
{
goto err; // 1
@@ -4346,6 +4751,13 @@ mysql_select(THD *thd,
}
err:
+
+ if (select_lex->pushdown_select)
+ {
+ delete select_lex->pushdown_select;
+ select_lex->pushdown_select= NULL;
+ }
+
if (free_join)
{
THD_STAGE_INFO(thd, stage_end);
@@ -4378,7 +4790,8 @@ static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select,
select->test_quick_select(thd, *(key_map *)keys,
(table_map) 0,
limit, 0, FALSE,
- TRUE /* remove_where_parts*/)) ==
+ TRUE, /* remove_where_parts*/
+ FALSE)) ==
1))
DBUG_RETURN(select->quick->records);
if (unlikely(error == -1))
@@ -4511,6 +4924,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
SARGABLE_PARAM *sargables= 0;
List_iterator<TABLE_LIST> ti(tables_list);
TABLE_LIST *tables;
+ THD *thd= join->thd;
DBUG_ENTER("make_join_statistics");
table_count=join->table_count;
@@ -4533,6 +4947,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
/* The following should be optimized to only clear critical things */
bzero((void*)stat, sizeof(JOIN_TAB)* table_count);
+
/* Initialize POSITION objects */
for (i=0 ; i <= table_count ; i++)
(void) new ((char*) (join->positions + i)) POSITION;
@@ -4547,10 +4962,6 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
{
TABLE_LIST *embedding= tables->embedding;
stat_vector[i]=s;
- s->keys.init();
- s->const_keys.init();
- s->checked_keys.init();
- s->needed_reg.init();
table_vector[i]=s->table=table=tables->table;
s->tab_list= tables;
table->pos_in_table_list= tables;
@@ -4706,9 +5117,12 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
}
}
+ if (thd->trace_started())
+ trace_table_dependencies(thd, stat, join->table_count);
+
if (join->conds || outer_join)
{
- if (update_ref_and_keys(join->thd, keyuse_array, stat, join->table_count,
+ if (update_ref_and_keys(thd, keyuse_array, stat, join->table_count,
join->conds, ~outer_join, join->select_lex, &sargables))
goto error;
/*
@@ -4720,10 +5134,12 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
((Item_in_subselect*)join->unit->item)->test_strategy(SUBS_IN_TO_EXISTS));
if (keyuse_array->elements &&
- sort_and_filter_keyuse(join->thd, keyuse_array,
+ sort_and_filter_keyuse(thd, keyuse_array,
skip_unprefixed_keyparts))
goto error;
DBUG_EXECUTE("opt", print_keyuse_array(keyuse_array););
+ if (thd->trace_started())
+ print_keyuse_array_for_trace(thd, keyuse_array);
}
join->const_table_map= no_rows_const_tables;
@@ -4971,7 +5387,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
if (join->cond_value == Item::COND_FALSE)
{
join->impossible_where= true;
- conds= new (join->thd->mem_root) Item_int(join->thd, (longlong) 0, 1);
+ conds= new (join->thd->mem_root) Item_bool(join->thd, false);
}
join->cond_equal= NULL;
@@ -5008,143 +5424,170 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
/* Calc how many (possible) matched records in each table */
- for (s=stat ; s < stat_end ; s++)
+ /*
+ Todo: add a function so that we can add these Json_writer_objects
+ easily.
+ Another way would be to enclose them in a scope {};
+ */
{
- s->startup_cost= 0;
- if (s->type == JT_SYSTEM || s->type == JT_CONST)
- {
- /* Only one matching row */
- s->found_records= s->records= 1;
- s->read_time=1.0;
- s->worst_seeks=1.0;
- continue;
- }
- /* Approximate found rows and time to read them */
- if (s->table->is_filled_at_execution())
- {
- get_delayed_table_estimates(s->table, &s->records, &s->read_time,
- &s->startup_cost);
- s->found_records= s->records;
- table->quick_condition_rows=s->records;
- }
- else
+ Json_writer_object rows_estimation_wrapper(thd);
+ Json_writer_array rows_estimation(thd, "rows_estimation");
+
+ for (s=stat ; s < stat_end ; s++)
{
- s->scan_time();
- }
+ s->startup_cost= 0;
+ if (s->type == JT_SYSTEM || s->type == JT_CONST)
+ {
- if (s->table->is_splittable())
- s->add_keyuses_for_splitting();
+ Json_writer_object table_records(thd);
+ /* Only one matching row */
+ s->found_records= s->records= 1;
+ s->read_time=1.0;
+ s->worst_seeks=1.0;
+ table_records.add_table_name(s)
+ .add("rows", s->found_records)
+ .add("cost", s->read_time)
+ .add("table_type", s->type == JT_CONST ?
+ "const" :
+ "system");
+ continue;
+ }
+ /* Approximate found rows and time to read them */
+ if (s->table->is_filled_at_execution())
+ {
+ get_delayed_table_estimates(s->table, &s->records, &s->read_time,
+ &s->startup_cost);
+ s->found_records= s->records;
+ table->quick_condition_rows=s->records;
+ }
+ else
+ s->scan_time();
- /*
- Set a max range of how many seeks we can expect when using keys
- This is can't be to high as otherwise we are likely to use
- table scan.
- */
- s->worst_seeks= MY_MIN((double) s->found_records / 10,
- (double) s->read_time*3);
- if (s->worst_seeks < 2.0) // Fix for small tables
- s->worst_seeks=2.0;
+ if (s->table->is_splittable())
+ s->add_keyuses_for_splitting();
- /*
- Add to stat->const_keys those indexes for which all group fields or
- all select distinct fields participate in one index.
- */
- add_group_and_distinct_keys(join, s);
+ /*
+ Set a max range of how many seeks we can expect when using keys
+ This is can't be to high as otherwise we are likely to use
+ table scan.
+ */
+ s->worst_seeks= MY_MIN((double) s->found_records / 10,
+ (double) s->read_time*3);
+ if (s->worst_seeks < 2.0) // Fix for small tables
+ s->worst_seeks=2.0;
- s->table->cond_selectivity= 1.0;
-
- /*
- Perform range analysis if there are keys it could use (1).
- Don't do range analysis for materialized subqueries (2).
- Don't do range analysis for materialized derived tables (3)
- */
- if ((!s->const_keys.is_clear_all() ||
- !bitmap_is_clear_all(&s->table->cond_set)) && // (1)
- !s->table->is_filled_at_execution() && // (2)
- !(s->table->pos_in_table_list->derived && // (3)
- s->table->pos_in_table_list->is_materialized_derived())) // (3)
- {
- bool impossible_range= FALSE;
- ha_rows records= HA_POS_ERROR;
- SQL_SELECT *select= 0;
- Item **sargable_cond= NULL;
- if (!s->const_keys.is_clear_all())
- {
- sargable_cond= get_sargable_cond(join, s->table);
-
- select= make_select(s->table, found_const_table_map,
- found_const_table_map,
- *sargable_cond,
- (SORT_INFO*) 0,
- 1, &error);
- if (!select)
- goto error;
- records= get_quick_record_count(join->thd, select, s->table,
- &s->const_keys, join->row_limit);
+ /*
+ Add to stat->const_keys those indexes for which all group fields or
+ all select distinct fields participate in one index.
+ */
+ add_group_and_distinct_keys(join, s);
- /*
- Range analyzer might have modified the condition. Put it the new
- condition to where we got it from.
- */
- *sargable_cond= select->cond;
+ s->table->cond_selectivity= 1.0;
- s->quick=select->quick;
- s->needed_reg=select->needed_reg;
- select->quick=0;
- impossible_range= records == 0 && s->table->reginfo.impossible_range;
- }
- if (!impossible_range)
- {
- if (!sargable_cond)
+ /*
+ Perform range analysis if there are keys it could use (1).
+ Don't do range analysis for materialized subqueries (2).
+ Don't do range analysis for materialized derived tables (3)
+ */
+ if ((!s->const_keys.is_clear_all() ||
+ !bitmap_is_clear_all(&s->table->cond_set)) && // (1)
+ !s->table->is_filled_at_execution() && // (2)
+ !(s->table->pos_in_table_list->derived && // (3)
+ s->table->pos_in_table_list->is_materialized_derived())) // (3)
+ {
+ bool impossible_range= FALSE;
+ ha_rows records= HA_POS_ERROR;
+ SQL_SELECT *select= 0;
+ Item **sargable_cond= NULL;
+ if (!s->const_keys.is_clear_all())
+ {
sargable_cond= get_sargable_cond(join, s->table);
- if (join->thd->variables.optimizer_use_condition_selectivity > 1)
- calculate_cond_selectivity_for_table(join->thd, s->table,
- sargable_cond);
- if (s->table->reginfo.impossible_range)
- {
- impossible_range= TRUE;
- records= 0;
+
+ select= make_select(s->table, found_const_table_map,
+ found_const_table_map,
+ *sargable_cond,
+ (SORT_INFO*) 0, 1, &error);
+ if (!select)
+ goto error;
+ records= get_quick_record_count(join->thd, select, s->table,
+ &s->const_keys, join->row_limit);
+
+ /*
+ Range analyzer might have modified the condition. Put it the new
+ condition to where we got it from.
+ */
+ *sargable_cond= select->cond;
+
+ s->quick=select->quick;
+ s->needed_reg=select->needed_reg;
+ select->quick=0;
+ impossible_range= records == 0 && s->table->reginfo.impossible_range;
+ if (join->thd->lex->sql_command == SQLCOM_SELECT &&
+ optimizer_flag(join->thd, OPTIMIZER_SWITCH_USE_ROWID_FILTER))
+ s->table->init_cost_info_for_usable_range_rowid_filters(join->thd);
}
- }
- if (impossible_range)
- {
- /*
- Impossible WHERE or ON expression
- In case of ON, we mark that the we match one empty NULL row.
- In case of WHERE, don't set found_const_table_map to get the
- caller to abort with a zero row result.
- */
- TABLE_LIST *emb= s->table->pos_in_table_list->embedding;
- if (emb && !emb->sj_on_expr)
+ if (!impossible_range)
{
- /* Mark all tables in a multi-table join nest as const */
- mark_join_nest_as_const(join, emb, &found_const_table_map,
- &const_count);
+ if (!sargable_cond)
+ sargable_cond= get_sargable_cond(join, s->table);
+ if (join->thd->variables.optimizer_use_condition_selectivity > 1)
+ calculate_cond_selectivity_for_table(join->thd, s->table,
+ sargable_cond);
+ if (s->table->reginfo.impossible_range)
+ {
+ impossible_range= TRUE;
+ records= 0;
+ }
}
- else
+ if (impossible_range)
{
- join->const_table_map|= s->table->map;
- set_position(join,const_count++,s,(KEYUSE*) 0);
- s->type= JT_CONST;
- s->table->const_table= 1;
- if (*s->on_expr_ref)
+ /*
+ Impossible WHERE or ON expression
+ In case of ON, we mark that the we match one empty NULL row.
+ In case of WHERE, don't set found_const_table_map to get the
+ caller to abort with a zero row result.
+ */
+ TABLE_LIST *emb= s->table->pos_in_table_list->embedding;
+ if (emb && !emb->sj_on_expr)
{
- /* Generate empty row */
- s->info= ET_IMPOSSIBLE_ON_CONDITION;
- found_const_table_map|= s->table->map;
- mark_as_null_row(s->table); // All fields are NULL
+ /* Mark all tables in a multi-table join nest as const */
+ mark_join_nest_as_const(join, emb, &found_const_table_map,
+ &const_count);
}
+ else
+ {
+ join->const_table_map|= s->table->map;
+ set_position(join,const_count++,s,(KEYUSE*) 0);
+ s->type= JT_CONST;
+ s->table->const_table= 1;
+ if (*s->on_expr_ref)
+ {
+ /* Generate empty row */
+ s->info= ET_IMPOSSIBLE_ON_CONDITION;
+ found_const_table_map|= s->table->map;
+ mark_as_null_row(s->table); // All fields are NULL
+ }
+ }
+ }
+ if (records != HA_POS_ERROR)
+ {
+ s->found_records=records;
+ s->read_time= s->quick ? s->quick->read_time : 0.0;
+ }
+ if (select)
+ delete select;
+ else
+ {
+ if (thd->trace_started())
+ add_table_scan_values_to_trace(thd, s);
}
}
- if (records != HA_POS_ERROR)
+ else
{
- s->found_records=records;
- s->read_time= s->quick ? s->quick->read_time : 0.0;
+ if (thd->trace_started())
+ add_table_scan_values_to_trace(thd, s);
}
- if (select)
- delete select;
}
-
}
if (pull_out_semijoin_tables(join))
@@ -6788,6 +7231,7 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
// join->positions[idx].loosescan_key= MAX_KEY; /* Not a LooseScan */
join->positions[idx].sj_strategy= SJ_OPT_NONE;
join->positions[idx].use_join_buffer= FALSE;
+ join->positions[idx].range_rowid_filter_info= 0;
/* Move the const table as down as possible in best_ref */
JOIN_TAB **pos=join->best_ref+idx+1;
@@ -6908,17 +7352,24 @@ best_access_path(JOIN *join,
double best_time= DBL_MAX;
double records= DBL_MAX;
table_map best_ref_depends_map= 0;
+ Range_rowid_filter_cost_info *best_filter= 0;
double tmp;
ha_rows rec;
bool best_uses_jbuf= FALSE;
MY_BITMAP *eq_join_set= &s->table->eq_join_set;
KEYUSE *hj_start_key= 0;
SplM_plan_info *spl_plan= 0;
+ Range_rowid_filter_cost_info *filter= 0;
+ const char* cause= NULL;
+ enum join_type best_type= JT_UNKNOWN, type= JT_UNKNOWN;
disable_jbuf= disable_jbuf || idx == join->const_tables;
Loose_scan_opt loose_scan_opt;
DBUG_ENTER("best_access_path");
+
+ Json_writer_object trace_wrapper(thd, "best_access_path");
+ Json_writer_array trace_paths(thd, "considered_access_paths");
bitmap_clear_all(eq_join_set);
@@ -6946,6 +7397,7 @@ best_access_path(JOIN *join,
key_part_map notnull_part=0; // key parts which won't have NULL in lookup tuple.
table_map found_ref= 0;
uint key= keyuse->key;
+ filter= 0;
bool ft_key= (keyuse->keypart == FT_KEYPART);
/* Bitmap of keyparts where the ref access is over 'keypart=const': */
key_part_map const_part= 0;
@@ -7037,8 +7489,9 @@ best_access_path(JOIN *join,
if (rec < MATCHING_ROWS_IN_OTHER_TABLE)
rec= MATCHING_ROWS_IN_OTHER_TABLE; // Fix for small tables
+ Json_writer_object trace_access_idx(thd);
/*
- ft-keys require special treatment
+ full text keys require special treatment
*/
if (ft_key)
{
@@ -7048,6 +7501,9 @@ best_access_path(JOIN *join,
*/
tmp= prev_record_reads(join_positions, idx, found_ref);
records= 1.0;
+ type= JT_FT;
+ trace_access_idx.add("access_type", join_type_str[type])
+ .add("full-text index", keyinfo->name);
}
else
{
@@ -7069,11 +7525,17 @@ best_access_path(JOIN *join,
(!(key_flags & HA_NULL_PART_KEY) || // (2)
all_key_parts == notnull_part)) // (3)
{
+ type= JT_EQ_REF;
+ trace_access_idx.add("access_type", join_type_str[type])
+ .add("index", keyinfo->name);
tmp = prev_record_reads(join_positions, idx, found_ref);
records=1.0;
}
else
{
+ type= JT_REF;
+ trace_access_idx.add("access_type", join_type_str[type])
+ .add("index", keyinfo->name);
if (!found_ref)
{ /* We found a const key */
/*
@@ -7094,11 +7556,16 @@ best_access_path(JOIN *join,
empty interval we wouldn't have got here).
*/
if (table->quick_keys.is_set(key))
+ {
records= (double) table->quick_rows[key];
+ trace_access_idx.add("used_range_estimates", true);
+ }
else
{
/* quick_range couldn't use key! */
records= (double) s->records/rec;
+ trace_access_idx.add("used_range_estimates", false)
+ .add("cause", "not available");
}
}
else
@@ -7130,7 +7597,23 @@ best_access_path(JOIN *join,
table->quick_n_ranges[key] == 1 &&
records > (double) table->quick_rows[key])
{
+
records= (double) table->quick_rows[key];
+ trace_access_idx.add("used_range_estimates", true);
+ }
+ else
+ {
+ if (table->quick_keys.is_set(key))
+ {
+ trace_access_idx.add("used_range_estimates",false)
+ .add("cause",
+ "not better than ref estimates");
+ }
+ else
+ {
+ trace_access_idx.add("used_range_estimates", false)
+ .add("cause", "not available");
+ }
}
}
/* Limit the number of matched rows */
@@ -7146,6 +7629,9 @@ best_access_path(JOIN *join,
}
else
{
+ type = ref_or_null_part ? JT_REF_OR_NULL : JT_REF;
+ trace_access_idx.add("access_type", join_type_str[type])
+ .add("index", keyinfo->name);
/*
Use as much key-parts as possible and a uniq key is better
than a not unique key
@@ -7200,6 +7686,7 @@ best_access_path(JOIN *join,
table->quick_n_ranges[key] == 1 + MY_TEST(ref_or_null_part)) //(C3)
{
tmp= records= (double) table->quick_rows[key];
+ trace_access_idx.add("used_range_estimates", true);
}
else
{
@@ -7225,7 +7712,19 @@ best_access_path(JOIN *join,
if (!found_ref && table->quick_keys.is_set(key) && // (1)
table->quick_key_parts[key] > max_key_part && // (2)
records < (double)table->quick_rows[key]) // (3)
+ {
+ trace_access_idx.add("used_range_estimates", true);
records= (double)table->quick_rows[key];
+ }
+ else
+ {
+ if (table->quick_keys.is_set(key) &&
+ table->quick_key_parts[key] < max_key_part)
+ {
+ trace_access_idx.add("chosen", false);
+ cause= "range uses more keyparts";
+ }
+ }
tmp= records;
}
@@ -7309,7 +7808,11 @@ best_access_path(JOIN *join,
tmp= COST_MULT(tmp, record_count);
}
else
- tmp= best_time; // Do nothing
+ {
+ if (!(found_part & 1))
+ cause= "no predicate for first keypart";
+ tmp= best_time; // Do nothing
+ }
}
tmp= COST_ADD(tmp, s->startup_cost);
@@ -7317,15 +7820,43 @@ best_access_path(JOIN *join,
found_ref);
} /* not ft_key */
+ if (records < DBL_MAX &&
+ (found_part & 1)) // start_key->key can be used for index access
+ {
+ double rows= record_count * records;
+ double access_cost_factor= MY_MIN(tmp / rows, 1.0);
+ filter=
+ table->best_range_rowid_filter_for_partial_join(start_key->key, rows,
+ access_cost_factor);
+ if (filter)
+ {
+ filter->get_cmp_gain(rows);
+ tmp-= filter->get_adjusted_gain(rows) - filter->get_cmp_gain(rows);
+ DBUG_ASSERT(tmp >= 0);
+ trace_access_idx.add("rowid_filter_key",
+ s->table->key_info[filter->key_no].name);
+ }
+ }
+ trace_access_idx.add("rows", records).add("cost", tmp);
+
if (tmp + 0.0001 < best_time - records/(double) TIME_FOR_COMPARE)
{
+ trace_access_idx.add("chosen", true);
best_time= COST_ADD(tmp, records/(double) TIME_FOR_COMPARE);
best= tmp;
best_records= records;
best_key= start_key;
best_max_key_part= max_key_part;
best_ref_depends_map= found_ref;
+ best_filter= filter;
+ best_type= type;
+ }
+ else
+ {
+ trace_access_idx.add("chosen", false)
+ .add("cause", cause ? cause : "cost");
}
+ cause= NULL;
} /* for each key */
records= best_records;
}
@@ -7347,6 +7878,7 @@ best_access_path(JOIN *join,
(!(s->table->map & join->outer_join) ||
join->allowed_outer_join_with_cache)) // (2)
{
+ Json_writer_object trace_access_hash(thd);
double join_sel= 0.1;
/* Estimate the cost of the hash join access to the table */
double rnd_records= matching_candidates_in_table(s, found_constraint,
@@ -7370,7 +7902,14 @@ best_access_path(JOIN *join,
best_key= hj_start_key;
best_ref_depends_map= 0;
best_uses_jbuf= TRUE;
- }
+ best_filter= 0;
+ best_type= JT_HASH;
+ trace_access_hash.add("type", "hash");
+ trace_access_hash.add("index", "hj-key");
+ trace_access_hash.add("cost", rnd_records);
+ trace_access_hash.add("cost", best);
+ trace_access_hash.add("chosen", true);
+ }
/*
Don't test table scan if it can't be better.
@@ -7406,6 +7945,7 @@ best_access_path(JOIN *join,
can be [considered to be] more expensive, which causes lookups not to
be used for cases with small datasets, which is annoying.
*/
+ Json_writer_object trace_access_scan(thd);
if ((records >= s->found_records || best > s->read_time) && // (1)
!(best_key && best_key->key == MAX_KEY) && // (2)
!(s->quick && best_key && s->quick->index == best_key->key && // (2)
@@ -7424,6 +7964,7 @@ best_access_path(JOIN *join,
Here we estimate its cost.
*/
+ filter= 0;
if (s->quick)
{
/*
@@ -7439,15 +7980,41 @@ best_access_path(JOIN *join,
tmp= COST_MULT(record_count,
COST_ADD(s->quick->read_time, cmp_time));
+ if ( s->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE)
+ {
+ double rows= record_count * s->found_records;
+ double access_cost_factor= MY_MIN(tmp / rows, 1.0);
+ uint key_no= s->quick->index;
+ filter=
+ s->table->best_range_rowid_filter_for_partial_join(key_no, rows,
+ access_cost_factor);
+ if (filter)
+ {
+ tmp-= filter->get_adjusted_gain(rows);
+ DBUG_ASSERT(tmp >= 0);
+ }
+ type= JT_RANGE;
+ }
+ else
+ {
+ type= JT_INDEX_MERGE;
+ best_filter= 0;
+ }
loose_scan_opt.check_range_access(join, idx, s->quick);
}
else
{
/* Estimate cost of reading table. */
if (s->table->force_index && !best_key) // index scan
+ {
+ type= JT_NEXT;
tmp= s->table->file->read_time(s->ref.key, 1, s->records);
+ }
else // table scan
+ {
tmp= s->scan_time();
+ type= JT_ALL;
+ }
if ((s->table->map & join->outer_join) || disable_jbuf) // Can't use join cache
{
@@ -7477,20 +8044,32 @@ best_access_path(JOIN *join,
}
}
+ trace_access_scan.add("access_type", type == JT_ALL ?
+ "scan" :
+ join_type_str[type]);
/* Splitting technique cannot be used with join cache */
if (s->table->is_splittable())
tmp+= s->table->get_materialization_cost();
else
tmp+= s->startup_cost;
+
/*
We estimate the cost of evaluating WHERE clause for found records
as record_count * rnd_records / TIME_FOR_COMPARE. This cost plus
tmp give us total cost of using TABLE SCAN
*/
+
+ const double best_filter_cmp_gain= best_filter
+ ? best_filter->get_cmp_gain(record_count * records)
+ : 0;
+ trace_access_scan.add("resulting_rows", rnd_records);
+ trace_access_scan.add("cost", tmp);
+
if (best == DBL_MAX ||
COST_ADD(tmp, record_count/(double) TIME_FOR_COMPARE*rnd_records) <
(best_key->is_for_hash_join() ? best_time :
- COST_ADD(best, record_count/(double) TIME_FOR_COMPARE*records)))
+ COST_ADD(best - best_filter_cmp_gain,
+ record_count/(double) TIME_FOR_COMPARE*records)))
{
/*
If the table has a range (s->quick is set) make_join_select()
@@ -7499,12 +8078,23 @@ best_access_path(JOIN *join,
best= tmp;
records= rnd_records;
best_key= 0;
+ best_filter= 0;
+ if (s->quick && s->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE)
+ best_filter= filter;
/* range/index_merge/ALL/index access method are "independent", so: */
best_ref_depends_map= 0;
best_uses_jbuf= MY_TEST(!disable_jbuf && !((s->table->map &
join->outer_join)));
spl_plan= 0;
+ best_type= type;
}
+ trace_access_scan.add("chosen", best_key == NULL);
+ }
+ else
+ {
+ trace_access_scan.add("type", "scan");
+ trace_access_scan.add("chosen", false);
+ trace_access_scan.add("cause", "cost");
}
/* Update the cost information for the current partial plan */
@@ -7516,6 +8106,7 @@ best_access_path(JOIN *join,
pos->loosescan_picker.loosescan_key= MAX_KEY;
pos->use_join_buffer= best_uses_jbuf;
pos->spl_plan= spl_plan;
+ pos->range_rowid_filter_info= best_filter;
loose_scan_opt.save_to_position(s, loose_scan_pos);
@@ -7523,7 +8114,15 @@ best_access_path(JOIN *join,
idx == join->const_tables &&
s->table == join->sort_by_table &&
join->unit->select_limit_cnt >= records)
+ {
+ trace_access_scan.add("use_tmp_table", true);
join->sort_by_table= (TABLE*) 1; // Must use temporary table
+ }
+ trace_access_scan.end();
+ trace_paths.end();
+
+ if (unlikely(thd->trace_started()))
+ print_best_access_for_table(thd, pos, best_type);
DBUG_VOID_RETURN;
}
@@ -7666,6 +8265,7 @@ choose_plan(JOIN *join, table_map join_tables)
uint use_cond_selectivity=
join->thd->variables.optimizer_use_condition_selectivity;
bool straight_join= MY_TEST(join->select_options & SELECT_STRAIGHT_JOIN);
+ THD *thd= join->thd;
DBUG_ENTER("choose_plan");
join->cur_embedding_map= 0;
@@ -7702,6 +8302,9 @@ choose_plan(JOIN *join, table_map join_tables)
join->table_count - join->const_tables, sizeof(JOIN_TAB*),
jtab_sort_func, (void*)join->emb_sjm_nest);
+ Json_writer_object wrapper(thd);
+ Json_writer_array trace_plan(thd,"considered_execution_plans");
+
if (!join->emb_sjm_nest)
{
choose_initial_table_order(join);
@@ -7995,19 +8598,30 @@ optimize_straight_join(JOIN *join, table_map join_tables)
uint use_cond_selectivity=
join->thd->variables.optimizer_use_condition_selectivity;
POSITION loose_scan_pos;
+ THD *thd= join->thd;
for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
{
+ POSITION *position= join->positions + idx;
+ Json_writer_object trace_one_table(thd);
+ if (unlikely(thd->trace_started()))
+ {
+ trace_plan_prefix(join, idx, join_tables);
+ trace_one_table.add_table_name(s);
+ }
/* Find the best access method from 's' to the current partial plan */
best_access_path(join, s, join_tables, join->positions, idx,
disable_jbuf, record_count,
- join->positions + idx, &loose_scan_pos);
+ position, &loose_scan_pos);
/* compute the cost of the new plan extended with 's' */
- record_count= COST_MULT(record_count, join->positions[idx].records_read);
- read_time= COST_ADD(read_time,
- COST_ADD(join->positions[idx].read_time,
- record_count / (double) TIME_FOR_COMPARE));
+ record_count= COST_MULT(record_count, position->records_read);
+ const double filter_cmp_gain= position->range_rowid_filter_info
+ ? position->range_rowid_filter_info->get_cmp_gain(record_count)
+ : 0;
+ read_time+= COST_ADD(read_time - filter_cmp_gain,
+ COST_ADD(position->read_time,
+ record_count / (double) TIME_FOR_COMPARE));
advance_sj_state(join, join_tables, idx, &record_count, &read_time,
&loose_scan_pos);
@@ -8016,7 +8630,7 @@ optimize_straight_join(JOIN *join, table_map join_tables)
if (use_cond_selectivity > 1)
pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
join_tables);
- join->positions[idx].cond_selectivity= pushdown_cond_selectivity;
+ position->cond_selectivity= pushdown_cond_selectivity;
++idx;
}
@@ -8677,7 +9291,7 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
fldno= table->key_info[key].key_part[keyparts-1].fieldnr - 1;
if (table->field[fldno]->cond_selectivity > 0)
- {
+ {
sel /= table->field[fldno]->cond_selectivity;
set_if_smaller(sel, 1.0);
}
@@ -8732,9 +9346,9 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
next_field= next_field->next_equal_field)
{
if (!(next_field->table->map & rem_tables) && next_field->table != table)
- {
+ {
if (field->cond_selectivity > 0)
- {
+ {
sel/= field->cond_selectivity;
set_if_smaller(sel, 1.0);
}
@@ -8928,6 +9542,13 @@ best_extension_by_limited_search(JOIN *join,
double current_record_count, current_read_time;
POSITION *position= join->positions + idx;
+ Json_writer_object trace_one_table(thd);
+ if (unlikely(thd->trace_started()))
+ {
+ trace_plan_prefix(join, idx, remaining_tables);
+ trace_one_table.add_table_name(s);
+ }
+
/* Find the best access method from 's' to the current partial plan */
POSITION loose_scan_pos;
best_access_path(join, s, remaining_tables, join->positions, idx,
@@ -8935,11 +9556,20 @@ best_extension_by_limited_search(JOIN *join,
/* Compute the cost of extending the plan with 's' */
current_record_count= COST_MULT(record_count, position->records_read);
+ const double filter_cmp_gain= position->range_rowid_filter_info
+ ? position->range_rowid_filter_info->get_cmp_gain(current_record_count)
+ : 0;
current_read_time=COST_ADD(read_time,
- COST_ADD(position->read_time,
+ COST_ADD(position->read_time -
+ filter_cmp_gain,
current_record_count /
(double) TIME_FOR_COMPARE));
+ if (unlikely(thd->trace_started()))
+ {
+ trace_one_table.add("rows_for_plan", current_record_count);
+ trace_one_table.add("cost_for_plan", current_read_time);
+ }
advance_sj_state(join, remaining_tables, idx, &current_record_count,
&current_read_time, &loose_scan_pos);
@@ -8951,6 +9581,7 @@ best_extension_by_limited_search(JOIN *join,
read_time,
current_read_time,
"prune_by_cost"););
+ trace_one_table.add("pruned_by_cost", true);
restore_prev_nj_state(s);
restore_prev_sj_state(remaining_tables, s, idx);
continue;
@@ -8984,6 +9615,7 @@ best_extension_by_limited_search(JOIN *join,
read_time,
current_read_time,
"pruned_by_heuristic"););
+ trace_one_table.add("pruned_by_heuristic", true);
restore_prev_nj_state(s);
restore_prev_sj_state(remaining_tables, s, idx);
continue;
@@ -8996,11 +9628,16 @@ best_extension_by_limited_search(JOIN *join,
remaining_tables &
~real_table_bit);
join->positions[idx].cond_selectivity= pushdown_cond_selectivity;
+
+ if (unlikely(thd->trace_started()) && pushdown_cond_selectivity < 1.0)
+ trace_one_table.add("selectivity", pushdown_cond_selectivity);
+
double partial_join_cardinality= current_record_count *
pushdown_cond_selectivity;
if ( (search_depth > 1) && (remaining_tables & ~real_table_bit) & allowed_tables )
{ /* Recursively expand the current partial plan */
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
+ Json_writer_array trace_rest(thd, "rest_of_plan");
if (best_extension_by_limited_search(join,
remaining_tables & ~real_table_bit,
idx + 1,
@@ -9020,12 +9657,17 @@ best_extension_by_limited_search(JOIN *join,
if (join->sort_by_table &&
join->sort_by_table !=
join->positions[join->const_tables].table->table)
+ {
/*
We may have to make a temp table, note that this is only a
heuristic since we cannot know for sure at this point.
Hence it may be wrong.
*/
+ trace_one_table.add("cost_for_sorting", current_record_count);
current_read_time= COST_ADD(current_read_time, current_record_count);
+ }
+ trace_one_table.add("estimated_join_cardinality",
+ partial_join_cardinality);
if (current_read_time < join->best_read)
{
memcpy((uchar*) join->best_positions, (uchar*) join->positions,
@@ -9648,6 +10290,7 @@ bool JOIN::inject_cond_into_where(Item *injected_cond)
static Item * const null_ptr= NULL;
+
/*
Set up join struct according to the picked join order in
@@ -9807,6 +10450,8 @@ bool JOIN::get_best_combination()
is_hash_join_key_no(j->ref.key))
hash_join= TRUE;
+ j->range_rowid_filter_info= best_positions[tablenr].range_rowid_filter_info;
+
loop_end:
/*
Save records_read in JOIN_TAB so that select_describe()/etc don't have
@@ -9847,6 +10492,9 @@ bool JOIN::get_best_combination()
top_join_tab_count= (uint)(join_tab_ranges.head()->end -
join_tab_ranges.head()->start);
+ if (unlikely(thd->trace_started()))
+ print_final_join_order(this);
+
update_depend_map(this);
DBUG_RETURN(0);
}
@@ -10696,23 +11344,40 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
there inside the triggers.
*/
{ // Check const tables
- join->exec_const_cond=
- make_cond_for_table(thd, cond,
+ Item* const_cond= NULL;
+ const_cond= make_cond_for_table(thd, cond,
join->const_table_map,
(table_map) 0, -1, FALSE, FALSE);
/* Add conditions added by add_not_null_conds(). */
for (uint i= 0 ; i < join->const_tables ; i++)
- add_cond_and_fix(thd, &join->exec_const_cond,
+ add_cond_and_fix(thd, &const_cond,
join->join_tab[i].select_cond);
- DBUG_EXECUTE("where",print_where(join->exec_const_cond,"constants",
+ DBUG_EXECUTE("where",print_where(const_cond,"constants",
QT_ORDINARY););
- if (join->exec_const_cond && !join->exec_const_cond->is_expensive() &&
- !join->exec_const_cond->val_int())
+
+ if (const_cond)
{
- DBUG_PRINT("info",("Found impossible WHERE condition"));
- join->exec_const_cond= NULL;
- DBUG_RETURN(1); // Impossible const condition
+ Json_writer_object trace_const_cond(thd);
+ trace_const_cond.add("condition_on_constant_tables", const_cond);
+ if (const_cond->is_expensive())
+ {
+ trace_const_cond.add("evalualted", "false")
+ .add("cause", "expensive cond");
+ }
+ else
+ {
+ const bool const_cond_result = const_cond->val_int() != 0;
+ if (!const_cond_result)
+ {
+ DBUG_PRINT("info",("Found impossible WHERE condition"));
+ trace_const_cond.add("evalualted", "true")
+ .add("found", "impossible where");
+ join->exec_const_cond= NULL;
+ DBUG_RETURN(1);
+ }
+ }
+ join->exec_const_cond= const_cond;
}
if (join->table_count != join->const_tables)
@@ -10749,6 +11414,11 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
/*
Step #2: Extract WHERE/ON parts
*/
+ Json_writer_object trace_wrapper(thd);
+ Json_writer_object trace_conditions(thd, "attaching_conditions_to_tables");
+ trace_conditions.add("original_condition", cond);
+ Json_writer_array trace_attached_comp(thd,
+ "attached_conditions_computation");
uint i;
for (i= join->top_join_tab_count - 1; i >= join->const_tables; i--)
{
@@ -10805,10 +11475,13 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
(!is_hash_join_key_no(tab->ref.key) &&
tab->table->intersect_keys.is_set(tab->ref.key))))
{
- /* Range uses longer key; Use this instead of ref on key */
- tab->type=JT_ALL;
- use_quick_range=1;
- tab->use_quick=1;
+ /* Range uses longer key; Use this instead of ref on key */
+ Json_writer_object ref_to_range(thd);
+ ref_to_range.add("ref_to_range", true);
+ ref_to_range.add("cause", "range uses longer key");
+ tab->type=JT_ALL;
+ use_quick_range=1;
+ tab->use_quick=1;
tab->ref.key= -1;
tab->ref.key_parts=0; // Don't use ref key.
join->best_positions[i].records_read= rows2double(tab->quick->records);
@@ -10888,7 +11561,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
below to check if we should use 'quick' instead.
*/
DBUG_PRINT("info", ("Item_int"));
- tmp= new (thd->mem_root) Item_int(thd, (longlong) 1, 1); // Always true
+ tmp= new (thd->mem_root) Item_bool(thd, true); // Always true
}
}
@@ -10967,6 +11640,11 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
sel->quick=tab->quick; // Use value from get_quick_...
sel->quick_keys.clear_all();
sel->needed_reg.clear_all();
+ if (is_hj && tab->rowid_filter)
+ {
+ delete tab->rowid_filter;
+ tab->rowid_filter= 0;
+ }
}
else
{
@@ -11026,7 +11704,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
Yet attributes of the just built condition are not needed.
Thus we call sel->cond->quick_fix_field for safety.
*/
- if (sel->cond && !sel->cond->fixed)
+ if (sel->cond && !sel->cond->is_fixed())
sel->cond->quick_fix_field();
if (sel->test_quick_select(thd, tab->keys,
@@ -11036,7 +11714,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
join->unit->select_limit_cnt), 0,
- FALSE, FALSE) < 0)
+ FALSE, FALSE, FALSE) < 0)
{
/*
Before reporting "Impossible WHERE" for the whole query
@@ -11050,7 +11728,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
join->unit->select_limit_cnt),0,
- FALSE, FALSE) < 0)
+ FALSE, FALSE, FALSE) < 0)
DBUG_RETURN(1); // Impossible WHERE
}
else
@@ -11292,6 +11970,23 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if (!tab->bush_children)
i++;
}
+
+ if (unlikely(thd->trace_started()))
+ {
+ trace_attached_comp.end();
+ Json_writer_array trace_attached_summary(thd,
+ "attached_conditions_summary");
+ for (tab= first_depth_first_tab(join); tab;
+ tab= next_depth_first_tab(join, tab))
+ {
+ if (!tab->table)
+ continue;
+ Item *const cond = tab->select_cond;
+ Json_writer_object trace_one_table(thd);
+ trace_one_table.add_table_name(tab);
+ trace_one_table.add("attached", cond);
+ }
+ }
}
DBUG_RETURN(0);
}
@@ -12675,6 +13370,37 @@ bool error_if_full_join(JOIN *join)
}
+void JOIN_TAB::build_range_rowid_filter_if_needed()
+{
+ if (rowid_filter && !is_rowid_filter_built)
+ {
+ /**
+ The same handler object (table->file) is used to build a filter
+ and to perfom a primary table access (by the main query).
+
+ To estimate the time for filter building tracker should be changed
+ and after building of the filter has been finished it should be
+ switched back to the previos tracker.
+ */
+ Exec_time_tracker *table_tracker= table->file->get_time_tracker();
+ Rowid_filter_tracker *rowid_tracker= rowid_filter->get_tracker();
+ table->file->set_time_tracker(rowid_tracker->get_time_tracker());
+ rowid_tracker->start_tracking();
+ if (!rowid_filter->build())
+ {
+ is_rowid_filter_built= true;
+ }
+ else
+ {
+ delete rowid_filter;
+ rowid_filter= 0;
+ }
+ rowid_tracker->stop_tracking();
+ table->file->set_time_tracker(table_tracker);
+ }
+}
+
+
/**
cleanup JOIN_TAB.
@@ -12694,6 +13420,11 @@ void JOIN_TAB::cleanup()
select= 0;
delete quick;
quick= 0;
+ if (rowid_filter)
+ {
+ delete rowid_filter;
+ rowid_filter= 0;
+ }
if (cache)
{
cache->free();
@@ -13066,7 +13797,8 @@ void JOIN::join_free()
!(select_options & SELECT_NO_UNLOCK) &&
!select_lex->subquery_in_having &&
(select_lex == (thd->lex->unit.fake_select_lex ?
- thd->lex->unit.fake_select_lex : &thd->lex->select_lex)))
+ thd->lex->unit.fake_select_lex :
+ thd->lex->first_select_lex())))
{
/*
TODO: unlock tables even if the join isn't top level select in the
@@ -13095,8 +13827,10 @@ void JOIN::join_free()
void JOIN::cleanup(bool full)
{
DBUG_ENTER("JOIN::cleanup");
- DBUG_PRINT("enter", ("full %u", (uint) full));
-
+ DBUG_PRINT("enter", ("select: %d (%p) join: %p full: %u",
+ select_lex->select_number, select_lex, this,
+ (uint) full));
+
if (full)
have_query_plan= QEP_DELETED;
@@ -13341,7 +14075,7 @@ static void update_depend_map_for_order(JOIN *join, ORDER *order)
order->used= 0;
// Not item_sum(), RAND() and no reference to table outside of sub select
if (!(order->depend_map & (OUTER_REF_TABLE_BIT | RAND_TABLE_BIT))
- && !order->item[0]->with_sum_func &&
+ && !order->item[0]->with_sum_func() &&
join->join_tab)
{
for (JOIN_TAB **tab=join->map2table;
@@ -13416,7 +14150,23 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
tab++)
tab->cached_eq_ref_table= FALSE;
- *simple_order= *join->join_tab[join->const_tables].on_expr_ref ? 0 : 1;
+ JOIN_TAB *head= join->join_tab + join->const_tables;
+ *simple_order= head->on_expr_ref[0] == NULL;
+ if (*simple_order && head->table->file->ha_table_flags() & HA_SLOW_RND_POS)
+ {
+ uint u1, u2, u3;
+ /*
+ normally the condition is (see filesort_use_addons())
+
+ length + sortlength <= max_length_for_sort_data
+
+ but for HA_SLOW_RND_POS tables we relax it a bit, as the alternative
+ is to use a temporary table, which is rather expensive.
+
+ TODO proper cost estimations
+ */
+ *simple_order= filesort_use_addons(head->table, 0, &u1, &u2, &u3);
+ }
}
else
{
@@ -13432,7 +14182,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
for (order=first_order; order ; order=order->next)
{
table_map order_tables=order->item[0]->used_tables();
- if (order->item[0]->with_sum_func ||
+ if (order->item[0]->with_sum_func() ||
order->item[0]->with_window_func ||
/*
If the outer table of an outer join is const (either by itself or
@@ -13609,7 +14359,7 @@ ORDER *simple_remove_const(ORDER *order, COND *where)
ORDER *first= NULL, *prev= NULL;
for (; order; order= order->next)
{
- DBUG_ASSERT(!order->item[0]->with_sum_func); // should never happen
+ DBUG_ASSERT(!order->item[0]->with_sum_func()); // should never happen
if (!const_expression_in_where(where, order->item[0]))
{
if (!first)
@@ -13907,31 +14657,44 @@ finish:
FALSE otherwise
*/
-static bool check_simple_equality(THD *thd, const Item::Context &ctx,
- Item *left_item, Item *right_item,
- COND_EQUAL *cond_equal)
+bool check_simple_equality(THD *thd, const Item::Context &ctx,
+ Item *left_item, Item *right_item,
+ COND_EQUAL *cond_equal)
{
Item *orig_left_item= left_item;
Item *orig_right_item= right_item;
- if (left_item->type() == Item::REF_ITEM &&
- ((Item_ref*)left_item)->ref_type() == Item_ref::VIEW_REF)
+ if (left_item->type() == Item::REF_ITEM)
{
- if (((Item_ref*)left_item)->get_depended_from())
- return FALSE;
- if (((Item_direct_view_ref*)left_item)->get_null_ref_table() !=
- NO_NULL_TABLE && !left_item->real_item()->used_tables())
- return FALSE;
- left_item= left_item->real_item();
+ Item_ref::Ref_Type left_ref= ((Item_ref*)left_item)->ref_type();
+
+ if (left_ref == Item_ref::VIEW_REF ||
+ left_ref == Item_ref::REF)
+ {
+ if (((Item_ref*)left_item)->get_depended_from())
+ return FALSE;
+ if (left_ref == Item_ref::VIEW_REF &&
+ ((Item_direct_view_ref*)left_item)->get_null_ref_table() !=
+ NO_NULL_TABLE &&
+ !left_item->real_item()->used_tables())
+ return FALSE;
+ left_item= left_item->real_item();
+ }
}
- if (right_item->type() == Item::REF_ITEM &&
- ((Item_ref*)right_item)->ref_type() == Item_ref::VIEW_REF)
+ if (right_item->type() == Item::REF_ITEM)
{
- if (((Item_ref*)right_item)->get_depended_from())
- return FALSE;
- if (((Item_direct_view_ref*)right_item)->get_null_ref_table() !=
- NO_NULL_TABLE && !right_item->real_item()->used_tables())
- return FALSE;
- right_item= right_item->real_item();
+ Item_ref::Ref_Type right_ref= ((Item_ref*)right_item)->ref_type();
+ if (right_ref == Item_ref::VIEW_REF ||
+ (right_ref == Item_ref::REF))
+ {
+ if (((Item_ref*)right_item)->get_depended_from())
+ return FALSE;
+ if (right_ref == Item_ref::VIEW_REF &&
+ ((Item_direct_view_ref*)right_item)->get_null_ref_table() !=
+ NO_NULL_TABLE &&
+ !right_item->real_item()->used_tables())
+ return FALSE;
+ right_item= right_item->real_item();
+ }
}
if (left_item->type() == Item::FIELD_ITEM &&
right_item->type() == Item::FIELD_ITEM &&
@@ -14368,7 +15131,7 @@ COND *Item_cond_and::build_equal_items(THD *thd,
if (!cond_args->elements &&
!cond_equal.current_level.elements &&
!eq_list.elements)
- return new (thd->mem_root) Item_int(thd, (longlong) 1, 1);
+ return new (thd->mem_root) Item_bool(thd, true);
List_iterator_fast<Item_equal> it(cond_equal.current_level);
while ((item_equal= it++))
@@ -14475,7 +15238,7 @@ COND *Item_func_eq::build_equal_items(THD *thd,
Item_equal *item_equal;
int n= cond_equal.current_level.elements + eq_list.elements;
if (n == 0)
- return new (thd->mem_root) Item_int(thd, (longlong) 1, 1);
+ return new (thd->mem_root) Item_bool(thd, true);
else if (n == 1)
{
if ((item_equal= cond_equal.current_level.pop()))
@@ -14869,7 +15632,7 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels,
List<Item> eq_list;
Item_func_eq *eq_item= 0;
if (((Item *) item_equal)->const_item() && !item_equal->val_int())
- return new (thd->mem_root) Item_int(thd, (longlong) 0, 1);
+ return new (thd->mem_root) Item_bool(thd, false);
Item *item_const= item_equal->get_const();
Item_equal_fields_iterator it(*item_equal);
Item *head;
@@ -14877,12 +15640,12 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels,
Item *current_sjm_head= NULL;
DBUG_ASSERT(!cond ||
- cond->type() == Item::INT_ITEM ||
+ cond->is_bool_literal() ||
(cond->type() == Item::FUNC_ITEM &&
((Item_func *) cond)->functype() == Item_func::EQ_FUNC) ||
(cond->type() == Item::COND_ITEM &&
((Item_func *) cond)->functype() == Item_func::COND_AND_FUNC));
-
+
/*
Pick the "head" item: the constant one or the first in the join order
(if the first in the join order happends to be inside an SJM nest, that's
@@ -14979,11 +15742,9 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels,
*/
Item *head_item= (!item_const && current_sjm &&
current_sjm_head != field_item) ? current_sjm_head: head;
- Item *head_real_item= head_item->real_item();
- if (head_real_item->type() == Item::FIELD_ITEM)
- head_item= head_real_item;
-
- eq_item= new (thd->mem_root) Item_func_eq(thd, field_item->real_item(), head_item);
+ eq_item= new (thd->mem_root) Item_func_eq(thd,
+ field_item->remove_item_direct_ref(),
+ head_item->remove_item_direct_ref());
if (!eq_item || eq_item->set_cmp_func())
return 0;
@@ -14998,13 +15759,13 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels,
cond AND eq_1 AND eq_2 AND eq_3 AND ...
- 'cond' is a parameter for this function, which may be NULL, an Item_int(1),
+ 'cond' is a parameter for this function, which may be NULL, an Item_bool(1),
or an Item_func_eq or an Item_cond_and.
We want to return a well-formed condition: no nested Item_cond_and objects,
or Item_cond_and with a single child:
- if 'cond' is an Item_cond_and, we add eq_i as its tail
- - if 'cond' is Item_int(1), we return eq_i
+ - if 'cond' is Item_bool(1), we return eq_i
- otherwise, we create our own Item_cond_and and put 'cond' at the front of
it.
- if we have only one condition to return, we don't create an Item_cond_and
@@ -15016,10 +15777,10 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels,
switch (eq_list.elements)
{
case 0:
- res= cond ? cond : new (thd->mem_root) Item_int(thd, (longlong) 1, 1);
+ res= cond ? cond : new (thd->mem_root) Item_bool(thd, true);
break;
case 1:
- if (!cond || cond->type() == Item::INT_ITEM)
+ if (!cond || cond->is_bool_literal())
res= eq_item;
break;
default:
@@ -15070,6 +15831,7 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels,
@param cond condition to process
@param cond_equal multiple equalities to take into consideration
@param table_join_idx index to tables determining field preference
+ @param do_substitution if false: do not do any field substitution
@note
At the first glance full sort of fields in multiple equality
@@ -15109,7 +15871,8 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels,
static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
COND *cond,
COND_EQUAL *cond_equal,
- void *table_join_idx)
+ void *table_join_idx,
+ bool do_substitution)
{
Item_equal *item_equal;
COND *org_cond= cond; // Return this in case of fatal error
@@ -15138,7 +15901,8 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
{
Item *new_item= substitute_for_best_equal_field(thd, context_tab,
item, cond_equal,
- table_join_idx);
+ table_join_idx,
+ do_substitution);
/*
This works OK with PS/SP re-execution as changes are made to
the arguments of AND/OR items only
@@ -15152,8 +15916,12 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
COND *eq_cond= 0;
List_iterator_fast<Item_equal> it(cond_equal->current_level);
bool false_eq_cond= FALSE;
+ bool all_deleted= true;
while ((item_equal= it++))
{
+ if (item_equal->get_extraction_flag() == DELETION_FL)
+ continue;
+ all_deleted= false;
eq_cond= eliminate_item_equal(thd, eq_cond, cond_equal->upper_levels,
item_equal);
if (!eq_cond)
@@ -15161,7 +15929,7 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
eq_cond= 0;
break;
}
- else if (eq_cond->type() == Item::INT_ITEM && !eq_cond->val_bool())
+ else if (eq_cond->is_bool_literal() && !eq_cond->val_bool())
{
/*
This occurs when eliminate_item_equal() founds that cond is
@@ -15186,13 +15954,13 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
else
{
/* Do not add an equality condition if it's always true */
- if (eq_cond->type() != Item::INT_ITEM &&
+ if (!eq_cond->is_bool_literal() &&
cond_list->push_front(eq_cond, thd->mem_root))
eq_cond= 0;
}
}
}
- if (!eq_cond)
+ if (!eq_cond && !all_deleted)
{
/*
We are out of memory doing the transformation.
@@ -15211,10 +15979,12 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
cond_equal= item_equal->upper_levels;
if (cond_equal && cond_equal->current_level.head() == item_equal)
cond_equal= cond_equal->upper_levels;
+ if (item_equal->get_extraction_flag() == DELETION_FL)
+ return 0;
cond= eliminate_item_equal(thd, 0, cond_equal, item_equal);
return cond ? cond : org_cond;
}
- else
+ else if (do_substitution)
{
while (cond_equal)
{
@@ -15265,7 +16035,7 @@ static void update_const_equal_items(THD *thd, COND *cond, JOIN_TAB *tab,
Item_func::COND_AND_FUNC));
}
else if (cond->type() == Item::FUNC_ITEM &&
- ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
+ ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
{
Item_equal *item_equal= (Item_equal *) cond;
bool contained_const= item_equal->get_const() != NULL;
@@ -15460,7 +16230,7 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list,
(((Item_func*) cond)->functype() == Item_func::EQ_FUNC ||
((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC))
{
- Item_func_eq *func=(Item_func_eq*) cond;
+ Item_bool_func2 *func= dynamic_cast<Item_bool_func2*>(cond);
Item **args= func->arguments();
bool left_const= args[0]->const_item() && !args[0]->is_expensive();
bool right_const= args[1]->const_item() && !args[1]->is_expensive();
@@ -15712,7 +16482,7 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top,
conds= and_conds(join->thd, conds, table->on_expr);
conds->top_level_item();
/* conds is always a new item as both cond and on_expr existed */
- DBUG_ASSERT(!conds->fixed);
+ DBUG_ASSERT(!conds->is_fixed());
conds->fix_fields(join->thd, &conds);
}
else
@@ -16185,6 +16955,8 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab,
double cost, rec_count;
table_map reopt_remaining_tables= last_remaining_tables;
uint i;
+ THD *thd= join->thd;
+ Json_writer_temp_disable trace_wo_join_buffering(thd);
if (first_tab > join->const_tables)
{
@@ -16219,7 +16991,7 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab,
{
JOIN_TAB *rs= join->positions[i].table;
POSITION pos, loose_scan_pos;
-
+
if ((i == first_tab && first_alt) || join->positions[i].use_join_buffer)
{
/* Find the best access method that would not use join buffering */
@@ -16283,12 +17055,24 @@ optimize_cond(JOIN *join, COND *conds,
that occurs in a function set a pointer to the multiple equality
predicate. Substitute a constant instead of this field if the
multiple equality contains a constant.
- */
+ */
+
+ Json_writer_object trace_wrapper(thd);
+ Json_writer_object trace_cond(thd, "condition_processing");
+ trace_cond.add("condition", join->conds == conds ? "WHERE" : "HAVING")
+ .add("original_condition", conds);
+
+ Json_writer_array trace_steps(thd, "steps");
DBUG_EXECUTE("where", print_where(conds, "original", QT_ORDINARY););
conds= build_equal_items(join, conds, NULL, join_list,
ignore_on_conds, cond_equal,
MY_TEST(flags & OPT_LINK_EQUAL_FIELDS));
DBUG_EXECUTE("where",print_where(conds,"after equal_items", QT_ORDINARY););
+ {
+ Json_writer_object equal_prop_wrapper(thd);
+ equal_prop_wrapper.add("transformation", "equality_propagation")
+ .add("resulting_condition", conds);
+ }
/* change field = field to field = const for each found field = const */
propagate_cond_constants(thd, (I_List<COND_CMP> *) 0, conds, conds);
@@ -16297,10 +17081,21 @@ optimize_cond(JOIN *join, COND *conds,
Remove all and-levels where CONST item != CONST item
*/
DBUG_EXECUTE("where",print_where(conds,"after const change", QT_ORDINARY););
+ {
+ Json_writer_object const_prop_wrapper(thd);
+ const_prop_wrapper.add("transformation", "constant_propagation")
+ .add("resulting_condition", conds);
+ }
conds= conds->remove_eq_conds(thd, cond_value, true);
if (conds && conds->type() == Item::COND_ITEM &&
((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
*cond_equal= &((Item_cond_and*) conds)->m_cond_equal;
+
+ {
+ Json_writer_object cond_removal_wrapper(thd);
+ cond_removal_wrapper.add("transformation", "trivial_condition_removal")
+ .add("resulting_condition", conds);
+ }
DBUG_EXECUTE("info",print_where(conds,"after remove", QT_ORDINARY););
}
DBUG_RETURN(conds);
@@ -16375,7 +17170,7 @@ void propagate_new_equalities(THD *thd, Item *cond,
}
}
else if (cond->type() == Item::FUNC_ITEM &&
- ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
+ ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
{
Item_equal *equal_item;
List_iterator<Item_equal> it(*new_equalities);
@@ -16620,7 +17415,7 @@ Item_cond::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
}
else if (and_level &&
new_item->type() == Item::FUNC_ITEM &&
- ((Item_cond*) new_item)->functype() ==
+ ((Item_func*) new_item)->functype() ==
Item_func::MULT_EQUAL_FUNC)
{
li.remove();
@@ -16762,7 +17557,8 @@ Item_bool_func2::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
{
if (args[0]->eq(args[1], true))
{
- if (!args[0]->maybe_null || functype() == Item_func::EQUAL_FUNC)
+ if (*cond_value == Item::COND_FALSE ||
+ !args[0]->maybe_null || functype() == Item_func::EQUAL_FUNC)
return (COND*) 0; // Compare of identical items
}
}
@@ -16800,9 +17596,8 @@ Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
{
Field *field= ((Item_field*) real_item)->field;
- if (((field->type() == MYSQL_TYPE_DATE) ||
- (field->type() == MYSQL_TYPE_DATETIME)) &&
- (field->flags & NOT_NULL_FLAG))
+ if ((field->flags & NOT_NULL_FLAG) &&
+ field->type_handler()->cond_notnull_field_isnull_to_field_eq_zero())
{
/* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */
/*
@@ -16818,7 +17613,7 @@ Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
*/
- Item *item0= new(thd->mem_root) Item_int(thd, (longlong) 0, 1);
+ Item *item0= new(thd->mem_root) Item_bool(thd, false);
Item *eq_cond= new(thd->mem_root) Item_func_eq(thd, args[0], item0);
if (!eq_cond)
return this;
@@ -16888,7 +17683,7 @@ Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
cond= new_cond;
/*
Item_func_eq can't be fixed after creation so we do not check
- cond->fixed, also it do not need tables so we use 0 as second
+ cond->is_fixed(), also it do not need tables so we use 0 as second
argument.
*/
cond->fix_fields(thd, &cond);
@@ -17048,60 +17843,6 @@ const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field,
Create internal temporary table
****************************************************************************/
-/**
- Create field for temporary table from given field.
-
- @param thd Thread handler
- @param org_field field from which new field will be created
- @param name New field name
- @param table Temporary table
- @param item !=NULL if item->result_field should point to new field.
- This is relevant for how fill_record() is going to work:
- If item != NULL then fill_record() will update
- the record in the original table.
- If item == NULL then fill_record() will update
- the temporary table
-
- @retval
- NULL on error
- @retval
- new_created field
-*/
-
-Field *create_tmp_field_from_field(THD *thd, Field *org_field,
- LEX_CSTRING *name, TABLE *table,
- Item_field *item)
-{
- Field *new_field;
-
- new_field= org_field->make_new_field(thd->mem_root, table,
- table == org_field->table);
- if (new_field)
- {
- new_field->init(table);
- new_field->orig_table= org_field->orig_table;
- if (item)
- item->result_field= new_field;
- else
- new_field->field_name= *name;
- new_field->flags|= org_field->flags & NO_DEFAULT_VALUE_FLAG;
- if (org_field->maybe_null() || (item && item->maybe_null))
- new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join
- if (org_field->type() == MYSQL_TYPE_VAR_STRING ||
- org_field->type() == MYSQL_TYPE_VARCHAR)
- 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->cond_selectivity= 1.0;
- new_field->next_equal_field= NULL;
- new_field->option_list= NULL;
- new_field->option_struct= NULL;
- }
- return new_field;
-}
-
-
Field *Item::create_tmp_field_int(TABLE *table, uint convert_int_length)
{
const Type_handler *h= &type_handler_long;
@@ -17111,6 +17852,26 @@ Field *Item::create_tmp_field_int(TABLE *table, uint convert_int_length)
*this, table);
}
+Field *Item::tmp_table_field_from_field_type_maybe_null(TABLE *table,
+ Tmp_field_src *src,
+ const Tmp_field_param *param,
+ bool is_explicit_null)
+{
+ /*
+ item->type() == CONST_ITEM excluded due to making fields for counter
+ With help of Item_uint
+ */
+ DBUG_ASSERT(!param->make_copy_field() || type() == CONST_ITEM);
+ DBUG_ASSERT(!is_result_field());
+ Field *result;
+ if ((result= tmp_table_field_from_field_type(table)))
+ {
+ if (result && is_explicit_null)
+ result->is_created_from_null_item= true;
+ }
+ return result;
+}
+
Field *Item_sum::create_tmp_field(bool group, TABLE *table)
{
@@ -17142,57 +17903,6 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table)
}
-static void create_tmp_field_from_item_finalize(THD *thd,
- Field *new_field,
- Item *item,
- Item ***copy_func,
- bool modify_item)
-{
- if (copy_func &&
- (item->is_result_field() ||
- (item->real_item()->is_result_field())))
- *((*copy_func)++) = item; // Save for copy_funcs
- if (modify_item)
- item->set_result_field(new_field);
- if (item->type() == Item::NULL_ITEM)
- new_field->is_created_from_null_item= TRUE;
-}
-
-
-/**
- Create field for temporary table using type of given item.
-
- @param thd Thread handler
- @param item Item to create a field for
- @param table Temporary table
- @param copy_func If set and item is a function, store copy of
- item in this array
- @param modify_item 1 if item->result_field should point to new
- item. This is relevent for how fill_record()
- is going to work:
- If modify_item is 1 then fill_record() will
- update the record in the original table.
- If modify_item is 0 then fill_record() will
- update the temporary table
-
- @retval
- 0 on error
- @retval
- new_created field
-*/
-
-static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
- Item ***copy_func, bool modify_item)
-{
- DBUG_ASSERT(thd == table->in_use);
- Field* new_field= item->create_tmp_field(false, table);
- if (new_field)
- create_tmp_field_from_item_finalize(thd, new_field, item,
- copy_func, modify_item);
- return new_field;
-}
-
-
/**
Create field for information schema table.
@@ -17226,19 +17936,199 @@ Field *Item::create_field_for_schema(THD *thd, TABLE *table)
/**
+ Create a temporary field for Item_field (or its descendant),
+ either direct or referenced by an Item_ref.
+*/
+Field *
+Item_field::create_tmp_field_from_item_field(TABLE *new_table,
+ Item_ref *orig_item,
+ const Tmp_field_param *param)
+{
+ DBUG_ASSERT(!is_result_field());
+ Field *result;
+ /*
+ If item have to be able to store NULLs but underlaid field can't do it,
+ create_tmp_field_from_field() can't be used for tmp field creation.
+ */
+ if (((maybe_null && in_rollup) ||
+ (new_table->in_use->create_tmp_table_for_derived && /* for mat. view/dt */
+ orig_item && orig_item->maybe_null)) &&
+ !field->maybe_null())
+ {
+ /*
+ The item the ref points to may have maybe_null flag set while
+ the ref doesn't have it. This may happen for outer fields
+ when the outer query decided at some point after name resolution phase
+ that this field might be null. Take this into account here.
+ */
+ Record_addr rec(orig_item ? orig_item->maybe_null : maybe_null);
+ const Type_handler *handler= type_handler()->
+ type_handler_for_tmp_table(this);
+ result= handler->make_and_init_table_field(orig_item ? &orig_item->name : &name,
+ rec, *this, new_table);
+ }
+ else if (param->table_cant_handle_bit_fields() &&
+ field->type() == MYSQL_TYPE_BIT)
+ {
+ const Type_handler *handler= type_handler_long_or_longlong();
+ result= handler->make_and_init_table_field(&name,
+ Record_addr(maybe_null),
+ *this, new_table);
+ }
+ else
+ {
+ LEX_CSTRING *tmp= orig_item ? &orig_item->name : &name;
+ bool tmp_maybe_null= param->modify_item() ? maybe_null :
+ field->maybe_null();
+ result= field->create_tmp_field(new_table->in_use->mem_root, new_table,
+ tmp_maybe_null);
+ if (result)
+ result->field_name= *tmp;
+ }
+ if (result && param->modify_item())
+ result_field= result;
+ return result;
+}
+
+
+Field *Item_field::create_tmp_field_ex(TABLE *table,
+ Tmp_field_src *src,
+ const Tmp_field_param *param)
+{
+ DBUG_ASSERT(!is_result_field());
+ Field *result;
+ src->set_field(field);
+ if (!(result= create_tmp_field_from_item_field(table, NULL, param)))
+ return NULL;
+ if (field->eq_def(result))
+ src->set_default_field(field);
+ return result;
+}
+
+
+Field *Item_default_value::create_tmp_field_ex(TABLE *table,
+ Tmp_field_src *src,
+ const Tmp_field_param *param)
+{
+ if (field->default_value && (field->flags & BLOB_FLAG))
+ {
+ /*
+ We have to use a copy function when using a blob with default value
+ as the we have to calculate the default value before we can use it.
+ */
+ get_tmp_field_src(src, param);
+ return tmp_table_field_from_field_type(table);
+ }
+ /*
+ Same code as in Item_field::create_tmp_field_ex, except no default field
+ handling
+ */
+ src->set_field(field);
+ return create_tmp_field_from_item_field(table, NULL, param);
+}
+
+
+Field *Item_ref::create_tmp_field_ex(TABLE *table,
+ Tmp_field_src *src,
+ const Tmp_field_param *param)
+{
+ Item *item= real_item();
+ DBUG_ASSERT(is_result_field());
+ if (item->type() == Item::FIELD_ITEM)
+ {
+ Field *result;
+ Item_field *field= (Item_field*) item;
+ Tmp_field_param prm2(*param);
+ prm2.set_modify_item(false);
+ src->set_field(field->field);
+ if (!(result= field->create_tmp_field_from_item_field(table, this, &prm2)))
+ return NULL;
+ if (param->modify_item())
+ result_field= result;
+ return result;
+ }
+ return Item_result_field::create_tmp_field_ex(table, src, param);
+}
+
+
+void Item_result_field::get_tmp_field_src(Tmp_field_src *src,
+ const Tmp_field_param *param)
+{
+ if (param->make_copy_field())
+ {
+ DBUG_ASSERT(result_field);
+ src->set_field(result_field);
+ }
+ else
+ {
+ src->set_item_result_field(this); // Save for copy_funcs
+ }
+}
+
+
+Field *Item_result_field::create_tmp_field_ex(TABLE *table,
+ Tmp_field_src *src,
+ const Tmp_field_param *param)
+{
+ /*
+ Possible Item types:
+ - Item_cache_wrapper (only for CREATE..SELECT ?)
+ - Item_func
+ - Item_subselect
+ */
+ DBUG_ASSERT(is_result_field());
+ DBUG_ASSERT(type() != NULL_ITEM);
+ get_tmp_field_src(src, param);
+ Field *result;
+ if ((result= tmp_table_field_from_field_type(table)) && param->modify_item())
+ result_field= result;
+ return result;
+}
+
+
+Field *Item_func_user_var::create_tmp_field_ex(TABLE *table,
+ Tmp_field_src *src,
+ const Tmp_field_param *param)
+{
+ DBUG_ASSERT(is_result_field());
+ DBUG_ASSERT(type() != NULL_ITEM);
+ get_tmp_field_src(src, param);
+ Field *result;
+ if ((result= create_table_field_from_handler(table)) && param->modify_item())
+ result_field= result;
+ return result;
+}
+
+
+Field *Item_func_sp::create_tmp_field_ex(TABLE *table,
+ Tmp_field_src *src,
+ const Tmp_field_param *param)
+{
+ Field *result;
+ get_tmp_field_src(src, param);
+ if ((result= sp_result_field->create_tmp_field(table->in_use->mem_root,
+ table)))
+ {
+ result->field_name= name;
+ if (param->modify_item())
+ result_field= result;
+ }
+ return result;
+}
+
+/**
Create field for temporary table.
- @param thd Thread handler
- @param table Temporary table
- @param item Item to create a field for
- @param type Type of item (normally item->type)
- @param copy_func If set and item is a function, store copy of item
+ @param table Temporary table
+ @param item Item to create a field for
+ @param type Type of item (normally item->type)
+ @param copy_func If set and item is a function, store copy of item
in this array
@param from_field if field will be created using other field as example,
pointer example field will be written here
- @param default_field If field has a default value field, store it here
- @param group 1 if we are going to do a relative group by on result
- @param modify_item 1 if item->result_field should point to new item.
+ @param default_field If field has a default value field, store it here
+ @param group 1 if we are going to do a relative group by on result
+ @param modify_item 1 if item->result_field should point to new item.
This is relevent for how fill_record() is going to
work:
If modify_item is 1 then fill_record() will update
@@ -17253,190 +18143,28 @@ Field *Item::create_field_for_schema(THD *thd, TABLE *table)
Set when using with rollup when we want to have
an exact copy of the field.
@retval
- 0 on error
+ 0 on error
@retval
new_created field
+ Create a temporary field for Item_field (or its descendant),
+ either direct or referenced by an Item_ref.
*/
-
-Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
+Field *create_tmp_field(TABLE *table, Item *item,
Item ***copy_func, Field **from_field,
Field **default_field,
bool group, bool modify_item,
bool table_cant_handle_bit_fields,
bool make_copy_field)
{
- Field *result;
- Item::Type orig_type= type;
- Item *orig_item= 0;
-
- DBUG_ASSERT(thd == table->in_use);
-
- if (type != Item::FIELD_ITEM &&
- item->real_item()->type() == Item::FIELD_ITEM)
- {
- orig_item= item;
- item= item->real_item();
- type= Item::FIELD_ITEM;
- }
-
- switch (type) {
- case Item::TYPE_HOLDER:
- case Item::SUM_FUNC_ITEM:
- {
- result= item->create_tmp_field(group, table);
- if (!result)
- my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
- return result;
- }
- case Item::DEFAULT_VALUE_ITEM:
- {
- Field *field= ((Item_default_value*) item)->field;
- if (field->default_value && (field->flags & BLOB_FLAG))
- {
- /*
- We have to use a copy function when using a blob with default value
- as the we have to calcuate the default value before we can use it.
- */
- return create_tmp_field_from_item(thd, item, table,
- (make_copy_field ? 0 : copy_func),
- modify_item);
- }
- }
- /* Fall through */
- case Item::FIELD_ITEM:
- case Item::CONTEXTUALLY_TYPED_VALUE_ITEM:
- case Item::INSERT_VALUE_ITEM:
- case Item::TRIGGER_FIELD_ITEM:
- {
- Item_field *field= (Item_field*) item;
- bool orig_modify= modify_item;
- if (orig_type == Item::REF_ITEM)
- modify_item= 0;
- /*
- If item have to be able to store NULLs but underlaid field can't do it,
- create_tmp_field_from_field() can't be used for tmp field creation.
- */
- if (((field->maybe_null && field->in_rollup) ||
- (thd->create_tmp_table_for_derived && /* for mat. view/dt */
- orig_item && orig_item->maybe_null)) &&
- !field->field->maybe_null())
- {
- bool save_maybe_null= FALSE;
- /*
- The item the ref points to may have maybe_null flag set while
- the ref doesn't have it. This may happen for outer fields
- when the outer query decided at some point after name resolution phase
- that this field might be null. Take this into account here.
- */
- if (orig_item)
- {
- save_maybe_null= item->maybe_null;
- item->maybe_null= orig_item->maybe_null;
- }
- result= create_tmp_field_from_item(thd, item, table, NULL,
- modify_item);
- *from_field= field->field;
- if (result && modify_item)
- field->result_field= result;
- if (orig_item)
- {
- item->maybe_null= save_maybe_null;
- result->field_name= orig_item->name;
- }
- }
- else if (table_cant_handle_bit_fields && field->field->type() ==
- MYSQL_TYPE_BIT)
- {
- const Type_handler *handler= item->type_handler_long_or_longlong();
- *from_field= field->field;
- if ((result=
- handler->make_and_init_table_field(&item->name,
- Record_addr(item->maybe_null),
- *item, table)))
- create_tmp_field_from_item_finalize(thd, result, item,
- copy_func, modify_item);
- if (result && modify_item)
- field->result_field= result;
- }
- else
- {
- LEX_CSTRING *tmp= orig_item ? &orig_item->name : &item->name;
- result= create_tmp_field_from_field(thd, (*from_field= field->field),
- tmp, table,
- modify_item ? field :
- NULL);
- }
-
- if (orig_type == Item::REF_ITEM && orig_modify)
- ((Item_ref*)orig_item)->set_result_field(result);
- /*
- Fields that are used as arguments to the DEFAULT() function already have
- their data pointers set to the default value during name resolution. See
- Item_default_value::fix_fields.
- */
- if (orig_type != Item::DEFAULT_VALUE_ITEM && field->field->eq_def(result))
- *default_field= field->field;
- return result;
- }
- /* Fall through */
- case Item::FUNC_ITEM:
- if (((Item_func *) item)->functype() == Item_func::FUNC_SP)
- {
- Item_func_sp *item_func_sp= (Item_func_sp *) item;
- Field *sp_result_field= item_func_sp->get_sp_result_field();
-
- if (make_copy_field)
- {
- DBUG_ASSERT(item_func_sp->result_field);
- *from_field= item_func_sp->result_field;
- }
- else
- {
- *((*copy_func)++)= item;
- }
- Field *result_field=
- create_tmp_field_from_field(thd,
- sp_result_field,
- &item_func_sp->name,
- table,
- NULL);
-
- if (modify_item)
- item->set_result_field(result_field);
-
- return result_field;
- }
-
- /* Fall through */
- case Item::COND_ITEM:
- case Item::SUBSELECT_ITEM:
- case Item::REF_ITEM:
- case Item::EXPR_CACHE_ITEM:
- if (make_copy_field)
- {
- DBUG_ASSERT(((Item_result_field*)item)->result_field);
- *from_field= ((Item_result_field*)item)->result_field;
- }
- /* Fall through */
- case Item::FIELD_AVG_ITEM:
- case Item::FIELD_STD_ITEM:
- case Item::PROC_ITEM:
- case Item::INT_ITEM:
- case Item::REAL_ITEM:
- case Item::DECIMAL_ITEM:
- case Item::STRING_ITEM:
- case Item::DATE_ITEM:
- case Item::NULL_ITEM:
- case Item::VARBIN_ITEM:
- case Item::CACHE_ITEM:
- case Item::WINDOW_FUNC_ITEM: // psergey-winfunc:
- case Item::PARAM_ITEM:
- return create_tmp_field_from_item(thd, item, table,
- (make_copy_field ? 0 : copy_func),
- modify_item);
- default: // Dosen't have to be stored
- return 0;
- }
+ Tmp_field_src src;
+ Tmp_field_param prm(group, modify_item, table_cant_handle_bit_fields,
+ make_copy_field);
+ Field *result= item->create_tmp_field_ex(table, &src, &prm);
+ *from_field= src.field();
+ *default_field= src.default_field();
+ if (src.item_result_field())
+ *((*copy_func)++)= src.item_result_field();
+ return result;
}
/*
@@ -17452,7 +18180,7 @@ setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps, uint field_count)
{
uint bitmap_size= bitmap_buffer_size(field_count);
- DBUG_ASSERT(table->s->virtual_fields == 0 && table->def_vcol_set == 0);
+ DBUG_ASSERT(table->s->virtual_fields == 0);
my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count,
FALSE);
@@ -17682,19 +18410,15 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
table->temp_pool_slot = temp_pool_slot;
table->copy_blobs= 1;
table->in_use= thd;
- table->quick_keys.init();
- table->covering_keys.init();
- table->intersect_keys.init();
- table->keys_in_use_for_query.init();
table->no_rows_with_nulls= param->force_not_null_cols;
+ table->update_handler= NULL;
+ table->check_unique_buf= NULL;
table->s= share;
init_tmp_table_share(thd, share, "", 0, "(temporary)", tmpname);
share->blob_field= blob_field;
share->table_charset= param->table_charset;
share->primary_key= MAX_KEY; // Indicate no primary key
- share->keys_for_keyread.init();
- share->keys_in_use.init();
if (param->schema_table)
share->db= INFORMATION_SCHEMA_NAME;
@@ -17717,7 +18441,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
}
if (not_all_columns)
{
- if (item->with_sum_func && type != Item::SUM_FUNC_ITEM)
+ if (item->with_sum_func() && type != Item::SUM_FUNC_ITEM)
{
if (item->used_tables() & OUTER_REF_TABLE_BIT)
item->update_used_tables();
@@ -17747,7 +18471,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
{
Item *tmp_item;
Field *new_field=
- create_tmp_field(thd, table, arg, arg->type(), &copy_func,
+ create_tmp_field(table, arg, &copy_func,
tmp_from_field, &default_field[fieldnr],
group != 0,not_all_columns,
distinct, false);
@@ -17797,7 +18521,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
else
{
/*
- The last parameter to create_tmp_field() is a bit tricky:
+ The last parameter to create_tmp_field_ex() is a bit tricky:
We need to set it to 0 in union, to get fill_record() to modify the
temporary table.
@@ -17811,7 +18535,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
*/
Field *new_field= (param->schema_table) ?
item->create_field_for_schema(thd, table) :
- create_tmp_field(thd, table, item, type, &copy_func,
+ create_tmp_field(table, item, &copy_func,
tmp_from_field, &default_field[fieldnr],
group != 0,
!force_copy_fields &&
@@ -17825,8 +18549,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
*/
item->marker == 4 || param->bit_fields_as_long,
force_copy_fields);
-
- if (unlikely(!new_field))
+ if (!new_field)
{
if (unlikely(thd->is_fatal_error))
goto err; // Got OOM
@@ -18423,12 +19146,10 @@ bool Virtual_tmp_table::add(List<Spvar_definition> &field_list)
while ((cdef= it++))
{
Field *tmp;
- if (!(tmp= cdef->make_field(s, in_use->mem_root, 0,
- (uchar*) (f_maybe_null(cdef->pack_flag) ? "" : 0),
- f_maybe_null(cdef->pack_flag) ? 1 : 0,
- &cdef->field_name)))
+ Record_addr addr(f_maybe_null(cdef->pack_flag));
+ if (!(tmp= cdef->make_field(s, in_use->mem_root, &addr, &cdef->field_name)))
DBUG_RETURN(true);
- add(tmp);
+ add(tmp);
}
DBUG_RETURN(false);
}
@@ -18548,7 +19269,7 @@ bool Virtual_tmp_table::sp_set_all_fields_from_item_list(THD *thd,
bool Virtual_tmp_table::sp_set_all_fields_from_item(THD *thd, Item *value)
{
- DBUG_ASSERT(value->fixed);
+ DBUG_ASSERT(value->is_fixed());
DBUG_ASSERT(value->cols() == s->fields);
for (uint i= 0; i < value->cols(); i++)
{
@@ -18972,7 +19693,7 @@ create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
We don't want this error to be converted to a warning, e.g. in case of
INSERT IGNORE ... SELECT.
*/
- table->file->print_error(error, MYF(ME_FATALERROR));
+ table->file->print_error(error, MYF(ME_FATAL));
DBUG_RETURN(1);
}
new_table= *table;
@@ -18995,7 +19716,7 @@ create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
new_table.no_rows= table->no_rows;
if (create_internal_tmp_table(&new_table, table->key_info, start_recinfo,
recinfo,
- thd->lex->select_lex.options |
+ thd->lex->first_select_lex()->options |
thd->variables.option_bits))
goto err2;
if (open_tmp_table(&new_table))
@@ -19788,6 +20509,8 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
if (!join_tab->preread_init_done && join_tab->preread_init())
DBUG_RETURN(NESTED_LOOP_ERROR);
+ join_tab->build_range_rowid_filter_if_needed();
+
join->return_tab= join_tab;
if (join_tab->last_inner)
@@ -20711,7 +21434,8 @@ test_if_quick_select(JOIN_TAB *tab)
int res= tab->select->test_quick_select(tab->join->thd, tab->keys,
(table_map) 0, HA_POS_ERROR, 0,
- FALSE, /*remove where parts*/FALSE);
+ FALSE, /*remove where parts*/FALSE,
+ FALSE);
if (tab->explain_plan && tab->explain_plan->range_checked_fer)
tab->explain_plan->range_checked_fer->collect_data(tab->select->quick);
@@ -20733,6 +21457,9 @@ int join_init_read_record(JOIN_TAB *tab)
*/
if (tab->distinct && tab->remove_duplicates()) // Remove duplicates.
return 1;
+
+ tab->build_range_rowid_filter_if_needed();
+
if (tab->filesort && tab->sort_table()) // Sort table.
return 1;
@@ -20750,6 +21477,8 @@ int join_init_read_record(JOIN_TAB *tab)
tab->join->thd->reset_killed(););
if (!tab->preread_init_done && tab->preread_init())
return 1;
+
+
if (init_read_record(&tab->read_record, tab->join->thd, tab->table,
tab->select, tab->filesort_result, 1,1, FALSE))
return 1;
@@ -20793,6 +21522,9 @@ JOIN_TAB::sort_table()
JOIN::ordered_index_order_by :
JOIN::ordered_index_group_by));
rc= create_sort_index(join->thd, join, this, NULL);
+ /* Disactivate rowid filter if it was used when creating sort index */
+ if (rowid_filter)
+ table->file->rowid_filter_is_active= false;
return (rc != 0);
}
@@ -22634,9 +23366,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
res= select->test_quick_select(tab->join->thd, new_ref_key_map, 0,
(tab->join->select_options &
OPTION_FOUND_ROWS) ?
- HA_POS_ERROR :
- tab->join->unit->select_limit_cnt,TRUE,
- TRUE, FALSE) <= 0;
+ HA_POS_ERROR :
+ tab->join->unit->select_limit_cnt,TRUE,
+ TRUE, FALSE, FALSE) <= 0;
if (res)
{
select->cond= save_cond;
@@ -22738,7 +23470,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
join->select_options & OPTION_FOUND_ROWS ?
HA_POS_ERROR :
join->unit->select_limit_cnt,
- TRUE, FALSE, FALSE);
+ TRUE, FALSE, FALSE, FALSE);
if (cond_saved)
select->cond= saved_cond;
@@ -22813,6 +23545,19 @@ check_reverse_order:
join_read_first:join_read_last;
tab->type=JT_NEXT; // Read with index_first(), index_next()
+ /*
+ Currently usage of rowid filters is not supported in InnoDB
+ if the table is accessed by the primary key
+ */
+ if (tab->rowid_filter &&
+ tab->index == table->s->primary_key &&
+ table->file->primary_key_is_clustered())
+ {
+ tab->range_rowid_filter_info= 0;
+ delete tab->rowid_filter;
+ tab->rowid_filter= 0;
+ }
+
if (tab->pre_idx_push_select_cond)
{
tab->set_cond(tab->pre_idx_push_select_cond);
@@ -22846,6 +23591,12 @@ check_reverse_order:
tab->use_quick=1;
tab->ref.key= -1;
tab->ref.key_parts=0; // Don't use ref key.
+ tab->range_rowid_filter_info= 0;
+ if (tab->rowid_filter)
+ {
+ delete tab->rowid_filter;
+ tab->rowid_filter= 0;
+ }
tab->read_first_record= join_init_read_record;
if (tab->is_using_loose_index_scan())
tab->join->tmp_table_param.precomputed_group_by= TRUE;
@@ -23235,7 +23986,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
if (unlikely(copy_blobs(first_field)))
{
my_message(ER_OUTOFMEMORY, ER_THD(thd,ER_OUTOFMEMORY),
- MYF(ME_FATALERROR));
+ MYF(ME_FATAL));
error=0;
goto err;
}
@@ -23510,12 +24261,7 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array,
uint counter;
enum_resolution_type resolution;
- /*
- Local SP variables may be int but are expressions, not positions.
- (And they can't be used before fix_fields is called for them).
- */
- if (order_item->type() == Item::INT_ITEM && order_item->basic_const_item() &&
- !from_window_spec)
+ if (order_item->is_order_clause_position() && !from_window_spec)
{ /* Order by position */
uint count;
if (order->counter_used)
@@ -23629,7 +24375,7 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array,
inspite of that fix_fields() calls find_item_in_list() one more
time.
- We check order_item->fixed because Item_func_group_concat can put
+ We check order_item->is_fixed() because Item_func_group_concat can put
arguments for which fix_fields already was called.
*/
if (order_item->fix_fields_if_needed_for_order_by(thd, order->item) ||
@@ -23689,19 +24435,21 @@ int setup_order(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
return 1;
}
+ if (!(*order->item)->with_sum_func())
+ continue;
+
/*
UNION queries cannot be used with an aggregate function in
an ORDER BY clause
*/
- if (for_union && (*order->item)->with_sum_func)
+ if (for_union)
{
my_error(ER_AGGREGATE_ORDER_FOR_UNION, MYF(0), number);
return 1;
}
- if (from_window_spec && (*order->item)->with_sum_func &&
- (*order->item)->type() != Item::SUM_FUNC_ITEM)
+ if (from_window_spec && (*order->item)->type() != Item::SUM_FUNC_ITEM)
(*order->item)->split_sum_func(thd, ref_pointer_array,
all_fields, SPLIT_SUM_SELECT);
}
@@ -23758,7 +24506,7 @@ setup_group(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
all_fields, true, true, from_window_spec))
return 1;
(*ord->item)->marker= UNDEF_POS; /* Mark found */
- if ((*ord->item)->with_sum_func && context_analysis_place == IN_GROUP_BY)
+ if ((*ord->item)->with_sum_func() && context_analysis_place == IN_GROUP_BY)
{
my_error(ER_WRONG_GROUP_FIELD, MYF(0), (*ord->item)->full_name());
return 1;
@@ -23771,7 +24519,7 @@ setup_group(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
my_error(ER_WINDOW_FUNCTION_IN_WINDOW_SPEC, MYF(0));
return 1;
}
- if (from_window_spec && (*ord->item)->with_sum_func &&
+ if (from_window_spec && (*ord->item)->with_sum_func() &&
(*ord->item)->type() != Item::SUM_FUNC_ITEM)
(*ord->item)->split_sum_func(thd, ref_pointer_array,
all_fields, SPLIT_SUM_SELECT);
@@ -23921,7 +24669,7 @@ create_distinct_group(THD *thd, Ref_ptr_array ref_pointer_array,
li.rewind();
while ((item=li++))
{
- if (!item->const_item() && !item->with_sum_func && !item->marker)
+ if (!item->const_item() && !item->with_sum_func() && !item->marker)
{
/*
Don't put duplicate columns from the SELECT list into the
@@ -24018,9 +24766,11 @@ count_field_types(SELECT_LEX *select_lex, TMP_TABLE_PARAM *param,
}
else
{
+ With_sum_func_cache *cache= field->get_with_sum_func_cache();
param->func_count++;
- if (reset_with_sum_func)
- field->with_sum_func=0;
+ // "field" can point to Item_std_field, so "cache" can be NULL here.
+ if (reset_with_sum_func && cache)
+ cache->reset_with_sum_func();
}
}
}
@@ -24160,7 +24910,7 @@ void calc_group_buffer(TMP_TABLE_PARAM *param, ORDER *group)
{
/*
Group strings are taken as varstrings and require an length field.
- A field is not yet created by create_tmp_field()
+ A field is not yet created by create_tmp_field_ex()
and the sizes should match up.
*/
key_length+= group_item->max_length + HA_KEY_BLOB_LENGTH;
@@ -24170,7 +24920,7 @@ void calc_group_buffer(TMP_TABLE_PARAM *param, ORDER *group)
default:
/* This case should never be choosen */
DBUG_ASSERT(0);
- my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL));
}
}
parts++;
@@ -24424,7 +25174,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
real_pos->real_type() == Item::SUBSELECT_ITEM ||
real_pos->type() == Item::CACHE_ITEM ||
real_pos->type() == Item::COND_ITEM) &&
- !real_pos->with_sum_func)
+ !real_pos->with_sum_func())
{ // Save for send fields
LEX_CSTRING real_name= pos->name;
pos= real_pos;
@@ -24435,7 +25185,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
on how the value is to be used: In some cases this may be an
argument in a group function, like: IF(ISNULL(col),0,COUNT(*))
*/
- if (!(pos=new (thd->mem_root) Item_copy_string(thd, pos)))
+ if (!(pos= pos->type_handler()->create_item_copy(thd, pos)))
goto err;
if (i < border) // HAVING, ORDER and GROUP BY
{
@@ -24490,8 +25240,8 @@ copy_fields(TMP_TABLE_PARAM *param)
(*ptr->do_copy)(ptr);
List_iterator_fast<Item> it(param->copy_funcs);
- Item_copy_string *item;
- while ((item = (Item_copy_string*) it++))
+ Item_copy *item;
+ while ((item= (Item_copy*) it++))
item->copy();
}
@@ -24633,7 +25383,7 @@ change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array,
for (uint i= 0; (item= it++); i++)
{
Field *field;
- if ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) ||
+ if ((item->with_sum_func() && item->type() != Item::SUM_FUNC_ITEM) ||
item->with_window_func)
item_field= item;
else if (item->type() == Item::FIELD_ITEM)
@@ -24942,7 +25692,7 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab)
}
if (unlikely(thd->is_fatal_error))
DBUG_RETURN(TRUE);
- if (!cond->fixed)
+ if (!cond->is_fixed())
{
Item *tmp_item= (Item*) cond;
cond->fix_fields(thd, &tmp_item);
@@ -25161,7 +25911,7 @@ bool JOIN::rollup_init()
Marking the expression item as 'with_sum_func' will ensure this.
*/
if (changed)
- item->with_sum_func= 1;
+ item->get_with_sum_func_cache()->set_with_sum_func();
}
}
return 0;
@@ -25503,11 +26253,13 @@ int print_explain_message_line(select_result_sink *result,
item_list.push_back(item_null, mem_root);
/* `rows` */
+ StringBuffer<64> rows_str;
if (rows)
{
- item_list.push_back(new (mem_root) Item_int(thd, *rows,
- MY_INT64_NUM_DECIMAL_DIGITS),
- mem_root);
+ rows_str.append_ulonglong((ulonglong)(*rows));
+ item_list.push_back(new (mem_root)
+ Item_string_sys(thd, rows_str.ptr(),
+ rows_str.length()), mem_root);
}
else
item_list.push_back(item_null, mem_root);
@@ -25607,7 +26359,7 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
filesort)))
return 1;
}
-
+ // psergey-todo: data for filtering!
tracker= &eta->tracker;
jbuf_tracker= &eta->jbuf_tracker;
@@ -25649,7 +26401,8 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
*/
if (real_table->merged_for_insert)
{
- TABLE_LIST *view_child= real_table->view->select_lex.table_list.first;
+ TABLE_LIST *view_child=
+ real_table->view->first_select_lex()->table_list.first;
for (;view_child; view_child= view_child->next_local)
{
if (view_child->table == table)
@@ -25707,6 +26460,22 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
// psergey-todo: ^ check for error return code
/* Build "key", "key_len", and "ref" */
+
+ if (rowid_filter)
+ {
+ Range_rowid_filter *range_filter= (Range_rowid_filter *) rowid_filter;
+ QUICK_SELECT_I *quick= range_filter->get_select()->quick;
+
+ Explain_rowid_filter *erf= new (thd->mem_root) Explain_rowid_filter;
+ erf->quick= quick->get_explain(thd->mem_root);
+ erf->selectivity= range_rowid_filter_info->selectivity;
+ erf->rows= quick->records;
+ if (!(erf->tracker= new Rowid_filter_tracker(thd->lex->analyze_stmt)))
+ return 1;
+ rowid_filter->set_tracker(erf->tracker);
+ eta->rowid_filter= erf;
+ }
+
if (tab_type == JT_NEXT)
{
key_info= table->key_info+index;
@@ -26111,8 +26880,9 @@ int JOIN::save_explain_data_intern(Explain_query *output,
{
JOIN *join= this; /* Legacy: this code used to be a non-member function */
DBUG_ENTER("JOIN::save_explain_data_intern");
- DBUG_PRINT("info", ("Select %p, type %s, message %s",
- join->select_lex, join->select_lex->type,
+ DBUG_PRINT("info", ("Select %p (%u), type %s, message %s",
+ join->select_lex, join->select_lex->select_number,
+ join->select_lex->type,
message ? message : "NULL"));
DBUG_ASSERT(have_query_plan == QEP_AVAILABLE);
/* fake_select_lex is created/printed by Explain_union */
@@ -26138,7 +26908,7 @@ int JOIN::save_explain_data_intern(Explain_query *output,
explain->select_id= join->select_lex->select_number;
explain->select_type= join->select_lex->type;
- explain->linkage= select_lex->linkage;
+ explain->linkage= select_lex->get_linkage();
explain->using_temporary= need_tmp;
explain->using_filesort= need_order_arg;
/* Setting explain->message means that all other members are invalid */
@@ -26161,7 +26931,7 @@ int JOIN::save_explain_data_intern(Explain_query *output,
explain->select_id= select_lex->select_number;
explain->select_type= select_lex->type;
- explain->linkage= select_lex->linkage;
+ explain->linkage= select_lex->get_linkage();
explain->using_temporary= need_tmp;
explain->using_filesort= need_order_arg;
explain->message= "Storage engine handles GROUP BY";
@@ -26184,8 +26954,8 @@ int JOIN::save_explain_data_intern(Explain_query *output,
join->select_lex->set_explain_type(true);
xpl_sel->select_id= join->select_lex->select_number;
xpl_sel->select_type= join->select_lex->type;
- xpl_sel->linkage= select_lex->linkage;
- xpl_sel->is_lateral= ((select_lex->linkage == DERIVED_TABLE_TYPE) &&
+ xpl_sel->linkage= select_lex->get_linkage();
+ xpl_sel->is_lateral= ((select_lex->get_linkage() == DERIVED_TABLE_TYPE) &&
(select_lex->uncacheable & UNCACHEABLE_DEPENDENT));
if (select_lex->master_unit()->derived)
xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
@@ -26267,24 +27037,33 @@ int JOIN::save_explain_data_intern(Explain_query *output,
output->add_node(xpl_sel);
}
- for (SELECT_LEX_UNIT *tmp_unit= join->select_lex->first_inner_unit();
- tmp_unit;
- tmp_unit= tmp_unit->next_unit())
+ /*
+ Don't try to add query plans for child selects if this select was pushed
+ down into a Smart Storage Engine:
+ - the entire statement was pushed down ("PUSHED SELECT"), or
+ - this derived table was pushed down ("PUSHED DERIVED")
+ */
+ if (!select_lex->pushdown_select && select_lex->type != pushed_derived_text)
{
- /*
- Display subqueries only if
- (1) they are not parts of ON clauses that were eliminated by table
- elimination.
- (2) they are not merged derived tables
- (3) they are not hanging CTEs (they are needed for execution)
- */
- if (!(tmp_unit->item && tmp_unit->item->eliminated) && // (1)
- (!tmp_unit->derived ||
- tmp_unit->derived->is_materialized_derived()) && // (2)
- !(tmp_unit->with_element &&
- (!tmp_unit->derived || !tmp_unit->derived->derived_result))) // (3)
- {
- explain->add_child(tmp_unit->first_select()->select_number);
+ for (SELECT_LEX_UNIT *tmp_unit= join->select_lex->first_inner_unit();
+ tmp_unit;
+ tmp_unit= tmp_unit->next_unit())
+ {
+ /*
+ Display subqueries only if
+ (1) they are not parts of ON clauses that were eliminated by table
+ elimination.
+ (2) they are not merged derived tables
+ (3) they are not hanging CTEs (they are needed for execution)
+ */
+ if (!(tmp_unit->item && tmp_unit->item->eliminated) && // (1)
+ (!tmp_unit->derived ||
+ tmp_unit->derived->is_materialized_derived()) && // (2)
+ !(tmp_unit->with_element &&
+ (!tmp_unit->derived || !tmp_unit->derived->derived_result))) // (3)
+ {
+ explain->add_child(tmp_unit->first_select()->select_number);
+ }
}
}
@@ -26317,7 +27096,16 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
THD *thd=join->thd;
select_result *result=join->result;
DBUG_ENTER("select_describe");
-
+
+ if (join->select_lex->pushdown_select)
+ {
+ /*
+ The whole statement was pushed down to a Smart Storage Engine. Do not
+ attempt to produce a query plan locally.
+ */
+ DBUG_VOID_RETURN;
+ }
+
/* Update the QPF with latest values of using_temporary, using_filesort */
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
unit;
@@ -26331,7 +27119,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
for such queries, we'll get here before having called
subquery_expr->fix_fields(), which will cause failure to
*/
- if (unit->item && !unit->item->fixed)
+ if (unit->item && !unit->item->is_fixed())
{
Item *ref= unit->item;
if (unit->item->fix_fields(thd, &ref))
@@ -26363,6 +27151,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
DBUG_ENTER("mysql_explain_union");
bool res= 0;
SELECT_LEX *first= unit->first_select();
+ bool is_pushed_union= unit->derived && unit->derived->pushdown_derived;
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
{
@@ -26380,7 +27169,10 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
}
if (!(res= unit->prepare(unit->derived, result,
SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
- res= unit->exec();
+ {
+ if (!is_pushed_union)
+ res= unit->exec();
+ }
}
else
{
@@ -26398,6 +27190,13 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
first->options | thd->variables.option_bits | SELECT_DESCRIBE,
result, unit, first);
}
+
+ if (unit->derived && unit->derived->pushdown_derived)
+ {
+ delete unit->derived->pushdown_derived;
+ unit->derived->pushdown_derived= NULL;
+ }
+
DBUG_RETURN(res || thd->is_error());
}
@@ -26655,7 +27454,8 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
// A view
if (!(belong_to_view &&
- belong_to_view->compact_view_format))
+ belong_to_view->compact_view_format) &&
+ !(query_type & QT_ITEM_IDENT_SKIP_DB_NAMES))
{
append_identifier(thd, str, &view_db);
str->append('.');
@@ -26684,7 +27484,8 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
// A normal table
if (!(belong_to_view &&
- belong_to_view->compact_view_format))
+ belong_to_view->compact_view_format) &&
+ !(query_type & QT_ITEM_IDENT_SKIP_DB_NAMES))
{
append_identifier(thd, str, &db);
str->append('.');
@@ -26771,6 +27572,18 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
{
str->append("/* select#");
str->append_ulonglong(select_number);
+ if (thd->lex->describe & DESCRIBE_EXTENDED2)
+ {
+ str->append("/");
+ str->append_ulonglong(nest_level);
+
+ if (master_unit()->fake_select_lex &&
+ master_unit()->first_select() == this)
+ {
+ str->append(" Filter Select: ");
+ master_unit()->fake_select_lex->print(thd, str, query_type);
+ }
+ }
str->append(" */ ");
}
@@ -26802,18 +27615,21 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN("sql_buffer_result "));
if (options & OPTION_FOUND_ROWS)
str->append(STRING_WITH_LEN("sql_calc_found_rows "));
- switch (sql_cache)
+ if (this == parent_lex->first_select_lex())
{
- case SQL_NO_CACHE:
- str->append(STRING_WITH_LEN("sql_no_cache "));
- break;
- case SQL_CACHE:
- str->append(STRING_WITH_LEN("sql_cache "));
- break;
- case SQL_CACHE_UNSPECIFIED:
- break;
- default:
- DBUG_ASSERT(0);
+ switch (parent_lex->sql_cache)
+ {
+ case LEX::SQL_NO_CACHE:
+ str->append(STRING_WITH_LEN("sql_no_cache "));
+ break;
+ case LEX::SQL_CACHE:
+ str->append(STRING_WITH_LEN("sql_cache "));
+ break;
+ case LEX::SQL_CACHE_UNSPECIFIED:
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
}
//Item List
@@ -27203,16 +28019,22 @@ void JOIN::cache_const_exprs()
/*
- Get a cost of reading rows_limit rows through index keynr.
+ Get the cost of using index keynr to read #LIMIT matching rows
@detail
- If there is a quick select, we try to use it.
- if there is a ref(const) access, we try to use it, too.
- quick and ref(const) use different cost formulas, so if both are possible
we should make a cost-based choice.
-
+
+ rows_limit is the number of rows we would need to read when using a full
+ index scan. This is generally higher than the N from "LIMIT N" clause,
+ because there's a WHERE condition (a part of which is used to construct a
+ range access we are considering using here)
+
@param tab JOIN_TAB with table access (is NULL for single-table
UPDATE/DELETE)
+ @param rows_limit See explanation above
@param read_time OUT Cost of reading using quick or ref(const) access.
@@ -27225,6 +28047,7 @@ void JOIN::cache_const_exprs()
static bool get_range_limit_read_cost(const JOIN_TAB *tab,
const TABLE *table,
+ ha_rows table_records,
uint keynr,
ha_rows rows_limit,
double *read_time)
@@ -27291,8 +28114,32 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab,
}
}
}
+
+ /*
+ Consider an example:
+
+ SELECT *
+ FROM t1
+ WHERE key1 BETWEEN 10 AND 20 AND col2='foo'
+ ORDER BY key1 LIMIT 10
+
+ If we were using a full index scan on key1, we would need to read this
+ many rows to get 10 matches:
+
+ 10 / selectivity(key1 BETWEEN 10 AND 20 AND col2='foo')
+
+ This is the number we get in rows_limit.
+ But we intend to use range access on key1. The rows returned by quick
+ select will satisfy the range part of the condition,
+ "key1 BETWEEN 10 and 20". We will still need to filter them with
+ the remainder condition, (col2='foo').
+
+ The selectivity of the range access is (best_rows/table_records). We need
+ to discount it from the rows_limit:
+ */
+ double rows_limit_for_quick= rows_limit * (best_rows / table_records);
- if (best_rows > rows_limit)
+ if (best_rows > rows_limit_for_quick)
{
/*
LIMIT clause specifies that we will need to read fewer records than
@@ -27301,7 +28148,7 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab,
only need 1/3rd of records, it will cost us 1/3rd of quick select's
read time)
*/
- best_cost *= rows_limit / best_rows;
+ best_cost *= rows_limit_for_quick / best_rows;
}
*read_time= best_cost;
res= true;
@@ -27373,6 +28220,12 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
bool group= join && join->group && order == join->group_list;
ha_rows refkey_rows_estimate= table->quick_condition_rows;
const bool has_limit= (select_limit_arg != HA_POS_ERROR);
+ THD* thd= join ? join->thd : table->in_use;
+
+ Json_writer_object trace_wrapper(thd);
+ Json_writer_object trace_cheaper_ordering(
+ thd, "reconsidering_access_paths_for_index_ordering");
+ trace_cheaper_ordering.add("clause", group ? "GROUP BY" : "ORDER BY");
/*
If not used with LIMIT, only use keys if the whole query can be
@@ -27402,16 +28255,21 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
uint tablenr= (uint)(tab - join->join_tab);
read_time= join->best_positions[tablenr].read_time;
for (uint i= tablenr+1; i < join->table_count; i++)
+ {
fanout*= join->best_positions[i].records_read; // fanout is always >= 1
+ // But selectivity is =< 1 :
+ fanout*= join->best_positions[i].cond_selectivity;
+ }
}
else
read_time= table->file->scan_time();
+ trace_cheaper_ordering.add("fanout", fanout);
/*
TODO: add cost of sorting here.
*/
read_time += COST_EPS;
-
+ trace_cheaper_ordering.add("read_time", read_time);
/*
Calculate the selectivity of the ref_key for REF_ACCESS. For
RANGE_ACCESS we use table->quick_condition_rows.
@@ -27436,11 +28294,20 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
set_if_bigger(refkey_rows_estimate, 1);
}
+ if (tab)
+ trace_cheaper_ordering.add_table_name(tab);
+ else
+ trace_cheaper_ordering.add_table_name(table);
+ trace_cheaper_ordering.add("rows_estimation", refkey_rows_estimate);
+
+ Json_writer_array possible_keys(thd,"possible_keys");
for (nr=0; nr < table->s->keys ; nr++)
{
int direction;
ha_rows select_limit= select_limit_arg;
uint used_key_parts= 0;
+ Json_writer_object possible_key(thd);
+ possible_key.add("index", table->key_info[nr].name);
if (keys.is_set(nr) &&
(direction= test_if_order_by_key(join, order, table, nr,
@@ -27453,6 +28320,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
*/
DBUG_ASSERT (ref_key != (int) nr);
+ possible_key.add("can_resolve_order", true);
bool is_covering= (table->covering_keys.is_set(nr) ||
(table->file->index_flags(nr, 0, 1) &
HA_CLUSTERED_INDEX));
@@ -27548,6 +28416,24 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
*/
select_limit= (ha_rows) (select_limit < fanout ?
1 : select_limit/fanout);
+
+ /*
+ refkey_rows_estimate is E(#rows) produced by the table access
+ strategy that was picked without regard to ORDER BY ... LIMIT.
+
+ It will be used as the source of selectivity data.
+ Use table->cond_selectivity as a better estimate which includes
+ condition selectivity too.
+ */
+ {
+ // we use MIN(...), because "Using LooseScan" queries have
+ // cond_selectivity=1 while refkey_rows_estimate has a better
+ // estimate.
+ refkey_rows_estimate= MY_MIN(refkey_rows_estimate,
+ ha_rows(table_records *
+ table->cond_selectivity));
+ }
+
/*
We assume that each of the tested indexes is not correlated
with ref_key. Thus, to select first N records we have to scan
@@ -27558,12 +28444,14 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
N/(refkey_rows_estimate/table_records) > table_records
<=> N > refkey_rows_estimate.
*/
+
if (select_limit > refkey_rows_estimate)
select_limit= table_records;
else
select_limit= (ha_rows) (select_limit *
(double) table_records /
refkey_rows_estimate);
+ possible_key.add("updated_limit", select_limit);
rec_per_key= keyinfo->actual_rec_per_key(keyinfo->user_defined_key_parts-1);
set_if_bigger(rec_per_key, 1);
/*
@@ -27580,12 +28468,14 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
index_scan_time= select_limit/rec_per_key *
MY_MIN(rec_per_key, table->file->scan_time());
double range_scan_time;
- if (get_range_limit_read_cost(tab, table, nr, select_limit,
- &range_scan_time))
+ if (get_range_limit_read_cost(tab, table, table_records, nr,
+ select_limit, &range_scan_time))
{
+ possible_key.add("range_scan_time", range_scan_time);
if (range_scan_time < index_scan_time)
index_scan_time= range_scan_time;
}
+ possible_key.add("index_scan_time", index_scan_time);
if ((ref_key < 0 && (group || table->force_index || is_covering)) ||
index_scan_time < read_time)
@@ -27596,17 +28486,29 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
table->covering_keys.is_set(ref_key)) ?
refkey_rows_estimate :
HA_POS_ERROR;
- if ((is_best_covering && !is_covering) ||
- (is_covering && refkey_select_limit < select_limit))
+ if (is_best_covering && !is_covering)
+ {
+ possible_key.add("chosen", false);
+ possible_key.add("cause", "covering index already found");
continue;
+ }
+
+ if (is_covering && refkey_select_limit < select_limit)
+ {
+ possible_key.add("chosen", false);
+ possible_key.add("cause", "ref estimates better");
+ continue;
+ }
if (table->quick_keys.is_set(nr))
quick_records= table->quick_rows[nr];
+ possible_key.add("records", quick_records);
if (best_key < 0 ||
(select_limit <= MY_MIN(quick_records,best_records) ?
keyinfo->user_defined_key_parts < best_key_parts :
quick_records < best_records) ||
(!is_best_covering && is_covering))
{
+ possible_key.add("chosen", true);
best_key= nr;
best_key_parts= keyinfo->user_defined_key_parts;
if (saved_best_key_parts)
@@ -27616,8 +28518,47 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
best_key_direction= direction;
best_select_limit= select_limit;
}
+ else
+ {
+ char const *cause;
+ possible_key.add("chosen", false);
+ if (is_covering)
+ cause= "covering index already found";
+ else
+ {
+ if (select_limit <= MY_MIN(quick_records,best_records))
+ cause= "keyparts greater than the current best keyparts";
+ else
+ cause= "rows estimation greater";
+ }
+ possible_key.add("cause", cause);
+ }
+ }
+ else
+ {
+ possible_key.add("usable", false);
+ possible_key.add("cause", "cost");
}
- }
+ }
+ else
+ {
+ possible_key.add("usable", false);
+ if (!group && select_limit == HA_POS_ERROR)
+ possible_key.add("cause", "order by without limit");
+ }
+ }
+ else
+ {
+ if (keys.is_set(nr))
+ {
+ possible_key.add("can_resolve_order", false);
+ possible_key.add("cause", "order can not be resolved by key");
+ }
+ else
+ {
+ possible_key.add("can_resolve_order", false);
+ possible_key.add("cause", "not usable index for the query");
+ }
}
}
@@ -28027,6 +28968,7 @@ Item *remove_pushed_top_conjuncts(THD *thd, Item *cond)
return cond;
}
+
/*
There are 5 cases in which we shortcut the join optimization process as we
conclude that the join would be a degenerate one
@@ -28040,7 +28982,6 @@ Item *remove_pushed_top_conjuncts(THD *thd, Item *cond)
*/
-
void JOIN::handle_implicit_grouping_with_window_funcs()
{
if (select_lex->have_window_funcs() && send_row_on_empty_set())
@@ -28050,6 +28991,7 @@ void JOIN::handle_implicit_grouping_with_window_funcs()
}
+
/*
@brief
Perform a partial cleanup for the JOIN_TAB structure
@@ -28084,5 +29026,45 @@ void JOIN_TAB::partial_cleanup()
/**
+ @brief
+ Look for provision of the select_handler interface by a foreign engine
+
+ @param thd The thread handler
+
+ @details
+ The function checks that this is an upper level select and if so looks
+ through its tables searching for one whose handlerton owns a
+ create_select call-back function. If the call of this function returns
+ a select_handler interface object then the server will push the select
+ query into this engine.
+ This is a responsibility of the create_select call-back function to
+ check whether the engine can execute the query.
+
+ @retval the found select_handler if the search is successful
+ 0 otherwise
+*/
+
+select_handler *SELECT_LEX::find_select_handler(THD *thd)
+{
+ if (next_select())
+ return 0;
+ if (master_unit()->outer_select())
+ return 0;
+ for (TABLE_LIST *tbl= join->tables_list; tbl; tbl= tbl->next_global)
+ {
+ if (!tbl->table)
+ continue;
+ handlerton *ht= tbl->table->file->partition_ht();
+ if (!ht->create_select)
+ continue;
+ select_handler *sh= ht->create_select(thd, this);
+ return sh;
+ }
+ return 0;
+}
+
+
+
+/**
@} (end of group Query_Optimizer)
*/
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 1efb2471793..8d65a6183a0 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -231,6 +231,10 @@ Next_select_func setup_end_select_func(JOIN *join, JOIN_TAB *tab);
int rr_sequential(READ_RECORD *info);
int rr_sequential_and_unpack(READ_RECORD *info);
Item *remove_pushed_top_conjuncts(THD *thd, Item *cond);
+Item *and_new_conditions_to_optimized_cond(THD *thd, Item *cond,
+ COND_EQUAL **cond_eq,
+ List<Item> &new_conds,
+ Item::cond_result *cond_value);
#include "sql_explain.h"
@@ -516,6 +520,18 @@ typedef struct st_join_table {
bool preread_init_done;
+ /*
+ Cost info to the range filter used when joining this join table
+ (Defined when the best join order has been already chosen)
+ */
+ Range_rowid_filter_cost_info *range_rowid_filter_info;
+ /* Rowid filter to be used when joining this join table */
+ Rowid_filter *rowid_filter;
+ /* Becomes true just after the used range filter has been built / filled */
+ bool is_rowid_filter_built;
+
+ void build_range_rowid_filter_if_needed();
+
void cleanup();
inline bool is_using_loose_index_scan()
{
@@ -688,8 +704,6 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
bool end_of_records);
-struct st_position;
-
class Semi_join_strategy_picker
{
public:
@@ -700,7 +714,7 @@ public:
Update internal state after another table has been added to the join
prefix
*/
- virtual void set_from_prev(struct st_position *prev) = 0;
+ virtual void set_from_prev(POSITION *prev) = 0;
virtual bool check_qep(JOIN *join,
uint idx,
@@ -710,7 +724,7 @@ public:
double *read_time,
table_map *handled_fanout,
sj_strategy_enum *strategy,
- struct st_position *loose_scan_pos) = 0;
+ POSITION *loose_scan_pos) = 0;
virtual void mark_used() = 0;
@@ -741,7 +755,7 @@ public:
first_dupsweedout_table= MAX_TABLES;
is_used= FALSE;
}
- void set_from_prev(struct st_position *prev);
+ void set_from_prev(POSITION *prev);
bool check_qep(JOIN *join,
uint idx,
@@ -751,7 +765,7 @@ public:
double *read_time,
table_map *handled_fanout,
sj_strategy_enum *stratey,
- struct st_position *loose_scan_pos);
+ POSITION *loose_scan_pos);
void mark_used() { is_used= TRUE; }
friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join);
@@ -787,7 +801,7 @@ public:
is_used= FALSE;
}
- void set_from_prev(struct st_position *prev);
+ void set_from_prev(POSITION *prev);
bool check_qep(JOIN *join,
uint idx,
table_map remaining_tables,
@@ -796,7 +810,7 @@ public:
double *read_time,
table_map *handled_fanout,
sj_strategy_enum *strategy,
- struct st_position *loose_scan_pos);
+ POSITION *loose_scan_pos);
void mark_used() { is_used= TRUE; }
friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join);
@@ -805,6 +819,7 @@ public:
class LooseScan_picker : public Semi_join_strategy_picker
{
+public:
/* The first (i.e. driving) table we're doing loose scan for */
uint first_loosescan_table;
/*
@@ -823,14 +838,13 @@ class LooseScan_picker : public Semi_join_strategy_picker
uint loosescan_parts; /* Number of keyparts to be kept distinct */
bool is_used;
-public:
void set_empty()
{
first_loosescan_table= MAX_TABLES;
is_used= FALSE;
}
- void set_from_prev(struct st_position *prev);
+ void set_from_prev(POSITION *prev);
bool check_qep(JOIN *join,
uint idx,
table_map remaining_tables,
@@ -839,19 +853,19 @@ public:
double *read_time,
table_map *handled_fanout,
sj_strategy_enum *strategy,
- struct st_position *loose_scan_pos);
+ POSITION *loose_scan_pos);
void mark_used() { is_used= TRUE; }
friend class Loose_scan_opt;
friend void best_access_path(JOIN *join,
JOIN_TAB *s,
table_map remaining_tables,
- const struct st_position *join_positions,
+ const POSITION *join_positions,
uint idx,
bool disable_jbuf,
double record_count,
- struct st_position *pos,
- struct st_position *loose_scan_pos);
+ POSITION *pos,
+ POSITION *loose_scan_pos);
friend bool get_best_combination(JOIN *join);
friend int setup_semijoin_loosescan(JOIN *join);
friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join);
@@ -878,7 +892,7 @@ public:
sjm_scan_last_inner= 0;
is_used= FALSE;
}
- void set_from_prev(struct st_position *prev);
+ void set_from_prev(POSITION *prev);
bool check_qep(JOIN *join,
uint idx,
table_map remaining_tables,
@@ -887,19 +901,24 @@ public:
double *read_time,
table_map *handled_fanout,
sj_strategy_enum *strategy,
- struct st_position *loose_scan_pos);
+ POSITION *loose_scan_pos);
void mark_used() { is_used= TRUE; }
friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join);
};
+class Range_rowid_filter_cost_info;
+class Rowid_filter;
+
+
/**
Information about a position of table within a join order. Used in join
optimization.
*/
-typedef struct st_position
+class POSITION
{
+public:
/* The table that's put into join order */
JOIN_TAB *table;
@@ -911,7 +930,7 @@ typedef struct st_position
double records_read;
/* The selectivity of the pushed down conditions */
- double cond_selectivity;
+ double cond_selectivity;
/*
Cost accessing the table in course of the entire complete join execution,
@@ -920,8 +939,6 @@ typedef struct st_position
*/
double read_time;
- /* Cumulative cost and record count for the join prefix */
- Cost_estimate prefix_cost;
double prefix_record_count;
/*
@@ -930,29 +947,46 @@ typedef struct st_position
*/
KEYUSE *key;
+ /* Info on splitting plan used at this position */
+ SplM_plan_info *spl_plan;
+
+ /* Cost info for the range filter used at this position */
+ Range_rowid_filter_cost_info *range_rowid_filter_info;
+
/* If ref-based access is used: bitmap of tables this table depends on */
table_map ref_depend_map;
-
+
/*
- TRUE <=> join buffering will be used. At the moment this is based on
- *very* imprecise guesses made in best_access_path().
+ Bitmap of semi-join inner tables that are in the join prefix and for
+ which there's no provision for how to eliminate semi-join duplicates
+ they produce.
*/
- bool use_join_buffer;
-
+ table_map dups_producing_tables;
+
+ table_map inner_tables_handled_with_other_sjs;
+
+ Duplicate_weedout_picker dups_weedout_picker;
+ Firstmatch_picker firstmatch_picker;
+ LooseScan_picker loosescan_picker;
+ Sj_materialization_picker sjmat_picker;
+
+ /* Cumulative cost and record count for the join prefix */
+ Cost_estimate prefix_cost;
+
/*
Current optimization state: Semi-join strategy to be used for this
and preceding join tables.
-
+
Join optimizer sets this for the *last* join_tab in the
- duplicate-generating range. That is, in order to interpret this field,
+ duplicate-generating range. That is, in order to interpret this field,
one needs to traverse join->[best_]positions array from right to left.
When you see a join table with sj_strategy!= SJ_OPT_NONE, some other
- field (depending on the strategy) tells how many preceding positions
+ field (depending on the strategy) tells how many preceding positions
this applies to. The values of covered_preceding_positions->sj_strategy
must be ignored.
*/
enum sj_strategy_enum sj_strategy;
-
+
/*
Valid only after fix_semijoin_strategies_for_picked_join_order() call:
if sj_strategy!=SJ_OPT_NONE, this is the number of subsequent tables that
@@ -961,22 +995,12 @@ typedef struct st_position
uint n_sj_tables;
/*
- Bitmap of semi-join inner tables that are in the join prefix and for
- which there's no provision for how to eliminate semi-join duplicates
- they produce.
+ TRUE <=> join buffering will be used. At the moment this is based on
+ *very* imprecise guesses made in best_access_path().
*/
- table_map dups_producing_tables;
-
- table_map inner_tables_handled_with_other_sjs;
-
- Duplicate_weedout_picker dups_weedout_picker;
- Firstmatch_picker firstmatch_picker;
- LooseScan_picker loosescan_picker;
- Sj_materialization_picker sjmat_picker;
-
- /* Info on splitting plan used at this position */
- SplM_plan_info *spl_plan;
-} POSITION;
+ bool use_join_buffer;
+ POSITION();
+};
typedef Bounds_checked_array<Item_null_result*> Item_null_array;
@@ -1487,6 +1511,11 @@ public:
Dynamic_array<KEYUSE_EXT> *ext_keyuses_for_splitting;
JOIN_TAB *sort_and_group_aggr_tab;
+ /*
+ Flag is set to true if select_lex was found to be degenerated before
+ the optimize_cond() call in JOIN::optimize_inner() method.
+ */
+ bool is_orig_degenerated;
JOIN(THD *thd_arg, List<Item> &fields_arg, ulonglong select_options_arg,
select_result *result_arg)
@@ -1567,6 +1596,7 @@ public:
fields_list= fields_arg;
non_agg_fields.empty();
bzero((char*) &keyuse,sizeof(keyuse));
+ having_value= Item::COND_UNDEF;
tmp_table_param.init();
tmp_table_param.end_write_records= HA_POS_ERROR;
rollup.state= ROLLUP::STATE_NONE;
@@ -1582,6 +1612,7 @@ public:
emb_sjm_nest= NULL;
sjm_lookup_tables= 0;
sjm_scan_tables= 0;
+ is_orig_degenerated= false;
}
/* True if the plan guarantees that it will be returned zero or one row */
@@ -1620,6 +1651,8 @@ public:
bool optimize_unflattened_subqueries();
bool optimize_constant_subqueries();
int init_join_caches();
+ bool make_range_rowid_filters();
+ bool init_range_rowid_filters();
bool make_sum_func_list(List<Item> &all_fields, List<Item> &send_fields,
bool before_group_by, bool recompute= FALSE);
@@ -1830,10 +1863,6 @@ bool setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
void copy_fields(TMP_TABLE_PARAM *param);
bool copy_funcs(Item **func_ptr, const THD *thd);
uint find_shortest_key(TABLE *table, const key_map *usable_keys);
-Field* create_tmp_field_from_field(THD *thd, Field* org_field,
- LEX_CSTRING *name, TABLE *table,
- Item_field *item);
-
bool is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args);
/* functions from opt_sum.cc */
@@ -2083,12 +2112,6 @@ bool mysql_select(THD *thd,
void free_underlaid_joins(THD *thd, SELECT_LEX *select);
bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit,
select_result *result);
-Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
- Item ***copy_func, Field **from_field,
- Field **def_field,
- bool group, bool modify_item,
- bool table_cant_handle_bit_fields,
- bool make_copy_field);
/*
General routine to change field->ptr of a NULL-terminated array of Field
@@ -2356,7 +2379,7 @@ Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
extern bool test_if_ref(Item *,
Item_field *left_item,Item *right_item);
-inline bool optimizer_flag(THD *thd, uint flag)
+inline bool optimizer_flag(THD *thd, ulonglong flag)
{
return (thd->variables.optimizer_switch & flag);
}
@@ -2462,9 +2485,53 @@ public:
~Pushdown_query() { delete handler; }
/* Function that calls the above scan functions */
- int execute(JOIN *join);
+ int execute(JOIN *);
+};
+
+class derived_handler;
+
+class Pushdown_derived: public Sql_alloc
+{
+private:
+ bool is_analyze;
+public:
+ TABLE_LIST *derived;
+ derived_handler *handler;
+
+ Pushdown_derived(TABLE_LIST *tbl, derived_handler *h);
+
+ ~Pushdown_derived();
+
+ int execute();
+};
+
+
+class select_handler;
+
+
+class Pushdown_select: public Sql_alloc
+{
+private:
+ bool is_analyze;
+ List<Item> result_columns;
+ bool send_result_set_metadata();
+ bool send_data();
+ bool send_eof();
+
+public:
+ SELECT_LEX *select;
+ select_handler *handler;
+
+ Pushdown_select(SELECT_LEX *sel, select_handler *h);
+
+ ~Pushdown_select();
+
+ bool init();
+
+ int execute();
};
+
bool test_if_order_compatible(SQL_I_List<ORDER> &a, SQL_I_List<ORDER> &b);
int test_if_group_changed(List<Cached_item> &list);
int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort);
@@ -2472,4 +2539,13 @@ int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort);
JOIN_TAB *first_explain_order_tab(JOIN* join);
JOIN_TAB *next_explain_order_tab(JOIN* join, JOIN_TAB* tab);
+bool check_simple_equality(THD *thd, const Item::Context &ctx,
+ Item *left_item, Item *right_item,
+ COND_EQUAL *cond_equal);
+
+void propagate_new_equalities(THD *thd, Item *cond,
+ List<Item_equal> *new_equalities,
+ COND_EQUAL *inherited,
+ bool *is_simplifiable_cond);
+
#endif /* SQL_SELECT_INCLUDED */
diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc
index 11125c0a619..83091cd67da 100644
--- a/sql/sql_sequence.cc
+++ b/sql/sql_sequence.cc
@@ -231,8 +231,8 @@ bool check_sequence_fields(LEX *lex, List<Create_field> *fields)
err:
my_error(ER_SEQUENCE_INVALID_TABLE_STRUCTURE, MYF(0),
- lex->select_lex.table_list.first->db.str,
- lex->select_lex.table_list.first->table_name.str, reason);
+ lex->first_select_lex()->table_list.first->db.str,
+ lex->first_select_lex()->table_list.first->table_name.str, reason);
DBUG_RETURN(TRUE);
}
@@ -730,8 +730,8 @@ longlong SEQUENCE::next_value(TABLE *table, bool second_round, int *error)
if (real_increment > 0)
{
- if (reserved_until + add_to > max_value ||
- reserved_until > max_value - add_to)
+ if (reserved_until > max_value - add_to ||
+ reserved_until + add_to > max_value)
{
reserved_until= max_value + 1;
out_of_values= res_value >= reserved_until;
diff --git a/sql/sql_sequence.h b/sql/sql_sequence.h
index 2d609d8591b..29c589e67cd 100644
--- a/sql/sql_sequence.h
+++ b/sql/sql_sequence.h
@@ -111,8 +111,8 @@ public:
{
if (real_increment > 0)
{
- if (value + real_increment > max_value ||
- value > max_value - real_increment)
+ if (value > max_value - real_increment ||
+ value + real_increment > max_value)
value= max_value + 1;
else
value+= real_increment;
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 2e02d418210..7b5b2c8bf89 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -63,6 +63,8 @@
#include "ha_partition.h"
#endif
#include "transaction.h"
+#include "opt_trace.h"
+#include "my_cpu.h"
enum enum_i_s_events_fields
{
@@ -1034,9 +1036,9 @@ find_files(THD *thd, Dynamic_array<LEX_CSTRING*> *files, LEX_CSTRING *db,
if (!(dirp = my_dir(path, MY_THREAD_SPECIFIC | (db ? 0 : MY_WANT_STAT))))
{
if (my_errno == ENOENT)
- my_error(ER_BAD_DB_ERROR, MYF(ME_BELL | ME_WAITTANG), db->str);
+ my_error(ER_BAD_DB_ERROR, MYF(0), db->str);
else
- my_error(ER_CANT_READ_DIR, MYF(ME_BELL | ME_WAITTANG), path, my_errno);
+ my_error(ER_CANT_READ_DIR, MYF(0), path, my_errno);
DBUG_RETURN(FIND_FILES_DIR);
}
@@ -1564,7 +1566,6 @@ void
mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
{
TABLE *table;
- MEM_ROOT *mem_root= thd->mem_root;
DBUG_ENTER("mysqld_list_fields");
DBUG_PRINT("enter",("table: %s", table_list->table_name.str));
@@ -1574,28 +1575,18 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
DBUG_VOID_RETURN;
table= table_list->table;
- List<Item> field_list;
+ List<Field> field_list;
Field **ptr,*field;
for (ptr=table->field ; (field= *ptr); ptr++)
{
if (!wild || !wild[0] ||
!wild_case_compare(system_charset_info, field->field_name.str,wild))
- {
- if (table_list->view)
- field_list.push_back(new (mem_root)
- Item_ident_for_show(thd, field,
- table_list->view_db.str,
- table_list->view_name.str),
- mem_root);
- else
- field_list.push_back(new (mem_root) Item_field(thd, field), mem_root);
- }
+ field_list.push_back(field);
}
restore_record(table, s->default_values); // Get empty record
table->use_all_columns();
- if (thd->protocol->send_result_set_metadata(&field_list,
- Protocol::SEND_DEFAULTS))
+ if (thd->protocol->send_list_fields(&field_list, table_list))
DBUG_VOID_RETURN;
my_eof(thd);
DBUG_VOID_RETURN;
@@ -2090,6 +2081,22 @@ end_options:
append_directory(thd, packet, "INDEX", create_info.index_file_name);
}
+static void append_period(THD *thd, String *packet, const LEX_CSTRING &start,
+ const LEX_CSTRING &end, const LEX_CSTRING &period,
+ bool ident)
+{
+ packet->append(STRING_WITH_LEN(",\n PERIOD FOR "));
+ if (ident)
+ append_identifier(thd, packet, period.str, period.length);
+ else
+ packet->append(period);
+ packet->append(STRING_WITH_LEN(" ("));
+ append_identifier(thd, packet, start.str, start.length);
+ packet->append(STRING_WITH_LEN(", "));
+ append_identifier(thd, packet, end.str, end.length);
+ packet->append(STRING_WITH_LEN(")"));
+}
+
/*
Build a CREATE TABLE statement for a table.
@@ -2128,6 +2135,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
KEY *key_info;
TABLE *table= table_list->table;
TABLE_SHARE *share= table->s;
+ TABLE_SHARE::period_info_t &period= share->period;
sql_mode_t sql_mode= thd->variables.sql_mode;
bool explicit_fields= false;
bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE |
@@ -2234,6 +2242,12 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
field->sql_type(type);
packet->append(type.ptr(), type.length(), system_charset_info);
+ DBUG_EXECUTE_IF("sql_type",
+ packet->append(" /* ");
+ packet->append(field->type_handler()->version().ptr());
+ packet->append(" */ ");
+ );
+
if (field->has_charset() && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)))
{
if (field->charset() != share->table_charset)
@@ -2342,7 +2356,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
}
- key_info= table->key_info;
+ key_info= table->s->key_info;
primary_key= share->primary_key;
for (uint i=0 ; i < share->keys ; i++,key_info++)
@@ -2397,7 +2411,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
}
}
packet->append(')');
- store_key_options(thd, packet, table, key_info);
+ store_key_options(thd, packet, table, &table->key_info[i]);
if (key_info->parser)
{
LEX_CSTRING *parser_name= plugin_name(key_info->parser);
@@ -2419,11 +2433,8 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
DBUG_ASSERT(!explicit_fields || fe->invisible < INVISIBLE_SYSTEM);
if (explicit_fields)
{
- packet->append(STRING_WITH_LEN(",\n PERIOD FOR SYSTEM_TIME ("));
- append_identifier(thd,packet,fs->field_name.str, fs->field_name.length);
- packet->append(STRING_WITH_LEN(", "));
- append_identifier(thd,packet,fe->field_name.str, fe->field_name.length);
- packet->append(STRING_WITH_LEN(")"));
+ append_period(thd, packet, fs->field_name, fe->field_name,
+ table->s->vers.name, false);
}
else
{
@@ -2432,6 +2443,15 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
}
}
+ if (period.name)
+ {
+ append_period(thd, packet,
+ period.start_field(share)->field_name,
+ period.end_field(share)->field_name,
+ period.name, true);
+ }
+
+
/*
Get possible foreign key definitions stored in InnoDB and append them
to the CREATE TABLE statement
@@ -2449,8 +2469,12 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
for (uint i= share->field_check_constraints;
i < share->table_check_constraints ; i++)
{
- StringBuffer<MAX_FIELD_WIDTH> str(&my_charset_utf8mb4_general_ci);
Virtual_column_info *check= table->check_constraints[i];
+ // period constraint is implicit
+ if (share->period.constr_name.streq(check->name))
+ continue;
+
+ StringBuffer<MAX_FIELD_WIDTH> str(&my_charset_utf8mb4_general_ci);
check->print(&str);
packet->append(STRING_WITH_LEN(",\n "));
@@ -2522,7 +2546,8 @@ static void store_key_options(THD *thd, String *packet, TABLE *table,
if (key_info->algorithm == HA_KEY_ALG_BTREE)
packet->append(STRING_WITH_LEN(" USING BTREE"));
- if (key_info->algorithm == HA_KEY_ALG_HASH)
+ if (key_info->algorithm == HA_KEY_ALG_HASH ||
+ key_info->algorithm == HA_KEY_ALG_LONG_HASH)
packet->append(STRING_WITH_LEN(" USING HASH"));
/* send USING only in non-default case: non-spatial rtree */
@@ -2791,13 +2816,111 @@ static const char *thread_state_info(THD *tmp)
}
+struct list_callback_arg
+{
+ list_callback_arg(const char *u, THD *t, ulong m):
+ user(u), thd(t), max_query_length(m) {}
+ I_List<thread_info> thread_infos;
+ const char *user;
+ THD *thd;
+ ulong max_query_length;
+};
+
+
+static my_bool list_callback(THD *tmp, list_callback_arg *arg)
+{
+
+ Security_context *tmp_sctx= tmp->security_ctx;
+ bool got_thd_data;
+ if ((tmp->vio_ok() || tmp->system_thread) &&
+ (!arg->user || (!tmp->system_thread &&
+ tmp_sctx->user && !strcmp(tmp_sctx->user, arg->user))))
+ {
+ thread_info *thd_info= new (arg->thd->mem_root) thread_info;
+
+ thd_info->thread_id=tmp->thread_id;
+ thd_info->os_thread_id=tmp->os_thread_id;
+ thd_info->user= arg->thd->strdup(tmp_sctx->user ? tmp_sctx->user :
+ (tmp->system_thread ?
+ "system user" : "unauthenticated user"));
+ if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) &&
+ arg->thd->security_ctx->host_or_ip[0])
+ {
+ if ((thd_info->host= (char*) arg->thd->alloc(LIST_PROCESS_HOST_LEN+1)))
+ my_snprintf((char *) thd_info->host, LIST_PROCESS_HOST_LEN,
+ "%s:%u", tmp_sctx->host_or_ip, tmp->peer_port);
+ }
+ else
+ thd_info->host= arg->thd->strdup(tmp_sctx->host_or_ip[0] ?
+ tmp_sctx->host_or_ip :
+ tmp_sctx->host ? tmp_sctx->host : "");
+ thd_info->command=(int) tmp->get_command();
+
+ if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data)))
+ {
+ /* This is an approximation */
+ thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ?
+ "Killed" : 0);
+
+ /* The following variables are only safe to access under a lock */
+ thd_info->db= 0;
+ if (tmp->db.str)
+ thd_info->db= arg->thd->strmake(tmp->db.str, tmp->db.length);
+
+ if (tmp->query())
+ {
+ uint length= MY_MIN(arg->max_query_length, tmp->query_length());
+ char *q= arg->thd->strmake(tmp->query(),length);
+ /* Safety: in case strmake failed, we set length to 0. */
+ thd_info->query_string=
+ CSET_STRING(q, q ? length : 0, tmp->query_charset());
+ }
+
+ /*
+ Progress report. We need to do this under a lock to ensure that all
+ is from the same stage.
+ */
+ if (tmp->progress.max_counter)
+ {
+ uint max_stage= MY_MAX(tmp->progress.max_stage, 1);
+ thd_info->progress= (((tmp->progress.stage / (double) max_stage) +
+ ((tmp->progress.counter /
+ (double) tmp->progress.max_counter) /
+ (double) max_stage)) *
+ 100.0);
+ set_if_smaller(thd_info->progress, 100);
+ }
+ else
+ thd_info->progress= 0.0;
+ }
+ else
+ {
+ thd_info->proc_info= "Busy";
+ thd_info->progress= 0.0;
+ thd_info->db= "";
+ }
+
+ thd_info->state_info= thread_state_info(tmp);
+ thd_info->start_time= tmp->start_utime;
+ ulonglong utime_after_query_snapshot= tmp->utime_after_query;
+ if (thd_info->start_time < utime_after_query_snapshot)
+ thd_info->start_time= utime_after_query_snapshot; // COM_SLEEP
+
+ if (got_thd_data)
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ arg->thread_infos.append(thd_info);
+ }
+ return 0;
+}
+
+
void mysqld_list_processes(THD *thd,const char *user, bool verbose)
{
Item *field;
List<Item> field_list;
- I_List<thread_info> thread_infos;
- ulong max_query_length= (verbose ? thd->variables.max_allowed_packet :
- PROCESS_LIST_WIDTH);
+ list_callback_arg arg(user, thd,
+ verbose ? thd->variables.max_allowed_packet :
+ PROCESS_LIST_WIDTH);
Protocol *protocol= thd->protocol;
MEM_ROOT *mem_root= thd->mem_root;
DBUG_ENTER("mysqld_list_processes");
@@ -2828,7 +2951,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
mem_root);
field->maybe_null=1;
field_list.push_back(field=new (mem_root)
- Item_empty_string(thd, "Info", max_query_length),
+ Item_empty_string(thd, "Info", arg.max_query_length),
mem_root);
field->maybe_null=1;
if (!thd->variables.old_mode &&
@@ -2847,102 +2970,13 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
if (thd->killed)
DBUG_VOID_RETURN;
- mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
- I_List_iterator<THD> it(threads);
- THD *tmp;
- while ((tmp=it++))
- {
- Security_context *tmp_sctx= tmp->security_ctx;
- bool got_thd_data;
- if ((tmp->vio_ok() || tmp->system_thread) &&
- (!user || (!tmp->system_thread &&
- tmp_sctx->user && !strcmp(tmp_sctx->user, user))))
- {
- thread_info *thd_info= new (thd->mem_root) thread_info;
-
- thd_info->thread_id=tmp->thread_id;
- thd_info->os_thread_id=tmp->os_thread_id;
- thd_info->user= thd->strdup(tmp_sctx->user ? tmp_sctx->user :
- (tmp->system_thread ?
- "system user" : "unauthenticated user"));
- if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) &&
- thd->security_ctx->host_or_ip[0])
- {
- if ((thd_info->host= (char*) thd->alloc(LIST_PROCESS_HOST_LEN+1)))
- my_snprintf((char *) thd_info->host, LIST_PROCESS_HOST_LEN,
- "%s:%u", tmp_sctx->host_or_ip, tmp->peer_port);
- }
- else
- thd_info->host= thd->strdup(tmp_sctx->host_or_ip[0] ?
- tmp_sctx->host_or_ip :
- tmp_sctx->host ? tmp_sctx->host : "");
- thd_info->command=(int) tmp->get_command();
-
- if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data)))
- {
- /* This is an approximation */
- thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ?
- "Killed" : 0);
- /*
- The following variables are only safe to access under a lock
- */
-
- thd_info->db= 0;
- if (tmp->db.str)
- thd_info->db= thd->strmake(tmp->db.str, tmp->db.length);
-
- if (tmp->query())
- {
- uint length= MY_MIN(max_query_length, tmp->query_length());
- char *q= thd->strmake(tmp->query(),length);
- /* Safety: in case strmake failed, we set length to 0. */
- thd_info->query_string=
- CSET_STRING(q, q ? length : 0, tmp->query_charset());
- }
-
- /*
- Progress report. We need to do this under a lock to ensure that all
- is from the same stage.
- */
- if (tmp->progress.max_counter)
- {
- uint max_stage= MY_MAX(tmp->progress.max_stage, 1);
- thd_info->progress= (((tmp->progress.stage / (double) max_stage) +
- ((tmp->progress.counter /
- (double) tmp->progress.max_counter) /
- (double) max_stage)) *
- 100.0);
- set_if_smaller(thd_info->progress, 100);
- }
- else
- thd_info->progress= 0.0;
- }
- else
- {
- thd_info->proc_info= "Busy";
- thd_info->progress= 0.0;
- thd_info->db= "";
- }
-
- thd_info->state_info= thread_state_info(tmp);
- thd_info->start_time= tmp->start_utime;
- ulonglong utime_after_query_snapshot= tmp->utime_after_query;
- if (thd_info->start_time < utime_after_query_snapshot)
- thd_info->start_time= utime_after_query_snapshot; // COM_SLEEP
-
- if (got_thd_data)
- mysql_mutex_unlock(&tmp->LOCK_thd_data);
- thread_infos.append(thd_info);
- }
- }
- mysql_mutex_unlock(&LOCK_thread_count);
+ server_threads.iterate(list_callback, &arg);
- thread_info *thd_info;
ulonglong now= microsecond_interval_timer();
char buff[20]; // For progress
String store_buffer(buff, sizeof(buff), system_charset_info);
- while ((thd_info=thread_infos.get()))
+ while (auto thd_info= arg.thread_infos.get())
{
protocol->prepare_for_resend();
protocol->store(thd_info->thread_id);
@@ -3222,160 +3256,155 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond)
}
DBUG_RETURN(bres);
}
- else
- {
- my_error(ER_NO_SUCH_THREAD, MYF(0), (ulong) thread_id);
- DBUG_RETURN(1);
- }
+ my_error(ER_NO_SUCH_THREAD, MYF(0), (ulong) thread_id);
+ DBUG_RETURN(1);
}
-int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
+struct processlist_callback_arg
{
- TABLE *table= tables->table;
- CHARSET_INFO *cs= system_charset_info;
- char *user;
- ulonglong unow= microsecond_interval_timer();
- DBUG_ENTER("fill_schema_processlist");
+ processlist_callback_arg(THD *thd_arg, TABLE *table_arg):
+ thd(thd_arg), table(table_arg), unow(microsecond_interval_timer()) {}
+ THD *thd;
+ TABLE *table;
+ ulonglong unow;
+};
- DEBUG_SYNC(thd,"fill_schema_processlist_after_unow");
- user= thd->security_ctx->master_access & PROCESS_ACL ?
- NullS : thd->security_ctx->priv_user;
+static my_bool processlist_callback(THD *tmp, processlist_callback_arg *arg)
+{
+ Security_context *tmp_sctx= tmp->security_ctx;
+ CHARSET_INFO *cs= system_charset_info;
+ const char *val;
+ ulonglong max_counter;
+ bool got_thd_data;
+ char *user= arg->thd->security_ctx->master_access & PROCESS_ACL ?
+ NullS : arg->thd->security_ctx->priv_user;
+
+ if ((!tmp->vio_ok() && !tmp->system_thread) ||
+ (user && (tmp->system_thread || !tmp_sctx->user ||
+ strcmp(tmp_sctx->user, user))))
+ return 0;
- mysql_mutex_lock(&LOCK_thread_count);
+ restore_record(arg->table, s->default_values);
+ /* ID */
+ arg->table->field[0]->store((longlong) tmp->thread_id, TRUE);
+ /* USER */
+ val= tmp_sctx->user ? tmp_sctx->user :
+ (tmp->system_thread ? "system user" : "unauthenticated user");
+ arg->table->field[1]->store(val, strlen(val), cs);
+ /* HOST */
+ if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) &&
+ arg->thd->security_ctx->host_or_ip[0])
+ {
+ char host[LIST_PROCESS_HOST_LEN + 1];
+ my_snprintf(host, LIST_PROCESS_HOST_LEN, "%s:%u",
+ tmp_sctx->host_or_ip, tmp->peer_port);
+ arg->table->field[2]->store(host, strlen(host), cs);
+ }
+ else
+ arg->table->field[2]->store(tmp_sctx->host_or_ip,
+ strlen(tmp_sctx->host_or_ip), cs);
- if (!thd->killed)
+ if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data)))
{
- I_List_iterator<THD> it(threads);
- THD* tmp;
-
- while ((tmp= it++))
+ /* DB */
+ if (tmp->db.str)
{
- Security_context *tmp_sctx= tmp->security_ctx;
- const char *val;
- ulonglong max_counter;
- bool got_thd_data;
-
- if ((!tmp->vio_ok() && !tmp->system_thread) ||
- (user && (tmp->system_thread || !tmp_sctx->user ||
- strcmp(tmp_sctx->user, user))))
- continue;
-
- restore_record(table, s->default_values);
- /* ID */
- table->field[0]->store((longlong) tmp->thread_id, TRUE);
- /* USER */
- val= tmp_sctx->user ? tmp_sctx->user :
- (tmp->system_thread ? "system user" : "unauthenticated user");
- table->field[1]->store(val, strlen(val), cs);
- /* HOST */
- if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) &&
- thd->security_ctx->host_or_ip[0])
- {
- char host[LIST_PROCESS_HOST_LEN + 1];
- my_snprintf(host, LIST_PROCESS_HOST_LEN, "%s:%u",
- tmp_sctx->host_or_ip, tmp->peer_port);
- table->field[2]->store(host, strlen(host), cs);
- }
- else
- table->field[2]->store(tmp_sctx->host_or_ip,
- strlen(tmp_sctx->host_or_ip), cs);
+ arg->table->field[3]->store(tmp->db.str, tmp->db.length, cs);
+ arg->table->field[3]->set_notnull();
+ }
+ }
- if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data)))
- {
- /* DB */
- if (tmp->db.str)
- {
- table->field[3]->store(tmp->db.str, tmp->db.length, cs);
- table->field[3]->set_notnull();
- }
- }
+ /* COMMAND */
+ if ((val= (char *) (!got_thd_data ? "Busy" :
+ (tmp->killed >= KILL_QUERY ?
+ "Killed" : 0))))
+ arg->table->field[4]->store(val, strlen(val), cs);
+ else
+ arg->table->field[4]->store(command_name[tmp->get_command()].str,
+ command_name[tmp->get_command()].length, cs);
- /* COMMAND */
- if ((val= (char *) (!got_thd_data ? "Busy" :
- (tmp->killed >= KILL_QUERY ?
- "Killed" : 0))))
- table->field[4]->store(val, strlen(val), cs);
- else
- table->field[4]->store(command_name[tmp->get_command()].str,
- command_name[tmp->get_command()].length, cs);
+ /* MYSQL_TIME */
+ ulonglong utime= tmp->start_utime;
+ ulonglong utime_after_query_snapshot= tmp->utime_after_query;
+ if (utime < utime_after_query_snapshot)
+ utime= utime_after_query_snapshot; // COM_SLEEP
+ utime= utime && utime < arg->unow ? arg->unow - utime : 0;
- /* MYSQL_TIME */
- ulonglong utime= tmp->start_utime;
- ulonglong utime_after_query_snapshot= tmp->utime_after_query;
- if (utime < utime_after_query_snapshot)
- utime= utime_after_query_snapshot; // COM_SLEEP
- utime= utime && utime < unow ? unow - utime : 0;
+ arg->table->field[5]->store(utime / HRTIME_RESOLUTION, TRUE);
- table->field[5]->store(utime / HRTIME_RESOLUTION, TRUE);
+ if (got_thd_data)
+ {
+ if (tmp->query())
+ {
+ arg->table->field[7]->store(tmp->query(),
+ MY_MIN(PROCESS_LIST_INFO_WIDTH,
+ tmp->query_length()), cs);
+ arg->table->field[7]->set_notnull();
- if (got_thd_data)
- {
- if (tmp->query())
- {
- table->field[7]->store(tmp->query(),
- MY_MIN(PROCESS_LIST_INFO_WIDTH,
- tmp->query_length()), cs);
- table->field[7]->set_notnull();
+ /* INFO_BINARY */
+ arg->table->field[16]->store(tmp->query(),
+ MY_MIN(PROCESS_LIST_INFO_WIDTH,
+ tmp->query_length()),
+ &my_charset_bin);
+ arg->table->field[16]->set_notnull();
+ }
- /* INFO_BINARY */
- table->field[16]->store(tmp->query(),
- MY_MIN(PROCESS_LIST_INFO_WIDTH,
- tmp->query_length()),
- &my_charset_bin);
- table->field[16]->set_notnull();
- }
+ /*
+ Progress report. We need to do this under a lock to ensure that all
+ is from the same stage.
+ */
+ if ((max_counter= tmp->progress.max_counter))
+ {
+ arg->table->field[9]->store((longlong) tmp->progress.stage + 1, 1);
+ arg->table->field[10]->store((longlong) tmp->progress.max_stage, 1);
+ arg->table->field[11]->store((double) tmp->progress.counter /
+ (double) max_counter*100.0);
+ }
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ }
- /*
- Progress report. We need to do this under a lock to ensure that all
- is from the same stage.
- */
- if ((max_counter= tmp->progress.max_counter))
- {
- table->field[9]->store((longlong) tmp->progress.stage + 1, 1);
- table->field[10]->store((longlong) tmp->progress.max_stage, 1);
- table->field[11]->store((double) tmp->progress.counter /
- (double) max_counter*100.0);
- }
- mysql_mutex_unlock(&tmp->LOCK_thd_data);
- }
+ /* STATE */
+ if ((val= thread_state_info(tmp)))
+ {
+ arg->table->field[6]->store(val, strlen(val), cs);
+ arg->table->field[6]->set_notnull();
+ }
- /* STATE */
- if ((val= thread_state_info(tmp)))
- {
- table->field[6]->store(val, strlen(val), cs);
- table->field[6]->set_notnull();
- }
+ /* TIME_MS */
+ arg->table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0)));
- /* TIME_MS */
- table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0)));
+ /*
+ This may become negative if we free a memory allocated by another
+ thread in this thread. However it's better that we notice it eventually
+ than hide it.
+ */
+ arg->table->field[12]->store((longlong) tmp->status_var.local_memory_used,
+ FALSE);
+ arg->table->field[13]->store((longlong) tmp->status_var.max_local_memory_used,
+ FALSE);
+ arg->table->field[14]->store((longlong) tmp->get_examined_row_count(), TRUE);
- /*
- This may become negative if we free a memory allocated by another
- thread in this thread. However it's better that we notice it eventually
- than hide it.
- */
- table->field[12]->store((longlong) tmp->status_var.local_memory_used,
- FALSE);
- table->field[13]->store((longlong) tmp->status_var.max_local_memory_used,
- FALSE);
- table->field[14]->store((longlong) tmp->get_examined_row_count(), TRUE);
+ /* QUERY_ID */
+ arg->table->field[15]->store(tmp->query_id, TRUE);
- /* QUERY_ID */
- table->field[15]->store(tmp->query_id, TRUE);
+ arg->table->field[17]->store(tmp->os_thread_id);
- table->field[17]->store(tmp->os_thread_id);
+ if (schema_table_store_record(arg->thd, arg->table))
+ return 1;
+ return 0;
+}
- if (schema_table_store_record(thd, table))
- {
- mysql_mutex_unlock(&LOCK_thread_count);
- DBUG_RETURN(1);
- }
- }
- }
- mysql_mutex_unlock(&LOCK_thread_count);
+int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
+{
+ processlist_callback_arg arg(thd, tables->table);
+ DBUG_ENTER("fill_schema_processlist");
+ DEBUG_SYNC(thd,"fill_schema_processlist_after_unow");
+ if (!thd->killed &&
+ server_threads.iterate(processlist_callback, &arg))
+ DBUG_RETURN(1);
DBUG_RETURN(0);
}
@@ -3437,7 +3466,7 @@ int add_status_vars(SHOW_VAR *list)
{
int res= 0;
if (status_vars_inited)
- mysql_mutex_lock(&LOCK_show_status);
+ mysql_rwlock_wrlock(&LOCK_all_status_vars);
if (!all_status_vars.buffer && // array is not allocated yet - do it now
my_init_dynamic_array(&all_status_vars, sizeof(SHOW_VAR), 250, 50, MYF(0)))
{
@@ -3452,7 +3481,7 @@ int add_status_vars(SHOW_VAR *list)
sort_dynamic(&all_status_vars, show_var_cmp);
err:
if (status_vars_inited)
- mysql_mutex_unlock(&LOCK_show_status);
+ mysql_rwlock_unlock(&LOCK_all_status_vars);
return res;
}
@@ -3514,7 +3543,7 @@ void remove_status_vars(SHOW_VAR *list)
{
if (status_vars_inited)
{
- mysql_mutex_lock(&LOCK_show_status);
+ mysql_rwlock_wrlock(&LOCK_all_status_vars);
SHOW_VAR *all= dynamic_element(&all_status_vars, 0, SHOW_VAR *);
for (; list->name; list++)
@@ -3535,7 +3564,7 @@ void remove_status_vars(SHOW_VAR *list)
}
}
shrink_var_array(&all_status_vars);
- mysql_mutex_unlock(&LOCK_show_status);
+ mysql_rwlock_unlock(&LOCK_all_status_vars);
}
else
{
@@ -3672,6 +3701,11 @@ const char* get_one_variable(THD *thd,
end= pos + ls->length;
break;
}
+ case SHOW_ATOMIC_COUNTER_UINT32_T:
+ end= int10_to_str(
+ static_cast<long>(*static_cast<Atomic_counter<uint32_t>*>(value)),
+ buff, 10);
+ break;
case SHOW_UNDEF:
break; // Return empty string
case SHOW_SYS: // Cannot happen
@@ -3832,34 +3866,37 @@ end:
Return number of threads used
*/
-uint calc_sum_of_all_status(STATUS_VAR *to)
+struct calc_sum_callback_arg
{
- uint count= 0;
- DBUG_ENTER("calc_sum_of_all_status");
+ calc_sum_callback_arg(STATUS_VAR *to_arg): to(to_arg), count(0) {}
+ STATUS_VAR *to;
+ uint count;
+};
- /* Ensure that thread id not killed during loop */
- mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
- I_List_iterator<THD> it(threads);
- THD *tmp;
+static my_bool calc_sum_callback(THD *thd, calc_sum_callback_arg *arg)
+{
+ arg->count++;
+ if (!thd->status_in_global)
+ {
+ add_to_status(arg->to, &thd->status_var);
+ arg->to->local_memory_used+= thd->status_var.local_memory_used;
+ }
+ if (thd->get_command() != COM_SLEEP)
+ arg->to->threads_running++;
+ return 0;
+}
- to->local_memory_used= 0;
+uint calc_sum_of_all_status(STATUS_VAR *to)
+{
+ calc_sum_callback_arg arg(to);
+ DBUG_ENTER("calc_sum_of_all_status");
+
+ to->local_memory_used= 0;
/* Add to this status from existing threads */
- while ((tmp= it++))
- {
- count++;
- if (!tmp->status_in_global)
- {
- add_to_status(to, &tmp->status_var);
- to->local_memory_used+= tmp->status_var.local_memory_used;
- }
- if (tmp->get_command() != COM_SLEEP)
- to->threads_running++;
- }
-
- mysql_mutex_unlock(&LOCK_thread_count);
- DBUG_RETURN(count);
+ server_threads.iterate(calc_sum_callback, &arg);
+ DBUG_RETURN(arg.count);
}
@@ -4209,8 +4246,9 @@ bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables,
case SQLCOM_SHOW_TABLE_STATUS:
case SQLCOM_SHOW_TRIGGERS:
case SQLCOM_SHOW_EVENTS:
- thd->make_lex_string(&lookup_field_values->db_value,
- lex->select_lex.db.str, lex->select_lex.db.length);
+ thd->make_lex_string(&lookup_field_values->db_value,
+ lex->first_select_lex()->db.str,
+ lex->first_select_lex()->db.length);
if (wild)
{
thd->make_lex_string(&lookup_field_values->table_value,
@@ -4602,10 +4640,10 @@ fill_schema_table_by_open(THD *thd, MEM_ROOT *mem_root,
temporary LEX. The latter is required to correctly open views and
produce table describing their structure.
*/
- if (make_table_list(thd, &lex->select_lex, &db_name, &table_name))
+ if (make_table_list(thd, lex->first_select_lex(), &db_name, &table_name))
goto end;
- table_list= lex->select_lex.table_list.first;
+ table_list= lex->first_select_lex()->table_list.first;
if (is_show_fields_or_keys)
{
@@ -5673,7 +5711,9 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
table->field[16]->store_time(&time);
table->field[16]->set_notnull();
}
- if (file->ha_table_flags() & (HA_HAS_OLD_CHECKSUM | HA_HAS_NEW_CHECKSUM))
+ if ((file->ha_table_flags() &
+ (HA_HAS_OLD_CHECKSUM | HA_HAS_NEW_CHECKSUM)) &&
+ !file->stats.checksum_null)
{
table->field[18]->store((longlong) file->stats.checksum, TRUE);
table->field[18]->set_notnull();
@@ -6426,7 +6466,6 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
const char *wild, bool full_access, const char *sp_user)
{
- MYSQL_TIME time;
LEX *lex= thd->lex;
CHARSET_INFO *cs= system_charset_info;
const Sp_handler *sph;
@@ -6514,14 +6553,11 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
copy_field_as_string(table->field[22],
proc_table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]);
- bzero((char *)&time, sizeof(time));
- ((Field_timestamp *) proc_table->field[MYSQL_PROC_FIELD_CREATED])->
- get_time(&time);
- table->field[23]->store_time(&time);
- bzero((char *)&time, sizeof(time));
- ((Field_timestamp *) proc_table->field[MYSQL_PROC_FIELD_MODIFIED])->
- get_time(&time);
- table->field[24]->store_time(&time);
+ proc_table->field[MYSQL_PROC_FIELD_CREATED]->
+ save_in_field(table->field[23]);
+ proc_table->field[MYSQL_PROC_FIELD_MODIFIED]->
+ save_in_field(table->field[24]);
+
copy_field_as_string(table->field[25],
proc_table->field[MYSQL_PROC_FIELD_SQL_MODE]);
copy_field_as_string(table->field[26],
@@ -6650,6 +6686,7 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
(void) read_statistics_for_tables(thd, tables);
show_table->file->info(HA_STATUS_VARIABLE |
HA_STATUS_NO_LOCK |
+ HA_STATUS_CONST |
HA_STATUS_TIME);
set_statistics_for_table(thd, show_table);
}
@@ -6684,16 +6721,26 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
"D" : "A"), 1, cs);
table->field[8]->set_notnull();
}
- KEY *key=show_table->key_info+i;
- if (key->rec_per_key[j])
+ if (key_info->algorithm == HA_KEY_ALG_LONG_HASH)
+ table->field[13]->store(STRING_WITH_LEN("HASH"), cs);
+ else
{
- ha_rows records= (ha_rows) ((double) show_table->stat_records() /
- key->actual_rec_per_key(j));
- table->field[9]->store((longlong) records, TRUE);
- table->field[9]->set_notnull();
+ /*
+ We have to use table key information to get the key statistics
+ from table as key_info points to TABLE_SHARE which has no
+ statistics.
+ */
+ KEY *key_info= show_table->key_info + i;
+ if (key_info->rec_per_key[j])
+ {
+ ha_rows records= (ha_rows) ((double) show_table->stat_records() /
+ key_info->actual_rec_per_key(j));
+ table->field[9]->store((longlong) records, TRUE);
+ table->field[9]->set_notnull();
+ }
+ const char *tmp= show_table->file->index_type(i);
+ table->field[13]->store(tmp, strlen(tmp), cs);
}
- const char *tmp= show_table->file->index_type(i);
- table->field[13]->store(tmp, strlen(tmp), cs);
}
if (!(key_info->flags & HA_FULLTEXT) &&
(key_part->field &&
@@ -6815,7 +6862,7 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
& 'field_translation_end' are uninitialized is this
case.
*/
- List<Item> *fields= &tables->view->select_lex.item_list;
+ List<Item> *fields= &tables->view->first_select_lex()->item_list;
List_iterator<Item> it(*fields);
Item *item;
Item_field *field;
@@ -6956,7 +7003,7 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
{
List<FOREIGN_KEY_INFO> f_key_list;
TABLE *show_table= tables->table;
- KEY *key_info=show_table->key_info;
+ KEY *key_info=show_table->s->key_info;
uint primary_key= show_table->s->primary_key;
show_table->file->info(HA_STATUS_VARIABLE |
HA_STATUS_NO_LOCK |
@@ -7049,8 +7096,7 @@ static bool store_trigger(THD *thd, Trigger *trigger,
(my_time_t)(trigger->create_time/100));
/* timestamp is with 6 digits */
timestamp.second_part= (trigger->create_time % 100) * 10000;
- ((Field_temporal_with_date*) table->field[16])->store_time_dec(&timestamp,
- 2);
+ table->field[16]->store_time_dec(&timestamp, 2);
}
sql_mode_string_representation(thd, trigger->sql_mode, &sql_mode_rep);
@@ -7154,7 +7200,7 @@ static int get_schema_key_column_usage_record(THD *thd,
{
List<FOREIGN_KEY_INFO> f_key_list;
TABLE *show_table= tables->table;
- KEY *key_info=show_table->key_info;
+ KEY *key_info=show_table->s->key_info;
uint primary_key= show_table->s->primary_key;
show_table->file->info(HA_STATUS_VARIABLE |
HA_STATUS_NO_LOCK |
@@ -7476,7 +7522,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
break;
default:
DBUG_ASSERT(0);
- my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL));
DBUG_RETURN(1);
}
table->field[7]->set_notnull();
@@ -7602,7 +7648,8 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
}
else if (part_info->vers_info->interval.is_set())
{
- table->field[11]->store_timestamp((my_time_t)part_elem->range_value, 0);
+ Timeval tv((my_time_t) part_elem->range_value, 0);
+ table->field[11]->store_timestamp_dec(tv, AUTO_SEC_PART_DIGITS);
table->field[11]->set_notnull();
}
}
@@ -7788,11 +7835,11 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
sch_table->field[ISE_ON_COMPLETION]->
store(STRING_WITH_LEN("PRESERVE"), scs);
- number_to_datetime(et.created, 0, &time, 0, &not_used);
+ number_to_datetime_or_date(et.created, 0, &time, 0, &not_used);
DBUG_ASSERT(not_used==0);
sch_table->field[ISE_CREATED]->store_time(&time);
- number_to_datetime(et.modified, 0, &time, 0, &not_used);
+ number_to_datetime_or_date(et.modified, 0, &time, 0, &not_used);
DBUG_ASSERT(not_used==0);
sch_table->field[ISE_LAST_ALTERED]->store_time(&time);
@@ -7838,9 +7885,9 @@ int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
CHARSET_INFO *cs= system_charset_info;
OPEN_TABLE_LIST *open_list;
- if (unlikely(!(open_list= list_open_tables(thd, thd->lex->select_lex.db.str,
- wild))) &&
- unlikely(thd->is_fatal_error))
+ if (!(open_list= list_open_tables(thd, thd->lex->first_select_lex()->db.str,
+ wild))
+ && thd->is_fatal_error)
DBUG_RETURN(1);
for (; open_list ; open_list=open_list->next)
@@ -7933,12 +7980,12 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond)
tmp.local_memory_used= 0; // meaning tmp was not populated yet
- mysql_mutex_lock(&LOCK_show_status);
+ mysql_rwlock_rdlock(&LOCK_all_status_vars);
res= show_status_array(thd, wild,
(SHOW_VAR *)all_status_vars.buffer,
scope, tmp1, "", tables->table,
upper_case_names, partial_cond);
- mysql_mutex_unlock(&LOCK_show_status);
+ mysql_rwlock_unlock(&LOCK_all_status_vars);
DBUG_RETURN(res);
}
@@ -8242,7 +8289,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
tmp_table_param->table_charset= cs;
tmp_table_param->field_count= field_count;
tmp_table_param->schema_table= 1;
- SELECT_LEX *select_lex= thd->lex->current_select;
+ SELECT_LEX *select_lex= table_list->select_lex;
bool keep_row_order= is_show_command(thd);
if (!(table= create_tmp_table(thd, tmp_table_param, field_list, (ORDER*) 0, 0,
0, (select_lex->options | thd->variables.option_bits |
@@ -8278,7 +8325,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
static int make_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
{
ST_FIELD_INFO *field_info= schema_table->fields_info;
- Name_resolution_context *context= &thd->lex->select_lex.context;
+ Name_resolution_context *context= &thd->lex->first_select_lex()->context;
for (; field_info->field_name; field_info++)
{
if (field_info->old_name)
@@ -8338,14 +8385,14 @@ int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
char tmp[128];
String buffer(tmp,sizeof(tmp), thd->charset());
LEX *lex= thd->lex;
- Name_resolution_context *context= &lex->select_lex.context;
+ Name_resolution_context *context= &lex->first_select_lex()->context;
ST_FIELD_INFO *field_info= &schema_table->fields_info[2];
LEX_CSTRING field_name= {field_info->field_name,
strlen(field_info->field_name) };
buffer.length(0);
buffer.append(field_info->old_name);
- buffer.append(&lex->select_lex.db);
+ buffer.append(&lex->first_select_lex()->db);
if (lex->wild && lex->wild->ptr())
{
buffer.append(STRING_WITH_LEN(" ("));
@@ -8378,7 +8425,7 @@ int make_columns_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
int fields_arr[]= {3, 15, 14, 6, 16, 5, 17, 18, 19, -1};
int *field_num= fields_arr;
ST_FIELD_INFO *field_info;
- Name_resolution_context *context= &thd->lex->select_lex.context;
+ Name_resolution_context *context= &thd->lex->first_select_lex()->context;
for (; *field_num >= 0; field_num++)
{
@@ -8409,7 +8456,7 @@ int make_character_sets_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
int fields_arr[]= {0, 2, 1, 3, -1};
int *field_num= fields_arr;
ST_FIELD_INFO *field_info;
- Name_resolution_context *context= &thd->lex->select_lex.context;
+ Name_resolution_context *context= &thd->lex->first_select_lex()->context;
for (; *field_num >= 0; field_num++)
{
@@ -8436,7 +8483,7 @@ int make_proc_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
int fields_arr[]= {2, 3, 4, 27, 24, 23, 22, 26, 28, 29, 30, -1};
int *field_num= fields_arr;
ST_FIELD_INFO *field_info;
- Name_resolution_context *context= &thd->lex->select_lex.context;
+ Name_resolution_context *context= &thd->lex->first_select_lex()->context;
for (; *field_num >= 0; field_num++)
{
@@ -8717,6 +8764,7 @@ bool optimize_schema_tables_memory_usage(List<TABLE_LIST> &tables)
{
/* all fields were optimized away. Force a non-0-length row */
table->s->reclength= to_recinfo->length= 1;
+ to_recinfo->type= FIELD_NORMAL;
to_recinfo++;
}
p->recinfo= to_recinfo;
@@ -9845,6 +9893,10 @@ ST_FIELD_INFO check_constraints_fields_info[]=
OPEN_FULL_TABLE},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
+
+/** For creating fields of information_schema.OPTIMIZER_TRACE */
+extern ST_FIELD_INFO optimizer_trace_info[];
+
/*
Description of ST_FIELD_INFO in table.h
@@ -9897,6 +9949,8 @@ ST_SCHEMA_TABLE schema_tables[]=
OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
{"OPEN_TABLES", open_tables_fields_info, 0,
fill_open_tables, make_old_format, 0, -1, -1, 1, 0},
+ {"OPTIMIZER_TRACE", optimizer_trace_info, 0,
+ fill_optimizer_trace_info, NULL, NULL, -1, -1, false, 0},
{"PARAMETERS", parameters_fields_info, 0,
fill_schema_proc, 0, 0, -1, -1, 0, 0},
{"PARTITIONS", partitions_fields_info, 0,
@@ -10020,11 +10074,6 @@ int finalize_schema_table(st_plugin_int *plugin)
DBUG_RETURN(0);
}
-/*
- This is used to create a timestamp field
-*/
-
-MYSQL_TIME zero_time={ 0,0,0,0,0,0,0,0, MYSQL_TIMESTAMP_TIME };
/**
Output trigger information (SHOW CREATE TRIGGER) to the client.
@@ -10109,8 +10158,9 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger)
MY_CS_NAME_SIZE),
mem_root);
+ static const Datetime zero_datetime(Datetime::zero());
Item_datetime_literal *tmp= (new (mem_root)
- Item_datetime_literal(thd, &zero_time, 2));
+ Item_datetime_literal(thd, &zero_datetime, 2));
tmp->set_name(thd, STRING_WITH_LEN("Created"), system_charset_info);
fields.push_back(tmp, mem_root);
diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc
index bf0757f78fe..320a954711a 100644
--- a/sql/sql_signal.cc
+++ b/sql/sql_signal.cc
@@ -323,7 +323,7 @@ end:
set= m_set_signal_information.m_item[i];
if (set)
{
- if (set->fixed)
+ if (set->is_fixed())
set->cleanup();
}
}
diff --git a/sql/sql_sort.h b/sql/sql_sort.h
index 93bcaf45a2c..7abbc808632 100644
--- a/sql/sql_sort.h
+++ b/sql/sql_sort.h
@@ -92,7 +92,6 @@ public:
memset(this, 0, sizeof(*this));
}
void init_for_filesort(uint sortlen, TABLE *table,
- ulong max_length_for_sort_data,
ha_rows maxrows, bool sort_positions);
};
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index 721b2de75a3..042e86fbd86 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -30,7 +30,6 @@
#include "sql_statistics.h"
#include "opt_range.h"
#include "uniques.h"
-#include "my_atomic.h"
#include "sql_show.h"
#include "sql_partition.h"
@@ -104,29 +103,6 @@ inline void init_table_list_for_stat_tables(TABLE_LIST *tables, bool for_write)
}
}
-
-/**
- @details
- The function builds a TABLE_LIST containing only one element 'tbl' for
- the statistical table called 'stat_tab_name'.
- The lock type of the element is set to TL_READ if for_write = FALSE,
- otherwise it is set to TL_WRITE.
-*/
-
-static inline
-void init_table_list_for_single_stat_table(TABLE_LIST *tbl,
- const LEX_CSTRING *stat_tab_name,
- bool for_write)
-{
- memset((char *) tbl, 0, sizeof(TABLE_LIST));
-
- tbl->db= MYSQL_SCHEMA_NAME;
- tbl->table_name= *stat_tab_name;
- tbl->alias= *stat_tab_name;
- tbl->lock_type= for_write ? TL_WRITE : TL_READ;
-}
-
-
static Table_check_intact_log_error stat_table_intact;
static const
@@ -288,16 +264,21 @@ static int open_stat_tables(THD *thd, TABLE_LIST *tables,
/**
@brief
Open a statistical table and lock it
+
+ @details
+ This is used by DDLs. When a column or index is dropped or renamed,
+ stat tables need to be adjusted accordingly.
*/
-static
-inline int open_single_stat_table(THD *thd, TABLE_LIST *table,
- const LEX_CSTRING *stat_tab_name,
- Open_tables_backup *backup,
- bool for_write)
+static inline int open_stat_table_for_ddl(THD *thd, TABLE_LIST *table,
+ const LEX_CSTRING *stat_tab_name,
+ Open_tables_backup *backup)
{
- init_table_list_for_single_stat_table(table, stat_tab_name, for_write);
- init_mdl_requests(table);
- return open_system_tables_for_read(thd, table, backup);
+ table->init_one_table(&MYSQL_SCHEMA_NAME, stat_tab_name, NULL, TL_WRITE);
+ No_such_table_error_handler nst_handler;
+ thd->push_internal_handler(&nst_handler);
+ int res= open_system_tables_for_read(thd, table, backup);
+ thd->pop_internal_handler();
+ return res;
}
@@ -326,8 +307,8 @@ private:
public:
inline void init(THD *thd, Field * table_field);
- inline bool add(ha_rows rowno);
- inline void finish(ha_rows rows);
+ inline bool add();
+ inline void finish(ha_rows rows, double sample_fraction);
inline void cleanup();
};
@@ -1558,6 +1539,8 @@ class Histogram_builder
uint curr_bucket; /* number of the current bucket to be built */
ulonglong count; /* number of values retrieved */
ulonglong count_distinct; /* number of distinct values retrieved */
+ /* number of distinct values that occured only once */
+ ulonglong count_distinct_single_occurence;
public:
Histogram_builder(Field *col, uint col_len, ha_rows rows)
@@ -1571,14 +1554,21 @@ public:
bucket_capacity= (double) records / (hist_width + 1);
curr_bucket= 0;
count= 0;
- count_distinct= 0;
+ count_distinct= 0;
+ count_distinct_single_occurence= 0;
}
- ulonglong get_count_distinct() { return count_distinct; }
+ ulonglong get_count_distinct() const { return count_distinct; }
+ ulonglong get_count_single_occurence() const
+ {
+ return count_distinct_single_occurence;
+ }
int next(void *elem, element_count elem_cnt)
{
count_distinct++;
+ if (elem_cnt == 1)
+ count_distinct_single_occurence++;
count+= elem_cnt;
if (curr_bucket == hist_width)
return 0;
@@ -1592,7 +1582,7 @@ public:
count > bucket_capacity * (curr_bucket + 1))
{
histogram->set_prev_value(curr_bucket);
- curr_bucket++;
+ curr_bucket++;
}
}
return 0;
@@ -1608,9 +1598,18 @@ int histogram_build_walk(void *elem, element_count elem_cnt, void *arg)
return hist_builder->next(elem, elem_cnt);
}
-C_MODE_END
+static int count_distinct_single_occurence_walk(void *elem,
+ element_count count, void *arg)
+{
+ ((ulonglong*)arg)[0]+= 1;
+ if (count == 1)
+ ((ulonglong*)arg)[1]+= 1;
+ return 0;
+}
+
+C_MODE_END
/*
The class Count_distinct_field is a helper class used to calculate
the number of distinct values for a column. The class employs the
@@ -1629,6 +1628,9 @@ protected:
Unique *tree; /* The helper object to contain distinct values */
uint tree_key_length; /* The length of the keys for the elements of 'tree */
+ ulonglong distincts;
+ ulonglong distincts_single_occurence;
+
public:
Count_distinct_field() {}
@@ -1681,30 +1683,40 @@ public:
table_field->mark_unused_memory_as_defined();
return tree->unique_add(table_field->ptr);
}
-
+
/*
@brief
Calculate the number of elements accumulated in the container of 'tree'
*/
- ulonglong get_value()
+ void walk_tree()
{
- ulonglong count;
- if (tree->elements == 0)
- return (ulonglong) tree->elements_in_tree();
- count= 0;
- tree->walk(table_field->table, count_distinct_walk, (void*) &count);
- return count;
+ ulonglong counts[2] = {0, 0};
+ tree->walk(table_field->table,
+ count_distinct_single_occurence_walk, counts);
+ distincts= counts[0];
+ distincts_single_occurence= counts[1];
}
/*
@brief
- Build the histogram for the elements accumulated in the container of 'tree'
+ Calculate a histogram of the tree
*/
- ulonglong get_value_with_histogram(ha_rows rows)
+ void walk_tree_with_histogram(ha_rows rows)
{
Histogram_builder hist_builder(table_field, tree_key_length, rows);
tree->walk(table_field->table, histogram_build_walk, (void *) &hist_builder);
- return hist_builder.get_count_distinct();
+ distincts= hist_builder.get_count_distinct();
+ distincts_single_occurence= hist_builder.get_count_single_occurence();
+ }
+
+ ulonglong get_count_distinct()
+ {
+ return distincts;
+ }
+
+ ulonglong get_count_distinct_single_occurence()
+ {
+ return distincts_single_occurence;
}
/*
@@ -2349,7 +2361,7 @@ void Column_statistics_collected::init(THD *thd, Field *table_field)
*/
inline
-bool Column_statistics_collected::add(ha_rows rowno)
+bool Column_statistics_collected::add()
{
bool err= 0;
@@ -2358,9 +2370,11 @@ bool Column_statistics_collected::add(ha_rows rowno)
else
{
column_total_length+= column->value_length();
- if (min_value && column->update_min(min_value, rowno == nulls))
+ if (min_value && column->update_min(min_value,
+ is_null(COLUMN_STAT_MIN_VALUE)))
set_not_null(COLUMN_STAT_MIN_VALUE);
- if (max_value && column->update_max(max_value, rowno == nulls))
+ if (max_value && column->update_max(max_value,
+ is_null(COLUMN_STAT_MAX_VALUE)))
set_not_null(COLUMN_STAT_MAX_VALUE);
if (count_distinct)
err= count_distinct->add();
@@ -2378,7 +2392,7 @@ bool Column_statistics_collected::add(ha_rows rowno)
*/
inline
-void Column_statistics_collected::finish(ha_rows rows)
+void Column_statistics_collected::finish(ha_rows rows, double sample_fraction)
{
double val;
@@ -2396,16 +2410,44 @@ void Column_statistics_collected::finish(ha_rows rows)
}
if (count_distinct)
{
- ulonglong distincts;
uint hist_size= count_distinct->get_hist_size();
+
+ /* Compute cardinality statistics and optionally histogram. */
if (hist_size == 0)
- distincts= count_distinct->get_value();
+ count_distinct->walk_tree();
else
- distincts= count_distinct->get_value_with_histogram(rows - nulls);
+ count_distinct->walk_tree_with_histogram(rows - nulls);
+
+ ulonglong distincts= count_distinct->get_count_distinct();
+ ulonglong distincts_single_occurence=
+ count_distinct->get_count_distinct_single_occurence();
+
if (distincts)
{
- val= (double) (rows - nulls) / distincts;
- set_avg_frequency(val);
+ /*
+ We use the unsmoothed first-order jackknife estimator" to estimate
+ the number of distinct values.
+ With a sufficient large percentage of rows sampled (80%), we revert back
+ to computing the avg_frequency off of the raw data.
+ */
+ if (sample_fraction > 0.8)
+ val= (double) (rows - nulls) / distincts;
+ else
+ {
+ if (nulls == 1)
+ distincts_single_occurence+= 1;
+ if (nulls)
+ distincts+= 1;
+ double fraction_single_occurence=
+ static_cast<double>(distincts_single_occurence) / rows;
+ double total_number_of_rows= rows / sample_fraction;
+ double estimate_total_distincts= total_number_of_rows /
+ (distincts /
+ (1.0 - (1.0 - sample_fraction) * fraction_single_occurence));
+ val = std::fmax(estimate_total_distincts * (rows - nulls) / rows, 1.0);
+ }
+
+ set_avg_frequency(val);
set_not_null(COLUMN_STAT_AVG_FREQUENCY);
}
else
@@ -2593,12 +2635,28 @@ int collect_statistics_for_table(THD *thd, TABLE *table)
Field *table_field;
ha_rows rows= 0;
handler *file=table->file;
+ double sample_fraction= thd->variables.sample_percentage / 100;
+ const ha_rows MIN_THRESHOLD_FOR_SAMPLING= 50000;
DBUG_ENTER("collect_statistics_for_table");
table->collected_stats->cardinality_is_null= TRUE;
table->collected_stats->cardinality= 0;
+ if (thd->variables.sample_percentage == 0)
+ {
+ if (file->records() < MIN_THRESHOLD_FOR_SAMPLING)
+ {
+ sample_fraction= 1;
+ }
+ else
+ {
+ sample_fraction= std::fmin(
+ (MIN_THRESHOLD_FOR_SAMPLING + 4096 *
+ log(200 * file->records())) / file->records(), 1);
+ }
+ }
+
for (field_ptr= table->field; *field_ptr; field_ptr++)
{
table_field= *field_ptr;
@@ -2611,7 +2669,7 @@ int collect_statistics_for_table(THD *thd, TABLE *table)
/* Perform a full table scan to collect statistics on 'table's columns */
if (!(rc= file->ha_rnd_init(TRUE)))
- {
+ {
DEBUG_SYNC(table->in_use, "statistics_collection_start");
while ((rc= file->ha_rnd_next(table->record[0])) != HA_ERR_END_OF_FILE)
@@ -2622,17 +2680,20 @@ int collect_statistics_for_table(THD *thd, TABLE *table)
if (rc)
break;
- for (field_ptr= table->field; *field_ptr; field_ptr++)
+ if (thd_rnd(thd) <= sample_fraction)
{
- table_field= *field_ptr;
- if (!table_field->collected_stats)
- continue;
- if ((rc= table_field->collected_stats->add(rows)))
+ for (field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ table_field= *field_ptr;
+ if (!table_field->collected_stats)
+ continue;
+ if ((rc= table_field->collected_stats->add()))
+ break;
+ }
+ if (rc)
break;
+ rows++;
}
- if (rc)
- break;
- rows++;
}
file->ha_rnd_end();
}
@@ -2646,7 +2707,8 @@ int collect_statistics_for_table(THD *thd, TABLE *table)
if (!rc)
{
table->collected_stats->cardinality_is_null= FALSE;
- table->collected_stats->cardinality= rows;
+ table->collected_stats->cardinality=
+ static_cast<ha_rows>(rows / sample_fraction);
}
bitmap_clear_all(table->write_set);
@@ -2657,7 +2719,7 @@ int collect_statistics_for_table(THD *thd, TABLE *table)
continue;
bitmap_set_bit(table->write_set, table_field->field_index);
if (!rc)
- table_field->collected_stats->finish(rows);
+ table_field->collected_stats->finish(rows, sample_fraction);
else
table_field->collected_stats->cleanup();
}
@@ -3191,7 +3253,7 @@ int read_statistics_for_tables(THD *thd, TABLE_LIST *tables)
'db' from all statistical tables: table_stats, column_stats, index_stats.
@retval
- 0 If all deletions are successful
+ 0 If all deletions are successful or we couldn't open statistics table
@retval
1 Otherwise
@@ -3199,7 +3261,8 @@ int read_statistics_for_tables(THD *thd, TABLE_LIST *tables)
The function is called when executing the statement DROP TABLE 'tab'.
*/
-int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab)
+int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *tab)
{
int err;
enum_binlog_format save_binlog_format;
@@ -3207,11 +3270,10 @@ int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db, const LEX_CSTRI
TABLE_LIST tables[STATISTICS_TABLES];
Open_tables_backup open_tables_backup;
int rc= 0;
-
DBUG_ENTER("delete_statistics_for_table");
if (open_stat_tables(thd, tables, &open_tables_backup, TRUE))
- DBUG_RETURN(rc);
+ DBUG_RETURN(0);
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3264,21 +3326,16 @@ int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db, const LEX_CSTRI
@brief
Delete statistics on a column of the specified table
- @param
- thd The thread handle
- @param
- tab The table the column belongs to
- @param
- col The field of the column whose statistics is to be deleted
+ @param thd The thread handle
+ @param tab The table the column belongs to
+ @param col The field of the column whose statistics is to be deleted
@details
The function delete statistics on the column 'col' belonging to the table
'tab' from the statistical table column_stats.
- @retval
- 0 If the deletion is successful
- @retval
- 1 Otherwise
+ @retval 0 If all deletions are successful or we couldn't open statistics table
+ @retval 1 Otherwise
@note
The function is called when dropping a table column or when changing
@@ -3293,15 +3350,11 @@ int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col)
TABLE_LIST tables;
Open_tables_backup open_tables_backup;
int rc= 0;
-
DBUG_ENTER("delete_statistics_for_column");
- if (open_single_stat_table(thd, &tables, &stat_table_name[1],
- &open_tables_backup, TRUE))
- {
- thd->clear_error();
- DBUG_RETURN(rc);
- }
+ if (open_stat_table_for_ddl(thd, &tables, &stat_table_name[1],
+ &open_tables_backup))
+ DBUG_RETURN(0);
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3327,24 +3380,18 @@ int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col)
@brief
Delete statistics on an index of the specified table
- @param
- thd The thread handle
- @param
- tab The table the index belongs to
- @param
- key_info The descriptor of the index whose statistics is to be deleted
- @param
- ext_prefixes_only Delete statistics only on the index prefixes extended by
- the components of the primary key
+ @param thd The thread handle
+ @param tab The table the index belongs to
+ @param key_info The descriptor of the index whose statistics is to be deleted
+ @param ext_prefixes_only Delete statistics only on the index prefixes
+ extended by the components of the primary key
@details
The function delete statistics on the index specified by 'key_info'
defined on the table 'tab' from the statistical table index_stats.
- @retval
- 0 If the deletion is successful
- @retval
- 1 Otherwise
+ @retval 0 If all deletions are successful or we couldn't open statistics table
+ @retval 1 Otherwise
@note
The function is called when dropping an index, or dropping/changing the
@@ -3360,15 +3407,11 @@ int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
TABLE_LIST tables;
Open_tables_backup open_tables_backup;
int rc= 0;
-
DBUG_ENTER("delete_statistics_for_index");
- if (open_single_stat_table(thd, &tables, &stat_table_name[2],
- &open_tables_backup, TRUE))
- {
- thd->clear_error();
- DBUG_RETURN(rc);
- }
+ if (open_stat_table_for_ddl(thd, &tables, &stat_table_name[2],
+ &open_tables_backup))
+ DBUG_RETURN(0);
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3429,7 +3472,7 @@ int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
index_stats.
@retval
- 0 If all updates of the table name are successful
+ 0 If all updates of the table name are successful
@retval
1 Otherwise
@@ -3437,8 +3480,10 @@ int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
The function is called when executing any statement that renames a table
*/
-int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab,
- const LEX_CSTRING *new_db, const LEX_CSTRING *new_tab)
+int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *tab,
+ const LEX_CSTRING *new_db,
+ const LEX_CSTRING *new_tab)
{
int err;
enum_binlog_format save_binlog_format;
@@ -3449,7 +3494,9 @@ int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db, const LEX_CSTRI
DBUG_ENTER("rename_table_in_stat_tables");
if (open_stat_tables(thd, tables, &open_tables_backup, TRUE))
+ {
DBUG_RETURN(0); // not an error
+ }
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3506,26 +3553,19 @@ int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db, const LEX_CSTRI
/**
- @brief
Rename a column in the statistical table column_stats
- @param
- thd The thread handle
- @param
- tab The table the column belongs to
- @param
- col The column to be renamed
- @param
- new_name The new column name
+ @param thd The thread handle
+ @param tab The table the column belongs to
+ @param col The column to be renamed
+ @param new_name The new column name
@details
The function replaces the name of the column 'col' belonging to the table
'tab' for 'new_name' in the statistical table column_stats.
- @retval
- 0 If all updates of the table name are successful
- @retval
- 1 Otherwise
+ @retval 0 If all updates of the table name are successful
+ @retval 1 Otherwise
@note
The function is called when executing any statement that renames a column,
@@ -3541,18 +3581,14 @@ int rename_column_in_stat_tables(THD *thd, TABLE *tab, Field *col,
TABLE_LIST tables;
Open_tables_backup open_tables_backup;
int rc= 0;
-
DBUG_ENTER("rename_column_in_stat_tables");
if (tab->s->tmp_table != NO_TMP_TABLE)
DBUG_RETURN(0);
- if (open_single_stat_table(thd, &tables, &stat_table_name[1],
- &open_tables_backup, TRUE))
- {
- thd->clear_error();
+ if (open_stat_table_for_ddl(thd, &tables, &stat_table_name[1],
+ &open_tables_backup))
DBUG_RETURN(rc);
- }
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3594,9 +3630,8 @@ void set_statistics_for_table(THD *thd, TABLE *table)
{
TABLE_STATISTICS_CB *stats_cb= &table->s->stats_cb;
Table_statistics *read_stats= stats_cb->table_stats;
- Use_stat_tables_mode use_stat_table_mode= get_use_stat_tables_mode(thd);
table->used_stat_records=
- (use_stat_table_mode <= COMPLEMENTARY ||
+ (!check_eits_preferred(thd) ||
!table->stats_is_read || read_stats->cardinality_is_null) ?
table->file->stats.records : read_stats->cardinality;
@@ -3620,7 +3655,7 @@ void set_statistics_for_table(THD *thd, TABLE *table)
key_info < key_info_end; key_info++)
{
key_info->is_statistics_from_stat_tables=
- (use_stat_table_mode > COMPLEMENTARY &&
+ (check_eits_preferred(thd) &&
table->stats_is_read &&
key_info->read_stats->avg_frequency_is_inited() &&
key_info->read_stats->get_avg_frequency(0) > 0.5);
diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h
index e2e66488b06..35b3aa33acc 100644
--- a/sql/sql_statistics.h
+++ b/sql/sql_statistics.h
@@ -16,12 +16,26 @@
#ifndef SQL_STATISTICS_H
#define SQL_STATISTICS_H
+/*
+ For COMPLEMENTARY_FOR_QUERIES and PREFERABLY_FOR_QUERIES they are
+ similar to the COMPLEMENTARY and PREFERABLY respectively except that
+ with these values we would not be collecting EITS for queries like
+ ANALYZE TABLE t1;
+ To collect EITS with these values, we have to use PERSISITENT FOR
+ analyze table t1 persistent for
+ columns (col1,col2...) index (idx1, idx2...)
+ or
+ analyze table t1 persistent for all
+*/
+
typedef
enum enum_use_stat_tables_mode
{
NEVER,
COMPLEMENTARY,
PREFERABLY,
+ COMPLEMENTARY_FOR_QUERIES,
+ PREFERABLY_FOR_QUERIES
} Use_stat_tables_mode;
typedef
@@ -87,6 +101,19 @@ Use_stat_tables_mode get_use_stat_tables_mode(THD *thd)
{
return (Use_stat_tables_mode) (thd->variables.use_stat_tables);
}
+inline
+bool check_eits_collection_allowed(THD *thd)
+{
+ return (get_use_stat_tables_mode(thd) == COMPLEMENTARY ||
+ get_use_stat_tables_mode(thd) == PREFERABLY);
+}
+
+inline
+bool check_eits_preferred(THD *thd)
+{
+ return (get_use_stat_tables_mode(thd) == PREFERABLY ||
+ get_use_stat_tables_mode(thd) == PREFERABLY_FOR_QUERIES);
+}
int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables);
int read_statistics_for_tables(THD *thd, TABLE_LIST *tables);
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 2b40a72edf3..e3f4497274c 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -31,7 +31,7 @@
** String functions
*****************************************************************************/
-bool String::real_alloc(size_t length)
+bool Binary_string::real_alloc(size_t length)
{
size_t arg_length= ALIGN_SIZE(length + 1);
DBUG_ASSERT(arg_length > length);
@@ -81,7 +81,7 @@ bool String::real_alloc(size_t length)
@retval true An error occurred when attempting to allocate memory.
*/
-bool String::realloc_raw(size_t alloc_length)
+bool Binary_string::realloc_raw(size_t alloc_length)
{
if (Alloced_length <= alloc_length)
{
@@ -103,8 +103,7 @@ bool String::realloc_raw(size_t alloc_length)
(thread_specific ?
MY_THREAD_SPECIFIC : 0)))))
{
- if (str_length > len - 1)
- str_length= 0;
+ DBUG_ASSERT(str_length < len);
if (str_length) // Avoid bugs in memcpy on AIX
memcpy(new_ptr,Ptr,str_length);
new_ptr[str_length]=0;
@@ -127,19 +126,18 @@ bool String::set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs)
if (alloc(l))
return TRUE;
str_length=(uint32) (cs->cset->longlong10_to_str)(cs,Ptr,l,base,num);
- str_charset=cs;
+ set_charset(cs);
return FALSE;
}
// Convert a number into its HEX representation
-bool String::set_hex(ulonglong num)
+bool Binary_string::set_hex(ulonglong num)
{
char *n_end;
if (alloc(65) || !(n_end= longlong2str(num, Ptr, 16)))
return true;
length((uint32) (n_end - Ptr));
- set_charset(&my_charset_latin1);
return false;
}
@@ -157,7 +155,7 @@ static inline void APPEND_HEX(char *&to, uchar value)
}
-void String::qs_append_hex(const char *str, uint32 len)
+void Static_binary_string::qs_append_hex(const char *str, uint32 len)
{
const char *str_end= str + len;
for (char *to= Ptr + str_length ; str < str_end; str++)
@@ -167,7 +165,7 @@ void String::qs_append_hex(const char *str, uint32 len)
// Convert a string to its HEX representation
-bool String::set_hex(const char *str, uint32 len)
+bool Binary_string::set_hex(const char *str, uint32 len)
{
/*
Safety: cut the source string if "len" is too large.
@@ -181,7 +179,17 @@ bool String::set_hex(const char *str, uint32 len)
return true;
length(0);
qs_append_hex(str, len);
- set_charset(&my_charset_latin1);
+ return false;
+}
+
+
+bool Binary_string::set_fcvt(double num, uint decimals)
+{
+ // Assert that `decimals` is small enough to fit into FLOATING_POINT_BUFFER
+ DBUG_ASSERT(decimals < DECIMAL_NOT_SPECIFIED);
+ if (alloc(FLOATING_POINT_BUFFER))
+ return true;
+ length(my_fcvt(num, decimals, Ptr, NULL));
return false;
}
@@ -192,7 +200,7 @@ bool String::set_real(double num,uint decimals, CHARSET_INFO *cs)
uint dummy_errors;
size_t len;
- str_charset=cs;
+ set_charset(cs);
if (decimals >= FLOATING_POINT_DECIMALS)
{
len= my_gcvt(num, MY_GCVT_ARG_DOUBLE, sizeof(buff) - 1, buff, NULL);
@@ -204,7 +212,7 @@ bool String::set_real(double num,uint decimals, CHARSET_INFO *cs)
}
-bool String::copy()
+bool Binary_string::copy()
{
if (!alloced)
{
@@ -225,18 +233,17 @@ bool String::copy()
@retval false Success.
@retval true Memory allocation failed.
*/
-bool String::copy(const String &str)
+bool Binary_string::copy(const Binary_string &str)
{
if (alloc(str.str_length))
return TRUE;
if ((str_length=str.str_length))
bmove(Ptr,str.Ptr,str_length); // May be overlapping
Ptr[str_length]=0;
- str_charset=str.str_charset;
return FALSE;
}
-bool String::copy(const char *str,size_t arg_length, CHARSET_INFO *cs)
+bool Binary_string::copy(const char *str, size_t arg_length)
{
DBUG_ASSERT(arg_length < UINT_MAX32);
if (alloc(arg_length))
@@ -253,7 +260,6 @@ bool String::copy(const char *str,size_t arg_length, CHARSET_INFO *cs)
else if ((str_length=uint32(arg_length)))
memcpy(Ptr,str,arg_length);
Ptr[arg_length]=0;
- str_charset=cs;
return FALSE;
}
@@ -263,7 +269,7 @@ bool String::copy(const char *str,size_t arg_length, CHARSET_INFO *cs)
from valgrind
*/
-bool String::copy_or_move(const char *str,size_t arg_length, CHARSET_INFO *cs)
+bool Binary_string::copy_or_move(const char *str, size_t arg_length)
{
DBUG_ASSERT(arg_length < UINT_MAX32);
if (alloc(arg_length))
@@ -271,7 +277,6 @@ bool String::copy_or_move(const char *str,size_t arg_length, CHARSET_INFO *cs)
if ((str_length=uint32(arg_length)))
memmove(Ptr,str,arg_length);
Ptr[arg_length]=0;
- str_charset=cs;
return FALSE;
}
@@ -397,7 +402,7 @@ bool String::copy_aligned(const char *str, size_t arg_length, size_t offset,
Ptr[aligned_length]=0;
/* str_length is always >= 0 as arg_length is != 0 */
str_length= (uint32)aligned_length;
- str_charset= cs;
+ set_charset(cs);
return FALSE;
}
@@ -450,7 +455,7 @@ bool String::copy(const char *str, size_t arg_length,
return TRUE;
str_length=copy_and_convert((char*) Ptr, new_length, to_cs,
str, arg_length, from_cs, errors);
- str_charset=to_cs;
+ set_charset(to_cs);
return FALSE;
}
@@ -476,19 +481,20 @@ bool String::copy(const char *str, size_t arg_length,
bool String::set_ascii(const char *str, size_t arg_length)
{
- if (str_charset->mbminlen == 1)
+ if (mbminlen() == 1)
{
- set(str, arg_length, str_charset);
+ set(str, arg_length, charset());
return 0;
}
uint dummy_errors;
- return copy(str, (uint32)arg_length, &my_charset_latin1, str_charset, &dummy_errors);
+ return copy(str, (uint32) arg_length, &my_charset_latin1,
+ charset(), &dummy_errors);
}
/* This is used by mysql.cc */
-bool String::fill(uint32 max_length,char fill_char)
+bool Binary_string::fill(uint32 max_length,char fill_char)
{
if (str_length > max_length)
Ptr[str_length=max_length]=0;
@@ -504,22 +510,10 @@ bool String::fill(uint32 max_length,char fill_char)
void String::strip_sp()
{
- while (str_length && my_isspace(str_charset,Ptr[str_length-1]))
+ while (str_length && my_isspace(charset(), Ptr[str_length-1]))
str_length--;
}
-bool String::append(const String &s)
-{
- if (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();
- }
- return FALSE;
-}
-
/*
Append an ASCII string to the a string of the current character set
@@ -535,13 +529,13 @@ bool String::append(const char *s,size_t size)
/*
For an ASCII incompatible string, e.g. UCS-2, we need to convert
*/
- if (str_charset->mbminlen > 1)
+ if (mbminlen() > 1)
{
- uint32 add_length=arg_length * str_charset->mbmaxlen;
+ uint32 add_length= arg_length * mbmaxlen();
uint dummy_errors;
if (realloc_with_extra_if_needed(str_length+ add_length))
return TRUE;
- str_length+= copy_and_convert(Ptr+str_length, add_length, str_charset,
+ str_length+= copy_and_convert(Ptr + str_length, add_length, charset(),
s, arg_length, &my_charset_latin1,
&dummy_errors);
return FALSE;
@@ -550,24 +544,11 @@ bool String::append(const char *s,size_t size)
/*
For an ASCII compatinble string we can just append.
*/
- if (realloc_with_extra_if_needed(str_length+arg_length))
- return TRUE;
- memcpy(Ptr+str_length,s,arg_length);
- str_length+=arg_length;
- return FALSE;
+ return Binary_string::append(s, arg_length);
}
-/*
- Append a 0-terminated ASCII string
-*/
-
-bool String::append(const char *s)
-{
- return append(s, (uint) strlen(s));
-}
-
-bool String::append_longlong(longlong val)
+bool Binary_string::append_longlong(longlong val)
{
if (realloc(str_length+MAX_BIGINT_WIDTH+2))
return TRUE;
@@ -577,7 +558,7 @@ bool String::append_longlong(longlong val)
}
-bool String::append_ulonglong(ulonglong val)
+bool Binary_string::append_ulonglong(ulonglong val)
{
if (realloc(str_length+MAX_BIGINT_WIDTH+2))
return TRUE;
@@ -598,13 +579,13 @@ bool String::append(const char *s, size_t arg_length, CHARSET_INFO *cs)
uint32 offset;
- if (needs_conversion((uint32)arg_length, cs, str_charset, &offset))
+ if (needs_conversion((uint32)arg_length, cs, charset(), &offset))
{
size_t add_length;
if ((cs == &my_charset_bin) && offset)
{
- DBUG_ASSERT(str_charset->mbminlen > offset);
- offset= str_charset->mbminlen - offset; // How many characters to pad
+ DBUG_ASSERT(mbminlen() > offset);
+ offset= mbminlen() - offset; // How many characters to pad
add_length= arg_length + offset;
if (realloc(str_length + add_length))
return TRUE;
@@ -614,24 +595,19 @@ bool String::append(const char *s, size_t arg_length, CHARSET_INFO *cs)
return FALSE;
}
- add_length= arg_length / cs->mbminlen * str_charset->mbmaxlen;
+ add_length= arg_length / cs->mbminlen * mbmaxlen();
uint dummy_errors;
if (realloc_with_extra_if_needed(str_length + add_length))
return TRUE;
- str_length+= copy_and_convert(Ptr+str_length, (uint32)add_length, str_charset,
- s, (uint32)arg_length, cs, &dummy_errors);
- }
- else
- {
- if (realloc_with_extra_if_needed(str_length + arg_length))
- return TRUE;
- memcpy(Ptr + str_length, s, arg_length);
- str_length+= (uint32)arg_length;
+ str_length+= copy_and_convert(Ptr + str_length, (uint32)add_length, charset(),
+ s, (uint32)arg_length, cs, &dummy_errors);
+ return false;
}
- return FALSE;
+ return Binary_string::append(s, arg_length);
}
-bool String::append(IO_CACHE* file, uint32 arg_length)
+
+bool Binary_string::append(IO_CACHE* file, uint32 arg_length)
{
if (realloc_with_extra_if_needed(str_length+arg_length))
return TRUE;
@@ -679,19 +655,8 @@ bool String::append_with_prefill(const char *s,uint32 arg_length,
return FALSE;
}
-uint32 String::numchars() const
-{
- return (uint32) str_charset->cset->numchars(str_charset, Ptr, Ptr+str_length);
-}
-
-int String::charpos(longlong i,uint32 offset)
-{
- if (i <= 0)
- return (int)i;
- return (int)str_charset->cset->charpos(str_charset,Ptr+offset,Ptr+str_length,(size_t)i);
-}
-int String::strstr(const String &s,uint32 offset)
+int Static_binary_string::strstr(const Static_binary_string &s, uint32 offset)
{
if (s.length()+offset <= str_length)
{
@@ -722,7 +687,7 @@ skip:
** Search string from end. Offset is offset to the end of string
*/
-int String::strrstr(const String &s,uint32 offset)
+int Static_binary_string::strrstr(const Static_binary_string &s, uint32 offset)
{
if (s.length() <= offset && offset <= str_length)
{
@@ -749,18 +714,9 @@ skip:
return -1;
}
-/*
- Replace substring with string
- If wrong parameter or not enough memory, do nothing
-*/
-bool String::replace(uint32 offset,uint32 arg_length,const String &to)
-{
- return replace(offset,arg_length,to.ptr(),to.length());
-}
-
-bool String::replace(uint32 offset,uint32 arg_length,
- const char *to, uint32 to_length)
+bool Binary_string::replace(uint32 offset, uint32 arg_length,
+ const char *to, uint32 to_length)
{
long diff = (long) to_length-(long) arg_length;
if (offset+arg_length <= str_length)
@@ -791,7 +747,7 @@ bool String::replace(uint32 offset,uint32 arg_length,
// added by Holyfoot for "geometry" needs
-int String::reserve(size_t space_needed, size_t grow_by)
+int Binary_string::reserve(size_t space_needed, size_t grow_by)
{
if (Alloced_length < str_length + space_needed)
{
@@ -801,34 +757,34 @@ int String::reserve(size_t space_needed, size_t grow_by)
return FALSE;
}
-void String::qs_append(const char *str, size_t len)
+void Static_binary_string::qs_append(const char *str, size_t len)
{
memcpy(Ptr + str_length, str, len + 1);
str_length += (uint32)len;
}
-void String::qs_append(double d)
+void Static_binary_string::qs_append(double d)
{
char *buff = Ptr + str_length;
str_length+= (uint32) my_gcvt(d, MY_GCVT_ARG_DOUBLE, FLOATING_POINT_BUFFER - 1, buff,
NULL);
}
-void String::qs_append(double *d)
+void Static_binary_string::qs_append(double *d)
{
double ld;
float8get(ld, (char*) d);
qs_append(ld);
}
-void String::qs_append(int i)
+void Static_binary_string::qs_append(int i)
{
char *buff= Ptr + str_length;
char *end= int10_to_str(i, buff, -10);
str_length+= (int) (end-buff);
}
-void String::qs_append(ulonglong i)
+void Static_binary_string::qs_append(ulonglong i)
{
char *buff= Ptr + str_length;
char *end= longlong10_to_str(i, buff, 10);
@@ -950,12 +906,12 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length)
of a constant string.
Not safe to reuse.
*/
- if (from->Alloced_length > 0) // "from" is #c or #d (not a constant)
+ if (from->alloced_length() > 0) // "from" is #c or #d (not a constant)
{
- if (from->Alloced_length >= from_length)
+ if (from->alloced_length() >= from_length)
return from; // #c or #d (large enough to store from_length bytes)
- if (from->alloced)
+ if (from->is_alloced())
{
(void) from->realloc(from_length);
return from; // #d (reallocated to fit from_length bytes)
@@ -994,15 +950,15 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length)
Note, as we can't distinguish between #a and #b for sure,
so we can't assert "not #a", but we can at least assert "not #e".
*/
- DBUG_ASSERT(!from->alloced || from->Alloced_length > 0); // Not #e
+ DBUG_ASSERT(!from->is_alloced() || from->alloced_length() > 0); // Not #e
(void) from->realloc(from_length);
return from;
}
if (from->uses_buffer_owned_by(to))
{
- DBUG_ASSERT(!from->alloced);
- DBUG_ASSERT(to->alloced);
+ DBUG_ASSERT(!from->is_alloced());
+ DBUG_ASSERT(to->is_alloced());
/*
"from" is a constant string pointing to a fragment of alloced string "to":
to= xxxFFFyyy
@@ -1017,14 +973,14 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length)
DBUG_ASSERT(to->length() >= xxx_length);
to->replace(0, xxx_length, "", 0); // Remove the "xxx" part
to->realloc(from_length);
- to->str_charset= from->str_charset;
+ to->set_charset(from->charset());
return to;
}
- if (to->realloc(from_length))
+ if (to->alloc(from_length))
return from; // Actually an error
if ((to->str_length=MY_MIN(from->str_length,from_length)))
memcpy(to->Ptr,from->Ptr,to->str_length);
- to->str_charset=from->str_charset;
+ to->set_charset(*from);
return to; // "from" was of types #a, #b, #e, or small #c.
}
@@ -1184,26 +1140,6 @@ void String::print_with_conversion(String *print, CHARSET_INFO *cs) const
}
-/*
- Exchange state of this object and argument.
-
- SYNOPSIS
- String::swap()
-
- RETURN
- Target string will contain state of this object and vice versa.
-*/
-
-void String::swap(String &s)
-{
- swap_variables(char *, Ptr, s.Ptr);
- swap_variables(uint32, str_length, s.str_length);
- swap_variables(uint32, Alloced_length, s.Alloced_length);
- swap_variables(bool, alloced, s.alloced);
- swap_variables(CHARSET_INFO*, str_charset, s.str_charset);
-}
-
-
/**
Convert string to printable ASCII string
@@ -1274,3 +1210,21 @@ uint convert_to_printable(char *to, size_t to_len,
*t= '\0';
return (uint) (t - to);
}
+
+size_t convert_to_printable_required_length(uint len)
+{
+ return static_cast<size_t>(len) * 4 + 3/*dots*/ + 1/*trailing \0 */;
+}
+
+bool String::append_semi_hex(const char *s, uint len, CHARSET_INFO *cs)
+{
+ if (!len)
+ return false;
+ size_t dst_len= convert_to_printable_required_length(len);
+ if (reserve(dst_len))
+ return true;
+ uint nbytes= convert_to_printable(Ptr + str_length, dst_len, s, len, cs);
+ DBUG_ASSERT((ulonglong) str_length + nbytes < UINT_MAX32);
+ str_length+= nbytes;
+ return false;
+}
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 4f9a68acbb4..0098ad15cb6 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -130,6 +130,8 @@ size_t my_copy_with_hex_escaping(CHARSET_INFO *cs,
uint convert_to_printable(char *to, size_t to_len,
const char *from, size_t from_len,
CHARSET_INFO *from_cs, size_t nbytes= 0);
+size_t convert_to_printable_required_length(uint len);
+
class Charset
{
@@ -139,6 +141,37 @@ public:
Charset(CHARSET_INFO *cs) :m_charset(cs) { }
CHARSET_INFO *charset() const { return m_charset; }
+ uint mbminlen() const { return m_charset->mbminlen; }
+ uint mbmaxlen() const { return m_charset->mbmaxlen; }
+
+ size_t numchars(const char *str, const char *end) const
+ {
+ return m_charset->cset->numchars(m_charset, str, end);
+ }
+ size_t lengthsp(const char *str, size_t length) const
+ {
+ return m_charset->cset->lengthsp(m_charset, str, length);
+ }
+ size_t charpos(const char *str, const char *end, size_t pos) const
+ {
+ return m_charset->cset->charpos(m_charset, str, end, pos);
+ }
+ void set_charset(CHARSET_INFO *charset_arg)
+ {
+ m_charset= charset_arg;
+ }
+ void set_charset(const Charset &other)
+ {
+ m_charset= other.m_charset;
+ }
+ void swap(Charset &other)
+ {
+ swap_variables(CHARSET_INFO*, m_charset, other.m_charset);
+ }
+ bool same_encoding(const Charset &other) const
+ {
+ return !strcmp(m_charset->csname, other.m_charset->csname);
+ }
/*
Collation name without the character set name.
For example, in case of "latin1_swedish_ci",
@@ -149,57 +182,242 @@ public:
bool eq_collation_specific_names(CHARSET_INFO *cs) const;
};
-class String : public Sql_alloc
+
+/*
+ A storage for String.
+ Should be eventually derived from LEX_STRING.
+*/
+class Static_binary_string : public Sql_alloc
{
+protected:
char *Ptr;
- uint32 str_length,Alloced_length, extra_alloc;
- bool alloced,thread_specific;
- CHARSET_INFO *str_charset;
+ uint32 str_length;
public:
- String()
- {
- Ptr=0; str_length=Alloced_length=extra_alloc=0;
- alloced= thread_specific= 0;
- str_charset= &my_charset_bin;
+ Static_binary_string()
+ :Ptr(NULL),
+ str_length(0)
+ { }
+ Static_binary_string(char *str, size_t length_arg)
+ :Ptr(str),
+ str_length((uint32) length_arg)
+ {
+ DBUG_ASSERT(length_arg < UINT_MAX32);
}
- String(size_t length_arg)
- {
- alloced= thread_specific= 0;
- Alloced_length= extra_alloc= 0; (void) real_alloc(length_arg);
- str_charset= &my_charset_bin;
+ inline uint32 length() const { return str_length;}
+ inline char& operator [] (size_t i) const { return Ptr[i]; }
+ inline void length(size_t len) { str_length=(uint32)len ; }
+ inline bool is_empty() const { return (str_length == 0); }
+ inline const char *ptr() const { return Ptr; }
+ inline const char *end() const { return Ptr + str_length; }
+
+ LEX_STRING lex_string() const
+ {
+ LEX_STRING str = { (char*) ptr(), length() };
+ return str;
}
- String(const char *str, CHARSET_INFO *cs)
- {
- Ptr=(char*) str; str_length= (uint32) strlen(str);
+ LEX_CSTRING lex_cstring() const
+ {
+ LEX_CSTRING skr = { ptr(), length() };
+ return skr;
+ }
+
+ bool has_8bit_bytes() const
+ {
+ for (const char *c= ptr(), *c_end= end(); c < c_end; c++)
+ {
+ if (!my_isascii(*c))
+ return true;
+ }
+ return false;
+ }
+
+ bool bin_eq(const Static_binary_string *other) const
+ {
+ return length() == other->length() &&
+ !memcmp(ptr(), other->ptr(), length());
+ }
+
+ void set(char *str, size_t len)
+ {
+ Ptr= str;
+ str_length= (uint32) len;
+ }
+
+ void swap(Static_binary_string &s)
+ {
+ swap_variables(char *, Ptr, s.Ptr);
+ swap_variables(uint32, str_length, s.str_length);
+ }
+
+ /*
+ PMG 2004.11.12
+ This is a method that works the same as perl's "chop". It simply
+ drops the last character of a string. This is useful in the case
+ of the federated storage handler where I'm building a unknown
+ number, list of values and fields to be used in a sql insert
+ statement to be run on the remote server, and have a comma after each.
+ When the list is complete, I "chop" off the trailing comma
+
+ ex.
+ String stringobj;
+ stringobj.append("VALUES ('foo', 'fi', 'fo',");
+ stringobj.chop();
+ stringobj.append(")");
+
+ In this case, the value of string was:
+
+ VALUES ('foo', 'fi', 'fo',
+ VALUES ('foo', 'fi', 'fo'
+ VALUES ('foo', 'fi', 'fo')
+ */
+ inline void chop()
+ {
+ str_length--;
+ Ptr[str_length]= '\0';
+ DBUG_ASSERT(strlen(Ptr) == str_length);
+ }
+
+ // Returns offset to substring or -1
+ int strstr(const Static_binary_string &search, uint32 offset=0);
+ // Returns offset to substring or -1
+ int strrstr(const Static_binary_string &search, uint32 offset=0);
+
+ /*
+ The following append operations do NOT check alloced memory
+ q_*** methods writes values of parameters itself
+ qs_*** methods writes string representation of value
+ */
+ void q_append(const char c)
+ {
+ Ptr[str_length++] = c;
+ }
+ void q_append2b(const uint32 n)
+ {
+ int2store(Ptr + str_length, n);
+ str_length += 2;
+ }
+ void q_append(const uint32 n)
+ {
+ int4store(Ptr + str_length, n);
+ str_length += 4;
+ }
+ void q_append(double d)
+ {
+ float8store(Ptr + str_length, d);
+ str_length += 8;
+ }
+ void q_append(double *d)
+ {
+ float8store(Ptr + str_length, *d);
+ str_length += 8;
+ }
+ void q_append(const char *data, size_t data_len)
+ {
+ if (data_len)
+ memcpy(Ptr + str_length, data, data_len);
+ DBUG_ASSERT(str_length <= UINT_MAX32 - data_len);
+ str_length += (uint)data_len;
+ }
+ void q_append(const LEX_CSTRING *ls)
+ {
+ DBUG_ASSERT(ls->length < UINT_MAX32 &&
+ ((ls->length == 0 && !ls->str) ||
+ ls->length == strlen(ls->str)));
+ q_append(ls->str, (uint32) ls->length);
+ }
+
+ void write_at_position(int position, uint32 value)
+ {
+ int4store(Ptr + position,value);
+ }
+
+ void qs_append(const char *str)
+ {
+ qs_append(str, (uint32)strlen(str));
+ }
+ void qs_append(const LEX_CSTRING *ls)
+ {
+ DBUG_ASSERT(ls->length < UINT_MAX32 &&
+ ((ls->length == 0 && !ls->str) ||
+ ls->length == strlen(ls->str)));
+ qs_append(ls->str, (uint32)ls->length);
+ }
+ void qs_append(const char *str, size_t len);
+ void qs_append_hex(const char *str, uint32 len);
+ void qs_append(double d);
+ void qs_append(double *d);
+ inline void qs_append(const char c)
+ {
+ Ptr[str_length]= c;
+ str_length++;
+ }
+ void qs_append(int i);
+ void qs_append(uint i)
+ {
+ qs_append((ulonglong)i);
+ }
+ void qs_append(ulong i)
+ {
+ qs_append((ulonglong)i);
+ }
+ void qs_append(ulonglong i);
+ void qs_append(longlong i, int radix)
+ {
+ char *buff= Ptr + str_length;
+ char *end= ll2str(i, buff, radix, 0);
+ str_length+= uint32(end-buff);
+ }
+};
+
+
+class Binary_string: public Static_binary_string
+{
+ uint32 Alloced_length, extra_alloc;
+ bool alloced, thread_specific;
+ void init_private_data()
+ {
Alloced_length= extra_alloc= 0;
- alloced= thread_specific= 0;
- str_charset=cs;
+ alloced= thread_specific= false;
+ }
+public:
+ Binary_string()
+ {
+ init_private_data();
+ }
+ explicit Binary_string(size_t length_arg)
+ {
+ init_private_data();
+ (void) real_alloc(length_arg);
}
+ explicit Binary_string(const char *str)
+ :Binary_string(str, strlen(str))
+ { }
/*
NOTE: If one intend to use the c_ptr() method, the following two
contructors need the size of memory for STR to be at least LEN+1 (to make
room for zero termination).
*/
- String(const char *str,size_t len, CHARSET_INFO *cs)
- {
- Ptr=(char*) str; str_length=(uint32)len; Alloced_length= extra_alloc=0;
- alloced= thread_specific= 0;
- str_charset=cs;
+ Binary_string(const char *str, size_t len)
+ :Static_binary_string((char *) str, len)
+ {
+ init_private_data();
}
- String(char *str,size_t len, CHARSET_INFO *cs)
- {
- Ptr=(char*) str; Alloced_length=str_length=(uint32)len; extra_alloc= 0;
+ Binary_string(char *str, size_t len)
+ :Static_binary_string(str, len)
+ {
+ Alloced_length= (uint32) len;
+ extra_alloc= 0;
alloced= thread_specific= 0;
- str_charset=cs;
}
- String(const String &str)
- {
- Ptr=str.Ptr ; str_length=str.str_length ;
- Alloced_length=str.Alloced_length; extra_alloc= 0;
+ explicit Binary_string(const Binary_string &str)
+ :Static_binary_string(str)
+ {
+ Alloced_length= str.Alloced_length;
+ extra_alloc= 0;
alloced= thread_specific= 0;
- str_charset=str.str_charset;
}
- ~String() { free(); }
+
+ ~Binary_string() { free(); }
/* Mark variable thread specific it it's not allocated already */
inline void set_thread_specific()
@@ -207,158 +425,181 @@ public:
if (!alloced)
thread_specific= 1;
}
- inline void set_charset(CHARSET_INFO *charset_arg)
- { str_charset= charset_arg; }
- inline CHARSET_INFO *charset() const { return str_charset; }
- inline uint32 length() const { return str_length;}
+ bool is_alloced() const { return alloced; }
inline uint32 alloced_length() const { return Alloced_length;}
inline uint32 extra_allocation() const { return extra_alloc;}
- inline char& operator [] (size_t i) const { return Ptr[i]; }
- inline void length(size_t len) { str_length=(uint32)len ; }
inline void extra_allocation(size_t len) { extra_alloc= (uint32)len; }
- inline bool is_empty() const { return (str_length == 0); }
inline void mark_as_const() { Alloced_length= 0;}
- inline const char *ptr() const { return Ptr; }
- inline const char *end() const { return Ptr + str_length; }
- inline char *c_ptr()
- {
- DBUG_ASSERT(!alloced || !Ptr || !Alloced_length ||
- (Alloced_length >= (str_length + 1)));
- if (!Ptr || Ptr[str_length]) /* Should be safe */
- (void) realloc(str_length);
- return Ptr;
- }
- inline char *c_ptr_quick()
+ inline bool uses_buffer_owned_by(const Binary_string *s) const
{
- if (Ptr && str_length < Alloced_length)
- Ptr[str_length]=0;
- return Ptr;
+ return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length);
}
- inline char *c_ptr_safe()
+
+ /* Swap two string objects. Efficient way to exchange data without memcpy. */
+ void swap(Binary_string &s)
{
- if (Ptr && str_length < Alloced_length)
- Ptr[str_length]=0;
- else
- (void) realloc(str_length);
- return Ptr;
+ Static_binary_string::swap(s);
+ swap_variables(uint32, Alloced_length, s.Alloced_length);
+ swap_variables(bool, alloced, s.alloced);
}
- LEX_STRING lex_string() const
+
+ /**
+ Points the internal buffer to the supplied one. The old buffer is freed.
+ @param str Pointer to the new buffer.
+ @param arg_length Length of the new buffer in characters, excluding any
+ null character.
+ @note The new buffer will not be null terminated.
+ */
+ void set_alloced(char *str, size_t length_arg, size_t alloced_length_arg)
{
- LEX_STRING str = { (char*) ptr(), length() };
- return str;
+ free();
+ Static_binary_string::set(str, length_arg);
+ DBUG_ASSERT(alloced_length_arg < UINT_MAX32);
+ Alloced_length= (uint32) alloced_length_arg;
}
- LEX_CSTRING lex_cstring() const
+ inline void set(char *str, size_t arg_length)
{
- LEX_CSTRING skr = { ptr(), length() };
- return skr;
+ set_alloced(str, arg_length, arg_length);
}
-
- size_t lengthsp() const
+ inline void set(const char *str, size_t arg_length)
{
- return str_charset->cset->lengthsp(str_charset, Ptr, str_length);
+ free();
+ Static_binary_string::set((char *) str, arg_length);
}
- void set(String &str,size_t offset,size_t arg_length)
+ void set(Binary_string &str, size_t offset, size_t arg_length)
{
DBUG_ASSERT(&str != this);
free();
- Ptr=(char*) str.ptr()+offset; str_length=(uint32)arg_length;
+ Static_binary_string::set((char*) str.ptr() + offset, arg_length);
if (str.Alloced_length)
- Alloced_length=(uint32)(str.Alloced_length-offset);
- str_charset=str.str_charset;
+ Alloced_length= (uint32) (str.Alloced_length - offset);
}
-
- /**
- Points the internal buffer to the supplied one. The old buffer is freed.
- @param str Pointer to the new buffer.
- @param arg_length Length of the new buffer in characters, excluding any
- null character.
- @param cs Character set to use for interpreting string data.
- @note The new buffer will not be null terminated.
- */
- inline void set(char *str,size_t arg_length, CHARSET_INFO *cs)
+ /* Take over handling of buffer from some other object */
+ void reset(char *ptr_arg, size_t length_arg, size_t alloced_length_arg)
{
- free();
- Ptr=(char*) str; str_length=Alloced_length=(uint32)arg_length;
- str_charset=cs;
+ set_alloced(ptr_arg, length_arg, alloced_length_arg);
+ alloced= ptr_arg != 0;
}
- inline void set(const char *str,size_t arg_length, CHARSET_INFO *cs)
+
+ /* Forget about the buffer, let some other object handle it */
+ char *release()
{
- free();
- Ptr=(char*) str; str_length=(uint32)arg_length;
- str_charset=cs;
+ char *old= Ptr;
+ Static_binary_string::set(NULL, 0);
+ init_private_data();
+ return old;
}
- bool set_ascii(const char *str, size_t arg_length);
- inline void set_quick(char *str,size_t arg_length, CHARSET_INFO *cs)
+
+ inline void set_quick(char *str, size_t arg_length)
{
if (!alloced)
{
- Ptr=(char*) str; str_length=Alloced_length=(uint32)arg_length;
+ Static_binary_string::set(str, arg_length);
+ Alloced_length= (uint32) arg_length;
}
- str_charset=cs;
}
- bool set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs);
- bool set(int num, CHARSET_INFO *cs) { return set_int(num, false, cs); }
- bool set(uint num, CHARSET_INFO *cs) { return set_int(num, true, cs); }
- bool set(long num, CHARSET_INFO *cs) { return set_int(num, false, cs); }
- bool set(ulong num, CHARSET_INFO *cs) { return set_int(num, true, cs); }
- bool set(longlong num, CHARSET_INFO *cs) { return set_int(num, false, cs); }
- bool set(ulonglong num, CHARSET_INFO *cs) { return set_int((longlong)num, true, cs); }
- bool set_real(double num,uint decimals, CHARSET_INFO *cs);
+
+ inline Binary_string& operator=(const Binary_string &s)
+ {
+ if (&s != this)
+ {
+ /*
+ It is forbidden to do assignments like
+ some_string = substring_of_that_string
+ */
+ DBUG_ASSERT(!s.uses_buffer_owned_by(this));
+ set_alloced((char *) s.Ptr, s.str_length, s.Alloced_length);
+ }
+ return *this;
+ }
bool set_hex(ulonglong num);
bool set_hex(const char *str, uint32 len);
+ bool set_fcvt(double num, uint decimals);
- /* Take over handling of buffer from some other object */
- void reset(char *ptr_arg, size_t length_arg, size_t alloced_length_arg,
- CHARSET_INFO *cs)
- {
- free();
- Ptr= ptr_arg;
- str_length= (uint32)length_arg;
- Alloced_length= (uint32)alloced_length_arg;
- str_charset= cs;
- alloced= ptr_arg != 0;
- }
+ bool copy(); // Alloc string if not alloced
+ bool copy(const Binary_string &s); // Allocate new string
+ bool copy(const char *s, size_t arg_length); // Allocate new string
+ bool copy_or_move(const char *s,size_t arg_length);
- /* Forget about the buffer, let some other object handle it */
- char *release()
+ bool append_ulonglong(ulonglong val);
+ bool append_longlong(longlong val);
+
+ bool append(const char *s, size_t size)
{
- char *old= Ptr;
- Ptr=0; str_length= Alloced_length= extra_alloc= 0;
- alloced= thread_specific= 0;
- return old;
+ if (!size)
+ return false;
+ if (realloc_with_extra_if_needed(str_length + size))
+ return true;
+ q_append(s, size);
+ return false;
+ }
+ bool append(const Binary_string &s)
+ {
+ return append(s.ptr(), s.length());
}
+ bool append(IO_CACHE* file, uint32 arg_length);
- /*
- PMG 2004.11.12
- This is a method that works the same as perl's "chop". It simply
- drops the last character of a string. This is useful in the case
- of the federated storage handler where I'm building a unknown
- number, list of values and fields to be used in a sql insert
- statement to be run on the remote server, and have a comma after each.
- When the list is complete, I "chop" off the trailing comma
+ inline bool append_char(char chr)
+ {
+ if (str_length < Alloced_length)
+ {
+ Ptr[str_length++]= chr;
+ }
+ else
+ {
+ if (unlikely(realloc_with_extra(str_length + 1)))
+ return true;
+ Ptr[str_length++]= chr;
+ }
+ return false;
+ }
+ bool append_hex(const char *src, uint32 srclen)
+ {
+ for (const char *src_end= src + srclen ; src != src_end ; src++)
+ {
+ if (unlikely(append_char(_dig_vec_lower[((uchar) *src) >> 4])) ||
+ unlikely(append_char(_dig_vec_lower[((uchar) *src) & 0x0F])))
+ return true;
+ }
+ return false;
+ }
- ex.
- String stringobj;
- stringobj.append("VALUES ('foo', 'fi', 'fo',");
- stringobj.chop();
- stringobj.append(")");
+ bool append_with_step(const char *s, uint32 arg_length, uint32 step_alloc)
+ {
+ uint32 new_length= arg_length + str_length;
+ if (new_length > Alloced_length &&
+ unlikely(realloc(new_length + step_alloc)))
+ return true;
+ q_append(s, arg_length);
+ return false;
+ }
- In this case, the value of string was:
+ inline char *c_ptr()
+ {
+ DBUG_ASSERT(!alloced || !Ptr || !Alloced_length ||
+ (Alloced_length >= (str_length + 1)));
- VALUES ('foo', 'fi', 'fo',
- VALUES ('foo', 'fi', 'fo'
- VALUES ('foo', 'fi', 'fo')
-
- */
- inline void chop()
+ if (!Ptr || Ptr[str_length]) // Should be safe
+ (void) realloc(str_length);
+ return Ptr;
+ }
+ inline char *c_ptr_quick()
{
- str_length--;
- Ptr[str_length]= '\0';
- DBUG_ASSERT(strlen(Ptr) == str_length);
+ if (Ptr && str_length < Alloced_length)
+ Ptr[str_length]=0;
+ return Ptr;
+ }
+ inline char *c_ptr_safe()
+ {
+ if (Ptr && str_length < Alloced_length)
+ Ptr[str_length]=0;
+ else
+ (void) realloc(str_length);
+ return Ptr;
}
inline void free()
@@ -369,8 +610,7 @@ public:
my_free(Ptr);
}
Alloced_length= extra_alloc= 0;
- Ptr=0;
- str_length=0; /* Safety */
+ Static_binary_string::set(NULL, 0); // Safety
}
inline bool alloc(size_t arg_length)
{
@@ -378,13 +618,13 @@ public:
return 0;
return real_alloc(arg_length);
}
- bool real_alloc(size_t arg_length); // Empties old string
+ bool real_alloc(size_t arg_length); // Empties old string
bool realloc_raw(size_t arg_length);
bool realloc(size_t arg_length)
{
if (realloc_raw(arg_length))
return TRUE;
- Ptr[arg_length]=0; // This make other funcs shorter
+ Ptr[arg_length]= 0; // This make other funcs shorter
return FALSE;
}
bool realloc_with_extra(size_t arg_length)
@@ -418,37 +658,185 @@ public:
arg_length,MYF((thread_specific ?
MY_THREAD_SPECIFIC : 0))))))
{
- Alloced_length = 0;
- real_alloc(arg_length);
+ Alloced_length= 0;
+ real_alloc(arg_length);
}
else
{
- Ptr=new_ptr;
- Alloced_length=(uint32)arg_length;
+ Ptr= new_ptr;
+ Alloced_length= (uint32) arg_length;
}
}
}
- bool is_alloced() const { return alloced; }
+ void move(Binary_string &s)
+ {
+ set_alloced(s.Ptr, s.str_length, s.Alloced_length);
+ extra_alloc= s.extra_alloc;
+ alloced= s.alloced;
+ thread_specific= s.thread_specific;
+ s.alloced= 0;
+ }
+ bool fill(uint32 max_length,char fill);
+ /*
+ Replace substring with string
+ If wrong parameter or not enough memory, do nothing
+ */
+ bool replace(uint32 offset,uint32 arg_length, const char *to, uint32 length);
+ bool replace(uint32 offset,uint32 arg_length, const Static_binary_string &to)
+ {
+ return replace(offset,arg_length,to.ptr(),to.length());
+ }
+
+ int reserve(size_t space_needed)
+ {
+ DBUG_ASSERT((ulonglong) str_length + space_needed < UINT_MAX32);
+ return realloc(str_length + space_needed);
+ }
+ int reserve(size_t space_needed, size_t grow_by);
+
+ inline char *prep_append(uint32 arg_length, uint32 step_alloc)
+ {
+ uint32 new_length= arg_length + str_length;
+ if (new_length > Alloced_length)
+ {
+ if (unlikely(realloc(new_length + step_alloc)))
+ return 0;
+ }
+ uint32 old_length= str_length;
+ str_length+= arg_length;
+ return Ptr + old_length; // Area to use
+ }
+
+
+ void q_net_store_length(ulonglong length)
+ {
+ DBUG_ASSERT(Alloced_length >= (str_length + net_length_size(length)));
+ char *pos= (char *) net_store_length((uchar *)(Ptr + str_length), length);
+ str_length= uint32(pos - Ptr);
+ }
+ void q_net_store_data(const uchar *from, size_t length)
+ {
+ DBUG_ASSERT(length < UINT_MAX32);
+ DBUG_ASSERT(Alloced_length >= (str_length + length +
+ net_length_size(length)));
+ q_net_store_length(length);
+ q_append((const char *)from, (uint32) length);
+ }
+};
+
+
+class String: public Charset, public Binary_string
+{
+public:
+ String() { }
+ String(size_t length_arg)
+ :Binary_string(length_arg)
+ { }
+ String(const char *str, CHARSET_INFO *cs)
+ :Charset(cs),
+ Binary_string(str)
+ { }
+ /*
+ NOTE: If one intend to use the c_ptr() method, the following two
+ contructors need the size of memory for STR to be at least LEN+1 (to make
+ room for zero termination).
+ */
+ String(const char *str, size_t len, CHARSET_INFO *cs)
+ :Charset(cs),
+ Binary_string((char *) str, len)
+ { }
+ String(char *str, size_t len, CHARSET_INFO *cs)
+ :Charset(cs),
+ Binary_string(str, len)
+ { }
+ String(const String &str)
+ :Charset(str),
+ Binary_string(str)
+ { }
+
+ void set(String &str,size_t offset,size_t arg_length)
+ {
+ Binary_string::set(str, offset, arg_length);
+ set_charset(str);
+ }
+ inline void set(char *str,size_t arg_length, CHARSET_INFO *cs)
+ {
+ Binary_string::set(str, arg_length);
+ set_charset(cs);
+ }
+ inline void set(const char *str,size_t arg_length, CHARSET_INFO *cs)
+ {
+ Binary_string::set(str, arg_length);
+ set_charset(cs);
+ }
+ bool set_ascii(const char *str, size_t arg_length);
+ inline void set_quick(char *str,size_t arg_length, CHARSET_INFO *cs)
+ {
+ Binary_string::set_quick(str, arg_length);
+ set_charset(cs);
+ }
+ bool set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs);
+ bool set(int num, CHARSET_INFO *cs) { return set_int(num, false, cs); }
+ bool set(uint num, CHARSET_INFO *cs) { return set_int(num, true, cs); }
+ bool set(long num, CHARSET_INFO *cs) { return set_int(num, false, cs); }
+ bool set(ulong num, CHARSET_INFO *cs) { return set_int(num, true, cs); }
+ bool set(longlong num, CHARSET_INFO *cs) { return set_int(num, false, cs); }
+ bool set(ulonglong num, CHARSET_INFO *cs) { return set_int((longlong)num, true, cs); }
+ bool set_real(double num,uint decimals, CHARSET_INFO *cs);
+ bool set_fcvt(double num, uint decimals)
+ {
+ set_charset(&my_charset_latin1);
+ return Binary_string::set_fcvt(num, decimals);
+ }
+
+ bool set_hex(ulonglong num)
+ {
+ set_charset(&my_charset_latin1);
+ return Binary_string::set_hex(num);
+ }
+ bool set_hex(const char *str, uint32 len)
+ {
+ set_charset(&my_charset_latin1);
+ return Binary_string::set_hex(str, len);
+ }
+
+ /* Take over handling of buffer from some other object */
+ void reset(char *ptr_arg, size_t length_arg, size_t alloced_length_arg,
+ CHARSET_INFO *cs)
+ {
+ Binary_string::reset(ptr_arg, length_arg, alloced_length_arg);
+ set_charset(cs);
+ }
+
inline String& operator = (const String &s)
{
if (&s != this)
{
- /*
- It is forbidden to do assignments like
- some_string = substring_of_that_string
- */
- DBUG_ASSERT(!s.uses_buffer_owned_by(this));
- free();
- Ptr=s.Ptr ; str_length=s.str_length ; Alloced_length=s.Alloced_length;
- str_charset=s.str_charset;
+ set_charset(s);
+ Binary_string::operator=(s);
}
return *this;
}
- bool copy(); // Alloc string if not alloced
- bool copy(const String &s); // Allocate new string
- bool copy(const char *s,size_t arg_length, CHARSET_INFO *cs); // Allocate new string
- bool copy_or_move(const char *s,size_t arg_length, CHARSET_INFO *cs);
+ bool copy()
+ {
+ return Binary_string::copy();
+ }
+ bool copy(const String &s)
+ {
+ set_charset(s);
+ return Binary_string::copy(s);
+ }
+ bool copy(const char *s, size_t arg_length, CHARSET_INFO *cs)
+ {
+ set_charset(cs);
+ return Binary_string::copy(s, arg_length);
+ }
+ bool copy_or_move(const char *s, size_t arg_length, CHARSET_INFO *cs)
+ {
+ set_charset(cs);
+ return Binary_string::copy_or_move(s, arg_length);
+ }
static bool needs_conversion(size_t arg_length,
CHARSET_INFO *cs_from, CHARSET_INFO *cs_to,
uint32 *offset);
@@ -470,211 +858,92 @@ public:
{
if (unlikely(alloc(tocs->mbmaxlen * src_length)))
return true;
- str_length= copier->well_formed_copy(tocs, Ptr, Alloced_length,
+ str_length= copier->well_formed_copy(tocs, Ptr, alloced_length(),
fromcs, src, (uint)src_length, (uint)nchars);
- str_charset= tocs;
+ set_charset(tocs);
return false;
}
- void move(String &s)
+ // Append without character set conversion
+ bool append(const String &s)
{
- free();
- Ptr=s.Ptr ; str_length=s.str_length ; Alloced_length=s.Alloced_length;
- extra_alloc= s.extra_alloc;
- alloced= s.alloced;
- thread_specific= s.thread_specific;
- s.alloced= 0;
+ return Binary_string::append(s);
}
- bool append(const String &s);
- bool append(const char *s);
- bool append(const LEX_STRING *ls)
- {
- DBUG_ASSERT(ls->length < UINT_MAX32 &&
- ((ls->length == 0 && !ls->str) ||
- ls->length == strlen(ls->str)));
- return append(ls->str, (uint32) ls->length);
- }
- bool append(const LEX_CSTRING *ls)
- {
- DBUG_ASSERT(ls->length < UINT_MAX32 &&
- ((ls->length == 0 && !ls->str) ||
- ls->length == strlen(ls->str)));
- return append(ls->str, (uint32) ls->length);
- }
- bool append(const LEX_CSTRING &ls)
- {
- return append(&ls);
- }
- bool append(const char *s, size_t size);
- bool append(const char *s, size_t arg_length, CHARSET_INFO *cs);
- bool append(const LEX_CSTRING &s, CHARSET_INFO *cs)
- {
- return append(s.str, s.length, cs);
- }
- bool append_ulonglong(ulonglong val);
- bool append_longlong(longlong val);
- bool append(IO_CACHE* file, uint32 arg_length);
- bool append_with_prefill(const char *s, uint32 arg_length,
- uint32 full_length, char fill_char);
- bool append_parenthesized(long nr, int radix= 10);
- int strstr(const String &search,uint32 offset=0); // Returns offset to substring or -1
- int strrstr(const String &search,uint32 offset=0); // Returns offset to substring or -1
- bool replace(uint32 offset,uint32 arg_length,const char *to,uint32 length);
- bool replace(uint32 offset,uint32 arg_length,const String &to);
inline bool append(char chr)
{
- if (str_length < Alloced_length)
- {
- Ptr[str_length++]=chr;
- }
- else
- {
- if (unlikely(realloc_with_extra(str_length + 1)))
- return 1;
- Ptr[str_length++]=chr;
- }
- return 0;
+ return Binary_string::append_char(chr);
}
bool append_hex(const char *src, uint32 srclen)
{
- for (const char *src_end= src + srclen ; src != src_end ; src++)
- {
- if (unlikely(append(_dig_vec_lower[((uchar) *src) >> 4])) ||
- unlikely(append(_dig_vec_lower[((uchar) *src) & 0x0F])))
- return true;
- }
- return false;
+ return Binary_string::append_hex(src, srclen);
}
bool append_hex(const uchar *src, uint32 srclen)
{
- return append_hex((const char*)src, srclen);
- }
- bool fill(uint32 max_length,char fill);
- void strip_sp();
- friend int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
- friend int stringcmp(const String *a,const String *b);
- friend String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
- friend class Field;
- uint32 numchars() const;
- int charpos(longlong i,uint32 offset=0);
-
- int reserve(size_t space_needed)
- {
- return realloc(str_length + space_needed);
+ return Binary_string::append_hex((const char*)src, srclen);
}
- int reserve(size_t space_needed, size_t grow_by);
-
- /*
- The following append operations do NOT check alloced memory
- q_*** methods writes values of parameters itself
- qs_*** methods writes string representation of value
- */
- void q_append(const char c)
- {
- Ptr[str_length++] = c;
- }
- void q_append2b(const uint32 n)
+ bool append(IO_CACHE* file, uint32 arg_length)
{
- int2store(Ptr + str_length, n);
- str_length += 2;
+ return Binary_string::append(file, arg_length);
}
- void q_append(const uint32 n)
+ inline bool append(const char *s, uint32 arg_length, uint32 step_alloc)
{
- int4store(Ptr + str_length, n);
- str_length += 4;
+ return append_with_step(s, arg_length, step_alloc);
}
- void q_append(double d)
- {
- float8store(Ptr + str_length, d);
- str_length += 8;
- }
- void q_append(double *d)
- {
- float8store(Ptr + str_length, *d);
- str_length += 8;
- }
- void q_append(const char *data, size_t data_len)
+
+ // Append with optional character set conversion from ASCII (e.g. to UCS2)
+ bool append(const char *s)
{
- if (data_len)
- memcpy(Ptr + str_length, data, data_len);
- DBUG_ASSERT(str_length <= UINT_MAX32 - data_len);
- str_length += (uint)data_len;
+ return append(s, strlen(s));
}
- void q_append(const LEX_CSTRING *ls)
+ bool append(const LEX_STRING *ls)
{
DBUG_ASSERT(ls->length < UINT_MAX32 &&
((ls->length == 0 && !ls->str) ||
ls->length == strlen(ls->str)));
- q_append(ls->str, (uint32) ls->length);
- }
-
- void write_at_position(int position, uint32 value)
- {
- int4store(Ptr + position,value);
- }
-
- void qs_append(const char *str)
- {
- qs_append(str, (uint32)strlen(str));
+ return append(ls->str, (uint32) ls->length);
}
- void qs_append(const LEX_CSTRING *ls)
+ bool append(const LEX_CSTRING *ls)
{
DBUG_ASSERT(ls->length < UINT_MAX32 &&
((ls->length == 0 && !ls->str) ||
ls->length == strlen(ls->str)));
- qs_append(ls->str, (uint32)ls->length);
+ return append(ls->str, (uint32) ls->length);
}
- void qs_append(const char *str, size_t len);
- void qs_append_hex(const char *str, uint32 len);
- void qs_append(double d);
- void qs_append(double *d);
- inline void qs_append(const char c)
+ bool append(const LEX_CSTRING &ls)
{
- Ptr[str_length]= c;
- str_length++;
+ return append(&ls);
}
- void qs_append(int i);
- void qs_append(uint i)
+ bool append(const char *s, size_t size);
+ bool append_with_prefill(const char *s, uint32 arg_length,
+ uint32 full_length, char fill_char);
+ bool append_parenthesized(long nr, int radix= 10);
+
+ // Append with optional character set conversion from cs to charset()
+ bool append(const char *s, size_t arg_length, CHARSET_INFO *cs);
+ bool append(const LEX_CSTRING &s, CHARSET_INFO *cs)
{
- qs_append((ulonglong)i);
+ return append(s.str, s.length, cs);
}
- void qs_append(ulong i)
+
+ void strip_sp();
+ friend int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
+ friend int stringcmp(const String *a,const String *b);
+ friend String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
+ friend class Field;
+ uint32 numchars() const
{
- qs_append((ulonglong)i);
+ return (uint32) Charset::numchars(ptr(), end());
}
- void qs_append(ulonglong i);
- void qs_append(longlong i, int radix)
+ int charpos(longlong i, uint32 offset=0)
{
- char *buff= Ptr + str_length;
- char *end= ll2str(i, buff, radix, 0);
- str_length+= uint32(end-buff);
+ if (i <= 0)
+ return (int) i;
+ return (int) Charset::charpos(ptr() + offset, end(), (size_t) i);
}
-
- /* Inline (general) functions used by the protocol functions */
-
- inline char *prep_append(uint32 arg_length, uint32 step_alloc)
+ size_t lengthsp() const
{
- uint32 new_length= arg_length + str_length;
- if (new_length > Alloced_length)
- {
- if (unlikely(realloc(new_length + step_alloc)))
- return 0;
- }
- uint32 old_length= str_length;
- str_length+= arg_length;
- return Ptr+ old_length; /* Area to use */
+ return Charset::lengthsp(Ptr, str_length);
}
-
- inline bool append(const char *s, uint32 arg_length, uint32 step_alloc)
- {
- uint32 new_length= arg_length + str_length;
- if (new_length > Alloced_length &&
- unlikely(realloc(new_length + step_alloc)))
- return TRUE;
- memcpy(Ptr+str_length, s, arg_length);
- str_length+= arg_length;
- return FALSE;
- }
void print(String *to) const;
void print_with_conversion(String *to, CHARSET_INFO *cs) const;
void print(String *to, CHARSET_INFO *cs) const
@@ -697,13 +966,12 @@ public:
return append_for_single_quote(st, (uint32) len);
}
- /* Swap two string objects. Efficient way to exchange data without memcpy. */
- void swap(String &s);
-
- inline bool uses_buffer_owned_by(const String *s) const
+ void swap(String &s)
{
- return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length);
+ Charset::swap(s);
+ Binary_string::swap(s);
}
+
uint well_formed_length() const
{
return (uint) Well_formed_prefix(charset(), ptr(), length()).length();
@@ -714,36 +982,14 @@ public:
return TRUE;
if (charset()->mbminlen > 1)
return FALSE;
- for (const char *c= ptr(), *c_end= c + length(); c < c_end; c++)
- {
- if (!my_isascii(*c))
- return FALSE;
- }
- return TRUE;
- }
- bool bin_eq(const String *other) const
- {
- return length() == other->length() &&
- !memcmp(ptr(), other->ptr(), length());
+ return !has_8bit_bytes();
}
bool eq(const String *other, CHARSET_INFO *cs) const
{
return !sortcmp(this, other, cs);
}
- void q_net_store_length(ulonglong length)
- {
- DBUG_ASSERT(Alloced_length >= (str_length + net_length_size(length)));
- char *pos= (char *) net_store_length((uchar *)(Ptr + str_length), length);
- str_length= uint32(pos - Ptr);
- }
- void q_net_store_data(const uchar *from, size_t length)
- {
- DBUG_ASSERT(length < UINT_MAX32);
- DBUG_ASSERT(Alloced_length >= (str_length + length +
- net_length_size(length)));
- q_net_store_length(length);
- q_append((const char *)from, (uint32) length);
- }
+private:
+ bool append_semi_hex(const char *s, uint len, CHARSET_INFO *cs);
};
@@ -773,8 +1019,19 @@ public:
};
+class String_space: public String
+{
+public:
+ String_space(uint n)
+ {
+ if (fill(n, ' '))
+ set("", 0, &my_charset_bin);
+ }
+};
+
+
static inline bool check_if_only_end_space(CHARSET_INFO *cs,
- const char *str,
+ const char *str,
const char *end)
{
return str+ cs->cset->scan(cs, str, end, MY_SEQ_SPACES) == end;
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index c70639d71db..8fe1f34256c 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -33,7 +33,6 @@
// partition_info
// NOT_A_PARTITION_ID
#include "sql_db.h" // load_db_opt_by_name
-#include "sql_time.h" // make_truncated_value_warning
#include "records.h" // init_read_record, end_read_record
#include "filesort.h" // filesort_free_buffers
#include "sql_select.h" // setup_order
@@ -55,7 +54,7 @@
#include "sql_audit.h"
#include "sql_sequence.h"
#include "tztime.h"
-
+#include <algorithm>
#ifdef __WIN__
#include <io.h>
@@ -64,29 +63,23 @@
const char *primary_key_name="PRIMARY";
static int check_if_keyname_exists(const char *name,KEY *start, KEY *end);
-static char *make_unique_key_name(THD *thd, const char *field_name, KEY *start,
- KEY *end);
-static bool make_unique_constraint_name(THD *thd, LEX_CSTRING *name,
- List<Virtual_column_info> *vcol,
- uint *nr);
-static const
-char * make_unique_invisible_field_name(THD *thd, const char *field_name,
- List<Create_field> *fields);
-
-static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
- List<Create_field> &create, bool ignore,
- uint order_num, ORDER *order,
- ha_rows *copied,ha_rows *deleted,
- Alter_info::enum_enable_or_disable keys_onoff,
- Alter_table_ctx *alter_ctx);
-
+static char *make_unique_key_name(THD *, const char *, KEY *, KEY *);
+static bool make_unique_constraint_name(THD *, LEX_CSTRING *, const char *,
+ List<Virtual_column_info> *, uint *);
+static const char *make_unique_invisible_field_name(THD *, const char *,
+ List<Create_field> *);
+static int copy_data_between_tables(THD *, TABLE *,TABLE *,
+ List<Create_field> &, bool, uint, ORDER *,
+ ha_rows *, ha_rows *,
+ Alter_info::enum_enable_or_disable,
+ Alter_table_ctx *);
static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *,
uint *, handler *, KEY **, uint *, int,
const LEX_CSTRING db,
const LEX_CSTRING table_name);
static uint blob_length_by_type(enum_field_types type);
-static bool fix_constraints_names(THD *thd, List<Virtual_column_info>
- *check_constraint_list);
+static bool fix_constraints_names(THD *, List<Virtual_column_info> *,
+ const HA_CREATE_INFO *);
/**
@brief Helper function for explain_filename
@@ -1853,7 +1846,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
#endif
/* Write shadow frm file */
lpt->create_info->table_options= lpt->db_options;
- LEX_CUSTRING frm= build_frm_image(lpt->thd, &lpt->table_name,
+ LEX_CUSTRING frm= build_frm_image(lpt->thd, lpt->table_name,
lpt->create_info,
lpt->alter_info->create_list,
lpt->key_count, lpt->key_info_buffer,
@@ -2045,18 +2038,6 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists,
if (!drop_temporary)
{
- if (!in_bootstrap)
- {
- for (table= tables; table; table= table->next_local)
- {
- LEX_CSTRING db_name= table->db;
- LEX_CSTRING table_name= table->table_name;
- if (table->open_type == OT_BASE_ONLY ||
- !thd->find_temporary_table(table))
- (void) delete_statistics_for_table(thd, &db_name, &table_name);
- }
- }
-
if (!thd->locked_tables_mode)
{
if (drop_sequence)
@@ -2120,6 +2101,15 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists,
}
}
}
+ /* We remove statistics for table last, after we have the DDL lock */
+ for (table= tables; table; table= table->next_local)
+ {
+ LEX_CSTRING db_name= table->db;
+ LEX_CSTRING table_name= table->table_name;
+ if (table->open_type == OT_BASE_ONLY ||
+ !thd->find_temporary_table(table))
+ (void) delete_statistics_for_table(thd, &db_name, &table_name);
+ }
}
DBUG_EXECUTE_IF("ib_purge_virtual_mdev_16222_1",
@@ -2137,7 +2127,6 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists,
DBUG_RETURN(TRUE);
my_ok(thd);
DBUG_RETURN(FALSE);
-
}
@@ -2655,9 +2644,6 @@ err:
/* Chop of the last comma */
built_non_trans_tmp_query.chop();
built_non_trans_tmp_query.append(" /* generated by server */");
-#ifdef WITH_WSREP
- thd->wsrep_skip_wsrep_GTID = true;
-#endif /* WITH_WSREP */
error |= (thd->binlog_query(THD::STMT_QUERY_TYPE,
built_non_trans_tmp_query.ptr(),
built_non_trans_tmp_query.length(),
@@ -2670,9 +2656,6 @@ err:
/* Chop of the last comma */
built_trans_tmp_query.chop();
built_trans_tmp_query.append(" /* generated by server */");
-#ifdef WITH_WSREP
- thd->wsrep_skip_wsrep_GTID = true;
-#endif /* WITH_WSREP */
error |= (thd->binlog_query(THD::STMT_QUERY_TYPE,
built_trans_tmp_query.ptr(),
built_trans_tmp_query.length(),
@@ -2687,9 +2670,6 @@ err:
built_query.append(" /* generated by server */");
int error_code = non_tmp_error ? thd->get_stmt_da()->sql_errno()
: 0;
-#ifdef WITH_WSREP
- thd->wsrep_skip_wsrep_GTID = false;
-#endif /* WITH_WSREP */
error |= (thd->binlog_query(THD::STMT_QUERY_TYPE,
built_query.ptr(),
built_query.length(),
@@ -2738,9 +2718,6 @@ err:
}
end:
-#ifdef WITH_WSREP
- thd->wsrep_skip_wsrep_GTID = false;
-#endif /* WITH_WSREP */
DBUG_RETURN(error);
}
@@ -2838,6 +2815,7 @@ bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db,
- UNIQUE keys where all column are NOT NULL
- UNIQUE keys that don't contain partial segments
- Other UNIQUE keys
+ - LONG UNIQUE keys
- Normal keys
- Fulltext keys
@@ -2849,10 +2827,26 @@ static int sort_keys(KEY *a, KEY *b)
{
ulong a_flags= a->flags, b_flags= b->flags;
+ /*
+ Do not reorder LONG_HASH indexes, because they must match the order
+ of their LONG_UNIQUE_HASH_FIELD's.
+ */
+ if (a->algorithm == HA_KEY_ALG_LONG_HASH &&
+ b->algorithm == HA_KEY_ALG_LONG_HASH)
+ return a->usable_key_parts - b->usable_key_parts;
+
if (a_flags & HA_NOSAME)
{
if (!(b_flags & HA_NOSAME))
return -1;
+ /*
+ Long Unique keys should always be last unique key.
+ Before this patch they used to change order wrt to partial keys (MDEV-19049)
+ */
+ if (a->algorithm == HA_KEY_ALG_LONG_HASH)
+ return 1;
+ if (b->algorithm == HA_KEY_ALG_LONG_HASH)
+ return -1;
if ((a_flags ^ b_flags) & HA_NULL_PART_KEY)
{
/* Sort NOT NULL keys before other keys */
@@ -2877,9 +2871,7 @@ static int sort_keys(KEY *a, KEY *b)
Prefer original key order. usable_key_parts contains here
the original key position.
*/
- return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
- (a->usable_key_parts > b->usable_key_parts) ? 1 :
- 0);
+ return a->usable_key_parts - b->usable_key_parts;
}
/*
@@ -2936,6 +2928,15 @@ bool check_duplicates_in_interval(const char *set_or_name,
}
+bool Column_definition::
+ prepare_charset_for_string(const Column_derived_attributes *dattr)
+{
+ if (!charset)
+ charset= dattr->charset();
+ return (flags & BINCMP_FLAG) && !(charset= find_bin_collation(charset));
+}
+
+
bool Column_definition::prepare_stage2_blob(handler *file,
ulonglong table_flags,
uint field_flags)
@@ -3017,44 +3018,13 @@ bool Column_definition::prepare_stage2(handler *file,
}
-/*
- Get character set from field object generated by parser using
- default values when not set.
-
- SYNOPSIS
- get_sql_field_charset()
- sql_field The sql_field object
- create_info Info generated by parser
-
- RETURN VALUES
- cs Character set
-*/
-
-CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field,
- HA_CREATE_INFO *create_info)
-{
- CHARSET_INFO *cs= sql_field->charset;
-
- if (!cs)
- cs= create_info->default_table_charset;
- /*
- table_charset is set only in ALTER TABLE t1 CONVERT TO CHARACTER SET csname
- if we want change character set for all varchar/char columns.
- But the table charset must not affect the BLOB fields, so don't
- allow to change my_charset_bin to somethig else.
- */
- if (create_info->table_charset && cs != &my_charset_bin)
- cs= create_info->table_charset;
- return cs;
-}
-
-
/**
Modifies the first column definition whose SQL type is TIMESTAMP
by adding the features DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP.
If the first TIMESTAMP column appears to be nullable, or to have an
- explicit default, or to be a virtual column, then no promition is done.
+ explicit default, or to be a virtual column, or to be part of table period,
+ then no promotion is done.
@param column_definitions The list of column definitions, in the physical
order in which they appear in the table.
@@ -3062,33 +3032,36 @@ CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field,
void promote_first_timestamp_column(List<Create_field> *column_definitions)
{
- List_iterator_fast<Create_field> it(*column_definitions);
- Create_field *column_definition;
-
- while ((column_definition= it++) != NULL)
+ for (Create_field &column_definition : *column_definitions)
{
- if (column_definition->is_timestamp_type() || // TIMESTAMP
- column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy
+ if (column_definition.is_timestamp_type() || // TIMESTAMP
+ column_definition.unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy
{
- DBUG_PRINT("info", ("field-ptr:%p", column_definition->field));
- if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL,
- column_definition->default_value == NULL && // no constant default,
- column_definition->unireg_check == Field::NONE && // no function default
- column_definition->vcol_info == NULL &&
- !(column_definition->flags & VERS_SYSTEM_FIELD)) // column isn't generated
+ DBUG_PRINT("info", ("field-ptr:%p", column_definition.field));
+ if ((column_definition.flags & NOT_NULL_FLAG) != 0 && // NOT NULL,
+ column_definition.default_value == NULL && // no constant default,
+ column_definition.unireg_check == Field::NONE && // no function default
+ column_definition.vcol_info == NULL &&
+ column_definition.period == NULL &&
+ !(column_definition.flags & VERS_SYSTEM_FIELD)) // column isn't generated
{
DBUG_PRINT("info", ("First TIMESTAMP column '%s' was promoted to "
"DEFAULT CURRENT_TIMESTAMP ON UPDATE "
"CURRENT_TIMESTAMP",
- column_definition->field_name.str
+ column_definition.field_name.str
));
- column_definition->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
+ column_definition.unireg_check= Field::TIMESTAMP_DNUN_FIELD;
}
return;
}
}
}
+static bool key_cmp(const Key_part_spec &a, const Key_part_spec &b)
+{
+ return a.length == b.length &&
+ !lex_string_cmp(system_charset_info, &a.field_name, &b.field_name);
+}
/**
Check if there is a duplicate key. Report a warning for every duplicate key.
@@ -3098,8 +3071,8 @@ void promote_first_timestamp_column(List<Create_field> *column_definitions)
@param key_info Key meta-data info.
@param key_list List of existing keys.
*/
-static void check_duplicate_key(THD *thd, Key *key, KEY *key_info,
- List<Key> *key_list)
+static void check_duplicate_key(THD *thd, const Key *key, const KEY *key_info,
+ const List<Key> *key_list)
{
/*
We only check for duplicate indexes if it is requested and the
@@ -3111,56 +3084,28 @@ static void check_duplicate_key(THD *thd, Key *key, KEY *key_info,
if (!key->key_create_info.check_for_duplicate_indexes || key->generated)
return;
- List_iterator_fast<Key> key_list_iterator(*key_list);
- List_iterator_fast<Key_part_spec> key_column_iterator(key->columns);
- Key *k;
-
- while ((k= key_list_iterator++))
+ for (const Key &k : *key_list)
{
// Looking for a similar key...
- if (k == key)
+ if (&k == key)
break;
- if (k->generated ||
- (key->type != k->type) ||
- (key->key_create_info.algorithm != k->key_create_info.algorithm) ||
- (key->columns.elements != k->columns.elements))
+ if (k.generated ||
+ (key->type != k.type) ||
+ (key->key_create_info.algorithm != k.key_create_info.algorithm) ||
+ (key->columns.elements != k.columns.elements))
{
// Keys are different.
continue;
}
- /*
- Keys 'key' and 'k' might be identical.
- Check that the keys have identical columns in the same order.
- */
-
- List_iterator_fast<Key_part_spec> k_column_iterator(k->columns);
- uint i;
- key_column_iterator.rewind();
-
- for (i= 0; i < key->columns.elements; ++i)
- {
- Key_part_spec *c1= key_column_iterator++;
- Key_part_spec *c2= k_column_iterator++;
-
- DBUG_ASSERT(c1 && c2);
-
- if (lex_string_cmp(system_charset_info,
- &c1->field_name, &c2->field_name) ||
- (c1->length != c2->length))
- break;
- }
-
- // Report a warning if we have two identical keys.
-
- if (i == key->columns.elements)
+ if (std::equal(key->columns.begin(), key->columns.end(), k.columns.begin(),
+ key_cmp))
{
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_DUP_INDEX, ER_THD(thd, ER_DUP_INDEX),
- key_info->name.str);
- break;
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DUP_INDEX,
+ ER_THD(thd, ER_DUP_INDEX), key_info->name.str);
+ return;
}
}
}
@@ -3245,11 +3190,14 @@ bool Column_definition::prepare_stage1_bit(THD *thd,
bool Column_definition::prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
handler *file,
- ulonglong table_flags)
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
{
return type_handler()->Column_definition_prepare_stage1(thd, mem_root,
this, file,
- table_flags);
+ table_flags,
+ derived_attr);
}
@@ -3355,6 +3303,55 @@ int mysql_add_invisible_field(THD *thd, List<Create_field> * field_list,
return 0;
}
+#define LONG_HASH_FIELD_NAME_LENGTH 30
+static inline void make_long_hash_field_name(LEX_CSTRING *buf, uint num)
+{
+ buf->length= my_snprintf((char *)buf->str,
+ LONG_HASH_FIELD_NAME_LENGTH, "DB_ROW_HASH_%u", num);
+}
+
+/**
+ Add fully invisible hash field to table in case of long
+ unique column
+ @param thd Thread Context.
+ @param create_list List of table fields.
+ @param key_info current long unique key info
+*/
+static Create_field * add_hash_field(THD * thd, List<Create_field> *create_list,
+ KEY *key_info)
+{
+ List_iterator<Create_field> it(*create_list);
+ Create_field *dup_field, *cf= new (thd->mem_root) Create_field();
+ cf->flags|= UNSIGNED_FLAG | LONG_UNIQUE_HASH_FIELD;
+ cf->decimals= 0;
+ cf->length= cf->char_length= cf->pack_length= HA_HASH_FIELD_LENGTH;
+ cf->invisible= INVISIBLE_FULL;
+ cf->pack_flag|= FIELDFLAG_MAYBE_NULL;
+ cf->vcol_info= new (thd->mem_root) Virtual_column_info();
+ cf->vcol_info->stored_in_db= false;
+ uint num= 1;
+ LEX_CSTRING field_name;
+ field_name.str= (char *)thd->alloc(LONG_HASH_FIELD_NAME_LENGTH);
+ make_long_hash_field_name(&field_name, num);
+ /*
+ Check for collisions
+ */
+ while ((dup_field= it++))
+ {
+ if (!my_strcasecmp(system_charset_info, field_name.str, dup_field->field_name.str))
+ {
+ num++;
+ make_long_hash_field_name(&field_name, num);
+ it.rewind();
+ }
+ }
+ cf->field_name= field_name;
+ cf->set_handler(&type_handler_longlong);
+ key_info->algorithm= HA_KEY_ALG_LONG_HASH;
+ create_list->push_back(cf,thd->mem_root);
+ return cf;
+}
+
Key *
mysql_add_invisible_index(THD *thd, List<Key> *key_list,
LEX_CSTRING* field_name, enum Key::Keytype type)
@@ -3414,6 +3411,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
int select_field_count= C_CREATE_SELECT(create_table_mode);
bool tmp_table= create_table_mode == C_ALTER_TABLE;
const bool create_simple= thd->lex->create_simple();
+ bool is_hash_field_needed= false;
+ const Column_derived_attributes dattr(create_info->default_table_charset);
+ const Column_bulk_alter_attributes
+ battr(create_info->alter_table_convert_to_charset);
DBUG_ENTER("mysql_prepare_create_table");
DBUG_EXECUTE_IF("test_pseudo_invisible",{
@@ -3470,26 +3471,27 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
for (field_no=0; (sql_field=it++) ; field_no++)
{
+ /* Virtual fields are always NULL */
+ if (sql_field->vcol_info)
+ sql_field->flags&= ~NOT_NULL_FLAG;
+
/*
Initialize length from its original value (number of characters),
which was set in the parser. This is necessary if we're
executing a prepared statement for the second time.
*/
sql_field->length= sql_field->char_length;
- /* Set field charset. */
- sql_field->charset= get_sql_field_charset(sql_field, create_info);
- if ((sql_field->flags & BINCMP_FLAG) &&
- !(sql_field->charset= find_bin_collation(sql_field->charset)))
- DBUG_RETURN(true);
- /* Virtual fields are always NULL */
- if (sql_field->vcol_info)
- sql_field->flags&= ~NOT_NULL_FLAG;
+ if (sql_field->bulk_alter(&dattr, &battr))
+ DBUG_RETURN(true);
if (sql_field->prepare_stage1(thd, thd->mem_root,
- file, file->ha_table_flags()))
+ file, file->ha_table_flags(),
+ &dattr))
DBUG_RETURN(true);
+ DBUG_ASSERT(sql_field->charset);
+
if (sql_field->real_field_type() == MYSQL_TYPE_BIT &&
file->ha_table_flags() & HA_CAN_BIT_FIELD)
total_uneven_bit_length+= sql_field->length & 7;
@@ -3540,7 +3542,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (!(sql_field->flags & NOT_NULL_FLAG))
null_fields--;
- if (sql_field->redefine_stage1(dup_field, file, create_info))
+ if (sql_field->redefine_stage1(dup_field, file))
DBUG_RETURN(true);
it2.remove(); // Remove first (create) definition
@@ -3742,6 +3744,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
uint key_length=0;
Key_part_spec *column;
+ is_hash_field_needed= false;
if (key->name.str == ignore_key)
{
/* ignore redundant keys */
@@ -3952,22 +3955,29 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (f_is_blob(sql_field->pack_flag) ||
(f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
- {
- if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
- {
- my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str,
+ {
+ if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
+ {
+ my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str,
file->table_type());
- DBUG_RETURN(TRUE);
- }
+ DBUG_RETURN(TRUE);
+ }
if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
Field::GEOM_POINT)
column->length= MAX_LEN_GEOM_POINT_FIELD;
- if (!column->length)
- {
- my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str);
- DBUG_RETURN(TRUE);
- }
- }
+ if (!column->length)
+ {
+ if (key->type == Key::UNIQUE)
+ is_hash_field_needed= true;
+ else if (key->type == Key::MULTIPLE)
+ column->length= file->max_key_length() + 1;
+ else
+ {
+ my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
#ifdef HAVE_SPATIAL
if (key->type == Key::SPATIAL)
{
@@ -4036,33 +4046,30 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (column->length)
{
- if (f_is_blob(sql_field->pack_flag))
- {
- key_part_length= MY_MIN(column->length,
- blob_length_by_type(sql_field->real_field_type())
- * sql_field->charset->mbmaxlen);
- if (key_part_length > max_key_length ||
- key_part_length > file->max_key_part_length())
- {
- key_part_length= MY_MIN(max_key_length, file->max_key_part_length());
- if (key->type == Key::MULTIPLE)
- {
- /* not a critical problem */
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ if (f_is_blob(sql_field->pack_flag))
+ {
+ key_part_length= MY_MIN(column->length,
+ blob_length_by_type(sql_field->real_field_type())
+ * sql_field->charset->mbmaxlen);
+ if (key_part_length > max_key_length ||
+ key_part_length > file->max_key_part_length())
+ {
+ if (key->type == Key::MULTIPLE)
+ {
+ key_part_length= MY_MIN(max_key_length, file->max_key_part_length());
+ /* not a critical problem */
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_TOO_LONG_KEY, ER_THD(thd, ER_TOO_LONG_KEY),
key_part_length);
/* Align key length to multibyte char boundary */
key_part_length-= key_part_length % sql_field->charset->mbmaxlen;
- }
- else
- {
- my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length);
- DBUG_RETURN(TRUE);
- }
- }
- }
+ }
+ else
+ is_hash_field_needed= true;
+ }
+ }
// Catch invalid use of partial keys
- else if (!f_is_geom(sql_field->pack_flag) &&
+ else if (!f_is_geom(sql_field->pack_flag) &&
// is the key partial?
column->length != key_part_length &&
// is prefix length bigger than field length?
@@ -4076,13 +4083,14 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
// and is this a 'unique' key?
(key_info->flags & HA_NOSAME))))
{
- my_message(ER_WRONG_SUB_KEY, ER_THD(thd, ER_WRONG_SUB_KEY), MYF(0));
- DBUG_RETURN(TRUE);
- }
- else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
- key_part_length= column->length;
+ my_message(ER_WRONG_SUB_KEY, ER_THD(thd, ER_WRONG_SUB_KEY), MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
+ key_part_length= column->length;
}
- else if (key_part_length == 0 && (sql_field->flags & NOT_NULL_FLAG))
+ else if (key_part_length == 0 && (sql_field->flags & NOT_NULL_FLAG) &&
+ !is_hash_field_needed)
{
my_error(ER_WRONG_KEY_COLUMN, MYF(0), file->table_type(),
column->field_name.str);
@@ -4091,30 +4099,45 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (key_part_length > file->max_key_part_length() &&
key->type != Key::FULLTEXT)
{
- key_part_length= file->max_key_part_length();
- if (key->type == Key::MULTIPLE)
- {
- /* not a critical problem */
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ if (key->type == Key::MULTIPLE)
+ {
+ key_part_length= file->max_key_part_length();
+ /* not a critical problem */
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_TOO_LONG_KEY, ER_THD(thd, ER_TOO_LONG_KEY),
key_part_length);
/* Align key length to multibyte char boundary */
key_part_length-= key_part_length % sql_field->charset->mbmaxlen;
- }
- else
- {
- my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length);
- DBUG_RETURN(TRUE);
- }
+ }
+ else
+ {
+ if (key->type == Key::UNIQUE)
+ {
+ is_hash_field_needed= true;
+ }
+ else
+ {
+ key_part_length= MY_MIN(max_key_length, file->max_key_part_length());
+ my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+ /* We can not store key_part_length more then 2^16 - 1 in frm */
+ if (is_hash_field_needed && column->length > UINT_MAX16)
+ {
+ my_error(ER_TOO_LONG_KEYPART, MYF(0), UINT_MAX16);
+ DBUG_RETURN(TRUE);
}
- key_part_info->length= (uint16) key_part_length;
+ else
+ key_part_info->length= (uint16) key_part_length;
/* Use packed keys for long strings on the first column */
if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
!((create_info->table_options & HA_OPTION_NO_PACK_KEYS)) &&
(key_part_length >= KEY_DEFAULT_PACK_LENGTH &&
(sql_field->real_field_type() == MYSQL_TYPE_STRING ||
sql_field->real_field_type() == MYSQL_TYPE_VARCHAR ||
- f_is_blob(sql_field->pack_flag))))
+ f_is_blob(sql_field->pack_flag))) && !is_hash_field_needed)
{
if ((column_nr == 0 && f_is_blob(sql_field->pack_flag)) ||
sql_field->real_field_type() == MYSQL_TYPE_VARCHAR)
@@ -4123,7 +4146,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_info->flags|= HA_PACK_KEY;
}
/* Check if the key segment is partial, set the key flag accordingly */
- if (key_part_length != sql_field->key_length)
+ if (key_part_length != sql_field->key_length &&
+ key_part_length != sql_field->type_handler()->max_octet_length())
key_info->flags|= HA_KEY_HAS_PART_KEY_SEG;
key_length+= key_part_length;
@@ -4163,12 +4187,43 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (key->type == Key::UNIQUE && !(key_info->flags & HA_NULL_PART_KEY))
unique_key=1;
key_info->key_length=(uint16) key_length;
- if (key_length > max_key_length && key->type != Key::FULLTEXT)
+ if (key_info->key_length > max_key_length && key->type == Key::UNIQUE)
+ is_hash_field_needed= true;
+ if (key_length > max_key_length && key->type != Key::FULLTEXT &&
+ !is_hash_field_needed)
{
- my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
+ my_error(ER_TOO_LONG_KEY, MYF(0), max_key_length);
DBUG_RETURN(TRUE);
}
+ if (is_hash_field_needed && key_info->algorithm != HA_KEY_ALG_UNDEF &&
+ key_info->algorithm != HA_KEY_ALG_HASH )
+ {
+ my_error(ER_TOO_LONG_KEY, MYF(0), max_key_length);
+ DBUG_RETURN(TRUE);
+ }
+ if (is_hash_field_needed ||
+ (key_info->algorithm == HA_KEY_ALG_HASH &&
+ key->type != Key::PRIMARY &&
+ key_info->flags & HA_NOSAME &&
+ !(file->ha_table_flags() & HA_CAN_HASH_KEYS ) &&
+ file->ha_table_flags() & HA_CAN_VIRTUAL_COLUMNS))
+ {
+ Create_field *hash_fld= add_hash_field(thd, &alter_info->create_list,
+ key_info);
+ if (!hash_fld)
+ DBUG_RETURN(TRUE);
+ hash_fld->offset= record_offset;
+ hash_fld->charset= create_info->default_table_charset;
+ record_offset+= hash_fld->pack_length;
+ if (key_info->flags & HA_NULL_PART_KEY)
+ null_fields++;
+ else
+ {
+ hash_fld->flags|= NOT_NULL_FLAG;
+ hash_fld->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
+ }
+ }
if (validate_comment_length(thd, &key->key_create_info.comment,
INDEX_COMMENT_MAXLEN,
ER_TOO_LONG_INDEX_COMMENT,
@@ -4184,14 +4239,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
// Check if a duplicate index is defined.
check_duplicate_key(thd, key, key_info, &alter_info->key_list);
-
key_info++;
}
- if (!unique_key && !primary_key &&
- ((file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY) ||
- ((file->ha_table_flags() & HA_WANTS_PRIMARY_KEY) &&
- !create_info->sequence)))
+ if (!unique_key && !primary_key && !create_info->sequence &&
+ (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
{
my_message(ER_REQUIRES_PRIMARY_KEY, ER_THD(thd, ER_REQUIRES_PRIMARY_KEY),
MYF(0));
@@ -4270,9 +4322,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
NOTE: we cannot do this in check_vcol_func_processor() as there is already
no table name qualifier in expression.
*/
- if (sql_field->vcol_info &&
+ if (sql_field->vcol_info && sql_field->vcol_info->expr &&
sql_field->vcol_info->expr->walk(&Item::check_table_name_processor,
- false, (void *) &walk_prm))
+ false, (void *) &walk_prm))
{
my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "GENERATED ALWAYS");
DBUG_RETURN(TRUE);
@@ -4361,11 +4413,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
/* Give warnings for not supported table options */
-#if defined(WITH_ARIA_STORAGE_ENGINE)
extern handlerton *maria_hton;
- if (file->partition_ht() != maria_hton)
-#endif
- if (create_info->transactional)
+ if (file->partition_ht() != maria_hton && create_info->transactional &&
+ !file->has_transaction_manager())
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER_THD(thd, ER_ILLEGAL_HA_CREATE_OPTION),
@@ -4456,9 +4506,8 @@ bool validate_comment_length(THD *thd, LEX_CSTRING *comment, size_t max_len,
apply it to the table.
*/
-static void set_table_default_charset(THD *thd,
- HA_CREATE_INFO *create_info,
- const LEX_CSTRING *db)
+static void set_table_default_charset(THD *thd, HA_CREATE_INFO *create_info,
+ const LEX_CSTRING &db)
{
/*
If the table character set was not given explicitly,
@@ -4469,7 +4518,7 @@ static void set_table_default_charset(THD *thd,
{
Schema_specification_st db_info;
- load_db_opt_by_name(thd, db->str, &db_info);
+ load_db_opt_by_name(thd, db.str, &db_info);
create_info->default_table_charset= db_info.default_table_charset;
}
@@ -4523,7 +4572,7 @@ bool Column_definition::prepare_blob_field(THD *thd)
set_handler(Type_handler::blob_type_handler((uint) length));
pack_length= type_handler()->calc_pack_length(0);
}
- length= 0;
+ length= key_length= 0;
}
DBUG_RETURN(0);
}
@@ -4546,7 +4595,9 @@ bool Column_definition::prepare_blob_field(THD *thd)
bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root)
{
- return prepare_stage1(thd, mem_root, NULL, HA_CAN_GEOMETRY) ||
+ DBUG_ASSERT(charset);
+ const Column_derived_attributes dattr(&my_charset_bin);
+ return prepare_stage1(thd, mem_root, NULL, HA_CAN_GEOMETRY, &dattr) ||
prepare_stage2(NULL, HA_CAN_GEOMETRY);
}
@@ -4593,12 +4644,11 @@ static bool vers_prepare_keys(THD *thd, HA_CREATE_INFO *create_info,
return false;
}
-handler *mysql_create_frm_image(THD *thd,
- const LEX_CSTRING *db, const LEX_CSTRING *table_name,
+handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db,
+ const LEX_CSTRING &table_name,
HA_CREATE_INFO *create_info,
Alter_info *alter_info, int create_table_mode,
- KEY **key_info,
- uint *key_count,
+ KEY **key_info, uint *key_count,
LEX_CUSTRING *frm)
{
uint db_options;
@@ -4732,7 +4782,7 @@ handler *mysql_create_frm_image(THD *thd,
if (part_info->vers_info && !create_info->versioned())
{
- my_error(ER_VERS_NOT_VERSIONED, MYF(0), table_name->str);
+ my_error(ER_VERS_NOT_VERSIONED, MYF(0), table_name.str);
goto err;
}
@@ -4837,7 +4887,7 @@ handler *mysql_create_frm_image(THD *thd,
if (mysql_prepare_create_table(thd, create_info, alter_info, &db_options,
file, key_info, key_count,
- create_table_mode, *db, *table_name))
+ create_table_mode, db, table_name))
goto err;
create_info->table_options=db_options;
@@ -4889,19 +4939,13 @@ err:
*/
static
-int create_table_impl(THD *thd,
- const LEX_CSTRING *orig_db,
- const LEX_CSTRING *orig_table_name,
- const LEX_CSTRING *db, const LEX_CSTRING *table_name,
- const char *path,
- const DDL_options_st options,
- HA_CREATE_INFO *create_info,
- Alter_info *alter_info,
- int create_table_mode,
- bool *is_trans,
- KEY **key_info,
- uint *key_count,
- LEX_CUSTRING *frm)
+int create_table_impl(THD *thd, const LEX_CSTRING &orig_db,
+ const LEX_CSTRING &orig_table_name,
+ const LEX_CSTRING &db, const LEX_CSTRING &table_name,
+ const char *path, const DDL_options_st options,
+ HA_CREATE_INFO *create_info, Alter_info *alter_info,
+ int create_table_mode, bool *is_trans, KEY **key_info,
+ uint *key_count, LEX_CUSTRING *frm)
{
LEX_CSTRING *alias;
handler *file= 0;
@@ -4910,9 +4954,10 @@ int create_table_impl(THD *thd,
bool internal_tmp_table= create_table_mode == C_ALTER_TABLE || frm_only;
DBUG_ENTER("mysql_create_table_no_lock");
DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d path: %s",
- db->str, table_name->str, internal_tmp_table, path));
+ db.str, table_name.str, internal_tmp_table, path));
- if (fix_constraints_names(thd, &alter_info->check_constraint_list))
+ if (fix_constraints_names(thd, &alter_info->check_constraint_list,
+ create_info))
DBUG_RETURN(1);
if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
@@ -4939,7 +4984,7 @@ int create_table_impl(THD *thd,
goto err;
}
- alias= const_cast<LEX_CSTRING*>(table_case_name(create_info, table_name));
+ alias= const_cast<LEX_CSTRING*>(table_case_name(create_info, &table_name));
/* Check if table exists */
if (create_info->tmp_table())
@@ -4948,7 +4993,7 @@ int create_table_impl(THD *thd,
If a table exists, it must have been pre-opened. Try looking for one
in-use in THD::all_temp_tables list of TABLE_SHAREs.
*/
- TABLE *tmp_table= thd->find_temporary_table(db->str, table_name->str);
+ TABLE *tmp_table= thd->find_temporary_table(db.str, table_name.str);
if (tmp_table)
{
@@ -4983,14 +5028,14 @@ int create_table_impl(THD *thd,
}
else
{
- if (!internal_tmp_table && ha_table_exists(thd, db, table_name))
+ if (!internal_tmp_table && ha_table_exists(thd, &db, &table_name))
{
if (options.or_replace())
{
- (void) delete_statistics_for_table(thd, db, table_name);
+ (void) delete_statistics_for_table(thd, &db, &table_name);
TABLE_LIST table_list;
- table_list.init_one_table(db, table_name, 0, TL_WRITE_ALLOW_WRITE);
+ table_list.init_one_table(&db, &table_name, 0, TL_WRITE_ALLOW_WRITE);
table_list.table= create_info->table;
if (check_if_log_table(&table_list, TRUE, "CREATE OR REPLACE"))
@@ -5017,7 +5062,7 @@ int create_table_impl(THD *thd,
/*
Restart statement transactions for the case of CREATE ... SELECT.
*/
- if (thd->lex->select_lex.item_list.elements &&
+ if (thd->lex->first_select_lex()->item_list.elements &&
restart_trans_for_tables(thd, thd->lex->query_tables))
goto err;
}
@@ -5025,7 +5070,7 @@ int create_table_impl(THD *thd,
goto warn;
else
{
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name->str);
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name.str);
goto err;
}
}
@@ -5033,7 +5078,7 @@ int create_table_impl(THD *thd,
THD_STAGE_INFO(thd, stage_creating_table);
- if (check_engine(thd, orig_db->str, orig_table_name->str, create_info))
+ if (check_engine(thd, orig_db.str, orig_table_name.str, create_info))
goto err;
if (create_table_mode == C_ASSISTED_DISCOVERY)
@@ -5053,7 +5098,7 @@ int create_table_impl(THD *thd,
goto err;
}
- init_tmp_table_share(thd, &share, db->str, 0, table_name->str, path);
+ init_tmp_table_share(thd, &share, db.str, 0, table_name.str, path);
/* prepare everything for discovery */
share.field= &no_fields;
@@ -5095,17 +5140,29 @@ int create_table_impl(THD *thd,
*/
if (!file || thd->is_error())
goto err;
- if (rea_create_table(thd, frm, path, db->str, table_name->str, create_info,
- file, frm_only))
+
+ if (thd->variables.keep_files_on_create)
+ create_info->options|= HA_CREATE_KEEP_FILES;
+
+ if (file->ha_create_partitioning_metadata(path, NULL, CHF_CREATE_FLAG))
goto err;
+
+ if (!frm_only)
+ {
+ if (ha_create_table(thd, path, db.str, table_name.str, create_info, frm))
+ {
+ file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG);
+ deletefrm(path);
+ goto err;
+ }
+ }
}
create_info->table= 0;
if (!frm_only && create_info->tmp_table())
{
- TABLE *table= thd->create_and_open_tmp_table(create_info->db_type, frm,
- path, db->str,
- table_name->str, true,
+ TABLE *table= thd->create_and_open_tmp_table(frm, path, db.str,
+ table_name.str,
false);
if (!table)
@@ -5120,43 +5177,7 @@ int create_table_impl(THD *thd,
thd->thread_specific_used= TRUE;
create_info->table= table; // Store pointer to table
}
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- else if (thd->work_part_info && frm_only)
- {
- /*
- For partitioned tables we can't find some problems with table
- until table is opened. Therefore in order to disallow creation
- of corrupted tables we have to try to open table as the part
- of its creation process.
- In cases when both .FRM and SE part of table are created table
- is implicitly open in ha_create_table() call.
- In cases when we create .FRM without SE part we have to open
- table explicitly.
- */
- TABLE table;
- TABLE_SHARE share;
-
- init_tmp_table_share(thd, &share, db->str, 0, table_name->str, path);
-
- bool result= (open_table_def(thd, &share, GTS_TABLE) ||
- open_table_from_share(thd, &share, &empty_clex_str, 0,
- (uint) READ_ALL, 0, &table, true));
- if (!result)
- (void) closefrm(&table);
-
- free_table_share(&share);
- if (result)
- {
- char frm_name[FN_REFLEN];
- strxnmov(frm_name, sizeof(frm_name), path, reg_ext, NullS);
- (void) mysql_file_delete(key_file_frm, frm_name, MYF(0));
- (void) file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG);
- goto err;
- }
- }
-#endif
-
error= 0;
err:
THD_STAGE_INFO(thd, stage_after_create);
@@ -5184,13 +5205,11 @@ warn:
-1 Table was used with IF NOT EXISTS and table existed (warning, not error)
*/
-int mysql_create_table_no_lock(THD *thd,
- const LEX_CSTRING *db,
+int mysql_create_table_no_lock(THD *thd, const LEX_CSTRING *db,
const LEX_CSTRING *table_name,
Table_specification_st *create_info,
Alter_info *alter_info, bool *is_trans,
- int create_table_mode,
- TABLE_LIST *table_list)
+ int create_table_mode, TABLE_LIST *table_list)
{
KEY *not_used_1;
uint not_used_2;
@@ -5214,7 +5233,7 @@ int mysql_create_table_no_lock(THD *thd,
}
}
- res= create_table_impl(thd, db, table_name, db, table_name, path,
+ res= create_table_impl(thd, *db, *table_name, *db, *table_name, path,
*create_info, create_info,
alter_info, create_table_mode,
is_trans, &not_used_1, &not_used_2, &frm);
@@ -5451,17 +5470,22 @@ make_unique_key_name(THD *thd, const char *field_name,KEY *start,KEY *end)
*/
static bool make_unique_constraint_name(THD *thd, LEX_CSTRING *name,
+ const char *own_name_base,
List<Virtual_column_info> *vcol,
uint *nr)
{
char buff[MAX_FIELD_NAME], *end;
List_iterator_fast<Virtual_column_info> it(*vcol);
-
- end=strmov(buff, "CONSTRAINT_");
- for (;;)
+ end=strmov(buff, own_name_base ? own_name_base : "CONSTRAINT_");
+ for (int round= 0;; round++)
{
Virtual_column_info *check;
- char *real_end= int10_to_str((*nr)++, end, 10);
+ char *real_end= end;
+ if (round == 1 && own_name_base)
+ *end++= '_';
+ // if own_base_name provided, try it first
+ if (round != 0 || !own_name_base)
+ real_end= int10_to_str((*nr)++, end, 10);
it.rewind();
while ((check= it++))
{
@@ -5685,7 +5709,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
#ifdef WITH_WSREP
if (WSREP(thd) && !thd->wsrep_applier &&
wsrep_create_like_table(thd, table, src_table, create_info))
+ {
DBUG_RETURN(res);
+ }
#endif
/*
@@ -5700,12 +5726,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
properly isolated from all concurrent operations which matter.
*/
- /* Copy temporarily the statement flags to thd for lock_table_names() */
- // QQ: is this really needed???
- uint save_thd_create_info_options= thd->lex->create_info.options;
- thd->lex->create_info.options|= create_info->options;
- res= open_tables(thd, &thd->lex->query_tables, &not_used, 0);
- thd->lex->create_info.options= save_thd_create_info_options;
+ res= open_tables(thd, *create_info, &thd->lex->query_tables, &not_used, 0);
if (res)
{
@@ -6104,6 +6125,7 @@ static bool is_candidate_key(KEY *key)
thd Thread object.
table The altered table.
alter_info List of columns and indexes to create
+ period_info Application-time period info
DESCRIPTION
Looks for the IF [NOT] EXISTS options, checks the states and remove items
@@ -6115,7 +6137,8 @@ static bool is_candidate_key(KEY *key)
*/
static bool
-handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info)
+handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info,
+ Table_period_info *period_info)
{
Field **f_ptr;
DBUG_ENTER("handle_if_exists_option");
@@ -6301,6 +6324,11 @@ drop_create_field:
}
}
}
+ else if (drop->type == Alter_drop::PERIOD)
+ {
+ if (table->s->period.name.streq(drop->name))
+ remove_drop= FALSE;
+ }
else /* Alter_drop::KEY and Alter_drop::FOREIGN_KEY */
{
uint n_key;
@@ -6582,12 +6610,33 @@ remove_key:
}
}
- DBUG_RETURN(FALSE);
+ /* ADD PERIOD */
+
+ if (period_info->create_if_not_exists && table->s->period.name
+ && table->s->period.name.streq(period_info->name))
+ {
+ DBUG_ASSERT(period_info->is_set());
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_DUP_FIELDNAME, ER_THD(thd, ER_DUP_FIELDNAME),
+ period_info->name.str, table->s->table_name.str);
+
+ List_iterator<Virtual_column_info> vit(alter_info->check_constraint_list);
+ while (vit++ != period_info->constr)
+ {
+ // do nothing
+ }
+ vit.remove();
+
+ *period_info= {};
+ }
+
+ DBUG_RETURN(false);
}
static bool fix_constraints_names(THD *thd, List<Virtual_column_info>
- *check_constraint_list)
+ *check_constraint_list,
+ const HA_CREATE_INFO *create_info)
{
List_iterator<Virtual_column_info> it((*check_constraint_list));
Virtual_column_info *check;
@@ -6611,7 +6660,12 @@ static bool fix_constraints_names(THD *thd, List<Virtual_column_info>
if (!check->name.length)
{
check->automatic_name= TRUE;
+
+ const char *own_name_base= create_info->period_info.constr == check
+ ? create_info->period_info.name.str : NULL;
+
if (make_unique_constraint_name(thd, &check->name,
+ own_name_base,
check_constraint_list,
&nr))
DBUG_RETURN(TRUE);
@@ -6620,31 +6674,101 @@ static bool fix_constraints_names(THD *thd, List<Virtual_column_info>
DBUG_RETURN(FALSE);
}
-/**
- Get Create_field object for newly created table by field index.
- @param alter_info Alter_info describing newly created table.
- @param idx Field index.
-*/
-
-static Create_field *get_field_by_index(Alter_info *alter_info, uint idx)
+static int compare_uint(const uint *s, const uint *t)
{
- List_iterator_fast<Create_field> field_it(alter_info->create_list);
- uint field_idx= 0;
- Create_field *field;
+ return (*s < *t) ? -1 : ((*s > *t) ? 1 : 0);
+}
- while ((field= field_it++) && field_idx < idx)
- { field_idx++; }
+static Compare_keys merge(Compare_keys current, Compare_keys add) {
+ if (current == Compare_keys::Equal)
+ return add;
- return field;
-}
+ if (add == Compare_keys::Equal)
+ return current;
+ if (current == add)
+ return current;
-static int compare_uint(const uint *s, const uint *t)
-{
- return (*s < *t) ? -1 : ((*s > *t) ? 1 : 0);
+ if (current == Compare_keys::EqualButComment) {
+ return Compare_keys::NotEqual;
+ }
+
+ if (current == Compare_keys::EqualButKeyPartLength) {
+ if (add == Compare_keys::EqualButComment)
+ return Compare_keys::NotEqual;
+ DBUG_ASSERT(add == Compare_keys::NotEqual);
+ return Compare_keys::NotEqual;
+ }
+
+ DBUG_ASSERT(current == Compare_keys::NotEqual);
+ return current;
}
+Compare_keys compare_keys_but_name(const KEY *table_key, const KEY *new_key,
+ Alter_info *alter_info, const TABLE *table,
+ const KEY *const new_pk,
+ const KEY *const old_pk)
+{
+ if (table_key->algorithm != new_key->algorithm)
+ return Compare_keys::NotEqual;
+
+ if ((table_key->flags & HA_KEYFLAG_MASK) !=
+ (new_key->flags & HA_KEYFLAG_MASK))
+ return Compare_keys::NotEqual;
+
+ if (table_key->user_defined_key_parts != new_key->user_defined_key_parts)
+ return Compare_keys::NotEqual;
+
+ if (table_key->block_size != new_key->block_size)
+ return Compare_keys::NotEqual;
+
+ /*
+ Rebuild the index if following condition get satisfied:
+
+ (i) Old table doesn't have primary key, new table has it and vice-versa
+ (ii) Primary key changed to another existing index
+ */
+ if ((new_key == new_pk) != (table_key == old_pk))
+ return Compare_keys::NotEqual;
+
+ if (engine_options_differ(table_key->option_struct, new_key->option_struct,
+ table->file->ht->index_options))
+ return Compare_keys::NotEqual;
+
+ Compare_keys result= Compare_keys::Equal;
+
+ for (const KEY_PART_INFO *
+ key_part= table_key->key_part,
+ *new_part= new_key->key_part,
+ *end= table_key->key_part + table_key->user_defined_key_parts;
+ key_part < end; key_part++, new_part++)
+ {
+ /*
+ For prefix keys KEY_PART_INFO::field points to cloned Field
+ object with adjusted length. So below we have to check field
+ indexes instead of simply comparing pointers to Field objects.
+ */
+ const Create_field &new_field=
+ *alter_info->create_list.elem(new_part->fieldnr);
+
+ if (!new_field.field ||
+ new_field.field->field_index != key_part->fieldnr - 1)
+ {
+ return Compare_keys::NotEqual;
+ }
+
+ auto compare= table->file->compare_key_parts(
+ *table->field[key_part->fieldnr - 1], new_field, *key_part, *new_part);
+ result= merge(result, compare);
+ }
+
+ /* Check that key comment is not changed. */
+ if (cmp(table_key->comment, new_key->comment) != 0)
+ result= merge(result, Compare_keys::EqualButComment);
+
+ return result;
+}
/**
Compare original and new versions of a table and fill Alter_inplace_info
@@ -6691,21 +6815,21 @@ static int compare_uint(const uint *s, const uint *t)
static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
Alter_inplace_info *ha_alter_info)
{
- Field **f_ptr, *field, *old_field;
+ Field **f_ptr, *field;
List_iterator_fast<Create_field> new_field_it;
Create_field *new_field;
- KEY_PART_INFO *key_part, *new_part;
- KEY_PART_INFO *end;
Alter_info *alter_info= ha_alter_info->alter_info;
DBUG_ENTER("fill_alter_inplace_info");
DBUG_PRINT("info", ("alter_info->flags: %llu", alter_info->flags));
/* Allocate result buffers. */
+ DBUG_ASSERT(ha_alter_info->rename_keys.mem_root() == thd->mem_root);
if (! (ha_alter_info->index_drop_buffer=
(KEY**) thd->alloc(sizeof(KEY*) * table->s->keys)) ||
! (ha_alter_info->index_add_buffer=
(uint*) thd->alloc(sizeof(uint) *
- alter_info->key_list.elements)))
+ alter_info->key_list.elements)) ||
+ ha_alter_info->rename_keys.reserve(ha_alter_info->index_add_count))
DBUG_RETURN(true);
/*
@@ -6780,61 +6904,50 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
/* Field is not dropped. Evaluate changes bitmap for it. */
/*
- Check if type of column has changed to some incompatible type.
+ Check if type of column has changed.
*/
- uint is_equal= field->is_equal(new_field);
- switch (is_equal)
+ bool is_equal= field->is_equal(*new_field);
+ if (!is_equal)
{
- case IS_EQUAL_NO:
- /* New column type is incompatible with old one. */
- if (field->stored_in_db())
- ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE;
+ if (field->can_be_converted_by_engine(*new_field))
+ {
+ /*
+ New column type differs from the old one, but storage engine can
+ change it by itself.
+ (for example, VARCHAR(300) is changed to VARCHAR(400)).
+ */
+ ha_alter_info->handler_flags|= ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE;
+ }
else
- ha_alter_info->handler_flags|= ALTER_VIRTUAL_COLUMN_TYPE;
- if (table->s->tmp_table == NO_TMP_TABLE)
{
- delete_statistics_for_column(thd, table, field);
- KEY *key_info= table->key_info;
- for (uint i=0; i < table->s->keys; i++, key_info++)
+ /* New column type is incompatible with old one. */
+ ha_alter_info->handler_flags|= field->stored_in_db()
+ ? ALTER_STORED_COLUMN_TYPE
+ : ALTER_VIRTUAL_COLUMN_TYPE;
+
+ if (table->s->tmp_table == NO_TMP_TABLE)
{
- if (field->part_of_key.is_set(i))
+ delete_statistics_for_column(thd, table, field);
+ KEY *key_info= table->key_info;
+ for (uint i= 0; i < table->s->keys; i++, key_info++)
{
+ if (!field->part_of_key.is_set(i))
+ continue;
+
uint key_parts= table->actual_n_key_parts(key_info);
for (uint j= 0; j < key_parts; j++)
{
- if (key_info->key_part[j].fieldnr-1 == field->field_index)
+ if (key_info->key_part[j].fieldnr - 1 == field->field_index)
{
- delete_statistics_for_index(thd, table, key_info,
- j >= key_info->user_defined_key_parts);
+ delete_statistics_for_index(
+ thd, table, key_info,
+ j >= key_info->user_defined_key_parts);
break;
}
- }
+ }
}
- }
+ }
}
- break;
- case IS_EQUAL_YES:
- /*
- New column is the same as the old one or the fully compatible with
- it (for example, ENUM('a','b') was changed to ENUM('a','b','c')).
- Such a change if any can ALWAYS be carried out by simply updating
- data-dictionary without even informing storage engine.
- No flag is set in this case.
- */
- break;
- case IS_EQUAL_PACK_LENGTH:
- /*
- New column type differs from the old one, but has compatible packed
- data representation. Depending on storage engine, such a change can
- be carried out by simply updating data dictionary without changing
- actual data (for example, VARCHAR(300) is changed to VARCHAR(400)).
- */
- ha_alter_info->handler_flags|= ALTER_COLUMN_EQUAL_PACK_LENGTH;
- break;
- default:
- DBUG_ASSERT(0);
- /* Safety. */
- ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE;
}
if (field->vcol_info || new_field->vcol_info)
@@ -6845,7 +6958,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
ALTER_VIRTUAL_COLUMN_TYPE);
if (field->vcol_info && new_field->vcol_info)
{
- bool value_changes= is_equal == IS_EQUAL_NO;
+ bool value_changes= !is_equal;
alter_table_operations alter_expr;
if (field->stored_in_db())
alter_expr= ALTER_STORED_GCOL_EXPR;
@@ -6939,7 +7052,6 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
ha_alter_info->create_info->fields_option_struct[f_ptr - table->field]=
new_field->option_struct;
}
-
}
else
{
@@ -6989,7 +7101,6 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
Go through keys and check if the original ones are compatible
with new table.
*/
- uint old_field_len= 0;
KEY *table_key;
KEY *table_key_end= table->key_info + table->s->keys;
KEY *new_key;
@@ -7036,88 +7147,21 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
continue;
}
- /* Check that the key types are compatible between old and new tables. */
- if ((table_key->algorithm != new_key->algorithm) ||
- ((table_key->flags & HA_KEYFLAG_MASK) !=
- (new_key->flags & HA_KEYFLAG_MASK)) ||
- (table_key->user_defined_key_parts !=
- new_key->user_defined_key_parts))
- goto index_changed;
-
- if (table_key->block_size != new_key->block_size)
- goto index_changed;
-
- if (engine_options_differ(table_key->option_struct, new_key->option_struct,
- table->file->ht->index_options))
- goto index_changed;
-
- /*
- Check that the key parts remain compatible between the old and
- new tables.
- */
- end= table_key->key_part + table_key->user_defined_key_parts;
- for (key_part= table_key->key_part, new_part= new_key->key_part;
- key_part < end;
- key_part++, new_part++)
+ switch (compare_keys_but_name(table_key, new_key, alter_info, table, new_pk,
+ old_pk))
{
- new_field= get_field_by_index(alter_info, new_part->fieldnr);
- old_field= table->field[key_part->fieldnr - 1];
- /*
- If there is a change in index length due to column expansion
- like varchar(X) changed to varchar(X + N) and has a compatible
- packed data representation, we mark it for fast/INPLACE change
- in index definition. InnoDB supports INPLACE for this cases
-
- Key definition has changed if we are using a different field or
- if the user key part length is different.
- */
- old_field_len= old_field->pack_length();
-
- if (old_field->type() == MYSQL_TYPE_VARCHAR)
- {
- old_field_len= (old_field->pack_length()
- - ((Field_varstring*) old_field)->length_bytes);
- }
-
- if (key_part->length == old_field_len &&
- key_part->length < new_part->length &&
- (key_part->field->is_equal((Create_field*) new_field)
- == IS_EQUAL_PACK_LENGTH))
- {
- ha_alter_info->handler_flags |= ALTER_COLUMN_INDEX_LENGTH;
- }
- else if (key_part->length != new_part->length)
- goto index_changed;
-
- /*
- For prefix keys KEY_PART_INFO::field points to cloned Field
- object with adjusted length. So below we have to check field
- indexes instead of simply comparing pointers to Field objects.
- */
- if (! new_field->field ||
- new_field->field->field_index != key_part->fieldnr - 1)
- goto index_changed;
+ case Compare_keys::Equal:
+ continue;
+ case Compare_keys::EqualButKeyPartLength:
+ ha_alter_info->handler_flags|= ALTER_COLUMN_INDEX_LENGTH;
+ continue;
+ case Compare_keys::EqualButComment:
+ ha_alter_info->handler_flags|= ALTER_CHANGE_INDEX_COMMENT;
+ continue;
+ case Compare_keys::NotEqual:
+ break;
}
- /*
- Rebuild the index if following condition get satisfied:
-
- (i) Old table doesn't have primary key, new table has it and vice-versa
- (ii) Primary key changed to another existing index
- */
- if ((new_key == new_pk) != (table_key == old_pk))
- goto index_changed;
-
- /* Check that key comment is not changed. */
- if (table_key->comment.length != new_key->comment.length ||
- (table_key->comment.length &&
- memcmp(table_key->comment.str, new_key->comment.str,
- table_key->comment.length) != 0))
- goto index_changed;
-
- continue;
-
- index_changed:
/* Key modified. Add the key / key offset to both buffers. */
ha_alter_info->index_drop_buffer
[ha_alter_info->index_drop_count++]=
@@ -7157,6 +7201,40 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
new_key->option_struct;
}
+ for (uint i= 0; i < ha_alter_info->index_add_count; i++)
+ {
+ uint *add_buffer= ha_alter_info->index_add_buffer;
+ const KEY *new_key= ha_alter_info->key_info_buffer + add_buffer[i];
+
+ for (uint j= 0; j < ha_alter_info->index_drop_count; j++)
+ {
+ KEY **drop_buffer= ha_alter_info->index_drop_buffer;
+ const KEY *old_key= drop_buffer[j];
+
+ if (compare_keys_but_name(old_key, new_key, alter_info, table, new_pk,
+ old_pk) != Compare_keys::Equal)
+ {
+ continue;
+ }
+
+ DBUG_ASSERT(
+ lex_string_cmp(system_charset_info, &old_key->name, &new_key->name));
+
+ ha_alter_info->handler_flags|= ALTER_RENAME_INDEX;
+ ha_alter_info->rename_keys.push_back(
+ Alter_inplace_info::Rename_key_pair(old_key, new_key));
+
+ --ha_alter_info->index_add_count;
+ --ha_alter_info->index_drop_count;
+ memmove(add_buffer + i, add_buffer + i + 1,
+ sizeof(add_buffer[0]) * (ha_alter_info->index_add_count - i));
+ memmove(drop_buffer + j, drop_buffer + j + 1,
+ sizeof(drop_buffer[0]) * (ha_alter_info->index_drop_count - j));
+ --i; // this index once again
+ break;
+ }
+ }
+
/*
Sort index_add_buffer according to how key_info_buffer is sorted.
I.e. with primary keys first - see sort_keys().
@@ -7343,7 +7421,7 @@ bool mysql_compare_tables(TABLE *table,
DBUG_RETURN(false);
/* Evaluate changes bitmap and send to check_if_incompatible_data() */
- uint field_changes= field->is_equal(tmp_new_field);
+ uint field_changes= field->is_equal(*tmp_new_field);
if (field_changes != IS_EQUAL_YES)
DBUG_RETURN(false);
@@ -7586,7 +7664,6 @@ static bool mysql_inplace_alter_table(THD *thd,
Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN | MYSQL_OPEN_IGNORE_KILLED);
handlerton *db_type= table->s->db_type();
MDL_ticket *mdl_ticket= table->mdl_ticket;
- HA_CREATE_INFO *create_info= ha_alter_info->create_info;
Alter_info *alter_info= ha_alter_info->alter_info;
bool reopen_tables= false;
bool res;
@@ -7595,6 +7672,9 @@ static bool mysql_inplace_alter_table(THD *thd,
DBUG_ENTER("mysql_inplace_alter_table");
+ /* Downgrade DDL lock while we are waiting for exclusive lock below */
+ backup_set_alter_copy_lock(thd, table);
+
/*
Upgrade to EXCLUSIVE lock if:
- This is requested by the storage engine
@@ -7667,9 +7747,7 @@ static bool mysql_inplace_alter_table(THD *thd,
thd->mdl_context.upgrade_shared_lock(table->mdl_ticket,
MDL_SHARED_NO_WRITE,
thd->variables.lock_wait_timeout))
- {
goto cleanup;
- }
// It's now safe to take the table level lock.
if (lock_tables(thd, table_list, alter_ctx->tables_opened, 0))
@@ -7706,9 +7784,7 @@ static bool mysql_inplace_alter_table(THD *thd,
if (table->file->ha_prepare_inplace_alter_table(altered_table,
ha_alter_info))
- {
goto rollback;
- }
/*
Downgrade the lock if storage engine has told us that exclusive lock was
@@ -7751,6 +7827,10 @@ static bool mysql_inplace_alter_table(THD *thd,
if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME))
goto rollback;
+ /* Set MDL_BACKUP_DDL */
+ if (backup_reset_alter_copy_lock(thd))
+ goto rollback;
+
/*
If we are killed after this point, we should ignore and continue.
We have mostly completed the operation at this point, there should
@@ -7794,10 +7874,7 @@ static bool mysql_inplace_alter_table(THD *thd,
{
goto rollback;
}
-
DEBUG_SYNC(thd, "alter_table_inplace_after_commit");
-
- thd->drop_temporary_table(altered_table, NULL, false);
}
close_all_tables_for_name(thd, table->s,
@@ -7821,9 +7898,6 @@ static bool mysql_inplace_alter_table(THD *thd,
thd->is_error())
{
// Since changes were done in-place, we can't revert them.
- (void) quick_rm_table(thd, db_type,
- &alter_ctx->new_db, &alter_ctx->tmp_name,
- FN_IS_TMP | NO_HA_TABLE);
DBUG_RETURN(true);
}
@@ -7900,10 +7974,6 @@ static bool mysql_inplace_alter_table(THD *thd,
thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
/* QQ; do something about metadata locks ? */
}
- thd->drop_temporary_table(altered_table, NULL, false);
- // Delete temporary .frm/.par
- (void) quick_rm_table(thd, create_info->db_type, &alter_ctx->new_db,
- &alter_ctx->tmp_name, FN_IS_TMP | NO_HA_TABLE);
DBUG_RETURN(true);
}
@@ -8018,6 +8088,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
Create_field *def;
Field **f_ptr,*field;
MY_BITMAP *dropped_fields= NULL; // if it's NULL - no dropped fields
+ bool drop_period= false;
DBUG_ENTER("mysql_prepare_alter_table");
/*
@@ -8392,9 +8463,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
Collect all keys which isn't in drop list. Add only those
for which some fields exists.
*/
-
for (uint i=0 ; i < table->s->keys ; i++,key_info++)
{
+ bool long_hash_key= false;
if (key_info->flags & HA_INVISIBLE_KEY)
continue;
const char *key_name= key_info->name.str;
@@ -8427,6 +8498,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
continue;
}
+ if (key_info->algorithm == HA_KEY_ALG_LONG_HASH)
+ {
+ setup_keyinfo_hash(key_info);
+ long_hash_key= true;
+ }
const char *dropped_key_part= NULL;
bool user_keyparts= false; // some user-defined keyparts left
KEY_PART_INFO *key_part= key_info->key_part;
@@ -8531,6 +8607,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
enum Key::Keytype key_type;
LEX_CSTRING tmp_name;
bzero((char*) &key_create_info, sizeof(key_create_info));
+ if (key_info->algorithm == HA_KEY_ALG_LONG_HASH)
+ key_info->algorithm= HA_KEY_ALG_UNDEF;
key_create_info.algorithm= key_info->algorithm;
/*
We copy block size directly as some engines, like Area, sets this
@@ -8560,6 +8638,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (dropped_key_part)
{
my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), dropped_key_part);
+ if (long_hash_key)
+ {
+ key_info->algorithm= HA_KEY_ALG_LONG_HASH;
+ re_setup_keyinfo_hash(key_info);
+ }
goto err;
}
}
@@ -8570,11 +8653,17 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
tmp_name.str= key_name;
tmp_name.length= strlen(key_name);
- key= new Key(key_type, &tmp_name, &key_create_info,
+ /* We dont need LONG_UNIQUE_HASH_FIELD flag because it will be autogenerated */
+ key= new (thd->mem_root) Key(key_type, &tmp_name, &key_create_info,
MY_TEST(key_info->flags & HA_GENERATED_KEY),
&key_parts, key_info->option_list, DDL_options());
new_key_list.push_back(key, thd->mem_root);
}
+ if (long_hash_key)
+ {
+ key_info->algorithm= HA_KEY_ALG_LONG_HASH;
+ re_setup_keyinfo_hash(key_info);
+ }
}
{
Key *key;
@@ -8593,6 +8682,35 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
}
+ if (table->s->period.name)
+ {
+ drop_it.rewind();
+ Alter_drop *drop;
+ for (bool found= false; !found && (drop= drop_it++); )
+ {
+ found= drop->type == Alter_drop::PERIOD &&
+ table->s->period.name.streq(drop->name);
+ }
+
+ if (drop)
+ {
+ drop_period= true;
+ drop_it.remove();
+ }
+ else if (create_info->period_info.is_set() && table->s->period.name)
+ {
+ my_error(ER_MORE_THAN_ONE_PERIOD, MYF(0));
+ goto err;
+ }
+ else
+ {
+ Field *s= table->s->period.start_field(table->s);
+ Field *e= table->s->period.end_field(table->s);
+ create_info->period_info.set_period(s->field_name, e->field_name);
+ create_info->period_info.name= table->s->period.name;
+ }
+ }
+
/* Add all table level constraints which are not in the drop list */
if (table->s->table_check_constraints)
{
@@ -8603,6 +8721,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
{
Virtual_column_info *check= table->check_constraints[i];
Alter_drop *drop;
+ bool keep= true;
drop_it.rewind();
while ((drop=drop_it++))
{
@@ -8610,17 +8729,41 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
!my_strcasecmp(system_charset_info, check->name.str, drop->name))
{
drop_it.remove();
+ keep= false;
break;
}
}
+
+ if (share->period.constr_name.streq(check->name.str))
+ {
+ if (!drop_period && !keep)
+ {
+ my_error(ER_PERIOD_CONSTRAINT_DROP, MYF(0), check->name.str,
+ share->period.name.str);
+ goto err;
+ }
+ keep= keep && !drop_period;
+
+ DBUG_ASSERT(create_info->period_info.constr == NULL || drop_period);
+
+ if (keep)
+ {
+ Item *expr_copy= check->expr->get_copy(thd);
+ check= new Virtual_column_info();
+ check->name= share->period.constr_name;
+ check->automatic_name= true;
+ check->expr= expr_copy;
+ create_info->period_info.constr= check;
+ }
+ }
/* see if the constraint depends on *only* on dropped fields */
- if (!drop && dropped_fields)
+ if (keep && dropped_fields)
{
table->default_column_bitmaps();
bitmap_clear_all(table->read_set);
check->expr->walk(&Item::register_field_in_read_map, 1, 0);
if (bitmap_is_subset(table->read_set, dropped_fields))
- drop= (Alter_drop*)1;
+ keep= false;
else if (bitmap_is_overlapping(dropped_fields, table->read_set))
{
bitmap_intersect(table->read_set, dropped_fields);
@@ -8630,7 +8773,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
goto err;
}
}
- if (!drop)
+ if (keep)
{
if (alter_info->flags & ALTER_RENAME_COLUMN)
{
@@ -8684,8 +8827,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
case Alter_drop::KEY:
case Alter_drop::COLUMN:
case Alter_drop::CHECK_CONSTRAINT:
- my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), drop->type_name(),
- alter_info->drop_list.head()->name);
+ case Alter_drop::PERIOD:
+ my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), drop->type_name(),
+ alter_info->drop_list.head()->name);
goto err;
case Alter_drop::FOREIGN_KEY:
// Leave the DROP FOREIGN KEY names in the alter_info->drop_list.
@@ -8821,7 +8965,7 @@ fk_check_column_changes(THD *thd, Alter_info *alter_info,
return FK_COLUMN_RENAMED;
}
- if ((old_field->is_equal(new_field) == IS_EQUAL_NO) ||
+ if ((old_field->is_equal(*new_field) == IS_EQUAL_NO) ||
((new_field->flags & NOT_NULL_FLAG) &&
!(old_field->flags & NOT_NULL_FLAG)))
{
@@ -9219,11 +9363,6 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
close_all_tables_for_name(thd, table->s, HA_EXTRA_PREPARE_FOR_RENAME,
NULL);
- (void) rename_table_in_stat_tables(thd, &alter_ctx->db,
- &alter_ctx->table_name,
- &alter_ctx->new_db,
- &alter_ctx->new_alias);
-
if (mysql_rename_table(old_db_type, &alter_ctx->db, &alter_ctx->table_name,
&alter_ctx->new_db, &alter_ctx->new_alias, 0))
error= -1;
@@ -9240,6 +9379,12 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
NO_FK_CHECKS);
error= -1;
}
+ /* Update stat tables last. This is to be able to handle rename of a stat table */
+ if (error == 0)
+ (void) rename_table_in_stat_tables(thd, &alter_ctx->db,
+ &alter_ctx->table_name,
+ &alter_ctx->new_db,
+ &alter_ctx->new_alias);
}
if (likely(!error))
@@ -9269,6 +9414,49 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
}
+static void cleanup_table_after_inplace_alter_keep_files(TABLE *table)
+{
+ TABLE_SHARE *share= table->s;
+ closefrm(table);
+ free_table_share(share);
+}
+
+
+static void cleanup_table_after_inplace_alter(TABLE *table)
+{
+ table->file->ha_create_partitioning_metadata(table->s->normalized_path.str, 0,
+ CHF_DELETE_FLAG);
+ deletefrm(table->s->normalized_path.str);
+ cleanup_table_after_inplace_alter_keep_files(table);
+}
+
+
+static int create_table_for_inplace_alter(THD *thd,
+ const Alter_table_ctx &alter_ctx,
+ LEX_CUSTRING *frm,
+ TABLE_SHARE *share,
+ TABLE *table)
+{
+ init_tmp_table_share(thd, share, alter_ctx.new_db.str, 0,
+ alter_ctx.new_name.str, alter_ctx.get_tmp_path());
+ if (share->init_from_binary_frm_image(thd, true, frm->str, frm->length) ||
+ open_table_from_share(thd, share, &alter_ctx.new_name, 0,
+ EXTRA_RECORD, thd->open_options,
+ table, false))
+ {
+ free_table_share(share);
+ deletefrm(alter_ctx.get_tmp_path());
+ return 1;
+ }
+ if (table->internal_tables && open_and_lock_internal_tables(table, false))
+ {
+ cleanup_table_after_inplace_alter(table);
+ return 1;
+ }
+ return 0;
+}
+
+
/**
Alter table
@@ -9372,6 +9560,7 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
uint tables_opened;
thd->open_options|= HA_OPEN_FOR_ALTER;
+ thd->mdl_backup_ticket= 0;
bool error= open_tables(thd, &table_list, &tables_opened, 0,
&alter_prelocking_strategy);
thd->open_options&= ~HA_OPEN_FOR_ALTER;
@@ -9449,7 +9638,8 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
If such table exists, there must be a corresponding TABLE_SHARE in
THD::all_temp_tables list.
*/
- if (thd->find_tmp_table_share(alter_ctx.new_db.str, alter_ctx.new_name.str))
+ if (thd->find_tmp_table_share(alter_ctx.new_db.str,
+ alter_ctx.new_name.str))
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias.str);
DBUG_RETURN(true);
@@ -9479,12 +9669,12 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
}
/*
- Global intention exclusive lock must have been already acquired when
- table to be altered was open, so there is no need to do it here.
+ Protection against global read lock must have been acquired when table
+ to be altered was being opened.
*/
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::GLOBAL,
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::BACKUP,
"", "",
- MDL_INTENTION_EXCLUSIVE));
+ MDL_BACKUP_DDL));
if (thd->mdl_context.acquire_locks(&mdl_requests,
thd->variables.lock_wait_timeout))
@@ -9590,6 +9780,17 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
if (table->s->tmp_table == NO_TMP_TABLE)
mysql_audit_alter_table(thd, table_list);
+ else if (table->s->table_creation_was_logged && mysql_bin_log.is_open())
+ {
+ /* Protect against MDL error in binary logging */
+ MDL_request mdl_request;
+ DBUG_ASSERT(!mdl_ticket);
+ mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_TRANSACTION);
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(true);
+ }
THD_STAGE_INFO(thd, stage_setup);
@@ -9651,11 +9852,12 @@ do_continue:;
}
}
- if (handle_if_exists_options(thd, table, alter_info) ||
- fix_constraints_names(thd, &alter_info->check_constraint_list))
+ if (handle_if_exists_options(thd, table, alter_info,
+ &create_info->period_info) ||
+ fix_constraints_names(thd, &alter_info->check_constraint_list,
+ create_info))
DBUG_RETURN(true);
-
/*
Look if we have to do anything at all.
ALTER can become NOOP after handling
@@ -9668,9 +9870,12 @@ do_continue:;
thd->get_stmt_da()->current_statement_warn_count());
my_ok(thd, 0L, 0L, alter_ctx.tmp_buff);
- /* We don't replicate alter table statement on temporary tables */
+ /*
+ We don't replicate alter table statement on temporary tables
+ For which we did not log the CREATE TEMPORARY TABLE statement.
+ */
if (table->s->tmp_table == NO_TMP_TABLE ||
- !thd->is_current_stmt_binlog_format_row())
+ table->s->table_creation_was_logged)
{
if (write_bin_log(thd, true, thd->query(), thd->query_length()))
DBUG_RETURN(true);
@@ -9734,13 +9939,12 @@ do_continue:;
DBUG_RETURN(true);
}
- if (create_info->vers_check_system_fields(thd, alter_info,
- table->s->table_name, table->s->db))
- {
- DBUG_RETURN(true);
- }
+ set_table_default_charset(thd, create_info, alter_ctx.db);
- set_table_default_charset(thd, create_info, &alter_ctx.db);
+ if (create_info->check_fields(thd, alter_info,
+ table_list->table_name, table_list->db) ||
+ create_info->fix_period_fields(thd, alter_info))
+ DBUG_RETURN(true);
if (!opt_explicit_defaults_for_timestamp)
promote_first_timestamp_column(&alter_info->create_list);
@@ -9896,8 +10100,6 @@ do_continue:;
}
DEBUG_SYNC(thd, "alter_table_before_create_table_no_lock");
- /* We can abort alter table for any table type */
- thd->abort_on_warning= !ignore && thd->is_strict_mode();
/*
Create .FRM for new version of table with a temporary name.
@@ -9920,15 +10122,13 @@ do_continue:;
tmp_disable_binlog(thd);
create_info->options|=HA_CREATE_TMP_ALTER;
create_info->alias= alter_ctx.table_name;
- error= create_table_impl(thd,
- &alter_ctx.db, &alter_ctx.table_name,
- &alter_ctx.new_db, &alter_ctx.tmp_name,
+ error= create_table_impl(thd, alter_ctx.db, alter_ctx.table_name,
+ alter_ctx.new_db, alter_ctx.tmp_name,
alter_ctx.get_tmp_path(),
thd->lex->create_info, create_info, alter_info,
C_ALTER_TABLE_FRM_ONLY, NULL,
&key_info, &key_count, &frm);
reenable_binlog(thd);
- thd->abort_on_warning= false;
if (unlikely(error))
{
my_free(const_cast<uchar*>(frm.str));
@@ -9944,7 +10144,8 @@ do_continue:;
key_info, key_count,
IF_PARTITIONING(thd->work_part_info, NULL),
ignore, alter_ctx.error_if_not_empty);
- TABLE *altered_table= NULL;
+ TABLE_SHARE altered_share;
+ TABLE altered_table;
bool use_inplace= true;
/* Fill the Alter_inplace_info structure. */
@@ -9973,11 +10174,10 @@ do_continue:;
Also note that we ignore the LOCK clause here.
- TODO don't create the frm in the first place
+ TODO don't create partitioning metadata in the first place
*/
- const char *path= alter_ctx.get_tmp_path();
- table->file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG);
- deletefrm(path);
+ table->file->ha_create_partitioning_metadata(alter_ctx.get_tmp_path(),
+ NULL, CHF_DELETE_FLAG);
my_free(const_cast<uchar*>(frm.str));
goto end_inplace;
}
@@ -9985,44 +10185,43 @@ do_continue:;
// We assume that the table is non-temporary.
DBUG_ASSERT(!table->s->tmp_table);
- if (!(altered_table=
- thd->create_and_open_tmp_table(new_db_type, &frm,
- alter_ctx.get_tmp_path(),
- alter_ctx.new_db.str,
- alter_ctx.new_name.str,
- false, true)))
+ if (create_table_for_inplace_alter(thd, alter_ctx, &frm, &altered_share,
+ &altered_table))
goto err_new_table_cleanup;
/* Set markers for fields in TABLE object for altered table. */
- update_altered_table(ha_alter_info, altered_table);
+ update_altered_table(ha_alter_info, &altered_table);
/*
Mark all columns in 'altered_table' as used to allow usage
of its record[0] buffer and Field objects during in-place
ALTER TABLE.
*/
- altered_table->column_bitmaps_set_no_signal(&altered_table->s->all_set,
- &altered_table->s->all_set);
- restore_record(altered_table, s->default_values); // Create empty record
+ altered_table.column_bitmaps_set_no_signal(&altered_table.s->all_set,
+ &altered_table.s->all_set);
+ restore_record(&altered_table, s->default_values); // Create empty record
/* Check that we can call default functions with default field values */
thd->count_cuted_fields= CHECK_FIELD_EXPRESSION;
- altered_table->reset_default_fields();
- if (altered_table->default_field &&
- altered_table->update_default_fields(true))
+ altered_table.reset_default_fields();
+ if (altered_table.default_field &&
+ altered_table.update_default_fields(true))
+ {
+ cleanup_table_after_inplace_alter(&altered_table);
goto err_new_table_cleanup;
+ }
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
if (alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_NONE)
ha_alter_info.online= true;
// Ask storage engine whether to use copy or in-place
ha_alter_info.inplace_supported=
- table->file->check_if_supported_inplace_alter(altered_table,
+ table->file->check_if_supported_inplace_alter(&altered_table,
&ha_alter_info);
if (alter_info->supports_algorithm(thd, &ha_alter_info) ||
alter_info->supports_lock(thd, &ha_alter_info))
{
- thd->drop_temporary_table(altered_table, NULL, false);
+ cleanup_table_after_inplace_alter(&altered_table);
goto err_new_table_cleanup;
}
@@ -10046,20 +10245,22 @@ do_continue:;
for alter table.
*/
Check_level_instant_set check_level_save(thd, CHECK_FIELD_WARN);
- int res= mysql_inplace_alter_table(thd, table_list, table, altered_table,
+ int res= mysql_inplace_alter_table(thd, table_list, table, &altered_table,
&ha_alter_info,
&target_mdl_request, &alter_ctx);
my_free(const_cast<uchar*>(frm.str));
if (res)
+ {
+ cleanup_table_after_inplace_alter(&altered_table);
DBUG_RETURN(true);
+ }
+ cleanup_table_after_inplace_alter_keep_files(&altered_table);
goto end_inplace;
}
else
- {
- thd->drop_temporary_table(altered_table, NULL, false);
- }
+ cleanup_table_after_inplace_alter_keep_files(&altered_table);
}
/* ALTER TABLE using copy algorithm. */
@@ -10113,15 +10314,15 @@ do_continue:;
no_ha_table= false;
DEBUG_SYNC(thd, "alter_table_intermediate_table_created");
- new_table=
- thd->create_and_open_tmp_table(new_db_type, &frm, alter_ctx.get_tmp_path(),
- alter_ctx.new_db.str,
- alter_ctx.new_name.str,
- true, true);
+ /* Open the table since we need to copy the data. */
+ new_table= thd->create_and_open_tmp_table(&frm,
+ alter_ctx.get_tmp_path(),
+ alter_ctx.new_db.str,
+ alter_ctx.new_name.str,
+ true);
if (!new_table)
goto err_new_table_cleanup;
- /* Open the table since we need to copy the data. */
if (table->s->tmp_table != NO_TMP_TABLE)
{
/* in case of alter temp table send the tracker in OK packet */
@@ -10241,7 +10442,7 @@ do_continue:;
close_all_tables_for_name(thd, table->s,
alter_ctx.is_table_renamed() ?
- HA_EXTRA_PREPARE_FOR_RENAME:
+ HA_EXTRA_PREPARE_FOR_RENAME:
HA_EXTRA_NOT_USED,
NULL);
table_list->table= table= NULL; /* Safety */
@@ -10366,19 +10567,17 @@ err_new_table_cleanup:
if (unlikely(alter_ctx.error_if_not_empty &&
thd->get_stmt_da()->current_row_for_warning()))
{
- const char *f_val= 0;
- enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
+ const char *f_val= "0000-00-00";
+ const char *f_type= "date";
switch (alter_ctx.datetime_field->real_field_type())
{
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_NEWDATE:
- f_val= "0000-00-00";
- t_type= MYSQL_TIMESTAMP_DATE;
break;
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_DATETIME2:
f_val= "0000-00-00 00:00:00";
- t_type= MYSQL_TIMESTAMP_DATETIME;
+ f_type= "datetime";
break;
default:
/* Shouldn't get here. */
@@ -10386,11 +10585,12 @@ err_new_table_cleanup:
}
bool save_abort_on_warning= thd->abort_on_warning;
thd->abort_on_warning= true;
- make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
- f_val, strlength(f_val), t_type,
- alter_ctx.new_db.str,
- alter_ctx.new_name.str,
- alter_ctx.datetime_field->field_name.str);
+ thd->push_warning_truncated_value_for_field(Sql_condition::WARN_LEVEL_WARN,
+ f_type, f_val,
+ alter_ctx.new_db.str,
+ alter_ctx.new_name.str,
+ alter_ctx.datetime_field->
+ field_name.str);
thd->abort_on_warning= save_abort_on_warning;
}
@@ -10438,7 +10638,7 @@ bool mysql_trans_prepare_alter_copy_data(THD *thd)
/*
Turn off recovery logging since rollback of an alter table is to
delete the new table so there is no need to log the changes to it.
-
+
This needs to be done before external_lock.
*/
DBUG_RETURN(ha_enable_transaction(thd, FALSE) != 0);
@@ -10505,6 +10705,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
bool make_versioned= !from->versioned() && to->versioned();
bool make_unversioned= from->versioned() && !to->versioned();
bool keep_versioned= from->versioned() && to->versioned();
+ bool bulk_insert_started= 0;
Field *to_row_start= NULL, *to_row_end= NULL, *from_row_end= NULL;
MYSQL_TIME query_start;
DBUG_ENTER("copy_data_between_tables");
@@ -10530,12 +10731,11 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
DBUG_RETURN(-1);
}
+ backup_set_alter_copy_lock(thd, from);
+
alter_table_manage_keys(to, from->file->indexes_are_disabled(), keys_onoff);
- /* Set read map for all fields in from table */
from->default_column_bitmaps();
- bitmap_set_all(from->read_set);
- from->file->column_bitmaps_signal();
/* We can abort alter table for any table type */
thd->abort_on_warning= !ignore && thd->is_strict_mode();
@@ -10544,6 +10744,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
to->file->extra(HA_EXTRA_PREPARE_FOR_ALTER_TABLE);
to->file->ha_start_bulk_insert(from->file->stats.records,
ignore ? 0 : HA_CREATE_UNIQUE_INDEX_BY_SORT);
+ bulk_insert_started= 1;
List_iterator<Create_field> it(create);
Create_field *def;
copy_end=copy;
@@ -10565,7 +10766,11 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
if (def->field == from->found_next_number_field)
thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO;
}
- (copy_end++)->set(*ptr,def->field,0);
+ if (!(*ptr)->vcol_info)
+ {
+ bitmap_set_bit(from->read_set, def->field->field_index);
+ (copy_end++)->set(*ptr,def->field,0);
+ }
}
else
{
@@ -10593,7 +10798,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
char warn_buff[MYSQL_ERRMSG_SIZE];
bool save_abort_on_warning= thd->abort_on_warning;
thd->abort_on_warning= false;
- my_snprintf(warn_buff, sizeof(warn_buff),
+ my_snprintf(warn_buff, sizeof(warn_buff),
"ORDER BY ignored as there is a user-defined clustered index"
" in the table '%-.192s'", from->s->table_name.str);
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
@@ -10611,8 +10816,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
Filesort_tracker dummy_tracker(false);
Filesort fsort(order, HA_POS_ERROR, true, NULL);
- if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
- setup_order(thd, thd->lex->select_lex.ref_pointer_array,
+ if (thd->lex->first_select_lex()->setup_ref_array(thd, order_num) ||
+ setup_order(thd, thd->lex->first_select_lex()->ref_pointer_array,
&tables, fields, all_fields, order))
goto err;
@@ -10633,6 +10838,11 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
from_row_end= from->vers_end_field();
}
+ if (from_row_end)
+ bitmap_set_bit(from->read_set, from_row_end->field_index);
+
+ from->file->column_bitmaps_signal();
+
THD_STAGE_INFO(thd, stage_copy_to_tmp_table);
/* Tell handler that we have values for all columns in the to table */
to->use_all_columns();
@@ -10797,17 +11007,24 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
to->file->print_error(my_errno,MYF(0));
error= 1;
}
+ bulk_insert_started= 0;
if (!ignore)
to->file->extra(HA_EXTRA_END_ALTER_COPY);
cleanup_done= 1;
to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+ if (backup_reset_alter_copy_lock(thd))
+ error= 1;
+
if (unlikely(mysql_trans_commit_alter_copy_data(thd)))
error= 1;
err:
- /* Free resources */
+ if (bulk_insert_started)
+ (void) to->file->ha_end_bulk_insert();
+
+/* Free resources */
if (init_read_record_done)
end_read_record(&info);
delete [] copy;
@@ -10822,7 +11039,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
if (!cleanup_done)
{
- /* This happens if we get an error during initialzation of data */
+ /* This happens if we get an error during initialization of data */
DBUG_ASSERT(error);
to->file->ha_end_bulk_insert();
ha_enable_transaction(thd, TRUE);
@@ -10961,7 +11178,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
(((t->file->ha_table_flags() & HA_HAS_OLD_CHECKSUM) && thd->variables.old_mode) ||
((t->file->ha_table_flags() & HA_HAS_NEW_CHECKSUM) && !thd->variables.old_mode)))
{
- if (t->file->info(HA_STATUS_VARIABLE))
+ if (t->file->info(HA_STATUS_VARIABLE) || t->file->stats.checksum_null)
protocol->store_null();
else
protocol->store((longlong)t->file->stats.checksum);
@@ -10981,7 +11198,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
thd->protocol->remove_last_row();
goto err;
}
- if (error)
+ if (error || t->file->stats.checksum_null)
protocol->store_null();
else
protocol->store((longlong)t->file->stats.checksum);
@@ -11065,10 +11282,10 @@ bool check_engine(THD *thd, const char *db_name,
if (req_engine && req_engine != *new_engine)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_WARN_USING_OTHER_HANDLER,
+ ER_WARN_USING_OTHER_HANDLER,
ER_THD(thd, ER_WARN_USING_OTHER_HANDLER),
- ha_resolve_storage_engine_name(*new_engine),
- table_name);
+ ha_resolve_storage_engine_name(*new_engine),
+ table_name);
}
if (create_info->tmp_table() &&
ha_check_storage_engine_flag(*new_engine, HTON_TEMPORARY_NOT_SUPPORTED))
@@ -11091,7 +11308,7 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
{
DBUG_ENTER("Sql_cmd_create_table::execute");
LEX *lex= thd->lex;
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
TABLE_LIST *first_table= select_lex->table_list.first;
DBUG_ASSERT(first_table == lex->query_tables);
DBUG_ASSERT(first_table != 0);
@@ -11179,8 +11396,8 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
{
create_info.used_fields&= ~HA_CREATE_USED_CHARSET;
create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
- create_info.default_table_charset= create_info.table_charset;
- create_info.table_charset= 0;
+ create_info.default_table_charset= create_info.alter_table_convert_to_charset;
+ create_info.alter_table_convert_to_charset= 0;
}
/*
@@ -11210,7 +11427,7 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
}
#endif
- if (select_lex->item_list.elements) // With select
+ if (select_lex->item_list.elements || select_lex->tvc) // With select or TVC
{
select_result *result;
@@ -11279,11 +11496,7 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
goto end_with_restore_list;
}
- /* Copy temporarily the statement flags to thd for lock_table_names() */
- uint save_thd_create_info_options= thd->lex->create_info.options;
- thd->lex->create_info.options|= create_info.options;
res= open_and_lock_tables(thd, create_info, lex->query_tables, TRUE, 0);
- thd->lex->create_info.options= save_thd_create_info_options;
if (unlikely(res))
{
/* Got error or warning. Set res to 1 if error */
@@ -11355,10 +11568,9 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
}
else
{
- if (create_info.vers_fix_system_fields(thd, &alter_info, *create_table) ||
- create_info.vers_check_system_fields(thd, &alter_info,
- create_table->table_name,
- create_table->db))
+ if (create_info.fix_create_fields(thd, &alter_info, *create_table) ||
+ create_info.check_fields(thd, &alter_info,
+ create_table->table_name, create_table->db))
goto end_with_restore_list;
/*
@@ -11372,7 +11584,7 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
(!thd->is_current_stmt_binlog_format_row() ||
!create_info.tmp_table()))
{
- WSREP_TO_ISOLATION_BEGIN(create_table->db.str, create_table->table_name.str, NULL)
+ WSREP_TO_ISOLATION_BEGIN(create_table->db.str, create_table->table_name.str, NULL);
}
/* Regular CREATE TABLE */
res= mysql_create_table(thd, create_table, &create_info, &alter_info);
@@ -11395,6 +11607,8 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
end_with_restore_list:
DBUG_RETURN(res);
-WSREP_ERROR_LABEL:
+#ifdef WITH_WSREP
+wsrep_error_label:
DBUG_RETURN(true);
+#endif
}
diff --git a/sql/sql_table.h b/sql/sql_table.h
index 0ccd1bfe827..62b61684286 100644
--- a/sql/sql_table.h
+++ b/sql/sql_table.h
@@ -198,8 +198,8 @@ int mysql_create_table_no_lock(THD *thd, const LEX_CSTRING *db,
int create_table_mode, TABLE_LIST *table);
handler *mysql_create_frm_image(THD *thd,
- const LEX_CSTRING *db,
- const LEX_CSTRING *table_name,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &table_name,
HA_CREATE_INFO *create_info,
Alter_info *alter_info,
int create_table_mode,
@@ -252,8 +252,6 @@ bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db,
const char *table_path=0);
void close_cached_table(THD *thd, TABLE *table);
void sp_prepare_create_field(THD *thd, Column_definition *sql_field);
-CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field,
- HA_CREATE_INFO *create_info);
bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags);
int write_bin_log(THD *thd, bool clear_error,
char const *query, ulong query_length,
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 5a5e73c5b1a..5ea132c83d4 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -24,6 +24,7 @@
#include "sql_show.h" // calc_sum_of_all_status
#include "sql_select.h"
#include "keycaches.h"
+#include "my_json_writer.h"
#include <hash.h>
#include <thr_alarm.h>
#if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_H)
@@ -36,6 +37,8 @@
#include "events.h"
#endif
+#define FT_KEYPART (MAX_FIELDS+10)
+
static const char *lock_descriptions[] =
{
/* TL_UNLOCK */ "No lock",
@@ -225,8 +228,6 @@ TEST_join(JOIN *join)
}
-#define FT_KEYPART (MAX_FIELDS+10)
-
static void print_keyuse(KEYUSE *keyuse)
{
char buff[256];
@@ -263,7 +264,6 @@ void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array)
DBUG_UNLOCK_FILE;
}
-
/*
Print the current state during query optimization.
@@ -294,7 +294,6 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time,
double current_read_time, const char *info)
{
uint i;
- POSITION pos;
JOIN_TAB *join_table;
JOIN_TAB **plan_nodes;
TABLE* table;
@@ -321,8 +320,8 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time,
fputs(" POSITIONS: ", DBUG_FILE);
for (i= 0; i < idx ; i++)
{
- pos = join->positions[i];
- table= pos.table->table;
+ POSITION *pos= join->positions + i;
+ table= pos->table->table;
if (table)
fputs(table->s->table_name.str, DBUG_FILE);
fputc(' ', DBUG_FILE);
@@ -338,8 +337,8 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time,
fputs("BEST_POSITIONS: ", DBUG_FILE);
for (i= 0; i < idx ; i++)
{
- pos= join->best_positions[i];
- table= pos.table->table;
+ POSITION *pos= join->best_positions + i;
+ table= pos->table->table;
if (table)
fputs(table->s->table_name.str, DBUG_FILE);
fputc(' ', DBUG_FILE);
@@ -660,3 +659,27 @@ Memory allocated by threads: %s\n",
puts("");
fflush(stdout);
}
+
+void print_keyuse_array_for_trace(THD *thd, DYNAMIC_ARRAY *keyuse_array)
+{
+ DBUG_ASSERT(thd->trace_started());
+
+ Json_writer_object wrapper(thd);
+ Json_writer_array trace_key_uses(thd, "ref_optimizer_key_uses");
+
+ for (uint i=0; i < keyuse_array->elements; i++)
+ {
+ KEYUSE *keyuse= (KEYUSE*)dynamic_array_ptr(keyuse_array, i);
+ Json_writer_object keyuse_elem(thd);
+ keyuse_elem.add_table_name(keyuse->table->reginfo.join_tab);
+ keyuse_elem.add("field", (keyuse->keypart == FT_KEYPART) ? "<fulltext>":
+ (keyuse->is_for_hash_join() ?
+ keyuse->table->field[keyuse->keypart]
+ ->field_name.str :
+ keyuse->table->key_info[keyuse->key]
+ .key_part[keyuse->keypart]
+ .field->field_name.str));
+ keyuse_elem.add("equals",keyuse->val);
+ keyuse_elem.add("null_rejecting",keyuse->null_rejecting);
+ }
+}
diff --git a/sql/sql_test.h b/sql/sql_test.h
index 58262580a75..2ab305acd66 100644
--- a/sql/sql_test.h
+++ b/sql/sql_test.h
@@ -17,6 +17,7 @@
#define SQL_TEST_INCLUDED
#include "mysqld.h"
+#include "opt_trace_context.h"
class JOIN;
struct TABLE_LIST;
@@ -34,6 +35,7 @@ void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array);
void print_sjm(SJ_MATERIALIZATION_INFO *sjm);
void dump_TABLE_LIST_graph(SELECT_LEX *select_lex, TABLE_LIST* tl);
#endif
+void print_keyuse_array_for_trace(THD *thd, DYNAMIC_ARRAY *keyuse_array);
void mysql_print_status();
#endif /* SQL_TEST_INCLUDED */
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index cb09daff6f5..abc91907a55 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -174,7 +174,7 @@ int calc_weekday(long daynr,bool sunday_first_day_of_week)
next week is week 1.
*/
-uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year)
+uint calc_week(const MYSQL_TIME *l_time, uint week_behaviour, uint *year)
{
uint days;
ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day);
@@ -288,15 +288,15 @@ ulong convert_month_to_period(ulong month)
bool
-check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date,
- timestamp_type ts_type)
+check_date_with_warn(THD *thd, const MYSQL_TIME *ltime,
+ date_conv_mode_t fuzzydate, timestamp_type ts_type)
{
int unused;
- if (check_date(ltime, fuzzy_date, &unused))
+ if (check_date(ltime, fuzzydate, &unused))
{
ErrConvTime str(ltime);
- make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
- &str, ts_type, NULL, NULL, NULL);
+ make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ &str, ts_type, nullptr, nullptr, nullptr);
return true;
}
return false;
@@ -304,7 +304,7 @@ check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date,
bool
-adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec)
+adjust_time_range_with_warn(THD *thd, MYSQL_TIME *ltime, uint dec)
{
MYSQL_TIME copy= *ltime;
ErrConvTime str(&copy);
@@ -312,8 +312,7 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec)
if (check_time_range(ltime, dec, &warnings))
return true;
if (warnings)
- make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
- &str, MYSQL_TIMESTAMP_TIME, NULL, NULL, NULL);
+ thd->push_warning_truncated_wrong_value("time", str.ptr());
return false;
}
@@ -351,33 +350,70 @@ to_ascii(CHARSET_INFO *cs,
}
-/* Character set-aware version of str_to_time() */
-bool
-str_to_time(CHARSET_INFO *cs, const char *str, size_t length,
- MYSQL_TIME *l_time, ulonglong fuzzydate, MYSQL_TIME_STATUS *status)
+class TemporalAsciiBuffer: public LEX_CSTRING
{
char cnv[32];
- if ((cs->state & MY_CS_NONASCII) != 0)
+public:
+ TemporalAsciiBuffer(const char *str, size_t length, CHARSET_INFO *cs)
{
- length= to_ascii(cs, str, length, cnv, sizeof(cnv));
- str= cnv;
+ if ((cs->state & MY_CS_NONASCII) != 0)
+ {
+ LEX_CSTRING::str= cnv;
+ LEX_CSTRING::length= to_ascii(cs, str, length, cnv, sizeof(cnv));
+ }
+ else
+ {
+ LEX_CSTRING::str= str;
+ LEX_CSTRING::length= length;
+ }
}
- return str_to_time(str, length, l_time, fuzzydate, status);
+};
+
+
+/* Character set-aware version of ascii_to_datetime_or_date_or_time() */
+bool Temporal::str_to_datetime_or_date_or_time(THD *thd, MYSQL_TIME_STATUS *st,
+ const char *str, size_t length,
+ CHARSET_INFO *cs,
+ date_mode_t fuzzydate)
+{
+ TemporalAsciiBuffer tmp(str, length, cs);
+ return ascii_to_datetime_or_date_or_time(st, tmp.str, tmp.length, fuzzydate)||
+ add_nanoseconds(thd, &st->warnings, fuzzydate, st->nanoseconds);
}
-/* Character set-aware version of str_to_datetime() */
-bool str_to_datetime(CHARSET_INFO *cs, const char *str, size_t length,
- MYSQL_TIME *l_time, ulonglong flags,
- MYSQL_TIME_STATUS *status)
+/* Character set-aware version of str_to_datetime_or_date() */
+bool Temporal::str_to_datetime_or_date(THD *thd, MYSQL_TIME_STATUS *status,
+ const char *str, size_t length,
+ CHARSET_INFO *cs,
+ date_mode_t flags)
{
- char cnv[32];
- if ((cs->state & MY_CS_NONASCII) != 0)
- {
- length= to_ascii(cs, str, length, cnv, sizeof(cnv));
- str= cnv;
- }
- return str_to_datetime(str, length, l_time, flags, status);
+ TemporalAsciiBuffer tmp(str, length, cs);
+ return ascii_to_datetime_or_date(status, tmp.str, tmp.length, flags) ||
+ add_nanoseconds(thd, &status->warnings, flags, status->nanoseconds);
+}
+
+
+/* Character set-aware version of ascii_to_temporal() */
+bool Temporal::str_to_temporal(THD *thd, MYSQL_TIME_STATUS *status,
+ const char *str, size_t length, CHARSET_INFO *cs,
+ date_mode_t flags)
+{
+ TemporalAsciiBuffer tmp(str, length, cs);
+ return ascii_to_temporal(status, tmp.str, tmp.length, flags) ||
+ add_nanoseconds(thd, &status->warnings, flags, status->nanoseconds);
+}
+
+
+/* Character set-aware version of str_to_DDhhmmssff() */
+bool Interval_DDhhmmssff::str_to_DDhhmmssff(MYSQL_TIME_STATUS *status,
+ const char *str, size_t length,
+ CHARSET_INFO *cs, ulong max_hour)
+{
+ TemporalAsciiBuffer tmp(str, length, cs);
+ bool rc= ::str_to_DDhhmmssff(tmp.str, tmp.length, this, UINT_MAX32, status);
+ DBUG_ASSERT(status->warnings || !rc);
+ return rc;
}
@@ -386,127 +422,59 @@ bool str_to_datetime(CHARSET_INFO *cs, const char *str, size_t length,
if string was truncated during conversion.
NOTE
- See description of str_to_datetime() for more information.
+ See description of str_to_datetime_xxx() for more information.
*/
bool
-str_to_datetime_with_warn(CHARSET_INFO *cs,
- const char *str, size_t length, MYSQL_TIME *l_time,
- ulonglong flags)
+str_to_datetime_with_warn(THD *thd, CHARSET_INFO *cs,
+ const char *str, size_t length, MYSQL_TIME *to,
+ date_mode_t mode)
{
- MYSQL_TIME_STATUS status;
- THD *thd= current_thd;
- bool ret_val= str_to_datetime(cs, str, length, l_time, flags, &status);
- if (ret_val || status.warnings)
- make_truncated_value_warning(thd,
- ret_val ? Sql_condition::WARN_LEVEL_WARN :
- Sql_condition::time_warn_level(status.warnings),
- str, length, flags & TIME_TIME_ONLY ?
- MYSQL_TIMESTAMP_TIME : l_time->time_type,
- NULL, NULL, NULL);
- DBUG_EXECUTE_IF("str_to_datetime_warn",
- push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_YES, str););
- return ret_val;
+ Temporal::Warn_push warn(thd, nullptr, nullptr, nullptr, to, mode);
+ Temporal_hybrid *t= new(to) Temporal_hybrid(thd, &warn, str, length, cs, mode);
+ return !t->is_valid_temporal();
}
-/**
- converts a pair of numbers (integer part, microseconds) to MYSQL_TIME
-
- @param neg sign of the time value
- @param nr integer part of the number to convert
- @param sec_part microsecond part of the number
- @param ltime converted value will be written here
- @param fuzzydate conversion flags (TIME_INVALID_DATE, etc)
- @param str original number, as an ErrConv. For the warning
- @param field_name field name or NULL if not a field. For the warning
-
- @returns 0 for success, 1 for a failure
-*/
-static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part,
- MYSQL_TIME *ltime, ulonglong fuzzydate,
- const ErrConv *str,
- const TABLE_SHARE *s, const char *field_name)
-{
- int was_cut;
- longlong res;
- enum_mysql_timestamp_type ts_type;
- bool have_warnings= false;
-
- if (fuzzydate & TIME_TIME_ONLY)
- {
- fuzzydate= TIME_TIME_ONLY; // clear other flags
- ts_type= MYSQL_TIMESTAMP_TIME;
- res= number_to_time(neg, nr, sec_part, ltime, &was_cut);
- have_warnings= MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut);
- }
- else
- {
- ts_type= MYSQL_TIMESTAMP_DATETIME;
- if (neg)
- {
- res= -1;
- }
- else
- {
- res= number_to_datetime(nr, sec_part, ltime, fuzzydate, &was_cut);
- have_warnings= was_cut && (fuzzydate & TIME_NO_ZERO_IN_DATE);
- }
- }
-
- if (res < 0 || have_warnings)
- {
- make_truncated_value_warning(current_thd,
- Sql_condition::WARN_LEVEL_WARN, str,
- res < 0 ? MYSQL_TIMESTAMP_ERROR : ts_type,
- s ? s->db.str : NULL,
- s ? s->table_name.str : NULL, field_name);
- }
- return res < 0;
-}
-
-
-bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
- ulonglong fuzzydate,
+bool double_to_datetime_with_warn(THD *thd, double value, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate,
const TABLE_SHARE *s, const char *field_name)
{
- const ErrConvDouble str(value);
- bool neg= value < 0;
-
- if (neg)
- value= -value;
-
- if (value > static_cast<double>(LONGLONG_MAX))
- value= static_cast<double>(LONGLONG_MAX);
-
- longlong nr= static_cast<ulonglong>(floor(value));
- uint sec_part= static_cast<ulong>((value - floor(value))*TIME_SECOND_PART_FACTOR);
- return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str,
- s, field_name);
+ Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
+ s ? s->table_name.str : nullptr,
+ field_name, ltime, fuzzydate);
+ Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, value, fuzzydate);
+ return !t->is_valid_temporal();
}
-bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime,
- ulonglong fuzzydate,
+bool decimal_to_datetime_with_warn(THD *thd, const my_decimal *value,
+ MYSQL_TIME *ltime,
+ date_mode_t fuzzydate,
const TABLE_SHARE *s, const char *field_name)
{
- const ErrConvDecimal str(value);
- ulonglong nr;
- ulong sec_part;
- bool neg= my_decimal2seconds(value, &nr, &sec_part);
- return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str,
- s, field_name);
+ Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
+ s ? s->table_name.str : nullptr,
+ field_name, ltime, fuzzydate);
+ Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, value, fuzzydate);
+ return !t->is_valid_temporal();
}
-bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime,
- ulonglong fuzzydate,
+bool int_to_datetime_with_warn(THD *thd, const Longlong_hybrid &nr,
+ MYSQL_TIME *ltime,
+ date_mode_t fuzzydate,
const TABLE_SHARE *s, const char *field_name)
{
- const ErrConvInteger str(neg ? - (longlong) value : (longlong) value, !neg);
- return number_to_time_with_warn(neg, value, 0, ltime,
- fuzzydate, &str, s, field_name);
+ /*
+ Note: conversion from an integer to TIME can overflow to '838:59:59.999999',
+ so the conversion result can have fractional digits.
+ */
+ Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
+ s ? s->table_name.str : nullptr,
+ field_name, ltime, fuzzydate);
+ Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, nr, fuzzydate);
+ return !t->is_valid_temporal();
}
@@ -553,7 +521,7 @@ void localtime_to_TIME(MYSQL_TIME *to, struct tm *from)
}
-void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds)
+void calc_time_from_sec(MYSQL_TIME *to, ulong seconds, ulong microseconds)
{
long t_seconds;
// to->neg is not cleared, it may already be set to a useful value
@@ -937,47 +905,10 @@ void make_truncated_value_warning(THD *thd,
const char *db_name, const char *table_name,
const char *field_name)
{
- char warn_buff[MYSQL_ERRMSG_SIZE];
- const char *type_str;
- CHARSET_INFO *cs= &my_charset_latin1;
-
- switch (time_type) {
- case MYSQL_TIMESTAMP_DATE:
- type_str= "date";
- break;
- case MYSQL_TIMESTAMP_TIME:
- type_str= "time";
- break;
- case MYSQL_TIMESTAMP_DATETIME: // FALLTHROUGH
- default:
- type_str= "datetime";
- break;
- }
- if (field_name)
- {
- if (!db_name)
- db_name= "";
- if (!table_name)
- table_name= "";
-
- cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
- ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- type_str, sval->ptr(),
- db_name, table_name, field_name,
- (ulong) thd->get_stmt_da()->current_row_for_warning());
- }
- else
- {
- if (time_type > MYSQL_TIMESTAMP_ERROR)
- cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
- ER_THD(thd, ER_TRUNCATED_WRONG_VALUE),
- type_str, sval->ptr());
- else
- cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
- ER_THD(thd, ER_WRONG_VALUE), type_str, sval->ptr());
- }
- push_warning(thd, level,
- ER_TRUNCATED_WRONG_VALUE, warn_buff);
+ const char *type_str= Temporal::type_name_by_timestamp_type(time_type);
+ return thd->push_warning_wrong_or_truncated_value
+ (level, time_type <= MYSQL_TIMESTAMP_ERROR, type_str, sval->ptr(),
+ db_name, table_name, field_name);
}
@@ -988,8 +919,8 @@ void make_truncated_value_warning(THD *thd,
(X)->second_part)
#define GET_PART(X, N) X % N ## LL; X/= N ## LL
-bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
- const INTERVAL &interval)
+bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type,
+ const INTERVAL &interval, bool push_warn)
{
long period, sign;
@@ -1102,8 +1033,8 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
return 0; // Ok
invalid_date:
+ if (push_warn)
{
- THD *thd= current_thd;
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_DATETIME_FUNCTION_OVERFLOW,
ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW),
@@ -1143,7 +1074,7 @@ null_date:
bool
calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
- int l_sign, longlong *seconds_out, long *microseconds_out)
+ int l_sign, ulonglong *seconds_out, ulong *microseconds_out)
{
long days;
bool neg;
@@ -1170,10 +1101,10 @@ calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
}
microseconds= ((longlong)days * SECONDS_IN_24H +
- (longlong)(l_time1->hour*3600L +
+ (longlong)(l_time1->hour*3600LL +
l_time1->minute*60L +
l_time1->second) -
- l_sign*(longlong)(l_time2->hour*3600L +
+ l_sign*(longlong)(l_time2->hour*3600LL +
l_time2->minute*60L +
l_time2->second)) * 1000000LL +
(longlong)l_time1->second_part -
@@ -1185,17 +1116,17 @@ calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
microseconds= -microseconds;
neg= 1;
}
- *seconds_out= microseconds/1000000L;
- *microseconds_out= (long) (microseconds%1000000L);
+ *seconds_out= (ulonglong) microseconds/1000000L;
+ *microseconds_out= (ulong) (microseconds%1000000L);
return neg;
}
bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
- int l_sign, MYSQL_TIME *l_time3, ulonglong fuzzydate)
+ int l_sign, MYSQL_TIME *l_time3, date_mode_t fuzzydate)
{
- longlong seconds;
- long microseconds;
+ ulonglong seconds;
+ ulong microseconds;
bzero((char *) l_time3, sizeof(*l_time3));
l_time3->neg= calc_time_diff(l_time1, l_time2, l_sign,
&seconds, &microseconds);
@@ -1214,7 +1145,7 @@ bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
("invalid" means > TIME_MAX_SECOND)
*/
set_if_smaller(seconds, INT_MAX32);
- calc_time_from_sec(l_time3, (long) seconds, microseconds);
+ calc_time_from_sec(l_time3, (ulong) seconds, microseconds);
return ((fuzzydate & TIME_NO_ZERO_DATE) && (seconds == 0) &&
(microseconds == 0));
}
@@ -1271,56 +1202,6 @@ bool time_to_datetime(MYSQL_TIME *ltime)
}
-/**
- Return a valid DATE or DATETIME value from an arbitrary MYSQL_TIME.
- If ltime is TIME, it's first converted to DATETIME.
- If ts_type is DATE, hhmmss is set to zero.
- The date part of the result is checked against fuzzy_date.
-
- @param ltime The value to convert.
- @param fuzzy_date Flags to check date.
- @param ts_type The type to convert to.
- @return false on success, true of error (negative time).*/
-bool
-make_date_with_warn(MYSQL_TIME *ltime, ulonglong fuzzy_date,
- timestamp_type ts_type)
-{
- DBUG_ASSERT(ts_type == MYSQL_TIMESTAMP_DATE ||
- ts_type == MYSQL_TIMESTAMP_DATETIME);
- if (ltime->time_type == MYSQL_TIMESTAMP_TIME && time_to_datetime(ltime))
- {
- /* e.g. negative time */
- ErrConvTime str(ltime);
- make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
- &str, ts_type, NULL, NULL, NULL);
- return true;
- }
- if ((ltime->time_type= ts_type) == MYSQL_TIMESTAMP_DATE)
- ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0;
- return check_date_with_warn(ltime, fuzzy_date, ts_type);
-}
-
-
-/*
- Convert a TIME value to DAY-TIME interval, e.g. for extraction:
- EXTRACT(DAY FROM x), EXTRACT(HOUR FROM x), etc.
- Moves full days from ltime->hour to ltime->day.
- Note, time_type is set to MYSQL_TIMESTAMP_NONE, to make sure that
- the structure is not used for anything else other than extraction:
- non-extraction TIME functions expect zero day value!
-*/
-void time_to_daytime_interval(MYSQL_TIME *ltime)
-{
- DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_TIME);
- DBUG_ASSERT(ltime->year == 0);
- DBUG_ASSERT(ltime->month == 0);
- DBUG_ASSERT(ltime->day == 0);
- ltime->day= ltime->hour / 24;
- ltime->hour%= 24;
- ltime->time_type= MYSQL_TIMESTAMP_NONE;
-}
-
-
/*** Conversion from TIME to DATETIME ***/
/*
@@ -1348,8 +1229,8 @@ mix_date_and_time_complex(MYSQL_TIME *ldate, const MYSQL_TIME *ltime)
{
DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE ||
ldate->time_type == MYSQL_TIMESTAMP_DATETIME);
- longlong seconds;
- long days, useconds;
+ ulonglong seconds;
+ ulong days, useconds;
int sign= ltime->neg ? 1 : -1;
ldate->neg= calc_time_diff(ldate, ltime, sign, &seconds, &useconds);
@@ -1439,7 +1320,7 @@ time_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
bool
time_to_datetime_with_warn(THD *thd,
const MYSQL_TIME *from, MYSQL_TIME *to,
- ulonglong fuzzydate)
+ date_conv_mode_t fuzzydate)
{
int warn= 0;
DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME);
@@ -1455,34 +1336,13 @@ time_to_datetime_with_warn(THD *thd,
check_date(to, fuzzydate, &warn)))
{
ErrConvTime str(from);
- make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
- &str, MYSQL_TIMESTAMP_DATETIME, NULL, NULL, NULL);
+ thd->push_warning_truncated_wrong_value("datetime", str.ptr());
return true;
}
return false;
}
-bool datetime_to_time_with_warn(THD *thd, const MYSQL_TIME *dt,
- MYSQL_TIME *tm, uint dec)
-{
- if (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)
- {
- *tm= *dt;
- datetime_to_time(tm);
- return false;
- }
- else /* new mode */
- {
- MYSQL_TIME current_date;
- set_current_date(thd, &current_date);
- calc_time_diff(dt, &current_date, 1, tm, 0);
- }
- int warnings= 0;
- return check_time_range(tm, dec, &warnings);
-}
-
-
longlong pack_time(const MYSQL_TIME *my_time)
{
return ((((((my_time->year * 13ULL +
diff --git a/sql/sql_time.h b/sql/sql_time.h
index a0eea359c6a..c918eb6d807 100644
--- a/sql/sql_time.h
+++ b/sql/sql_time.h
@@ -17,6 +17,7 @@
#ifndef SQL_TIME_INCLUDED
#define SQL_TIME_INCLUDED
+#include "sql_basic_types.h"
#include "my_time.h"
#include "mysql_time.h" /* timestamp_type */
#include "sql_error.h" /* Sql_condition */
@@ -35,69 +36,28 @@ ulong convert_period_to_month(ulong period);
ulong convert_month_to_period(ulong month);
void set_current_date(THD *thd, MYSQL_TIME *to);
bool time_to_datetime(MYSQL_TIME *ltime);
-void time_to_daytime_interval(MYSQL_TIME *l_time);
bool get_date_from_daynr(long daynr,uint *year, uint *month, uint *day);
my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code);
-bool str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str, size_t length, MYSQL_TIME *l_time,
- ulonglong flags);
-bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
- ulonglong fuzzydate,
+bool str_to_datetime_with_warn(THD *thd,
+ CHARSET_INFO *cs, const char *str, size_t length,
+ MYSQL_TIME *l_time,
+ date_mode_t flags);
+bool double_to_datetime_with_warn(THD *thd, double value, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate,
const TABLE_SHARE *s, const char *name);
-bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime,
- ulonglong fuzzydate,
+bool decimal_to_datetime_with_warn(THD *thd,
+ const my_decimal *value, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate,
const TABLE_SHARE *s, const char *name);
-bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime,
- ulonglong fuzzydate,
+bool int_to_datetime_with_warn(THD *thd, const Longlong_hybrid &nr,
+ MYSQL_TIME *ltime,
+ date_mode_t fuzzydate,
const TABLE_SHARE *s, const char *name);
bool time_to_datetime(THD *thd, const MYSQL_TIME *tm, MYSQL_TIME *dt);
bool time_to_datetime_with_warn(THD *thd,
const MYSQL_TIME *tm, MYSQL_TIME *dt,
- ulonglong fuzzydate);
-/*
- Simply truncate the YYYY-MM-DD part to 0000-00-00
- and change time_type to MYSQL_TIMESTAMP_TIME
-*/
-inline void datetime_to_time(MYSQL_TIME *ltime)
-{
- DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
- ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
- DBUG_ASSERT(ltime->neg == 0);
- ltime->year= ltime->month= ltime->day= 0;
- ltime->time_type= MYSQL_TIMESTAMP_TIME;
-}
-
-
-/**
- Convert DATE/DATETIME to TIME(dec)
- using CURRENT_DATE in a non-old mode,
- or using simple truncation in old mode (OLD_MODE_ZERO_DATE_TIME_CAST).
-
- @param thd - the thread to get the variables.old_behaviour value from
- @param dt - the DATE of DATETIME value to convert
- @param[out] tm - store result here
- @param dec - the desired scale. The fractional part of the result
- is checked according to this parameter before returning
- the conversion result. "dec" is important in the corner
- cases near the max/min limits.
- If the result is '838:59:59.999999' and the desired scale
- is less than 6, an error is returned.
- Note, dec is not important in the
- OLD_MODE_ZERO_DATE_TIME_CAST old mode.
-
- - in case of OLD_MODE_ZERO_DATE_TIME_CAST
- the TIME part is simply truncated and "false" is returned.
- - otherwise, the result is calculated effectively similar to:
- TIMEDIFF(dt, CAST(CURRENT_DATE AS DATETIME))
- If the difference fits into the supported TIME range, "false" is returned,
- otherwise a warning is issued and "true" is returned.
-
- @return false - on success
- @return true - on error
-*/
-bool datetime_to_time_with_warn(THD *, const MYSQL_TIME *dt,
- MYSQL_TIME *tm, uint dec);
-
+ date_conv_mode_t fuzzydate);
inline void datetime_to_date(MYSQL_TIME *ltime)
{
@@ -121,17 +81,6 @@ void make_truncated_value_warning(THD *thd,
const char *db_name, const char *table_name,
const char *field_name);
-static inline void make_truncated_value_warning(THD *thd,
- Sql_condition::enum_warning_level level, const char *str_val,
- size_t str_length, timestamp_type time_type,
- const char *db_name, const char *table_name,
- const char *field_name)
-{
- const ErrConvString str(str_val, str_length, &my_charset_bin);
- make_truncated_value_warning(thd, level, &str, time_type, db_name, table_name,
- field_name);
-}
-
extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type,
const char *format_str,
uint format_length);
@@ -142,10 +91,10 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec);
/* MYSQL_TIME operations */
-bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
- const INTERVAL &interval);
+bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type,
+ const INTERVAL &interval, bool push_warn= true);
bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
- int l_sign, longlong *seconds_out, long *microseconds_out);
+ int l_sign, ulonglong *seconds_out, ulong *microseconds_out);
int append_interval(String *str, interval_type int_type,
const INTERVAL &interval);
/**
@@ -171,26 +120,17 @@ int append_interval(String *str, interval_type int_type,
@return false - otherwise
*/
bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
- int lsign, MYSQL_TIME *l_time3, ulonglong fuzzydate);
+ int lsign, MYSQL_TIME *l_time3, date_mode_t fuzzydate);
int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b);
void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
-void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds);
-uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year);
+void calc_time_from_sec(MYSQL_TIME *to, ulong seconds, ulong microseconds);
+uint calc_week(const MYSQL_TIME *l_time, uint week_behaviour, uint *year);
int calc_weekday(long daynr,bool sunday_first_day_of_week);
bool parse_date_time_format(timestamp_type format_type,
const char *format, uint format_length,
DATE_TIME_FORMAT *date_time_format);
-/* Character set-aware version of str_to_time() */
-bool str_to_time(CHARSET_INFO *cs, const char *str,size_t length,
- MYSQL_TIME *l_time, ulonglong fuzzydate,
- MYSQL_TIME_STATUS *status);
-/* Character set-aware version of str_to_datetime() */
-bool str_to_datetime(CHARSET_INFO *cs,
- const char *str, size_t length,
- MYSQL_TIME *l_time, ulonglong flags,
- MYSQL_TIME_STATUS *status);
/* convenience wrapper */
inline bool parse_date_time_format(timestamp_type format_type,
@@ -227,15 +167,21 @@ non_zero_date(const MYSQL_TIME *ltime)
non_zero_hhmmssuu(ltime));
}
static inline bool
-check_date(const MYSQL_TIME *ltime, ulonglong flags, int *was_cut)
+check_date(const MYSQL_TIME *ltime, date_conv_mode_t flags, int *was_cut)
{
- return check_date(ltime, non_zero_date(ltime), flags, was_cut);
+ return check_date(ltime, non_zero_date(ltime),
+ ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), was_cut);
}
-bool check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date,
- timestamp_type ts_type);
-bool make_date_with_warn(MYSQL_TIME *ltime,
- ulonglong fuzzy_date, timestamp_type ts_type);
-bool adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec);
+bool check_date_with_warn(THD *thd, const MYSQL_TIME *ltime,
+ date_conv_mode_t fuzzy_date, timestamp_type ts_type);
+static inline bool
+check_date_with_warn(THD *thd, const MYSQL_TIME *ltime,
+ date_mode_t fuzzydate, timestamp_type ts_type)
+{
+ return check_date_with_warn(thd, ltime, date_conv_mode_t(fuzzydate), ts_type);
+}
+
+bool adjust_time_range_with_warn(THD *thd, MYSQL_TIME *ltime, uint dec);
longlong pack_time(const MYSQL_TIME *my_time);
void unpack_time(longlong packed, MYSQL_TIME *my_time,
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 247055d397c..5802d2c811e 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -34,19 +34,7 @@
#include "sql_handler.h" // mysql_ha_rm_tables
#include "sp_cache.h" // sp_invalidate_cache
#include <mysys_err.h>
-#ifdef WITH_WSREP
#include "debug_sync.h"
-#endif /* WITH_WSREP */
-
-LEX_CSTRING *make_lex_string(LEX_CSTRING *lex_str,
- const char* str, size_t length,
- MEM_ROOT *mem_root)
-{
- if (!(lex_str->str= strmake_root(mem_root, str, length)))
- return 0;
- lex_str->length= length;
- return lex_str;
-}
/*************************************************************************/
@@ -520,8 +508,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
}
#ifdef WITH_WSREP
- if (thd->wsrep_exec_mode == LOCAL_STATE)
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, tables);
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, tables);
#endif
/* We should have only one table in table list. */
@@ -593,6 +580,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
STRING_WITH_LEN(act)));
};);
#endif /* WITH_WSREP */
+
result= (create ?
table->triggers->create_trigger(thd, tables, &stmt_query):
table->triggers->drop_trigger(thd, tables, &stmt_query));
@@ -635,9 +623,10 @@ end:
my_ok(thd);
DBUG_RETURN(result);
-
-WSREP_ERROR_LABEL:
+#ifdef WITH_WSREP
+wsrep_error_label:
DBUG_RETURN(true);
+#endif
}
@@ -1515,8 +1504,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db,
if (likely((name= error_handler.get_trigger_name())))
{
- if (unlikely(!(make_lex_string(&trigger->name, name->str,
- name->length, &table->mem_root))))
+ trigger->name= safe_lexcstrdup_root(&table->mem_root, *name);
+ if (unlikely(!trigger->name.str))
goto err_with_lex_cleanup;
}
trigger->definer= ((!trg_definer || !trg_definer->length) ?
@@ -2325,12 +2314,10 @@ void Table_triggers_list::mark_fields_used(trg_event_type event)
if (trg_field->field_idx != (uint)-1)
{
DBUG_PRINT("info", ("marking field: %d", trg_field->field_idx));
- bitmap_set_bit(trigger_table->read_set, trg_field->field_idx);
if (trg_field->get_settable_routine_parameter())
bitmap_set_bit(trigger_table->write_set, trg_field->field_idx);
- if (trigger_table->field[trg_field->field_idx]->vcol_info)
- trigger_table->mark_virtual_col(trigger_table->
- field[trg_field->field_idx]);
+ trigger_table->mark_column_with_deps(
+ trigger_table->field[trg_field->field_idx]);
}
}
}
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
index 897db0a2b21..c495a417961 100644
--- a/sql/sql_truncate.cc
+++ b/sql/sql_truncate.cc
@@ -271,6 +271,9 @@ Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref,
bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
bool *hton_can_recreate)
{
+ handlerton *hton;
+ bool versioned;
+ bool sequence= false;
TABLE *table= NULL;
DBUG_ENTER("Sql_cmd_truncate_table::lock_table");
@@ -298,43 +301,43 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
table_ref->table_name.str, NULL)))
DBUG_RETURN(TRUE);
- *hton_can_recreate= ha_check_storage_engine_flag(table->file->ht,
- HTON_CAN_RECREATE);
+ versioned= table->versioned();
+ hton= table->file->ht;
table_ref->mdl_request.ticket= table->mdl_ticket;
}
else
{
- handlerton *hton;
- bool is_sequence;
-
- /* Acquire an exclusive lock. */
DBUG_ASSERT(table_ref->next_global == NULL);
if (lock_table_names(thd, table_ref, NULL,
thd->variables.lock_wait_timeout, 0))
DBUG_RETURN(TRUE);
- if (!ha_table_exists(thd, &table_ref->db, &table_ref->table_name,
- &hton, &is_sequence) ||
- hton == view_pseudo_hton)
+ TABLE_SHARE *share= tdc_acquire_share(thd, table_ref, GTS_TABLE | GTS_VIEW);
+ if (share == NULL)
+ DBUG_RETURN(TRUE);
+ DBUG_ASSERT(share != UNUSABLE_TABLE_SHARE);
+
+ versioned= share->versioned;
+ sequence= share->table_type == TABLE_TYPE_SEQUENCE;
+ hton= share->db_type();
+
+ tdc_release_share(share);
+
+ if (hton == view_pseudo_hton)
{
my_error(ER_NO_SUCH_TABLE, MYF(0), table_ref->db.str,
table_ref->table_name.str);
DBUG_RETURN(TRUE);
}
+ }
- if (!hton)
- {
- /*
- The table exists, but its storage engine is unknown, perhaps not
- loaded at the moment. We need to open and parse the frm to know the
- storage engine in question, so let's proceed with the truncation and
- try to open the table. This will produce the correct error message
- about unknown engine.
- */
- *hton_can_recreate= false;
- }
- else
- *hton_can_recreate= !is_sequence && hton->flags & HTON_CAN_RECREATE;
+ *hton_can_recreate= !sequence
+ && ha_check_storage_engine_flag(hton, HTON_CAN_RECREATE);
+
+ if (versioned)
+ {
+ my_error(ER_VERS_NOT_SUPPORTED, MYF(0), "TRUNCATE TABLE");
+ DBUG_RETURN(TRUE);
}
/*
@@ -412,9 +415,28 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
{
bool hton_can_recreate;
- if (WSREP(thd) &&
- wsrep_to_isolation_begin(thd, table_ref->db.str, table_ref->table_name.str, 0))
- DBUG_RETURN(TRUE);
+#ifdef WITH_WSREP
+ if (WSREP(thd) && wsrep_thd_is_local(thd))
+ {
+ wsrep::key_array keys;
+ /* Do not start TOI if table is not found */
+ if (!wsrep_append_fk_parent_table(thd, table_ref, &keys))
+ {
+ if (keys.empty())
+ {
+ WSREP_TO_ISOLATION_BEGIN_IF(table_ref->db.str, table_ref->table_name.str, NULL)
+ {
+ DBUG_RETURN(TRUE);
+ }
+ } else {
+ WSREP_TO_ISOLATION_BEGIN_FK_TABLES(NULL, NULL, table_ref, &keys)
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+ }
+#endif /* WITH_WSREP */
if (lock_table(thd, table_ref, &hton_can_recreate))
DBUG_RETURN(TRUE);
@@ -500,7 +522,7 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
bool Sql_cmd_truncate_table::execute(THD *thd)
{
bool res= TRUE;
- TABLE_LIST *table= thd->lex->select_lex.table_list.first;
+ TABLE_LIST *table= thd->lex->first_select_lex()->table_list.first;
DBUG_ENTER("Sql_cmd_truncate_table::execute");
if (check_one_table_access(thd, DROP_ACL, table))
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index 96c5223ee6a..63e9e76e135 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -181,7 +181,7 @@ bool get_type_attributes_for_tvc(THD *thd,
Item *item;
for (uint holder_pos= 0 ; (item= it++); holder_pos++)
{
- DBUG_ASSERT(item->fixed);
+ DBUG_ASSERT(item->is_fixed());
holders[holder_pos].add_argument(item);
}
}
@@ -257,7 +257,6 @@ bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl,
Item_type_holder(thd, item, holders[pos].type_handler(),
&holders[pos]/*Type_all_attributes*/,
holders[pos].get_maybe_null());
- new_holder->fix_fields(thd, 0);
sl->item_list.push_back(new_holder);
}
if (arena)
@@ -281,7 +280,7 @@ bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl,
for (; order; order=order->next)
{
Item *order_item= *order->item;
- if (order_item->type() == Item::INT_ITEM && order_item->basic_const_item())
+ if (order_item->is_order_clause_position())
{
uint count= 0;
if (order->counter_used)
@@ -333,7 +332,7 @@ int table_value_constr::save_explain_data_intern(THD *thd,
explain->select_id= select_lex->select_number;
explain->select_type= select_lex->type;
- explain->linkage= select_lex->linkage;
+ explain->linkage= select_lex->get_linkage();
explain->using_temporary= false;
explain->using_filesort= false;
/* Setting explain->message means that all other members are invalid */
@@ -620,8 +619,8 @@ static bool create_tvc_name(THD *thd, st_select_lex *parent_select,
bool table_value_constr::to_be_wrapped_as_with_tail()
{
- return select_lex->master_unit()->first_select()->next_select() &&
- select_lex->order_list.elements && select_lex->explicit_limit;
+ return select_lex->master_unit()->first_select()->next_select() &&
+ select_lex->order_list.elements && select_lex->explicit_limit;
}
@@ -672,7 +671,7 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
wrapper_sl->nest_level= tvc_sl->nest_level;
wrapper_sl->parsing_place= tvc_sl->parsing_place;
- wrapper_sl->linkage= tvc_sl->linkage;
+ wrapper_sl->set_linkage(tvc_sl->get_linkage());
wrapper_sl->exclude_from_table_unique_test=
tvc_sl->exclude_from_table_unique_test;
@@ -705,7 +704,7 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
the derived table tvc_x of the transformation
*/
derived_unit->add_slave(tvc_sl);
- tvc_sl->linkage= DERIVED_TABLE_TYPE;
+ tvc_sl->set_linkage(DERIVED_TABLE_TYPE);
/*
Generate the name of the derived table created for TVC and
@@ -952,7 +951,7 @@ Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd,
mysql_init_select(lex);
tvc_select= lex->current_select;
derived_unit= tvc_select->master_unit();
- tvc_select->linkage= DERIVED_TABLE_TYPE;
+ tvc_select->set_linkage(DERIVED_TABLE_TYPE);
/* Create TVC used in the transformation */
if (create_value_list_for_tvc(thd, &values))
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 32862be371a..e92dc97771e 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -22,11 +22,19 @@
#include "sql_string.h"
#include "item.h"
#include "log.h"
+#include "tztime.h"
+
+const DTCollation &DTCollation_numeric::singleton()
+{
+ static const DTCollation_numeric tmp;
+ return tmp;
+}
Type_handler_row type_handler_row;
Type_handler_null type_handler_null;
+Type_handler_bool type_handler_bool;
Type_handler_tiny type_handler_tiny;
Type_handler_short type_handler_short;
Type_handler_long type_handler_long;
@@ -42,6 +50,7 @@ Type_handler_olddecimal type_handler_olddecimal;
Type_handler_newdecimal type_handler_newdecimal;
Type_handler_year type_handler_year;
+Type_handler_year type_handler_year2;
Type_handler_time type_handler_time;
Type_handler_date type_handler_date;
Type_handler_timestamp type_handler_timestamp;
@@ -57,6 +66,7 @@ Type_handler_set type_handler_set;
Type_handler_string type_handler_string;
Type_handler_var_string type_handler_var_string;
Type_handler_varchar type_handler_varchar;
+Type_handler_hex_hybrid type_handler_hex_hybrid;
static Type_handler_varchar_compressed type_handler_varchar_compressed;
Type_handler_tiny_blob type_handler_tiny_blob;
@@ -65,6 +75,8 @@ Type_handler_long_blob type_handler_long_blob;
Type_handler_blob type_handler_blob;
static Type_handler_blob_compressed type_handler_blob_compressed;
+Type_handler_interval_DDhhmmssff type_handler_interval_DDhhmmssff;
+
#ifdef HAVE_SPATIAL
Type_handler_geometry type_handler_geometry;
#endif
@@ -98,6 +110,9 @@ bool Type_handler_data::init()
&type_handler_geometry,
&type_handler_geometry) ||
m_type_aggregator_for_result.add(&type_handler_geometry,
+ &type_handler_hex_hybrid,
+ &type_handler_long_blob) ||
+ m_type_aggregator_for_result.add(&type_handler_geometry,
&type_handler_tiny_blob,
&type_handler_long_blob) ||
m_type_aggregator_for_result.add(&type_handler_geometry,
@@ -158,18 +173,723 @@ bool Float::to_string(String *val_buffer, uint dec) const
}
-void Time::make_from_item(Item *item, const Options opt)
+String_ptr::String_ptr(Item *item, String *buffer)
+ :m_string_ptr(item->val_str(buffer))
+{ }
+
+
+Ascii_ptr::Ascii_ptr(Item *item, String *buffer)
+ :String_ptr(item->val_str_ascii(buffer))
+{ }
+
+
+void VDec::set(Item *item)
+{
+ m_ptr= item->val_decimal(&m_buffer);
+ DBUG_ASSERT((m_ptr == NULL) == item->null_value);
+}
+
+
+VDec::VDec(Item *item)
+{
+ m_ptr= item->val_decimal(&m_buffer);
+ DBUG_ASSERT((m_ptr == NULL) == item->null_value);
+}
+
+
+VDec_op::VDec_op(Item_func_hybrid_field_type *item)
+{
+ m_ptr= item->decimal_op(&m_buffer);
+ DBUG_ASSERT((m_ptr == NULL) == item->null_value);
+}
+
+
+date_conv_mode_t Temporal::sql_mode_for_dates(THD *thd)
+{
+ return ::sql_mode_for_dates(thd);
+}
+
+
+time_round_mode_t Temporal::default_round_mode(THD *thd)
+{
+ return thd->temporal_round_mode();
+}
+
+
+time_round_mode_t Timestamp::default_round_mode(THD *thd)
{
- if (item->get_date(this, opt.get_date_flags()))
+ return thd->temporal_round_mode();
+}
+
+
+my_decimal *Temporal::to_decimal(my_decimal *to) const
+{
+ return date2my_decimal(this, to);
+}
+
+
+my_decimal *Temporal::bad_to_decimal(my_decimal *to) const
+{
+ my_decimal_set_zero(to);
+ return NULL;
+}
+
+
+void Temporal::make_from_str(THD *thd, Warn *warn,
+ const char *str, size_t length,
+ CHARSET_INFO *cs, date_mode_t fuzzydate)
+{
+ DBUG_EXECUTE_IF("str_to_datetime_warn",
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_YES, ErrConvString(str, length,cs).ptr()););
+
+ if (str_to_temporal(thd, warn, str, length, cs, fuzzydate))
+ make_fuzzy_date(&warn->warnings, date_conv_mode_t(fuzzydate));
+ if (warn->warnings)
+ warn->set_str(str, length, &my_charset_bin);
+}
+
+
+Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate)
+{
+ if (item->get_date(thd, this, fuzzydate))
time_type= MYSQL_TIMESTAMP_NONE;
+}
+
+
+uint Timestamp::binary_length_to_precision(uint length)
+{
+ switch (length) {
+ case 4: return 0;
+ case 5: return 2;
+ case 6: return 4;
+ case 7: return 6;
+ }
+ DBUG_ASSERT(0);
+ return 0;
+}
+
+
+Timestamp::Timestamp(const Native &native)
+{
+ DBUG_ASSERT(native.length() >= 4 && native.length() <= 7);
+ uint dec= binary_length_to_precision(native.length());
+ my_timestamp_from_binary(this, (const uchar *) native.ptr(), dec);
+}
+
+
+bool Timestamp::to_native(Native *to, uint decimals) const
+{
+ uint len= my_timestamp_binary_length(decimals);
+ if (to->reserve(len))
+ return true;
+ my_timestamp_to_binary(this, (uchar *) to->ptr(), decimals);
+ to->length(len);
+ return false;
+}
+
+
+bool Timestamp::to_TIME(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate) const
+{
+ return thd->timestamp_to_TIME(to, tv_sec, tv_usec, fuzzydate);
+}
+
+
+Timestamp::Timestamp(THD *thd, const MYSQL_TIME *ltime, uint *error_code)
+ :Timeval(TIME_to_timestamp(thd, ltime, error_code), ltime->second_part)
+{ }
+
+
+Timestamp_or_zero_datetime::Timestamp_or_zero_datetime(THD *thd,
+ const MYSQL_TIME *ltime,
+ uint *error_code)
+ :Timestamp(thd, ltime, error_code),
+ m_is_zero_datetime(*error_code == ER_WARN_DATA_OUT_OF_RANGE)
+{
+ if (m_is_zero_datetime)
+ {
+ if (!non_zero_date(ltime))
+ *error_code= 0; // ltime was '0000-00-00 00:00:00'
+ }
+ else if (*error_code == ER_WARN_INVALID_TIMESTAMP)
+ *error_code= 0; // ltime fell into spring time gap, adjusted.
+}
+
+
+bool Timestamp_or_zero_datetime::to_TIME(THD *thd, MYSQL_TIME *to,
+ date_mode_t fuzzydate) const
+{
+ if (m_is_zero_datetime)
+ {
+ set_zero_time(to, MYSQL_TIMESTAMP_DATETIME);
+ return false;
+ }
+ return Timestamp::to_TIME(thd, to, fuzzydate);
+}
+
+
+bool Timestamp_or_zero_datetime::to_native(Native *to, uint decimals) const
+{
+ if (m_is_zero_datetime)
+ {
+ to->length(0);
+ return false;
+ }
+ return Timestamp::to_native(to, decimals);
+}
+
+
+int Timestamp_or_zero_datetime_native::save_in_field(Field *field,
+ uint decimals) const
+{
+ field->set_notnull();
+ if (field->type_handler()->type_handler_for_native_format() ==
+ &type_handler_timestamp2)
+ return field->store_native(*this);
+ if (is_zero_datetime())
+ {
+ static Datetime zero(Datetime::zero());
+ return field->store_time_dec(zero.get_mysql_time(), decimals);
+ }
+ return field->store_timestamp_dec(Timestamp(*this).tv(), decimals);
+}
+
+
+void Sec6::make_from_decimal(const my_decimal *d, ulong *nanoseconds)
+{
+ m_neg= my_decimal2seconds(d, &m_sec, &m_usec, nanoseconds);
+ m_truncated= (m_sec >= LONGLONG_MAX);
+}
+
+
+void Sec6::make_from_double(double nr, ulong *nanoseconds)
+{
+ if ((m_neg= nr < 0))
+ nr= -nr;
+ if ((m_truncated= nr > (double) LONGLONG_MAX))
+ {
+ m_sec= LONGLONG_MAX;
+ m_usec= 0;
+ *nanoseconds= 0;
+ }
else
- valid_MYSQL_TIME_to_valid_value(opt);
+ {
+ m_sec= (ulonglong) nr;
+ m_usec= (ulong) ((nr - floor(nr)) * 1000000000);
+ *nanoseconds= m_usec % 1000;
+ m_usec/= 1000;
+ }
}
-void Temporal_with_date::make_from_item(THD *thd, Item *item, sql_mode_t flags)
+void Sec6::make_truncated_warning(THD *thd, const char *type_str) const
{
- flags&= ~TIME_TIME_ONLY;
+ char buff[1 + MAX_BIGINT_WIDTH + 1 + 6 + 1]; // '-' int '.' frac '\0'
+ to_string(buff, sizeof(buff));
+ thd->push_warning_truncated_wrong_value(type_str, buff);
+}
+
+
+bool Sec6::convert_to_mysql_time(THD *thd, int *warn, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate) const
+{
+ bool rc= fuzzydate & (TIME_INTERVAL_hhmmssff | TIME_INTERVAL_DAY) ?
+ to_datetime_or_to_interval_hhmmssff(ltime, warn) :
+ fuzzydate & TIME_TIME_ONLY ?
+ to_datetime_or_time(ltime, warn, date_conv_mode_t(fuzzydate)) :
+ to_datetime_or_date(ltime, warn, date_conv_mode_t(fuzzydate));
+ DBUG_ASSERT(*warn || !rc);
+ if (truncated())
+ *warn|= MYSQL_TIME_WARN_TRUNCATED;
+ return rc;
+}
+
+
+void Temporal::push_conversion_warnings(THD *thd, bool totally_useless_value,
+ int warn,
+ const char *typestr,
+ const char *db_name,
+ const char *table_name,
+ const char *field_name,
+ const char *value)
+{
+ if (MYSQL_TIME_WARN_HAVE_WARNINGS(warn))
+ thd->push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_WARN,
+ totally_useless_value,
+ typestr, value,
+ db_name, table_name,
+ field_name);
+ else if (MYSQL_TIME_WARN_HAVE_NOTES(warn))
+ thd->push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_NOTE,
+ false, typestr, value,
+ db_name, table_name,
+ field_name);
+}
+
+
+VSec9::VSec9(THD *thd, Item *item, const char *type_str, ulonglong limit)
+{
+ if (item->decimals == 0)
+ { // optimize for an important special case
+ Longlong_hybrid nr(item->val_int(), item->unsigned_flag);
+ make_from_int(nr);
+ m_is_null= item->null_value;
+ if (!m_is_null && m_sec > limit)
+ {
+ m_sec= limit;
+ m_truncated= true;
+ ErrConvInteger err(nr);
+ thd->push_warning_truncated_wrong_value(type_str, err.ptr());
+ }
+ }
+ else if (item->cmp_type() == REAL_RESULT)
+ {
+ double nr= item->val_real();
+ make_from_double(nr, &m_nsec);
+ m_is_null= item->null_value;
+ if (!m_is_null && m_sec > limit)
+ {
+ m_sec= limit;
+ m_truncated= true;
+ }
+ if (m_truncated)
+ {
+ ErrConvDouble err(nr);
+ thd->push_warning_truncated_wrong_value(type_str, err.ptr());
+ }
+ }
+ else
+ {
+ VDec tmp(item);
+ (m_is_null= tmp.is_null()) ? reset() : make_from_decimal(tmp.ptr(), &m_nsec);
+ if (!m_is_null && m_sec > limit)
+ {
+ m_sec= limit;
+ m_truncated= true;
+ }
+ if (m_truncated)
+ {
+ ErrConvDecimal err(tmp.ptr());
+ thd->push_warning_truncated_wrong_value(type_str, err.ptr());
+ }
+ }
+}
+
+
+Year::Year(longlong value, bool unsigned_flag, uint length)
+{
+ if ((m_truncated= (value < 0))) // Negative or huge unsigned
+ m_year= unsigned_flag ? 9999 : 0;
+ else if (value > 9999)
+ {
+ m_truncated= true;
+ m_year= 9999;
+ }
+ else if (length == 2)
+ {
+ m_year= value < 70 ? (uint) value + 2000 :
+ value <= 1900 ? (uint) value + 1900 :
+ (uint) value;
+ }
+ else
+ m_year= (uint) value;
+ DBUG_ASSERT(m_year <= 9999);
+}
+
+
+uint Year::year_precision(const Item *item) const
+{
+ return item->type_handler() == &type_handler_year2 ? 2 : 4;
+}
+
+
+VYear::VYear(Item *item)
+ :Year_null(item->to_longlong_null(), item->unsigned_flag, year_precision(item))
+{ }
+
+
+VYear_op::VYear_op(Item_func_hybrid_field_type *item)
+ :Year_null(item->to_longlong_null_op(), item->unsigned_flag,
+ year_precision(item))
+{ }
+
+
+const LEX_CSTRING Interval_DDhhmmssff::m_type_name=
+ {STRING_WITH_LEN("INTERVAL DAY TO SECOND")};
+
+
+Interval_DDhhmmssff::Interval_DDhhmmssff(THD *thd, Status *st,
+ bool push_warnings,
+ Item *item, ulong max_hour,
+ time_round_mode_t mode, uint dec)
+{
+ switch (item->cmp_type()) {
+ case ROW_RESULT:
+ DBUG_ASSERT(0);
+ time_type= MYSQL_TIMESTAMP_NONE;
+ break;
+ case TIME_RESULT:
+ {
+ // Rounding mode is not important here
+ if (item->get_date(thd, this, Options(TIME_TIME_ONLY, TIME_FRAC_NONE)))
+ time_type= MYSQL_TIMESTAMP_NONE;
+ else if (time_type != MYSQL_TIMESTAMP_TIME)
+ {
+ st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ push_warning_wrong_or_truncated_value(thd, ErrConvTime(this),
+ st->warnings);
+ time_type= MYSQL_TIMESTAMP_NONE;
+ }
+ break;
+ }
+ case INT_RESULT:
+ case REAL_RESULT:
+ case DECIMAL_RESULT:
+ case STRING_RESULT:
+ {
+ StringBuffer<STRING_BUFFER_USUAL_SIZE> tmp;
+ String *str= item->val_str(&tmp);
+ if (!str)
+ time_type= MYSQL_TIMESTAMP_NONE;
+ else if (str_to_DDhhmmssff(st, str->ptr(), str->length(), str->charset(),
+ UINT_MAX32))
+ {
+ if (push_warnings)
+ thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
+ m_type_name.str,
+ ErrConvString(str).ptr());
+ time_type= MYSQL_TIMESTAMP_NONE;
+ }
+ else
+ {
+ if (mode == TIME_FRAC_ROUND)
+ time_round_or_set_max(dec, &st->warnings, max_hour, st->nanoseconds);
+ if (hour > max_hour)
+ {
+ st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ time_type= MYSQL_TIMESTAMP_NONE;
+ }
+ // Warn if hour or nanosecond truncation happened
+ if (push_warnings)
+ push_warning_wrong_or_truncated_value(thd, ErrConvString(str),
+ st->warnings);
+ }
+ }
+ break;
+ }
+ DBUG_ASSERT(is_valid_value_slow());
+}
+
+
+void
+Interval_DDhhmmssff::push_warning_wrong_or_truncated_value(THD *thd,
+ const ErrConv &str,
+ int warnings)
+{
+ if (warnings & MYSQL_TIME_WARN_OUT_OF_RANGE)
+ {
+ thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
+ m_type_name.str, str.ptr());
+ }
+ else if (MYSQL_TIME_WARN_HAVE_WARNINGS(warnings))
+ {
+ thd->push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_WARN,
+ m_type_name.str, str.ptr());
+ }
+ else if (MYSQL_TIME_WARN_HAVE_NOTES(warnings))
+ {
+ thd->push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_NOTE,
+ m_type_name.str, str.ptr());
+ }
+}
+
+
+uint Interval_DDhhmmssff::fsp(THD *thd, Item *item)
+{
+ switch (item->cmp_type()) {
+ case INT_RESULT:
+ case TIME_RESULT:
+ return item->decimals;
+ case REAL_RESULT:
+ case DECIMAL_RESULT:
+ return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
+ case ROW_RESULT:
+ DBUG_ASSERT(0);
+ return 0;
+ case STRING_RESULT:
+ break;
+ }
+ if (!item->const_item() || item->is_expensive())
+ return TIME_SECOND_PART_DIGITS;
+ Status st;
+ Interval_DDhhmmssff it(thd, &st, false/*no warnings*/, item, UINT_MAX32,
+ TIME_FRAC_TRUNCATE, TIME_SECOND_PART_DIGITS);
+ return it.is_valid_interval_DDhhmmssff() ? st.precision :
+ TIME_SECOND_PART_DIGITS;
+}
+
+
+bool Time::to_native(Native *to, uint decimals) const
+{
+ if (!is_valid_time())
+ {
+ to->length(0);
+ return true;
+ }
+ uint len= my_time_binary_length(decimals);
+ if (to->reserve(len))
+ return true;
+ longlong tmp= TIME_to_longlong_time_packed(get_mysql_time());
+ my_time_packed_to_binary(tmp, (uchar*) to->ptr(), decimals);
+ to->length(len);
+ return false;
+}
+
+
+void Time::make_from_item(THD *thd, int *warn, Item *item, const Options opt)
+{
+ *warn= 0;
+ if (item->get_date(thd, this, opt))
+ time_type= MYSQL_TIMESTAMP_NONE;
+ else
+ valid_MYSQL_TIME_to_valid_value(thd, warn, opt);
+}
+
+
+static uint msec_round_add[7]=
+{
+ 500000000,
+ 50000000,
+ 5000000,
+ 500000,
+ 50000,
+ 5000,
+ 0
+};
+
+
+Sec9 & Sec9::round(uint dec)
+{
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ if (Sec6::add_nanoseconds(m_nsec + msec_round_add[dec]))
+ m_sec++;
+ m_nsec= 0;
+ Sec6::trunc(dec);
+ return *this;
+}
+
+
+void Timestamp::round_or_set_max(uint dec, int *warn)
+{
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ if (add_nanoseconds_usec(msec_round_add[dec]) &&
+ tv_sec++ >= TIMESTAMP_MAX_VALUE)
+ {
+ tv_sec= TIMESTAMP_MAX_VALUE;
+ tv_usec= TIME_MAX_SECOND_PART;
+ *warn|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ }
+ my_timeval_trunc(this, dec);
+}
+
+
+bool Temporal::add_nanoseconds_with_round(THD *thd, int *warn,
+ date_conv_mode_t mode,
+ ulong nsec)
+{
+ switch (time_type) {
+ case MYSQL_TIMESTAMP_TIME:
+ {
+ ulong max_hour= (mode & (TIME_INTERVAL_DAY | TIME_INTERVAL_hhmmssff)) ?
+ TIME_MAX_INTERVAL_HOUR : TIME_MAX_HOUR;
+ time_round_or_set_max(6, warn, max_hour, nsec);
+ return false;
+ }
+ case MYSQL_TIMESTAMP_DATETIME:
+ return datetime_round_or_invalidate(thd, 6, warn, nsec);
+ case MYSQL_TIMESTAMP_DATE:
+ return false;
+ case MYSQL_TIMESTAMP_NONE:
+ return false;
+ case MYSQL_TIMESTAMP_ERROR:
+ break;
+ }
+ DBUG_ASSERT(0);
+ return false;
+}
+
+
+void Temporal::time_round_or_set_max(uint dec, int *warn,
+ ulong max_hour, ulong nsec)
+{
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ if (add_nanoseconds_mmssff(nsec) && ++hour > max_hour)
+ {
+ time_hhmmssff_set_max(max_hour);
+ *warn|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ }
+ my_time_trunc(this, dec);
+}
+
+
+void Time::round_or_set_max(uint dec, int *warn, ulong nsec)
+{
+ Temporal::time_round_or_set_max(dec, warn, TIME_MAX_HOUR, nsec);
+ DBUG_ASSERT(is_valid_time_slow());
+}
+
+
+void Time::round_or_set_max(uint dec, int *warn)
+{
+ round_or_set_max(dec, warn, msec_round_add[dec]);
+}
+
+/**
+ Create from a DATETIME by subtracting a given number of days,
+ implementing an optimized version of calc_time_diff().
+*/
+void Time::make_from_datetime_with_days_diff(int *warn, const MYSQL_TIME *from,
+ long days)
+{
+ *warn= 0;
+ DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_DATETIME ||
+ from->time_type == MYSQL_TIMESTAMP_DATE);
+ long daynr= calc_daynr(from->year, from->month, from->day);
+ long daydiff= daynr - days;
+ if (!daynr) // Zero date
+ {
+ set_zero_time(this, MYSQL_TIMESTAMP_TIME);
+ neg= true;
+ hour= TIME_MAX_HOUR + 1; // to report "out of range" in "warn"
+ }
+ else if (daydiff >=0)
+ {
+ neg= false;
+ year= month= day= 0;
+ hhmmssff_copy(from);
+ hour+= daydiff * 24;
+ time_type= MYSQL_TIMESTAMP_TIME;
+ }
+ else
+ {
+ longlong timediff= ((((daydiff * 24LL +
+ from->hour) * 60LL +
+ from->minute) * 60LL +
+ from->second) * 1000000LL +
+ from->second_part);
+ unpack_time(timediff, this, MYSQL_TIMESTAMP_TIME);
+ if (year || month)
+ {
+ *warn|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ year= month= day= 0;
+ hour= TIME_MAX_HOUR + 1;
+ }
+ }
+ // The above code can generate TIME values outside of the valid TIME range.
+ adjust_time_range_or_invalidate(warn);
+}
+
+
+void Time::make_from_datetime_move_day_to_hour(int *warn,
+ const MYSQL_TIME *from)
+{
+ *warn= 0;
+ DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_DATE ||
+ from->time_type == MYSQL_TIMESTAMP_DATETIME);
+ time_type= MYSQL_TIMESTAMP_TIME;
+ neg= false;
+ year= month= day= 0;
+ hhmmssff_copy(from);
+ datetime_to_time_YYYYMMDD_000000DD_mix_to_hours(warn, from->year,
+ from->month, from->day);
+ adjust_time_range_or_invalidate(warn);
+}
+
+
+void Time::make_from_datetime(int *warn, const MYSQL_TIME *from, long curdays)
+{
+ if (!curdays)
+ make_from_datetime_move_day_to_hour(warn, from);
+ else
+ make_from_datetime_with_days_diff(warn, from, curdays);
+}
+
+
+void Time::make_from_time(int *warn, const MYSQL_TIME *from)
+{
+ DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME);
+ if (from->year || from->month)
+ make_from_out_of_range(warn);
+ else
+ {
+ *warn= 0;
+ DBUG_ASSERT(from->day == 0);
+ *(static_cast<MYSQL_TIME*>(this))= *from;
+ adjust_time_range_or_invalidate(warn);
+ }
+}
+
+
+uint Time::binary_length_to_precision(uint length)
+{
+ switch (length) {
+ case 3: return 0;
+ case 4: return 2;
+ case 5: return 4;
+ case 6: return 6;
+ }
+ DBUG_ASSERT(0);
+ return 0;
+}
+
+
+Time::Time(const Native &native)
+{
+ uint dec= binary_length_to_precision(native.length());
+ longlong tmp= my_time_packed_from_binary((const uchar *) native.ptr(), dec);
+ TIME_from_longlong_time_packed(this, tmp);
+ DBUG_ASSERT(is_valid_time());
+}
+
+
+Time::Time(int *warn, const MYSQL_TIME *from, long curdays)
+{
+ switch (from->time_type) {
+ case MYSQL_TIMESTAMP_NONE:
+ case MYSQL_TIMESTAMP_ERROR:
+ make_from_out_of_range(warn);
+ break;
+ case MYSQL_TIMESTAMP_DATE:
+ case MYSQL_TIMESTAMP_DATETIME:
+ make_from_datetime(warn, from, curdays);
+ break;
+ case MYSQL_TIMESTAMP_TIME:
+ make_from_time(warn, from);
+ break;
+ }
+ DBUG_ASSERT(is_valid_value_slow());
+}
+
+
+Time::Time(int *warn, bool neg, ulonglong hour, uint minute, const Sec6 &second)
+{
+ DBUG_ASSERT(second.sec() <= 59);
+ *warn= 0;
+ set_zero_time(this, MYSQL_TIMESTAMP_TIME);
+ MYSQL_TIME::neg= neg;
+ MYSQL_TIME::hour= hour > TIME_MAX_HOUR ? (uint) (TIME_MAX_HOUR + 1) :
+ (uint) hour;
+ MYSQL_TIME::minute= minute;
+ MYSQL_TIME::second= (uint) second.sec();
+ MYSQL_TIME::second_part= second.usec();
+ adjust_time_range_or_invalidate(warn);
+}
+
+
+void Temporal_with_date::make_from_item(THD *thd, Item *item,
+ date_mode_t fuzzydate)
+{
+ date_conv_mode_t flags= date_conv_mode_t(fuzzydate) & ~TIME_TIME_ONLY;
/*
Some TIME type items return error when trying to do get_date()
without TIME_TIME_ONLY set (e.g. Item_field for Field_time).
@@ -177,10 +897,11 @@ void Temporal_with_date::make_from_item(THD *thd, Item *item, sql_mode_t flags)
In the legacy time->datetime conversion mode we do not add TIME_TIME_ONLY
and leave it to get_date() to check date.
*/
- ulonglong time_flag= (item->field_type() == MYSQL_TYPE_TIME &&
- !(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ?
- TIME_TIME_ONLY : 0;
- if (item->get_date(this, flags | time_flag))
+ date_conv_mode_t time_flag= (item->field_type() == MYSQL_TYPE_TIME &&
+ !(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ?
+ TIME_TIME_ONLY : TIME_CONV_NONE;
+ Options opt(flags | time_flag, time_round_mode_t(fuzzydate));
+ if (item->get_date(thd, this, opt))
time_type= MYSQL_TIMESTAMP_NONE;
else if (time_type == MYSQL_TIMESTAMP_TIME)
{
@@ -193,6 +914,165 @@ void Temporal_with_date::make_from_item(THD *thd, Item *item, sql_mode_t flags)
}
+void Temporal_with_date::check_date_or_invalidate(int *warn,
+ date_conv_mode_t flags)
+{
+ if (::check_date(this, pack_time(this) != 0,
+ ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), warn))
+ time_type= MYSQL_TIMESTAMP_NONE;
+}
+
+
+void Datetime::make_from_time(THD *thd, int *warn, const MYSQL_TIME *from,
+ date_conv_mode_t flags)
+{
+ DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME);
+ if (time_to_datetime(thd, from, this))
+ make_from_out_of_range(warn);
+ else
+ {
+ *warn= 0;
+ check_date_or_invalidate(warn, flags);
+ }
+}
+
+
+void Datetime::make_from_datetime(THD *thd, int *warn, const MYSQL_TIME *from,
+ date_conv_mode_t flags)
+{
+ DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_DATE ||
+ from->time_type == MYSQL_TIMESTAMP_DATETIME);
+ if (from->neg || check_datetime_range(from))
+ make_from_out_of_range(warn);
+ else
+ {
+ *warn= 0;
+ *(static_cast<MYSQL_TIME*>(this))= *from;
+ date_to_datetime(this);
+ check_date_or_invalidate(warn, flags);
+ }
+}
+
+
+Datetime::Datetime(THD *thd, const timeval &tv)
+{
+ thd->variables.time_zone->gmt_sec_to_TIME(this, tv.tv_sec);
+ second_part= tv.tv_usec;
+ thd->time_zone_used= 1;
+ DBUG_ASSERT(is_valid_value_slow());
+}
+
+
+Datetime::Datetime(THD *thd, int *warn, const MYSQL_TIME *from,
+ date_conv_mode_t flags)
+{
+ DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false);
+ switch (from->time_type) {
+ case MYSQL_TIMESTAMP_ERROR:
+ case MYSQL_TIMESTAMP_NONE:
+ make_from_out_of_range(warn);
+ break;
+ case MYSQL_TIMESTAMP_TIME:
+ make_from_time(thd, warn, from, flags);
+ break;
+ case MYSQL_TIMESTAMP_DATETIME:
+ case MYSQL_TIMESTAMP_DATE:
+ make_from_datetime(thd, warn, from, flags);
+ break;
+ }
+ DBUG_ASSERT(is_valid_value_slow());
+}
+
+
+bool Temporal::datetime_add_nanoseconds_or_invalidate(THD *thd, int *warn, ulong nsec)
+{
+ if (!add_nanoseconds_mmssff(nsec))
+ return false;
+ /*
+ Overflow happened on minutes. Now we need to add 1 hour to the value.
+ Catch a special case for the maximum possible date and hour==23, to
+ truncate '9999-12-31 23:59:59.9999999' (with 7 fractional digits)
+ to '9999-12-31 23:59:59.999999' (with 6 fractional digits),
+ with a warning, instead of returning an error, so this statement:
+ INSERT INTO (datetime_column) VALUES ('9999-12-31 23:59:59.9999999');
+ inserts a value truncated to 6 fractional digits, instead of zero
+ date '0000-00-00 00:00:00.000000'.
+ */
+ if (year == 9999 && month == 12 && day == 31 && hour == 23)
+ {
+ minute= 59;
+ second= 59;
+ second_part= 999999;
+ *warn= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ return false;
+ }
+ INTERVAL interval;
+ memset(&interval, 0, sizeof(interval));
+ interval.hour= 1;
+ /*
+ date_add_interval cannot handle bad dates with zero YYYY or MM.
+ Note, check_date(NO_ZERO_XX) does not check YYYY against zero,
+ so let's additionally check it.
+ */
+ if (year == 0 ||
+ check_date(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE, warn) ||
+ date_add_interval(thd, this, INTERVAL_HOUR, interval, false/*no warn*/))
+ {
+ char buf[MAX_DATE_STRING_REP_LENGTH];
+ my_date_to_str(this, buf);
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_TYPE,
+ ER_THD(thd, ER_WRONG_VALUE_FOR_TYPE),
+ "date", buf, "round(datetime)");
+ make_from_out_of_range(warn);
+ return true;
+ }
+ return false;
+}
+
+
+bool Temporal::datetime_round_or_invalidate(THD *thd, uint dec, int *warn, ulong nsec)
+{
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ if (datetime_add_nanoseconds_or_invalidate(thd, warn, nsec))
+ return true;
+ my_datetime_trunc(this, dec);
+ return false;
+
+}
+
+
+bool Datetime::round_or_invalidate(THD *thd, uint dec, int *warn)
+{
+ return round_or_invalidate(thd, dec, warn, msec_round_add[dec]);
+}
+
+
+Datetime_from_temporal::Datetime_from_temporal(THD *thd, Item *temporal,
+ date_conv_mode_t fuzzydate)
+ :Datetime(thd, temporal, Options(fuzzydate, TIME_FRAC_NONE))
+{
+ // Exact rounding mode does not matter
+ DBUG_ASSERT(temporal->cmp_type() == TIME_RESULT);
+}
+
+
+Datetime_truncation_not_needed::Datetime_truncation_not_needed(THD *thd, Item *item,
+ date_conv_mode_t mode)
+ :Datetime(thd, item, Options(mode, TIME_FRAC_NONE))
+{
+ /*
+ The called Datetime() constructor only would truncate nanoseconds if they
+ existed (but we know there were no nanoseconds). Here we assert that there
+ are also no microsecond digits outside of the scale specified in "dec".
+ */
+ DBUG_ASSERT(!is_valid_datetime() ||
+ fraction_remainder(MY_MIN(item->decimals,
+ TIME_SECOND_PART_DIGITS)) == 0);
+}
+
+/********************************************************************/
+
uint Type_std_attributes::count_max_decimals(Item **item, uint nitems)
{
uint res= 0;
@@ -313,6 +1193,32 @@ bool Type_std_attributes::count_string_length(const char *func_name,
}
+/*
+ Find a handler by its ODBC literal data type.
+
+ @param type_str - data type name, not necessarily 0-terminated
+ @retval - a pointer to data type handler if type_str points
+ to a known ODBC literal data type, or NULL otherwise
+*/
+const Type_handler *
+Type_handler::odbc_literal_type_handler(const LEX_CSTRING *type_str)
+{
+ if (type_str->length == 1)
+ {
+ if (type_str->str[0] == 'd') // {d'2001-01-01'}
+ return &type_handler_newdate;
+ else if (type_str->str[0] == 't') // {t'10:20:30'}
+ return &type_handler_time2;
+ }
+ else if (type_str->length == 2) // {ts'2001-01-01 10:20:30'}
+ {
+ if (type_str->str[0] == 't' && type_str->str[1] == 's')
+ return &type_handler_datetime2;
+ }
+ return NULL; // Not a known ODBC literal type
+}
+
+
/**
This method is used by:
- Item_user_var_as_out_param::field_type()
@@ -468,6 +1374,7 @@ const Name
Type_handler_string::m_name_char(STRING_WITH_LEN("char")),
Type_handler_var_string::m_name_var_string(STRING_WITH_LEN("varchar")),
Type_handler_varchar::m_name_varchar(STRING_WITH_LEN("varchar")),
+ Type_handler_hex_hybrid::m_name_hex_hybrid(STRING_WITH_LEN("hex_hybrid")),
Type_handler_tiny_blob::m_name_tinyblob(STRING_WITH_LEN("tinyblob")),
Type_handler_medium_blob::m_name_mediumblob(STRING_WITH_LEN("mediumblob")),
Type_handler_long_blob::m_name_longblob(STRING_WITH_LEN("longblob")),
@@ -478,6 +1385,7 @@ const Name
Type_handler_set::m_name_set(STRING_WITH_LEN("set"));
const Name
+ Type_handler_bool::m_name_bool(STRING_WITH_LEN("boolean")),
Type_handler_tiny::m_name_tiny(STRING_WITH_LEN("tinyint")),
Type_handler_short::m_name_short(STRING_WITH_LEN("smallint")),
Type_handler_long::m_name_int(STRING_WITH_LEN("int")),
@@ -500,6 +1408,11 @@ const Name
Type_handler_datetime_common::m_name_datetime(STRING_WITH_LEN("datetime")),
Type_handler_timestamp_common::m_name_timestamp(STRING_WITH_LEN("timestamp"));
+const Name
+ Type_handler::m_version_default(STRING_WITH_LEN("")),
+ Type_handler::m_version_mariadb53(STRING_WITH_LEN("mariadb-5.3")),
+ Type_handler::m_version_mysql56(STRING_WITH_LEN("mysql-5.6"));
+
const Type_limits_int
Type_handler_tiny::m_limits_sint8= Type_limits_sint8(),
@@ -565,7 +1478,7 @@ const Type_handler *Type_handler_datetime_common::type_handler_for_comparison()
const Type_handler *Type_handler_timestamp_common::type_handler_for_comparison() const
{
- return &type_handler_datetime;
+ return &type_handler_timestamp;
}
@@ -576,6 +1489,22 @@ const Type_handler *Type_handler_row::type_handler_for_comparison() const
/***************************************************************************/
+const Type_handler *
+Type_handler_timestamp_common::type_handler_for_native_format() const
+{
+ return &type_handler_timestamp2;
+}
+
+
+const Type_handler *
+Type_handler_time_common::type_handler_for_native_format() const
+{
+ return &type_handler_time2;
+}
+
+
+/***************************************************************************/
+
const Type_handler *Type_handler_typelib::type_handler_for_item_field() const
{
return &type_handler_string;
@@ -750,6 +1679,16 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h)
*/
if (b == TIME_RESULT)
m_type_handler= h; // Temporal types bit non-temporal types
+ /*
+ Compare TIMESTAMP to a non-temporal type as DATETIME.
+ This is needed to make queries with fuzzy dates work:
+ SELECT * FROM t1
+ WHERE
+ ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00';
+ */
+ if (m_type_handler->type_handler_for_native_format() ==
+ &type_handler_timestamp2)
+ m_type_handler= &type_handler_datetime;
}
else
{
@@ -833,7 +1772,19 @@ Type_handler_hybrid_field_type::aggregate_for_min_max(const Type_handler *h)
}
else if (a == TIME_RESULT || b == TIME_RESULT)
{
- if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1)
+ if ((m_type_handler->type_handler_for_native_format() ==
+ &type_handler_timestamp2) +
+ (h->type_handler_for_native_format() ==
+ &type_handler_timestamp2) == 1)
+ {
+ /*
+ Handle LEAST(TIMESTAMP, non-TIMESTAMP) as DATETIME,
+ to make sure fuzzy dates work in this context:
+ LEAST('2001-00-00', timestamp_field)
+ */
+ m_type_handler= &type_handler_datetime2;
+ }
+ else if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1)
{
/*
We're here if there's only one temporal data type:
@@ -1479,6 +2430,17 @@ Field *Type_handler_set::make_conversion_table_field(TABLE *table,
((const Field_enum*) target)->typelib, target->charset());
}
+
+/*************************************************************************/
+
+bool Type_handler::
+ Column_definition_validate_check_constraint(THD *thd,
+ Column_definition * c) const
+{
+ return c->validate_check_constraint(thd);
+}
+
+
/*************************************************************************/
bool Type_handler_null::
Column_definition_fix_attributes(Column_definition *def) const
@@ -1640,14 +2602,81 @@ bool Type_handler_bit::
/*************************************************************************/
+void Type_handler_blob_common::
+ Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *def,
+ const Field *field) const
+{
+ DBUG_ASSERT(def->key_length == 0);
+}
+
+
+void Type_handler_typelib::
+ Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *def,
+ const Field *field) const
+{
+ DBUG_ASSERT(def->flags & (ENUM_FLAG | SET_FLAG));
+ def->interval= field->get_typelib();
+}
+
+
+#ifdef HAVE_SPATIAL
+void Type_handler_geometry::
+ Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *def,
+ const Field *field) const
+{
+ def->geom_type= ((Field_geom*) field)->geom_type;
+ def->srid= ((Field_geom*) field)->srid;
+}
+#endif
+
+
+void Type_handler_year::
+ Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *def,
+ const Field *field) const
+{
+ if (def->length != 4)
+ {
+ char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1];
+ my_snprintf(buff, sizeof(buff), "YEAR(%llu)", def->length);
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_WARN_DEPRECATED_SYNTAX,
+ ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX),
+ buff, "YEAR(4)");
+ }
+}
+
+
+void Type_handler_real_result::
+ Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *def,
+ const Field *field) const
+{
+ /*
+ Floating points are stored with FLOATING_POINT_DECIMALS but internally
+ in MariaDB used with NOT_FIXED_DEC, which is >= FLOATING_POINT_DECIMALS.
+ */
+ if (def->decimals >= FLOATING_POINT_DECIMALS)
+ def->decimals= NOT_FIXED_DEC;
+}
+
+
+/*************************************************************************/
+
bool Type_handler::
Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *def,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const
{
- def->create_length_to_internal_length_simple();
+ def->prepare_stage1_simple(&my_charset_bin);
return false;
}
@@ -1656,8 +2685,12 @@ bool Type_handler_null::
MEM_ROOT *mem_root,
Column_definition *def,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const
{
+ def->prepare_charset_for_string(derived_attr);
def->create_length_to_internal_length_null();
return false;
}
@@ -1667,19 +2700,56 @@ bool Type_handler_row::
MEM_ROOT *mem_root,
Column_definition *def,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const
{
+ def->charset= &my_charset_bin;
def->create_length_to_internal_length_null();
return false;
}
+bool Type_handler_temporal_result::
+ Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *def,
+ handler *file,
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const
+{
+ def->prepare_stage1_simple(&my_charset_numeric);
+ return false;
+}
+
+
+bool Type_handler_numeric::
+ Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *def,
+ handler *file,
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const
+{
+ def->prepare_stage1_simple(&my_charset_numeric);
+ return false;
+}
+
bool Type_handler_newdecimal::
Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *def,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const
{
+ def->charset= &my_charset_numeric;
def->create_length_to_internal_length_newdecimal();
return false;
}
@@ -1689,8 +2759,12 @@ bool Type_handler_bit::
MEM_ROOT *mem_root,
Column_definition *def,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const
{
+ def->charset= &my_charset_numeric;
return def->prepare_stage1_bit(thd, mem_root, file, table_flags);
}
@@ -1699,9 +2773,13 @@ bool Type_handler_typelib::
MEM_ROOT *mem_root,
Column_definition *def,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const
{
- return def->prepare_stage1_typelib(thd, mem_root, file, table_flags);
+ return def->prepare_charset_for_string(derived_attr) ||
+ def->prepare_stage1_typelib(thd, mem_root, file, table_flags);
}
@@ -1710,20 +2788,31 @@ bool Type_handler_string_result::
MEM_ROOT *mem_root,
Column_definition *def,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const
{
- return def->prepare_stage1_string(thd, mem_root, file, table_flags);
+ return def->prepare_charset_for_string(derived_attr) ||
+ def->prepare_stage1_string(thd, mem_root, file, table_flags);
}
#ifdef HAVE_SPATIAL
+#if MYSQL_VERSION_ID > 100500
+#error The below method is in sql/sql_type_geom.cc starting from 10.5
+#endif
bool Type_handler_geometry::
Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *def,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const
{
+ def->charset= &my_charset_bin;
def->create_length_to_internal_length_string();
return def->prepare_blob_field(thd);
}
@@ -1732,14 +2821,38 @@ bool Type_handler_geometry::
/*************************************************************************/
+bool Type_handler_general_purpose_string::
+ Column_definition_bulk_alter(Column_definition *def,
+ const Column_derived_attributes
+ *derived_attr,
+ const Column_bulk_alter_attributes
+ *bulk_alter_attr)
+ const
+{
+ if (!bulk_alter_attr->alter_table_convert_to_charset())
+ return false; // No "CONVERT TO" clause.
+ CHARSET_INFO *defcs= def->explicit_or_derived_charset(derived_attr);
+ DBUG_ASSERT(defcs);
+ /*
+ Handle 'ALTER TABLE t1 CONVERT TO CHARACTER SET csname'.
+ Change character sets for all varchar/char/text columns,
+ but do not touch varbinary/binary/blob columns.
+ */
+ if (defcs != &my_charset_bin)
+ def->charset= bulk_alter_attr->alter_table_convert_to_charset();
+ return false;
+};
+
+
+/*************************************************************************/
+
bool Type_handler::
Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
const
{
- def->redefine_stage1_common(dup, file, schema);
+ def->redefine_stage1_common(dup, file);
def->create_length_to_internal_length_simple();
return false;
}
@@ -1748,11 +2861,10 @@ bool Type_handler::
bool Type_handler_null::
Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
const
{
- def->redefine_stage1_common(dup, file, schema);
+ def->redefine_stage1_common(dup, file);
def->create_length_to_internal_length_null();
return false;
}
@@ -1761,11 +2873,10 @@ bool Type_handler_null::
bool Type_handler_newdecimal::
Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
const
{
- def->redefine_stage1_common(dup, file, schema);
+ def->redefine_stage1_common(dup, file);
def->create_length_to_internal_length_newdecimal();
return false;
}
@@ -1774,11 +2885,10 @@ bool Type_handler_newdecimal::
bool Type_handler_string_result::
Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
const
{
- def->redefine_stage1_common(dup, file, schema);
+ def->redefine_stage1_common(dup, file);
def->set_compression_method(dup->compression_method());
def->create_length_to_internal_length_string();
return false;
@@ -1788,11 +2898,10 @@ bool Type_handler_string_result::
bool Type_handler_typelib::
Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
const
{
- def->redefine_stage1_common(dup, file, schema);
+ def->redefine_stage1_common(dup, file);
def->create_length_to_internal_length_typelib();
return false;
}
@@ -1801,11 +2910,10 @@ bool Type_handler_typelib::
bool Type_handler_bit::
Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
const
{
- def->redefine_stage1_common(dup, file, schema);
+ def->redefine_stage1_common(dup, file);
/*
If we are replacing a field with a BIT field, we need
to initialize pack_flag.
@@ -2043,8 +3151,8 @@ Field *Type_handler_tiny::make_table_field(const LEX_CSTRING *name,
TABLE *table) const
{
return new (table->in_use->mem_root)
- Field_tiny(addr.ptr, attr.max_char_length(),
- addr.null_ptr, addr.null_bit,
+ Field_tiny(addr.ptr(), attr.max_char_length(),
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag);
}
@@ -2056,8 +3164,8 @@ Field *Type_handler_short::make_table_field(const LEX_CSTRING *name,
{
return new (table->in_use->mem_root)
- Field_short(addr.ptr, attr.max_char_length(),
- addr.null_ptr, addr.null_bit,
+ Field_short(addr.ptr(), attr.max_char_length(),
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag);
}
@@ -2068,8 +3176,8 @@ Field *Type_handler_int24::make_table_field(const LEX_CSTRING *name,
TABLE *table) const
{
return new (table->in_use->mem_root)
- Field_medium(addr.ptr, attr.max_char_length(),
- addr.null_ptr, addr.null_bit,
+ Field_medium(addr.ptr(), attr.max_char_length(),
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
0/*zerofill*/, attr.unsigned_flag);
}
@@ -2081,8 +3189,8 @@ Field *Type_handler_long::make_table_field(const LEX_CSTRING *name,
TABLE *table) const
{
return new (table->in_use->mem_root)
- Field_long(addr.ptr, attr.max_char_length(),
- addr.null_ptr, addr.null_bit,
+ Field_long(addr.ptr(), attr.max_char_length(),
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag);
}
@@ -2093,8 +3201,8 @@ Field *Type_handler_longlong::make_table_field(const LEX_CSTRING *name,
TABLE *table) const
{
return new (table->in_use->mem_root)
- Field_longlong(addr.ptr, attr.max_char_length(),
- addr.null_ptr, addr.null_bit,
+ Field_longlong(addr.ptr(), attr.max_char_length(),
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
0/*zerofill*/, attr.unsigned_flag);
}
@@ -2106,8 +3214,8 @@ Field *Type_handler_vers_trx_id::make_table_field(const LEX_CSTRING *name,
TABLE *table) const
{
return new (table->in_use->mem_root)
- Field_vers_trx_id(addr.ptr, attr.max_char_length(),
- addr.null_ptr, addr.null_bit,
+ Field_vers_trx_id(addr.ptr(), attr.max_char_length(),
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
0/*zerofill*/, attr.unsigned_flag);
}
@@ -2119,8 +3227,8 @@ Field *Type_handler_float::make_table_field(const LEX_CSTRING *name,
TABLE *table) const
{
return new (table->in_use->mem_root)
- Field_float(addr.ptr, attr.max_char_length(),
- addr.null_ptr, addr.null_bit,
+ Field_float(addr.ptr(), attr.max_char_length(),
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
(uint8) attr.decimals, 0/*zerofill*/, attr.unsigned_flag);
}
@@ -2132,8 +3240,8 @@ Field *Type_handler_double::make_table_field(const LEX_CSTRING *name,
TABLE *table) const
{
return new (table->in_use->mem_root)
- Field_double(addr.ptr, attr.max_char_length(),
- addr.null_ptr, addr.null_bit,
+ Field_double(addr.ptr(), attr.max_char_length(),
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
(uint8) attr.decimals, 0/*zerofill*/, attr.unsigned_flag);
}
@@ -2154,7 +3262,8 @@ Type_handler_olddecimal::make_table_field(const LEX_CSTRING *name,
*/
DBUG_ASSERT(0);
return new (table->in_use->mem_root)
- Field_decimal(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit,
+ Field_decimal(addr.ptr(), attr.max_length,
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name, (uint8) attr.decimals,
0/*zerofill*/,attr.unsigned_flag);
}
@@ -2201,7 +3310,7 @@ Type_handler_newdecimal::make_table_field(const LEX_CSTRING *name,
len= required_length;
}
return new (table->in_use->mem_root)
- Field_new_decimal(addr.ptr, len, addr.null_ptr, addr.null_bit,
+ Field_new_decimal(addr.ptr(), len, addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
dec, 0/*zerofill*/, attr.unsigned_flag);
}
@@ -2213,7 +3322,8 @@ Field *Type_handler_year::make_table_field(const LEX_CSTRING *name,
TABLE *table) const
{
return new (table->in_use->mem_root)
- Field_year(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit,
+ Field_year(addr.ptr(), attr.max_length,
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name);
}
@@ -2225,7 +3335,7 @@ Field *Type_handler_null::make_table_field(const LEX_CSTRING *name,
{
return new (table->in_use->mem_root)
- Field_null(addr.ptr, attr.max_length,
+ Field_null(addr.ptr(), attr.max_length,
Field::NONE, name, attr.collation.collation);
}
@@ -2237,7 +3347,7 @@ Field *Type_handler_timestamp::make_table_field(const LEX_CSTRING *name,
{
return new_Field_timestamp(table->in_use->mem_root,
- addr.ptr, addr.null_ptr, addr.null_bit,
+ addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, table->s, attr.decimals);
}
@@ -2253,7 +3363,7 @@ Field *Type_handler_timestamp2::make_table_field(const LEX_CSTRING *name,
make_table_field() for make_field() purposes in field.cc.
*/
return new_Field_timestamp(table->in_use->mem_root,
- addr.ptr, addr.null_ptr, addr.null_bit,
+ addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, table->s, attr.decimals);
}
@@ -2265,7 +3375,7 @@ Field *Type_handler_newdate::make_table_field(const LEX_CSTRING *name,
{
return new (table->in_use->mem_root)
- Field_newdate(addr.ptr, addr.null_ptr, addr.null_bit,
+ Field_newdate(addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name);
}
@@ -2282,7 +3392,7 @@ Field *Type_handler_date::make_table_field(const LEX_CSTRING *name,
*/
DBUG_ASSERT(0);
return new (table->in_use->mem_root)
- Field_date(addr.ptr, addr.null_ptr, addr.null_bit,
+ Field_date(addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name);
}
@@ -2294,7 +3404,7 @@ Field *Type_handler_time::make_table_field(const LEX_CSTRING *name,
{
return new_Field_time(table->in_use->mem_root,
- addr.ptr, addr.null_ptr, addr.null_bit,
+ addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, attr.decimals);
}
@@ -2311,7 +3421,7 @@ Field *Type_handler_time2::make_table_field(const LEX_CSTRING *name,
make_table_field() for make_field() purposes in field.cc.
*/
return new_Field_time(table->in_use->mem_root,
- addr.ptr, addr.null_ptr, addr.null_bit,
+ addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, attr.decimals);
}
@@ -2323,7 +3433,7 @@ Field *Type_handler_datetime::make_table_field(const LEX_CSTRING *name,
{
return new_Field_datetime(table->in_use->mem_root,
- addr.ptr, addr.null_ptr, addr.null_bit,
+ addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, attr.decimals);
}
@@ -2338,7 +3448,7 @@ Field *Type_handler_datetime2::make_table_field(const LEX_CSTRING *name,
make_table_field() for make_field() purposes in field.cc.
*/
return new_Field_datetime(table->in_use->mem_root,
- addr.ptr, addr.null_ptr, addr.null_bit,
+ addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, attr.decimals);
}
@@ -2350,8 +3460,8 @@ Field *Type_handler_bit::make_table_field(const LEX_CSTRING *name,
{
return new (table->in_use->mem_root)
- Field_bit_as_char(addr.ptr, attr.max_length,
- addr.null_ptr, addr.null_bit,
+ Field_bit_as_char(addr.ptr(), attr.max_length,
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name);
}
@@ -2363,7 +3473,8 @@ Field *Type_handler_string::make_table_field(const LEX_CSTRING *name,
{
return new (table->in_use->mem_root)
- Field_string(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit,
+ Field_string(addr.ptr(), attr.max_length,
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name, attr.collation);
}
@@ -2377,9 +3488,9 @@ Field *Type_handler_varchar::make_table_field(const LEX_CSTRING *name,
DBUG_ASSERT(HA_VARCHAR_PACKLENGTH(attr.max_length) <=
MAX_FIELD_VARCHARLENGTH);
return new (table->in_use->mem_root)
- Field_varstring(addr.ptr, attr.max_length,
+ Field_varstring(addr.ptr(), attr.max_length,
HA_VARCHAR_PACKLENGTH(attr.max_length),
- addr.null_ptr, addr.null_bit,
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
table->s, attr.collation);
}
@@ -2392,7 +3503,7 @@ Field *Type_handler_tiny_blob::make_table_field(const LEX_CSTRING *name,
{
return new (table->in_use->mem_root)
- Field_blob(addr.ptr, addr.null_ptr, addr.null_bit,
+ Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, table->s,
1, attr.collation);
}
@@ -2405,7 +3516,7 @@ Field *Type_handler_blob::make_table_field(const LEX_CSTRING *name,
{
return new (table->in_use->mem_root)
- Field_blob(addr.ptr, addr.null_ptr, addr.null_bit,
+ Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, table->s,
2, attr.collation);
}
@@ -2419,7 +3530,7 @@ Type_handler_medium_blob::make_table_field(const LEX_CSTRING *name,
{
return new (table->in_use->mem_root)
- Field_blob(addr.ptr, addr.null_ptr, addr.null_bit,
+ Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, table->s,
3, attr.collation);
}
@@ -2432,7 +3543,7 @@ Field *Type_handler_long_blob::make_table_field(const LEX_CSTRING *name,
{
return new (table->in_use->mem_root)
- Field_blob(addr.ptr, addr.null_ptr, addr.null_bit,
+ Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, table->s,
4, attr.collation);
}
@@ -2446,7 +3557,7 @@ Field *Type_handler_geometry::make_table_field(const LEX_CSTRING *name,
TABLE *table) const
{
return new (table->in_use->mem_root)
- Field_geom(addr.ptr, addr.null_ptr, addr.null_bit,
+ Field_geom(addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, table->s, 4,
(Field::geometry_type) attr.uint_geometry_type(),
0);
@@ -2462,7 +3573,8 @@ Field *Type_handler_enum::make_table_field(const LEX_CSTRING *name,
TYPELIB *typelib= attr.get_typelib();
DBUG_ASSERT(typelib);
return new (table->in_use->mem_root)
- Field_enum(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit,
+ Field_enum(addr.ptr(), attr.max_length,
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
get_enum_pack_length(typelib->count), typelib,
attr.collation);
@@ -2478,7 +3590,8 @@ Field *Type_handler_set::make_table_field(const LEX_CSTRING *name,
TYPELIB *typelib= attr.get_typelib();
DBUG_ASSERT(typelib);
return new (table->in_use->mem_root)
- Field_set(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit,
+ Field_set(addr.ptr(), attr.max_length,
+ addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
get_enum_pack_length(typelib->count), typelib,
attr.collation);
@@ -2522,7 +3635,6 @@ uint32 Type_handler_temporal_result::max_display_length(const Item *item) const
return item->max_length;
}
-
uint32 Type_handler_string_result::max_display_length(const Item *item) const
{
return item->max_length;
@@ -2547,6 +3659,121 @@ uint32 Type_handler_general_purpose_int::max_display_length(const Item *item)
return type_limits_int_by_unsigned_flag(item->unsigned_flag)->char_length();
}
+/*************************************************************************/
+
+uint32
+Type_handler_decimal_result::Item_decimal_notation_int_digits(const Item *item)
+ const
+{
+ return item->decimal_int_part();
+}
+
+
+uint32
+Type_handler_temporal_result::Item_decimal_notation_int_digits(const Item *item)
+ const
+{
+ return item->decimal_int_part();
+}
+
+
+uint32
+Type_handler_bit::Item_decimal_notation_int_digits(const Item *item)
+ const
+{
+ return Bit_decimal_notation_int_digits_by_nbits(item->max_length);
+}
+
+
+uint32
+Type_handler_general_purpose_int::Item_decimal_notation_int_digits(
+ const Item *item) const
+{
+ return type_limits_int_by_unsigned_flag(item->unsigned_flag)->precision();
+}
+
+/*************************************************************************/
+
+/*
+ Binary to Decimal digits ratio converges to log2(10) thus using 3 as
+ a divisor.
+*/
+uint32
+Type_handler_bit::Bit_decimal_notation_int_digits_by_nbits(uint nbits)
+{
+ DBUG_ASSERT(nbits > 0);
+ DBUG_ASSERT(nbits <= 64);
+ set_if_smaller(nbits, 64); // Safety
+ static uint ndigits[65]=
+ {0,
+ 1,1,1,2,2,2,3,3, // 1..8 bits
+ 3,4,4,4,4,5,5,5, // 9..16 bits
+ 6,6,6,7,7,7,7,8, // 17..24 bits
+ 8,8,9,9,9,10,10,10, // 25..32 bits
+ 10,11,11,11,12,12,12,13, // 33..40 bits
+ 13,13,13,14,14,14,15,15, // 41..48 bits
+ 15,16,16,16,16,17,17,17, // 49..56 bits
+ 18,18,18,19,19,19,19,20 // 57..64 bits
+ };
+ return ndigits[nbits];
+}
+
+/*************************************************************************/
+
+void Type_handler_row::Item_update_null_value(Item *item) const
+{
+ DBUG_ASSERT(0);
+ item->null_value= true;
+}
+
+
+void Type_handler_time_common::Item_update_null_value(Item *item) const
+{
+ MYSQL_TIME ltime;
+ THD *thd= current_thd;
+ (void) item->get_date(thd, &ltime, Time::Options(TIME_TIME_ONLY, thd));
+}
+
+
+void Type_handler_temporal_with_date::Item_update_null_value(Item *item) const
+{
+ MYSQL_TIME ltime;
+ THD *thd= current_thd;
+ (void) item->get_date(thd, &ltime, Datetime::Options(thd));
+}
+
+
+void Type_handler_string_result::Item_update_null_value(Item *item) const
+{
+ StringBuffer<MAX_FIELD_WIDTH> tmp;
+ (void) item->val_str(&tmp);
+}
+
+
+void Type_handler_real_result::Item_update_null_value(Item *item) const
+{
+ (void) item->val_real();
+}
+
+
+void Type_handler_decimal_result::Item_update_null_value(Item *item) const
+{
+ my_decimal tmp;
+ (void) item->val_decimal(&tmp);
+}
+
+
+void Type_handler_int_result::Item_update_null_value(Item *item) const
+{
+ (void) item->val_int();
+}
+
+
+void Type_handler_bool::Item_update_null_value(Item *item) const
+{
+ (void) item->val_bool();
+}
+
/*************************************************************************/
@@ -2565,6 +3792,18 @@ int Type_handler_temporal_with_date::Item_save_in_field(Item *item,
}
+int Type_handler_timestamp_common::Item_save_in_field(Item *item,
+ Field *field,
+ bool no_conversions)
+ const
+{
+ Timestamp_or_zero_datetime_native_null tmp(field->table->in_use, item, true);
+ if (tmp.is_null())
+ return set_field_to_null_with_conversions(field, no_conversions);
+ return tmp.save_in_field(field, item->decimals);
+}
+
+
int Type_handler_string_result::Item_save_in_field(Item *item, Field *field,
bool no_conversions) const
{
@@ -2631,6 +3870,12 @@ Type_handler_temporal_with_date::set_comparator_func(Arg_comparator *cmp) const
return cmp->set_cmp_func_datetime();
}
+bool
+Type_handler_timestamp_common::set_comparator_func(Arg_comparator *cmp) const
+{
+ return cmp->set_cmp_func_native();
+}
+
/*************************************************************************/
@@ -2742,7 +3987,7 @@ Type_handler_int_result::Item_get_cache(THD *thd, const Item *item) const
Item_cache *
Type_handler_year::Item_get_cache(THD *thd, const Item *item) const
{
- return new (thd->mem_root) Item_cache_year(thd);
+ return new (thd->mem_root) Item_cache_year(thd, item->type_handler());
}
Item_cache *
@@ -2772,7 +4017,7 @@ Type_handler_string_result::Item_get_cache(THD *thd, const Item *item) const
Item_cache *
Type_handler_timestamp_common::Item_get_cache(THD *thd, const Item *item) const
{
- return new (thd->mem_root) Item_cache_datetime(thd);
+ return new (thd->mem_root) Item_cache_timestamp(thd);
}
Item_cache *
@@ -2793,6 +4038,22 @@ Type_handler_date_common::Item_get_cache(THD *thd, const Item *item) const
return new (thd->mem_root) Item_cache_date(thd);
}
+
+/*************************************************************************/
+
+Item_copy *
+Type_handler::create_item_copy(THD *thd, Item *item) const
+{
+ return new (thd->mem_root) Item_copy_string(thd, item);
+}
+
+
+Item_copy *
+Type_handler_timestamp_common::create_item_copy(THD *thd, Item *item) const
+{
+ return new (thd->mem_root) Item_copy_timestamp(thd, item);
+}
+
/*************************************************************************/
bool Type_handler_int_result::
@@ -2868,8 +4129,21 @@ bool Type_handler_typelib::
TYPELIB *typelib= NULL;
for (uint i= 0; i < nitems; i++)
{
- if ((typelib= items[i]->get_typelib()))
- break;
+ TYPELIB *typelib2;
+ if ((typelib2= items[i]->get_typelib()))
+ {
+ if (typelib)
+ {
+ /*
+ Two ENUM/SET columns found. We convert such combinations to VARCHAR.
+ This may change in the future to preserve ENUM/SET
+ if typelib definitions are equal.
+ */
+ handler->set_handler(&type_handler_varchar);
+ return func->aggregate_attributes_string(func_name, items, nitems);
+ }
+ typelib= typelib2;
+ }
}
DBUG_ASSERT(typelib); // There must be at least one typelib
func->set_typelib(typelib);
@@ -2981,8 +4255,18 @@ bool Type_handler_temporal_result::
Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
Item **items, uint nitems) const
{
+ DBUG_ASSERT(func->field_type() != MYSQL_TYPE_DATE);
bool rc= Type_handler::Item_func_min_max_fix_attributes(thd, func,
items, nitems);
+ bool is_time= func->field_type() == MYSQL_TYPE_TIME;
+ func->decimals= 0;
+ for (uint i= 0; i < nitems; i++)
+ {
+ uint deci= is_time ? items[i]->time_precision(thd) :
+ items[i]->datetime_precision(thd);
+ set_if_bigger(func->decimals, deci);
+ }
+
if (rc || func->maybe_null)
return rc;
/*
@@ -3026,7 +4310,6 @@ bool Type_handler_temporal_result::
DATETIME DATETIME no conversion
DATETIME TIMESTAMP safe conversion
DATETIME DATE safe conversion
- DATE DATE no conversion
TIME TIME no conversion
Note, a function cannot return TIMESTAMP if it has non-TIMESTAMP
@@ -3043,9 +4326,6 @@ bool Type_handler_temporal_result::
-------------------- ------------- -------
TIMESTAMP TIME Not possible
DATETIME TIME depends on OLD_MODE_ZERO_DATE_TIME_CAST
- DATE TIMESTAMP Not possible
- DATE DATETIME Not possible
- DATE TIME Not possible
TIME TIMESTAMP Not possible
TIME DATETIME Not possible
TIME DATE Not possible
@@ -3068,6 +4348,30 @@ bool Type_handler_temporal_result::
}
+bool Type_handler_date_common::
+ Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
+ Item **items, uint nitems) const
+{
+ func->fix_attributes_date();
+ if (func->maybe_null)
+ return false;
+ /*
+ We cannot trust the generic maybe_null value calculated during fix_fields().
+ If a conversion from non-temoral types to DATE happens,
+ then the result can be NULL (even if all arguments are not NULL).
+ */
+ for (uint i= 0; i < nitems; i++)
+ {
+ if (items[i]->type_handler()->cmp_type() != TIME_RESULT)
+ {
+ func->maybe_null= true;
+ break;
+ }
+ }
+ return false;
+}
+
+
bool Type_handler_real_result::
Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
Item **items, uint nitems) const
@@ -3090,6 +4394,13 @@ bool Type_handler_int_result::
}
+bool Type_handler_bool::
+ Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
+{
+ return func->fix_length_and_dec_numeric(&type_handler_bool);
+}
+
+
bool Type_handler_real_result::
Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
{
@@ -3113,9 +4424,6 @@ bool Type_handler_string_result::
}
-/**
- Traditional temporal types always preserve the type of the argument.
-*/
bool Type_handler_temporal_result::
Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
{
@@ -3288,15 +4596,6 @@ bool Type_handler_int_result::Item_val_bool(Item *item) const
return item->val_int() != 0;
}
-bool Type_handler_decimal_result::Item_val_bool(Item *item) const
-{
- my_decimal decimal_value;
- my_decimal *val= item->val_decimal(&decimal_value);
- if (val)
- return !my_decimal_is_zero(val);
- return false;
-}
-
bool Type_handler_temporal_result::Item_val_bool(Item *item) const
{
return item->val_real() != 0.0;
@@ -3310,48 +4609,93 @@ bool Type_handler_string_result::Item_val_bool(Item *item) const
/*************************************************************************/
-bool Type_handler_int_result::Item_get_date(Item *item, MYSQL_TIME *ltime,
- ulonglong fuzzydate) const
+
+bool Type_handler::Item_get_date_with_warn(THD *thd, Item *item,
+ MYSQL_TIME *ltime,
+ date_mode_t fuzzydate) const
{
- return item->get_date_from_int(ltime, fuzzydate);
+ const TABLE_SHARE *s= item->field_table_or_null();
+ Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
+ s ? s->table_name.str : nullptr,
+ item->field_name_or_null(), ltime, fuzzydate);
+ Item_get_date(thd, item, &warn, ltime, fuzzydate);
+ return ltime->time_type < 0;
}
-bool Type_handler_year::Item_get_date(Item *item, MYSQL_TIME *ltime,
- ulonglong fuzzydate) const
+bool Type_handler::Item_func_hybrid_field_type_get_date_with_warn(THD *thd,
+ Item_func_hybrid_field_type *item,
+ MYSQL_TIME *ltime,
+ date_mode_t mode) const
{
- return item->get_date_from_year(ltime, fuzzydate);
+ const TABLE_SHARE *s= item->field_table_or_null();
+ Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
+ s ? s->table_name.str : nullptr,
+ item->field_name_or_null(), ltime, mode);
+ Item_func_hybrid_field_type_get_date(thd, item, &warn, ltime, mode);
+ return ltime->time_type < 0;
}
-bool Type_handler_real_result::Item_get_date(Item *item, MYSQL_TIME *ltime,
- ulonglong fuzzydate) const
+/************************************************************************/
+void Type_handler_decimal_result::Item_get_date(THD *thd, Item *item,
+ Temporal::Warn *warn,
+ MYSQL_TIME *ltime,
+ date_mode_t fuzzydate) const
{
- return item->get_date_from_real(ltime, fuzzydate);
+ new(ltime) Temporal_hybrid(thd, warn, VDec(item).ptr(), fuzzydate);
}
-bool Type_handler_decimal_result::Item_get_date(Item *item, MYSQL_TIME *ltime,
- ulonglong fuzzydate) const
+void Type_handler_int_result::Item_get_date(THD *thd, Item *item,
+ Temporal::Warn *warn,
+ MYSQL_TIME *to,
+ date_mode_t mode) const
{
- return item->get_date_from_decimal(ltime, fuzzydate);
+ new(to) Temporal_hybrid(thd, warn, item->to_longlong_hybrid_null(), mode);
}
-bool Type_handler_string_result::Item_get_date(Item *item, MYSQL_TIME *ltime,
- ulonglong fuzzydate) const
+void Type_handler_year::Item_get_date(THD *thd, Item *item,
+ Temporal::Warn *warn,
+ MYSQL_TIME *ltime,
+ date_mode_t fuzzydate) const
{
- return item->get_date_from_string(ltime, fuzzydate);
+ VYear year(item);
+ DBUG_ASSERT(!year.truncated());
+ Longlong_hybrid_null nr(Longlong_null(year.to_YYYYMMDD(), year.is_null()),
+ item->unsigned_flag);
+ new(ltime) Temporal_hybrid(thd, warn, nr, fuzzydate);
}
-bool Type_handler_temporal_result::Item_get_date(Item *item, MYSQL_TIME *ltime,
- ulonglong fuzzydate) const
+void Type_handler_real_result::Item_get_date(THD *thd, Item *item,
+ Temporal::Warn *warn,
+ MYSQL_TIME *ltime,
+ date_mode_t fuzzydate) const
+{
+ new(ltime) Temporal_hybrid(thd, warn, item->to_double_null(), fuzzydate);
+}
+
+
+void Type_handler_string_result::Item_get_date(THD *thd, Item *item,
+ Temporal::Warn *warn,
+ MYSQL_TIME *ltime,
+ date_mode_t mode) const
+{
+ StringBuffer<40> tmp;
+ new(ltime) Temporal_hybrid(thd, warn, item->val_str(&tmp), mode);
+}
+
+
+void Type_handler_temporal_result::Item_get_date(THD *thd, Item *item,
+ Temporal::Warn *warn,
+ MYSQL_TIME *ltime,
+ date_mode_t fuzzydate) const
{
DBUG_ASSERT(0); // Temporal type items must implement native get_date()
item->null_value= true;
- set_zero_time(ltime, mysql_timestamp_type());
- return true;
+ set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
}
@@ -3360,19 +4704,19 @@ bool Type_handler_temporal_result::Item_get_date(Item *item, MYSQL_TIME *ltime,
longlong Type_handler_real_result::
Item_val_int_signed_typecast(Item *item) const
{
- return item->val_int();
+ return item->val_int_signed_typecast_from_real();
}
longlong Type_handler_int_result::
Item_val_int_signed_typecast(Item *item) const
{
- return item->val_int_signed_typecast_from_int();
+ return item->val_int();
}
longlong Type_handler_decimal_result::
Item_val_int_signed_typecast(Item *item) const
{
- return item->val_int();
+ return VDec(item).to_longlong(false);
}
longlong Type_handler_temporal_result::
@@ -3392,7 +4736,7 @@ longlong Type_handler_string_result::
longlong Type_handler_real_result::
Item_val_int_unsigned_typecast(Item *item) const
{
- return item->val_int_unsigned_typecast_from_int();
+ return item->val_int_unsigned_typecast_from_real();
}
longlong Type_handler_int_result::
@@ -3401,16 +4745,36 @@ longlong Type_handler_int_result::
return item->val_int_unsigned_typecast_from_int();
}
-longlong Type_handler_decimal_result::
+longlong Type_handler_temporal_result::
Item_val_int_unsigned_typecast(Item *item) const
{
- return item->val_int_unsigned_typecast_from_decimal();
+ return item->val_int_unsigned_typecast_from_int();
}
-longlong Type_handler_temporal_result::
+longlong Type_handler_time_common::
Item_val_int_unsigned_typecast(Item *item) const
{
- return item->val_int_unsigned_typecast_from_int();
+ /*
+ TODO: this should eventually be fixed to do rounding
+ when TIME_ROUND_FRACTIONAL is enabled, together with
+ Field_{tiny|short|long|longlong}::store_time_dec().
+ See MDEV-19502.
+ */
+ THD *thd= current_thd;
+ Time tm(thd, item);
+ DBUG_ASSERT(!tm.is_valid_time() == item->null_value);
+ if (!tm.is_valid_time())
+ return 0;
+ longlong res= tm.to_longlong();
+ if (res < 0)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_DATA_OVERFLOW, ER_THD(thd, ER_DATA_OVERFLOW),
+ ErrConvTime(tm.get_mysql_time()).ptr(),
+ "UNSIGNED BIGINT");
+ return 0;
+ }
+ return res;
}
longlong Type_handler_string_result::
@@ -3467,7 +4831,7 @@ Type_handler_decimal_result::Item_func_hybrid_field_type_val_str(
Item_func_hybrid_field_type *item,
String *str) const
{
- return item->val_str_from_decimal_op(str);
+ return VDec_op(item).to_string_round(str, item->decimals);
}
@@ -3476,7 +4840,7 @@ Type_handler_decimal_result::Item_func_hybrid_field_type_val_real(
Item_func_hybrid_field_type *item)
const
{
- return item->val_real_from_decimal_op();
+ return VDec_op(item).to_double();
}
@@ -3485,7 +4849,7 @@ Type_handler_decimal_result::Item_func_hybrid_field_type_val_int(
Item_func_hybrid_field_type *item)
const
{
- return item->val_int_from_decimal_op();
+ return VDec_op(item).to_longlong(item->unsigned_flag);
}
@@ -3494,17 +4858,35 @@ Type_handler_decimal_result::Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *item,
my_decimal *dec) const
{
- return item->val_decimal_from_decimal_op(dec);
+ return VDec_op(item).to_decimal(dec);
}
-bool
+void
Type_handler_decimal_result::Item_func_hybrid_field_type_get_date(
+ THD *thd,
+ Item_func_hybrid_field_type *item,
+ Temporal::Warn *warn,
+ MYSQL_TIME *ltime,
+ date_mode_t fuzzydate) const
+{
+ new (ltime) Temporal_hybrid(thd, warn, VDec_op(item).ptr(), fuzzydate);
+}
+
+
+void
+Type_handler_year::Item_func_hybrid_field_type_get_date(
+ THD *thd,
Item_func_hybrid_field_type *item,
+ Temporal::Warn *warn,
MYSQL_TIME *ltime,
- ulonglong fuzzydate) const
+ date_mode_t fuzzydate) const
{
- return item->get_date_from_decimal_op(ltime, fuzzydate);
+ VYear_op year(item);
+ DBUG_ASSERT(!year.truncated());
+ Longlong_hybrid_null nr(Longlong_null(year.to_YYYYMMDD(), year.is_null()),
+ item->unsigned_flag);
+ new(ltime) Temporal_hybrid(thd, warn, nr, fuzzydate);
}
@@ -3547,17 +4929,18 @@ Type_handler_int_result::Item_func_hybrid_field_type_val_decimal(
}
-bool
+void
Type_handler_int_result::Item_func_hybrid_field_type_get_date(
+ THD *thd,
Item_func_hybrid_field_type *item,
- MYSQL_TIME *ltime,
- ulonglong fuzzydate) const
+ Temporal::Warn *warn,
+ MYSQL_TIME *to,
+ date_mode_t mode) const
{
- return item->get_date_from_int_op(ltime, fuzzydate);
+ new(to) Temporal_hybrid(thd, warn, item->to_longlong_hybrid_null_op(), mode);
}
-
/***************************************************************************/
String *
@@ -3568,7 +4951,6 @@ Type_handler_double::Item_func_hybrid_field_type_val_str(
return item->val_str_from_real_op(str);
}
-
String *
Type_handler_float::Item_func_hybrid_field_type_val_str(
Item_func_hybrid_field_type *item,
@@ -3581,7 +4963,6 @@ Type_handler_float::Item_func_hybrid_field_type_val_str(
return str;
}
-
double
Type_handler_real_result::Item_func_hybrid_field_type_val_real(
Item_func_hybrid_field_type *item)
@@ -3609,13 +4990,15 @@ Type_handler_real_result::Item_func_hybrid_field_type_val_decimal(
}
-bool
+void
Type_handler_real_result::Item_func_hybrid_field_type_get_date(
+ THD *thd,
Item_func_hybrid_field_type *item,
- MYSQL_TIME *ltime,
- ulonglong fuzzydate) const
+ Temporal::Warn *warn,
+ MYSQL_TIME *to,
+ date_mode_t mode) const
{
- return item->get_date_from_real_op(ltime, fuzzydate);
+ new(to) Temporal_hybrid(thd, warn, item->to_double_null_op(), mode);
}
@@ -3657,13 +5040,16 @@ Type_handler_temporal_result::Item_func_hybrid_field_type_val_decimal(
}
-bool
+void
Type_handler_temporal_result::Item_func_hybrid_field_type_get_date(
+ THD *thd,
Item_func_hybrid_field_type *item,
+ Temporal::Warn *warn,
MYSQL_TIME *ltime,
- ulonglong fuzzydate) const
+ date_mode_t fuzzydate) const
{
- return item->date_op(ltime, fuzzydate);
+ if (item->date_op(thd, ltime, fuzzydate))
+ set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
}
@@ -3705,13 +5091,16 @@ Type_handler_time_common::Item_func_hybrid_field_type_val_decimal(
}
-bool
+void
Type_handler_time_common::Item_func_hybrid_field_type_get_date(
+ THD *thd,
Item_func_hybrid_field_type *item,
+ Temporal::Warn *warn,
MYSQL_TIME *ltime,
- ulonglong fuzzydate) const
+ date_mode_t fuzzydate) const
{
- return item->time_op(ltime);
+ if (item->time_op(thd, ltime))
+ set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
}
@@ -3753,13 +5142,18 @@ Type_handler_string_result::Item_func_hybrid_field_type_val_decimal(
}
-bool
+void
Type_handler_string_result::Item_func_hybrid_field_type_get_date(
+ THD *thd,
Item_func_hybrid_field_type *item,
+ Temporal::Warn *warn,
MYSQL_TIME *ltime,
- ulonglong fuzzydate) const
+ date_mode_t mode) const
{
- return item->get_date_from_str_op(ltime, fuzzydate);
+ StringBuffer<40> tmp;
+ String *res= item->str_op(&tmp);
+ DBUG_ASSERT((res == NULL) == item->null_value);
+ new(ltime) Temporal_hybrid(thd, warn, res, mode);
}
/***************************************************************************/
@@ -3797,10 +5191,22 @@ longlong Type_handler_string_result::
return func->val_int_cmp_string();
}
-longlong Type_handler_temporal_result::
+longlong Type_handler_temporal_with_date::
+ Item_func_between_val_int(Item_func_between *func) const
+{
+ return func->val_int_cmp_datetime();
+}
+
+longlong Type_handler_time_common::
+ Item_func_between_val_int(Item_func_between *func) const
+{
+ return func->val_int_cmp_time();
+}
+
+longlong Type_handler_timestamp_common::
Item_func_between_val_int(Item_func_between *func) const
{
- return func->val_int_cmp_temporal();
+ return func->val_int_cmp_native();
}
longlong Type_handler_int_result::
@@ -3866,6 +5272,12 @@ cmp_item *Type_handler_temporal_with_date::make_cmp_item(THD *thd,
return new (thd->mem_root) cmp_item_datetime;
}
+cmp_item *Type_handler_timestamp_common::make_cmp_item(THD *thd,
+ CHARSET_INFO *cs) const
+{
+ return new (thd->mem_root) cmp_item_timestamp;
+}
+
/***************************************************************************/
static int srtcmp_in(const void *cs_, const void *x_, const void *y_)
@@ -3929,6 +5341,15 @@ Type_handler_temporal_with_date::make_in_vector(THD *thd,
}
+in_vector *
+Type_handler_timestamp_common::make_in_vector(THD *thd,
+ const Item_func_in *func,
+ uint nargs) const
+{
+ return new (thd->mem_root) in_timestamp(thd, nargs);
+}
+
+
in_vector *Type_handler_row::make_in_vector(THD *thd,
const Item_func_in *func,
uint nargs) const
@@ -4023,10 +5444,33 @@ String *Type_handler_string_result::
}
-String *Type_handler_temporal_result::
+String *Type_handler_time_common::
+ Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
+{
+ return Time(func).to_string(str, func->decimals);
+}
+
+
+String *Type_handler_date_common::
+ Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
+{
+ return Date(func).to_string(str);
+}
+
+
+String *Type_handler_datetime_common::
+ Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
+{
+ return Datetime(func).to_string(str, func->decimals);
+}
+
+
+String *Type_handler_timestamp_common::
Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
{
- return func->val_string_from_date(str);
+ THD *thd= current_thd;
+ return Timestamp_or_zero_datetime_native_null(thd, func).
+ to_datetime(thd).to_string(str, func->decimals);
}
@@ -4040,7 +5484,7 @@ String *Type_handler_int_result::
String *Type_handler_decimal_result::
Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
{
- return func->val_string_from_decimal(str);
+ return VDec(func).to_string_round(str, func->decimals);
}
@@ -4069,13 +5513,33 @@ double Type_handler_string_result::
}
-double Type_handler_temporal_result::
+double Type_handler_time_common::
Item_func_min_max_val_real(Item_func_min_max *func) const
{
- MYSQL_TIME ltime;
- if (func->get_date(&ltime, 0))
- return 0;
- return TIME_to_double(&ltime);
+ return Time(current_thd, func).to_double();
+}
+
+
+double Type_handler_date_common::
+ Item_func_min_max_val_real(Item_func_min_max *func) const
+{
+ return Date(current_thd, func).to_double();
+}
+
+
+double Type_handler_datetime_common::
+ Item_func_min_max_val_real(Item_func_min_max *func) const
+{
+ return Datetime(current_thd, func).to_double();
+}
+
+
+double Type_handler_timestamp_common::
+ Item_func_min_max_val_real(Item_func_min_max *func) const
+{
+ THD *thd= current_thd;
+ return Timestamp_or_zero_datetime_native_null(thd, func).
+ to_datetime(thd).to_double();
}
@@ -4093,13 +5557,33 @@ longlong Type_handler_string_result::
}
-longlong Type_handler_temporal_result::
+longlong Type_handler_time_common::
Item_func_min_max_val_int(Item_func_min_max *func) const
{
- MYSQL_TIME ltime;
- if (func->get_date(&ltime, 0))
- return 0;
- return TIME_to_ulonglong(&ltime);
+ return Time(current_thd, func).to_longlong();
+}
+
+
+longlong Type_handler_date_common::
+ Item_func_min_max_val_int(Item_func_min_max *func) const
+{
+ return Date(current_thd, func).to_longlong();
+}
+
+
+longlong Type_handler_datetime_common::
+ Item_func_min_max_val_int(Item_func_min_max *func) const
+{
+ return Datetime(current_thd, func).to_longlong();
+}
+
+
+longlong Type_handler_timestamp_common::
+ Item_func_min_max_val_int(Item_func_min_max *func) const
+{
+ THD *thd= current_thd;
+ return Timestamp_or_zero_datetime_native_null(thd, func).
+ to_datetime(thd).to_longlong();
}
@@ -4126,20 +5610,43 @@ my_decimal *Type_handler_numeric::
}
-my_decimal *Type_handler_temporal_result::
+my_decimal *Type_handler_time_common::
Item_func_min_max_val_decimal(Item_func_min_max *func,
my_decimal *dec) const
{
- MYSQL_TIME ltime;
- if (func->get_date(&ltime, 0))
- return 0;
- return date2my_decimal(&ltime, dec);
+ return Time(current_thd, func).to_decimal(dec);
+}
+
+
+my_decimal *Type_handler_date_common::
+ Item_func_min_max_val_decimal(Item_func_min_max *func,
+ my_decimal *dec) const
+{
+ return Date(current_thd, func).to_decimal(dec);
+}
+
+
+my_decimal *Type_handler_datetime_common::
+ Item_func_min_max_val_decimal(Item_func_min_max *func,
+ my_decimal *dec) const
+{
+ return Datetime(current_thd, func).to_decimal(dec);
+}
+
+
+my_decimal *Type_handler_timestamp_common::
+ Item_func_min_max_val_decimal(Item_func_min_max *func,
+ my_decimal *dec) const
+{
+ THD *thd= current_thd;
+ return Timestamp_or_zero_datetime_native_null(thd, func).
+ to_datetime(thd).to_decimal(dec);
}
bool Type_handler_string_result::
- Item_func_min_max_get_date(Item_func_min_max *func,
- MYSQL_TIME *ltime, ulonglong fuzzydate) const
+ Item_func_min_max_get_date(THD *thd, Item_func_min_max *func,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const
{
/*
just like ::val_int() method of a string item can be called,
@@ -4147,30 +5654,51 @@ bool Type_handler_string_result::
::get_date() can be called for non-temporal values,
for example, SELECT MONTH(GREATEST("2011-11-21", "2010-10-09"))
*/
- return func->get_date_from_string(ltime, fuzzydate);
+ return func->get_date_from_string(thd, ltime, fuzzydate);
}
bool Type_handler_numeric::
- Item_func_min_max_get_date(Item_func_min_max *func,
- MYSQL_TIME *ltime, ulonglong fuzzydate) const
+ Item_func_min_max_get_date(THD *thd, Item_func_min_max *func,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const
{
- return Item_get_date(func, ltime, fuzzydate);
+ return Item_get_date_with_warn(thd, func, ltime, fuzzydate);
}
bool Type_handler_temporal_result::
- Item_func_min_max_get_date(Item_func_min_max *func,
- MYSQL_TIME *ltime, ulonglong fuzzydate) const
+ Item_func_min_max_get_date(THD *thd, Item_func_min_max *func,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const
{
- return func->get_date_native(ltime, fuzzydate);
+ /*
+ - If the caller specified TIME_TIME_ONLY, then it's going to convert
+ a DATETIME or DATE to TIME. So we pass the default flags for date. This is
+ exactly the same with what Item_func_min_max_val_{int|real|decimal|str} or
+ Item_send_datetime() do. We return the value in accordance with the
+ current session date flags and let the caller further convert it to TIME.
+ - If the caller did not specify TIME_TIME_ONLY, then return the value
+ according to the flags supplied by the caller.
+ */
+ return func->get_date_native(thd, ltime,
+ fuzzydate & TIME_TIME_ONLY ?
+ Datetime::Options(thd) :
+ fuzzydate);
}
bool Type_handler_time_common::
- Item_func_min_max_get_date(Item_func_min_max *func,
- MYSQL_TIME *ltime, ulonglong fuzzydate) const
+ Item_func_min_max_get_date(THD *thd, Item_func_min_max *func,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const
{
- return func->get_time_native(ltime);
+ return func->get_time_native(thd, ltime);
+}
+
+
+bool Type_handler_timestamp_common::
+ Item_func_min_max_get_date(THD *thd, Item_func_min_max *func,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const
+{
+ return Timestamp_or_zero_datetime_native_null(thd, func).
+ to_datetime(thd).copy_to_mysql_time(ltime);
}
/***************************************************************************/
@@ -4304,7 +5832,41 @@ bool Type_handler_row::
bool Type_handler_int_result::
Item_func_round_fix_length_and_dec(Item_func_round *item) const
{
- item->fix_arg_int();
+ item->fix_arg_int(this, item->arguments()[0],
+ field_type() == MYSQL_TYPE_LONGLONG);
+ return false;
+}
+
+
+bool Type_handler_year::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ item->fix_arg_int(&type_handler_long, item->arguments()[0], false); // 10.5 merge: fix to type_handler_ulong
+ return false;
+}
+
+
+bool Type_handler_hex_hybrid::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ item->fix_arg_hex_hybrid();
+ return false;
+}
+
+
+bool Type_handler_bit::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ uint nbits= item->arguments()[0]->max_length;
+ item->fix_length_and_dec_ulong_or_ulonglong_by_nbits(nbits);
+ return false;
+}
+
+
+bool Type_handler_typelib::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ item->fix_length_and_dec_long_or_longlong(5, true);
return false;
}
@@ -4325,10 +5887,36 @@ bool Type_handler_decimal_result::
}
-bool Type_handler_temporal_result::
+bool Type_handler_date_common::
Item_func_round_fix_length_and_dec(Item_func_round *item) const
{
- item->fix_arg_double();
+ static const Type_std_attributes attr(8, 0/*dec*/, true/*unsigned*/,
+ DTCollation_numeric::singleton());
+ item->fix_arg_int(&type_handler_long, &attr, false); // 10.5 merge: fix to *_ulong
+ return false;
+}
+
+
+bool Type_handler_time_common::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ item->fix_arg_time();
+ return false;
+}
+
+
+bool Type_handler_datetime_common::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ item->fix_arg_datetime();
+ return false;
+}
+
+
+bool Type_handler_timestamp_common::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ item->fix_arg_datetime();
return false;
}
@@ -4362,7 +5950,43 @@ bool Type_handler_row::
bool Type_handler_int_result::
Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
{
- item->fix_length_and_dec_int_or_decimal();
+ item->Type_std_attributes::set(item->arguments()[0]);
+ item->set_handler(this);
+ return false;
+}
+
+
+bool Type_handler_year::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ item->Type_std_attributes::set(item->arguments()[0]);
+ item->set_handler(&type_handler_long);
+ return false;
+}
+
+
+bool Type_handler_bit::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ uint nbits= item->arguments()[0]->max_length;
+ item->fix_length_and_dec_ulong_or_ulonglong_by_nbits(nbits);
+ return false;
+}
+
+
+bool Type_handler_typelib::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ item->fix_length_and_dec_long_or_longlong(5, true);
+ return false;
+}
+
+
+bool Type_handler_hex_hybrid::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ uint nchars= item->arguments()[0]->decimal_precision();
+ item->fix_length_and_dec_long_or_longlong(nchars, true);
return false;
}
@@ -4383,10 +6007,37 @@ bool Type_handler_decimal_result::
}
-bool Type_handler_temporal_result::
+bool Type_handler_date_common::
Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
{
- item->fix_length_and_dec_int_or_decimal();
+ static const Type_std_attributes attr(8, 0/*dec*/, true/*unsigned*/,
+ DTCollation_numeric::singleton());
+ item->Type_std_attributes::set(attr);
+ item->set_handler(&type_handler_long); // 10.5 merge: fix to *_ulong
+ return false;
+}
+
+
+bool Type_handler_time_common::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ item->fix_length_and_dec_time();
+ return false;
+}
+
+
+bool Type_handler_datetime_common::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ item->fix_length_and_dec_datetime();
+ return false;
+}
+
+
+bool Type_handler_timestamp_common::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ item->fix_length_and_dec_datetime();
return false;
}
@@ -4643,7 +6294,7 @@ bool Type_handler::
Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const
{
uint dec= item->decimals == NOT_FIXED_DEC ?
- item->arguments()[0]->time_precision() :
+ item->arguments()[0]->time_precision(current_thd) :
item->decimals;
item->fix_attributes_temporal(MIN_TIME_WIDTH, dec);
item->maybe_null= true;
@@ -4665,7 +6316,7 @@ bool Type_handler::
const
{
uint dec= item->decimals == NOT_FIXED_DEC ?
- item->arguments()[0]->datetime_precision() :
+ item->arguments()[0]->datetime_precision(current_thd) :
item->decimals;
item->fix_attributes_temporal(MAX_DATETIME_WIDTH, dec);
item->maybe_null= true;
@@ -4929,7 +6580,6 @@ bool Type_handler_decimal_result::
bool Type_handler_temporal_result::
Item_func_div_fix_length_and_dec(Item_func_div *item) const
{
- // Item_func_div::int_op() is not implemented. Disallow DECIMAL->INT downcast.
item->fix_length_and_dec_temporal(false);
return false;
}
@@ -4993,32 +6643,35 @@ bool Type_handler_string_result::
/***************************************************************************/
-uint Type_handler::Item_time_precision(Item *item) const
+uint Type_handler::Item_time_precision(THD *thd, Item *item) const
{
return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
}
-uint Type_handler::Item_datetime_precision(Item *item) const
+uint Type_handler::Item_datetime_precision(THD *thd, Item *item) const
{
return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
}
-uint Type_handler_string_result::Item_temporal_precision(Item *item,
+uint Type_handler_string_result::Item_temporal_precision(THD *thd, Item *item,
bool is_time) const
{
- MYSQL_TIME ltime;
StringBuffer<64> buf;
String *tmp;
MYSQL_TIME_STATUS status;
- DBUG_ASSERT(item->fixed);
+ DBUG_ASSERT(item->is_fixed());
+ // Nanosecond rounding is not needed here, for performance purposes
if ((tmp= item->val_str(&buf)) &&
- !(is_time ?
- str_to_time(tmp->charset(), tmp->ptr(), tmp->length(),
- &ltime, TIME_TIME_ONLY, &status) :
- str_to_datetime(tmp->charset(), tmp->ptr(), tmp->length(),
- &ltime, TIME_FUZZY_DATES, &status)))
+ (is_time ?
+ Time(thd, &status, tmp->ptr(), tmp->length(), tmp->charset(),
+ Time::Options(TIME_TIME_ONLY, TIME_FRAC_TRUNCATE,
+ Time::DATETIME_TO_TIME_YYYYMMDD_TRUNCATE)).
+ is_valid_time() :
+ Datetime(thd, &status, tmp->ptr(), tmp->length(), tmp->charset(),
+ Datetime::Options(TIME_FUZZY_DATES, TIME_FRAC_TRUNCATE)).
+ is_valid_datetime()))
return MY_MIN(status.precision, TIME_SECOND_PART_DIGITS);
return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
}
@@ -5224,7 +6877,7 @@ bool Type_handler::check_null(const Item *item, st_value *value) const
bool Type_handler_null::
- Item_save_in_value(Item *item, st_value *value) const
+ Item_save_in_value(THD *thd, Item *item, st_value *value) const
{
value->m_type= DYN_COL_NULL;
return true;
@@ -5232,7 +6885,7 @@ bool Type_handler_null::
bool Type_handler_row::
- Item_save_in_value(Item *item, st_value *value) const
+ Item_save_in_value(THD *thd, Item *item, st_value *value) const
{
DBUG_ASSERT(0);
value->m_type= DYN_COL_NULL;
@@ -5241,7 +6894,7 @@ bool Type_handler_row::
bool Type_handler_int_result::
- Item_save_in_value(Item *item, st_value *value) const
+ Item_save_in_value(THD *thd, Item *item, st_value *value) const
{
value->m_type= item->unsigned_flag ? DYN_COL_UINT : DYN_COL_INT;
value->value.m_longlong= item->val_int();
@@ -5250,7 +6903,7 @@ bool Type_handler_int_result::
bool Type_handler_real_result::
- Item_save_in_value(Item *item, st_value *value) const
+ Item_save_in_value(THD *thd, Item *item, st_value *value) const
{
value->m_type= DYN_COL_DOUBLE;
value->value.m_double= item->val_real();
@@ -5259,7 +6912,7 @@ bool Type_handler_real_result::
bool Type_handler_decimal_result::
- Item_save_in_value(Item *item, st_value *value) const
+ Item_save_in_value(THD *thd, Item *item, st_value *value) const
{
value->m_type= DYN_COL_DECIMAL;
my_decimal *dec= item->val_decimal(&value->m_decimal);
@@ -5270,7 +6923,7 @@ bool Type_handler_decimal_result::
bool Type_handler_string_result::
- Item_save_in_value(Item *item, st_value *value) const
+ Item_save_in_value(THD *thd, Item *item, st_value *value) const
{
value->m_type= DYN_COL_STRING;
String *str= item->val_str(&value->m_string);
@@ -5281,19 +6934,20 @@ bool Type_handler_string_result::
bool Type_handler_temporal_with_date::
- Item_save_in_value(Item *item, st_value *value) const
+ Item_save_in_value(THD *thd, Item *item, st_value *value) const
{
value->m_type= DYN_COL_DATETIME;
- item->get_date(&value->value.m_time, sql_mode_for_dates(current_thd));
+ item->get_date(thd, &value->value.m_time,
+ Datetime::Options(thd, TIME_FRAC_NONE));
return check_null(item, value);
}
bool Type_handler_time_common::
- Item_save_in_value(Item *item, st_value *value) const
+ Item_save_in_value(THD *thd, Item *item, st_value *value) const
{
value->m_type= DYN_COL_DATETIME;
- item->get_time(&value->value.m_time);
+ item->get_time(thd, &value->value.m_time);
return check_null(item, value);
}
@@ -5474,10 +7128,23 @@ bool Type_handler::
}
+bool Type_handler::Item_send_timestamp(Item *item,
+ Protocol *protocol,
+ st_value *buf) const
+{
+ Timestamp_or_zero_datetime_native_null native(protocol->thd, item);
+ if (native.is_null())
+ return protocol->store_null();
+ native.to_TIME(protocol->thd, &buf->value.m_time);
+ return protocol->store(&buf->value.m_time, item->decimals);
+}
+
+
bool Type_handler::
Item_send_datetime(Item *item, Protocol *protocol, st_value *buf) const
{
- item->get_date(&buf->value.m_time, sql_mode_for_dates(current_thd));
+ item->get_date(protocol->thd, &buf->value.m_time,
+ Datetime::Options(protocol->thd));
if (!item->null_value)
return protocol->store(&buf->value.m_time, item->decimals);
return protocol->store_null();
@@ -5487,7 +7154,8 @@ bool Type_handler::
bool Type_handler::
Item_send_date(Item *item, Protocol *protocol, st_value *buf) const
{
- item->get_date(&buf->value.m_time, sql_mode_for_dates(current_thd));
+ item->get_date(protocol->thd, &buf->value.m_time,
+ Date::Options(protocol->thd));
if (!item->null_value)
return protocol->store_date(&buf->value.m_time);
return protocol->store_null();
@@ -5497,7 +7165,7 @@ bool Type_handler::
bool Type_handler::
Item_send_time(Item *item, Protocol *protocol, st_value *buf) const
{
- item->get_time(&buf->value.m_time);
+ item->get_time(protocol->thd, &buf->value.m_time);
if (!item->null_value)
return protocol->store_time(&buf->value.m_time, item->decimals);
return protocol->store_null();
@@ -5530,11 +7198,10 @@ Item *Type_handler_real_result::
Item *Type_handler_decimal_result::
make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const
{
- my_decimal decimal_value;
- my_decimal *result= item->val_decimal(&decimal_value);
- if (item->null_value)
+ VDec result(item);
+ if (result.is_null())
return new (thd->mem_root) Item_null(thd, item->name.str);
- return new (thd->mem_root) Item_decimal(thd, item->name.str, result,
+ return new (thd->mem_root) Item_decimal(thd, item->name.str, result.ptr(),
item->max_length, item->decimals);
}
@@ -5557,7 +7224,7 @@ Item *Type_handler_time_common::
make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const
{
Item_cache_temporal *cache;
- longlong value= item->val_time_packed();
+ longlong value= item->val_time_packed(thd);
if (item->null_value)
return new (thd->mem_root) Item_null(thd, item->name.str);
cache= new (thd->mem_root) Item_cache_time(thd);
@@ -5571,7 +7238,7 @@ Item *Type_handler_temporal_with_date::
make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const
{
Item_cache_temporal *cache;
- longlong value= item->val_datetime_packed();
+ longlong value= item->val_datetime_packed(thd);
if (item->null_value)
return new (thd->mem_root) Item_null(thd, item->name.str);
cache= new (thd->mem_root) Item_cache_datetime(thd);
@@ -5782,6 +7449,21 @@ Item *Type_handler_long_blob::
return new (thd->mem_root) Item_char_typecast(thd, item, len, real_cs);
}
+Item *Type_handler_interval_DDhhmmssff::
+ create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const
+{
+ if (attr.decimals() > MAX_DATETIME_PRECISION)
+ {
+ wrong_precision_error(ER_TOO_BIG_PRECISION, item, attr.decimals(),
+ MAX_DATETIME_PRECISION);
+ return 0;
+ }
+ return new (thd->mem_root) Item_interval_DDhhmmssff_typecast(thd, item,
+ (uint)
+ attr.decimals());
+}
+
/***************************************************************************/
void Type_handler_string_result::Item_param_setup_conversion(THD *thd,
@@ -5933,6 +7615,490 @@ void Type_handler_geometry::Item_param_set_param_func(Item_param *param,
/***************************************************************************/
+Field *Type_handler_row::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ DBUG_ASSERT(attr->length == 0);
+ DBUG_ASSERT(f_maybe_null(attr->pack_flag));
+ return new (mem_root) Field_row(rec.ptr(), name);
+}
+
+
+Field *Type_handler_olddecimal::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_decimal(rec.ptr(), (uint32) attr->length,
+ rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name,
+ f_decimals(attr->pack_flag),
+ f_is_zerofill(attr->pack_flag) != 0,
+ f_is_dec(attr->pack_flag) == 0);
+}
+
+
+Field *Type_handler_newdecimal::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_new_decimal(rec.ptr(), (uint32) attr->length,
+ rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name,
+ f_decimals(attr->pack_flag),
+ f_is_zerofill(attr->pack_flag) != 0,
+ f_is_dec(attr->pack_flag) == 0);
+}
+
+
+Field *Type_handler_float::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ int decimals= f_decimals(attr->pack_flag);
+ if (decimals == FLOATING_POINT_DECIMALS)
+ decimals= NOT_FIXED_DEC;
+ return new (mem_root)
+ Field_float(rec.ptr(), (uint32) attr->length,
+ rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name, decimals,
+ f_is_zerofill(attr->pack_flag) != 0,
+ f_is_dec(attr->pack_flag)== 0);
+}
+
+
+Field *Type_handler_double::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ int decimals= f_decimals(attr->pack_flag);
+ if (decimals == FLOATING_POINT_DECIMALS)
+ decimals= NOT_FIXED_DEC;
+ return new (mem_root)
+ Field_double(rec.ptr(), (uint32) attr->length,
+ rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name, decimals,
+ f_is_zerofill(attr->pack_flag) != 0,
+ f_is_dec(attr->pack_flag)== 0);
+}
+
+
+Field *Type_handler_tiny::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_tiny(rec.ptr(), (uint32) attr->length, rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name,
+ f_is_zerofill(attr->pack_flag) != 0,
+ f_is_dec(attr->pack_flag) == 0);
+}
+
+
+Field *Type_handler_short::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_short(rec.ptr(), (uint32) attr->length,
+ rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name,
+ f_is_zerofill(attr->pack_flag) != 0,
+ f_is_dec(attr->pack_flag) == 0);
+}
+
+
+Field *Type_handler_int24::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_medium(rec.ptr(), (uint32) attr->length,
+ rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name,
+ f_is_zerofill(attr->pack_flag) != 0,
+ f_is_dec(attr->pack_flag) == 0);
+}
+
+
+Field *Type_handler_long::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_long(rec.ptr(), (uint32) attr->length, rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name,
+ f_is_zerofill(attr->pack_flag) != 0,
+ f_is_dec(attr->pack_flag) == 0);
+}
+
+
+Field *Type_handler_longlong::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ if (flags & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG))
+ return new (mem_root)
+ Field_vers_trx_id(rec.ptr(), (uint32) attr->length,
+ rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name,
+ f_is_zerofill(attr->pack_flag) != 0,
+ f_is_dec(attr->pack_flag) == 0);
+ return new (mem_root)
+ Field_longlong(rec.ptr(), (uint32) attr->length,
+ rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name,
+ f_is_zerofill(attr->pack_flag) != 0,
+ f_is_dec(attr->pack_flag) == 0);
+}
+
+
+Field *Type_handler_timestamp::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new_Field_timestamp(mem_root,
+ rec.ptr(), rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name, share,
+ attr->temporal_dec(MAX_DATETIME_WIDTH));
+}
+
+
+Field *Type_handler_timestamp2::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_timestampf(rec.ptr(), rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check,
+ name, share, attr->temporal_dec(MAX_DATETIME_WIDTH));
+}
+
+
+Field *Type_handler_year::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_year(rec.ptr(), (uint32) attr->length, rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name);
+}
+
+
+Field *Type_handler_date::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_date(rec.ptr(),rec.null_ptr(),rec.null_bit(),
+ attr->unireg_check, name);
+}
+
+
+Field *Type_handler_newdate::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_newdate(rec.ptr(), rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name);
+}
+
+
+Field *Type_handler_time::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new_Field_time(mem_root, rec.ptr(), rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name,
+ attr->temporal_dec(MIN_TIME_WIDTH));
+}
+
+
+Field *Type_handler_time2::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_timef(rec.ptr(), rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name,
+ attr->temporal_dec(MIN_TIME_WIDTH));
+}
+
+
+Field *Type_handler_datetime::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new_Field_datetime(mem_root, rec.ptr(), rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name,
+ attr->temporal_dec(MAX_DATETIME_WIDTH));
+}
+
+
+Field *Type_handler_datetime2::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_datetimef(rec.ptr(), rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name,
+ attr->temporal_dec(MAX_DATETIME_WIDTH));
+}
+
+
+Field *Type_handler_null::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_null(rec.ptr(), (uint32) attr->length, attr->unireg_check,
+ name, attr->charset);
+}
+
+
+Field *Type_handler_bit::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return f_bit_as_char(attr->pack_flag) ?
+ new (mem_root) Field_bit_as_char(rec.ptr(), (uint32) attr->length,
+ rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name) :
+ new (mem_root) Field_bit(rec.ptr(), (uint32) attr->length,
+ rec.null_ptr(), rec.null_bit(),
+ bit.ptr(), bit.offs(), attr->unireg_check, name);
+}
+
+
+#ifdef HAVE_SPATIAL
+Field *Type_handler_geometry::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ status_var_increment(current_thd->status_var.feature_gis);
+ return new (mem_root)
+ Field_geom(rec.ptr(), rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name, share,
+ attr->pack_flag_to_pack_length(), attr->geom_type, attr->srid);
+}
+#endif
+
+
+Field *Type_handler_string::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_string(rec.ptr(), (uint32) attr->length,
+ rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name, attr->charset);
+}
+
+
+Field *Type_handler_varchar::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ if (attr->unireg_check == Field::TMYSQL_COMPRESSED)
+ return new (mem_root)
+ Field_varstring_compressed(rec.ptr(), (uint32) attr->length,
+ HA_VARCHAR_PACKLENGTH((uint32) attr->length),
+ rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name, share, attr->charset,
+ zlib_compression_method);
+ return new (mem_root)
+ Field_varstring(rec.ptr(), (uint32) attr->length,
+ HA_VARCHAR_PACKLENGTH((uint32) attr->length),
+ rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name, share, attr->charset);
+}
+
+
+Field *Type_handler_blob_common::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ if (attr->unireg_check == Field::TMYSQL_COMPRESSED)
+ return new (mem_root)
+ Field_blob_compressed(rec.ptr(), rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name, share,
+ attr->pack_flag_to_pack_length(), attr->charset,
+ zlib_compression_method);
+ return new (mem_root)
+ Field_blob(rec.ptr(), rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name, share,
+ attr->pack_flag_to_pack_length(), attr->charset);
+}
+
+
+Field *Type_handler_enum::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_enum(rec.ptr(), (uint32) attr->length, rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name, attr->pack_flag_to_pack_length(),
+ attr->interval, attr->charset);
+}
+
+
+Field *Type_handler_set::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ return new (mem_root)
+ Field_set(rec.ptr(), (uint32) attr->length, rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name, attr->pack_flag_to_pack_length(),
+ attr->interval, attr->charset);
+}
+
+
+/***************************************************************************/
+
+void Type_handler::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ def->frm_pack_basic(buff);
+ def->frm_pack_charset(buff);
+}
+
+
+#ifdef HAVE_SPATIAL
+void Type_handler_geometry::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ def->frm_pack_basic(buff);
+ buff[11]= 0;
+ buff[14]= (uchar) def->geom_type;
+}
+#endif
+
+
+/***************************************************************************/
+
+bool Type_handler::
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options)
+ const
+{
+ attr->frm_unpack_basic(buffer);
+ return attr->frm_unpack_charset(share, buffer);
+}
+
+
+#ifdef HAVE_SPATIAL
+bool Type_handler_geometry::
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options)
+ const
+{
+ uint gis_opt_read, gis_length, gis_decimals;
+ Field_geom::storage_type st_type;
+ attr->frm_unpack_basic(buffer);
+ // charset and geometry_type share the same byte in frm
+ attr->geom_type= (Field::geometry_type) buffer[14];
+ gis_opt_read= gis_field_options_read(gis_options->str,
+ gis_options->length,
+ &st_type, &gis_length,
+ &gis_decimals, &attr->srid);
+ gis_options->str+= gis_opt_read;
+ gis_options->length-= gis_opt_read;
+ return false;
+}
+#endif
+
+/***************************************************************************/
+
bool Type_handler::Vers_history_point_resolve_unit(THD *thd,
Vers_history_point *point)
const
@@ -5992,6 +8158,556 @@ bool Type_handler_general_purpose_string::
/***************************************************************************/
+bool Type_handler_null::Item_const_eq(const Item_const *a,
+ const Item_const *b,
+ bool binary_cmp) const
+{
+ return true;
+}
+
+
+bool Type_handler_real_result::Item_const_eq(const Item_const *a,
+ const Item_const *b,
+ bool binary_cmp) const
+{
+ const double *va= a->const_ptr_double();
+ const double *vb= b->const_ptr_double();
+ return va[0] == vb[0];
+}
+
+
+bool Type_handler_int_result::Item_const_eq(const Item_const *a,
+ const Item_const *b,
+ bool binary_cmp) const
+{
+ const longlong *va= a->const_ptr_longlong();
+ const longlong *vb= b->const_ptr_longlong();
+ bool res= va[0] == vb[0] &&
+ (va[0] >= 0 ||
+ (a->get_type_all_attributes_from_const()->unsigned_flag ==
+ b->get_type_all_attributes_from_const()->unsigned_flag));
+ return res;
+}
+
+
+bool Type_handler_string_result::Item_const_eq(const Item_const *a,
+ const Item_const *b,
+ bool binary_cmp) const
+{
+ const String *sa= a->const_ptr_string();
+ const String *sb= b->const_ptr_string();
+ return binary_cmp ? sa->bin_eq(sb) :
+ a->get_type_all_attributes_from_const()->collation.collation ==
+ b->get_type_all_attributes_from_const()->collation.collation &&
+ sa->eq(sb, a->get_type_all_attributes_from_const()->collation.collation);
+}
+
+
+bool
+Type_handler_decimal_result::Item_const_eq(const Item_const *a,
+ const Item_const *b,
+ bool binary_cmp) const
+{
+ const my_decimal *da= a->const_ptr_my_decimal();
+ const my_decimal *db= b->const_ptr_my_decimal();
+ return !da->cmp(db) &&
+ (!binary_cmp ||
+ a->get_type_all_attributes_from_const()->decimals ==
+ b->get_type_all_attributes_from_const()->decimals);
+}
+
+
+bool
+Type_handler_temporal_result::Item_const_eq(const Item_const *a,
+ const Item_const *b,
+ bool binary_cmp) const
+{
+ const MYSQL_TIME *ta= a->const_ptr_mysql_time();
+ const MYSQL_TIME *tb= b->const_ptr_mysql_time();
+ return !my_time_compare(ta, tb) &&
+ (!binary_cmp ||
+ a->get_type_all_attributes_from_const()->decimals ==
+ b->get_type_all_attributes_from_const()->decimals);
+}
+
+/***************************************************************************/
+
+const Type_handler *
+Type_handler_hex_hybrid::cast_to_int_type_handler() const
+{
+ return &type_handler_longlong;
+}
+
+
+const Type_handler *
+Type_handler_hex_hybrid::type_handler_for_system_time() const
+{
+ return &type_handler_longlong;
+}
+
+
+/***************************************************************************/
+
+bool Type_handler_row::Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
+ Item *a, Item *b) const
+{
+ DBUG_ASSERT(0);
+ return false;
+}
+
+
+bool Type_handler_int_result::Item_eq_value(THD *thd,
+ const Type_cmp_attributes *attr,
+ Item *a, Item *b) const
+{
+ longlong value0= a->val_int();
+ longlong value1= b->val_int();
+ return !a->null_value && !b->null_value && value0 == value1 &&
+ (value0 >= 0 || a->unsigned_flag == b->unsigned_flag);
+}
+
+
+bool Type_handler_real_result::Item_eq_value(THD *thd,
+ const Type_cmp_attributes *attr,
+ Item *a, Item *b) const
+{
+ double value0= a->val_real();
+ double value1= b->val_real();
+ return !a->null_value && !b->null_value && value0 == value1;
+}
+
+
+bool Type_handler_time_common::Item_eq_value(THD *thd,
+ const Type_cmp_attributes *attr,
+ Item *a, Item *b) const
+{
+ longlong value0= a->val_time_packed(thd);
+ longlong value1= b->val_time_packed(thd);
+ return !a->null_value && !b->null_value && value0 == value1;
+}
+
+
+bool Type_handler_temporal_with_date::Item_eq_value(THD *thd,
+ const Type_cmp_attributes *attr,
+ Item *a, Item *b) const
+{
+ longlong value0= a->val_datetime_packed(thd);
+ longlong value1= b->val_datetime_packed(thd);
+ return !a->null_value && !b->null_value && value0 == value1;
+}
+
+
+bool Type_handler_timestamp_common::Item_eq_value(THD *thd,
+ const Type_cmp_attributes *attr,
+ Item *a, Item *b) const
+{
+ Timestamp_or_zero_datetime_native_null na(thd, a, true);
+ Timestamp_or_zero_datetime_native_null nb(thd, b, true);
+ return !na.is_null() && !nb.is_null() && !cmp_native(na, nb);
+}
+
+
+bool Type_handler_string_result::Item_eq_value(THD *thd,
+ const Type_cmp_attributes *attr,
+ Item *a, Item *b) const
+{
+ String *va, *vb;
+ StringBuffer<128> cmp_value1, cmp_value2;
+ return (va= a->val_str(&cmp_value1)) &&
+ (vb= b->val_str(&cmp_value2)) &&
+ va->eq(vb, attr->compare_collation());
+}
+
+
+/***************************************************************************/
+
+bool Type_handler_string_result::union_element_finalize(const Item * item) const
+{
+ if (item->collation.derivation == DERIVATION_NONE)
+ {
+ my_error(ER_CANT_AGGREGATE_NCOLLATIONS, MYF(0), "UNION");
+ return true;
+ }
+ return false;
+}
+
+
+/***************************************************************************/
+
+void Type_handler_var_string::
+ Column_definition_implicit_upgrade(Column_definition *c) const
+{
+ // Change old VARCHAR to new VARCHAR
+ c->set_handler(&type_handler_varchar);
+}
+
+
+void Type_handler_time_common::
+ Column_definition_implicit_upgrade(Column_definition *c) const
+{
+ if (opt_mysql56_temporal_format)
+ c->set_handler(&type_handler_time2);
+ else
+ c->set_handler(&type_handler_time);
+}
+
+
+void Type_handler_datetime_common::
+ Column_definition_implicit_upgrade(Column_definition *c) const
+{
+ if (opt_mysql56_temporal_format)
+ c->set_handler(&type_handler_datetime2);
+ else
+ c->set_handler(&type_handler_datetime);
+}
+
+
+void Type_handler_timestamp_common::
+ Column_definition_implicit_upgrade(Column_definition *c) const
+{
+ if (opt_mysql56_temporal_format)
+ c->set_handler(&type_handler_timestamp2);
+ else
+ c->set_handler(&type_handler_timestamp);
+}
+
+
+/***************************************************************************/
+
+
+int Type_handler_temporal_with_date::stored_field_cmp_to_item(THD *thd,
+ Field *field,
+ Item *item) const
+{
+ MYSQL_TIME field_time, item_time, item_time2, *item_time_cmp= &item_time;
+ field->get_date(&field_time, Datetime::Options(TIME_INVALID_DATES, thd));
+ item->get_date(thd, &item_time, Datetime::Options(TIME_INVALID_DATES, thd));
+ if (item_time.time_type == MYSQL_TIMESTAMP_TIME &&
+ time_to_datetime(thd, &item_time, item_time_cmp= &item_time2))
+ return 1;
+ return my_time_compare(&field_time, item_time_cmp);
+}
+
+
+int Type_handler_time_common::stored_field_cmp_to_item(THD *thd,
+ Field *field,
+ Item *item) const
+{
+ MYSQL_TIME field_time, item_time;
+ field->get_date(&field_time, Time::Options(thd));
+ item->get_date(thd, &item_time, Time::Options(thd));
+ return my_time_compare(&field_time, &item_time);
+}
+
+
+int Type_handler_string_result::stored_field_cmp_to_item(THD *thd,
+ Field *field,
+ Item *item) const
+{
+ StringBuffer<MAX_FIELD_WIDTH> item_tmp;
+ StringBuffer<MAX_FIELD_WIDTH> field_tmp;
+ String *item_result= item->val_str(&item_tmp);
+ /*
+ Some implementations of Item::val_str(String*) actually modify
+ the field Item::null_value, hence we can't check it earlier.
+ */
+ if (item->null_value)
+ return 0;
+ String *field_result= field->val_str(&field_tmp);
+ return sortcmp(field_result, item_result, field->charset());
+}
+
+
+int Type_handler_int_result::stored_field_cmp_to_item(THD *thd,
+ Field *field,
+ Item *item) const
+{
+ DBUG_ASSERT(0); // Not used yet
+ return 0;
+}
+
+
+int Type_handler_real_result::stored_field_cmp_to_item(THD *thd,
+ Field *field,
+ Item *item) const
+{
+ /*
+ The patch for Bug#13463415 started using this function for comparing
+ BIGINTs. That uncovered a bug in Visual Studio 32bit optimized mode.
+ Prefixing the auto variables with volatile fixes the problem....
+ */
+ volatile double result= item->val_real();
+ if (item->null_value)
+ return 0;
+ volatile double field_result= field->val_real();
+ if (field_result < result)
+ return -1;
+ else if (field_result > result)
+ return 1;
+ return 0;
+}
+
+
+/***************************************************************************/
+
+
+static bool have_important_literal_warnings(const MYSQL_TIME_STATUS *status)
+{
+ return (status->warnings & ~MYSQL_TIME_NOTE_TRUNCATED) != 0;
+}
+
+
+static void literal_warn(THD *thd, const Item *item,
+ const char *str, size_t length, CHARSET_INFO *cs,
+ const MYSQL_TIME_STATUS *st,
+ const char *typestr, bool send_error)
+{
+ if (likely(item))
+ {
+ if (st->warnings) // e.g. a note on nanosecond truncation
+ {
+ ErrConvString err(str, length, cs);
+ thd->push_warning_wrong_or_truncated_value(
+ Sql_condition::time_warn_level(st->warnings),
+ false, typestr, err.ptr(),
+ nullptr, nullptr, nullptr);
+ }
+ }
+ else if (send_error)
+ {
+ ErrConvString err(str, length, cs);
+ my_error(ER_WRONG_VALUE, MYF(0), typestr, err.ptr());
+ }
+}
+
+
+Item_literal *
+Type_handler_date_common::create_literal_item(THD *thd,
+ const char *str,
+ size_t length,
+ CHARSET_INFO *cs,
+ bool send_error) const
+{
+ Temporal::Warn st;
+ Item_literal *item= NULL;
+ Temporal_hybrid tmp(thd, &st, str, length, cs, Temporal_hybrid::Options(thd));
+ if (tmp.is_valid_temporal() &&
+ tmp.get_mysql_time()->time_type == MYSQL_TIMESTAMP_DATE &&
+ !have_important_literal_warnings(&st))
+ {
+ Date d(&tmp);
+ item= new (thd->mem_root) Item_date_literal(thd, &d);
+ }
+ literal_warn(thd, item, str, length, cs, &st, "DATE", send_error);
+ return item;
+}
+
+
+Item_literal *
+Type_handler_temporal_with_date::create_literal_item(THD *thd,
+ const char *str,
+ size_t length,
+ CHARSET_INFO *cs,
+ bool send_error) const
+{
+ Temporal::Warn st;
+ Item_literal *item= NULL;
+ Temporal_hybrid tmp(thd, &st, str, length, cs, Temporal_hybrid::Options(thd));
+ if (tmp.is_valid_temporal() &&
+ tmp.get_mysql_time()->time_type == MYSQL_TIMESTAMP_DATETIME &&
+ !have_important_literal_warnings(&st))
+ {
+ Datetime dt(&tmp);
+ item= new (thd->mem_root) Item_datetime_literal(thd, &dt, st.precision);
+ }
+ literal_warn(thd, item, str, length, cs, &st, "DATETIME", send_error);
+ return item;
+}
+
+
+Item_literal *
+Type_handler_time_common::create_literal_item(THD *thd,
+ const char *str,
+ size_t length,
+ CHARSET_INFO *cs,
+ bool send_error) const
+{
+ MYSQL_TIME_STATUS st;
+ Item_literal *item= NULL;
+ Time::Options opt(TIME_TIME_ONLY, thd, Time::DATETIME_TO_TIME_DISALLOW);
+ Time tmp(thd, &st, str, length, cs, opt);
+ if (tmp.is_valid_time() &&
+ !have_important_literal_warnings(&st))
+ item= new (thd->mem_root) Item_time_literal(thd, &tmp, st.precision);
+ literal_warn(thd, item, str, length, cs, &st, "TIME", send_error);
+ return item;
+}
+
+
+bool
+Type_handler_time_common::Item_val_native_with_conversion(THD *thd,
+ Item *item,
+ Native *to) const
+{
+ if (item->type_handler()->type_handler_for_native_format() ==
+ &type_handler_time2)
+ return item->val_native(thd, to);
+ return Time(thd, item).to_native(to, item->time_precision(thd));
+}
+
+
+bool
+Type_handler_time_common::Item_val_native_with_conversion_result(THD *thd,
+ Item *item,
+ Native *to)
+ const
+{
+ if (item->type_handler()->type_handler_for_native_format() ==
+ &type_handler_time2)
+ return item->val_native_result(thd, to);
+ MYSQL_TIME ltime;
+ if (item->get_date_result(thd, &ltime, Time::Options(thd)))
+ return true;
+ int warn;
+ return Time(&warn, &ltime, 0).to_native(to, item->time_precision(thd));
+}
+
+
+int Type_handler_time_common::cmp_native(const Native &a,
+ const Native &b) const
+{
+ // Optimize a simple case: equal fractional precision:
+ if (a.length() == b.length())
+ return memcmp(a.ptr(), b.ptr(), a.length());
+ longlong lla= Time(a).to_packed();
+ longlong llb= Time(b).to_packed();
+ if (lla < llb)
+ return -1;
+ if (lla> llb)
+ return 1;
+ return 0;
+}
+
+
+bool Type_handler_timestamp_common::TIME_to_native(THD *thd,
+ const MYSQL_TIME *ltime,
+ Native *to,
+ uint decimals) const
+{
+ uint error_code;
+ Timestamp_or_zero_datetime tm(thd, ltime, &error_code);
+ if (error_code)
+ return true;
+ tm.trunc(decimals);
+ return tm.to_native(to, decimals);
+}
+
+
+bool
+Type_handler_timestamp_common::Item_val_native_with_conversion(THD *thd,
+ Item *item,
+ Native *to) const
+{
+ MYSQL_TIME ltime;
+ if (item->type_handler()->type_handler_for_native_format() ==
+ &type_handler_timestamp2)
+ return item->val_native(thd, to);
+ return
+ item->get_date(thd, &ltime, Datetime::Options(TIME_NO_ZERO_IN_DATE, thd)) ||
+ TIME_to_native(thd, &ltime, to, item->datetime_precision(thd));
+}
+
+
+bool
+Type_handler_timestamp_common::Item_val_native_with_conversion_result(THD *thd,
+ Item *item,
+ Native *to)
+ const
+{
+ MYSQL_TIME ltime;
+ if (item->type_handler()->type_handler_for_native_format() ==
+ &type_handler_timestamp2)
+ return item->val_native_result(thd, to);
+ return
+ item->get_date_result(thd, &ltime,
+ Datetime::Options(TIME_NO_ZERO_IN_DATE, thd)) ||
+ TIME_to_native(thd, &ltime, to, item->datetime_precision(thd));
+}
+
+
+int Type_handler_timestamp_common::cmp_native(const Native &a,
+ const Native &b) const
+{
+ /*
+ Optimize a simple case:
+ Either both timeatamp values have the same fractional precision,
+ or both values are zero datetime '0000-00-00 00:00:00.000000',
+ */
+ if (a.length() == b.length())
+ return memcmp(a.ptr(), b.ptr(), a.length());
+ return Timestamp_or_zero_datetime(a).cmp(Timestamp_or_zero_datetime(b));
+}
+
+
+Timestamp_or_zero_datetime_native_null::
+ Timestamp_or_zero_datetime_native_null(THD *thd, Item *item, bool conv)
+ :Null_flag(false)
+{
+ DBUG_ASSERT(item->type_handler()->type_handler_for_native_format() ==
+ &type_handler_timestamp2 || conv);
+ if (conv ?
+ type_handler_timestamp2.Item_val_native_with_conversion(thd, item, this) :
+ item->val_native(thd, this))
+ Null_flag::operator=(true);
+ // If no conversion, then is_null() should be equal to item->null_value
+ DBUG_ASSERT(is_null() == item->null_value || conv);
+ /*
+ is_null() can be true together with item->null_value==false, which means
+ a non-NULL item was evaluated, but then the conversion to TIMESTAMP failed.
+ But is_null() can never be false if item->null_value==true.
+ */
+ DBUG_ASSERT(is_null() >= item->null_value);
+}
+
+
+bool
+Type_handler::Item_param_val_native(THD *thd,
+ Item_param *item,
+ Native *to) const
+{
+ DBUG_ASSERT(0); // TODO-TYPE: MDEV-14271
+ return item->null_value= true;
+}
+
+
+bool
+Type_handler_timestamp_common::Item_param_val_native(THD *thd,
+ Item_param *item,
+ Native *to) const
+{
+ /*
+ The below code may not run well in corner cases.
+ This will be fixed under terms of MDEV-14271.
+ Item_param should:
+ - either remember @@time_zone at bind time
+ - or store TIMESTAMP in my_time_t format, rather than in MYSQL_TIME format.
+ */
+ MYSQL_TIME ltime;
+ return
+ item->get_date(thd, &ltime, Datetime::Options(TIME_NO_ZERO_IN_DATE, thd)) ||
+ TIME_to_native(thd, &ltime, to, item->datetime_precision(thd));
+}
+
+
+bool
+Type_handler_time_common::Item_param_val_native(THD *thd,
+ Item_param *item,
+ Native *to) const
+{
+ return Time(thd, item).to_native(to, item->decimals);
+}
+
+
LEX_CSTRING Charset::collation_specific_name() const
{
/*
diff --git a/sql/sql_type.h b/sql/sql_type.h
index b52b1fda678..7a514643bf6 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -1,7 +1,7 @@
#ifndef SQL_TYPE_H_INCLUDED
#define SQL_TYPE_H_INCLUDED
/*
- Copyright (c) 2015 MariaDB Foundation.
+ Copyright (c) 2015, 2020, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,12 +26,17 @@
#include "sql_const.h"
#include "sql_time.h"
#include "sql_type_real.h"
+#include "compat56.h"
class Field;
class Column_definition;
+class Column_definition_attributes;
class Item;
+class Item_const;
+class Item_literal;
class Item_param;
class Item_cache;
+class Item_copy;
class Item_func_or_sum;
class Item_sum_hybrid;
class Item_sum_sum;
@@ -71,10 +76,1276 @@ class Spvar_definition;
struct st_value;
class Protocol;
class handler;
-struct Schema_specification_st;
struct TABLE;
struct SORT_FIELD_ATTR;
class Vers_history_point;
+class Virtual_column_info;
+
+#define my_charset_numeric my_charset_latin1
+
+enum protocol_send_type_t
+{
+ PROTOCOL_SEND_STRING,
+ PROTOCOL_SEND_FLOAT,
+ PROTOCOL_SEND_DOUBLE,
+ PROTOCOL_SEND_TINY,
+ PROTOCOL_SEND_SHORT,
+ PROTOCOL_SEND_LONG,
+ PROTOCOL_SEND_LONGLONG,
+ PROTOCOL_SEND_DATETIME,
+ PROTOCOL_SEND_DATE,
+ PROTOCOL_SEND_TIME
+};
+
+
+enum scalar_comparison_op
+{
+ SCALAR_CMP_EQ,
+ SCALAR_CMP_EQUAL,
+ SCALAR_CMP_LT,
+ SCALAR_CMP_LE,
+ SCALAR_CMP_GE,
+ SCALAR_CMP_GT
+};
+
+
+/*
+ A helper class to store column attributes that are inherited
+ by columns (from the table level) when not specified explicitly.
+*/
+class Column_derived_attributes
+{
+ /*
+ Table level CHARACTER SET and COLLATE value:
+
+ CREATE TABLE t1 (a VARCHAR(1), b CHAR(2)) CHARACTER SET latin1;
+
+ All character string columns (CHAR, VARCHAR, TEXT)
+ inherit CHARACTER SET from the table level.
+ */
+ CHARSET_INFO *m_charset;
+public:
+ explicit Column_derived_attributes(CHARSET_INFO *cs)
+ :m_charset(cs)
+ { }
+ CHARSET_INFO *charset() const { return m_charset; }
+};
+
+
+/*
+ A helper class to store requests for changes
+ in multiple column data types during ALTER.
+*/
+class Column_bulk_alter_attributes
+{
+ /*
+ Target CHARACTER SET specification in ALTER .. CONVERT, e.g.
+
+ ALTER TABLE t1 CONVERT TO CHARACTER SET utf8;
+
+ All character string columns (CHAR, VARCHAR, TEXT)
+ get converted to the "CONVERT TO CHARACTER SET".
+ */
+ CHARSET_INFO *m_alter_table_convert_to_charset;
+public:
+ explicit Column_bulk_alter_attributes(CHARSET_INFO *convert)
+ :m_alter_table_convert_to_charset(convert)
+ { }
+ CHARSET_INFO *alter_table_convert_to_charset() const
+ { return m_alter_table_convert_to_charset; }
+};
+
+
+class Native: public Binary_string
+{
+public:
+ Native(char *str, size_t len)
+ :Binary_string(str, len)
+ { }
+};
+
+
+template<size_t buff_sz>
+class NativeBuffer: public Native
+{
+ char buff[buff_sz];
+public:
+ NativeBuffer() : Native(buff, buff_sz) { length(0); }
+};
+
+
+class String_ptr
+{
+protected:
+ String *m_string_ptr;
+public:
+ String_ptr(String *str)
+ :m_string_ptr(str)
+ { }
+ String_ptr(Item *item, String *buffer);
+ const String *string() const
+ {
+ DBUG_ASSERT(m_string_ptr);
+ return m_string_ptr;
+ }
+ bool is_null() const { return m_string_ptr == NULL; }
+};
+
+
+class Ascii_ptr: public String_ptr
+{
+public:
+ Ascii_ptr(Item *item, String *buffer);
+};
+
+
+template<size_t buff_sz>
+class String_ptr_and_buffer: public StringBuffer<buff_sz>,
+ public String_ptr
+{
+public:
+ String_ptr_and_buffer(Item *item)
+ :String_ptr(item, this)
+ { }
+};
+
+
+template<size_t buff_sz>
+class Ascii_ptr_and_buffer: public StringBuffer<buff_sz>,
+ public Ascii_ptr
+{
+public:
+ Ascii_ptr_and_buffer(Item *item)
+ :Ascii_ptr(item, this)
+ { }
+};
+
+
+class Dec_ptr
+{
+protected:
+ my_decimal *m_ptr;
+ Dec_ptr() { }
+public:
+ Dec_ptr(my_decimal *ptr) :m_ptr(ptr) { }
+ bool is_null() const { return m_ptr == NULL; }
+ const my_decimal *ptr() const { return m_ptr; }
+ const my_decimal *ptr_or(const my_decimal *def) const
+ {
+ return m_ptr ? m_ptr : def;
+ }
+ my_decimal *to_decimal(my_decimal *to) const
+ {
+ if (!m_ptr)
+ return NULL;
+ *to= *m_ptr;
+ return to;
+ }
+ double to_double() const { return m_ptr ? m_ptr->to_double() : 0.0; }
+ longlong to_longlong(bool unsigned_flag)
+ { return m_ptr ? m_ptr->to_longlong(unsigned_flag) : 0; }
+ bool to_bool() const { return m_ptr ? m_ptr->to_bool() : false; }
+ String *to_string(String *to) const
+ {
+ return m_ptr ? m_ptr->to_string(to) : NULL;
+ }
+ String *to_string(String *to, uint prec, uint dec, char filler)
+ {
+ return m_ptr ? m_ptr->to_string(to, prec, dec, filler) : NULL;
+ }
+ int to_binary(uchar *bin, int prec, int scale) const
+ {
+ return (m_ptr ? m_ptr : &decimal_zero)->to_binary(bin, prec, scale);
+ }
+ int cmp(const my_decimal *dec) const
+ {
+ DBUG_ASSERT(m_ptr);
+ DBUG_ASSERT(dec);
+ return m_ptr->cmp(dec);
+ }
+ int cmp(const Dec_ptr &other) const
+ {
+ return cmp(other.m_ptr);
+ }
+};
+
+
+// A helper class to handle results of val_decimal(), date_op(), etc.
+class Dec_ptr_and_buffer: public Dec_ptr
+{
+protected:
+ my_decimal m_buffer;
+public:
+ int round_to(my_decimal *to, int scale, decimal_round_mode mode)
+ {
+ DBUG_ASSERT(m_ptr);
+ return m_ptr->round_to(to, scale, mode);
+ }
+ int round_self(uint scale, decimal_round_mode mode)
+ {
+ return round_to(&m_buffer, scale, mode);
+ }
+ int round_self_if_needed(int scale, decimal_round_mode mode)
+ {
+ if (scale >= m_ptr->frac)
+ return E_DEC_OK;
+ int res= m_ptr->round_to(&m_buffer, scale, mode);
+ m_ptr= &m_buffer;
+ return res;
+ }
+ String *to_string_round(String *to, uint dec)
+ {
+ /*
+ decimal_round() allows from==to
+ So it's save even if m_ptr points to m_buffer before this call:
+ */
+ return m_ptr ? m_ptr->to_string_round(to, dec, &m_buffer) : NULL;
+ }
+};
+
+
+// A helper class to handle val_decimal() results.
+class VDec: public Dec_ptr_and_buffer
+{
+public:
+ VDec(): Dec_ptr_and_buffer() { }
+ VDec(Item *item);
+ void set(Item *a);
+};
+
+
+// A helper class to handler decimal_op() results.
+class VDec_op: public Dec_ptr_and_buffer
+{
+public:
+ VDec_op(Item_func_hybrid_field_type *item);
+};
+
+
+/*
+ Get and cache val_decimal() values for two items.
+ If the first value appears to be NULL, the second value is not evaluated.
+*/
+class VDec2_lazy
+{
+public:
+ VDec m_a;
+ VDec m_b;
+ VDec2_lazy(Item *a, Item *b) :m_a(a)
+ {
+ if (!m_a.is_null())
+ m_b.set(b);
+ }
+ bool has_null() const
+ {
+ return m_a.is_null() || m_b.is_null();
+ }
+};
+
+
+/**
+ Class Sec6 represents a fixed point value with 6 fractional digits.
+ Used e.g. to convert double and my_decimal values to TIME/DATETIME.
+*/
+
+class Sec6
+{
+protected:
+ ulonglong m_sec; // The integer part, between 0 and LONGLONG_MAX
+ ulong m_usec; // The fractional part, between 0 and 999999
+ bool m_neg; // false if positive, true of negative
+ bool m_truncated; // Indicates if the constructor truncated the value
+ void make_from_decimal(const my_decimal *d, ulong *nanoseconds);
+ void make_from_double(double d, ulong *nanoseconds);
+ void make_from_int(const Longlong_hybrid &nr)
+ {
+ m_neg= nr.neg();
+ m_sec= nr.abs();
+ m_usec= 0;
+ m_truncated= false;
+ }
+ void reset()
+ {
+ m_sec= m_usec= m_neg= m_truncated= 0;
+ }
+ Sec6() { }
+ bool add_nanoseconds(uint nanoseconds)
+ {
+ DBUG_ASSERT(nanoseconds <= 1000000000);
+ if (nanoseconds < 500)
+ return false;
+ m_usec+= (nanoseconds + 500) / 1000;
+ if (m_usec < 1000000)
+ return false;
+ m_usec%= 1000000;
+ return true;
+ }
+public:
+ explicit Sec6(double nr)
+ {
+ ulong nanoseconds;
+ make_from_double(nr, &nanoseconds);
+ }
+ explicit Sec6(const my_decimal *d)
+ {
+ ulong nanoseconds;
+ make_from_decimal(d, &nanoseconds);
+ }
+ explicit Sec6(const Longlong_hybrid &nr)
+ {
+ make_from_int(nr);
+ }
+ explicit Sec6(longlong nr, bool unsigned_val)
+ {
+ make_from_int(Longlong_hybrid(nr, unsigned_val));
+ }
+ bool neg() const { return m_neg; }
+ bool truncated() const { return m_truncated; }
+ ulonglong sec() const { return m_sec; }
+ long usec() const { return m_usec; }
+ /**
+ Converts Sec6 to MYSQL_TIME
+ @param thd current thd
+ @param [out] warn conversion warnings will be written here
+ @param [out] ltime converted value will be written here
+ @param fuzzydate conversion flags (TIME_INVALID_DATE, etc)
+ @returns false for success, true for a failure
+ */
+ bool convert_to_mysql_time(THD *thd,
+ int *warn,
+ MYSQL_TIME *ltime,
+ date_mode_t fuzzydate) const;
+
+protected:
+
+ bool to_interval_hhmmssff_only(MYSQL_TIME *to, int *warn) const
+ {
+ return number_to_time_only(m_neg, m_sec, m_usec,
+ TIME_MAX_INTERVAL_HOUR, to, warn);
+ }
+ bool to_datetime_or_to_interval_hhmmssff(MYSQL_TIME *to, int *warn) const
+ {
+ /*
+ Convert a number to a time interval.
+ The following formats are understood:
+ - 0 <= x <= 999999995959 - parse as hhhhmmss
+ - 999999995959 < x <= 99991231235959 - parse as YYYYMMDDhhmmss
+ (YYMMDDhhmmss) (YYYYMMDDhhmmss)
+
+ Note, these formats are NOT understood:
+ - YYMMDD - overlaps with INTERVAL range
+ - YYYYMMDD - overlaps with INTERVAL range
+ - YYMMDDhhmmss - overlaps with INTERVAL range, partially
+ (see TIME_MAX_INTERVAL_HOUR)
+
+ If we ever need wider intervals, this code switching between
+ full datetime and interval-only should be rewised.
+ */
+ DBUG_ASSERT(TIME_MAX_INTERVAL_HOUR <= 999999995959);
+ /* (YYMMDDhhmmss) */
+ if (m_sec > 999999995959ULL &&
+ m_sec <= 99991231235959ULL && m_neg == 0)
+ return to_datetime_or_date(to, warn, TIME_INVALID_DATES);
+ if (m_sec / 10000 > TIME_MAX_INTERVAL_HOUR)
+ {
+ *warn= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ return true;
+ }
+ return to_interval_hhmmssff_only(to, warn);
+ }
+public:
+ // [-][DD]hhhmmss.ff, YYMMDDhhmmss.ff, YYYYMMDDhhmmss.ff
+ bool to_datetime_or_time(MYSQL_TIME *to, int *warn,
+ date_conv_mode_t mode) const
+ {
+ bool rc= m_sec > 9999999 && m_sec <= 99991231235959ULL && !m_neg ?
+ ::number_to_datetime_or_date(m_sec, m_usec, to,
+ ulonglong(mode & TIME_MODE_FOR_XXX_TO_DATE), warn) < 0 :
+ ::number_to_time_only(m_neg, m_sec, m_usec, TIME_MAX_HOUR, to, warn);
+ DBUG_ASSERT(*warn || !rc);
+ return rc;
+ }
+ /*
+ Convert a number in formats YYYYMMDDhhmmss.ff or YYMMDDhhmmss.ff to
+ TIMESTAMP'YYYY-MM-DD hh:mm:ss.ff'
+ */
+ bool to_datetime_or_date(MYSQL_TIME *to, int *warn,
+ date_conv_mode_t flags) const
+ {
+ if (m_neg)
+ {
+ *warn= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ return true;
+ }
+ bool rc= number_to_datetime_or_date(m_sec, m_usec, to,
+ ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE),
+ warn) == -1;
+ DBUG_ASSERT(*warn || !rc);
+ return rc;
+ }
+ // Convert elapsed seconds to TIME
+ bool sec_to_time(MYSQL_TIME *ltime, uint dec) const
+ {
+ set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
+ ltime->neg= m_neg;
+ if (m_sec > TIME_MAX_VALUE_SECONDS)
+ {
+ // use check_time_range() to set ltime to the max value depending on dec
+ int unused;
+ ltime->hour= TIME_MAX_HOUR + 1;
+ check_time_range(ltime, dec, &unused);
+ return true;
+ }
+ DBUG_ASSERT(usec() <= TIME_MAX_SECOND_PART);
+ ltime->hour= (uint) (m_sec / 3600);
+ ltime->minute= (uint) (m_sec % 3600) / 60;
+ ltime->second= (uint) m_sec % 60;
+ ltime->second_part= m_usec;
+ return false;
+ }
+ Sec6 &trunc(uint dec)
+ {
+ m_usec-= my_time_fraction_remainder(m_usec, dec);
+ return *this;
+ }
+ size_t to_string(char *to, size_t nbytes) const
+ {
+ return m_usec ?
+ my_snprintf(to, nbytes, "%s%llu.%06lu",
+ m_neg ? "-" : "", m_sec, (uint) m_usec) :
+ my_snprintf(to, nbytes, "%s%llu", m_neg ? "-" : "", m_sec);
+ }
+ void make_truncated_warning(THD *thd, const char *type_str) const;
+};
+
+
+class Sec9: public Sec6
+{
+protected:
+ ulong m_nsec; // Nanoseconds 0..999
+ void make_from_int(const Longlong_hybrid &nr)
+ {
+ Sec6::make_from_int(nr);
+ m_nsec= 0;
+ }
+ Sec9() { }
+public:
+ Sec9(const my_decimal *d)
+ {
+ Sec6::make_from_decimal(d, &m_nsec);
+ }
+ Sec9(double d)
+ {
+ Sec6::make_from_double(d, &m_nsec);
+ }
+ ulong nsec() const { return m_nsec; }
+ Sec9 &trunc(uint dec)
+ {
+ m_nsec= 0;
+ Sec6::trunc(dec);
+ return *this;
+ }
+ Sec9 &round(uint dec);
+ Sec9 &round(uint dec, time_round_mode_t mode)
+ {
+ return mode == TIME_FRAC_TRUNCATE ? trunc(dec) : round(dec);
+ }
+};
+
+
+class VSec9: protected Sec9
+{
+ bool m_is_null;
+ Sec9& to_sec9()
+ {
+ DBUG_ASSERT(!is_null());
+ return *this;
+ }
+public:
+ VSec9(THD *thd, Item *item, const char *type_str, ulonglong limit);
+ bool is_null() const { return m_is_null; }
+ const Sec9& to_const_sec9() const
+ {
+ DBUG_ASSERT(!is_null());
+ return *this;
+ }
+ bool neg() const { return to_const_sec9().neg(); }
+ bool truncated() const { return to_const_sec9().truncated(); }
+ ulonglong sec() const { return to_const_sec9().sec(); }
+ long usec() const { return to_const_sec9().usec(); }
+ bool sec_to_time(MYSQL_TIME *ltime, uint dec) const
+ {
+ return to_const_sec9().sec_to_time(ltime, dec);
+ }
+ void make_truncated_warning(THD *thd, const char *type_str) const
+ {
+ return to_const_sec9().make_truncated_warning(thd, type_str);
+ }
+ Sec9 &round(uint dec)
+ {
+ return to_sec9().round(dec);
+ }
+ Sec9 &round(uint dec, time_round_mode_t mode)
+ {
+ return to_sec9().round(dec, mode);
+ }
+};
+
+
+/*
+ A heler class to perform additive operations between
+ two MYSQL_TIME structures and return the result as a
+ combination of seconds, microseconds and sign.
+*/
+class Sec6_add
+{
+ ulonglong m_sec; // number of seconds
+ ulong m_usec; // number of microseconds
+ bool m_neg; // false if positive, true if negative
+ bool m_error; // false if the value is OK, true otherwise
+ void to_hh24mmssff(MYSQL_TIME *ltime, timestamp_type tstype) const
+ {
+ bzero(ltime, sizeof(*ltime));
+ ltime->neg= m_neg;
+ calc_time_from_sec(ltime, (ulong) (m_sec % SECONDS_IN_24H), m_usec);
+ ltime->time_type= tstype;
+ }
+public:
+ /*
+ @param ltime1 - the first value to add (must be a valid DATE,TIME,DATETIME)
+ @param ltime2 - the second value to add (must be a valid TIME)
+ @param sign - the sign of the operation
+ (+1 for addition, -1 for subtraction)
+ */
+ Sec6_add(const MYSQL_TIME *ltime1, const MYSQL_TIME *ltime2, int sign)
+ {
+ DBUG_ASSERT(sign == -1 || sign == 1);
+ DBUG_ASSERT(!ltime1->neg || ltime1->time_type == MYSQL_TIMESTAMP_TIME);
+ if (!(m_error= (ltime2->time_type != MYSQL_TIMESTAMP_TIME)))
+ {
+ if (ltime1->neg != ltime2->neg)
+ sign= -sign;
+ m_neg= calc_time_diff(ltime1, ltime2, -sign, &m_sec, &m_usec);
+ if (ltime1->neg && (m_sec || m_usec))
+ m_neg= !m_neg; // Swap sign
+ }
+ }
+ bool to_time(THD *thd, MYSQL_TIME *ltime, uint decimals) const
+ {
+ if (m_error)
+ return true;
+ to_hh24mmssff(ltime, MYSQL_TIMESTAMP_TIME);
+ ltime->hour+= to_days_abs() * 24;
+ return adjust_time_range_with_warn(thd, ltime, decimals);
+ }
+ bool to_datetime(MYSQL_TIME *ltime) const
+ {
+ if (m_error || m_neg)
+ return true;
+ to_hh24mmssff(ltime, MYSQL_TIMESTAMP_DATETIME);
+ return get_date_from_daynr(to_days_abs(),
+ &ltime->year, &ltime->month, &ltime->day) ||
+ !ltime->day;
+ }
+ long to_days_abs() const { return (long) (m_sec / SECONDS_IN_24H); }
+};
+
+
+class Year
+{
+protected:
+ uint m_year;
+ bool m_truncated;
+ uint year_precision(const Item *item) const;
+public:
+ Year(): m_year(0), m_truncated(false) { }
+ Year(longlong value, bool unsigned_flag, uint length);
+ uint year() const { return m_year; }
+ uint to_YYYYMMDD() const { return m_year * 10000; }
+ bool truncated() const { return m_truncated; }
+};
+
+
+class Year_null: public Year, public Null_flag
+{
+public:
+ Year_null(const Longlong_null &nr, bool unsigned_flag, uint length)
+ :Year(nr.is_null() ? 0 : nr.value(), unsigned_flag, length),
+ Null_flag(nr.is_null())
+ { }
+};
+
+
+class VYear: public Year_null
+{
+public:
+ VYear(Item *item);
+};
+
+
+class VYear_op: public Year_null
+{
+public:
+ VYear_op(Item_func_hybrid_field_type *item);
+};
+
+
+class Double_null: public Null_flag
+{
+protected:
+ double m_value;
+public:
+ Double_null(double value, bool is_null)
+ :Null_flag(is_null), m_value(value)
+ { }
+ double value() const { return m_value; }
+};
+
+
+class Temporal: protected MYSQL_TIME
+{
+public:
+ class Status: public MYSQL_TIME_STATUS
+ {
+ public:
+ Status() { my_time_status_init(this); }
+ };
+
+ class Warn: public ErrBuff,
+ public Status
+ {
+ public:
+ void push_conversion_warnings(THD *thd, bool totally_useless_value,
+ date_mode_t mode, timestamp_type tstype,
+ const char *db_name, const char *table_name,
+ const char *name)
+ {
+ const char *typestr= tstype >= 0 ? type_name_by_timestamp_type(tstype) :
+ mode & (TIME_INTERVAL_hhmmssff | TIME_INTERVAL_DAY) ?
+ "interval" :
+ mode & TIME_TIME_ONLY ? "time" : "datetime";
+ Temporal::push_conversion_warnings(thd, totally_useless_value, warnings,
+ typestr, db_name, table_name, name,
+ ptr());
+ }
+ };
+
+ class Warn_push: public Warn
+ {
+ THD * const m_thd;
+ const char * const m_db_name;
+ const char * const m_table_name;
+ const char * const m_name;
+ const MYSQL_TIME * const m_ltime;
+ const date_mode_t m_mode;
+ public:
+ Warn_push(THD *thd, const char *db_name, const char *table_name,
+ const char *name, const MYSQL_TIME *ltime, date_mode_t mode)
+ : m_thd(thd), m_db_name(db_name), m_table_name(table_name), m_name(name),
+ m_ltime(ltime), m_mode(mode)
+ { }
+ ~Warn_push()
+ {
+ if (warnings)
+ push_conversion_warnings(m_thd, m_ltime->time_type < 0,
+ m_mode, m_ltime->time_type,
+ m_db_name, m_table_name, m_name);
+ }
+ };
+
+public:
+ static date_conv_mode_t sql_mode_for_dates(THD *thd);
+ static time_round_mode_t default_round_mode(THD *thd);
+ class Options: public date_mode_t
+ {
+ public:
+ explicit Options(date_mode_t flags)
+ :date_mode_t(flags)
+ { }
+ Options(date_conv_mode_t flags, time_round_mode_t round_mode)
+ :date_mode_t(flags | round_mode)
+ {
+ DBUG_ASSERT(ulonglong(flags) <= UINT_MAX32);
+ }
+ Options(date_conv_mode_t flags, THD *thd)
+ :Options(flags, default_round_mode(thd))
+ { }
+ };
+
+ bool is_valid_temporal() const
+ {
+ DBUG_ASSERT(time_type != MYSQL_TIMESTAMP_ERROR);
+ return time_type != MYSQL_TIMESTAMP_NONE;
+ }
+ static const char *type_name_by_timestamp_type(timestamp_type time_type)
+ {
+ switch (time_type) {
+ case MYSQL_TIMESTAMP_DATE: return "date";
+ case MYSQL_TIMESTAMP_TIME: return "time";
+ case MYSQL_TIMESTAMP_DATETIME: // FALLTHROUGH
+ default:
+ break;
+ }
+ return "datetime";
+ }
+ static void push_conversion_warnings(THD *thd, bool totally_useless_value, int warn,
+ const char *type_name,
+ const char *db_name,
+ const char *table_name,
+ const char *field_name,
+ const char *value);
+ /*
+ This method is used if the item was not null but convertion to
+ TIME/DATE/DATETIME failed. We return a zero date if allowed,
+ otherwise - null.
+ */
+ void make_fuzzy_date(int *warn, date_conv_mode_t fuzzydate)
+ {
+ /*
+ In the following scenario:
+ - The caller expected to get a TIME value
+ - Item returned a not NULL string or numeric value
+ - But then conversion from string or number to TIME failed
+ we need to change the default time_type from MYSQL_TIMESTAMP_DATE
+ (which was set in bzero) to MYSQL_TIMESTAMP_TIME and therefore
+ return TIME'00:00:00' rather than DATE'0000-00-00'.
+ If we don't do this, methods like Item::get_time_with_conversion()
+ will erroneously subtract CURRENT_DATE from '0000-00-00 00:00:00'
+ and return TIME'-838:59:59' instead of TIME'00:00:00' as a result.
+ */
+ timestamp_type tstype= !(fuzzydate & TIME_FUZZY_DATES) ?
+ MYSQL_TIMESTAMP_NONE :
+ fuzzydate & TIME_TIME_ONLY ?
+ MYSQL_TIMESTAMP_TIME :
+ MYSQL_TIMESTAMP_DATETIME;
+ set_zero_time(this, tstype);
+ }
+
+protected:
+ my_decimal *bad_to_decimal(my_decimal *to) const;
+ my_decimal *to_decimal(my_decimal *to) const;
+ static double to_double(bool negate, ulonglong num, ulong frac)
+ {
+ double d= (double) num + frac / (double) TIME_SECOND_PART_FACTOR;
+ return negate ? -d : d;
+ }
+ longlong to_packed() const { return ::pack_time(this); }
+ void make_from_out_of_range(int *warn)
+ {
+ *warn= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ time_type= MYSQL_TIMESTAMP_NONE;
+ }
+ void make_from_sec6(THD *thd, MYSQL_TIME_STATUS *st,
+ const Sec6 &nr, date_mode_t mode)
+ {
+ if (nr.convert_to_mysql_time(thd, &st->warnings, this, mode))
+ make_fuzzy_date(&st->warnings, date_conv_mode_t(mode));
+ }
+ void make_from_sec9(THD *thd, MYSQL_TIME_STATUS *st,
+ const Sec9 &nr, date_mode_t mode)
+ {
+ if (nr.convert_to_mysql_time(thd, &st->warnings, this, mode) ||
+ add_nanoseconds(thd, &st->warnings, mode, nr.nsec()))
+ make_fuzzy_date(&st->warnings, date_conv_mode_t(mode));
+ }
+ void make_from_str(THD *thd, Warn *warn,
+ const char *str, size_t length, CHARSET_INFO *cs,
+ date_mode_t fuzzydate);
+ void make_from_double(THD *thd, Warn *warn, double nr, date_mode_t mode)
+ {
+ make_from_sec9(thd, warn, Sec9(nr), mode);
+ if (warn->warnings)
+ warn->set_double(nr);
+ }
+ void make_from_longlong_hybrid(THD *thd, Warn *warn,
+ const Longlong_hybrid &nr, date_mode_t mode)
+ {
+ /*
+ Note: conversion from an integer to TIME can overflow to
+ '838:59:59.999999', so the conversion result can have fractional digits.
+ */
+ make_from_sec6(thd, warn, Sec6(nr), mode);
+ if (warn->warnings)
+ warn->set_longlong(nr);
+ }
+ void make_from_decimal(THD *thd, Warn *warn,
+ const my_decimal *nr, date_mode_t mode)
+ {
+ make_from_sec9(thd, warn, Sec9(nr), mode);
+ if (warn->warnings)
+ warn->set_decimal(nr);
+ }
+ bool ascii_to_temporal(MYSQL_TIME_STATUS *st,
+ const char *str, size_t length,
+ date_mode_t mode)
+ {
+ if (mode & (TIME_INTERVAL_hhmmssff | TIME_INTERVAL_DAY))
+ return ascii_to_datetime_or_date_or_interval_DDhhmmssff(st, str, length,
+ mode);
+ if (mode & TIME_TIME_ONLY)
+ return ascii_to_datetime_or_date_or_time(st, str, length, mode);
+ return ascii_to_datetime_or_date(st, str, length, mode);
+ }
+ bool ascii_to_datetime_or_date_or_interval_DDhhmmssff(MYSQL_TIME_STATUS *st,
+ const char *str,
+ size_t length,
+ date_mode_t mode)
+ {
+ longlong cflags= ulonglong(mode & TIME_MODE_FOR_XXX_TO_DATE);
+ bool rc= mode & TIME_INTERVAL_DAY ?
+ ::str_to_datetime_or_date_or_interval_day(str, length, this, cflags, st,
+ TIME_MAX_INTERVAL_HOUR,
+ TIME_MAX_INTERVAL_HOUR) :
+ ::str_to_datetime_or_date_or_interval_hhmmssff(str, length, this,
+ cflags, st,
+ TIME_MAX_INTERVAL_HOUR,
+ TIME_MAX_INTERVAL_HOUR);
+ DBUG_ASSERT(!rc || st->warnings);
+ return rc;
+ }
+ bool ascii_to_datetime_or_date_or_time(MYSQL_TIME_STATUS *status,
+ const char *str, size_t length,
+ date_mode_t fuzzydate)
+ {
+ ulonglong cflags= ulonglong(fuzzydate & TIME_MODE_FOR_XXX_TO_DATE);
+ bool rc= ::str_to_datetime_or_date_or_time(str, length, this,
+ cflags, status,
+ TIME_MAX_HOUR, UINT_MAX32);
+ DBUG_ASSERT(!rc || status->warnings);
+ return rc;
+ }
+ bool ascii_to_datetime_or_date(MYSQL_TIME_STATUS *status,
+ const char *str, size_t length,
+ date_mode_t fuzzydate)
+ {
+ DBUG_ASSERT(bool(fuzzydate & TIME_TIME_ONLY) == false);
+ bool rc= ::str_to_datetime_or_date(str, length, this,
+ ulonglong(fuzzydate & TIME_MODE_FOR_XXX_TO_DATE),
+ status);
+ DBUG_ASSERT(!rc || status->warnings);
+ return rc;
+ }
+ // Character set aware versions for string conversion routines
+ bool str_to_temporal(THD *thd, MYSQL_TIME_STATUS *st,
+ const char *str, size_t length,
+ CHARSET_INFO *cs, date_mode_t fuzzydate);
+ bool str_to_datetime_or_date_or_time(THD *thd, MYSQL_TIME_STATUS *st,
+ const char *str, size_t length,
+ CHARSET_INFO *cs, date_mode_t mode);
+ bool str_to_datetime_or_date(THD *thd, MYSQL_TIME_STATUS *st,
+ const char *str, size_t length,
+ CHARSET_INFO *cs, date_mode_t mode);
+
+ bool has_valid_mmssff() const
+ {
+ return minute <= TIME_MAX_MINUTE &&
+ second <= TIME_MAX_SECOND &&
+ second_part <= TIME_MAX_SECOND_PART;
+ }
+ bool has_zero_YYYYMM() const
+ {
+ return year == 0 && month == 0;
+ }
+ bool has_zero_YYYYMMDD() const
+ {
+ return year == 0 && month == 0 && day == 0;
+ }
+ bool check_date(date_conv_mode_t flags, int *warn) const
+ {
+ return ::check_date(this, flags, warn);
+ }
+ void time_hhmmssff_set_max(ulong max_hour)
+ {
+ hour= max_hour;
+ minute= TIME_MAX_MINUTE;
+ second= TIME_MAX_SECOND;
+ second_part= TIME_MAX_SECOND_PART;
+ }
+ /*
+ Add nanoseconds to ssff
+ retval true if seconds overflowed (the caller should increment minutes)
+ false if no overflow happened
+ */
+ bool add_nanoseconds_ssff(uint nanoseconds)
+ {
+ DBUG_ASSERT(nanoseconds <= 1000000000);
+ if (nanoseconds < 500)
+ return false;
+ second_part+= (nanoseconds + 500) / 1000;
+ if (second_part < 1000000)
+ return false;
+ second_part%= 1000000;
+ if (second < 59)
+ {
+ second++;
+ return false;
+ }
+ second= 0;
+ return true;
+ }
+ /*
+ Add nanoseconds to mmssff
+ retval true if hours overflowed (the caller should increment hours)
+ false if no overflow happened
+ */
+ bool add_nanoseconds_mmssff(uint nanoseconds)
+ {
+ if (!add_nanoseconds_ssff(nanoseconds))
+ return false;
+ if (minute < 59)
+ {
+ minute++;
+ return false;
+ }
+ minute= 0;
+ return true;
+ }
+ void time_round_or_set_max(uint dec, int *warn, ulong max_hour, ulong nsec);
+ bool datetime_add_nanoseconds_or_invalidate(THD *thd, int *warn, ulong nsec);
+ bool datetime_round_or_invalidate(THD *thd, uint dec, int *warn, ulong nsec);
+ bool add_nanoseconds_with_round(THD *thd, int *warn,
+ date_conv_mode_t mode, ulong nsec);
+ bool add_nanoseconds(THD *thd, int *warn, date_mode_t mode, ulong nsec)
+ {
+ date_conv_mode_t cmode= date_conv_mode_t(mode);
+ return time_round_mode_t(mode) == TIME_FRAC_ROUND ?
+ add_nanoseconds_with_round(thd, warn, cmode, nsec) : false;
+ }
+public:
+ static void *operator new(size_t size, MYSQL_TIME *ltime) throw()
+ {
+ DBUG_ASSERT(size == sizeof(MYSQL_TIME));
+ return ltime;
+ }
+ static void operator delete(void *ptr, MYSQL_TIME *ltime) { }
+
+ long fraction_remainder(uint dec) const
+ {
+ return my_time_fraction_remainder(second_part, dec);
+ }
+};
+
+
+/*
+ Use this class when you need to get a MYSQL_TIME from an Item
+ using Item's native timestamp type, without automatic timestamp
+ type conversion.
+*/
+class Temporal_hybrid: public Temporal
+{
+public:
+ class Options: public Temporal::Options
+ {
+ public:
+ Options(THD *thd)
+ :Temporal::Options(sql_mode_for_dates(thd), default_round_mode(thd))
+ { }
+ Options(date_conv_mode_t flags, time_round_mode_t round_mode)
+ :Temporal::Options(flags, round_mode)
+ { }
+ explicit Options(const Temporal::Options &opt)
+ :Temporal::Options(opt)
+ { }
+ explicit Options(date_mode_t fuzzydate)
+ :Temporal::Options(fuzzydate)
+ { }
+ };
+
+public:
+ // Contructors for Item
+ Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate);
+ Temporal_hybrid(THD *thd, Item *item)
+ :Temporal_hybrid(thd, item, Options(thd))
+ { }
+ Temporal_hybrid(Item *item)
+ :Temporal_hybrid(current_thd, item)
+ { }
+
+ // Constructors for non-NULL values
+ Temporal_hybrid(THD *thd, Warn *warn,
+ const char *str, size_t length, CHARSET_INFO *cs,
+ date_mode_t fuzzydate)
+ {
+ make_from_str(thd, warn, str, length, cs, fuzzydate);
+ }
+ Temporal_hybrid(THD *thd, Warn *warn,
+ const Longlong_hybrid &nr, date_mode_t fuzzydate)
+ {
+ make_from_longlong_hybrid(thd, warn, nr, fuzzydate);
+ }
+ Temporal_hybrid(THD *thd, Warn *warn, double nr, date_mode_t fuzzydate)
+ {
+ make_from_double(thd, warn, nr, fuzzydate);
+ }
+
+ // Constructors for nullable values
+ Temporal_hybrid(THD *thd, Warn *warn, const String *str, date_mode_t mode)
+ {
+ if (!str)
+ time_type= MYSQL_TIMESTAMP_NONE;
+ else
+ make_from_str(thd, warn, str->ptr(), str->length(), str->charset(), mode);
+ }
+ Temporal_hybrid(THD *thd, Warn *warn,
+ const Longlong_hybrid_null &nr, date_mode_t fuzzydate)
+ {
+ if (nr.is_null())
+ time_type= MYSQL_TIMESTAMP_NONE;
+ else
+ make_from_longlong_hybrid(thd, warn, nr, fuzzydate);
+ }
+ Temporal_hybrid(THD *thd, Warn *warn, const Double_null &nr, date_mode_t mode)
+ {
+ if (nr.is_null())
+ time_type= MYSQL_TIMESTAMP_NONE;
+ else
+ make_from_double(thd, warn, nr.value(), mode);
+ }
+ Temporal_hybrid(THD *thd, Warn *warn, const my_decimal *nr, date_mode_t mode)
+ {
+ if (!nr)
+ time_type= MYSQL_TIMESTAMP_NONE;
+ else
+ make_from_decimal(thd, warn, nr, mode);
+ }
+ // End of constuctors
+
+ bool copy_valid_value_to_mysql_time(MYSQL_TIME *ltime) const
+ {
+ DBUG_ASSERT(is_valid_temporal());
+ *ltime= *this;
+ return false;
+ }
+
+ longlong to_longlong() const
+ {
+ if (!is_valid_temporal())
+ return 0;
+ ulonglong v= TIME_to_ulonglong(this);
+ return neg ? -(longlong) v : (longlong) v;
+ }
+ double to_double() const
+ {
+ return is_valid_temporal() ? TIME_to_double(this) : 0;
+ }
+ my_decimal *to_decimal(my_decimal *to)
+ {
+ return is_valid_temporal() ? Temporal::to_decimal(to) : bad_to_decimal(to);
+ }
+ String *to_string(String *str, uint dec) const
+ {
+ if (!is_valid_temporal())
+ return NULL;
+ str->set_charset(&my_charset_numeric);
+ if (!str->alloc(MAX_DATE_STRING_REP_LENGTH))
+ str->length(my_TIME_to_str(this, const_cast<char*>(str->ptr()), dec));
+ return str;
+ }
+ const MYSQL_TIME *get_mysql_time() const
+ {
+ DBUG_ASSERT(is_valid_temporal());
+ return this;
+ }
+};
+
+
+/*
+ This class resembles the SQL standard <extract source>,
+ used in extract expressions, e.g: EXTRACT(DAY FROM dt)
+ <extract expression> ::=
+ EXTRACT <left paren> <extract field> FROM <extract source> <right paren>
+ <extract source> ::= <datetime value expression> | <interval value expression>
+*/
+class Extract_source: public Temporal_hybrid
+{
+ /*
+ Convert a TIME value to DAY-TIME interval, e.g. for extraction:
+ EXTRACT(DAY FROM x), EXTRACT(HOUR FROM x), etc.
+ Moves full days from ltime->hour to ltime->day.
+ */
+ void time_to_daytime_interval()
+ {
+ DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_TIME);
+ DBUG_ASSERT(has_zero_YYYYMMDD());
+ MYSQL_TIME::day= MYSQL_TIME::hour / 24;
+ MYSQL_TIME::hour%= 24;
+ }
+ bool is_valid_extract_source_slow() const
+ {
+ return is_valid_temporal() && MYSQL_TIME::hour < 24 &&
+ (has_zero_YYYYMM() || time_type != MYSQL_TIMESTAMP_TIME);
+ }
+ bool is_valid_value_slow() const
+ {
+ return time_type == MYSQL_TIMESTAMP_NONE || is_valid_extract_source_slow();
+ }
+public:
+ Extract_source(THD *thd, Item *item, date_mode_t mode)
+ :Temporal_hybrid(thd, item, mode)
+ {
+ if (MYSQL_TIME::time_type == MYSQL_TIMESTAMP_TIME)
+ time_to_daytime_interval();
+ DBUG_ASSERT(is_valid_value_slow());
+ }
+ inline const MYSQL_TIME *get_mysql_time() const
+ {
+ DBUG_ASSERT(is_valid_extract_source_slow());
+ return this;
+ }
+ bool is_valid_extract_source() const { return is_valid_temporal(); }
+ int sign() const { return get_mysql_time()->neg ? -1 : 1; }
+ uint year() const { return get_mysql_time()->year; }
+ uint month() const { return get_mysql_time()->month; }
+ int day() const { return (int) get_mysql_time()->day * sign(); }
+ int hour() const { return (int) get_mysql_time()->hour * sign(); }
+ int minute() const { return (int) get_mysql_time()->minute * sign(); }
+ int second() const { return (int) get_mysql_time()->second * sign(); }
+ int microsecond() const { return (int) get_mysql_time()->second_part * sign(); }
+
+ uint year_month() const { return year() * 100 + month(); }
+ uint quarter() const { return (month() + 2)/3; }
+ uint week(THD *thd) const;
+
+ longlong second_microsecond() const
+ {
+ return (second() * 1000000LL + microsecond());
+ }
+
+ // DAY TO XXX
+ longlong day_hour() const
+ {
+ return (longlong) day() * 100LL + hour();
+ }
+ longlong day_minute() const
+ {
+ return day_hour() * 100LL + minute();
+ }
+ longlong day_second() const
+ {
+ return day_minute() * 100LL + second();
+ }
+ longlong day_microsecond() const
+ {
+ return day_second() * 1000000LL + microsecond();
+ }
+
+ // HOUR TO XXX
+ int hour_minute() const
+ {
+ return hour() * 100 + minute();
+ }
+ int hour_second() const
+ {
+ return hour_minute() * 100 + second();
+ }
+ longlong hour_microsecond() const
+ {
+ return hour_second() * 1000000LL + microsecond();
+ }
+
+ // MINUTE TO XXX
+ int minute_second() const
+ {
+ return minute() * 100 + second();
+ }
+ longlong minute_microsecond() const
+ {
+ return minute_second() * 1000000LL + microsecond();
+ }
+};
+
+
+/*
+ This class is used for the "time_interval" argument of these SQL functions:
+ TIMESTAMP(tm,time_interval)
+ ADDTIME(tm,time_interval)
+ Features:
+ - DATE and DATETIME formats are treated as errors
+ - Preserves hours for TIME format as is, without limiting to TIME_MAX_HOUR
+*/
+class Interval_DDhhmmssff: public Temporal
+{
+ static const LEX_CSTRING m_type_name;
+ bool str_to_DDhhmmssff(MYSQL_TIME_STATUS *status,
+ const char *str, size_t length, CHARSET_INFO *cs,
+ ulong max_hour);
+ void push_warning_wrong_or_truncated_value(THD *thd,
+ const ErrConv &str,
+ int warnings);
+ bool is_valid_interval_DDhhmmssff_slow() const
+ {
+ return time_type == MYSQL_TIMESTAMP_TIME &&
+ has_zero_YYYYMMDD() && has_valid_mmssff();
+ }
+ bool is_valid_value_slow() const
+ {
+ return time_type == MYSQL_TIMESTAMP_NONE ||
+ is_valid_interval_DDhhmmssff_slow();
+ }
+public:
+ // Get fractional second precision from an Item
+ static uint fsp(THD *thd, Item *item);
+ /*
+ Maximum useful HOUR value:
+ TIMESTAMP'0001-01-01 00:00:00' + '87649415:59:59' = '9999-12-31 23:59:59'
+ This gives maximum possible interval values:
+ - '87649415:59:59.999999' (in 'hh:mm:ss.ff' format)
+ - '3652058 23:59:59.999999' (in 'DD hh:mm:ss.ff' format)
+ */
+ static uint max_useful_hour()
+ {
+ return TIME_MAX_INTERVAL_HOUR;
+ }
+ static uint max_int_part_char_length()
+ {
+ // e.g. '+3652058 23:59:59'
+ return 1/*sign*/ + TIME_MAX_INTERVAL_DAY_CHAR_LENGTH + 1 + 8/*hh:mm:ss*/;
+ }
+ static uint max_char_length(uint fsp)
+ {
+ DBUG_ASSERT(fsp <= TIME_SECOND_PART_DIGITS);
+ return max_int_part_char_length() + (fsp ? 1 : 0) + fsp;
+ }
+
+public:
+ Interval_DDhhmmssff(THD *thd, Status *st, bool push_warnings,
+ Item *item, ulong max_hour,
+ time_round_mode_t mode, uint dec);
+ Interval_DDhhmmssff(THD *thd, Item *item, uint dec)
+ {
+ Status st;
+ new(this) Interval_DDhhmmssff(thd, &st, true, item, max_useful_hour(),
+ default_round_mode(thd), dec);
+ }
+ Interval_DDhhmmssff(THD *thd, Item *item)
+ :Interval_DDhhmmssff(thd, item, TIME_SECOND_PART_DIGITS)
+ { }
+ const MYSQL_TIME *get_mysql_time() const
+ {
+ DBUG_ASSERT(is_valid_interval_DDhhmmssff_slow());
+ return this;
+ }
+ bool is_valid_interval_DDhhmmssff() const
+ {
+ return time_type == MYSQL_TIMESTAMP_TIME;
+ }
+ bool is_valid_value() const
+ {
+ return time_type == MYSQL_TIMESTAMP_NONE || is_valid_interval_DDhhmmssff();
+ }
+ String *to_string(String *str, uint dec) const
+ {
+ if (!is_valid_interval_DDhhmmssff())
+ return NULL;
+ str->set_charset(&my_charset_numeric);
+ if (!str->alloc(MAX_DATE_STRING_REP_LENGTH))
+ str->length(my_interval_DDhhmmssff_to_str(this,
+ const_cast<char*>(str->ptr()),
+ dec));
+ return str;
+ }
+};
+
class Schema;
@@ -96,35 +1367,47 @@ class Schema;
Time derives from MYSQL_TIME privately to make sure it is accessed
externally only in the valid state.
*/
-class Time: private MYSQL_TIME
+class Time: public Temporal
{
+ static uint binary_length_to_precision(uint length);
public:
enum datetime_to_time_mode_t
{
+ DATETIME_TO_TIME_DISALLOW,
DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS,
- DATETIME_TO_TIME_YYYYMMDD_TRUNCATE
+ DATETIME_TO_TIME_YYYYMMDD_TRUNCATE,
+ DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY,
+ DATETIME_TO_TIME_MINUS_CURRENT_DATE
};
- class Options
+ class Options: public Temporal::Options
{
- sql_mode_t m_get_date_flags;
datetime_to_time_mode_t m_datetime_to_time_mode;
public:
- Options()
- :m_get_date_flags(flags_for_get_date()),
- m_datetime_to_time_mode(DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
+ Options(THD *thd)
+ :Temporal::Options(default_flags_for_get_date(), default_round_mode(thd)),
+ m_datetime_to_time_mode(default_datetime_to_time_mode())
{ }
- Options(sql_mode_t flags)
- :m_get_date_flags(flags),
- m_datetime_to_time_mode(DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
+ Options(date_conv_mode_t flags, THD *thd)
+ :Temporal::Options(flags, default_round_mode(thd)),
+ m_datetime_to_time_mode(default_datetime_to_time_mode())
{ }
- Options(sql_mode_t flags, datetime_to_time_mode_t dtmode)
- :m_get_date_flags(flags),
+ Options(date_conv_mode_t flags, THD *thd, datetime_to_time_mode_t dtmode)
+ :Temporal::Options(flags, default_round_mode(thd)),
m_datetime_to_time_mode(dtmode)
{ }
- sql_mode_t get_date_flags() const
- { return m_get_date_flags; }
+ Options(date_conv_mode_t fuzzydate, time_round_mode_t round_mode,
+ datetime_to_time_mode_t datetime_to_time_mode)
+ :Temporal::Options(fuzzydate, round_mode),
+ m_datetime_to_time_mode(datetime_to_time_mode)
+ { }
+
datetime_to_time_mode_t datetime_to_time_mode() const
{ return m_datetime_to_time_mode; }
+
+ static datetime_to_time_mode_t default_datetime_to_time_mode()
+ {
+ return DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS;
+ }
};
/*
CAST(AS TIME) historically does not mix days to hours.
@@ -134,8 +1417,34 @@ public:
class Options_for_cast: public Options
{
public:
- Options_for_cast()
- :Options(flags_for_get_date(), DATETIME_TO_TIME_YYYYMMDD_TRUNCATE)
+ Options_for_cast(THD *thd)
+ :Options(default_flags_for_get_date(), default_round_mode(thd),
+ DATETIME_TO_TIME_YYYYMMDD_TRUNCATE)
+ { }
+ Options_for_cast(date_mode_t mode, THD *thd)
+ :Options(default_flags_for_get_date() | (mode & TIME_FUZZY_DATES),
+ default_round_mode(thd),
+ DATETIME_TO_TIME_YYYYMMDD_TRUNCATE)
+ { }
+ };
+
+ class Options_for_round: public Options
+ {
+ public:
+ Options_for_round(time_round_mode_t round_mode= TIME_FRAC_TRUNCATE)
+ :Options(Time::default_flags_for_get_date(), round_mode,
+ Time::DATETIME_TO_TIME_DISALLOW)
+ { }
+ };
+ class Options_cmp: public Options
+ {
+ public:
+ Options_cmp(THD *thd)
+ :Options(comparison_flags_for_get_date(), thd)
+ { }
+ Options_cmp(THD *thd, datetime_to_time_mode_t dtmode)
+ :Options(comparison_flags_for_get_date(),
+ default_round_mode(thd), dtmode)
{ }
};
private:
@@ -146,46 +1455,79 @@ private:
bool is_valid_time_slow() const
{
return time_type == MYSQL_TIMESTAMP_TIME &&
- year == 0 && month == 0 && day == 0 &&
- minute <= TIME_MAX_MINUTE &&
- second <= TIME_MAX_SECOND &&
- second_part <= TIME_MAX_SECOND_PART;
+ has_zero_YYYYMMDD() && has_valid_mmssff();
+ }
+ void hhmmssff_copy(const MYSQL_TIME *from)
+ {
+ hour= from->hour;
+ minute= from->minute;
+ second= from->second;
+ second_part= from->second_part;
+ }
+ void datetime_to_time_YYYYMMDD_000000DD_mix_to_hours(int *warn,
+ uint from_year,
+ uint from_month,
+ uint from_day)
+ {
+ if (from_year != 0 || from_month != 0)
+ *warn|= MYSQL_TIME_NOTE_TRUNCATED;
+ else
+ hour+= from_day * 24;
+ }
+ /*
+ The result is calculated effectively similar to:
+ TIMEDIFF(dt, CAST(CURRENT_DATE AS DATETIME))
+ If the difference does not fit to the supported TIME range, it's truncated.
+ */
+ void datetime_to_time_minus_current_date(THD *thd)
+ {
+ MYSQL_TIME current_date, tmp;
+ set_current_date(thd, &current_date);
+ calc_time_diff(this, &current_date, 1, &tmp, date_mode_t(0));
+ static_cast<MYSQL_TIME*>(this)[0]= tmp;
+ int warnings= 0;
+ (void) check_time_range(this, TIME_SECOND_PART_DIGITS, &warnings);
+ DBUG_ASSERT(is_valid_time());
}
-
/*
Convert a valid DATE or DATETIME to TIME.
Before this call, "this" must be a valid DATE or DATETIME value,
- e.g. returned from Item::get_date().
+ e.g. returned from Item::get_date(), str_to_xxx(), number_to_xxx().
After this call, "this" is a valid TIME value.
*/
- void valid_datetime_to_valid_time(const Options opt)
+ void valid_datetime_to_valid_time(THD *thd, int *warn, const Options opt)
{
DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATE ||
time_type == MYSQL_TIMESTAMP_DATETIME);
/*
- Make sure that day and hour are valid, so the result hour value
+ We're dealing with a DATE or DATETIME returned from
+ str_to_xxx(), number_to_xxx() or unpack_time().
+ Do some asserts to make sure the result hour value
after mixing days to hours does not go out of the valid TIME range.
+ The maximum hour value after mixing days will be 31*24+23=767,
+ which is within the supported TIME range.
+ Thus no adjust_time_range_or_invalidate() is needed here.
*/
DBUG_ASSERT(day < 32);
DBUG_ASSERT(hour < 24);
- if (year == 0 && month == 0 &&
- opt.datetime_to_time_mode() ==
- DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
+ if (opt.datetime_to_time_mode() == DATETIME_TO_TIME_MINUS_CURRENT_DATE)
{
- /*
- The maximum hour value after mixing days will be 31*24+23=767,
- which is within the supported TIME range.
- Thus no adjust_time_range_or_invalidate() is needed here.
- */
- hour+= day * 24;
+ datetime_to_time_minus_current_date(thd);
+ }
+ else
+ {
+ if (opt.datetime_to_time_mode() ==
+ DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
+ datetime_to_time_YYYYMMDD_000000DD_mix_to_hours(warn, year, month, day);
+ year= month= day= 0;
+ time_type= MYSQL_TIMESTAMP_TIME;
}
- year= month= day= 0;
- time_type= MYSQL_TIMESTAMP_TIME;
DBUG_ASSERT(is_valid_time_slow());
}
/**
Convert valid DATE/DATETIME to valid TIME if needed.
This method is called after Item::get_date(),
+ str_to_xxx(), number_to_xxx().
which can return only valid TIME/DATE/DATETIME values.
Before this call, "this" is:
- either a valid TIME/DATE/DATETIME value
@@ -195,12 +1537,19 @@ private:
- either a valid TIME (within the supported TIME range),
- or MYSQL_TIMESTAMP_NONE
*/
- void valid_MYSQL_TIME_to_valid_value(const Options opt)
+ void valid_MYSQL_TIME_to_valid_value(THD *thd, int *warn, const Options opt)
{
switch (time_type) {
case MYSQL_TIMESTAMP_DATE:
case MYSQL_TIMESTAMP_DATETIME:
- valid_datetime_to_valid_time(opt);
+ if (opt.datetime_to_time_mode() ==
+ DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY &&
+ (year || month || day))
+ make_from_out_of_range(warn);
+ else if (opt.datetime_to_time_mode() == DATETIME_TO_TIME_DISALLOW)
+ make_from_out_of_range(warn);
+ else
+ valid_datetime_to_valid_time(thd, warn, opt);
break;
case MYSQL_TIMESTAMP_NONE:
break;
@@ -212,14 +1561,155 @@ private:
break;
}
}
- void make_from_item(class Item *item, const Options opt);
+
+ /*
+ This method is called after number_to_xxx() and str_to_xxx(),
+ which can return DATE or DATETIME values. Convert to TIME if needed.
+ We trust that xxx_to_time() returns a valid TIME/DATE/DATETIME value,
+ so here we need to do only simple validation.
+ */
+ void xxx_to_time_result_to_valid_value(THD *thd, int *warn, const Options opt)
+ {
+ // str_to_xxx(), number_to_xxx() never return MYSQL_TIMESTAMP_ERROR
+ DBUG_ASSERT(time_type != MYSQL_TIMESTAMP_ERROR);
+ valid_MYSQL_TIME_to_valid_value(thd, warn, opt);
+ }
+ void adjust_time_range_or_invalidate(int *warn)
+ {
+ if (check_time_range(this, TIME_SECOND_PART_DIGITS, warn))
+ time_type= MYSQL_TIMESTAMP_NONE;
+ DBUG_ASSERT(is_valid_value_slow());
+ }
public:
+ void round_or_set_max(uint dec, int *warn, ulong nsec);
+private:
+ void round_or_set_max(uint dec, int *warn);
+
+ /*
+ All make_from_xxx() methods initialize *warn.
+ The old value gets lost.
+ */
+ void make_from_datetime_move_day_to_hour(int *warn, const MYSQL_TIME *from);
+ void make_from_datetime_with_days_diff(int *warn, const MYSQL_TIME *from,
+ long curdays);
+ void make_from_time(int *warn, const MYSQL_TIME *from);
+ void make_from_datetime(int *warn, const MYSQL_TIME *from, long curdays);
+ void make_from_item(THD *thd, int *warn, Item *item, const Options opt);
+public:
+ /*
+ All constructors that accept an "int *warn" parameter initialize *warn.
+ The old value gets lost.
+ */
+ Time(int *warn, bool neg, ulonglong hour, uint minute, const Sec6 &second);
Time() { time_type= MYSQL_TIMESTAMP_NONE; }
- Time(Item *item) { make_from_item(item, Options()); }
- Time(Item *item, const Options opt) { make_from_item(item, opt); }
- static sql_mode_t flags_for_get_date()
+ Time(const Native &native);
+ Time(THD *thd, const MYSQL_TIME *ltime, const Options opt)
+ {
+ *(static_cast<MYSQL_TIME*>(this))= *ltime;
+ DBUG_ASSERT(is_valid_temporal());
+ int warn= 0;
+ valid_MYSQL_TIME_to_valid_value(thd, &warn, opt);
+ }
+ Time(Item *item)
+ :Time(current_thd, item)
+ { }
+ Time(THD *thd, Item *item, const Options opt)
+ {
+ int warn;
+ make_from_item(thd, &warn, item, opt);
+ }
+ Time(THD *thd, Item *item)
+ :Time(thd, item, Options(thd))
+ { }
+ Time(int *warn, const MYSQL_TIME *from, long curdays);
+ Time(THD *thd, MYSQL_TIME_STATUS *status,
+ const char *str, size_t len, CHARSET_INFO *cs,
+ const Options opt)
+ {
+ if (str_to_datetime_or_date_or_time(thd, status, str, len, cs, opt))
+ time_type= MYSQL_TIMESTAMP_NONE;
+ // The below call will optionally add notes to already collected warnings:
+ else
+ xxx_to_time_result_to_valid_value(thd, &status->warnings, opt);
+ }
+
+protected:
+ Time(THD *thd, int *warn, const Sec6 &nr, const Options opt)
+ {
+ if (nr.to_datetime_or_time(this, warn, TIME_INVALID_DATES))
+ time_type= MYSQL_TIMESTAMP_NONE;
+ xxx_to_time_result_to_valid_value(thd, warn, opt);
+ }
+ Time(THD *thd, int *warn, const Sec9 &nr, const Options &opt)
+ :Time(thd, warn, static_cast<Sec6>(nr), opt)
+ {
+ if (is_valid_time() && time_round_mode_t(opt) == TIME_FRAC_ROUND)
+ round_or_set_max(6, warn, nr.nsec());
+ }
+
+public:
+ Time(THD *thd, int *warn, const Longlong_hybrid &nr, const Options &opt)
+ :Time(thd, warn, Sec6(nr), opt)
+ { }
+ Time(THD *thd, int *warn, double nr, const Options &opt)
+ :Time(thd, warn, Sec9(nr), opt)
+ { }
+ Time(THD *thd, int *warn, const my_decimal *d, const Options &opt)
+ :Time(thd, warn, Sec9(d), opt)
+ { }
+
+ Time(THD *thd, Item *item, const Options opt, uint dec)
+ :Time(thd, item, opt)
+ {
+ round(dec, time_round_mode_t(opt));
+ }
+ Time(int *warn, const MYSQL_TIME *from, long curdays,
+ const Time::Options &opt, uint dec)
+ :Time(warn, from, curdays)
+ {
+ round(dec, time_round_mode_t(opt), warn);
+ }
+ Time(int *warn, bool neg, ulonglong hour, uint minute, const Sec9 &second,
+ time_round_mode_t mode, uint dec)
+ :Time(warn, neg, hour, minute, second)
+ {
+ DBUG_ASSERT(is_valid_time());
+ if ((ulonglong) mode == (ulonglong) TIME_FRAC_ROUND)
+ round_or_set_max(6, warn, second.nsec());
+ round(dec, mode, warn);
+ }
+ Time(THD *thd, MYSQL_TIME_STATUS *status,
+ const char *str, size_t len, CHARSET_INFO *cs,
+ const Options &opt, uint dec)
+ :Time(thd, status, str, len, cs, opt)
+ {
+ round(dec, time_round_mode_t(opt), &status->warnings);
+ }
+ Time(THD *thd, int *warn, const Longlong_hybrid &nr,
+ const Options &opt, uint dec)
+ :Time(thd, warn, nr, opt)
+ {
+ /*
+ Decimal digit truncation is needed here in case if nr was out
+ of the supported TIME range, so "this" was set to '838:59:59.999999'.
+ We always do truncation (not rounding) here, independently from "opt".
+ */
+ trunc(dec);
+ }
+ Time(THD *thd, int *warn, double nr, const Options &opt, uint dec)
+ :Time(thd, warn, nr, opt)
+ {
+ round(dec, time_round_mode_t(opt), warn);
+ }
+ Time(THD *thd, int *warn, const my_decimal *d, const Options &opt, uint dec)
+ :Time(thd, warn, d, opt)
+ {
+ round(dec, time_round_mode_t(opt), warn);
+ }
+
+ static date_conv_mode_t default_flags_for_get_date()
{ return TIME_TIME_ONLY | TIME_INVALID_DATES; }
- static sql_mode_t comparison_flags_for_get_date()
+ static date_conv_mode_t comparison_flags_for_get_date()
{ return TIME_TIME_ONLY | TIME_INVALID_DATES | TIME_FUZZY_DATES; }
bool is_valid_time() const
{
@@ -246,8 +1736,8 @@ public:
{
DBUG_ASSERT(is_valid_time_slow());
DBUG_ASSERT(other->is_valid_time_slow());
- longlong p0= pack_time(this);
- longlong p1= pack_time(other);
+ longlong p0= to_packed();
+ longlong p1= other->to_packed();
if (p0 < p1)
return -1;
if (p0 > p1)
@@ -263,6 +1753,114 @@ public:
{
return neg ? -to_seconds_abs() : to_seconds_abs();
}
+ longlong to_longlong() const
+ {
+ if (!is_valid_time())
+ return 0;
+ ulonglong v= TIME_to_ulonglong_time(this);
+ return neg ? -(longlong) v : (longlong) v;
+ }
+ double to_double() const
+ {
+ return !is_valid_time() ? 0 :
+ Temporal::to_double(neg, TIME_to_ulonglong_time(this), second_part);
+ }
+ bool to_native(Native *to, uint decimals) const;
+ String *to_string(String *str, uint dec) const
+ {
+ if (!is_valid_time())
+ return NULL;
+ str->set_charset(&my_charset_numeric);
+ if (!str->alloc(MAX_DATE_STRING_REP_LENGTH))
+ str->length(my_time_to_str(this, const_cast<char*>(str->ptr()), dec));
+ return str;
+ }
+ my_decimal *to_decimal(my_decimal *to)
+ {
+ return is_valid_time() ? Temporal::to_decimal(to) : bad_to_decimal(to);
+ }
+ longlong to_packed() const
+ {
+ return is_valid_time() ? Temporal::to_packed() : 0;
+ }
+ longlong valid_time_to_packed() const
+ {
+ DBUG_ASSERT(is_valid_time_slow());
+ return Temporal::to_packed();
+ }
+ long fraction_remainder(uint dec) const
+ {
+ DBUG_ASSERT(is_valid_time());
+ return Temporal::fraction_remainder(dec);
+ }
+
+ Time &trunc(uint dec)
+ {
+ if (is_valid_time())
+ my_time_trunc(this, dec);
+ DBUG_ASSERT(is_valid_value_slow());
+ return *this;
+ }
+ Time &ceiling(int *warn)
+ {
+ if (is_valid_time())
+ {
+ if (neg)
+ my_time_trunc(this, 0);
+ else if (second_part)
+ round_or_set_max(0, warn, 999999999);
+ }
+ DBUG_ASSERT(is_valid_value_slow());
+ return *this;
+ }
+ Time &ceiling()
+ {
+ int warn= 0;
+ return ceiling(&warn);
+ }
+ Time &floor(int *warn)
+ {
+ if (is_valid_time())
+ {
+ if (!neg)
+ my_time_trunc(this, 0);
+ else if (second_part)
+ round_or_set_max(0, warn, 999999999);
+ }
+ DBUG_ASSERT(is_valid_value_slow());
+ return *this;
+ }
+ Time &floor()
+ {
+ int warn= 0;
+ return floor(&warn);
+ }
+ Time &round(uint dec, int *warn)
+ {
+ if (is_valid_time())
+ round_or_set_max(dec, warn);
+ DBUG_ASSERT(is_valid_value_slow());
+ return *this;
+ }
+ Time &round(uint dec, time_round_mode_t mode, int *warn)
+ {
+ switch (mode.mode()) {
+ case time_round_mode_t::FRAC_NONE:
+ DBUG_ASSERT(fraction_remainder(dec) == 0);
+ return trunc(dec);
+ case time_round_mode_t::FRAC_TRUNCATE:
+ return trunc(dec);
+ case time_round_mode_t::FRAC_ROUND:
+ return round(dec, warn);
+ }
+ return *this;
+ }
+ Time &round(uint dec, time_round_mode_t mode)
+ {
+ int warn= 0;
+ return round(dec, mode, &warn);
+ }
+
};
@@ -289,10 +1887,25 @@ public:
it is accessed externally only in the valid state.
*/
-class Temporal_with_date: protected MYSQL_TIME
+class Temporal_with_date: public Temporal
{
+public:
+ class Options: public Temporal::Options
+ {
+ public:
+ Options(date_conv_mode_t fuzzydate, time_round_mode_t mode):
+ Temporal::Options(fuzzydate, mode)
+ {}
+ explicit Options(const Temporal::Options &opt)
+ :Temporal::Options(opt)
+ { }
+ explicit Options(date_mode_t mode)
+ :Temporal::Options(mode)
+ { }
+ };
protected:
- void make_from_item(THD *thd, Item *item, sql_mode_t flags);
+ void check_date_or_invalidate(int *warn, date_conv_mode_t flags);
+ void make_from_item(THD *thd, Item *item, date_mode_t flags);
ulong daynr() const
{
@@ -302,10 +1915,60 @@ protected:
{
return ::calc_weekday(daynr(), sunday_first_day_of_week);
}
- Temporal_with_date(THD *thd, Item *item, sql_mode_t flags)
+ ulong dayofyear() const
+ {
+ return (ulong) (daynr() - ::calc_daynr(year, 1, 1) + 1);
+ }
+ uint quarter() const
+ {
+ return (month + 2) / 3;
+ }
+ uint week(uint week_behaviour) const
{
- make_from_item(thd, item, flags);
+ uint year;
+ return calc_week(this, week_behaviour, &year);
}
+ uint yearweek(uint week_behaviour) const
+ {
+ uint year;
+ uint week= calc_week(this, week_behaviour, &year);
+ return week + year * 100;
+ }
+public:
+ Temporal_with_date()
+ {
+ time_type= MYSQL_TIMESTAMP_NONE;
+ }
+ Temporal_with_date(THD *thd, Item *item, date_mode_t fuzzydate)
+ {
+ make_from_item(thd, item, fuzzydate);
+ }
+ Temporal_with_date(int *warn, const Sec6 &nr, date_mode_t flags)
+ {
+ DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false);
+ if (nr.to_datetime_or_date(this, warn, date_conv_mode_t(flags)))
+ time_type= MYSQL_TIMESTAMP_NONE;
+ }
+ Temporal_with_date(THD *thd, MYSQL_TIME_STATUS *status,
+ const char *str, size_t len, CHARSET_INFO *cs,
+ date_mode_t flags)
+ {
+ DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false);
+ if (str_to_datetime_or_date(thd, status, str, len, cs, flags))
+ time_type= MYSQL_TIMESTAMP_NONE;
+ }
+public:
+ bool check_date_with_warn(THD *thd, date_conv_mode_t flags)
+ {
+ return ::check_date_with_warn(thd, this, flags, MYSQL_TIMESTAMP_ERROR);
+ }
+ bool check_date_with_warn(THD *thd)
+ {
+ return ::check_date_with_warn(thd, this, Temporal::sql_mode_for_dates(thd),
+ MYSQL_TIMESTAMP_ERROR);
+ }
+ static date_conv_mode_t comparison_flags_for_get_date()
+ { return TIME_INVALID_DATES | TIME_FUZZY_DATES; }
};
@@ -330,29 +1993,147 @@ class Date: public Temporal_with_date
return !check_datetime_range(this);
}
public:
- Date(THD *thd, Item *item, sql_mode_t flags)
- :Temporal_with_date(thd, item, flags)
+ class Options: public Temporal_with_date::Options
+ {
+ public:
+ explicit Options(date_conv_mode_t fuzzydate)
+ :Temporal_with_date::Options(fuzzydate, TIME_FRAC_TRUNCATE)
+ { }
+ Options(THD *thd, time_round_mode_t mode)
+ :Temporal_with_date::Options(sql_mode_for_dates(thd), mode)
+ { }
+ explicit Options(THD *thd)
+ :Temporal_with_date::Options(sql_mode_for_dates(thd), TIME_FRAC_TRUNCATE)
+ { }
+ explicit Options(date_mode_t fuzzydate)
+ :Temporal_with_date::Options(fuzzydate)
+ { }
+ };
+public:
+ Date(Item *item, date_mode_t fuzzydate)
+ :Date(current_thd, item, fuzzydate)
+ { }
+ Date(THD *thd, Item *item, date_mode_t fuzzydate)
+ :Temporal_with_date(thd, item, fuzzydate)
{
if (time_type == MYSQL_TIMESTAMP_DATETIME)
datetime_to_date(this);
DBUG_ASSERT(is_valid_value_slow());
}
+ Date(THD *thd, Item *item, date_conv_mode_t fuzzydate)
+ :Date(thd, item, Options(fuzzydate))
+ { }
+ Date(THD *thd, Item *item)
+ :Temporal_with_date(Date(thd, item, Options(thd, TIME_FRAC_TRUNCATE)))
+ { }
+ Date(Item *item)
+ :Temporal_with_date(Date(current_thd, item))
+ { }
Date(const Temporal_with_date *d)
:Temporal_with_date(*d)
{
datetime_to_date(this);
DBUG_ASSERT(is_valid_date_slow());
}
+ explicit Date(const Temporal_hybrid *from)
+ {
+ from->copy_valid_value_to_mysql_time(this);
+ DBUG_ASSERT(is_valid_date_slow());
+ }
bool is_valid_date() const
{
DBUG_ASSERT(is_valid_value_slow());
return time_type == MYSQL_TIMESTAMP_DATE;
}
+ bool check_date(date_conv_mode_t flags, int *warnings) const
+ {
+ DBUG_ASSERT(is_valid_date_slow());
+ return ::check_date(this, (year || month || day),
+ ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE),
+ warnings);
+ }
+ bool check_date(THD *thd, int *warnings) const
+ {
+ return check_date(Temporal::sql_mode_for_dates(thd), warnings);
+ }
+ bool check_date(date_conv_mode_t flags) const
+ {
+ int dummy; /* unused */
+ return check_date(flags, &dummy);
+ }
+ bool check_date(THD *thd) const
+ {
+ int dummy;
+ return check_date(Temporal::sql_mode_for_dates(thd), &dummy);
+ }
const MYSQL_TIME *get_mysql_time() const
{
DBUG_ASSERT(is_valid_date_slow());
return this;
}
+ bool copy_to_mysql_time(MYSQL_TIME *ltime) const
+ {
+ if (time_type == MYSQL_TIMESTAMP_NONE)
+ {
+ ltime->time_type= MYSQL_TIMESTAMP_NONE;
+ return true;
+ }
+ DBUG_ASSERT(is_valid_date_slow());
+ *ltime= *this;
+ return false;
+ }
+ ulong daynr() const
+ {
+ DBUG_ASSERT(is_valid_date_slow());
+ return Temporal_with_date::daynr();
+ }
+ ulong dayofyear() const
+ {
+ DBUG_ASSERT(is_valid_date_slow());
+ return Temporal_with_date::dayofyear();
+ }
+ uint quarter() const
+ {
+ DBUG_ASSERT(is_valid_date_slow());
+ return Temporal_with_date::quarter();
+ }
+ uint week(uint week_behaviour) const
+ {
+ DBUG_ASSERT(is_valid_date_slow());
+ return Temporal_with_date::week(week_behaviour);
+ }
+ uint yearweek(uint week_behaviour) const
+ {
+ DBUG_ASSERT(is_valid_date_slow());
+ return Temporal_with_date::yearweek(week_behaviour);
+ }
+
+ longlong valid_date_to_packed() const
+ {
+ DBUG_ASSERT(is_valid_date_slow());
+ return Temporal::to_packed();
+ }
+ longlong to_longlong() const
+ {
+ return is_valid_date() ? (longlong) TIME_to_ulonglong_date(this) : 0LL;
+ }
+ double to_double() const
+ {
+ return (double) to_longlong();
+ }
+ String *to_string(String *str) const
+ {
+ if (!is_valid_date())
+ return NULL;
+ str->set_charset(&my_charset_numeric);
+ if (!str->alloc(MAX_DATE_STRING_REP_LENGTH))
+ str->length(my_date_to_str(this, const_cast<char*>(str->ptr())));
+ return str;
+ }
+ my_decimal *to_decimal(my_decimal *to)
+ {
+ return is_valid_date() ? Temporal::to_decimal(to) : bad_to_decimal(to);
+ }
};
@@ -377,14 +2158,158 @@ class Datetime: public Temporal_with_date
DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATETIME);
return !check_datetime_range(this);
}
-public:
- Datetime(THD *thd, Item *item, sql_mode_t flags)
- :Temporal_with_date(thd, item, flags)
+ bool add_nanoseconds_or_invalidate(THD *thd, int *warn, ulong nsec)
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ bool rc= Temporal::datetime_add_nanoseconds_or_invalidate(thd, warn, nsec);
+ DBUG_ASSERT(is_valid_value_slow());
+ return rc;
+ }
+ void date_to_datetime_if_needed()
{
if (time_type == MYSQL_TIMESTAMP_DATE)
date_to_datetime(this);
+ }
+ void make_from_time(THD *thd, int *warn, const MYSQL_TIME *from,
+ date_conv_mode_t flags);
+ void make_from_datetime(THD *thd, int *warn, const MYSQL_TIME *from,
+ date_conv_mode_t flags);
+ bool round_or_invalidate(THD *thd, uint dec, int *warn);
+ bool round_or_invalidate(THD *thd, uint dec, int *warn, ulong nsec)
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ bool rc= Temporal::datetime_round_or_invalidate(thd, dec, warn, nsec);
+ DBUG_ASSERT(is_valid_value_slow());
+ return rc;
+ }
+public:
+
+ class Options: public Temporal_with_date::Options
+ {
+ public:
+ Options(date_conv_mode_t fuzzydate, time_round_mode_t nanosecond_rounding)
+ :Temporal_with_date::Options(fuzzydate, nanosecond_rounding)
+ { }
+ Options(THD *thd)
+ :Temporal_with_date::Options(sql_mode_for_dates(thd), default_round_mode(thd))
+ { }
+ Options(THD *thd, time_round_mode_t rounding_mode)
+ :Temporal_with_date::Options(sql_mode_for_dates(thd), rounding_mode)
+ { }
+ Options(date_conv_mode_t fuzzydate, THD *thd)
+ :Temporal_with_date::Options(fuzzydate, default_round_mode(thd))
+ { }
+ };
+
+ class Options_cmp: public Options
+ {
+ public:
+ Options_cmp(THD *thd)
+ :Options(comparison_flags_for_get_date(), thd)
+ { }
+ };
+
+ static Datetime zero()
+ {
+ int warn;
+ static Longlong_hybrid nr(0, false);
+ return Datetime(&warn, nr, date_mode_t(0));
+ }
+public:
+ Datetime() // NULL value
+ :Temporal_with_date()
+ { }
+ Datetime(THD *thd, Item *item, date_mode_t fuzzydate)
+ :Temporal_with_date(thd, item, fuzzydate)
+ {
+ date_to_datetime_if_needed();
+ DBUG_ASSERT(is_valid_value_slow());
+ }
+ Datetime(THD *thd, Item *item)
+ :Temporal_with_date(Datetime(thd, item, Options(thd)))
+ { }
+ Datetime(Item *item)
+ :Datetime(current_thd, item)
+ { }
+
+ Datetime(THD *thd, int *warn, const MYSQL_TIME *from, date_conv_mode_t flags);
+ Datetime(THD *thd, MYSQL_TIME_STATUS *status,
+ const char *str, size_t len, CHARSET_INFO *cs,
+ const date_mode_t fuzzydate)
+ :Temporal_with_date(thd, status, str, len, cs, fuzzydate)
+ {
+ date_to_datetime_if_needed();
DBUG_ASSERT(is_valid_value_slow());
}
+
+protected:
+ Datetime(int *warn, const Sec6 &nr, date_mode_t flags)
+ :Temporal_with_date(warn, nr, flags)
+ {
+ date_to_datetime_if_needed();
+ DBUG_ASSERT(is_valid_value_slow());
+ }
+ Datetime(THD *thd, int *warn, const Sec9 &nr, date_mode_t fuzzydate)
+ :Datetime(warn, static_cast<const Sec6>(nr), fuzzydate)
+ {
+ if (is_valid_datetime() &&
+ time_round_mode_t(fuzzydate) == TIME_FRAC_ROUND)
+ round_or_invalidate(thd, 6, warn, nr.nsec());
+ DBUG_ASSERT(is_valid_value_slow());
+ }
+
+public:
+ Datetime(int *warn, const Longlong_hybrid &nr, date_mode_t mode)
+ :Datetime(warn, Sec6(nr), mode)
+ { }
+ Datetime(THD *thd, int *warn, double nr, date_mode_t fuzzydate)
+ :Datetime(thd, warn, Sec9(nr), fuzzydate)
+ { }
+ Datetime(THD *thd, int *warn, const my_decimal *d, date_mode_t fuzzydate)
+ :Datetime(thd, warn, Sec9(d), fuzzydate)
+ { }
+ Datetime(THD *thd, const timeval &tv);
+
+ Datetime(THD *thd, Item *item, date_mode_t fuzzydate, uint dec)
+ :Datetime(thd, item, fuzzydate)
+ {
+ int warn= 0;
+ round(thd, dec, time_round_mode_t(fuzzydate), &warn);
+ }
+ Datetime(THD *thd, MYSQL_TIME_STATUS *status,
+ const char *str, size_t len, CHARSET_INFO *cs,
+ date_mode_t fuzzydate, uint dec)
+ :Datetime(thd, status, str, len, cs, fuzzydate)
+ {
+ round(thd, dec, time_round_mode_t(fuzzydate), &status->warnings);
+ }
+ Datetime(THD *thd, int *warn, double nr, date_mode_t fuzzydate, uint dec)
+ :Datetime(thd, warn, nr, fuzzydate)
+ {
+ round(thd, dec, time_round_mode_t(fuzzydate), warn);
+ }
+ Datetime(THD *thd, int *warn, const my_decimal *d, date_mode_t fuzzydate, uint dec)
+ :Datetime(thd, warn, d, fuzzydate)
+ {
+ round(thd, dec, time_round_mode_t(fuzzydate), warn);
+ }
+ Datetime(THD *thd, int *warn, const MYSQL_TIME *from,
+ date_mode_t fuzzydate, uint dec)
+ :Datetime(thd, warn, from, date_conv_mode_t(fuzzydate) & ~TIME_TIME_ONLY)
+ {
+ round(thd, dec, time_round_mode_t(fuzzydate), warn);
+ }
+ explicit Datetime(const Temporal_hybrid *from)
+ {
+ from->copy_valid_value_to_mysql_time(this);
+ DBUG_ASSERT(is_valid_datetime_slow());
+ }
+ explicit Datetime(const MYSQL_TIME *from)
+ {
+ *(static_cast<MYSQL_TIME*>(this))= *from;
+ DBUG_ASSERT(is_valid_datetime_slow());
+ }
+
bool is_valid_datetime() const
{
/*
@@ -394,16 +2319,72 @@ public:
DBUG_ASSERT(is_valid_value_slow());
return time_type == MYSQL_TIMESTAMP_DATETIME;
}
+ bool check_date(date_conv_mode_t flags, int *warnings) const
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ return ::check_date(this, (year || month || day),
+ ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE),
+ warnings);
+ }
+ bool check_date(date_conv_mode_t flags) const
+ {
+ int dummy; /* unused */
+ return check_date(flags, &dummy);
+ }
+ bool check_date(THD *thd) const
+ {
+ return check_date(Temporal::sql_mode_for_dates(thd));
+ }
bool hhmmssff_is_zero() const
{
DBUG_ASSERT(is_valid_datetime_slow());
return hour == 0 && minute == 0 && second == 0 && second_part == 0;
}
+ ulong daynr() const
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ return Temporal_with_date::daynr();
+ }
int weekday(bool sunday_first_day_of_week) const
{
DBUG_ASSERT(is_valid_datetime_slow());
return Temporal_with_date::weekday(sunday_first_day_of_week);
}
+ ulong dayofyear() const
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ return Temporal_with_date::dayofyear();
+ }
+ uint quarter() const
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ return Temporal_with_date::quarter();
+ }
+ uint week(uint week_behaviour) const
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ return Temporal_with_date::week(week_behaviour);
+ }
+ uint yearweek(uint week_behaviour) const
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ return Temporal_with_date::yearweek(week_behaviour);
+ }
+
+ longlong hhmmss_to_seconds_abs() const
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ return hour * 3600L + minute * 60 + second;
+ }
+ longlong hhmmss_to_seconds() const
+ {
+ return neg ? -hhmmss_to_seconds_abs() : hhmmss_to_seconds_abs();
+ }
+ longlong to_seconds() const
+ {
+ return hhmmss_to_seconds() + (longlong) daynr() * 24L * 3600L;
+ }
+
const MYSQL_TIME *get_mysql_time() const
{
DBUG_ASSERT(is_valid_datetime_slow());
@@ -435,8 +2416,322 @@ public:
ltime->time_type= type;
return false;
}
+ longlong to_longlong() const
+ {
+ return is_valid_datetime() ?
+ (longlong) TIME_to_ulonglong_datetime(this) : 0LL;
+ }
+ double to_double() const
+ {
+ return !is_valid_datetime() ? 0 :
+ Temporal::to_double(neg, TIME_to_ulonglong_datetime(this), second_part);
+ }
+ String *to_string(String *str, uint dec) const
+ {
+ if (!is_valid_datetime())
+ return NULL;
+ str->set_charset(&my_charset_numeric);
+ if (!str->alloc(MAX_DATE_STRING_REP_LENGTH))
+ str->length(my_datetime_to_str(this, const_cast<char*>(str->ptr()), dec));
+ return str;
+ }
+ my_decimal *to_decimal(my_decimal *to)
+ {
+ return is_valid_datetime() ? Temporal::to_decimal(to) : bad_to_decimal(to);
+ }
+ longlong to_packed() const
+ {
+ return is_valid_datetime() ? Temporal::to_packed() : 0;
+ }
+ longlong valid_datetime_to_packed() const
+ {
+ DBUG_ASSERT(is_valid_datetime_slow());
+ return Temporal::to_packed();
+ }
+ long fraction_remainder(uint dec) const
+ {
+ DBUG_ASSERT(is_valid_datetime());
+ return Temporal::fraction_remainder(dec);
+ }
+
+ Datetime &trunc(uint dec)
+ {
+ if (is_valid_datetime())
+ my_datetime_trunc(this, dec);
+ DBUG_ASSERT(is_valid_value_slow());
+ return *this;
+ }
+ Datetime &ceiling(THD *thd, int *warn)
+ {
+ if (is_valid_datetime() && second_part)
+ round_or_invalidate(thd, 0, warn, 999999999);
+ DBUG_ASSERT(is_valid_value_slow());
+ return *this;
+ }
+ Datetime &ceiling(THD *thd)
+ {
+ int warn= 0;
+ return ceiling(thd, &warn);
+ }
+ Datetime &round(THD *thd, uint dec, int *warn)
+ {
+ if (is_valid_datetime())
+ round_or_invalidate(thd, dec, warn);
+ DBUG_ASSERT(is_valid_value_slow());
+ return *this;
+ }
+ Datetime &round(THD *thd, uint dec, time_round_mode_t mode, int *warn)
+ {
+ switch (mode.mode()) {
+ case time_round_mode_t::FRAC_NONE:
+ DBUG_ASSERT(fraction_remainder(dec) == 0);
+ return trunc(dec);
+ case time_round_mode_t::FRAC_TRUNCATE:
+ return trunc(dec);
+ case time_round_mode_t::FRAC_ROUND:
+ return round(thd, dec, warn);
+ }
+ return *this;
+ }
+ Datetime &round(THD *thd, uint dec, time_round_mode_t mode)
+ {
+ int warn= 0;
+ return round(thd, dec, mode, &warn);
+ }
+
};
+
+/*
+ Datetime to be created from an Item who is known to be of a temporal
+ data type. For temporal data types we don't need nanosecond rounding
+ or truncation, as their precision is limited.
+*/
+class Datetime_from_temporal: public Datetime
+{
+public:
+ // The constructor DBUG_ASSERTs on a proper Item data type.
+ Datetime_from_temporal(THD *thd, Item *temporal, date_conv_mode_t flags);
+};
+
+
+/*
+ Datetime to be created from an Item who is known not to have digits outside
+ of the specified scale. So it's not important which rounding method to use.
+ TRUNCATE should work.
+ Typically, Item is of a temporal data type, but this is not strictly required.
+*/
+class Datetime_truncation_not_needed: public Datetime
+{
+public:
+ Datetime_truncation_not_needed(THD *thd, Item *item, date_conv_mode_t mode);
+ Datetime_truncation_not_needed(THD *thd, Item *item, date_mode_t mode)
+ :Datetime_truncation_not_needed(thd, item, date_conv_mode_t(mode))
+ { }
+};
+
+
+class Timestamp: protected Timeval
+{
+ static uint binary_length_to_precision(uint length);
+protected:
+ void round_or_set_max(uint dec, int *warn);
+ bool add_nanoseconds_usec(uint nanoseconds)
+ {
+ DBUG_ASSERT(nanoseconds <= 1000000000);
+ if (nanoseconds < 500)
+ return false;
+ tv_usec+= (nanoseconds + 500) / 1000;
+ if (tv_usec < 1000000)
+ return false;
+ tv_usec%= 1000000;
+ return true;
+ }
+public:
+ static date_conv_mode_t sql_mode_for_timestamp(THD *thd);
+ static time_round_mode_t default_round_mode(THD *thd);
+ class DatetimeOptions: public date_mode_t
+ {
+ public:
+ DatetimeOptions(date_conv_mode_t fuzzydate, time_round_mode_t round_mode)
+ :date_mode_t(fuzzydate | round_mode)
+ { }
+ DatetimeOptions(THD *thd)
+ :DatetimeOptions(sql_mode_for_timestamp(thd), default_round_mode(thd))
+ { }
+ };
+public:
+ Timestamp(my_time_t timestamp, ulong sec_part)
+ :Timeval(timestamp, sec_part)
+ { }
+ explicit Timestamp(const timeval &tv)
+ :Timeval(tv)
+ { }
+ explicit Timestamp(const Native &native);
+ Timestamp(THD *thd, const MYSQL_TIME *ltime, uint *error_code);
+ const struct timeval &tv() const { return *this; }
+ int cmp(const Timestamp &other) const
+ {
+ return tv_sec < other.tv_sec ? -1 :
+ tv_sec > other.tv_sec ? +1 :
+ tv_usec < other.tv_usec ? -1 :
+ tv_usec > other.tv_usec ? +1 : 0;
+ }
+ bool to_TIME(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
+ bool to_native(Native *to, uint decimals) const;
+ Datetime to_datetime(THD *thd) const
+ {
+ return Datetime(thd, *this);
+ }
+ long fraction_remainder(uint dec) const
+ {
+ return my_time_fraction_remainder(tv_usec, dec);
+ }
+ Timestamp &trunc(uint dec)
+ {
+ my_timeval_trunc(this, dec);
+ return *this;
+ }
+ Timestamp &round(uint dec, int *warn)
+ {
+ round_or_set_max(dec, warn);
+ return *this;
+ }
+ Timestamp &round(uint dec, time_round_mode_t mode, int *warn)
+ {
+ switch (mode.mode()) {
+ case time_round_mode_t::FRAC_NONE:
+ DBUG_ASSERT(fraction_remainder(dec) == 0);
+ return trunc(dec);
+ case time_round_mode_t::FRAC_TRUNCATE:
+ return trunc(dec);
+ case time_round_mode_t::FRAC_ROUND:
+ return round(dec, warn);
+ }
+ return *this;
+ }
+ Timestamp &round(uint dec, time_round_mode_t mode)
+ {
+ int warn= 0;
+ return round(dec, mode, &warn);
+ }
+};
+
+
+/**
+ A helper class to store MariaDB TIMESTAMP values, which can be:
+ - real TIMESTAMP (seconds and microseconds since epoch), or
+ - zero datetime '0000-00-00 00:00:00.000000'
+*/
+class Timestamp_or_zero_datetime: protected Timestamp
+{
+ bool m_is_zero_datetime;
+public:
+ Timestamp_or_zero_datetime()
+ :Timestamp(0,0), m_is_zero_datetime(true)
+ { }
+ Timestamp_or_zero_datetime(const Native &native)
+ :Timestamp(native.length() ? Timestamp(native) : Timestamp(0,0)),
+ m_is_zero_datetime(native.length() == 0)
+ { }
+ Timestamp_or_zero_datetime(const Timestamp &tm, bool is_zero_datetime)
+ :Timestamp(tm), m_is_zero_datetime(is_zero_datetime)
+ { }
+ Timestamp_or_zero_datetime(THD *thd, const MYSQL_TIME *ltime, uint *err_code);
+ Datetime to_datetime(THD *thd) const
+ {
+ if (is_zero_datetime())
+ return Datetime::zero();
+ return Timestamp::to_datetime(thd);
+ }
+ bool is_zero_datetime() const { return m_is_zero_datetime; }
+ void trunc(uint decimals)
+ {
+ if (!is_zero_datetime())
+ Timestamp::trunc(decimals);
+ }
+ int cmp(const Timestamp_or_zero_datetime &other) const
+ {
+ if (is_zero_datetime())
+ return other.is_zero_datetime() ? 0 : -1;
+ if (other.is_zero_datetime())
+ return 1;
+ return Timestamp::cmp(other);
+ }
+ bool to_TIME(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate) const;
+ /*
+ Convert to native format:
+ - Real timestamps are encoded in the same way how Field_timestamp2 stores
+ values (big endian seconds followed by big endian microseconds)
+ - Zero datetime '0000-00-00 00:00:00.000000' is encoded as empty string.
+ Two native values are binary comparable.
+ */
+ bool to_native(Native *to, uint decimals) const;
+};
+
+
+/**
+ A helper class to store non-null MariaDB TIMESTAMP values in
+ the native binary encoded representation.
+*/
+class Timestamp_or_zero_datetime_native:
+ public NativeBuffer<STRING_BUFFER_TIMESTAMP_BINARY_SIZE>
+{
+public:
+ Timestamp_or_zero_datetime_native() { }
+ Timestamp_or_zero_datetime_native(const Timestamp_or_zero_datetime &ts,
+ uint decimals)
+ {
+ if (ts.to_native(this, decimals))
+ length(0); // safety
+ }
+ int save_in_field(Field *field, uint decimals) const;
+ Datetime to_datetime(THD *thd) const
+ {
+ return is_zero_datetime() ?
+ Datetime::zero() :
+ Datetime(thd, Timestamp(*this).tv());
+ }
+ bool is_zero_datetime() const
+ {
+ return length() == 0;
+ }
+};
+
+
+/**
+ A helper class to store nullable MariaDB TIMESTAMP values in
+ the native binary encoded representation.
+*/
+class Timestamp_or_zero_datetime_native_null: public Timestamp_or_zero_datetime_native,
+ public Null_flag
+{
+public:
+ // With optional data type conversion
+ Timestamp_or_zero_datetime_native_null(THD *thd, Item *item, bool conv);
+ // Without data type conversion: item is known to be of the TIMESTAMP type
+ Timestamp_or_zero_datetime_native_null(THD *thd, Item *item)
+ :Timestamp_or_zero_datetime_native_null(thd, item, false)
+ { }
+ Datetime to_datetime(THD *thd) const
+ {
+ return is_null() ? Datetime() :
+ Timestamp_or_zero_datetime_native::to_datetime(thd);
+ }
+ void to_TIME(THD *thd, MYSQL_TIME *to)
+ {
+ DBUG_ASSERT(!is_null());
+ Datetime::Options opt(TIME_CONV_NONE, TIME_FRAC_NONE);
+ Timestamp_or_zero_datetime(*this).to_TIME(thd, to, opt);
+ }
+ bool is_zero_datetime() const
+ {
+ DBUG_ASSERT(!is_null());
+ return Timestamp_or_zero_datetime_native::is_zero_datetime();
+ }
+};
+
+
/*
Flags for collation aggregation modes, used in TDCollation::agg():
@@ -462,7 +2757,6 @@ public:
#define MY_COLL_CMP_CONV (MY_COLL_ALLOW_CONV | MY_COLL_DISALLOW_NONE)
-#define my_charset_numeric my_charset_latin1
#define MY_REPERTOIRE_NUMERIC MY_REPERTOIRE_ASCII
@@ -587,6 +2881,17 @@ public:
};
+class DTCollation_numeric: public DTCollation
+{
+public:
+ DTCollation_numeric()
+ :DTCollation(charset_info(), DERIVATION_NUMERIC, MY_REPERTOIRE_NUMERIC)
+ { }
+ static const CHARSET_INFO *charset_info() { return &my_charset_numeric; }
+ static const DTCollation & singleton();
+};
+
+
static inline uint32
char_to_byte_length_safe(size_t char_length_arg, uint32 mbmaxlen_arg)
{
@@ -851,6 +3156,14 @@ public:
};
+class Type_cmp_attributes
+{
+public:
+ virtual ~Type_cmp_attributes() { }
+ virtual CHARSET_INFO *compare_collation() const= 0;
+};
+
+
class Type_cast_attributes
{
CHARSET_INFO *m_charset;
@@ -903,28 +3216,67 @@ public:
};
-class Record_addr
+class Bit_addr
{
-public:
- uchar *ptr; // Position to field in record
/**
- Byte where the @c NULL bit is stored inside a record. If this Field is a
- @c NOT @c NULL field, this member is @c NULL.
+ Byte where the bit is stored inside a record.
+ If the corresponding Field is a NOT NULL field, this member is NULL.
*/
- uchar *null_ptr;
- uchar null_bit; // Bit used to test null bit
+ uchar *m_ptr;
+ /**
+ Offset of the bit inside m_ptr[0], in the range 0..7.
+ */
+ uchar m_offs;
+public:
+ Bit_addr()
+ :m_ptr(NULL),
+ m_offs(0)
+ { }
+ Bit_addr(uchar *ptr, uchar offs)
+ :m_ptr(ptr), m_offs(offs)
+ {
+ DBUG_ASSERT(ptr || offs == 0);
+ DBUG_ASSERT(offs < 8);
+ }
+ Bit_addr(bool maybe_null)
+ :m_ptr(maybe_null ? (uchar *) "" : NULL),
+ m_offs(0)
+ { }
+ uchar *ptr() const { return m_ptr; }
+ uchar offs() const { return m_offs; }
+ uchar bit() const { return m_ptr ? ((uchar) 1) << m_offs : 0; }
+ void inc()
+ {
+ DBUG_ASSERT(m_ptr);
+ m_ptr+= (m_offs == 7);
+ m_offs= (m_offs + 1) & 7;
+ }
+};
+
+
+class Record_addr
+{
+ uchar *m_ptr; // Position of the field in the record
+ Bit_addr m_null; // Position and offset of the null bit
+public:
Record_addr(uchar *ptr_arg,
uchar *null_ptr_arg,
uchar null_bit_arg)
- :ptr(ptr_arg),
- null_ptr(null_ptr_arg),
- null_bit(null_bit_arg)
+ :m_ptr(ptr_arg),
+ m_null(null_ptr_arg, null_bit_arg)
+ { }
+ Record_addr(uchar *ptr, const Bit_addr &null)
+ :m_ptr(ptr),
+ m_null(null)
{ }
Record_addr(bool maybe_null)
- :ptr(NULL),
- null_ptr(maybe_null ? (uchar*) "" : 0),
- null_bit(0)
+ :m_ptr(NULL),
+ m_null(maybe_null)
{ }
+ uchar *ptr() const { return m_ptr; }
+ const Bit_addr &null() const { return m_null; }
+ uchar *null_ptr() const { return m_null.ptr(); }
+ uchar null_bit() const { return m_null.bit(); }
};
@@ -999,6 +3351,9 @@ public:
class Type_handler
{
protected:
+ static const Name m_version_default;
+ static const Name m_version_mysql56;
+ static const Name m_version_mariadb53;
String *print_item_value_csstr(THD *thd, Item *item, String *str) const;
String *print_item_value_temporal(THD *thd, Item *item, String *str,
const Name &type_name, String *buf) const;
@@ -1020,6 +3375,7 @@ protected:
bool Item_send_double(Item *item, Protocol *protocol, st_value *buf) const;
bool Item_send_time(Item *item, Protocol *protocol, st_value *buf) const;
bool Item_send_date(Item *item, Protocol *protocol, st_value *buf) const;
+ bool Item_send_timestamp(Item *item, Protocol *protocol, st_value *buf) const;
bool Item_send_datetime(Item *item, Protocol *protocol, st_value *buf) const;
bool Column_definition_prepare_stage2_legacy(Column_definition *c,
enum_field_types type)
@@ -1031,6 +3387,7 @@ protected:
enum_field_types type)
const;
public:
+ static const Type_handler *odbc_literal_type_handler(const LEX_CSTRING *str);
static const Type_handler *blob_type_handler(uint max_octet_length);
static const Type_handler *string_type_handler(uint max_octet_length);
static const Type_handler *bit_and_int_mixture_handler(uint max_char_len);
@@ -1065,18 +3422,75 @@ public:
virtual Schema *schema() const;
virtual const Name name() const= 0;
+ virtual const Name version() const { return m_version_default; }
virtual enum_field_types field_type() const= 0;
virtual enum_field_types real_field_type() const { return field_type(); }
+ /**
+ Type code which is used for merging of traditional data types for result
+ (for UNION and for hybrid functions such as COALESCE).
+ Mapping can be done both ways: old->new, new->old, depending
+ on the particular data type implementation:
+ - type_handler_var_string (MySQL-4.1 old VARCHAR) is converted to
+ new VARCHAR before merging.
+ field_type_merge_rules[][] returns new VARCHAR.
+ - type_handler_newdate is converted to old DATE before merging.
+ field_type_merge_rules[][] returns NEWDATE.
+ - Temporal type_handler_xxx2 (new MySQL-5.6 types) are converted to
+ corresponding old type codes before merging (e.g. TIME2->TIME).
+ field_type_merge_rules[][] returns old type codes (e.g. TIME).
+ Then old types codes are supposed to convert to new type codes somehow,
+ but they do not. So UNION and COALESCE create old columns.
+ This is a bug and should be fixed eventually.
+ */
+ virtual enum_field_types traditional_merge_field_type() const
+ {
+ DBUG_ASSERT(is_traditional_type());
+ return field_type();
+ }
+ virtual enum_field_types type_code_for_protocol() const
+ {
+ return field_type();
+ }
+ virtual protocol_send_type_t protocol_send_type() const= 0;
virtual Item_result result_type() const= 0;
virtual Item_result cmp_type() const= 0;
virtual enum_mysql_timestamp_type mysql_timestamp_type() const
{
return MYSQL_TIMESTAMP_ERROR;
}
+ /*
+ Return true if the native format is fully implemented for a data type:
+ - Field_xxx::val_native()
+ - Item_xxx::val_native() for all classes supporting this data type
+ - Type_handler_xxx::cmp_native()
+ */
+ virtual bool is_val_native_ready() const
+ {
+ return false;
+ }
virtual bool is_timestamp_type() const
{
return false;
}
+ virtual bool is_order_clause_position_type() const
+ {
+ return false;
+ }
+ virtual bool is_limit_clause_valid_type() const
+ {
+ return false;
+ }
+ /*
+ Returns true if this data type supports a hack that
+ WHERE notnull_column IS NULL
+ finds zero values, e.g.:
+ WHERE date_notnull_column IS NULL ->
+ WHERE date_notnull_column = '0000-00-00'
+ */
+ virtual bool cond_notnull_field_isnull_to_field_eq_zero() const
+ {
+ return false;
+ }
/**
Check whether a field type can be partially indexed by a key.
@param type field type
@@ -1091,6 +3505,7 @@ public:
{
return false;
}
+ virtual uint max_octet_length() const { return 0; }
/**
Prepared statement long data:
Check whether this parameter data type is compatible with long data.
@@ -1099,6 +3514,10 @@ public:
*/
virtual bool is_param_long_data_type() const { return false; }
virtual const Type_handler *type_handler_for_comparison() const= 0;
+ virtual const Type_handler *type_handler_for_native_format() const
+ {
+ return this;
+ }
virtual const Type_handler *type_handler_for_item_field() const
{
return this;
@@ -1115,6 +3534,12 @@ public:
{
return this;
}
+ virtual const Type_handler *type_handler_for_system_time() const
+ {
+ return this;
+ }
+ virtual int
+ stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const= 0;
virtual CHARSET_INFO *charset_for_protocol(const Item *item) const;
virtual const Type_handler*
type_handler_adjusted_to_max_octet_length(uint max_octet_length,
@@ -1141,9 +3566,10 @@ public:
virtual bool can_return_text() const { return true; }
virtual bool can_return_date() const { return true; }
virtual bool can_return_time() const { return true; }
+ virtual bool is_bool_type() const { return false; }
virtual bool is_general_purpose_string_type() const { return false; }
- virtual uint Item_time_precision(Item *item) const;
- virtual uint Item_datetime_precision(Item *item) const;
+ virtual uint Item_time_precision(THD *thd, Item *item) const;
+ virtual uint Item_datetime_precision(THD *thd, Item *item) const;
virtual uint Item_decimal_scale(const Item *item) const;
virtual uint Item_decimal_precision(const Item *item) const= 0;
/*
@@ -1187,12 +3613,47 @@ public:
virtual Field *make_conversion_table_field(TABLE *TABLE,
uint metadata,
const Field *target) const= 0;
+ /*
+ Performs the final data type validation for a UNION element,
+ after the regular "aggregation for result" was done.
+ */
+ virtual bool union_element_finalize(const Item * item) const
+ {
+ return false;
+ }
+ // Automatic upgrade, e.g. for ALTER TABLE t1 FORCE
+ virtual void Column_definition_implicit_upgrade(Column_definition *c) const
+ { }
+ // Validate CHECK constraint after the parser
+ virtual bool Column_definition_validate_check_constraint(THD *thd,
+ Column_definition *c)
+ const;
+ // Fix attributes after the parser
virtual bool Column_definition_fix_attributes(Column_definition *c) const= 0;
+ /*
+ Fix attributes from an existing field. Used for:
+ - ALTER TABLE (for columns that do not change)
+ - DECLARE var TYPE OF t1.col1; (anchored SP variables)
+ */
+ virtual void Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *c,
+ const Field *field) const
+ { }
virtual bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const;
+ virtual bool Column_definition_bulk_alter(Column_definition *c,
+ const Column_derived_attributes
+ *derived_attr,
+ const Column_bulk_alter_attributes
+ *bulk_alter_attr)
+ const
+ { return false; }
/*
This method is called on queries like:
CREATE TABLE t2 (a INT) AS SELECT a FROM t1;
@@ -1211,9 +3672,7 @@ public:
*/
virtual bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *
- schema)
+ const handler *file)
const;
virtual bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
@@ -1226,6 +3685,23 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ virtual Field *
+ make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const= 0;
+ virtual void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const;
+ virtual bool
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options) const;
+
virtual void make_sort_key(uchar *to, Item *item,
const SORT_FIELD_ATTR *sort_field,
Sort_param *param) const= 0;
@@ -1234,8 +3710,10 @@ public:
SORT_FIELD_ATTR *attr) const= 0;
virtual uint32 max_display_length(const Item *item) const= 0;
+ virtual uint32 Item_decimal_notation_int_digits(const Item *item) const { return 0; }
virtual uint32 calc_pack_length(uint32 length) const= 0;
- virtual bool Item_save_in_value(Item *item, st_value *value) const= 0;
+ virtual void Item_update_null_value(Item *item) const= 0;
+ virtual bool Item_save_in_value(THD *thd, Item *item, st_value *value) const= 0;
virtual void Item_param_setup_conversion(THD *thd, Item_param *) const {}
virtual void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
@@ -1243,6 +3721,9 @@ public:
Item_param *param,
const Type_all_attributes *attr,
const st_value *value) const= 0;
+ virtual bool Item_param_val_native(THD *thd,
+ Item_param *item,
+ Native *to) const;
virtual bool Item_send(Item *item, Protocol *p, st_value *buf) const= 0;
virtual int Item_save_in_field(Item *item, Field *field,
bool no_conversions) const= 0;
@@ -1325,13 +3806,52 @@ public:
Item *src,
const Item *cmp) const= 0;
virtual Item_cache *Item_get_cache(THD *thd, const Item *item) const= 0;
+ /**
+ A builder for literals with data type name prefix, e.g.:
+ TIME'00:00:00', DATE'2001-01-01', TIMESTAMP'2001-01-01 00:00:00'.
+ @param thd The current thread
+ @param str Character literal
+ @param length Length of str
+ @param cs Character set of the string
+ @param send_error Whether to generate an error on failure
+
+ @retval A pointer to a new Item on success
+ NULL on error (wrong literal value, EOM)
+ */
+ virtual Item_literal *create_literal_item(THD *thd,
+ const char *str, size_t length,
+ CHARSET_INFO *cs,
+ bool send_error) const
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
+ Item_literal *create_literal_item(THD *thd, const String *str,
+ bool send_error) const
+ {
+ return create_literal_item(thd, str->ptr(), str->length(), str->charset(),
+ send_error);
+ }
virtual Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const
{
DBUG_ASSERT(0);
return NULL;
}
+ virtual Item_copy *create_item_copy(THD *thd, Item *item) const;
+ virtual int cmp_native(const Native &a, const Native &b) const
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
virtual bool set_comparator_func(Arg_comparator *cmp) const= 0;
+ virtual bool Item_const_eq(const Item_const *a, const Item_const *b,
+ bool binary_cmp) const
+ {
+ return false;
+ }
+ virtual bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
+ Item *a, Item *b) const= 0;
virtual bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
@@ -1348,9 +3868,23 @@ public:
virtual
bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const= 0;
+ virtual bool Item_val_native_with_conversion(THD *thd, Item *item,
+ Native *to) const
+ {
+ return true;
+ }
+ virtual bool Item_val_native_with_conversion_result(THD *thd, Item *item,
+ Native *to) const
+ {
+ return true;
+ }
+
virtual bool Item_val_bool(Item *item) const= 0;
- virtual bool Item_get_date(Item *item, MYSQL_TIME *ltime,
- ulonglong fuzzydate) const= 0;
+ virtual void Item_get_date(THD *thd, Item *item,
+ Temporal::Warn *buff, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate) const= 0;
+ bool Item_get_date_with_warn(THD *thd, Item *item, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate) const;
virtual longlong Item_val_int_signed_typecast(Item *item) const= 0;
virtual longlong Item_val_int_unsigned_typecast(Item *item) const= 0;
@@ -1371,9 +3905,15 @@ public:
Item_func_hybrid_field_type *,
my_decimal *) const= 0;
virtual
- bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ void Item_func_hybrid_field_type_get_date(THD *,
+ Item_func_hybrid_field_type *,
+ Temporal::Warn *,
MYSQL_TIME *,
- ulonglong fuzzydate) const= 0;
+ date_mode_t fuzzydate) const= 0;
+ bool Item_func_hybrid_field_type_get_date_with_warn(THD *thd,
+ Item_func_hybrid_field_type *,
+ MYSQL_TIME *,
+ date_mode_t) const;
virtual
String *Item_func_min_max_val_str(Item_func_min_max *, String *) const= 0;
virtual
@@ -1384,8 +3924,8 @@ public:
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
my_decimal *) const= 0;
virtual
- bool Item_func_min_max_get_date(Item_func_min_max*,
- MYSQL_TIME *, ulonglong fuzzydate) const= 0;
+ bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
+ MYSQL_TIME *, date_mode_t fuzzydate) const= 0;
virtual bool
Item_func_between_fix_length_and_dec(Item_func_between *func) const= 0;
virtual longlong
@@ -1470,6 +4010,11 @@ public:
DBUG_ASSERT(0);
return MYSQL_TYPE_NULL;
};
+ protocol_send_type_t protocol_send_type() const
+ {
+ DBUG_ASSERT(0);
+ return PROTOCOL_SEND_STRING;
+ }
Item_result result_type() const
{
return ROW_RESULT;
@@ -1479,6 +4024,11 @@ public:
return ROW_RESULT;
}
const Type_handler *type_handler_for_comparison() const;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
bool subquery_type_allows_materialization(const Item *inner,
const Item *outer,
bool is_in_predicate) const
@@ -1502,15 +4052,23 @@ public:
{
return false;
}
+ void Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *c,
+ const Field *field) const
+ {
+ DBUG_ASSERT(0);
+ }
bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const;
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
const
{
DBUG_ASSERT(0);
@@ -1530,6 +4088,13 @@ public:
DBUG_ASSERT(0);
return NULL;
}
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
void make_sort_key(uchar *to, Item *item,
const SORT_FIELD_ATTR *sort_field,
Sort_param *param) const
@@ -1551,12 +4116,14 @@ public:
DBUG_ASSERT(0);
return 0;
}
+ bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
+ Item *a, Item *b) const;
uint Item_decimal_precision(const Item *item) const
{
DBUG_ASSERT(0);
return DECIMAL_MAX_PRECISION;
}
- bool Item_save_in_value(Item *item, st_value *value) const;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
@@ -1566,6 +4133,7 @@ public:
DBUG_ASSERT(0);
return true;
}
+ void Item_update_null_value(Item *item) const;
int Item_save_in_field(Item *item, Field *field, bool no_conversions) const
{
DBUG_ASSERT(0);
@@ -1582,6 +4150,11 @@ public:
}
Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
+ Item_copy *create_item_copy(THD *thd, Item *item) const
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
bool set_comparator_func(Arg_comparator *cmp) const;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
@@ -1617,10 +4190,12 @@ public:
DBUG_ASSERT(0);
return false;
}
- bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const
+ void Item_get_date(THD *thd, Item *item,
+ Temporal::Warn *warn, MYSQL_TIME *ltime,
+ date_mode_t fuzzydate) const
{
DBUG_ASSERT(0);
- return true;
+ set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
}
longlong Item_val_int_signed_typecast(Item *item) const
{
@@ -1662,12 +4237,14 @@ public:
DBUG_ASSERT(0);
return NULL;
}
- bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
- MYSQL_TIME *,
- ulonglong fuzzydate) const
+ void Item_func_hybrid_field_type_get_date(THD *,
+ Item_func_hybrid_field_type *,
+ Temporal::Warn *,
+ MYSQL_TIME *ltime,
+ date_mode_t fuzzydate) const
{
DBUG_ASSERT(0);
- return true;
+ set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
}
String *Item_func_min_max_val_str(Item_func_min_max *, String *) const
@@ -1691,8 +4268,8 @@ public:
DBUG_ASSERT(0);
return NULL;
}
- bool Item_func_min_max_get_date(Item_func_min_max*,
- MYSQL_TIME *, ulonglong fuzzydate) const
+ bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
+ MYSQL_TIME *, date_mode_t fuzzydate) const
{
DBUG_ASSERT(0);
return true;
@@ -1773,12 +4350,20 @@ class Type_handler_numeric: public Type_handler
{
public:
String *print_item_value(THD *thd, Item *item, String *str) const;
+ bool Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *c,
+ handler *file,
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const;
double Item_func_min_max_val_real(Item_func_min_max *) const;
longlong Item_func_min_max_val_int(Item_func_min_max *) const;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
my_decimal *) const;
- bool Item_func_min_max_get_date(Item_func_min_max*,
- MYSQL_TIME *, ulonglong fuzzydate) const;
+ bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
+ MYSQL_TIME *, date_mode_t fuzzydate) const;
virtual ~Type_handler_numeric() { }
bool can_change_cond_ref_to_const(Item_bool_func2 *target,
Item *target_expr, Item *target_value,
@@ -1798,6 +4383,10 @@ public:
Item_result cmp_type() const { return REAL_RESULT; }
virtual ~Type_handler_real_result() {}
const Type_handler *type_handler_for_comparison() const;
+ void Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *c,
+ const Field *field) const;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
bool subquery_type_allows_materialization(const Item *inner,
const Item *outer,
bool is_in_predicate) const;
@@ -1806,12 +4395,17 @@ public:
void sortlength(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const;
+ bool Item_const_eq(const Item_const *a, const Item_const *b,
+ bool binary_cmp) const;
+ bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
+ Item *a, Item *b) const;
uint Item_decimal_precision(const Item *item) const;
- bool Item_save_in_value(Item *item, st_value *value) const;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
const st_value *value) const;
+ void Item_update_null_value(Item *item) const;
int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
bool set_comparator_func(Arg_comparator *cmp) const;
@@ -1829,7 +4423,8 @@ public:
bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const;
bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const;
bool Item_val_bool(Item *item) const;
- bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const;
+ void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
longlong Item_val_int_signed_typecast(Item *item) const;
longlong Item_val_int_unsigned_typecast(Item *item) const;
String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
@@ -1840,9 +4435,11 @@ public:
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
my_decimal *) const;
- bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ void Item_func_hybrid_field_type_get_date(THD *,
+ Item_func_hybrid_field_type *,
+ Temporal::Warn *,
MYSQL_TIME *,
- ulonglong fuzzydate) const;
+ date_mode_t fuzzydate) const;
longlong Item_func_between_val_int(Item_func_between *func) const;
cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const;
@@ -1864,10 +4461,19 @@ public:
class Type_handler_decimal_result: public Type_handler_numeric
{
public:
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_STRING;
+ }
Item_result result_type() const { return DECIMAL_RESULT; }
Item_result cmp_type() const { return DECIMAL_RESULT; }
virtual ~Type_handler_decimal_result() {};
const Type_handler *type_handler_for_comparison() const;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const
+ {
+ VDec item_val(item);
+ return item_val.is_null() ? 0 : my_decimal(field).cmp(item_val.ptr());
+ }
bool subquery_type_allows_materialization(const Item *inner,
const Item *outer,
bool is_in_predicate) const;
@@ -1878,10 +4484,19 @@ public:
const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const;
uint32 max_display_length(const Item *item) const;
+ uint32 Item_decimal_notation_int_digits(const Item *item) const;
Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const;
+ bool Item_const_eq(const Item_const *a, const Item_const *b,
+ bool binary_cmp) const;
+ bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
+ Item *a, Item *b) const
+ {
+ VDec va(a), vb(b);
+ return va.ptr() && vb.ptr() && !va.cmp(vb);
+ }
uint Item_decimal_precision(const Item *item) const;
- bool Item_save_in_value(Item *item, st_value *value) const;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
bool Item_param_set_from_value(THD *thd,
@@ -1892,6 +4507,7 @@ public:
{
return Item_send_str(item, protocol, buf);
}
+ void Item_update_null_value(Item *item) const;
int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
@@ -1905,10 +4521,17 @@ public:
bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const;
bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const;
bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const;
- bool Item_val_bool(Item *item) const;
- bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const;
+ bool Item_val_bool(Item *item) const
+ {
+ return VDec(item).to_bool();
+ }
+ void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
longlong Item_val_int_signed_typecast(Item *item) const;
- longlong Item_val_int_unsigned_typecast(Item *item) const;
+ longlong Item_val_int_unsigned_typecast(Item *item) const
+ {
+ return VDec(item).to_longlong(true);
+ }
String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
String *) const;
@@ -1919,9 +4542,11 @@ public:
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
my_decimal *) const;
- bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ void Item_func_hybrid_field_type_get_date(THD *,
+ Item_func_hybrid_field_type *,
+ Temporal::Warn *,
MYSQL_TIME *,
- ulonglong fuzzydate) const;
+ date_mode_t fuzzydate) const;
String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
longlong Item_func_between_val_int(Item_func_between *func) const;
cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
@@ -2070,8 +4695,11 @@ class Type_handler_int_result: public Type_handler_numeric
public:
Item_result result_type() const { return INT_RESULT; }
Item_result cmp_type() const { return INT_RESULT; }
+ bool is_order_clause_position_type() const { return true; }
+ bool is_limit_clause_valid_type() const { return true; }
virtual ~Type_handler_int_result() {}
const Type_handler *type_handler_for_comparison() const;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
bool subquery_type_allows_materialization(const Item *inner,
const Item *outer,
bool is_in_predicate) const;
@@ -2081,12 +4709,17 @@ public:
void sortlength(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const;
+ bool Item_const_eq(const Item_const *a, const Item_const *b,
+ bool binary_cmp) const;
+ bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
+ Item *a, Item *b) const;
uint Item_decimal_precision(const Item *item) const;
- bool Item_save_in_value(Item *item, st_value *value) const;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
const st_value *value) const;
+ void Item_update_null_value(Item *item) const;
int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
@@ -2101,7 +4734,8 @@ public:
bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const;
bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const;
bool Item_val_bool(Item *item) const;
- bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const;
+ void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
longlong Item_val_int_signed_typecast(Item *item) const;
longlong Item_val_int_unsigned_typecast(Item *item) const;
String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
@@ -2114,9 +4748,11 @@ public:
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
my_decimal *) const;
- bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ void Item_func_hybrid_field_type_get_date(THD *,
+ Item_func_hybrid_field_type *,
+ Temporal::Warn *,
MYSQL_TIME *,
- ulonglong fuzzydate) const;
+ date_mode_t fuzzydate) const;
String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
longlong Item_func_between_val_int(Item_func_between *func) const;
cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
@@ -2132,6 +4768,7 @@ public:
bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
+
};
@@ -2142,6 +4779,7 @@ public:
virtual const Type_limits_int *
type_limits_int_by_unsigned_flag(bool unsigned_flag) const= 0;
uint32 max_display_length(const Item *item) const;
+ uint32 Item_decimal_notation_int_digits(const Item *item) const;
bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const;
};
@@ -2160,11 +4798,22 @@ public:
void sortlength(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const;
+ bool Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *c,
+ handler *file,
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const;
+ bool Item_const_eq(const Item_const *a, const Item_const *b,
+ bool binary_cmp) const;
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
const st_value *value) const;
uint32 max_display_length(const Item *item) const;
+ uint32 Item_decimal_notation_int_digits(const Item *item) const;
bool can_change_cond_ref_to_const(Item_bool_func2 *target,
Item *target_expr, Item *target_value,
Item_bool_func2 *source,
@@ -2179,7 +4828,8 @@ public:
bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const;
bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const;
bool Item_val_bool(Item *item) const;
- bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const;
+ void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
longlong Item_val_int_signed_typecast(Item *item) const;
longlong Item_val_int_unsigned_typecast(Item *item) const;
String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
@@ -2192,22 +4842,16 @@ public:
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
my_decimal *) const;
- bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ void Item_func_hybrid_field_type_get_date(THD *,
+ Item_func_hybrid_field_type *,
+ Temporal::Warn *,
MYSQL_TIME *,
- ulonglong fuzzydate) const;
- String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
- double Item_func_min_max_val_real(Item_func_min_max *) const;
- longlong Item_func_min_max_val_int(Item_func_min_max *) const;
- my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
- my_decimal *) const;
- bool Item_func_min_max_get_date(Item_func_min_max*,
- MYSQL_TIME *, ulonglong fuzzydate) const;
+ date_mode_t fuzzydate) const;
+ bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
+ MYSQL_TIME *, date_mode_t fuzzydate) const;
bool Item_func_between_fix_length_and_dec(Item_func_between *func) const;
- longlong Item_func_between_val_int(Item_func_between *func) const;
bool Item_func_in_fix_comparator_compatible_types(THD *thd,
Item_func_in *) const;
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
@@ -2221,13 +4865,18 @@ public:
class Type_handler_string_result: public Type_handler
{
- uint Item_temporal_precision(Item *item, bool is_time) const;
+ uint Item_temporal_precision(THD *thd, Item *item, bool is_time) const;
public:
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_STRING;
+ }
Item_result result_type() const { return STRING_RESULT; }
Item_result cmp_type() const { return STRING_RESULT; }
CHARSET_INFO *charset_for_protocol(const Item *item) const;
virtual ~Type_handler_string_result() {}
const Type_handler *type_handler_for_comparison() const;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
const Type_handler *
type_handler_adjusted_to_max_octet_length(uint max_octet_length,
CHARSET_INFO *cs) const;
@@ -2237,27 +4886,41 @@ public:
const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const;
+ bool union_element_finalize(const Item * item) const;
+
bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const;
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
const;
uint32 max_display_length(const Item *item) const;
- uint Item_time_precision(Item *item) const
+/*
+ The next method returns 309 for long stringified doubles in scientific
+ notation, e.g. FORMAT('1e308', 2).
+*/
+ uint32 Item_decimal_notation_int_digits(const Item *item) const { return 309; }
+ bool Item_const_eq(const Item_const *a, const Item_const *b,
+ bool binary_cmp) const;
+ bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
+ Item *a, Item *b) const;
+ uint Item_time_precision(THD *thd, Item *item) const
{
- return Item_temporal_precision(item, true);
+ return Item_temporal_precision(thd, item, true);
}
- uint Item_datetime_precision(Item *item) const
+ uint Item_datetime_precision(THD *thd, Item *item) const
{
- return Item_temporal_precision(item, false);
+ return Item_temporal_precision(thd, item, false);
}
uint Item_decimal_precision(const Item *item) const;
- bool Item_save_in_value(Item *item, st_value *value) const;
+ void Item_update_null_value(Item *item) const;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
void Item_param_setup_conversion(THD *thd, Item_param *) const;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
@@ -2296,7 +4959,8 @@ public:
bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const;
bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const;
bool Item_val_bool(Item *item) const;
- bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const;
+ void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
longlong Item_val_int_signed_typecast(Item *item) const;
longlong Item_val_int_unsigned_typecast(Item *item) const;
String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
@@ -2309,16 +4973,18 @@ public:
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
my_decimal *) const;
- bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ void Item_func_hybrid_field_type_get_date(THD *,
+ Item_func_hybrid_field_type *,
+ Temporal::Warn *,
MYSQL_TIME *,
- ulonglong fuzzydate) const;
+ date_mode_t fuzzydate) const;
String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
double Item_func_min_max_val_real(Item_func_min_max *) const;
longlong Item_func_min_max_val_int(Item_func_min_max *) const;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
my_decimal *) const;
- bool Item_func_min_max_get_date(Item_func_min_max*,
- MYSQL_TIME *, ulonglong fuzzydate) const;
+ bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
+ MYSQL_TIME *, date_mode_t fuzzydate) const;
bool Item_func_between_fix_length_and_dec(Item_func_between *func) const;
longlong Item_func_between_val_int(Item_func_between *func) const;
bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const;
@@ -2343,6 +5009,12 @@ class Type_handler_general_purpose_string: public Type_handler_string_result
public:
bool is_general_purpose_string_type() const { return true; }
bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const;
+ bool Column_definition_bulk_alter(Column_definition *c,
+ const Column_derived_attributes
+ *derived_attr,
+ const Column_bulk_alter_attributes
+ *bulk_alter_attr)
+ const;
};
@@ -2375,6 +5047,10 @@ public:
virtual ~Type_handler_tiny() {}
const Name name() const { return m_name_tiny; }
enum_field_types field_type() const { return MYSQL_TYPE_TINY; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_TINY;
+ }
const Type_limits_int *type_limits_int_by_unsigned_flag(bool unsigned_fl) const
{
return unsigned_fl ? &m_limits_uint8 : &m_limits_sint8;
@@ -2395,6 +5071,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
};
@@ -2409,6 +5092,10 @@ public:
virtual ~Type_handler_short() {}
const Name name() const { return m_name_short; }
enum_field_types field_type() const { return MYSQL_TYPE_SHORT; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_SHORT;
+ }
bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
{
return Item_send_short(item, protocol, buf);
@@ -2429,6 +5116,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
};
@@ -2443,6 +5137,10 @@ public:
virtual ~Type_handler_long() {}
const Name name() const { return m_name_int; }
enum_field_types field_type() const { return MYSQL_TYPE_LONG; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_LONG;
+ }
const Type_limits_int *type_limits_int_by_unsigned_flag(bool unsigned_fl) const
{
return unsigned_fl ? &m_limits_uint32 : &m_limits_sint32;
@@ -2463,11 +5161,29 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
};
+class Type_handler_bool: public Type_handler_long
+{
+ static const Name m_name_bool;
+public:
+ const Name name() const { return m_name_bool; }
+ bool is_bool_type() const { return true; }
+ void Item_update_null_value(Item *item) const;
+ bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const;
+};
+
+
class Type_handler_longlong: public Type_handler_general_purpose_int
{
static const Name m_name_longlong;
@@ -2477,6 +5193,10 @@ public:
virtual ~Type_handler_longlong() {}
const Name name() const { return m_name_longlong; }
enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_LONGLONG;
+ }
const Type_limits_int *type_limits_int_by_unsigned_flag(bool unsigned_fl) const
{
return unsigned_fl ? &m_limits_uint64 : &m_limits_sint64;
@@ -2501,6 +5221,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
};
@@ -2526,6 +5253,10 @@ public:
virtual ~Type_handler_int24() {}
const Name name() const { return m_name_mediumint; }
enum_field_types field_type() const { return MYSQL_TYPE_INT24; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_LONG;
+ }
bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
{
return Item_send_long(item, protocol, buf);
@@ -2546,6 +5277,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -2556,7 +5294,12 @@ public:
virtual ~Type_handler_year() {}
const Name name() const { return m_name_year; }
enum_field_types field_type() const { return MYSQL_TYPE_YEAR; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_SHORT;
+ }
uint32 max_display_length(const Item *item) const;
+ uint32 Item_decimal_notation_int_digits(const Item *item) const { return 4; };
uint32 calc_pack_length(uint32 length) const { return 1; }
bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
{
@@ -2565,6 +5308,9 @@ public:
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
bool Column_definition_fix_attributes(Column_definition *c) const;
+ void Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *c,
+ const Field *field) const;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
ulonglong table_flags) const
@@ -2573,8 +5319,23 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
- bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
+ void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
+ void Item_func_hybrid_field_type_get_date(THD *,
+ Item_func_hybrid_field_type *item,
+ Temporal::Warn *,
+ MYSQL_TIME *to,
+ date_mode_t fuzzydate) const;
};
@@ -2585,7 +5346,13 @@ public:
virtual ~Type_handler_bit() {}
const Name name() const { return m_name_bit; }
enum_field_types field_type() const { return MYSQL_TYPE_BIT; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_STRING;
+ }
uint32 max_display_length(const Item *item) const;
+ uint32 Item_decimal_notation_int_digits(const Item *item) const;
+ static uint32 Bit_decimal_notation_int_digits_by_nbits(uint nbits);
uint32 calc_pack_length(uint32 length) const { return length / 8; }
bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
{
@@ -2595,6 +5362,8 @@ public:
{
return print_item_value_csstr(thd, item, str);
}
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
bool Column_definition_fix_attributes(Column_definition *c) const;
@@ -2602,11 +5371,13 @@ public:
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const;
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
const;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
@@ -2615,6 +5386,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const;
};
@@ -2626,8 +5404,13 @@ public:
virtual ~Type_handler_float() {}
const Name name() const { return m_name_float; }
enum_field_types field_type() const { return MYSQL_TYPE_FLOAT; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_FLOAT;
+ }
bool type_can_have_auto_increment_attribute() const { return true; }
uint32 max_display_length(const Item *item) const { return 25; }
+ uint32 Item_decimal_notation_int_digits(const Item *item) const { return 39; }
uint32 calc_pack_length(uint32 length) const { return sizeof(float); }
Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const;
@@ -2647,6 +5430,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
@@ -2664,8 +5454,13 @@ public:
virtual ~Type_handler_double() {}
const Name name() const { return m_name_double; }
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_DOUBLE;
+ }
bool type_can_have_auto_increment_attribute() const { return true; }
uint32 max_display_length(const Item *item) const { return 53; }
+ uint32 Item_decimal_notation_int_digits(const Item *item) const { return 309; }
uint32 calc_pack_length(uint32 length) const { return sizeof(double); }
Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const;
@@ -2684,6 +5479,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
@@ -2701,12 +5503,26 @@ public:
virtual ~Type_handler_time_common() { }
const Name name() const { return m_name_time; }
enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_TIME;
+ }
enum_mysql_timestamp_type mysql_timestamp_type() const
{
return MYSQL_TIMESTAMP_TIME;
}
+ bool is_val_native_ready() const { return true; }
+ const Type_handler *type_handler_for_native_format() const;
+ int cmp_native(const Native &a, const Native &b) const;
+ bool Item_val_native_with_conversion(THD *thd, Item *, Native *to) const;
+ bool Item_val_native_with_conversion_result(THD *thd, Item *, Native *to) const;
+ bool Item_param_val_native(THD *thd, Item_param *item, Native *to) const;
+ Item_literal *create_literal_item(THD *thd, const char *str, size_t length,
+ CHARSET_INFO *cs, bool send_error) const;
Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const;
+ bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
+ Item *a, Item *b) const;
uint Item_decimal_scale(const Item *item) const
{
return Item_decimal_scale_with_seconds(item);
@@ -2717,15 +5533,19 @@ public:
return Item_divisor_precision_increment_with_seconds(item);
}
const Type_handler *type_handler_for_comparison() const;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
+ void Column_definition_implicit_upgrade(Column_definition *c) const;
bool Column_definition_fix_attributes(Column_definition *c) const;
- bool Item_save_in_value(Item *item, st_value *value) const;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
{
return Item_send_time(item, protocol, buf);
}
+ void Item_update_null_value(Item *item) const;
int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
String *print_item_value(THD *thd, Item *item, String *str) const;
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
+ longlong Item_val_int_unsigned_typecast(Item *item) const;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
@@ -2740,11 +5560,21 @@ public:
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
my_decimal *) const;
- bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *,
+ void Item_func_hybrid_field_type_get_date(THD *,
+ Item_func_hybrid_field_type *,
+ Temporal::Warn *,
MYSQL_TIME *,
- ulonglong fuzzydate) const;
- bool Item_func_min_max_get_date(Item_func_min_max*,
- MYSQL_TIME *, ulonglong fuzzydate) const;
+ date_mode_t fuzzydate) const;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
+ double Item_func_min_max_val_real(Item_func_min_max *) const;
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const;
+ my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
+ my_decimal *) const;
+ bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
+ MYSQL_TIME *, date_mode_t fuzzydate) const;
+ longlong Item_func_between_val_int(Item_func_between *func) const;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
bool set_comparator_func(Arg_comparator *cmp) const;
cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
@@ -2761,6 +5591,7 @@ class Type_handler_time: public Type_handler_time_common
public:
static uint hires_bytes(uint dec) { return m_hires_bytes[dec]; }
virtual ~Type_handler_time() {}
+ const Name version() const { return m_version_mariadb53; }
uint32 calc_pack_length(uint32 length) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
@@ -2772,6 +5603,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -2779,6 +5617,7 @@ class Type_handler_time2: public Type_handler_time_common
{
public:
virtual ~Type_handler_time2() {}
+ const Name version() const { return m_version_mysql56; }
enum_field_types real_field_type() const { return MYSQL_TYPE_TIME2; }
uint32 calc_pack_length(uint32 length) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
@@ -2791,6 +5630,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -2798,16 +5644,23 @@ class Type_handler_temporal_with_date: public Type_handler_temporal_result
{
public:
virtual ~Type_handler_temporal_with_date() {}
- bool Item_save_in_value(Item *item, st_value *value) const;
+ Item_literal *create_literal_item(THD *thd, const char *str, size_t length,
+ CHARSET_INFO *cs, bool send_error) const;
+ bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
+ Item *a, Item *b) const;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
{
return Item_send_date(item, protocol, buf);
}
+ void Item_update_null_value(Item *item) const;
int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
bool set_comparator_func(Arg_comparator *cmp) const;
cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const;
+ longlong Item_func_between_val_int(Item_func_between *func) const;
};
@@ -2819,21 +5672,40 @@ public:
const Name name() const { return m_name_date; }
const Type_handler *type_handler_for_comparison() const;
enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_DATE;
+ }
enum_mysql_timestamp_type mysql_timestamp_type() const
{
return MYSQL_TIMESTAMP_DATE;
}
+ bool cond_notnull_field_isnull_to_field_eq_zero() const
+ {
+ return true;
+ }
+ Item_literal *create_literal_item(THD *thd, const char *str, size_t length,
+ CHARSET_INFO *cs, bool send_error) const;
Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const;
bool Column_definition_fix_attributes(Column_definition *c) const;
uint Item_decimal_precision(const Item *item) const;
String *print_item_value(THD *thd, Item *item, String *str) const;
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
+ double Item_func_min_max_val_real(Item_func_min_max *) const;
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const;
+ my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
+ my_decimal *) const;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
Item **items, uint nitems) const;
+ bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
+ Item **items, uint nitems) const;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
};
@@ -2853,6 +5725,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -2872,6 +5751,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -2883,12 +5769,21 @@ public:
const Name name() const { return m_name_datetime; }
const Type_handler *type_handler_for_comparison() const;
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_DATETIME;
+ }
enum_mysql_timestamp_type mysql_timestamp_type() const
{
return MYSQL_TIMESTAMP_DATETIME;
}
+ bool cond_notnull_field_isnull_to_field_eq_zero() const
+ {
+ return true;
+ }
Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const;
+ void Column_definition_implicit_upgrade(Column_definition *c) const;
bool Column_definition_fix_attributes(Column_definition *c) const;
uint Item_decimal_scale(const Item *item) const
{
@@ -2905,6 +5800,13 @@ public:
}
String *print_item_value(THD *thd, Item *item, String *str) const;
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
+ double Item_func_min_max_val_real(Item_func_min_max *) const;
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const;
+ my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
+ my_decimal *) const;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
@@ -2922,6 +5824,7 @@ class Type_handler_datetime: public Type_handler_datetime_common
public:
static uint hires_bytes(uint dec) { return m_hires_bytes[dec]; }
virtual ~Type_handler_datetime() {}
+ const Name version() const { return m_version_mariadb53; }
uint32 calc_pack_length(uint32 length) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
@@ -2933,6 +5836,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -2940,6 +5850,7 @@ class Type_handler_datetime2: public Type_handler_datetime_common
{
public:
virtual ~Type_handler_datetime2() {}
+ const Name version() const { return m_version_mysql56; }
enum_field_types real_field_type() const { return MYSQL_TYPE_DATETIME2; }
uint32 calc_pack_length(uint32 length) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
@@ -2952,25 +5863,60 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
class Type_handler_timestamp_common: public Type_handler_temporal_with_date
{
static const Name m_name_timestamp;
+protected:
+ bool TIME_to_native(THD *, const MYSQL_TIME *from, Native *to, uint dec) const;
public:
virtual ~Type_handler_timestamp_common() {}
const Name name() const { return m_name_timestamp; }
const Type_handler *type_handler_for_comparison() const;
+ const Type_handler *type_handler_for_native_format() const;
enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; }
+ protocol_send_type_t protocol_send_type() const
+ {
+ return PROTOCOL_SEND_DATETIME;
+ }
enum_mysql_timestamp_type mysql_timestamp_type() const
{
return MYSQL_TIMESTAMP_DATETIME;
}
+ bool is_val_native_ready() const
+ {
+ return true;
+ }
bool is_timestamp_type() const
{
return true;
}
+ void Column_definition_implicit_upgrade(Column_definition *c) const;
+ bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
+ Item *a, Item *b) const;
+ bool Item_val_native_with_conversion(THD *thd, Item *, Native *to) const;
+ bool Item_val_native_with_conversion_result(THD *thd, Item *, Native *to) const;
+ bool Item_param_val_native(THD *thd, Item_param *item, Native *to) const;
+ int cmp_native(const Native &a, const Native &b) const;
+ longlong Item_func_between_val_int(Item_func_between *func) const;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
+ cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
+ in_vector *make_in_vector(THD *thd, const Item_func_in *f, uint nargs) const;
+ void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const;
+ void sortlength(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *attr) const;
bool Column_definition_fix_attributes(Column_definition *c) const;
uint Item_decimal_scale(const Item *item) const
{
@@ -2983,10 +5929,18 @@ public:
}
bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
{
- return Item_send_datetime(item, protocol, buf);
+ return Item_send_timestamp(item, protocol, buf);
}
+ int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
String *print_item_value(THD *thd, Item *item, String *str) const;
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
+ Item_copy *create_item_copy(THD *thd, Item *item) const;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
+ double Item_func_min_max_val_real(Item_func_min_max *) const;
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const;
+ my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
+ my_decimal *) const;
+ bool set_comparator_func(Arg_comparator *cmp) const;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
@@ -2994,6 +5948,8 @@ public:
Item **items, uint nitems) const;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
+ bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
+ MYSQL_TIME *, date_mode_t fuzzydate) const;
};
@@ -3004,6 +5960,7 @@ class Type_handler_timestamp: public Type_handler_timestamp_common
public:
static uint sec_part_bytes(uint dec) { return m_sec_part_bytes[dec]; }
virtual ~Type_handler_timestamp() {}
+ const Name version() const { return m_version_mariadb53; }
uint32 calc_pack_length(uint32 length) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
@@ -3015,6 +5972,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -3022,6 +5986,7 @@ class Type_handler_timestamp2: public Type_handler_timestamp_common
{
public:
virtual ~Type_handler_timestamp2() {}
+ const Name version() const { return m_version_mysql56; }
enum_field_types real_field_type() const { return MYSQL_TYPE_TIMESTAMP2; }
uint32 calc_pack_length(uint32 length) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
@@ -3036,6 +6001,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -3060,6 +6032,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -3078,11 +6057,13 @@ public:
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const;
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
const;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
@@ -3091,6 +6072,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -3106,7 +6094,9 @@ public:
const Type_handler *type_handler_for_union(const Item *) const;
uint32 max_display_length(const Item *item) const { return 0; }
uint32 calc_pack_length(uint32 length) const { return 0; }
- bool Item_save_in_value(Item *item, st_value *value) const;
+ bool Item_const_eq(const Item_const *a, const Item_const *b,
+ bool binary_cmp) const;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
bool Item_send(Item *item, Protocol *protocol, st_value *buf) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
@@ -3115,11 +6105,13 @@ public:
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const;
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
const;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
@@ -3129,6 +6121,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -3165,6 +6164,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -3177,10 +6183,15 @@ public:
const Name name() const { return m_name_var_string; }
enum_field_types field_type() const { return MYSQL_TYPE_VAR_STRING; }
enum_field_types real_field_type() const { return MYSQL_TYPE_STRING; }
+ enum_field_types traditional_merge_field_type() const
+ {
+ return MYSQL_TYPE_VARCHAR;
+ }
const Type_handler *type_handler_for_tmp_table(const Item *item) const
{
return varstring_type_handler(item);
}
+ void Column_definition_implicit_upgrade(Column_definition *c) const;
bool Column_definition_fix_attributes(Column_definition *c) const;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
@@ -3200,6 +6211,10 @@ public:
virtual ~Type_handler_varchar() {}
const Name name() const { return m_name_varchar; }
enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
+ enum_field_types type_code_for_protocol() const
+ {
+ return MYSQL_TYPE_VAR_STRING; // Keep things compatible for old clients
+ }
uint32 calc_pack_length(uint32 length) const
{
return (length + (length < 256 ? 1: 2));
@@ -3223,10 +6238,30 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
bool adjust_spparam_type(Spvar_definition *def, Item *from) const;
};
+class Type_handler_hex_hybrid: public Type_handler_varchar
+{
+ static const Name m_name_hex_hybrid;
+public:
+ virtual ~Type_handler_hex_hybrid() {}
+ const Name name() const { return m_name_hex_hybrid; }
+ const Type_handler *cast_to_int_type_handler() const;
+ const Type_handler *type_handler_for_system_time() const;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
+};
+
+
class Type_handler_varchar_compressed: public Type_handler_varchar
{
public:
@@ -3257,6 +6292,9 @@ public:
}
bool is_param_long_data_type() const { return true; }
bool Column_definition_fix_attributes(Column_definition *c) const;
+ void Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *c,
+ const Field *field) const;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
ulonglong table_flags) const;
@@ -3267,6 +6305,13 @@ public:
Item **items, uint nitems) const;
void Item_param_setup_conversion(THD *thd, Item_param *) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -3282,6 +6327,7 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ uint max_octet_length() const { return UINT_MAX8; }
};
@@ -3297,6 +6343,7 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ uint max_octet_length() const { return UINT_MAX24; }
};
@@ -3314,6 +6361,7 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ uint max_octet_length() const { return UINT_MAX32; }
};
@@ -3329,6 +6377,7 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ uint max_octet_length() const { return UINT_MAX16; }
};
@@ -3369,12 +6418,26 @@ public:
const st_value *value) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const;
+ bool
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options) const;
bool Column_definition_fix_attributes(Column_definition *c) const;
+ void Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *c,
+ const Field *field) const;
bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
ulonglong table_flags) const;
@@ -3383,6 +6446,14 @@ public:
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
+
bool can_return_int() const { return false; }
bool can_return_decimal() const { return false; }
bool can_return_real() const { return false; }
@@ -3428,20 +6499,27 @@ public:
enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
const Type_handler *type_handler_for_item_field() const;
const Type_handler *cast_to_int_type_handler() const;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
Item **items, uint nitems) const;
+ void Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *c,
+ const Field *field) const;
bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags,
+ const Column_derived_attributes
+ *derived_attr)
+ const;
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
- const handler *file,
- const Schema_specification_st *schema)
+ const handler *file)
const;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
@@ -3455,7 +6533,11 @@ class Type_handler_enum: public Type_handler_typelib
public:
virtual ~Type_handler_enum() {}
const Name name() const { return m_name_enum; }
- virtual enum_field_types real_field_type() const { return MYSQL_TYPE_ENUM; }
+ enum_field_types real_field_type() const { return MYSQL_TYPE_ENUM; }
+ enum_field_types traditional_merge_field_type() const
+ {
+ return MYSQL_TYPE_ENUM;
+ }
uint32 calc_pack_length(uint32 length) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
@@ -3467,6 +6549,13 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
@@ -3476,7 +6565,11 @@ class Type_handler_set: public Type_handler_typelib
public:
virtual ~Type_handler_set() {}
const Name name() const { return m_name_set; }
- virtual enum_field_types real_field_type() const { return MYSQL_TYPE_SET; }
+ enum_field_types real_field_type() const { return MYSQL_TYPE_SET; }
+ enum_field_types traditional_merge_field_type() const
+ {
+ return MYSQL_TYPE_SET;
+ }
uint32 calc_pack_length(uint32 length) const;
Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const;
@@ -3488,9 +6581,26 @@ public:
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const;
};
+// A pseudo type handler, mostly for test purposes for now
+class Type_handler_interval_DDhhmmssff: public Type_handler_long_blob
+{
+public:
+ Item *create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr) const;
+};
+
+
+
/**
A handler for hybrid type functions, e.g.
COALESCE(), IF(), IFNULL(), NULLIF(), CASE,
@@ -3584,12 +6694,14 @@ extern MYSQL_PLUGIN_IMPORT Type_handler_set type_handler_set;
extern MYSQL_PLUGIN_IMPORT Type_handler_string type_handler_string;
extern MYSQL_PLUGIN_IMPORT Type_handler_var_string type_handler_var_string;
extern MYSQL_PLUGIN_IMPORT Type_handler_varchar type_handler_varchar;
+extern MYSQL_PLUGIN_IMPORT Type_handler_hex_hybrid type_handler_hex_hybrid;
extern MYSQL_PLUGIN_IMPORT Type_handler_tiny_blob type_handler_tiny_blob;
extern MYSQL_PLUGIN_IMPORT Type_handler_medium_blob type_handler_medium_blob;
extern MYSQL_PLUGIN_IMPORT Type_handler_long_blob type_handler_long_blob;
extern MYSQL_PLUGIN_IMPORT Type_handler_blob type_handler_blob;
+extern MYSQL_PLUGIN_IMPORT Type_handler_bool type_handler_bool;
extern MYSQL_PLUGIN_IMPORT Type_handler_tiny type_handler_tiny;
extern MYSQL_PLUGIN_IMPORT Type_handler_short type_handler_short;
extern MYSQL_PLUGIN_IMPORT Type_handler_int24 type_handler_int24;
@@ -3602,6 +6714,7 @@ extern MYSQL_PLUGIN_IMPORT Type_handler_newdecimal type_handler_newdecimal;
extern MYSQL_PLUGIN_IMPORT Type_handler_olddecimal type_handler_olddecimal;
extern MYSQL_PLUGIN_IMPORT Type_handler_year type_handler_year;
+extern MYSQL_PLUGIN_IMPORT Type_handler_year type_handler_year2;
extern MYSQL_PLUGIN_IMPORT Type_handler_newdate type_handler_newdate;
extern MYSQL_PLUGIN_IMPORT Type_handler_date type_handler_date;
extern MYSQL_PLUGIN_IMPORT Type_handler_time type_handler_time;
@@ -3611,10 +6724,8 @@ extern MYSQL_PLUGIN_IMPORT Type_handler_datetime2 type_handler_datetime2;
extern MYSQL_PLUGIN_IMPORT Type_handler_timestamp type_handler_timestamp;
extern MYSQL_PLUGIN_IMPORT Type_handler_timestamp2 type_handler_timestamp2;
-extern MYSQL_PLUGIN_IMPORT Type_handler_tiny_blob type_handler_tiny_blob;
-extern MYSQL_PLUGIN_IMPORT Type_handler_blob type_handler_blob;
-extern MYSQL_PLUGIN_IMPORT Type_handler_medium_blob type_handler_medium_blob;
-extern MYSQL_PLUGIN_IMPORT Type_handler_long_blob type_handler_long_blob;
+extern MYSQL_PLUGIN_IMPORT Type_handler_interval_DDhhmmssff
+ type_handler_interval_DDhhmmssff;
class Type_aggregator
{
diff --git a/sql/sql_type_int.h b/sql/sql_type_int.h
index 3e5e6a345fa..d3e9d0318cf 100644
--- a/sql/sql_type_int.h
+++ b/sql/sql_type_int.h
@@ -1,5 +1,4 @@
-/* Copyright (c) 2006, 2010, Oracle and/or its affiliates.
- Copyright (c) 2011, 2016, MariaDB
+/* Copyright (c) 2018, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,11 +17,39 @@
#define SQL_TYPE_INT_INCLUDED
-// A longlong/ulonglong hybrid. Good to store results of val_int().
-class Longlong_hybrid
+class Null_flag
+{
+protected:
+ bool m_is_null;
+public:
+ bool is_null() const { return m_is_null; }
+ Null_flag(bool is_null) :m_is_null(is_null) { }
+};
+
+
+class Longlong
{
protected:
longlong m_value;
+public:
+ longlong value() const { return m_value; }
+ Longlong(longlong nr) :m_value(nr) { }
+};
+
+
+class Longlong_null: public Longlong, public Null_flag
+{
+public:
+ Longlong_null(longlong nr, bool is_null)
+ :Longlong(nr), Null_flag(is_null)
+ { }
+};
+
+
+// A longlong/ulonglong hybrid. Good to store results of val_int().
+class Longlong_hybrid: public Longlong
+{
+protected:
bool m_unsigned;
int cmp_signed(const Longlong_hybrid& other) const
{
@@ -35,9 +62,8 @@ protected:
}
public:
Longlong_hybrid(longlong nr, bool unsigned_flag)
- :m_value(nr), m_unsigned(unsigned_flag)
+ :Longlong(nr), m_unsigned(unsigned_flag)
{ }
- longlong value() const { return m_value; }
bool is_unsigned() const { return m_unsigned; }
bool is_unsigned_outside_of_signed_range() const
{
@@ -105,4 +131,16 @@ public:
}
};
+
+class Longlong_hybrid_null: public Longlong_hybrid,
+ public Null_flag
+{
+public:
+ Longlong_hybrid_null(const Longlong_null &nr, bool unsigned_flag)
+ :Longlong_hybrid(nr.value(), unsigned_flag),
+ Null_flag(nr.is_null())
+ { }
+};
+
+
#endif // SQL_TYPE_INT_INCLUDED
diff --git a/sql/sql_type_json.cc b/sql/sql_type_json.cc
new file mode 100644
index 00000000000..f53a247d816
--- /dev/null
+++ b/sql/sql_type_json.cc
@@ -0,0 +1,55 @@
+/*
+ Copyright (c) 2019, MariaDB
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; version 2 of
+ the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "sql_type_json.h"
+#include "sql_class.h"
+
+
+Type_handler_json_longtext type_handler_json_longtext;
+
+
+/**
+ Create JSON_VALID(field_name) expression
+*/
+
+Virtual_column_info *
+Type_handler_json_longtext::make_json_valid_expr(THD *thd,
+ const LEX_CSTRING *field_name)
+ const
+{
+ Lex_ident_sys_st str;
+ Item *field, *expr;
+ str.set_valid_utf8(field_name);
+ if (unlikely(!(field= thd->lex->create_item_ident_field(thd, NullS, NullS,
+ &str))))
+ return 0;
+ if (unlikely(!(expr= new (thd->mem_root) Item_func_json_valid(thd, field))))
+ return 0;
+ return add_virtual_expression(thd, expr);
+}
+
+
+bool Type_handler_json_longtext::
+ Column_definition_validate_check_constraint(THD *thd,
+ Column_definition * c) const
+{
+ if (!c->check_constraint &&
+ !(c->check_constraint= make_json_valid_expr(thd, &c->field_name)))
+ return true;
+ return Type_handler::Column_definition_validate_check_constraint(thd, c);
+}
diff --git a/sql/sql_type_json.h b/sql/sql_type_json.h
new file mode 100644
index 00000000000..6c4ee8cb2eb
--- /dev/null
+++ b/sql/sql_type_json.h
@@ -0,0 +1,38 @@
+#ifndef SQL_TYPE_JSON_INCLUDED
+#define SQL_TYPE_JSON_INCLUDED
+/*
+ Copyright (c) 2019, MariaDB
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; version 2 of
+ the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "mariadb.h"
+#include "sql_type.h"
+
+class Type_handler_json_longtext: public Type_handler_long_blob
+{
+ Virtual_column_info *make_json_valid_expr(THD *thd,
+ const LEX_CSTRING *field_name)
+ const;
+public:
+ virtual ~Type_handler_json_longtext() {}
+ bool Column_definition_validate_check_constraint(THD *thd,
+ Column_definition *c) const;
+};
+
+extern MYSQL_PLUGIN_IMPORT
+ Type_handler_json_longtext type_handler_json_longtext;
+
+#endif // SQL_TYPE_JSON_INCLUDED
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index d0657cf973e..400a8ae8c30 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -78,6 +78,8 @@ static const char *init_syms(udf_func *tmp, char *nm)
(void)strmov(end, "_add");
if (!((tmp->func_add= (Udf_func_add) dlsym(tmp->dlhandle, nm))))
return nm;
+ (void)strmov(end, "_remove");
+ tmp->func_remove= (Udf_func_add) dlsym(tmp->dlhandle, nm);
}
(void) strmov(end,"_deinit");
@@ -601,6 +603,7 @@ int mysql_create_function(THD *thd,udf_func *udf)
u_d->func_deinit= udf->func_deinit;
u_d->func_clear= udf->func_clear;
u_d->func_add= udf->func_add;
+ u_d->func_remove= udf->func_remove;
/* create entry in mysql.func table */
diff --git a/sql/sql_udf.h b/sql/sql_udf.h
index 97b0f640b1e..cb1954353fa 100644
--- a/sql/sql_udf.h
+++ b/sql/sql_udf.h
@@ -47,6 +47,7 @@ typedef struct st_udf_func
Udf_func_deinit func_deinit;
Udf_func_clear func_clear;
Udf_func_add func_add;
+ Udf_func_add func_remove;
ulong usage_count;
} udf_func;
@@ -131,6 +132,20 @@ class udf_handler :public Sql_alloc
func(&initid, &f_args, &is_null, &error);
*null_value= (my_bool) (is_null || error);
}
+ bool supports_removal() const
+ { return MY_TEST(u_d->func_remove); }
+ void remove(my_bool *null_value)
+ {
+ DBUG_ASSERT(u_d->func_remove);
+ if (get_arguments())
+ {
+ *null_value=1;
+ return;
+ }
+ Udf_func_add func= u_d->func_remove;
+ func(&initid, &f_args, &is_null, &error);
+ *null_value= (my_bool) (is_null || error);
+ }
String *val_str(String *str,String *save_str);
};
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index af6f5233972..b295112b70d 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -68,7 +68,7 @@ void select_unit::change_select()
curr_sel= current_select_number;
/* New SELECT processing starts */
DBUG_ASSERT(table->file->inited == 0);
- step= thd->lex->current_select->linkage;
+ step= thd->lex->current_select->get_linkage();
switch (step)
{
case INTERSECT_TYPE:
@@ -248,7 +248,7 @@ bool select_unit::send_eof()
{
if (step != INTERSECT_TYPE ||
(thd->lex->current_select->next_select() &&
- thd->lex->current_select->next_select()->linkage == INTERSECT_TYPE))
+ thd->lex->current_select->next_select()->get_linkage() == INTERSECT_TYPE))
{
/*
it is not INTESECT or next SELECT in the sequence is INTERSECT so no
@@ -744,11 +744,11 @@ bool st_select_lex_unit::join_union_type_attributes(THD *thd_arg,
been fixed yet. An Item_type_holder must be created based on a fixed
Item, so use the inner Item instead.
*/
- DBUG_ASSERT(item_tmp->fixed ||
+ DBUG_ASSERT(item_tmp->is_fixed() ||
(item_tmp->type() == Item::REF_ITEM &&
((Item_ref *)(item_tmp))->ref_type() ==
Item_ref::OUTER_REF));
- if (!item_tmp->fixed)
+ if (!item_tmp->is_fixed())
item_tmp= item_tmp->real_item();
holders[holder_pos].add_argument(item_tmp);
}
@@ -822,8 +822,8 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
bool is_union_select;
bool have_except= FALSE, have_intersect= FALSE;
bool instantiate_tmp_table= false;
- bool single_tvc= !first_sl->next_select() && first_sl->tvc &&
- !fake_select_lex;
+ bool single_tvc= !first_sl->next_select() && first_sl->tvc;
+ bool single_tvc_wo_order= single_tvc && !first_sl->order_list.elements;
DBUG_ENTER("st_select_lex_unit::prepare");
DBUG_ASSERT(thd == current_thd);
@@ -935,8 +935,9 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
if (is_union_select || is_recursive)
{
- if ((is_unit_op() && !union_needs_tmp_table() &&
- !have_except && !have_intersect) || single_tvc)
+ if ((single_tvc_wo_order && !fake_select_lex) ||
+ (is_unit_op() && !union_needs_tmp_table() &&
+ !have_except && !have_intersect && !single_tvc))
{
SELECT_LEX *last= first_select();
while (last->next_select())
@@ -951,7 +952,7 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
else
{
if (!is_recursive)
- union_result= new (thd->mem_root) select_unit(thd);
+ union_result= new (thd->mem_root) select_unit(thd);
else
{
with_element->rec_result=
@@ -1005,7 +1006,47 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
types= first_sl->item_list;
goto cont;
}
-
+
+ if (sl->tvc && sl->order_list.elements &&
+ !sl->tvc->to_be_wrapped_as_with_tail())
+ {
+ SELECT_LEX_UNIT *unit= sl->master_unit();
+ if (thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)
+ {
+ unit->fake_select_lex= 0;
+ unit->saved_fake_select_lex= 0;
+ }
+ else
+ {
+ if (!unit->first_select()->next_select())
+ {
+ if (!unit->fake_select_lex)
+ {
+ Query_arena *arena, backup_arena;
+ arena= thd->activate_stmt_arena_if_needed(&backup_arena);
+ bool rc= unit->add_fake_select_lex(thd);
+ if (arena)
+ thd->restore_active_arena(arena, &backup_arena);
+ if (rc)
+ goto err;
+ }
+ SELECT_LEX *fake= unit->fake_select_lex;
+ fake->order_list= sl->order_list;
+ fake->explicit_limit= sl->explicit_limit;
+ fake->select_limit= sl->select_limit;
+ fake->offset_limit= sl->offset_limit;
+ sl->order_list.empty();
+ sl->explicit_limit= 0;
+ sl->select_limit= 0;
+ sl->offset_limit= 0;
+ if (describe)
+ fake->options|= SELECT_DESCRIBE;
+ }
+ else if (!sl->explicit_limit)
+ sl->order_list.empty();
+ }
+ }
+
for (;sl; sl= sl->next_select(), union_part_count++)
{
if (sl->tvc)
@@ -1027,7 +1068,7 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
goto err;
}
else if (sl->tvc->prepare(thd, sl, tmp_result, this))
- goto err;
+ goto err;
}
else if (prepare_join(thd, sl, tmp_result, additional_options,
is_union_select))
@@ -1149,12 +1190,12 @@ cont:
while ((type= tp++))
{
- if (type->cmp_type() == STRING_RESULT &&
- type->collation.derivation == DERIVATION_NONE)
- {
- my_error(ER_CANT_AGGREGATE_NCOLLATIONS, MYF(0), "UNION");
+ /*
+ Test if the aggregated data type is OK for a UNION element.
+ E.g. in case of string data, DERIVATION_NONE is not allowed.
+ */
+ if (type->type_handler()->union_element_finalize(type))
goto err;
- }
}
/*
@@ -1496,7 +1537,7 @@ bool st_select_lex_unit::exec()
union_result->change_select();
if (fake_select_lex)
{
- if (sl != &thd->lex->select_lex)
+ if (sl != thd->lex->first_select_lex())
fake_select_lex->uncacheable|= sl->uncacheable;
else
fake_select_lex->uncacheable= 0;
@@ -2049,6 +2090,8 @@ bool st_select_lex::cleanup()
bool error= FALSE;
DBUG_ENTER("st_select_lex::cleanup()");
+ DBUG_PRINT("info", ("select: %p (%u) JOIN %p",
+ this, select_number, join));
cleanup_order(order_list.first);
cleanup_order(group_list.first);
cleanup_ftfuncs(this);
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index eeed815b716..85db99f3a62 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates.
- Copyright (c) 2011, 2016, MariaDB
+ Copyright (c) 2011, 2021, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -137,7 +137,8 @@ bool compare_record(const TABLE *table)
FALSE Items are OK
*/
-static bool check_fields(THD *thd, List<Item> &items, bool update_view)
+static bool check_fields(THD *thd, TABLE_LIST *table, List<Item> &items,
+ bool update_view)
{
Item *item;
if (update_view)
@@ -182,6 +183,34 @@ static bool check_fields(THD *thd, List<Item> &items, bool update_view)
f->set_has_explicit_value();
}
}
+
+ if (table->has_period())
+ {
+ if (table->is_view_or_derived())
+ {
+ my_error(ER_IT_IS_A_VIEW, MYF(0), table->table_name.str);
+ return TRUE;
+ }
+ if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI)
+ {
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0),
+ "updating and querying the same temporal periods table");
+
+ return true;
+ }
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_UPDATE);
+ for (List_iterator_fast<Item> it(items); (item=it++);)
+ {
+ Field *f= item->field_for_view_update()->field;
+ vers_select_conds_t &period= table->period_conditions;
+ if (period.field_start->field == f || period.field_end->field == f)
+ {
+ my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0),
+ item->name.str, period.name.str);
+ return true;
+ }
+ }
+ }
return FALSE;
}
@@ -283,6 +312,34 @@ static void prepare_record_for_error_message(int error, TABLE *table)
}
+static
+int cut_fields_for_portion_of_time(THD *thd, TABLE *table,
+ const vers_select_conds_t &period_conds)
+{
+ bool lcond= period_conds.field_start->val_datetime_packed(thd)
+ < period_conds.start.item->val_datetime_packed(thd);
+ bool rcond= period_conds.field_end->val_datetime_packed(thd)
+ > period_conds.end.item->val_datetime_packed(thd);
+
+ Field *start_field= table->field[table->s->period.start_fieldno];
+ Field *end_field= table->field[table->s->period.end_fieldno];
+
+ int res= 0;
+ if (lcond)
+ {
+ res= period_conds.start.item->save_in_field(start_field, true);
+ start_field->set_has_explicit_value();
+ }
+
+ if (likely(!res) && rcond)
+ {
+ res= period_conds.end.item->save_in_field(end_field, true);
+ end_field->set_has_explicit_value();
+ }
+
+ return res;
+}
+
/*
Process usual UPDATE
@@ -333,7 +390,7 @@ int mysql_update(THD *thd,
SQL_SELECT *select= NULL;
SORT_INFO *file_sort= 0;
READ_RECORD info;
- SELECT_LEX *select_lex= &thd->lex->select_lex;
+ SELECT_LEX *select_lex= thd->lex->first_select_lex();
ulonglong id;
List<Item> all_fields;
killed_state killed_status= NOT_KILLED;
@@ -345,7 +402,7 @@ int mysql_update(THD *thd,
query_plan.using_filesort= FALSE;
// For System Versioning (may need to insert new fields to a table).
- ha_rows updated_sys_ver= 0;
+ ha_rows rows_inserted= 0;
DBUG_ENTER("mysql_update");
@@ -357,6 +414,12 @@ int mysql_update(THD *thd,
if (mysql_handle_derived(thd->lex, DT_INIT))
DBUG_RETURN(1);
+ if (table_list->has_period() && table_list->is_view_or_derived())
+ {
+ my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str);
+ DBUG_RETURN(TRUE);
+ }
+
if (((update_source_table=unique_table(thd, table_list,
table_list->next_global, 0)) ||
table_list->is_multitable()))
@@ -365,6 +428,14 @@ int mysql_update(THD *thd,
DBUG_PRINT("info", ("Switch to multi-update"));
/* pass counter value */
thd->lex->table_count= table_count;
+ if (thd->lex->period_conditions.is_set())
+ {
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0),
+ "updating and querying the same temporal periods table");
+
+ DBUG_RETURN(1);
+ }
+
/* convert to multiupdate */
DBUG_RETURN(2);
}
@@ -392,7 +463,7 @@ int mysql_update(THD *thd,
table->covering_keys= table->s->keys_in_use;
table->quick_keys.clear_all();
- query_plan.select_lex= &thd->lex->select_lex;
+ query_plan.select_lex= thd->lex->first_select_lex();
query_plan.table= table;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Force privilege re-checking for views after they have been opened. */
@@ -402,6 +473,17 @@ int mysql_update(THD *thd,
if (mysql_prepare_update(thd, table_list, &conds, order_num, order))
DBUG_RETURN(1);
+ if (table_list->has_period())
+ {
+ if (!table_list->period_conditions.start.item->const_item()
+ || !table_list->period_conditions.end.item->const_item())
+ {
+ my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF");
+ DBUG_RETURN(true);
+ }
+ table->no_cache= true;
+ }
+
old_covering_keys= table->covering_keys; // Keys used in WHERE
/* Check the fields we are going to modify */
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -415,7 +497,7 @@ int mysql_update(THD *thd,
if (setup_fields_with_no_wrap(thd, Ref_ptr_array(),
fields, MARK_COLUMNS_WRITE, 0, 0))
DBUG_RETURN(1); /* purecov: inspected */
- if (check_fields(thd, fields, table_list->view))
+ if (check_fields(thd, table_list, fields, table_list->view))
{
DBUG_RETURN(1);
}
@@ -528,7 +610,15 @@ int mysql_update(THD *thd,
if (unlikely(init_ftfuncs(thd, select_lex, 1)))
goto err;
- table->mark_columns_needed_for_update();
+ if (table_list->has_period())
+ {
+ table->use_all_columns();
+ table->rpl_write_set= table->write_set;
+ }
+ else
+ {
+ table->mark_columns_needed_for_update();
+ }
table->update_const_key_parts(conds);
order= simple_remove_const(order, conds);
@@ -609,6 +699,14 @@ int mysql_update(THD *thd,
TRG_ACTION_BEFORE) ||
table->triggers->has_triggers(TRG_EVENT_UPDATE,
TRG_ACTION_AFTER)));
+
+ if (table_list->has_period())
+ has_triggers= table->triggers &&
+ (table->triggers->has_triggers(TRG_EVENT_INSERT,
+ TRG_ACTION_BEFORE)
+ || table->triggers->has_triggers(TRG_EVENT_INSERT,
+ TRG_ACTION_AFTER)
+ || has_triggers);
DBUG_PRINT("info", ("has_triggers: %s", has_triggers ? "TRUE" : "FALSE"));
binlog_is_row= thd->is_current_stmt_binlog_format_row();
DBUG_PRINT("info", ("binlog_is_row: %s", binlog_is_row ? "TRUE" : "FALSE"));
@@ -642,6 +740,11 @@ int mysql_update(THD *thd,
Later we also ensure that we are only using one table (no sub queries)
*/
+ DBUG_PRINT("info", ("HA_CAN_DIRECT_UPDATE_AND_DELETE: %s", (table->file->ha_table_flags() & HA_CAN_DIRECT_UPDATE_AND_DELETE) ? "TRUE" : "FALSE"));
+ DBUG_PRINT("info", ("using_io_buffer: %s", query_plan.using_io_buffer ? "TRUE" : "FALSE"));
+ DBUG_PRINT("info", ("ignore: %s", ignore ? "TRUE" : "FALSE"));
+ DBUG_PRINT("info", ("virtual_columns_marked_for_read: %s", table->check_virtual_columns_marked_for_read() ? "TRUE" : "FALSE"));
+ DBUG_PRINT("info", ("virtual_columns_marked_for_write: %s", table->check_virtual_columns_marked_for_write() ? "TRUE" : "FALSE"));
if ((table->file->ha_table_flags() & HA_CAN_DIRECT_UPDATE_AND_DELETE) &&
!has_triggers && !binlog_is_row &&
!query_plan.using_io_buffer && !ignore &&
@@ -852,11 +955,16 @@ update_begin:
if (do_direct_update)
{
/* Direct updating is supported */
+ ha_rows update_rows= 0, found_rows= 0;
DBUG_PRINT("info", ("Using direct update"));
table->reset_default_fields();
- if (unlikely(!(error= table->file->ha_direct_update_rows(&updated))))
+ if (unlikely(!(error= table->file->ha_direct_update_rows(&update_rows,
+ &found_rows))))
error= -1;
- found= updated;
+ updated= update_rows;
+ found= found_rows;
+ if (found < updated)
+ found= updated;
goto update_end;
}
@@ -894,14 +1002,25 @@ update_begin:
explain->tracker.on_record_after_where();
store_record(table,record[1]);
+ if (table_list->has_period())
+ cut_fields_for_portion_of_time(thd, table,
+ table_list->period_conditions);
+
if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0,
TRG_EVENT_UPDATE))
break; /* purecov: inspected */
found++;
- if (!can_compare_record || compare_record(table))
+ bool record_was_same= false;
+ bool need_update= !can_compare_record || compare_record(table);
+
+ if (need_update)
{
+ if (table->versioned(VERS_TIMESTAMP) &&
+ thd->lex->sql_command == SQLCOM_DELETE)
+ table->vers_update_end();
+
if ((res= table_list->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK)
{
@@ -955,17 +1074,29 @@ update_begin:
error= table->file->ha_update_row(table->record[1],
table->record[0]);
}
- if (unlikely(error == HA_ERR_RECORD_IS_THE_SAME))
+
+ record_was_same= error == HA_ERR_RECORD_IS_THE_SAME;
+ if (unlikely(record_was_same))
{
error= 0;
}
else if (likely(!error))
{
if (has_vers_fields && table->versioned(VERS_TRX_ID))
- updated_sys_ver++;
+ rows_inserted++;
updated++;
}
+ if (likely(!error) && !record_was_same && table_list->has_period())
+ {
+ store_record(table, record[2]);
+ restore_record(table, record[1]);
+ error= table->insert_portion_of_time(thd,
+ table_list->period_conditions,
+ &rows_inserted);
+ restore_record(table, record[2]);
+ }
+
if (unlikely(error) &&
(!ignore || table->file->is_fatal_error(error, HA_CHECK_ALL)))
{
@@ -977,6 +1108,7 @@ update_begin:
{
store_record(table, record[2]);
table->mark_columns_per_binlog_row_image();
+ table->clone_handler_for_update();
error= vers_insert_history_row(table);
restore_record(table, record[2]);
if (unlikely(error))
@@ -989,14 +1121,14 @@ error:
myf flags= 0;
if (table->file->is_fatal_error(error, HA_CHECK_ALL))
- flags|= ME_FATALERROR; /* Other handler errors are fatal */
+ flags|= ME_FATAL; /* Other handler errors are fatal */
prepare_record_for_error_message(error, table);
table->file->print_error(error,MYF(flags));
error= 1;
break;
}
- updated_sys_ver++;
+ rows_inserted++;
}
if (table->triggers &&
@@ -1101,7 +1233,7 @@ error:
{
/* purecov: begin inspected */
prepare_record_for_error_message(loc_error, table);
- table->file->print_error(loc_error,MYF(ME_FATALERROR));
+ table->file->print_error(loc_error,MYF(ME_FATAL));
error= 1;
/* purecov: end */
}
@@ -1120,6 +1252,8 @@ update_end:
delete select;
select= NULL;
THD_STAGE_INFO(thd, stage_end);
+ if (table_list->has_period())
+ table->file->ha_release_auto_increment();
(void) table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
/*
@@ -1182,14 +1316,14 @@ update_end:
if (likely(error < 0) && likely(!thd->lex->analyze_stmt))
{
char buff[MYSQL_ERRMSG_SIZE];
- if (!table->versioned(VERS_TIMESTAMP))
+ if (!table->versioned(VERS_TIMESTAMP) && !table_list->has_period())
my_snprintf(buff, sizeof(buff), ER_THD(thd, ER_UPDATE_INFO), (ulong) found,
(ulong) updated,
(ulong) thd->get_stmt_da()->current_statement_warn_count());
else
my_snprintf(buff, sizeof(buff),
ER_THD(thd, ER_UPDATE_INFO_WITH_SYSTEM_VERSIONING),
- (ulong) found, (ulong) updated, (ulong) updated_sys_ver,
+ (ulong) found, (ulong) updated, (ulong) rows_inserted,
(ulong) thd->get_stmt_da()->current_statement_warn_count());
my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
id, buff);
@@ -1259,7 +1393,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
TABLE *table= table_list->table;
#endif
List<Item> all_fields;
- SELECT_LEX *select_lex= &thd->lex->select_lex;
+ SELECT_LEX *select_lex= thd->lex->first_select_lex();
DBUG_ENTER("mysql_prepare_update");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -1270,6 +1404,10 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
thd->lex->allow_sum_func.clear_all();
+ if (table_list->has_period() &&
+ select_lex->period_setup_conds(thd, table_list))
+ DBUG_RETURN(true);
+
DBUG_ASSERT(table_list->table);
// conds could be cached from previous SP call
DBUG_ASSERT(!table_list->vers_conditions.need_setup() ||
@@ -1297,6 +1435,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
setup_ftfuncs(select_lex))
DBUG_RETURN(TRUE);
+
select_lex->fix_prepare_information(thd, conds, &fake_conds);
DBUG_RETURN(FALSE);
}
@@ -1555,7 +1694,7 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
DBUG_RETURN(0);
LEX *lex= thd->lex;
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
TABLE_LIST *table_list= lex->query_tables, *tl;
done= true;
@@ -1576,7 +1715,7 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
FALSE, UPDATE_ACL, SELECT_ACL, TRUE))
DBUG_RETURN(1);
- List<Item> *fields= &lex->select_lex.item_list;
+ List<Item> *fields= &lex->first_select_lex()->item_list;
if (setup_fields_with_no_wrap(thd, Ref_ptr_array(),
*fields, MARK_COLUMNS_WRITE, 0, 0))
DBUG_RETURN(1);
@@ -1586,7 +1725,7 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
if (tl->view)
break;
// ... and pass this knowlage in check_fields call
- if (check_fields(thd, *fields, tl != NULL ))
+ if (check_fields(thd, table_list, *fields, tl != NULL ))
DBUG_RETURN(1);
table_map tables_for_update= thd->table_map_for_update= get_table_map(fields);
@@ -1597,7 +1736,7 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
/*
Setup timestamp handling and locking mode
*/
- List_iterator<TABLE_LIST> ti(select_lex->leaf_tables);
+ List_iterator<TABLE_LIST> ti(lex->first_select_lex()->leaf_tables);
const bool using_lock_tables= thd->locked_tables_mode != LTM_NONE;
while ((tl= ti++))
{
@@ -1748,10 +1887,9 @@ int mysql_multi_update_prepare(THD *thd)
Check that we are not using table that we are updating, but we should
skip all tables of UPDATE SELECT itself
*/
- lex->select_lex.exclude_from_table_unique_test= TRUE;
-
+ lex->first_select_lex()->exclude_from_table_unique_test= TRUE;
/* We only need SELECT privilege for columns in the values list */
- List_iterator<TABLE_LIST> ti(lex->select_lex.leaf_tables);
+ List_iterator<TABLE_LIST> ti(lex->first_select_lex()->leaf_tables);
while ((tl= ti++))
{
if (tl->is_jtbm())
@@ -1771,7 +1909,7 @@ int mysql_multi_update_prepare(THD *thd)
Set exclude_from_table_unique_test value back to FALSE. It is needed for
further check in multi_update::prepare whether to use record cache.
*/
- lex->select_lex.exclude_from_table_unique_test= FALSE;
+ lex->first_select_lex()->exclude_from_table_unique_test= FALSE;
if (lex->save_prep_leaf_tables())
DBUG_RETURN(TRUE);
@@ -1794,7 +1932,7 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields,
DBUG_ENTER("mysql_multi_update");
if (!(*result= new (thd->mem_root) multi_update(thd, table_list,
- &thd->lex->select_lex.leaf_tables,
+ &thd->lex->first_select_lex()->leaf_tables,
fields, values, handle_duplicates, ignore)))
{
DBUG_RETURN(TRUE);
@@ -2324,11 +2462,11 @@ int multi_update::prepare2(JOIN *join)
{
if (item_rowid_table(*it2) != tbl)
continue;
- Item *fld= new (thd->mem_root)
- Item_field(thd, (*it)->get_tmp_table_field());
+ Item_field *fld= new (thd->mem_root)
+ Item_field(thd, (*it)->get_tmp_table_field());
if (!fld)
return 1;
- fld->set_result_field((*it2)->get_tmp_table_field());
+ fld->result_field= (*it2)->get_tmp_table_field();
*it2= fld;
}
}
@@ -2470,6 +2608,7 @@ int multi_update::send_data(List<Item> &not_used_values)
if (has_vers_fields && table->versioned(VERS_TIMESTAMP))
{
store_record(table, record[2]);
+ table->clone_handler_for_update();
if (unlikely(error= vers_insert_history_row(table)))
{
restore_record(table, record[2]);
@@ -2529,7 +2668,7 @@ error:
myf flags= 0;
if (table->file->is_fatal_error(error, HA_CHECK_ALL))
- flags|= ME_FATALERROR; /* Other handler errors are fatal */
+ flags|= ME_FATAL; /* Other handler errors are fatal */
prepare_record_for_error_message(error, table);
table->file->print_error(error,MYF(flags));
@@ -2610,17 +2749,10 @@ int multi_update::do_updates()
not its dependencies
*/
while(TABLE *tbl= check_opt_it++)
- {
- if (tbl->vcol_set)
- {
- bitmap_clear_all(tbl->vcol_set);
- for (Field **vf= tbl->vfield; *vf; vf++)
- {
+ if (Field **vf= tbl->vfield)
+ for (; *vf; vf++)
if (bitmap_is_set(tbl->read_set, (*vf)->field_index))
- tbl->mark_virtual_col(*vf);
- }
- }
- }
+ (*vf)->vcol_info->expr->walk(&Item::register_field_in_read_map, 1, 0);
for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
@@ -2711,10 +2843,10 @@ int multi_update::do_updates()
do
{
DBUG_ASSERT(!tmp_table->field[field_num]->is_null());
- if (unlikely((local_error=
- tbl->file->ha_rnd_pos(tbl->record[0],
- (uchar *) tmp_table->
- field[field_num]->ptr))))
+ String rowid;
+ tmp_table->field[field_num]->val_str(&rowid);
+ if (unlikely((local_error= tbl->file->ha_rnd_pos(tbl->record[0],
+ (uchar*)rowid.ptr()))))
{
err_table= tbl;
goto err;
@@ -2829,7 +2961,7 @@ int multi_update::do_updates()
err:
{
prepare_record_for_error_message(local_error, err_table);
- err_table->file->print_error(local_error,MYF(ME_FATALERROR));
+ err_table->file->print_error(local_error,MYF(ME_FATAL));
}
err2:
@@ -2938,14 +3070,18 @@ bool multi_update::send_eof()
DBUG_ASSERT(trans_safe || !updated ||
thd->transaction.stmt.modified_non_trans_table);
- if (likely(local_error != 0))
- error_handled= TRUE; // to force early leave from ::abort_result_set()
-
- if (unlikely(local_error > 0)) // if the above log write did not fail ...
+ if (unlikely(local_error))
{
- /* Safety: If we haven't got an error before (can happen in do_updates) */
- my_message(ER_UNKNOWN_ERROR, "An error occurred in multi-table update",
- MYF(0));
+ error_handled= TRUE; // to force early leave from ::abort_result_set()
+ if (thd->killed == NOT_KILLED && !thd->get_stmt_da()->is_set())
+ {
+ /*
+ No error message was sent and query was not killed (in which case
+ mysql_execute_command() will send the error mesage).
+ */
+ my_message(ER_UNKNOWN_ERROR, "An error occurred in multi-table update",
+ MYF(0));
+ }
DBUG_RETURN(TRUE);
}
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 271e018d2ab..126db90656c 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -36,6 +36,7 @@
#include "datadict.h" // dd_frm_is_view()
#include "sql_derived.h"
#include "sql_cte.h" // check_dependencies_in_with_clauses()
+#include "opt_trace.h"
#define MD5_BUFF_LENGTH 33
@@ -255,7 +256,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
LEX *lex= thd->lex;
/* first table in list is target VIEW name => cut off it */
TABLE_LIST *tbl;
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
SELECT_LEX *sl;
bool res= TRUE;
DBUG_ENTER("create_view_precheck");
@@ -326,7 +327,6 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
}
}
- if (&lex->select_lex != lex->all_selects_list)
{
/* check tables of subqueries */
for (tbl= tables; tbl; tbl= tbl->next_global)
@@ -401,7 +401,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
TABLE_LIST *view= lex->unlink_first_table(&link_to_local);
TABLE_LIST *tables= lex->query_tables;
TABLE_LIST *tbl;
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
SELECT_LEX *sl;
SELECT_LEX_UNIT *unit= &lex->unit;
bool res= FALSE;
@@ -712,10 +712,11 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
lex->link_first_table_back(view, link_to_local);
DBUG_RETURN(0);
-
-WSREP_ERROR_LABEL:
- res= TRUE;
+#ifdef WITH_WSREP
+wsrep_error_label:
+ res= true;
goto err_no_relink;
+#endif
err:
lex->link_first_table_back(view, link_to_local);
@@ -991,7 +992,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
view->algorithm != VIEW_ALGORITHM_TMPTABLE)))
{
/* TODO: change here when we will support UNIONs */
- for (TABLE_LIST *tbl= lex->select_lex.table_list.first;
+ for (TABLE_LIST *tbl= lex->first_select_lex()->table_list.first;
tbl;
tbl= tbl->next_local)
{
@@ -1110,8 +1111,8 @@ loop_out:
UNION
*/
if (view->updatable_view &&
- !lex->select_lex.master_unit()->is_unit_op() &&
- !(lex->select_lex.table_list.first)->next_local &&
+ !lex->first_select_lex()->master_unit()->is_unit_op() &&
+ !(lex->first_select_lex()->table_list.first)->next_local &&
find_table_in_global_list(lex->query_tables->next_global,
&lex->query_tables->db,
&lex->query_tables->table_name))
@@ -1158,7 +1159,8 @@ err:
bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
bool open_view_no_parse)
{
- SELECT_LEX *end, *UNINIT_VAR(view_select);
+ SELECT_LEX_NODE *end;
+ SELECT_LEX *UNINIT_VAR(view_select);
LEX *old_lex, *lex;
Query_arena *arena, backup;
TABLE_LIST *top_view= table->top_table();
@@ -1201,7 +1203,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
in which case the reinit call wasn't done.
See MDEV-6668 for details.
*/
- mysql_derived_reinit(thd, NULL, table);
+ mysql_handle_single_derived(thd->lex, table, DT_REINIT);
DEBUG_SYNC(thd, "after_cached_view_opened");
DBUG_RETURN(0);
@@ -1357,8 +1359,6 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
lex_start(thd);
lex->stmt_lex= old_lex;
- view_select= &lex->select_lex;
- view_select->select_number= ++thd->lex->stmt_lex->current_select_number;
sql_mode_t saved_mode= thd->variables.sql_mode;
/* switch off modes which can prevent normal parsing of VIEW
@@ -1393,6 +1393,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
parse_status= parse_sql(thd, & parser_state, table->view_creation_ctx);
+ view_select= lex->first_select_lex();
+
/* Restore environment. */
if ((old_lex->sql_command == SQLCOM_SHOW_FIELDS) ||
@@ -1415,6 +1417,15 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
goto err;
/*
+ Check rights to run commands which show underlying tables.
+ In the optimizer trace we would not like to show trace for
+ cases when the current user does not have rights for the
+ underlying tables.
+ */
+ if (!table->prelocking_placeholder)
+ opt_trace_disable_if_no_view_access(thd, table, view_tables);
+
+ /*
Check rights to run commands (ANALYZE SELECT, EXPLAIN SELECT &
SHOW CREATE) which show underlying tables.
Skip this step if we are opening view for prelocking only.
@@ -1543,7 +1554,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
This may change in future, for example if we enable merging of
views with subqueries in select list.
*/
- view_main_select_tables= lex->select_lex.table_list.first;
+ view_main_select_tables= lex->first_select_lex()->table_list.first;
/*
Mergeable view can be used for inserting, so we move the flag down
*/
@@ -1583,7 +1594,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
/* Fields in this view can be used in upper select in case of merge. */
if (table->select_lex)
- table->select_lex->add_where_field(&lex->select_lex);
+ table->select_lex->add_where_field(lex->first_select_lex());
}
/*
This method has a dependency on the proper lock type being set,
@@ -1605,8 +1616,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
old_lex->safe_to_cache_query= (old_lex->safe_to_cache_query &&
lex->safe_to_cache_query);
/* move SQL_CACHE to whole query */
- if (view_select->options & OPTION_TO_QUERY_CACHE)
- old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
+ if (lex->first_select_lex()->options & OPTION_TO_QUERY_CACHE)
+ old_lex->first_select_lex()->options|= OPTION_TO_QUERY_CACHE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (table->view_suid)
@@ -1688,9 +1699,10 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
tbl->grant.want_privilege= top_view->grant.orig_want_privilege;
/* prepare view context */
- lex->select_lex.context.resolve_in_table_list_only(view_main_select_tables);
- lex->select_lex.context.outer_context= 0;
- lex->select_lex.select_n_having_items+=
+ lex->first_select_lex()->
+ context.resolve_in_table_list_only(view_main_select_tables);
+ lex->first_select_lex()->context.outer_context= 0;
+ lex->first_select_lex()->select_n_having_items+=
table->select_lex->select_n_having_items;
table->where= view_select->where;
@@ -1701,12 +1713,13 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
*/
if (!table->select_lex->master_unit()->is_unit_op() &&
table->select_lex->order_list.elements == 0)
- table->select_lex->order_list.push_back(&lex->select_lex.order_list);
+ table->select_lex->order_list.
+ push_back(&lex->first_select_lex()->order_list);
else
{
if (old_lex->sql_command == SQLCOM_SELECT &&
(old_lex->describe & DESCRIBE_EXTENDED) &&
- lex->select_lex.order_list.elements &&
+ lex->first_select_lex()->order_list.elements &&
!table->select_lex->master_unit()->is_unit_op())
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
@@ -1740,7 +1753,11 @@ ok:
lex->unit.include_down(table->select_lex);
lex->unit.slave= view_select; // fix include_down initialisation
/* global SELECT list linking */
- end= view_select; // primary SELECT_LEX is always last
+ /*
+ The primary SELECT_LEX is always last (because parsed first) if WITH not
+ used, otherwise it is good start point for last element finding
+ */
+ for (end= view_select; end->link_next; end= end->link_next);
end->link_next= old_lex->all_selects_list;
old_lex->all_selects_list->link_prev= &end->link_next;
old_lex->all_selects_list= lex->all_selects_list;
@@ -1925,7 +1942,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
*/
if ((!view->view && !view->belong_to_view) ||
thd->lex->sql_command == SQLCOM_INSERT ||
- thd->lex->select_lex.select_limit == 0)
+ thd->lex->first_select_lex()->select_limit == 0)
DBUG_RETURN(FALSE); /* it is normal table or query without LIMIT */
table= view->table;
view= view->top_table();
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index b26e2ddbf1b..9b2355e53c5 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -68,6 +68,7 @@
#include "sql_lex.h"
#include "sql_sequence.h"
#include "my_base.h"
+#include "sql_type_json.h"
/* this is to get the bison compilation windows warnings out */
#ifdef _MSC_VER
@@ -499,96 +500,6 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
}
/**
- @brief Creates a new SELECT_LEX for a UNION branch.
-
- Sets up and initializes a SELECT_LEX structure for a query once the parser
- discovers a UNION token. The current SELECT_LEX is pushed on the stack and
- the new SELECT_LEX becomes the current one.
-
- @param lex The parser state.
-
- @param is_union_distinct True if the union preceding the new select
- statement uses UNION DISTINCT.
-
- @param is_top_level This should be @c TRUE if the newly created SELECT_LEX
- is a non-nested statement.
-
- @return <code>false</code> if successful, <code>true</code> if an error was
- reported. In the latter case parsing should stop.
- */
-bool LEX::add_select_to_union_list(bool is_union_distinct,
- enum sub_select_type type,
- bool is_top_level)
-{
- const char *type_name= (type == INTERSECT_TYPE ? "INTERSECT" :
- (type == EXCEPT_TYPE ? "EXCEPT" : "UNION"));
- /*
- Only the last SELECT can have INTO. Since the grammar won't allow INTO in
- a nested SELECT, we make this check only when creating a top-level SELECT.
- */
- if (is_top_level && result)
- {
- my_error(ER_WRONG_USAGE, MYF(0), type_name, "INTO");
- return TRUE;
- }
- if (current_select->order_list.first && !current_select->braces)
- {
- my_error(ER_WRONG_USAGE, MYF(0), type_name, "ORDER BY");
- return TRUE;
- }
-
- if (current_select->explicit_limit && !current_select->braces)
- {
- my_error(ER_WRONG_USAGE, MYF(0), type_name, "LIMIT");
- return TRUE;
- }
- if (current_select->linkage == GLOBAL_OPTIONS_TYPE)
- {
- thd->parse_error();
- return TRUE;
- }
- if (!is_union_distinct && (type == INTERSECT_TYPE || type == EXCEPT_TYPE))
- {
- my_error(ER_WRONG_USAGE, MYF(0), type_name, "ALL");
- return TRUE;
- }
- /*
- Priority implementation, but also trying to keep things as flat
- as possible */
- if (type == INTERSECT_TYPE &&
- (current_select->linkage != INTERSECT_TYPE &&
- current_select != current_select->master_unit()->first_select())
- && !(thd->variables.sql_mode & MODE_ORACLE))
- {
- /*
- This and previous SELECTs should go one level down because of
- priority
- */
- SELECT_LEX *prev= exclude_last_select();
- if (add_unit_in_brackets(prev))
- return TRUE;
- return add_select_to_union_list(is_union_distinct, type, 0);
- }
- else
- {
- check_automatic_up(type);
- }
- /* This counter shouldn't be incremented for UNION parts */
- nest_level--;
- if (mysql_new_select(this, 0, NULL))
- return TRUE;
- mysql_init_select(this);
- current_select->linkage= type;
- current_select->with_all_modifier= !is_union_distinct;
- if (is_union_distinct) /* UNION DISTINCT - remember position */
- current_select->master_unit()->union_distinct= current_select;
- else
- DBUG_ASSERT(type == UNION_TYPE);
- return FALSE;
-}
-
-
-/**
Create a separate LEX for each assignment if in SP.
If we are in SP we want have own LEX for each assignment.
@@ -606,7 +517,7 @@ bool LEX::add_select_to_union_list(bool is_union_distinct,
@param no_lookahead True if the parser has no lookahead
*/
-void sp_create_assignment_lex(THD *thd, bool no_lookahead)
+bool sp_create_assignment_lex(THD *thd, bool no_lookahead)
{
LEX *lex= thd->lex;
@@ -619,6 +530,8 @@ void sp_create_assignment_lex(THD *thd, bool no_lookahead)
/* Set new LEX as if we at start of set rule. */
lex->sql_command= SQLCOM_SET_OPTION;
+ if (lex->main_select_push())
+ return true;
mysql_init_select(lex);
lex->var_list.empty();
lex->autocommit= 0;
@@ -630,6 +543,7 @@ void sp_create_assignment_lex(THD *thd, bool no_lookahead)
/* Inherit from outer lex. */
lex->option_type= old_lex->option_type;
}
+ return false;
}
@@ -650,8 +564,6 @@ bool sp_create_assignment_instr(THD *thd, bool no_lookahead)
if (lex->sphead)
{
- sp_head *sp= lex->sphead;
-
if (!lex->var_list.is_empty())
{
/*
@@ -659,35 +571,38 @@ bool sp_create_assignment_instr(THD *thd, bool no_lookahead)
option setting, so we should construct sp_instr_stmt
for it.
*/
- LEX_STRING qbuff;
- sp_instr_stmt *i;
Lex_input_stream *lip= &thd->m_parser_state->m_lip;
- if (!(i= new (thd->mem_root)
- sp_instr_stmt(sp->instructions(), lex->spcont, lex)))
- return true;
-
/*
Extract the query statement from the tokenizer. The
end is either lip->ptr, if there was no lookahead,
lip->tok_end otherwise.
*/
- if (no_lookahead)
- qbuff.length= lip->get_ptr() - sp->m_tmp_query;
- else
- qbuff.length= lip->get_tok_end() - sp->m_tmp_query;
-
- if (!(qbuff.str= (char*) alloc_root(thd->mem_root,
- qbuff.length + 5)))
- return true;
-
- strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query,
- qbuff.length);
- qbuff.length+= 4;
- i->m_query= qbuff;
- if (sp->add_instr(i))
+ static const LEX_CSTRING setsp= { STRING_WITH_LEN("SET ") };
+ const char *qend= no_lookahead ? lip->get_ptr() : lip->get_tok_end();
+ Lex_cstring qbuf(lex->sphead->m_tmp_query, qend);
+ if (lex->new_sp_instr_stmt(thd, setsp, qbuf))
return true;
}
+ lex->pop_select();
+ if (lex->check_main_unit_semantics())
+ {
+ /*
+ "lex" can be referrenced by:
+ - sp_instr_set SET a= expr;
+ - sp_instr_set_row_field SET r.a= expr;
+ - sp_instr_stmt (just generated above) SET @a= expr;
+ In this case, "lex" is fully owned by sp_instr_xxx and it will
+ be deleted by the destructor ~sp_instr_xxx().
+ So we should remove "lex" from the stack sp_head::m_lex,
+ to avoid double free.
+ Note, in case "lex" is not owned by any sp_instr_xxx,
+ it's also safe to remove it from the stack right now.
+ So we can remove it unconditionally, without testing lex->sp_lex_in_use.
+ */
+ lex->sphead->restore_lex(thd);
+ return true;
+ }
enum_var_type inner_option_type= lex->option_type;
if (lex->sphead->restore_lex(thd))
return true;
@@ -775,6 +690,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
return v;
}
+
%}
%union {
int num;
@@ -799,6 +715,20 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
Lex_for_loop_bounds_st for_loop_bounds;
Lex_trim_st trim;
vers_history_point_t vers_history_point;
+ struct
+ {
+ enum sub_select_type unit_type;
+ bool distinct;
+ } unit_operation;
+ struct
+ {
+ SELECT_LEX *first;
+ SELECT_LEX *prev_last;
+ } select_list;
+ SQL_I_List<ORDER> *select_order;
+ Lex_select_lock select_lock;
+ Lex_select_limit select_limit;
+ Lex_order_limit_lock *order_limit_lock;
/* pointers */
Create_field *create_field;
@@ -819,6 +749,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
class sp_lex_cursor *sp_cursor_stmt;
LEX_CSTRING *lex_str_ptr;
LEX_USER *lex_user;
+ USER_AUTH *user_auth;
List<Condition_information_item> *cond_info_list;
List<DYNCALL_CREATE_DEF> *dyncol_def_list;
List<Item> *item_list;
@@ -844,6 +775,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
handlerton *db_type;
st_select_lex *select_lex;
+ st_select_lex_unit *select_lex_unit;
struct p_elem_val *p_elem_value;
class Window_frame *window_frame;
class Window_frame_bound *window_frame_bound;
@@ -851,8 +783,9 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
st_trg_execution_order trg_execution_order;
/* enums */
+ enum enum_sp_suid_behaviour sp_suid;
+ enum enum_sp_aggregate_type sp_aggregate_type;
enum enum_view_suid view_suid;
- enum sub_select_type unit_type;
enum Condition_information_item::Name cond_info_item_name;
enum enum_diag_condition_item_name diag_condition_item_name;
enum Diagnostics_information::Which_area diag_area;
@@ -885,6 +818,8 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
}
%{
+/* avoid unintentional %union size increases, it's what a parser stack made of */
+static_assert(sizeof(YYSTYPE) == sizeof(void*)*2+8, "%union size check");
bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%}
@@ -894,7 +829,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
/*
We should not introduce any further shift/reduce conflicts.
*/
-%expect 81
+%expect 68
/*
Comments for TOKENS.
@@ -1051,6 +986,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token LEADING /* SQL-2003-R */
%token LEAVE_SYM
%token LEFT /* SQL-2003-R */
+%token LEFT_PAREN_ALT /* INTERNAL */
+%token LEFT_PAREN_WITH /* INTERNAL */
+%token LEFT_PAREN_LIKE /* INTERNAL */
%token LEX_HOSTNAME
%token LIKE /* SQL-2003-R */
%token LIMIT
@@ -1113,6 +1051,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token PERCENT_RANK_SYM
%token PERCENTILE_CONT_SYM
%token PERCENTILE_DISC_SYM
+%token PORTION_SYM /* SQL-2016-R */
%token POSITION_SYM /* SQL-2003-N */
%token PRECISION /* SQL-2003-R */
%token PRIMARY_SYM /* SQL-2003-R */
@@ -1241,6 +1180,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
Non-reserved keywords
*/
+%token <kwd> ACCOUNT_SYM /* MYSQL */
%token <kwd> ACTION /* SQL-2003-N */
%token <kwd> ADMIN_SYM /* SQL-2003-N */
%token <kwd> ADDDATE_SYM /* MYSQL-FUNC */
@@ -1358,6 +1298,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> EXIT_MARIADB_SYM /* PLSQL-R */
%token <kwd> EXIT_ORACLE_SYM /* PLSQL-R */
%token <kwd> EXPANSION_SYM
+%token <kwd> EXPIRE_SYM /* MySQL */
%token <kwd> EXPORT_SYM
%token <kwd> EXTENDED_SYM
%token <kwd> EXTENT_SIZE_SYM
@@ -1473,6 +1414,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> NAME_SYM /* SQL-2003-N */
%token <kwd> NATIONAL_SYM /* SQL-2003-R */
%token <kwd> NCHAR_SYM /* SQL-2003-R */
+%token <kwd> NEVER_SYM /* MySQL */
%token <kwd> NEW_SYM /* SQL-2003-R */
%token <kwd> NEXT_SYM /* SQL-2003-N */
%token <kwd> NEXTVAL_SYM /* PostgreSQL sequence function */
@@ -1596,6 +1538,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> SQL_CALC_FOUND_ROWS
%token <kwd> SQL_NO_CACHE_SYM
%token <kwd> SQL_THREAD
+%token <kwd> STAGE_SYM
%token <kwd> STARTS_SYM
%token <kwd> START_SYM /* SQL-2003-R */
%token <kwd> STATEMENT_SYM
@@ -1674,6 +1617,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
/* A dummy token to force the priority of table_ref production in a join. */
%left CONDITIONLESS_JOIN
%left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT ON_SYM USING
+
%left SET_VAR
%left OR_SYM OR2_SYM
%left XOR
@@ -1695,6 +1639,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%left MYSQL_CONCAT_SYM
%nonassoc NEG '~' NOT2_SYM BINARY
%nonassoc COLLATE_SYM
+%nonassoc SUBQUERY_AS_EXPR
/*
Tokens that can change their meaning from identifier to something else
@@ -1785,7 +1730,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
ALTER TABLE t1 ADD SYSTEM VERSIONING;
*/
%left PREC_BELOW_CONTRACTION_TOKEN2
-%left TEXT_STRING '(' VALUE_SYM VERSIONING_SYM
+%left TEXT_STRING '(' ')' VALUE_SYM VERSIONING_SYM
+%left EMPTY_FROM_CLAUSE
+%right INTO
%type <lex_str>
DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
@@ -1815,7 +1762,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
NCHAR_STRING
%type <lex_str_ptr>
- opt_table_alias
+ opt_table_alias_clause
+ table_alias_clause
%type <ident_cli>
IDENT
@@ -1848,7 +1796,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <simple_string>
remember_name remember_end
- remember_tok_start remember_tok_end
+ remember_tok_start
wild_and_where
%type <const_simple_string>
@@ -1881,7 +1829,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
opt_temporary all_or_any opt_distinct opt_glimit_clause
opt_ignore_leaves fulltext_options union_option
opt_not
- select_derived_init transaction_access_mode_types
+ transaction_access_mode_types
opt_natural_language_mode opt_query_expansion
opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment
ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt
@@ -1890,7 +1838,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
opt_default_time_precision
case_stmt_body opt_bin_mod opt_for_system_time_clause
opt_if_exists_table_element opt_if_not_exists_table_element
- opt_recursive opt_format_xid
+ opt_recursive opt_format_xid opt_for_portion_of_time_clause
%type <object_ddl_options>
create_or_replace
@@ -1970,6 +1918,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
expr_list opt_udf_expr_list udf_expr_list when_list when_list_opt_else
ident_list ident_list_arg opt_expr_list
decode_when_list_oracle
+ execute_using
+ execute_params
%type <sp_cursor_stmt>
sp_cursor_stmt_lex
@@ -2002,12 +1952,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <table_list>
join_table_list join_table
table_factor table_ref esc_table_ref
- table_primary_ident table_primary_derived
- select_derived derived_table_list
- select_derived_union
- derived_simple_table
- derived_query_specification
- derived_table_value_constructor
+ table_primary_ident table_primary_ident_opt_parens
+ table_primary_derived table_primary_derived_opt_parens
+ derived_table_list table_reference_list_parens
+ nested_table_reference_list join_table_parens
+ update_table_list
%type <date_time_type> date_time_type;
%type <interval> interval
@@ -2028,6 +1977,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <lex_user> user grant_user grant_role user_or_role current_role
admin_option_for_role user_maybe_role
+%type <user_auth> opt_auth_str auth_expression auth_token
+
%type <charset>
opt_collate
charset_name
@@ -2041,14 +1992,21 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
UNDERSCORE_CHARSET
%type <select_lex> subselect
- get_select_lex get_select_lex_derived
- simple_table
query_specification
- query_term_union_not_ready
- query_term_union_ready
- query_expression_body
- select_paren_derived
table_value_constructor
+ simple_table
+ query_simple
+ query_primary
+ subquery
+ select_into_query_specification
+
+%type <select_lex_unit>
+ query_expression
+ query_expression_no_with_clause
+ query_expression_body_ext
+ query_expression_body_ext_parens
+ query_expression_body
+ query_specification_start
%type <boolfunc2creator> comp_op
@@ -2060,11 +2018,30 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <virtual_column> opt_check_constraint check_constraint virtual_column_func
column_default_expr
-%type <unit_type> unit_type_decl
+
+%type <unit_operation> unit_type_decl
+
+%type <select_lock>
+ opt_procedure_or_into
+ opt_select_lock_type
+ select_lock_type
+ opt_lock_wait_timeout_new
+
+%type <select_limit> opt_limit_clause limit_clause limit_options
+
+%type <order_limit_lock>
+ query_expression_tail
+ opt_query_expression_tail
+ order_or_limit
+ order_limit_lock
+ opt_order_limit_lock
+
+%type <select_order> opt_order_clause order_clause order_list
%type <NONE>
- analyze_stmt_command
- query verb_clause create change select do drop insert replace insert2
+ analyze_stmt_command backup backup_statements
+ query verb_clause create change select select_into
+ do drop insert replace insert2
insert_values update delete truncate rename compound_statement
show describe load alter optimize keycache preload flush
reset purge begin_stmt_mariadb commit rollback savepoint release
@@ -2080,7 +2057,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
assign_to_keycache_parts
preload_list preload_list_or_parts preload_keys preload_keys_parts
select_item_list select_item values_list no_braces
- opt_limit_clause delete_limit_clause fields opt_values values
+ delete_limit_clause fields opt_values values
no_braces_with_names opt_values_with_names values_with_names
procedure_list procedure_list2 procedure_item
field_def handler opt_generated_always
@@ -2104,18 +2081,16 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
table_to_table_list table_to_table opt_table_list opt_as
handler_rkey_function handler_read_or_scan
single_multi table_wild_list table_wild_one opt_wild
- union_clause union_list
- subselect_start opt_and charset
- subselect_end select_var_list select_var_list_init help
+ opt_and charset
+ select_var_list select_var_list_init help
opt_extended_describe shutdown
opt_format_json
- prepare prepare_src execute deallocate
- statement sp_suid
+ prepare execute deallocate
+ statement
sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
view_list_opt view_list view_select
- trigger_tail sp_tail sf_tail event_tail
- udf_tail create_function_tail create_aggregate_function_tail
+ trigger_tail sp_tail event_tail
install uninstall partition_entry binlog_base64_event
normal_key_options normal_key_opts all_key_opt
spatial_key_options fulltext_key_options normal_key_opt
@@ -2124,6 +2099,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
key_using_alg
part_column_list
period_for_system_time
+ period_for_application_time
server_def server_options_list server_option
definer_opt no_definer definer get_diagnostics
parse_vcol_expr vcol_opt_specifier vcol_opt_attribute
@@ -2156,6 +2132,8 @@ END_OF_INPUT
%type <view_suid> view_suid opt_view_suid
%type <plsql_cursor_attr> plsql_cursor_attr
+%type <sp_suid> sp_suid
+%type <sp_aggregate_type> opt_aggregate
%type <num> sp_decl_idents sp_decl_idents_init_vars
%type <num> sp_handler_type sp_hcond_list
@@ -2199,13 +2177,16 @@ END_OF_INPUT
%type <frame_exclusion> opt_window_frame_exclusion;
%type <window_frame_bound> window_frame_start window_frame_bound;
-%type <NONE>
+%type <kwd>
'-' '+' '*' '/' '%' '(' ')'
- ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM BETWEEN_SYM CASE_SYM
+ ',' '!' '{' '}' '&' '|'
+
+%type <NONE>
+ AND_SYM OR_SYM BETWEEN_SYM CASE_SYM
THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM
MYSQL_CONCAT_SYM ORACLE_CONCAT_SYM
-%type <with_clause> opt_with_clause with_clause
+%type <with_clause> with_clause
%type <lex_str_ptr> query_name
@@ -2241,8 +2222,8 @@ rule: <-- starts at col 1
query:
END_OF_INPUT
{
- if (likely(!thd->bootstrap) &&
- unlikely(!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT)))
+ if (!thd->bootstrap &&
+ (!(thd->lex->lex_options & OPTION_LEX_FOUND_COMMENT)))
my_yyabort_error((ER_EMPTY_QUERY, MYF(0)));
thd->lex->sql_command= SQLCOM_EMPTY_QUERY;
@@ -2296,6 +2277,7 @@ statement:
alter
| analyze
| analyze_stmt_command
+ | backup
| binlog_base64_event
| call
| change
@@ -2338,6 +2320,7 @@ statement:
| rollback
| savepoint
| select
+ | select_into
| set
| signal_stmt
| show
@@ -2355,9 +2338,7 @@ statement:
deallocate:
deallocate_or_drop PREPARE_SYM ident
{
- LEX *lex= thd->lex;
- lex->sql_command= SQLCOM_DEALLOCATE_PREPARE;
- lex->prepared_stmt_name= $3;
+ Lex->stmt_deallocate_prepare($3);
}
;
@@ -2367,72 +2348,58 @@ deallocate_or_drop:
;
prepare:
- PREPARE_SYM ident FROM prepare_src
- {
- LEX *lex= thd->lex;
- if (unlikely(lex->table_or_sp_used()))
- my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
- "PREPARE..FROM"));
- lex->sql_command= SQLCOM_PREPARE;
- lex->prepared_stmt_name= $2;
- }
- ;
-
-prepare_src:
- { Lex->expr_allows_subselect= false; }
+ PREPARE_SYM ident FROM
+ { Lex->clause_that_disallows_subselect= "PREPARE..FROM"; }
expr
{
- Lex->prepared_stmt_code= $2;
- Lex->expr_allows_subselect= true;
+ Lex->clause_that_disallows_subselect= NULL;
+ if (Lex->stmt_prepare($2, $5))
+ MYSQL_YYABORT;
}
;
execute:
- EXECUTE_SYM ident
+ EXECUTE_SYM ident execute_using
{
- LEX *lex= thd->lex;
- lex->sql_command= SQLCOM_EXECUTE;
- lex->prepared_stmt_name= $2;
+ if (Lex->stmt_execute($2, $3))
+ MYSQL_YYABORT;
}
+ | EXECUTE_SYM IMMEDIATE_SYM
+ { Lex->clause_that_disallows_subselect= "EXECUTE IMMEDIATE"; }
+ expr
+ { Lex->clause_that_disallows_subselect= NULL; }
execute_using
- {}
- | EXECUTE_SYM IMMEDIATE_SYM prepare_src
{
- if (unlikely(Lex->table_or_sp_used()))
- my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
- "EXECUTE IMMEDIATE"));
- Lex->sql_command= SQLCOM_EXECUTE_IMMEDIATE;
+ if (Lex->stmt_execute_immediate($4, $6))
+ MYSQL_YYABORT;
}
- execute_using
- {}
;
execute_using:
- /* nothing */
- | USING { Lex->expr_allows_subselect= false; }
- execute_var_list
+ /* nothing */ { $$= NULL; }
+ | USING
+ { Lex->clause_that_disallows_subselect= "EXECUTE..USING"; }
+ execute_params
{
- if (unlikely(Lex->table_or_sp_used()))
- my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
- "EXECUTE..USING"));
- Lex->expr_allows_subselect= true;
+ $$= $3;
+ Lex->clause_that_disallows_subselect= NULL;
}
;
-execute_var_list:
- execute_var_list ',' execute_var_ident
- | execute_var_ident
- ;
-
-execute_var_ident:
+execute_params:
expr_or_default
{
- if (unlikely(Lex->prepared_stmt_params.push_back($1,
- thd->mem_root)))
+ if (unlikely(!($$= List<Item>::make(thd->mem_root, $1))))
+ MYSQL_YYABORT;
+ }
+ | execute_params ',' expr_or_default
+ {
+ if (($$= $1)->push_back($3, thd->mem_root))
MYSQL_YYABORT;
}
;
+
/* help */
help:
@@ -2691,19 +2658,24 @@ connection_name:
/* create a table */
create:
- create_or_replace opt_temporary TABLE_SYM opt_if_not_exists table_ident
+ create_or_replace opt_temporary TABLE_SYM opt_if_not_exists
{
LEX *lex= thd->lex;
if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_create_table()))
MYSQL_YYABORT;
lex->create_info.init();
- if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2,
- $1 | $4)))
+ if (lex->main_select_push())
+ MYSQL_YYABORT;
+ lex->current_select->parsing_place= BEFORE_OPT_LIST;
+ if (lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2, $1 | $4))
MYSQL_YYABORT;
- if (unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL,
- TL_OPTION_UPDATING,
- TL_WRITE,
- MDL_EXCLUSIVE)))
+ }
+ table_ident
+ {
+ LEX *lex= thd->lex;
+ if (!lex->first_select_lex()->
+ add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING,
+ TL_WRITE, MDL_SHARED_UPGRADABLE))
MYSQL_YYABORT;
lex->alter_info.reset();
/*
@@ -2718,12 +2690,14 @@ create:
create_body
{
LEX *lex= thd->lex;
- lex->current_select= &lex->select_lex;
create_table_set_open_action_and_adjust_tables(lex);
+ Lex->pop_select(); //main select
}
| create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident
{
LEX *lex= thd->lex;
+ if (lex->main_select_push())
+ MYSQL_YYABORT;
if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_create_sequence()))
MYSQL_YYABORT;
lex->create_info.init();
@@ -2731,10 +2705,9 @@ create:
$1 | $4)))
MYSQL_YYABORT;
- if (unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL,
- TL_OPTION_UPDATING,
- TL_WRITE,
- MDL_EXCLUSIVE)))
+ if (!lex->first_select_lex()->
+ add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING,
+ TL_WRITE, MDL_EXCLUSIVE))
MYSQL_YYABORT;
/*
@@ -2757,8 +2730,9 @@ create:
if (unlikely(lex->create_info.seq_create_info->check_and_adjust(1)))
{
my_error(ER_SEQUENCE_INVALID_DATA, MYF(0),
- lex->select_lex.table_list.first->db.str,
- lex->select_lex.table_list.first->table_name.str);
+ lex->first_select_lex()->table_list.first->db.str,
+ lex->first_select_lex()->table_list.first->
+ table_name.str);
MYSQL_YYABORT;
}
@@ -2771,42 +2745,64 @@ create:
Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE;
Lex->create_info.sequence= 1;
- lex->current_select= &lex->select_lex;
create_table_set_open_action_and_adjust_tables(lex);
+ Lex->pop_select(); //main select
}
- | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident
+ | create_or_replace opt_unique INDEX_SYM opt_if_not_exists
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ ident
opt_key_algorithm_clause
ON table_ident
{
- if (unlikely(Lex->add_create_index_prepare($8)))
+ if (Lex->add_create_index_prepare($9))
MYSQL_YYABORT;
- if (unlikely(Lex->add_create_index($2, &$5, $6, $1 | $4)))
+ if (Lex->add_create_index($2, &$6, $7, $1 | $4))
MYSQL_YYABORT;
}
'(' key_list ')' opt_lock_wait_timeout normal_key_options
- opt_index_lock_algorithm { }
- | create_or_replace fulltext INDEX_SYM opt_if_not_exists ident
+ opt_index_lock_algorithm
+ {
+ Lex->pop_select(); //main select
+ }
+ | create_or_replace fulltext INDEX_SYM
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ opt_if_not_exists ident
ON table_ident
{
- if (unlikely(Lex->add_create_index_prepare($7)))
+ if (Lex->add_create_index_prepare($8))
MYSQL_YYABORT;
- if (unlikely(Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF,
- $1 | $4)))
+ if (Lex->add_create_index($2, &$6, HA_KEY_ALG_UNDEF, $1 | $5))
MYSQL_YYABORT;
}
'(' key_list ')' opt_lock_wait_timeout fulltext_key_options
- opt_index_lock_algorithm { }
- | create_or_replace spatial INDEX_SYM opt_if_not_exists ident
+ opt_index_lock_algorithm
+ {
+ Lex->pop_select(); //main select
+ }
+ | create_or_replace spatial INDEX_SYM
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ opt_if_not_exists ident
ON table_ident
{
- if (unlikely(Lex->add_create_index_prepare($7)))
+ if (Lex->add_create_index_prepare($8))
MYSQL_YYABORT;
- if (unlikely(Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF,
- $1 | $4)))
+ if (Lex->add_create_index($2, &$6, HA_KEY_ALG_UNDEF, $1 | $5))
MYSQL_YYABORT;
}
'(' key_list ')' opt_lock_wait_timeout spatial_key_options
- opt_index_lock_algorithm { }
+ opt_index_lock_algorithm
+ {
+ Lex->pop_select(); //main select
+ }
| create_or_replace DATABASE opt_if_not_exists ident
{
Lex->create_info.default_table_charset= NULL;
@@ -2823,57 +2819,90 @@ create:
| create_or_replace definer_opt opt_view_suid VIEW_SYM
opt_if_not_exists table_ident
{
- if (unlikely(Lex->add_create_view(thd, $1 | $5,
- DTYPE_ALGORITHM_UNDEFINED, $3,
- $6)))
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ if (Lex->add_create_view(thd, $1 | $5,
+ DTYPE_ALGORITHM_UNDEFINED, $3, $6))
MYSQL_YYABORT;
}
view_list_opt AS view_select
- { }
+ {
+ Lex->pop_select(); //main select
+ }
| create_or_replace view_algorithm definer_opt opt_view_suid VIEW_SYM
opt_if_not_exists table_ident
{
if (unlikely(Lex->add_create_view(thd, $1 | $6, $2, $4, $7)))
MYSQL_YYABORT;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
}
view_list_opt AS view_select
- { }
+ {
+ Lex->pop_select(); //main select
+ }
| create_or_replace definer_opt TRIGGER_SYM
- { Lex->create_info.set($1); }
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ Lex->create_info.set($1);
+ }
trigger_tail
- { }
- | create_or_replace definer_opt PROCEDURE_SYM
- { Lex->create_info.set($1); }
+ {
+ Lex->pop_select(); //main select
+ }
+ | create_or_replace definer_opt PROCEDURE_SYM opt_if_not_exists
+ {
+ if (Lex->stmt_create_procedure_start($1 | $4))
+ MYSQL_YYABORT;
+ }
sp_tail
- { }
+ {
+ Lex->stmt_create_routine_finalize();
+ }
| create_or_replace definer_opt EVENT_SYM
- { Lex->create_info.set($1); }
- event_tail
- { }
- | create_or_replace definer FUNCTION_SYM
{
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
Lex->create_info.set($1);
}
- sf_tail_not_aggregate
- { }
- | create_or_replace definer AGGREGATE_SYM FUNCTION_SYM
+ event_tail
{
- Lex->create_info.set($1);
+ Lex->pop_select(); //main select
}
- sf_tail_aggregate
- { }
- | create_or_replace no_definer FUNCTION_SYM
- { Lex->create_info.set($1); }
- create_function_tail
- { }
- | create_or_replace no_definer AGGREGATE_SYM FUNCTION_SYM
+ | create_or_replace definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ sp_name '('
{
- Lex->create_info.set($1);
+ if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
+ MYSQL_YYABORT;
}
- create_aggregate_function_tail
- { }
- | create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list
- opt_require_clause opt_resource_options
+ sp_fdparam_list ')'
+ sf_return_type
+ sf_c_chistics_and_body
+ {
+ Lex->stmt_create_routine_finalize();
+ }
+ | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ sp_name '('
+ {
+ if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
+ MYSQL_YYABORT;
+ }
+ sp_fdparam_list ')'
+ sf_return_type
+ sf_c_chistics_and_body
+ {
+ Lex->stmt_create_routine_finalize();
+ }
+ | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ ident RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
+ {
+ if (Lex->stmt_create_udf_function($1 | $5, $3, $6,
+ (Item_result) $8, $10))
+ MYSQL_YYABORT;
+ }
+ | create_or_replace USER_SYM opt_if_not_exists clear_privileges
+ grant_list opt_require_clause opt_resource_options opt_account_locking_and_opt_password_expiration
{
if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_USER,
$1 | $3)))
@@ -2899,38 +2928,6 @@ create:
{ }
;
-sf_tail_not_aggregate:
- sf_tail
- {
- if (unlikely(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR))
- {
- my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0)));
- }
- Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE);
- }
- ;
-
-sf_tail_aggregate:
- sf_tail
- {
- if (unlikely(!(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR)))
- {
- my_yyabort_error((ER_INVALID_AGGREGATE_FUNCTION, MYF(0)));
- }
- Lex->sphead->set_chistics_agg_type(GROUP_AGGREGATE);
- }
- ;
-
-create_function_tail:
- sf_tail_not_aggregate { }
- | udf_tail { Lex->udf.type= UDFTYPE_FUNCTION; }
- ;
-
-create_aggregate_function_tail:
- sf_tail_aggregate
- { }
- | udf_tail { Lex->udf.type= UDFTYPE_AGGREGATE; }
- ;
opt_sequence:
/* empty */ { }
| sequence_defs
@@ -3249,20 +3246,17 @@ ev_sql_stmt:
if (unlikely(!lex->make_sp_head(thd,
lex->event_parse_data->identifier,
- &sp_handler_procedure)))
+ &sp_handler_procedure,
+ DEFAULT_AGGREGATE)))
MYSQL_YYABORT;
lex->sphead->set_body_start(thd, lip->get_cpp_ptr());
}
sp_proc_stmt
{
- LEX *lex= thd->lex;
-
/* return back to the original memory root ASAP */
- lex->sphead->set_stmt_end(thd);
- lex->sphead->restore_thd_mem_root(thd);
-
- lex->event_parse_data->body_changed= TRUE;
+ if (Lex->sp_body_finalize_event(thd))
+ MYSQL_YYABORT;
}
;
@@ -3274,13 +3268,16 @@ clear_privileges:
lex->columns.empty();
lex->grant= lex->grant_tot_col= 0;
lex->all_privileges= 0;
- lex->select_lex.db= null_clex_str;
- lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
- lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0;
- bzero((char *)&(lex->mqh),sizeof(lex->mqh));
+ lex->first_select_lex()->db= null_clex_str;
+ lex->account_options.reset();
}
;
+opt_aggregate:
+ /* Empty */ { $$= NOT_AGGREGATE; }
+ | AGGREGATE_SYM { $$= GROUP_AGGREGATE; }
+ ;
+
sp_name:
ident '.' ident
{
@@ -3319,7 +3316,7 @@ sp_chistic:
| MODIFIES_SYM SQL_SYM DATA_SYM
{ Lex->sp_chistics.daccess= SP_MODIFIES_SQL_DATA; }
| sp_suid
- {}
+ { Lex->sp_chistics.suid= $1; }
;
/* Create characteristics */
@@ -3329,14 +3326,8 @@ sp_c_chistic:
;
sp_suid:
- SQL_SYM SECURITY_SYM DEFINER_SYM
- {
- Lex->sp_chistics.suid= SP_IS_SUID;
- }
- | SQL_SYM SECURITY_SYM INVOKER_SYM
- {
- Lex->sp_chistics.suid= SP_IS_NOT_SUID;
- }
+ SQL_SYM SECURITY_SYM DEFINER_SYM { $$= SP_IS_SUID; }
+ | SQL_SYM SECURITY_SYM INVOKER_SYM { $$= SP_IS_NOT_SUID; }
;
call:
@@ -3373,7 +3364,18 @@ sp_cparams:
/* Stored FUNCTION parameter declaration list */
sp_fdparam_list:
/* Empty */
- | sp_fdparams
+ {
+ Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start();
+ Lex->sphead->m_param_end= Lex->sphead->m_param_begin;
+ }
+ |
+ {
+ Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start();
+ }
+ sp_fdparams
+ {
+ Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
+ }
;
sp_fdparams:
@@ -3447,18 +3449,6 @@ sp_opt_inout:
| INOUT_SYM { $$= sp_variable::MODE_INOUT; }
;
-sp_parenthesized_fdparam_list:
- '('
- {
- Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start() + 1;
- }
- sp_fdparam_list
- ')'
- {
- Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
- }
- ;
-
sp_parenthesized_pdparam_list:
'('
{
@@ -3526,12 +3516,8 @@ optionally_qualified_column_ident:
row_field_name:
ident
{
- if (unlikely(check_string_char_length(&$1, 0, NAME_CHAR_LEN,
- system_charset_info, 1)))
- my_yyabort_error((ER_TOO_LONG_IDENT, MYF(0), $1.str));
- if (unlikely(!($$= new (thd->mem_root) Spvar_definition())))
+ if (!($$= Lex->row_field_name(thd, $1)))
MYSQL_YYABORT;
- Lex->init_last_field($$, &$1, thd->variables.collation_database);
}
;
@@ -3542,17 +3528,12 @@ row_field_definition:
row_field_definition_list:
row_field_definition
{
- if (unlikely(!($$= new (thd->mem_root) Row_definition_list())) ||
- unlikely($$->push_back($1, thd->mem_root)))
+ if (!($$= Row_definition_list::make(thd->mem_root, $1)))
MYSQL_YYABORT;
}
| row_field_definition_list ',' row_field_definition
{
- uint unused;
- if (unlikely($1->find_row_field_by_name(&$3->field_name, &unused)))
- my_yyabort_error((ER_DUP_FIELDNAME, MYF(0), $3->field_name.str));
- $$= $1;
- if (unlikely($$->push_back($3, thd->mem_root)))
+ if (($$= $1)->append_uniq(thd->mem_root, $3))
MYSQL_YYABORT;
}
;
@@ -3667,10 +3648,13 @@ sp_cursor_stmt:
{
DBUG_ASSERT(thd->free_list == NULL);
Lex->sphead->reset_lex(thd, $1);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
select
{
DBUG_ASSERT(Lex == $1);
+ Lex->pop_select(); //main select
if (unlikely($1->stmt_finalize(thd)) ||
unlikely($1->sphead->restore_lex(thd)))
MYSQL_YYABORT;
@@ -3799,7 +3783,7 @@ raise_stmt_oracle:
signal_stmt:
SIGNAL_SYM signal_value opt_set_signal_information
{
- if (unlikely(Lex->add_signal_statement(thd, $2)))
+ if (Lex->add_signal_statement(thd, $2))
MYSQL_YYABORT;
}
;
@@ -4168,48 +4152,17 @@ sp_proc_stmt_statement:
Lex_input_stream *lip= YYLIP;
lex->sphead->reset_lex(thd);
+ /*
+ We should not push main select here, it will be done or not
+ done by the statement, we just provide only a new LEX for the
+ statement here as if it is start of parsing a new statement.
+ */
lex->sphead->m_tmp_query= lip->get_tok_start();
}
statement
{
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
- sp_head *sp= lex->sphead;
-
- sp->m_flags|= sp_get_flags_for_command(lex);
- /* "USE db" doesn't work in a procedure */
- if (unlikely(lex->sql_command == SQLCOM_CHANGE_DB))
- my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "USE"));
- /*
- Don't add an instruction for SET statements, since all
- instructions for them were already added during processing
- of "set" rule.
- */
- DBUG_ASSERT(lex->sql_command != SQLCOM_SET_OPTION ||
- lex->var_list.is_empty());
- if (lex->sql_command != SQLCOM_SET_OPTION)
- {
- sp_instr_stmt *i=new (thd->mem_root)
- sp_instr_stmt(sp->instructions(), lex->spcont, lex);
- if (unlikely(i == NULL))
- MYSQL_YYABORT;
-
- /*
- Extract the query statement from the tokenizer. The
- end is either lex->ptr, if there was no lookahead,
- lex->tok_end otherwise.
- */
- if (yychar == YYEMPTY)
- i->m_query.length= lip->get_ptr() - sp->m_tmp_query;
- else
- i->m_query.length= lip->get_tok_start() - sp->m_tmp_query;;
- if (unlikely(!(i->m_query.str= strmake_root(thd->mem_root,
- sp->m_tmp_query,
- i->m_query.length))) ||
- unlikely(sp->add_instr(i)))
- MYSQL_YYABORT;
- }
- if (unlikely(sp->restore_lex(thd)))
+ if (Lex->sp_proc_stmt_statement_finalize(thd, yychar == YYEMPTY) ||
+ Lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
;
@@ -4222,11 +4175,16 @@ RETURN_ALLMODES_SYM:
sp_proc_stmt_return:
RETURN_ALLMODES_SYM
- { Lex->sphead->reset_lex(thd); }
+ {
+ Lex->sphead->reset_lex(thd);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
expr
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
+ Lex->pop_select(); //main select
if (unlikely(sp->m_handler->add_instr_freturn(thd, sp, lex->spcont,
$3, lex)) ||
unlikely(sp->restore_lex(thd)))
@@ -4243,7 +4201,16 @@ sp_proc_stmt_return:
;
reset_lex_expr:
- { Lex->sphead->reset_lex(thd); } expr { $$= $2; }
+ {
+ Lex->sphead->reset_lex(thd);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
+ expr
+ {
+ Lex->pop_select(); //main select
+ $$= $2;
+ }
;
sp_proc_stmt_exit_oracle:
@@ -4335,6 +4302,8 @@ assignment_source_expr:
{
DBUG_ASSERT(thd->free_list == NULL);
Lex->sphead->reset_lex(thd, $1);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
expr
{
@@ -4343,7 +4312,8 @@ assignment_source_expr:
$$->sp_lex_in_use= true;
$$->set_item_and_free_list($3, thd->free_list);
thd->free_list= NULL;
- if (unlikely($$->sphead->restore_lex(thd)))
+ Lex->pop_select(); //min select
+ if ($$->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
;
@@ -4352,6 +4322,9 @@ for_loop_bound_expr:
assignment_source_lex
{
Lex->sphead->reset_lex(thd, $1);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ Lex->current_select->parsing_place= FOR_LOOP_BOUND;
}
expr
{
@@ -4359,8 +4332,10 @@ for_loop_bound_expr:
$$= $1;
$$->sp_lex_in_use= true;
$$->set_item_and_free_list($3, NULL);
+ Lex->pop_select(); //main select
if (unlikely($$->sphead->restore_lex(thd)))
MYSQL_YYABORT;
+ Lex->current_select->parsing_place= NO_MATTER;
}
;
@@ -4413,15 +4388,8 @@ sp_proc_stmt_fetch:
sp_proc_stmt_fetch_head sp_fetch_list { }
| FETCH_SYM GROUP_SYM NEXT_SYM ROW_SYM
{
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- lex->sphead->m_flags|= sp_head::HAS_AGGREGATE_INSTR;
- sp_instr_agg_cfetch *i=
- new (thd->mem_root) sp_instr_agg_cfetch(sp->instructions(),
- lex->spcont);
- if (unlikely(i == NULL) ||
- unlikely(sp->add_instr(i)))
- MYSQL_YYABORT;
+ if (unlikely(Lex->sp_add_agg_cfetch()))
+ MYSQL_YYABORT;
}
;
@@ -4479,7 +4447,11 @@ sp_fetch_list:
;
sp_if:
- { Lex->sphead->reset_lex(thd); }
+ {
+ Lex->sphead->reset_lex(thd);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
expr THEN_SYM
{
LEX *lex= Lex;
@@ -4493,6 +4465,7 @@ sp_if:
unlikely(sp->add_cont_backpatch(i)) ||
unlikely(sp->add_instr(i)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
if (unlikely(sp->restore_lex(thd)))
MYSQL_YYABORT;
}
@@ -4593,12 +4566,18 @@ case_stmt_specification:
;
case_stmt_body:
- { Lex->sphead->reset_lex(thd); /* For expr $2 */ }
+ {
+ Lex->sphead->reset_lex(thd); /* For expr $2 */
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
expr
{
if (unlikely(Lex->case_stmt_action_expr($2)))
MYSQL_YYABORT;
- if (unlikely(Lex->sphead->restore_lex(thd)))
+
+ Lex->pop_select(); //main select
+ if (Lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
simple_when_clause_list
@@ -4621,6 +4600,8 @@ simple_when_clause:
WHEN_SYM
{
Lex->sphead->reset_lex(thd); /* For expr $3 */
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
expr
{
@@ -4629,6 +4610,7 @@ simple_when_clause:
LEX *lex= Lex;
if (unlikely(lex->case_stmt_action_when($3, true)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
/* For expr $3 */
if (unlikely(lex->sphead->restore_lex(thd)))
MYSQL_YYABORT;
@@ -4645,12 +4627,15 @@ searched_when_clause:
WHEN_SYM
{
Lex->sphead->reset_lex(thd); /* For expr $3 */
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
expr
{
LEX *lex= Lex;
if (unlikely(lex->case_stmt_action_when($3, false)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
/* For expr $3 */
if (unlikely(lex->sphead->restore_lex(thd)))
MYSQL_YYABORT;
@@ -4749,9 +4734,15 @@ opt_sp_for_loop_direction:
;
sp_for_loop_index_and_bounds:
- ident sp_for_loop_bounds
+ ident
{
- if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $2)))
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
+ sp_for_loop_bounds
+ {
+ Lex->pop_select(); //main select
+ if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $3)))
MYSQL_YYABORT;
}
;
@@ -4797,8 +4788,11 @@ while_body:
LEX *lex= Lex;
if (unlikely(lex->sp_while_loop_expression(thd, $1)))
MYSQL_YYABORT;
- if (unlikely(lex->sphead->restore_lex(thd)))
+ Lex->pop_select(); //main select
+ if (lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
+ if (lex->main_select_push(true))
+ MYSQL_YYABORT;
}
sp_proc_stmts1 END WHILE_SYM
{
@@ -4809,7 +4803,11 @@ while_body:
repeat_body:
sp_proc_stmts1 UNTIL_SYM
- { Lex->sphead->reset_lex(thd); }
+ {
+ Lex->sphead->reset_lex(thd);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
expr END REPEAT_SYM
{
LEX *lex= Lex;
@@ -4820,7 +4818,8 @@ repeat_body:
if (unlikely(i == NULL) ||
unlikely(lex->sphead->add_instr(i)))
MYSQL_YYABORT;
- if (unlikely(lex->sphead->restore_lex(thd)))
+ Lex->pop_select(); //main select
+ if (lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
/* We can shortcut the cont_backpatch here */
i->m_cont_dest= ip+1;
@@ -4848,6 +4847,8 @@ sp_labeled_control:
if (unlikely(Lex->sp_push_loop_label(thd, &$1)))
MYSQL_YYABORT;
Lex->sphead->reset_lex(thd);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
while_body pop_sp_loop_label
{ }
@@ -4899,6 +4900,8 @@ sp_unlabeled_control:
if (unlikely(Lex->sp_push_loop_empty_label(thd)))
MYSQL_YYABORT;
Lex->sphead->reset_lex(thd);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
while_body
{
@@ -5300,26 +5303,16 @@ size_number:
*/
create_body:
- '(' create_field_list ')'
+ create_field_list_parens
{ Lex->create_info.option_list= NULL; }
opt_create_table_options opt_create_partitioning opt_create_select {}
| opt_create_table_options opt_create_partitioning opt_create_select {}
- /*
- the following rule is redundant, but there's a shift/reduce
- conflict that prevents the rule above from parsing a syntax like
- CREATE TABLE t1 (SELECT 1);
- */
- | '(' create_select_query_specification ')'
- | '(' create_select_query_specification ')'
- { Select->set_braces(1);} union_list {}
- | '(' create_select_query_specification ')'
- { Select->set_braces(1);} union_order_or_limit {}
| create_like
{
Lex->create_info.add(DDL_options_st::OPT_LIKE);
- TABLE_LIST *src_table= Lex->select_lex.add_table_to_list(thd,
- $1, NULL, 0, TL_READ, MDL_SHARED_READ);
+ TABLE_LIST *src_table= Lex->first_select_lex()->
+ add_table_to_list(thd, $1, NULL, 0, TL_READ, MDL_SHARED_READ);
if (unlikely(! src_table))
MYSQL_YYABORT;
/* CREATE TABLE ... LIKE is not allowed for views. */
@@ -5329,7 +5322,7 @@ create_body:
create_like:
LIKE table_ident { $$= $2; }
- | '(' LIKE table_ident ')' { $$= $3; }
+ | LEFT_PAREN_LIKE LIKE table_ident ')' { $$= $3; }
;
opt_create_select:
@@ -5338,23 +5331,19 @@ opt_create_select:
;
create_select_query_expression:
- opt_with_clause SELECT_SYM create_select_part2 opt_table_expression
- create_select_part4
- {
- Select->set_braces(0);
- Select->set_with_clause($1);
+ query_expression
+ {
+ if (Lex->parsed_insert_select($1->first_select()))
+ MYSQL_YYABORT;
}
- union_clause
- | opt_with_clause SELECT_SYM create_select_part2
- create_select_part3_union_not_ready create_select_part4
+ | LEFT_PAREN_WITH with_clause query_expression_no_with_clause ')'
{
- Select->set_with_clause($1);
+ SELECT_LEX *first_select= $3->first_select();
+ $3->set_with_clause($2);
+ $2->attach_to(first_select);
+ if (Lex->parsed_insert_select(first_select))
+ MYSQL_YYABORT;
}
- | '(' create_select_query_specification ')'
- | '(' create_select_query_specification ')'
- { Select->set_braces(1);} union_list {}
- | '(' create_select_query_specification ')'
- { Select->set_braces(1);} union_order_or_limit {}
;
opt_create_partitioning:
@@ -5437,13 +5426,17 @@ partition_entry:
thd->parse_error(ER_PARTITION_ENTRY_ERROR);
MYSQL_YYABORT;
}
- DBUG_ASSERT(Lex->part_info->table);
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
/*
We enter here when opening the frm file to translate
partition info string into part_info data structure.
*/
}
- partition {}
+ partition
+ {
+ Lex->pop_select(); //main select
+ }
;
partition:
@@ -6092,56 +6085,6 @@ opt_versioning_interval_start:
End of partition parser part
*/
-create_select_query_specification:
- opt_with_clause SELECT_SYM create_select_part2 create_select_part3
- create_select_part4
- {
- Select->set_with_clause($1);
- }
- ;
-
-create_select_part2:
- {
- LEX *lex=Lex;
- if (lex->sql_command == SQLCOM_INSERT)
- lex->sql_command= SQLCOM_INSERT_SELECT;
- else if (lex->sql_command == SQLCOM_REPLACE)
- lex->sql_command= SQLCOM_REPLACE_SELECT;
- /*
- The following work only with the local list, the global list
- is created correctly in this case
- */
- lex->current_select->table_list.save_and_clear(&lex->save_list);
- mysql_init_select(lex);
- lex->current_select->parsing_place= SELECT_LIST;
- }
- select_options select_item_list
- {
- Select->parsing_place= NO_MATTER;
- }
- ;
-
-create_select_part3:
- opt_table_expression
- | create_select_part3_union_not_ready
- ;
-
-create_select_part3_union_not_ready:
- table_expression order_or_limit
- | order_or_limit
- ;
-
-create_select_part4:
- opt_select_lock_type
- {
- /*
- The following work only with the local list, the global list
- is created correctly in this case
- */
- Lex->current_select->table_list.push_front(&Lex->save_list);
- }
- ;
-
opt_as:
/* empty */ {}
| AS {}
@@ -6369,7 +6312,7 @@ create_table_option:
}
| UNION_SYM opt_equal
{
- Lex->select_lex.table_list.save_and_clear(&Lex->save_list);
+ Lex->first_select_lex()->table_list.save_and_clear(&Lex->save_list);
}
'(' opt_table_list ')'
{
@@ -6378,8 +6321,8 @@ create_table_option:
from the global list.
*/
LEX *lex=Lex;
- lex->create_info.merge_list= lex->select_lex.table_list.first;
- lex->select_lex.table_list= lex->save_list;
+ lex->create_info.merge_list= lex->first_select_lex()->table_list.first;
+ lex->first_select_lex()->table_list= lex->save_list;
/*
When excluding union list from the global list we assume that
elements of the former immediately follow elements which represent
@@ -6482,7 +6425,7 @@ versioning_option:
{
if (DBUG_EVALUATE_IF("sysvers_force", 0, 1))
{
- my_error(ER_VERS_TEMPORARY, MYF(0));
+ my_error(ER_VERS_NOT_SUPPORTED, MYF(0), "CREATE TEMPORARY TABLE");
MYSQL_YYABORT;
}
}
@@ -6569,6 +6512,13 @@ create_field_list:
}
;
+create_field_list_parens:
+ LEFT_PAREN_ALT field_list ')'
+ {
+ Lex->create_last_non_select_table= Lex->last_table();
+ }
+ ;
+
field_list:
field_list_item
| field_list ',' field_list_item
@@ -6579,6 +6529,7 @@ field_list_item:
| key_def
| constraint_def
| period_for_system_time
+ | PERIOD_SYM period_for_application_time { }
;
column_def:
@@ -6675,7 +6626,7 @@ key_def:
constraint_def:
opt_constraint check_constraint
{
- Lex->add_constraint(&$1, $2, FALSE);
+ Lex->add_constraint($1, $2, FALSE);
}
;
@@ -6684,7 +6635,15 @@ period_for_system_time:
PERIOD_SYM FOR_SYSTEM_TIME_SYM '(' ident ',' ident ')'
{
Vers_parse_info &info= Lex->vers_get_info();
- info.set_system_time($4, $6);
+ info.set_period($4, $6);
+ }
+ ;
+
+period_for_application_time:
+ FOR_SYM ident '(' ident ',' ident ')'
+ {
+ if (Lex->add_period($2, $4, $6))
+ MYSQL_YYABORT;
}
;
@@ -6873,6 +6832,8 @@ parse_vcol_expr:
Prevent the end user from invoking this command.
*/
MYSQL_YYABORT_UNLESS(Lex->parse_vcol_expr);
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
}
expr
{
@@ -6880,17 +6841,12 @@ parse_vcol_expr:
if (unlikely(!v))
MYSQL_YYABORT;
Lex->last_field->vcol_info= v;
+ Lex->pop_select(); //main select
}
;
parenthesized_expr:
- subselect
- {
- $$= new (thd->mem_root) Item_singlerow_subselect(thd, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | expr
+ expr
| expr ',' expr_list
{
$3->push_front($1, thd->mem_root);
@@ -6909,6 +6865,16 @@ virtual_column_func:
MYSQL_YYABORT;
$$= v;
}
+ | subquery
+ {
+ Item *item;
+ if (!(item= new (thd->mem_root) Item_singlerow_subselect(thd, $1)))
+ MYSQL_YYABORT;
+ Virtual_column_info *v= add_virtual_expression(thd, item);
+ if (unlikely(!v))
+ MYSQL_YYABORT;
+ $$= v;
+ }
;
expr_or_literal: column_default_non_parenthesized_expr | signed_literal ;
@@ -7148,7 +7114,7 @@ field_type_lob:
| JSON_SYM opt_compressed
{
Lex->charset= &my_charset_utf8mb4_bin;
- $$.set(&type_handler_long_blob);
+ $$.set(&type_handler_json_longtext);
}
;
@@ -7876,23 +7842,25 @@ alter:
Lex->name= null_clex_str;
Lex->table_type= TABLE_TYPE_UNKNOWN;
Lex->sql_command= SQLCOM_ALTER_TABLE;
- Lex->duplicates= DUP_ERROR;
- Lex->select_lex.init_order();
+ Lex->duplicates= DUP_ERROR;
+ Lex->first_select_lex()->order_list.empty();
Lex->create_info.init();
Lex->create_info.row_type= ROW_TYPE_NOT_USED;
Lex->alter_info.reset();
Lex->no_write_to_binlog= 0;
Lex->create_info.storage_media= HA_SM_DEFAULT;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
DBUG_ASSERT(!Lex->m_sql_cmd);
}
alter_options TABLE_SYM table_ident opt_lock_wait_timeout
{
- if (unlikely(!Lex->select_lex.add_table_to_list(thd, $5, NULL,
- TL_OPTION_UPDATING,
- TL_READ_NO_INSERT,
- MDL_SHARED_UPGRADABLE)))
+ if (!Lex->first_select_lex()->
+ add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT, MDL_SHARED_UPGRADABLE))
MYSQL_YYABORT;
- Lex->select_lex.db= (Lex->select_lex.table_list.first)->db;
+ Lex->first_select_lex()->db=
+ (Lex->first_select_lex()->table_list.first)->db;
Lex->create_last_non_select_table= Lex->last_table();
Lex->mark_first_table_as_inserting();
}
@@ -7905,11 +7873,14 @@ alter:
if (unlikely(Lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
+ Lex->pop_select(); //main select
}
| ALTER DATABASE ident_or_empty
{
Lex->create_info.default_table_charset= NULL;
Lex->create_info.used_fields= 0;
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
create_database_options
{
@@ -7919,6 +7890,7 @@ alter:
if (lex->name.str == NULL &&
unlikely(lex->copy_db_to(&lex->name)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
}
| ALTER DATABASE ident UPGRADE_SYM DATA_SYM DIRECTORY_SYM NAME_SYM
{
@@ -7934,6 +7906,8 @@ alter:
if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"));
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
lex->sp_chistics.init();
}
sp_a_chistics
@@ -7942,6 +7916,9 @@ alter:
lex->sql_command= SQLCOM_ALTER_PROCEDURE;
lex->spname= $3;
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
}
| ALTER FUNCTION_SYM sp_name
{
@@ -7949,6 +7926,8 @@ alter:
if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"));
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
lex->sp_chistics.init();
}
sp_a_chistics
@@ -7957,14 +7936,23 @@ alter:
lex->sql_command= SQLCOM_ALTER_FUNCTION;
lex->spname= $3;
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
}
| ALTER view_algorithm definer_opt opt_view_suid VIEW_SYM table_ident
{
- if (unlikely(Lex->add_alter_view(thd, $2, $4, $6)))
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ if (Lex->add_alter_view(thd, $2, $4, $6))
MYSQL_YYABORT;
}
view_list_opt AS view_select
- {}
+ {
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
+ }
| ALTER definer_opt opt_view_suid VIEW_SYM table_ident
/*
We have two separate rules for ALTER VIEW rather that
@@ -7972,14 +7960,22 @@ alter:
with the ALTER EVENT below.
*/
{
- if (unlikely(Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5)))
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ if (Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5))
MYSQL_YYABORT;
}
view_list_opt AS view_select
- {}
+ {
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
+ }
| ALTER definer_opt remember_name EVENT_SYM sp_name
{
- /*
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ /*
It is safe to use Lex->spname because
ALTER EVENT xxx RENATE TO yyy DO ALTER EVENT RENAME TO
is not allowed. Lex->spname is used in the case of RENAME TO
@@ -8011,6 +8007,8 @@ alter:
*/
Lex->sql_command= SQLCOM_ALTER_EVENT;
Lex->stmt_definition_end= (char*)YYLIP->get_cpp_ptr();
+
+ Lex->pop_select(); //main select
}
| ALTER TABLESPACE alter_tablespace_info
{
@@ -8040,7 +8038,7 @@ alter:
} OPTIONS_SYM '(' server_options_list ')' { }
/* ALTER USER foo is allowed for MySQL compatibility. */
| ALTER USER_SYM opt_if_exists clear_privileges grant_list
- opt_require_clause opt_resource_options
+ opt_require_clause opt_resource_options opt_account_locking_and_opt_password_expiration
{
Lex->create_info.set($3);
Lex->sql_command= SQLCOM_ALTER_USER;
@@ -8054,16 +8052,17 @@ alter:
lex->create_info.init();
lex->no_write_to_binlog= 0;
DBUG_ASSERT(!lex->m_sql_cmd);
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
}
table_ident
{
LEX *lex= Lex;
- if (unlikely(!(lex->create_info.seq_create_info=
- new (thd->mem_root) sequence_definition())) ||
- unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL,
- TL_OPTION_SEQUENCE,
- TL_WRITE,
- MDL_EXCLUSIVE)))
+ if (!(lex->create_info.seq_create_info= new (thd->mem_root)
+ sequence_definition()) ||
+ !lex->first_select_lex()->
+ add_table_to_list(thd, $5, NULL, TL_OPTION_SEQUENCE,
+ TL_WRITE, MDL_EXCLUSIVE))
MYSQL_YYABORT;
}
sequence_defs
@@ -8072,9 +8071,52 @@ alter:
Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_sequence($3);
if (unlikely(Lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
}
;
+account_locking_option:
+ LOCK_SYM
+ {
+ Lex->account_options.account_locked= ACCOUNTLOCK_LOCKED;
+ }
+ | UNLOCK_SYM
+ {
+ Lex->account_options.account_locked= ACCOUNTLOCK_UNLOCKED;
+ }
+ ;
+
+opt_password_expire_option:
+ /* empty */
+ {
+ Lex->account_options.password_expire= PASSWORD_EXPIRE_NOW;
+ }
+ | NEVER_SYM
+ {
+ Lex->account_options.password_expire= PASSWORD_EXPIRE_NEVER;
+ }
+ | DEFAULT
+ {
+ Lex->account_options.password_expire= PASSWORD_EXPIRE_DEFAULT;
+ }
+ | INTERVAL_SYM NUM DAY_SYM
+ {
+ Lex->account_options.password_expire= PASSWORD_EXPIRE_INTERVAL;
+ if (!(Lex->account_options.num_expiration_days= atoi($2.str)))
+ my_yyabort_error((ER_WRONG_VALUE, MYF(0), "DAY", $2.str));
+ }
+ ;
+
+opt_account_locking_and_opt_password_expiration:
+ /* empty */
+ | ACCOUNT_SYM account_locking_option
+ | PASSWORD_SYM EXPIRE_SYM opt_password_expire_option
+ | ACCOUNT_SYM account_locking_option PASSWORD_SYM EXPIRE_SYM opt_password_expire_option
+ | PASSWORD_SYM EXPIRE_SYM opt_password_expire_option ACCOUNT_SYM account_locking_option
+ ;
+
ev_alter_on_schedule_completion:
/* empty */ { $$= 0;}
| ON SCHEDULE_SYM ev_schedule_time { $$= 1; }
@@ -8220,22 +8262,7 @@ alter_commands:
| EXCHANGE_SYM PARTITION_SYM alt_part_name_item
WITH TABLE_SYM table_ident have_partitioning
{
- LEX *lex= thd->lex;
- lex->select_lex.db= $6->db;
- if (lex->select_lex.db.str == NULL &&
- unlikely(lex->copy_db_to(&lex->select_lex.db)))
- MYSQL_YYABORT;
- lex->name= $6->table;
- lex->alter_info.partition_flags|= ALTER_PARTITION_EXCHANGE;
- if (unlikely(!lex->select_lex.add_table_to_list(thd, $6, NULL,
- TL_OPTION_UPDATING,
- TL_READ_NO_INSERT,
- MDL_SHARED_NO_WRITE)))
- MYSQL_YYABORT;
- DBUG_ASSERT(!lex->m_sql_cmd);
- lex->m_sql_cmd= new (thd->mem_root)
- Sql_cmd_alter_table_exchange_partition();
- if (unlikely(lex->m_sql_cmd == NULL))
+ if (Lex->stmt_alter_table_exchange_partition($6))
MYSQL_YYABORT;
}
;
@@ -8359,6 +8386,13 @@ alter_list_item:
{
Lex->alter_info.flags|= ALTER_ADD_PERIOD;
}
+ | ADD
+ PERIOD_SYM opt_if_not_exists_table_element period_for_application_time
+ {
+ Table_period_info &period= Lex->create_info.period_info;
+ period.create_if_not_exists= Lex->check_exists;
+ Lex->alter_info.flags|= ALTER_ADD_CHECK_CONSTRAINT;
+ }
| add_column '(' create_field_list ')'
{
LEX *lex=Lex;
@@ -8373,7 +8407,7 @@ alter_list_item:
| ADD CONSTRAINT IF_SYM not EXISTS field_ident check_constraint
{
Lex->alter_info.flags|= ALTER_ADD_CHECK_CONSTRAINT;
- Lex->add_constraint(&$6, $7, TRUE);
+ Lex->add_constraint($6, $7, TRUE);
}
| CHANGE opt_column opt_if_exists_table_element field_ident
field_spec opt_place
@@ -8471,9 +8505,9 @@ alter_list_item:
| RENAME opt_to table_ident
{
LEX *lex=Lex;
- lex->select_lex.db= $3->db;
- if (lex->select_lex.db.str == NULL &&
- unlikely(lex->copy_db_to(&lex->select_lex.db)))
+ lex->first_select_lex()->db= $3->db;
+ if (lex->first_select_lex()->db.str == NULL &&
+ lex->copy_db_to(&lex->first_select_lex()->db))
MYSQL_YYABORT;
if (unlikely(check_table_name($3->table.str,$3->table.length,
FALSE)) ||
@@ -8494,7 +8528,7 @@ alter_list_item:
$5->name, $4->csname));
if (unlikely(Lex->create_info.add_alter_list_item_convert_to_charset($5)))
MYSQL_YYABORT;
- Lex->alter_info.flags|= ALTER_OPTIONS;
+ Lex->alter_info.flags|= ALTER_CONVERT_TO;
}
| create_table_options_space_separated
{
@@ -8526,6 +8560,14 @@ alter_list_item:
{
Lex->alter_info.flags|= ALTER_DROP_PERIOD;
}
+ | DROP PERIOD_SYM opt_if_exists_table_element FOR_SYM ident
+ {
+ Alter_drop *ad= new Alter_drop(Alter_drop::PERIOD, $5.str, $3);
+ if (unlikely(ad == NULL))
+ MYSQL_YYABORT;
+ Lex->alter_info.drop_list.push_back(ad, thd->mem_root);
+ Lex->alter_info.flags|= ALTER_DROP_CHECK_CONSTRAINT;
+ }
;
opt_index_lock_algorithm:
@@ -8595,7 +8637,6 @@ alter_option:
}
;
-
opt_restrict:
/* empty */ { Lex->drop_mode= DROP_DEFAULT; }
| RESTRICT { Lex->drop_mode= DROP_RESTRICT; }
@@ -8874,6 +8915,7 @@ persistent_column_stat_spec:
}
table_column_list
')'
+ { }
;
persistent_index_stat_spec:
@@ -8887,6 +8929,7 @@ persistent_index_stat_spec:
}
table_index_list
')'
+ { }
;
table_column_list:
@@ -9029,9 +9072,13 @@ rename:
RENAME table_or_tables
{
Lex->sql_command= SQLCOM_RENAME_TABLE;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
}
table_to_table_list
- {}
+ {
+ Lex->pop_select(); //main select
+ }
| RENAME USER_SYM clear_privileges rename_list
{
Lex->sql_command = SQLCOM_RENAME_USER;
@@ -9129,9 +9176,13 @@ preload:
LEX *lex=Lex;
lex->sql_command=SQLCOM_PRELOAD_KEYS;
lex->alter_info.reset();
+ if (lex->main_select_push())
+ MYSQL_YYABORT;
}
preload_list_or_parts
- {}
+ {
+ Lex->pop_select(); //main select
+ }
;
preload_list_or_parts:
@@ -9176,8 +9227,8 @@ adm_partition:
cache_keys_spec:
{
- Lex->select_lex.alloc_index_hints(thd);
- Select->set_index_hint_type(INDEX_HINT_USE,
+ Lex->first_select_lex()->alloc_index_hints(thd);
+ Select->set_index_hint_type(INDEX_HINT_USE,
INDEX_HINT_MASK_ALL);
}
cache_key_list_or_empty
@@ -9200,243 +9251,387 @@ opt_ignore_leaves:
select:
- opt_with_clause select_init
+ query_expression_no_with_clause
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->current_select->set_with_clause($1);
+ if (Lex->push_select($1->fake_select_lex ?
+ $1->fake_select_lex :
+ $1->first_select()))
+ MYSQL_YYABORT;
+ }
+ opt_procedure_or_into
+ {
+ Lex->pop_select();
+ $1->set_with_clause(NULL);
+ if (Lex->select_finalize($1, $3))
+ MYSQL_YYABORT;
+ }
+ | with_clause query_expression_no_with_clause
+ {
+ if (Lex->push_select($2->fake_select_lex ?
+ $2->fake_select_lex :
+ $2->first_select()))
+ MYSQL_YYABORT;
+ }
+ opt_procedure_or_into
+ {
+ Lex->pop_select();
+ $2->set_with_clause($1);
+ $1->attach_to($2->first_select());
+ if (Lex->select_finalize($2, $4))
+ MYSQL_YYABORT;
}
;
-select_init:
- SELECT_SYM select_options_and_item_list select_init3
- | table_value_constructor
- | table_value_constructor union_list
- | table_value_constructor union_order_or_limit
- | '(' select_paren ')'
- | '(' select_paren ')' union_list
- | '(' select_paren ')' union_order_or_limit
- ;
-
-union_list_part2:
- SELECT_SYM select_options_and_item_list select_init3_union_query_term
- | table_value_constructor
- | table_value_constructor union_list
- | table_value_constructor union_order_or_limit
- | '(' select_paren_union_query_term ')'
- | '(' select_paren_union_query_term ')' union_list
- | '(' select_paren_union_query_term ')' union_order_or_limit
- ;
-
-select_paren:
+select_into:
+ select_into_query_specification
{
- Lex->current_select->set_braces(true);
+ if (Lex->push_select($1))
+ MYSQL_YYABORT;
}
- table_value_constructor select_part3
+ opt_order_limit_lock
{
- DBUG_ASSERT(Lex->current_select->braces);
+ SELECT_LEX_UNIT *unit;
+ if (!(unit = Lex->create_unit($1)))
+ MYSQL_YYABORT;
+ if ($3)
+ unit= Lex->add_tail_to_query_expression_body(unit, $3);
+ if (Lex->select_finalize(unit))
+ MYSQL_YYABORT;
}
- |
+ | with_clause
+ select_into_query_specification
{
- /*
- In order to correctly parse UNION's global ORDER BY we need to
- set braces before parsing the clause.
- */
- Lex->current_select->set_braces(true);
+ if (Lex->push_select($2))
+ MYSQL_YYABORT;
}
- SELECT_SYM select_options_and_item_list select_part3
- opt_select_lock_type
+ opt_order_limit_lock
{
- DBUG_ASSERT(Lex->current_select->braces);
+ SELECT_LEX_UNIT *unit;
+ if (!(unit = Lex->create_unit($2)))
+ MYSQL_YYABORT;
+ if ($4)
+ unit= Lex->add_tail_to_query_expression_body(unit, $4);
+ unit->set_with_clause($1);
+ $1->attach_to($2);
+ if (Lex->select_finalize(unit))
+ MYSQL_YYABORT;
}
- | '(' select_paren ')'
;
-select_parent_union_query_term_proper:
- SELECT_SYM select_options_and_item_list select_part3_union_query_term
- opt_select_lock_type
- | table_value_constructor select_part3_union_query_term
+simple_table:
+ query_specification { $$= $1; }
+ | table_value_constructor { $$= $1; }
;
-select_paren_union_query_term:
+table_value_constructor:
+ VALUES
+ {
+ if (Lex->parsed_TVC_start())
+ MYSQL_YYABORT;
+ }
+ values_list
+ {
+ if (!($$= Lex->parsed_TVC_end()))
+ MYSQL_YYABORT;
+ }
+ ;
+
+query_specification_start:
+ SELECT_SYM
{
- /*
- In order to correctly parse UNION's global ORDER BY we need to
- set braces before parsing the clause.
- */
- Lex->current_select->set_braces(true);
+ SELECT_LEX *sel;
+ LEX *lex= Lex;
+ if (!(sel= lex->alloc_select(TRUE)) ||
+ lex->push_select(sel))
+ MYSQL_YYABORT;
+ sel->init_select();
+ sel->braces= FALSE;
}
- select_parent_union_query_term_proper
+ select_options
{
- DBUG_ASSERT(Lex->current_select->braces);
+ Select->parsing_place= SELECT_LIST;
}
- | '(' select_paren_union_query_term ')'
- ;
-
-select_parent_view_proper:
- SELECT_SYM select_options_and_item_list select_part3_view
- opt_select_lock_type
- | table_value_constructor select_part3_view
- ;
+ select_item_list
+ {
+ Select->parsing_place= NO_MATTER;
+ }
+ ;
-select_paren_view:
+query_specification:
+ query_specification_start
+ opt_from_clause
+ opt_where_clause
+ opt_group_clause
+ opt_having_clause
+ opt_window_clause
{
- /*
- In order to correctly parse UNION's global ORDER BY we need to
- set braces before parsing the clause.
- */
- Lex->current_select->set_braces(true);
+ $$= Lex->pop_select();
}
- select_parent_view_proper
+ ;
+
+select_into_query_specification:
+ query_specification_start
+ into
+ opt_from_clause
+ opt_where_clause
+ opt_group_clause
+ opt_having_clause
+ opt_window_clause
{
- DBUG_ASSERT(Lex->current_select->braces);
+ $$= Lex->pop_select();
}
- | '(' select_paren_view ')'
;
-/* The equivalent of select_paren for nested queries. */
-select_paren_derived:
+/**
+
+ The following grammar for query expressions conformant to
+ the latest SQL Standard is supported:
+
+ <query expression> ::=
+ [ <with clause> ] <query expression body>
+ [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+
+ <with clause> ::=
+ WITH [ RECURSIVE ] <with_list
+
+ <with list> ::=
+ <with list element> [ { <comma> <with list element> }... ]
+
+ <with list element> ::=
+ <query name> [ '(' <with column list> ')' ]
+ AS <table subquery>
+
+ <with column list> ::=
+ <column name list>
+
+ <query expression body> ::
+ <query term>
+ | <query expression body> UNION [ ALL | DISTINCT ] <query term>
+ | <query expression body> EXCEPT [ DISTINCT ] <query term>
+
+ <query term> ::=
+ <query primary>
+ | <query term> INTERSECT [ DISTINCT ] <query primary>
+
+ <query primary> ::=
+ <simple table>
+ | '(' <query expression body>
+ [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+ ')'
+
+ <simple table>
+ <query specification>
+ | <table value constructor>
+
+ <subquery>
+ '(' <query_expression> ')'
+
+*/
+
+/*
+ query_expression produces the same expressions as
+ <query expression>
+*/
+
+query_expression:
+ query_expression_no_with_clause
{
- Lex->current_select->set_braces(true);
+ $1->set_with_clause(NULL);
+ $$= $1;
}
- table_value_constructor
+ | with_clause
+ query_expression_no_with_clause
{
- DBUG_ASSERT(Lex->current_select->braces);
- $$= Lex->current_select->master_unit()->first_select();
+ $2->set_with_clause($1);
+ $1->attach_to($2->first_select());
+ $$= $2;
}
- |
+ ;
+
+/*
+ query_expression_no_with_clause produces the same expressions as
+ <query expression> without [ <with clause> ]
+*/
+
+query_expression_no_with_clause:
+ query_expression_body_ext { $$= $1; }
+ | query_expression_body_ext_parens { $$= $1; }
+ ;
+
+/*
+ query_expression_body_ext produces the same expressions as
+ <query expression body>
+ [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+ | '('... <query expression body>
+ [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+ ')'...
+ Note: number of ')' must be equal to the number of '(' in the rule above
+*/
+
+query_expression_body_ext:
+ query_expression_body
{
- Lex->current_select->set_braces(true);
+ if ($1->first_select()->next_select())
+ {
+ if (Lex->parsed_multi_operand_query_expression_body($1))
+ MYSQL_YYABORT;
+ }
}
- SELECT_SYM select_part2_derived
- opt_table_expression
- opt_order_clause
- opt_limit_clause
- opt_select_lock_type
+ opt_query_expression_tail
{
- DBUG_ASSERT(Lex->current_select->braces);
- $$= Lex->current_select->master_unit()->first_select();
+ if (!$3)
+ $$= $1;
+ else
+ $$= Lex->add_tail_to_query_expression_body($1, $3);
}
- | '(' select_paren_derived ')' { $$= $2; }
- ;
-
-select_init3:
- opt_table_expression
- opt_select_lock_type
+ | query_expression_body_ext_parens
{
- /* Parentheses carry no meaning here */
- Lex->current_select->set_braces(false);
+ Lex->push_select(!$1->first_select()->next_select() ?
+ $1->first_select() : $1->fake_select_lex);
}
- union_clause
- | select_part3_union_not_ready
- opt_select_lock_type
+ query_expression_tail
{
- /* Parentheses carry no meaning here */
- Lex->current_select->set_braces(false);
+ if (!($$= Lex->add_tail_to_query_expression_body_ext_parens($1, $3)))
+ MYSQL_YYABORT;
}
;
-
-select_init3_union_query_term:
- opt_table_expression
- opt_select_lock_type
+query_expression_body_ext_parens:
+ '(' query_expression_body_ext_parens ')'
+ { $$= $2; }
+ | '(' query_expression_body_ext ')'
{
- /* Parentheses carry no meaning here */
- Lex->current_select->set_braces(false);
- }
- union_clause
- | select_part3_union_not_ready_noproc
- opt_select_lock_type
- {
- /* Parentheses carry no meaning here */
- Lex->current_select->set_braces(false);
+ SELECT_LEX *sel= $2->first_select()->next_select() ?
+ $2->fake_select_lex : $2->first_select();
+ sel->braces= true;
+ $$= $2;
}
;
+/*
+ query_expression_body produces the same expressions as
+ <query expression body>
+*/
-select_init3_view:
- opt_table_expression opt_select_lock_type
+query_expression_body:
+ query_simple
{
- Lex->current_select->set_braces(false);
+ Lex->push_select($1);
+ if (!($$= Lex->create_unit($1)))
+ MYSQL_YYABORT;
}
- | opt_table_expression opt_select_lock_type
+ | query_expression_body
+ unit_type_decl
{
- Lex->current_select->set_braces(false);
+ if (!$1->first_select()->next_select())
+ {
+ Lex->pop_select();
+ }
}
- union_list_view
- | order_or_limit opt_select_lock_type
+ query_primary
{
- Lex->current_select->set_braces(false);
+ if (!($$= Lex->add_primary_to_query_expression_body($1, $4,
+ $2.unit_type,
+ $2.distinct,
+ FALSE)))
+ MYSQL_YYABORT;
}
- | table_expression order_or_limit opt_select_lock_type
+ | query_expression_body_ext_parens
+ unit_type_decl
+ query_primary
{
- Lex->current_select->set_braces(false);
+ if (!($$= Lex->add_primary_to_query_expression_body_ext_parens(
+ $1, $3,
+ $2.unit_type,
+ $2.distinct)))
+ MYSQL_YYABORT;
}
;
/*
- The SELECT parts after select_item_list that cannot be followed by UNION.
+ query_primary produces the same expressions as
+ <query primary>
*/
-select_part3:
- opt_table_expression
- | select_part3_union_not_ready
+query_primary:
+ query_simple
+ { $$= $1; }
+ | query_expression_body_ext_parens
+ { $$= $1->first_select(); }
;
-select_part3_union_query_term:
- opt_table_expression
- | select_part3_union_not_ready_noproc
- ;
+/*
+ query_simple produces the same expressions as
+ <simple table>
+*/
-select_part3_view:
- opt_table_expression
- | order_or_limit
- | table_expression order_or_limit
+query_simple:
+ simple_table { $$= $1;}
;
-select_part3_union_not_ready:
- select_part3_union_not_ready_noproc
- | table_expression procedure_clause
- | table_expression order_or_limit procedure_clause
+subselect:
+ query_expression
+ {
+ if (!($$= Lex->parsed_subselect($1)))
+ YYABORT;
+ }
;
-select_part3_union_not_ready_noproc:
- order_or_limit
- | into opt_table_expression opt_order_clause opt_limit_clause
- | table_expression into
- | table_expression order_or_limit
- | table_expression order_or_limit into
- ;
+/*
+ subquery produces the same expressions as
+ <subquery>
+
+ Consider the production rule of the SQL Standard
+ subquery:
+ '(' query_expression')'
+
+ This rule is equivalent to the rule
+ subquery:
+ '(' query_expression_no_with_clause ')'
+ | '(' with_clause query_expression_no_with_clause ')'
+ that in its turn is equivalent to
+ subquery:
+ '(' query_expression_body_ext ')'
+ | query_expression_body_ext_parens
+ | '(' with_clause query_expression_no_with_clause ')'
+
+ The latter can be re-written into
+ subquery:
+ query_expression_body_ext_parens ')'
+ | '(' with_clause query_expression_no_with_clause ')'
+
+ The last rule allows us to resolve properly the shift/reduce conflict
+ when subquery is used in expressions such as in the following queries
+ select (select * from t1 limit 1) + t2.a from t2
+ select * from t1 where t1.a [not] in (select t2.a from t2)
+
+ In the rule below %prec SUBQUERY_AS_EXPR forces the parser to perform a shift
+ operation rather then a reduce operation when ')' is encountered and can be
+ considered as the last symbol a query expression.
+*/
-select_options_and_item_list:
+subquery:
+ query_expression_body_ext_parens %prec SUBQUERY_AS_EXPR
{
- LEX *lex= Lex;
- SELECT_LEX *sel= lex->current_select;
- if (sel->linkage != UNION_TYPE)
- mysql_init_select(lex);
- lex->current_select->parsing_place= SELECT_LIST;
+ if (!$1->fake_select_lex)
+ $1->first_select()->braces= false;
+ else
+ $1->fake_select_lex->braces= false;
+ if (!($$= Lex->parsed_subselect($1)))
+ YYABORT;
}
- select_options select_item_list
+ | '(' with_clause query_expression_no_with_clause ')'
{
- Select->parsing_place= NO_MATTER;
+ $3->set_with_clause($2);
+ $2->attach_to($3->first_select());
+ if (!($$= Lex->parsed_subselect($3)))
+ YYABORT;
}
;
-
-/**
- <table expression>, as in the SQL standard.
-*/
-table_expression:
- from_clause
- opt_where_clause
- opt_group_clause
- opt_having_clause
- opt_window_clause
- ;
-
-opt_table_expression:
- /* Empty */
- | table_expression
+opt_from_clause:
+ /* empty */ %prec EMPTY_FROM_CLAUSE
+ | from_clause
;
from_clause:
@@ -9486,8 +9681,9 @@ history_point:
TIMESTAMP TEXT_STRING
{
Item *item;
- if (!(item= create_temporal_literal(thd, $2.str, $2.length, YYCSCL,
- MYSQL_TYPE_DATETIME, true)))
+ if (!(item= type_handler_datetime.create_literal_item(thd,
+ $2.str, $2.length,
+ YYCSCL, true)))
MYSQL_YYABORT;
$$= Vers_history_point(VERS_TIMESTAMP, item);
}
@@ -9501,6 +9697,33 @@ history_point:
}
;
+for_portion_of_time_clause:
+ FOR_SYM PORTION_SYM OF_SYM remember_tok_start ident FROM
+ bit_expr TO_SYM bit_expr
+ {
+ if (unlikely(0 == strcasecmp($5.str, "SYSTEM_TIME")))
+ {
+ thd->parse_error(ER_SYNTAX_ERROR, $4);
+ MYSQL_YYABORT;
+ }
+ Lex->period_conditions.init(SYSTEM_TIME_FROM_TO,
+ Vers_history_point(VERS_TIMESTAMP, $7),
+ Vers_history_point(VERS_TIMESTAMP, $9),
+ $5);
+ }
+ ;
+
+opt_for_portion_of_time_clause:
+ /* empty */
+ {
+ $$= false;
+ }
+ | for_portion_of_time_clause
+ {
+ $$= true;
+ }
+ ;
+
opt_for_system_time_clause:
/* empty */
{
@@ -9540,59 +9763,70 @@ select_option:
query_expression_option
| SQL_NO_CACHE_SYM
{
- /*
- Allow this flag only on the first top-level SELECT statement, if
- SQL_CACHE wasn't specified, and only once per query.
- */
- if (unlikely(Lex->current_select != &Lex->select_lex))
- my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_NO_CACHE"));
- if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE))
- my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_CACHE", "SQL_NO_CACHE"));
- if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE))
+ /*
+ Allow this flag once per query.
+ */
+ if (Select->options & OPTION_NO_QUERY_CACHE)
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_NO_CACHE"));
-
- Lex->safe_to_cache_query=0;
- Lex->select_lex.options&= ~OPTION_TO_QUERY_CACHE;
- Lex->select_lex.sql_cache= SELECT_LEX::SQL_NO_CACHE;
+ Select->options|= OPTION_NO_QUERY_CACHE;
}
| SQL_CACHE_SYM
{
- /*
- Allow this flag only on the first top-level SELECT statement, if
- SQL_NO_CACHE wasn't specified, and only once per query.
- */
- if (unlikely(Lex->current_select != &Lex->select_lex))
- my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CACHE"));
- if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE))
- my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_NO_CACHE", "SQL_CACHE"));
- if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE))
+ /*
+ Allow this flag once per query.
+ */
+ if (Select->options & OPTION_TO_QUERY_CACHE)
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_CACHE"));
-
- Lex->safe_to_cache_query=1;
- Lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
- Lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE;
+ Select->options|= OPTION_TO_QUERY_CACHE;
}
;
-opt_select_lock_type:
- /* empty */
- | FOR_SYM UPDATE_SYM opt_lock_wait_timeout
+
+select_lock_type:
+ FOR_SYM UPDATE_SYM opt_lock_wait_timeout_new
{
- LEX *lex=Lex;
- lex->current_select->lock_type= TL_WRITE;
- lex->current_select->set_lock_for_tables(TL_WRITE, false);
- lex->safe_to_cache_query=0;
+ $$= $3;
+ $$.defined_lock= TRUE;
+ $$.update_lock= TRUE;
}
- | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout
+ | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout_new
{
- LEX *lex=Lex;
- lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS;
- lex->current_select->
- set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS, false);
- lex->safe_to_cache_query=0;
+ $$= $5;
+ $$.defined_lock= TRUE;
+ $$.update_lock= FALSE;
}
;
+
+opt_select_lock_type:
+ /* empty */
+ {
+ $$.empty();
+ }
+ | select_lock_type
+ {
+ $$= $1;
+ }
+ ;
+
+
+opt_lock_wait_timeout_new:
+ /* empty */
+ {
+ $$.empty();
+ }
+ | WAIT_SYM ulong_num
+ {
+ $$.defined_timeout= TRUE;
+ $$.timeout= $2;
+ }
+ | NOWAIT_SYM
+ {
+ $$.defined_timeout= TRUE;
+ $$.timeout= 0;
+ }
+ ;
+
select_item_list:
select_item_list ',' select_item
| select_item
@@ -9642,12 +9876,6 @@ remember_tok_start:
}
;
-remember_tok_end:
- {
- $$= (char*) YYLIP->get_tok_end();
- }
- ;
-
remember_name:
{
$$= (char*) YYLIP->get_cpp_tok_start();
@@ -9864,19 +10092,19 @@ expr:
;
predicate:
- predicate IN_SYM '(' subselect ')'
+ predicate IN_SYM subquery
{
- $$= new (thd->mem_root) Item_in_subselect(thd, $1, $4);
- if (unlikely($$ == NULL))
+ $$= new (thd->mem_root) Item_in_subselect(thd, $1, $3);
+ if (unlikely(!$$))
MYSQL_YYABORT;
}
- | predicate not IN_SYM '(' subselect ')'
+ | predicate not IN_SYM subquery
{
- Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $5);
- if (unlikely(item == NULL))
+ Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $4);
+ if (unlikely(!item))
MYSQL_YYABORT;
$$= negate_expression(thd, item);
- if (unlikely($$ == NULL))
+ if (unlikely(!$$))
MYSQL_YYABORT;
}
| predicate IN_SYM '(' expr ')'
@@ -10274,7 +10502,21 @@ column_default_non_parenthesized_expr:
| param_marker { $$= $1; }
| variable
| sum_expr
+ {
+ if (!Lex->select_stack_top)
+ {
+ my_error(ER_INVALID_GROUP_FUNC_USE, MYF(0));
+ MYSQL_YYABORT;
+ }
+ }
| window_func_expr
+ {
+ if (!Lex->select_stack_top)
+ {
+ my_error(ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION, MYF(0));
+ MYSQL_YYABORT;
+ }
+ }
| inverse_distribution_function
| ROW_SYM '(' expr ',' expr_list ')'
{
@@ -10390,6 +10632,11 @@ primary_expr:
column_default_non_parenthesized_expr
| explicit_cursor_attr
| '(' parenthesized_expr ')' { $$= $2; }
+ | subquery
+ {
+ if (!($$= Lex->create_item_query_expression(thd, $1->master_unit())))
+ MYSQL_YYABORT;
+ }
;
string_factor_expr:
@@ -10453,7 +10700,7 @@ function_call_keyword_timestamp:
}
| TIMESTAMP '(' expr ',' expr ')'
{
- $$= new (thd->mem_root) Item_func_add_time(thd, $3, $5, 1, 0);
+ $$= new (thd->mem_root) Item_func_timestamp(thd, $3, $5);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
@@ -11432,6 +11679,21 @@ window_func:
{
((Item_sum *) $1)->mark_as_window_func_sum_expr();
}
+ |
+ function_call_generic
+ {
+ Item* item = (Item*)$1;
+ /* Only UDF aggregate here possible */
+ if ((item == NULL) ||
+ (item->type() != Item::SUM_FUNC_ITEM)
+ || (((Item_sum *)item)->sum_func() != Item_sum::UDF_SUM_FUNC))
+ {
+ thd->parse_error();
+ MYSQL_YYABORT;
+ }
+
+ ((Item_sum *) $1)->mark_as_window_func_sum_expr();
+ }
;
simple_window_func:
@@ -11786,6 +12048,10 @@ cast_type_temporal:
DATE_SYM { $$.set(&type_handler_newdate); }
| TIME_SYM opt_field_length { $$.set(&type_handler_time2, 0, $2); }
| DATETIME opt_field_length { $$.set(&type_handler_datetime2, 0, $2); }
+ | INTERVAL_SYM DAY_SECOND_SYM field_length
+ {
+ $$.set(&type_handler_interval_DDhhmmssff, 0, $3);
+ }
;
opt_expr_list:
@@ -11796,9 +12062,7 @@ opt_expr_list:
expr_list:
expr
{
- $$= new (thd->mem_root) List<Item>;
- if (unlikely($$ == NULL) ||
- unlikely($$->push_back($1, thd->mem_root)))
+ if (unlikely(!($$= List<Item>::make(thd->mem_root, $1))))
MYSQL_YYABORT;
}
| expr_list ',' expr
@@ -11910,10 +12174,15 @@ esc_table_ref:
/* Equivalent to <table reference list> in the SQL:2003 standard. */
/* Warning - may return NULL in case of incomplete SELECT */
derived_table_list:
- esc_table_ref { $$=$1; }
+ esc_table_ref
+ {
+ $$=$1;
+ Select->add_joined_table($1);
+ }
| derived_table_list ',' esc_table_ref
{
MYSQL_YYABORT_UNLESS($1 && ($$=$3));
+ Select->add_joined_table($3);
}
;
@@ -11924,15 +12193,9 @@ derived_table_list:
*/
join_table:
/* INNER JOIN variants */
- /*
- Use %prec to evaluate production 'table_ref' before 'normal_join'
- so that [INNER | CROSS] JOIN is properly nested as other
- left-associative joins.
- */
table_ref normal_join table_ref %prec CONDITIONLESS_JOIN
{
MYSQL_YYABORT_UNLESS($1 && ($$=$3));
-
if (unlikely(Select->add_cross_joined_table($1, $3, $2)))
MYSQL_YYABORT;
}
@@ -11940,6 +12203,8 @@ join_table:
ON
{
MYSQL_YYABORT_UNLESS($1 && $3);
+ Select->add_joined_table($1);
+ Select->add_joined_table($3);
/* Change the current name resolution context to a local context. */
if (unlikely(push_new_name_resolution_context(thd, $1, $3)))
MYSQL_YYABORT;
@@ -11947,7 +12212,7 @@ join_table:
}
expr
{
- $3->straight=$2;
+ $3->straight=$2;
add_join_on(thd, $3, $6);
$3->on_context= Lex->pop_context();
Select->parsing_place= NO_MATTER;
@@ -11956,6 +12221,8 @@ join_table:
USING
{
MYSQL_YYABORT_UNLESS($1 && $3);
+ Select->add_joined_table($1);
+ Select->add_joined_table($3);
}
'(' using_list ')'
{
@@ -11966,6 +12233,8 @@ join_table:
| table_ref NATURAL inner_join table_factor
{
MYSQL_YYABORT_UNLESS($1 && ($$=$4));
+ Select->add_joined_table($1);
+ Select->add_joined_table($4);
$4->straight=$3;
add_join_natural($1,$4,NULL,Select);
}
@@ -11975,6 +12244,8 @@ join_table:
ON
{
MYSQL_YYABORT_UNLESS($1 && $5);
+ Select->add_joined_table($1);
+ Select->add_joined_table($5);
/* Change the current name resolution context to a local context. */
if (unlikely(push_new_name_resolution_context(thd, $1, $5)))
MYSQL_YYABORT;
@@ -11991,6 +12262,8 @@ join_table:
| table_ref LEFT opt_outer JOIN_SYM table_factor
{
MYSQL_YYABORT_UNLESS($1 && $5);
+ Select->add_joined_table($1);
+ Select->add_joined_table($5);
}
USING '(' using_list ')'
{
@@ -12001,6 +12274,8 @@ join_table:
| table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor
{
MYSQL_YYABORT_UNLESS($1 && $6);
+ Select->add_joined_table($1);
+ Select->add_joined_table($6);
add_join_natural($1,$6,NULL,Select);
$6->outer_join|=JOIN_TYPE_LEFT;
$$=$6;
@@ -12011,6 +12286,8 @@ join_table:
ON
{
MYSQL_YYABORT_UNLESS($1 && $5);
+ Select->add_joined_table($1);
+ Select->add_joined_table($5);
/* Change the current name resolution context to a local context. */
if (unlikely(push_new_name_resolution_context(thd, $1, $5)))
MYSQL_YYABORT;
@@ -12028,6 +12305,8 @@ join_table:
| table_ref RIGHT opt_outer JOIN_SYM table_factor
{
MYSQL_YYABORT_UNLESS($1 && $5);
+ Select->add_joined_table($1);
+ Select->add_joined_table($5);
}
USING '(' using_list ')'
{
@@ -12039,6 +12318,8 @@ join_table:
| table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor
{
MYSQL_YYABORT_UNLESS($1 && $6);
+ Select->add_joined_table($1);
+ Select->add_joined_table($6);
add_join_natural($6,$1,NULL,Select);
LEX *lex= Lex;
if (unlikely(!($$= lex->current_select->convert_right_join())))
@@ -12071,326 +12352,95 @@ use_partition:
PARTITION_SYM '(' using_list ')' have_partitioning
{
$$= $3;
+ Select->parsing_place= Select->save_parsing_place;
+ Select->save_parsing_place= NO_MATTER;
}
;
-
-/*
- This is a flattening of the rules <table factor> and <table primary>
- in the SQL:2003 standard, since we don't have <sample clause>
- I.e.
- <table factor> ::= <table primary> [ <sample clause> ]
-*/
-/* Warning - may return NULL in case of incomplete SELECT */
table_factor:
- table_primary_ident
- | table_primary_derived
- ;
-
-table_primary_ident:
- {
- DBUG_ASSERT(Select);
- SELECT_LEX *sel= Select;
- sel->table_join_options= 0;
- }
- table_ident opt_use_partition opt_for_system_time_clause opt_table_alias opt_key_definition
- {
- if (unlikely(!($$= Select->add_table_to_list(thd, $2, $5,
- Select->get_table_join_options(),
- YYPS->m_lock_type,
- YYPS->m_mdl_type,
- Select->
- pop_index_hints(),
- $3))))
- MYSQL_YYABORT;
- TABLE_LIST *tl= $$;
- Select->add_joined_table(tl);
- if ($4)
- tl->vers_conditions= Lex->vers_conditions;
- }
- ;
-
-
-
-/*
- Represents a flattening of the following rules from the SQL:2003
- standard. This sub-rule corresponds to the sub-rule
- <table primary> ::= ... | <derived table> [ AS ] <correlation name>
-
- <derived table> ::= <table subquery>
- <table subquery> ::= <subquery>
- <subquery> ::= <left paren> <query expression> <right paren>
- <query expression> ::= [ <with clause> ] <query expression body>
-
- For the time being we use the non-standard rule
- select_derived_union which is a compromise between the standard
- and our parser. Possibly this rule could be replaced by our
- query_expression_body.
-*/
-
-table_primary_derived:
- '(' get_select_lex select_derived_union ')' opt_for_system_time_clause opt_table_alias
- {
- /* Use $2 instead of Lex->current_select as derived table will
- alter value of Lex->current_select. */
- if (!($3 || $6) && $2->embedding &&
- !$2->embedding->nested_join->join_list.elements)
- {
- /* we have a derived table ($3 == NULL) but no alias,
- Since we are nested in further parentheses so we
- can pass NULL to the outer level parentheses
- Permits parsing of "((((select ...))) as xyz)" */
- $$= 0;
- }
- else if (!$3)
- {
- /* Handle case of derived table, alias may be NULL if there
- are no outer parentheses, add_table_to_list() will throw
- error in this case */
- LEX *lex=Lex;
- lex->check_automatic_up(UNSPECIFIED_TYPE);
- SELECT_LEX *sel= lex->current_select;
- SELECT_LEX_UNIT *unit= sel->master_unit();
- lex->current_select= sel= unit->outer_select();
- Table_ident *ti= new (thd->mem_root) Table_ident(unit);
- if (unlikely(ti == NULL))
- MYSQL_YYABORT;
- if (unlikely(!($$= sel->add_table_to_list(thd,
- ti, $6, 0,
- TL_READ,
- MDL_SHARED_READ))))
- MYSQL_YYABORT;
- sel->add_joined_table($$);
- lex->pop_context();
- lex->nest_level--;
- }
- else if (unlikely($6 != NULL))
- {
- /*
- Tables with or without joins within parentheses cannot
- have aliases, and we ruled out derived tables above.
- */
- thd->parse_error();
- MYSQL_YYABORT;
- }
- else
- {
- /* nested join: FROM (t1 JOIN t2 ...),
- nest_level is the same as in the outer query */
- $$= $3;
- }
- /*
- Fields in derived table can be used in upper select in
- case of merge. We do not add HAVING fields because we do
- not merge such derived. We do not add union because
- also do not merge them
- */
- if ($$ && $$->derived &&
- !$$->derived->first_select()->next_select())
- $$->select_lex->add_where_field($$->derived->first_select());
- if ($5)
- {
- MYSQL_YYABORT_UNLESS(!$3);
- $$->vers_conditions= Lex->vers_conditions;
- }
+ table_primary_ident_opt_parens { $$= $1; }
+ | table_primary_derived_opt_parens { $$= $1; }
+ | join_table_parens
+ {
+ $1->nested_join->nest_type= 0;
+ $$= $1;
}
- /* Represents derived table with WITH clause */
- | '(' get_select_lex subselect_start
- with_clause query_expression_body
- subselect_end ')' opt_for_system_time_clause opt_table_alias
- {
- LEX *lex=Lex;
- SELECT_LEX *sel= $2;
- SELECT_LEX_UNIT *unit= $5->master_unit();
- Table_ident *ti= new (thd->mem_root) Table_ident(unit);
- if (unlikely(ti == NULL))
- MYSQL_YYABORT;
- $5->set_with_clause($4);
- lex->current_select= sel;
- if (unlikely(!($$= sel->add_table_to_list(lex->thd,
- ti, $9, 0,
- TL_READ,
- MDL_SHARED_READ))))
- MYSQL_YYABORT;
- sel->add_joined_table($$);
- if ($8)
- $$->vers_conditions= Lex->vers_conditions;
- }
+ | table_reference_list_parens { $$= $1; }
;
-/*
- This rule accepts just about anything. The reason is that we have
- empty-producing rules in the beginning of rules, in this case
- subselect_start. This forces bison to take a decision which rules to
- reduce by long before it has seen any tokens. This approach ties us
- to a very limited class of parseable languages, and unfortunately
- SQL is not one of them. The chosen 'solution' was this rule, which
- produces just about anything, even complete bogus statements, for
- instance ( table UNION SELECT 1 ).
- Fortunately, we know that the semantic value returned by
- select_derived is NULL if it contained a derived table, and a pointer to
- the base table's TABLE_LIST if it was a base table. So in the rule
- regarding union's, we throw a parse error manually and pretend it
- was bison that did it.
-
- Also worth noting is that this rule concerns query expressions in
- the from clause only. Top level select statements and other types of
- subqueries have their own union rules.
-*/
-select_derived_union:
- select_derived
- | select_derived union_order_or_limit
- {
- if (unlikely($1))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- }
- | select_derived union_head_non_top
- {
- if (unlikely($1))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- }
- union_list_derived_part2
- | derived_simple_table opt_select_lock_type
- | derived_simple_table order_or_limit opt_select_lock_type
- | derived_simple_table opt_select_lock_type union_list_derived
- ;
-
-union_list_derived_part2:
- query_term_union_not_ready { Lex->pop_context(); }
- | query_term_union_ready { Lex->pop_context(); }
- | query_term_union_ready { Lex->pop_context(); } union_list_derived
- ;
-
-union_list_derived:
- union_head_non_top union_list_derived_part2
- ;
-
-
-/* The equivalent of select_init2 for nested queries. */
-select_init2_derived:
- select_part2_derived
- {
- Select->set_braces(0);
- }
+table_primary_ident_opt_parens:
+ table_primary_ident { $$= $1; }
+ | '(' table_primary_ident_opt_parens ')' { $$= $2; }
;
-/* The equivalent of select_part2 for nested queries. */
-select_part2_derived:
- {
- LEX *lex= Lex;
- SELECT_LEX *sel= lex->current_select;
- if (sel->linkage != UNION_TYPE)
- mysql_init_select(lex);
- lex->current_select->parsing_place= SELECT_LIST;
- }
- opt_query_expression_options select_item_list
- {
- Select->parsing_place= NO_MATTER;
- }
+table_primary_derived_opt_parens:
+ table_primary_derived { $$= $1; }
+ | '(' table_primary_derived_opt_parens ')' { $$= $2; }
;
-/* handle contents of parentheses in join expression */
-select_derived:
- get_select_lex_derived derived_table_list
+table_reference_list_parens:
+ '(' table_reference_list_parens ')' { $$= $2; }
+ | '(' nested_table_reference_list ')'
{
- LEX *lex= Lex;
- /* for normal joins, $2 != NULL and end_nested_join() != NULL,
- for derived tables, both must equal NULL */
-
- if (unlikely(!($$= $1->end_nested_join(lex->thd)) && $2))
+ if (!($$= Select->end_nested_join(thd)))
MYSQL_YYABORT;
- if (unlikely(!$2 && $$))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- }
- ;
-
-derived_simple_table:
- derived_query_specification { $$= $1; }
- | derived_table_value_constructor { $$= $1; }
- ;
-/*
- Similar to query_specification, but for derived tables.
- Example: the inner parenthesized SELECT in this query:
- SELECT * FROM (SELECT * FROM t1);
-*/
-derived_query_specification:
- SELECT_SYM select_derived_init select_derived2
- {
- if ($2)
- Select->set_braces(1);
- $$= NULL;
}
;
-derived_table_value_constructor:
- VALUES
+nested_table_reference_list:
+ table_ref ',' table_ref
{
- if (Lex->tvc_start_derived())
+ if (Select->init_nested_join(thd))
MYSQL_YYABORT;
+ Select->add_joined_table($1);
+ Select->add_joined_table($3);
+ $$= $1->embedding;
}
- values_list
+ | nested_table_reference_list ',' table_ref
{
- if (Lex->tvc_finalize_derived())
- MYSQL_YYABORT;
- $$= NULL;
+ Select->add_joined_table($3);
+ $$= $1;
}
;
-
-select_derived2:
+join_table_parens:
+ '(' join_table_parens ')' { $$= $2; }
+ | '(' join_table ')'
{
LEX *lex= Lex;
- lex->derived_tables|= DERIVED_SUBQUERY;
- if (unlikely(!lex->expr_allows_subselect ||
- lex->sql_command == (int)SQLCOM_PURGE))
+ if (!($$= lex->current_select->nest_last_join(thd)))
{
thd->parse_error();
MYSQL_YYABORT;
}
- if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE ||
- unlikely(mysql_new_select(lex, 1, NULL)))
- MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->current_select->linkage= DERIVED_TABLE_TYPE;
- lex->current_select->parsing_place= SELECT_LIST;
}
- select_options select_item_list
- {
- Select->parsing_place= NO_MATTER;
- }
- opt_table_expression
;
-get_select_lex:
- /* Empty */ { $$= Select; }
- ;
-get_select_lex_derived:
- get_select_lex
+table_primary_ident:
+ table_ident opt_use_partition opt_for_system_time_clause
+ opt_table_alias_clause opt_key_definition
{
- LEX *lex= Lex;
- if (unlikely($1->init_nested_join(lex->thd)))
+ SELECT_LEX *sel= Select;
+ sel->table_join_options= 0;
+ if (!($$= Select->add_table_to_list(thd, $1, $4,
+ Select->get_table_join_options(),
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type,
+ Select->pop_index_hints(),
+ $2)))
MYSQL_YYABORT;
+ if ($3)
+ $$->vers_conditions= Lex->vers_conditions;
}
- ;
+ ;
-select_derived_init:
+table_primary_derived:
+ subquery
+ opt_for_system_time_clause table_alias_clause
{
- LEX *lex= Lex;
-
- TABLE_LIST *embedding= lex->current_select->embedding;
- $$= embedding &&
- !embedding->nested_join->join_list.elements;
- /* return true if we are deeply nested */
+ if (!($$= Lex->parsed_derived_table($1->master_unit(), $2, $3)))
+ MYSQL_YYABORT;
}
;
@@ -12524,9 +12574,13 @@ table_alias:
| '='
;
-opt_table_alias:
+opt_table_alias_clause:
/* empty */ { $$=0; }
- | table_alias ident_table_alias
+ | table_alias_clause { $$= $1; }
+ ;
+
+table_alias_clause:
+ table_alias ident_table_alias
{
$$= (LEX_CSTRING*) thd->memdup(&$2,sizeof(LEX_STRING));
if (unlikely($$ == NULL))
@@ -12605,7 +12659,7 @@ olap_opt:
SQL-2003: GROUP BY ... CUBE(col1, col2, col3)
*/
LEX *lex=Lex;
- if (unlikely(lex->current_select->linkage == GLOBAL_OPTIONS_TYPE))
+ if (unlikely(lex->current_select->get_linkage() == GLOBAL_OPTIONS_TYPE))
my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH CUBE",
"global union parameters"));
lex->current_select->olap= CUBE_TYPE;
@@ -12622,7 +12676,7 @@ olap_opt:
SQL-2003: GROUP BY ... ROLLUP(col1, col2, col3)
*/
LEX *lex= Lex;
- if (unlikely(lex->current_select->linkage == GLOBAL_OPTIONS_TYPE))
+ if (unlikely(lex->current_select->get_linkage() == GLOBAL_OPTIONS_TYPE))
my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH ROLLUP",
"global union parameters"));
lex->current_select->olap= ROLLUP_TYPE;
@@ -12638,7 +12692,7 @@ opt_window_clause:
{}
| WINDOW_SYM
window_def_list
- {}
+ {}
;
window_def_list:
@@ -12664,6 +12718,7 @@ window_spec:
opt_window_ref opt_window_partition_clause
opt_window_order_clause opt_window_frame_clause
')'
+ { }
;
opt_window_ref:
@@ -12683,7 +12738,7 @@ opt_window_partition_clause:
opt_window_order_clause:
/* empty */ { }
- | ORDER_SYM BY order_list
+ | ORDER_SYM BY order_list { Select->order_list= *($3); }
;
opt_window_frame_clause:
@@ -12809,70 +12864,35 @@ alter_order_item:
opt_order_clause:
/* empty */
+ { $$= NULL; }
| order_clause
+ { $$= $1; }
;
order_clause:
ORDER_SYM BY
{
- LEX *lex=Lex;
- SELECT_LEX *sel= lex->current_select;
- SELECT_LEX_UNIT *unit= sel-> master_unit();
- if (unlikely(sel->linkage != GLOBAL_OPTIONS_TYPE &&
- sel->olap != UNSPECIFIED_OLAP_TYPE &&
- (sel->linkage != UNION_TYPE || sel->braces)))
- {
- my_error(ER_WRONG_USAGE, MYF(0),
- "CUBE/ROLLUP", "ORDER BY");
- MYSQL_YYABORT;
- }
- if (lex->sql_command != SQLCOM_ALTER_TABLE &&
- !unit->fake_select_lex)
- {
- /*
- A query of the of the form (SELECT ...) ORDER BY order_list is
- executed in the same way as the query
- SELECT ... ORDER BY order_list
- unless the SELECT construct contains ORDER BY or LIMIT clauses.
- Otherwise we create a fake SELECT_LEX if it has not been
- created yet.
- */
- SELECT_LEX *first_sl= unit->first_select();
- if (unlikely(!unit->is_unit_op() &&
- (first_sl->order_list.elements ||
- first_sl->select_limit) &&
- unit->add_fake_select_lex(thd)))
- MYSQL_YYABORT;
- }
- if (sel->master_unit()->is_unit_op() && !sel->braces)
- {
- /*
- At this point we don't know yet whether this is the last
- select in union or not, but we move ORDER BY to
- fake_select_lex anyway. If there would be one more select
- in union mysql_new_select will correctly throw error.
- */
- DBUG_ASSERT(sel->master_unit()->fake_select_lex);
- lex->current_select= sel->master_unit()->fake_select_lex;
- }
+ thd->where= "ORDER clause";
}
order_list
{
-
+ $$= $4;
}
;
order_list:
order_list ',' order_ident order_dir
{
- if (unlikely(add_order_to_list(thd, $3,(bool) $4)))
- MYSQL_YYABORT;
- }
+ $$= $1;
+ if (add_to_list(thd, *$$, $3,(bool) $4))
+ MYSQL_YYABORT;
+ }
| order_ident order_dir
{
- if (unlikely(add_order_to_list(thd, $1,(bool) $2)))
+ $$= new (thd->mem_root) SQL_I_List<ORDER>();
+ if (add_to_list(thd, *$$, $1, (bool) $2))
MYSQL_YYABORT;
- }
+ }
;
order_dir:
@@ -12882,63 +12902,62 @@ order_dir:
;
opt_limit_clause:
- /* empty */ {}
- | limit_clause {}
+ /* empty */
+ { $$.empty(); }
+ | limit_clause
+ { $$= $1; }
;
-limit_clause_init:
- LIMIT
- {
- SELECT_LEX *sel= Select;
- if (sel->master_unit()->is_unit_op() && !sel->braces)
- {
- /* Move LIMIT that belongs to UNION to fake_select_lex */
- Lex->current_select= sel->master_unit()->fake_select_lex;
- DBUG_ASSERT(Select);
- }
- }
- ;
-
limit_clause:
- limit_clause_init limit_options
+ LIMIT limit_options
{
- SELECT_LEX *sel= Select;
- if (!sel->select_limit->basic_const_item() ||
- sel->select_limit->val_int() > 0)
+ $$= $2;
+ if (!$$.select_limit->basic_const_item() ||
+ $$.select_limit->val_int() > 0)
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
}
- | limit_clause_init limit_options
+ | LIMIT limit_options
ROWS_SYM EXAMINED_SYM limit_rows_option
{
+ $$= $2;
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
}
- | limit_clause_init ROWS_SYM EXAMINED_SYM limit_rows_option
+ | LIMIT ROWS_SYM EXAMINED_SYM limit_rows_option
{
+ $$.select_limit= 0;
+ $$.offset_limit= 0;
+ $$.explicit_limit= 0;
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
}
;
+opt_global_limit_clause:
+ opt_limit_clause
+ {
+ Select->explicit_limit= $1.explicit_limit;
+ Select->select_limit= $1.select_limit;
+ Select->offset_limit= $1.offset_limit;
+ }
+ ;
+
limit_options:
limit_option
{
- SELECT_LEX *sel= Select;
- sel->select_limit= $1;
- sel->offset_limit= 0;
- sel->explicit_limit= 1;
+ $$.select_limit= $1;
+ $$.offset_limit= 0;
+ $$.explicit_limit= 1;
}
| limit_option ',' limit_option
{
- SELECT_LEX *sel= Select;
- sel->select_limit= $3;
- sel->offset_limit= $1;
- sel->explicit_limit= 1;
+ $$.select_limit= $3;
+ $$.offset_limit= $1;
+ $$.explicit_limit= 1;
}
| limit_option OFFSET_SYM limit_option
{
- SELECT_LEX *sel= Select;
- sel->select_limit= $1;
- sel->offset_limit= $3;
- sel->explicit_limit= 1;
+ $$.select_limit= $1;
+ $$.offset_limit= $3;
+ $$.explicit_limit= 1;
}
;
@@ -13002,6 +13021,88 @@ delete_limit_clause:
| LIMIT limit_option ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; }
;
+order_limit_lock:
+ order_or_limit
+ {
+ $$= $1;
+ $$->lock.empty();
+ }
+ | order_or_limit select_lock_type
+ {
+ $$= $1;
+ $$->lock= $2;
+ }
+ | select_lock_type
+ {
+ $$= new(thd->mem_root) Lex_order_limit_lock;
+ if (!$$)
+ YYABORT;
+ $$->order_list= NULL;
+ $$->limit.empty();
+ $$->lock= $1;
+ }
+ ;
+
+opt_order_limit_lock:
+ /* empty */
+ {
+ Lex->pop_select();
+ $$= NULL;
+ }
+ | order_limit_lock { $$= $1; }
+ ;
+
+query_expression_tail:
+ order_limit_lock
+ ;
+
+opt_query_expression_tail:
+ opt_order_limit_lock
+ ;
+
+opt_procedure_or_into:
+ /* empty */
+ {
+ $$.empty();
+ }
+ | procedure_clause opt_select_lock_type
+ {
+ $$= $2;
+ }
+ | into opt_select_lock_type
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WARN_DEPRECATED_SYNTAX,
+ ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX),
+ "<select expression> INTO <destination>;",
+ "'SELECT <select list> INTO <destination>"
+ " FROM...'");
+ $$= $2;
+ }
+ ;
+
+order_or_limit:
+ order_clause opt_limit_clause
+ {
+ $$= new(thd->mem_root) Lex_order_limit_lock;
+ if (!$$)
+ YYABORT;
+ $$->order_list= $1;
+ $$->limit= $2;
+ }
+ | limit_clause
+ {
+ Lex_order_limit_lock *op= $$= new(thd->mem_root) Lex_order_limit_lock;
+ if (!$$)
+ YYABORT;
+ op->order_list= NULL;
+ op->limit= $1;
+ $$->order_list= NULL;
+ $$->limit= $1;
+ }
+ ;
+
+
opt_plus:
/* empty */
| '+'
@@ -13078,8 +13179,6 @@ procedure_clause:
{
LEX *lex=Lex;
- DBUG_ASSERT(&lex->select_lex == lex->current_select);
-
lex->proc_list.elements=0;
lex->proc_list.first=0;
lex->proc_list.next= &lex->proc_list.first;
@@ -13094,16 +13193,16 @@ procedure_clause:
/*
PROCEDURE CLAUSE cannot handle subquery as one of its parameter,
- so set expr_allows_subselect as false to disallow any subqueries
- further. Reset expr_allows_subselect back to true once the
- parameters are reduced.
+ so disallow any subqueries further.
+ Alow subqueries back once the parameters are reduced.
*/
- Lex->expr_allows_subselect= false;
+ Lex->clause_that_disallows_subselect= "PROCEDURE";
+ Select->options|= OPTION_PROCEDURE_CLAUSE;
}
'(' procedure_list ')'
{
/* Subqueries are allowed from now.*/
- Lex->expr_allows_subselect= true;
+ Lex->clause_that_disallows_subselect= NULL;
}
;
@@ -13182,6 +13281,7 @@ select_outvar:
into:
INTO into_destination
+ {}
;
into_destination:
@@ -13229,11 +13329,14 @@ do:
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_DO;
+ if (lex->main_select_push(true))
+ MYSQL_YYABORT;
mysql_init_select(lex);
}
expr_list
{
Lex->insert_list= $3;
+ Lex->pop_select(); //main select
}
;
@@ -13251,11 +13354,16 @@ drop:
}
table_list opt_lock_wait_timeout opt_restrict
{}
- | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident opt_lock_wait_timeout
+ | DROP INDEX_SYM
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ opt_if_exists_table_element ident ON table_ident opt_lock_wait_timeout
{
LEX *lex=Lex;
Alter_drop *ad= (new (thd->mem_root)
- Alter_drop(Alter_drop::KEY, $4.str, $3));
+ Alter_drop(Alter_drop::KEY, $5.str, $4));
if (unlikely(ad == NULL))
MYSQL_YYABORT;
lex->sql_command= SQLCOM_DROP_INDEX;
@@ -13263,10 +13371,11 @@ drop:
lex->alter_info.flags= ALTER_DROP_INDEX;
lex->alter_info.drop_list.push_back(ad, thd->mem_root);
if (unlikely(!lex->current_select->
- add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING,
+ add_table_to_list(thd, $7, NULL, TL_OPTION_UPDATING,
TL_READ_NO_INSERT,
MDL_SHARED_UPGRADABLE)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
}
| DROP DATABASE opt_if_exists ident
{
@@ -13375,10 +13484,11 @@ table_list:
table_name:
table_ident
{
- if (unlikely(!Select->add_table_to_list(thd, $1, NULL,
- TL_OPTION_UPDATING,
- YYPS->m_lock_type,
- YYPS->m_mdl_type)))
+ if (!thd->lex->current_select_or_default()->
+ add_table_to_list(thd, $1, NULL,
+ TL_OPTION_UPDATING,
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type))
MYSQL_YYABORT;
}
;
@@ -13451,17 +13561,23 @@ insert:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_INSERT;
- lex->duplicates= DUP_ERROR;
+ lex->duplicates= DUP_ERROR;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
mysql_init_select(lex);
+ lex->current_select->parsing_place= BEFORE_OPT_LIST;
}
insert_lock_option
opt_ignore insert2
{
Select->set_lock_for_tables($3, true);
- Lex->current_select= &Lex->select_lex;
+ Lex->current_select= Lex->first_select_lex();
}
insert_field_spec opt_insert_update
{
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
Lex->mark_first_table_as_inserting();
}
;
@@ -13472,15 +13588,21 @@ replace:
LEX *lex=Lex;
lex->sql_command = SQLCOM_REPLACE;
lex->duplicates= DUP_REPLACE;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
mysql_init_select(lex);
+ lex->current_select->parsing_place= BEFORE_OPT_LIST;
}
replace_lock_option insert2
{
Select->set_lock_for_tables($3, true);
- Lex->current_select= &Lex->select_lex;
+ Lex->current_select= Lex->first_select_lex();
}
insert_field_spec
{
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
Lex->mark_first_table_as_inserting();
}
;
@@ -13522,10 +13644,13 @@ insert2:
;
insert_table:
+ {
+ Select->save_parsing_place= Select->parsing_place;
+ }
table_name_with_opt_use_partition
{
LEX *lex=Lex;
- lex->field_list.empty();
+ //lex->field_list.empty();
lex->many_values.empty();
lex->insert_list=0;
}
@@ -13533,8 +13658,7 @@ insert_table:
insert_field_spec:
insert_values {}
- | '(' ')' insert_values {}
- | '(' fields ')' insert_values {}
+ | insert_field_list insert_values {}
| SET
{
LEX *lex=Lex;
@@ -13542,20 +13666,33 @@ insert_field_spec:
unlikely(lex->many_values.push_back(lex->insert_list,
thd->mem_root)))
MYSQL_YYABORT;
+ lex->current_select->parsing_place= NO_MATTER;
}
ident_eq_list
;
+insert_field_list:
+ LEFT_PAREN_ALT opt_fields ')'
+ {
+ Lex->current_select->parsing_place= AFTER_LIST;
+ }
+ ;
+
+opt_fields:
+ /* empty */
+ | fields
+ ;
+
fields:
fields ',' insert_ident
{ Lex->field_list.push_back($3, thd->mem_root); }
| insert_ident { Lex->field_list.push_back($1, thd->mem_root); }
;
+
+
insert_values:
- VALUES values_list {}
- | VALUE_SYM values_list {}
- | create_select_query_expression {}
+ create_select_query_expression {}
;
values_list:
@@ -13699,23 +13836,43 @@ opt_insert_update:
}
;
+update_table_list:
+ table_ident opt_use_partition for_portion_of_time_clause
+ opt_table_alias_clause opt_key_definition
+ {
+ SELECT_LEX *sel= Select;
+ sel->table_join_options= 0;
+ if (!($$= Select->add_table_to_list(thd, $1, $4,
+ Select->get_table_join_options(),
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type,
+ Select->pop_index_hints(),
+ $2)))
+ MYSQL_YYABORT;
+ $$->period_conditions= Lex->period_conditions;
+ }
+ | join_table_list { $$= $1; }
+ ;
+
/* Update rows in a table */
update:
UPDATE_SYM
{
LEX *lex= Lex;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
mysql_init_select(lex);
lex->sql_command= SQLCOM_UPDATE;
lex->duplicates= DUP_ERROR;
}
- opt_low_priority opt_ignore join_table_list
+ opt_low_priority opt_ignore update_table_list
SET update_list
{
- SELECT_LEX *slex= &Lex->select_lex;
+ SELECT_LEX *slex= Lex->first_select_lex();
if (slex->table_list.elements > 1)
Lex->sql_command= SQLCOM_UPDATE_MULTI;
- else if (unlikely(slex->get_table_list()->derived))
+ else if (slex->get_table_list()->derived)
{
/* it is single table update and it is update of derived table */
my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
@@ -13729,7 +13886,14 @@ update:
*/
slex->set_lock_for_tables($3, slex->table_list.elements == 1);
}
- opt_where_clause opt_order_clause delete_limit_clause {}
+ opt_where_clause opt_order_clause delete_limit_clause
+ {
+ if ($10)
+ Select->order_list= *($10);
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
+ }
;
update_list:
@@ -13773,12 +13937,13 @@ delete:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_DELETE;
- mysql_init_select(lex);
YYPS->m_lock_type= TL_WRITE_DEFAULT;
YYPS->m_mdl_type= MDL_SHARED_WRITE;
-
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ mysql_init_select(lex);
lex->ignore= 0;
- lex->select_lex.init_order();
+ lex->first_select_lex()->order_list.empty();
}
delete_part2
;
@@ -13799,6 +13964,7 @@ delete_part2:
| HISTORY_SYM delete_single_table opt_delete_system_time
{
Lex->last_table()->vers_conditions= Lex->vers_conditions;
+ Lex->pop_select(); //main select
}
;
@@ -13817,12 +13983,25 @@ delete_single_table:
}
;
+delete_single_table_for_period:
+ delete_single_table opt_for_portion_of_time_clause
+ {
+ if ($2)
+ Lex->last_table()->period_conditions= Lex->period_conditions;
+ }
+ ;
+
single_multi:
- delete_single_table
+ delete_single_table_for_period
opt_where_clause
opt_order_clause
delete_limit_clause
- opt_select_expressions {}
+ opt_select_expressions
+ {
+ if ($3)
+ Select->order_list= *($3);
+ Lex->pop_select(); //main select
+ }
| table_wild_list
{
mysql_init_multi_delete(Lex);
@@ -13833,6 +14012,9 @@ single_multi:
{
if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
}
| FROM table_alias_ref_list
{
@@ -13844,6 +14026,9 @@ single_multi:
{
if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
}
;
@@ -13912,9 +14097,9 @@ truncate:
LEX* lex= Lex;
lex->sql_command= SQLCOM_TRUNCATE;
lex->alter_info.reset();
- lex->select_lex.options= 0;
- lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
- lex->select_lex.init_order();
+ lex->first_select_lex()->options= 0;
+ lex->sql_cache= LEX::SQL_CACHE_UNSPECIFIED;
+ lex->first_select_lex()->order_list.empty();
YYPS->m_lock_type= TL_WRITE;
YYPS->m_mdl_type= MDL_EXCLUSIVE;
}
@@ -13999,6 +14184,8 @@ show:
LEX *lex=Lex;
lex->wild=0;
lex->ident= null_clex_str;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
lex->create_info.init();
@@ -14006,6 +14193,7 @@ show:
show_param
{
Select->parsing_place= NO_MATTER;
+ Lex->pop_select(); //main select
}
;
@@ -14021,40 +14209,40 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TABLES;
- lex->select_lex.db= $3;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES)))
+ lex->first_select_lex()->db= $3;
+ if (prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES))
MYSQL_YYABORT;
}
| opt_full TRIGGERS_SYM opt_db wild_and_where
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TRIGGERS;
- lex->select_lex.db= $3;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TRIGGERS)))
+ lex->first_select_lex()->db= $3;
+ if (prepare_schema_table(thd, lex, 0, SCH_TRIGGERS))
MYSQL_YYABORT;
}
| EVENTS_SYM opt_db wild_and_where
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_EVENTS;
- lex->select_lex.db= $2;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_EVENTS)))
+ lex->first_select_lex()->db= $2;
+ if (prepare_schema_table(thd, lex, 0, SCH_EVENTS))
MYSQL_YYABORT;
}
| TABLE_SYM STATUS_SYM opt_db wild_and_where
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TABLE_STATUS;
- lex->select_lex.db= $3;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TABLES)))
+ lex->first_select_lex()->db= $3;
+ if (prepare_schema_table(thd, lex, 0, SCH_TABLES))
MYSQL_YYABORT;
}
| OPEN_SYM TABLES opt_db wild_and_where
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_OPEN_TABLES;
- lex->select_lex.db= $3;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES)))
+ lex->first_select_lex()->db= $3;
+ if (prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES))
MYSQL_YYABORT;
}
| PLUGINS_SYM
@@ -14103,12 +14291,13 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS;
}
- opt_limit_clause
+ opt_global_limit_clause
| RELAYLOG_SYM optional_connection_name EVENTS_SYM binlog_in binlog_from
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_RELAYLOG_EVENTS;
- } opt_limit_clause
+ }
+ opt_global_limit_clause
| keys_or_index from_or_in table_ident opt_db opt_where_clause
{
LEX *lex= Lex;
@@ -14150,13 +14339,13 @@ show_param:
LEX_CSTRING var= {STRING_WITH_LEN("error_count")};
(void) create_select_for_variable(thd, &var);
}
- | WARNINGS opt_limit_clause
+ | WARNINGS opt_global_limit_clause
{ Lex->sql_command = SQLCOM_SHOW_WARNS;}
- | ERRORS opt_limit_clause
+ | ERRORS opt_global_limit_clause
{ Lex->sql_command = SQLCOM_SHOW_ERRORS;}
| PROFILES_SYM
{ Lex->sql_command = SQLCOM_SHOW_PROFILES; }
- | PROFILE_SYM opt_profile_defs opt_profile_args opt_limit_clause
+ | PROFILE_SYM opt_profile_defs opt_profile_args opt_global_limit_clause
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_PROFILE;
@@ -14218,7 +14407,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE;
- if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL,0)))
+ if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL,0))
MYSQL_YYABORT;
lex->create_info.storage_media= HA_SM_DEFAULT;
}
@@ -14226,7 +14415,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE;
- if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)))
+ if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL, 0))
MYSQL_YYABORT;
lex->table_type= TABLE_TYPE_VIEW;
}
@@ -14234,7 +14423,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE;
- if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)))
+ if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL, 0))
MYSQL_YYABORT;
lex->table_type= TABLE_TYPE_SEQUENCE;
}
@@ -14448,10 +14637,12 @@ describe:
describe_command table_ident
{
LEX *lex= Lex;
+ if (lex->main_select_push())
+ MYSQL_YYABORT;
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
lex->sql_command= SQLCOM_SHOW_FIELDS;
- lex->select_lex.db= null_clex_str;
+ lex->first_select_lex()->db= null_clex_str;
lex->verbose= 0;
if (unlikely(prepare_schema_table(thd, lex, $2, SCH_COLUMNS)))
MYSQL_YYABORT;
@@ -14459,18 +14650,20 @@ describe:
opt_describe_column
{
Select->parsing_place= NO_MATTER;
+ Lex->pop_select(); //main select
}
| describe_command opt_extended_describe
{ Lex->describe|= DESCRIBE_NORMAL; }
explainable_command
{
LEX *lex=Lex;
- lex->select_lex.options|= SELECT_DESCRIBE;
+ lex->first_select_lex()->options|= SELECT_DESCRIBE;
}
;
explainable_command:
select
+ | select_into
| insert
| replace
| update
@@ -14491,6 +14684,8 @@ analyze_stmt_command:
opt_extended_describe:
EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; }
+ | EXTENDED_SYM ALL
+ { Lex->describe|= DESCRIBE_EXTENDED | DESCRIBE_EXTENDED2; }
| PARTITIONS_SYM { Lex->describe|= DESCRIBE_PARTITIONS; }
| opt_format_json {}
;
@@ -14533,8 +14728,7 @@ flush:
lex->type= 0;
lex->no_write_to_binlog= $2;
}
- flush_options
- {}
+ flush_options {}
;
flush_options:
@@ -14551,6 +14745,7 @@ flush_options:
opt_table_list opt_flush_lock
{}
| flush_options_list
+ {}
;
opt_flush_lock:
@@ -14636,6 +14831,8 @@ flush_option:
{ Lex->type|= REFRESH_DES_KEY_FILE; }
| RESOURCES
{ Lex->type|= REFRESH_USER_RESOURCES; }
+ | SSL_SYM
+ { Lex->type|= REFRESH_SSL;}
| IDENT_sys remember_tok_start
{
Lex->type|= REFRESH_GENERIC;
@@ -14657,6 +14854,47 @@ opt_table_list:
| table_list {}
;
+backup:
+ BACKUP_SYM backup_statements {}
+ ;
+
+backup_statements:
+ STAGE_SYM ident
+ {
+ int type;
+ if (unlikely(Lex->sphead))
+ my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "BACKUP STAGE"));
+ if ((type= find_type($2.str, &backup_stage_names,
+ FIND_TYPE_NO_PREFIX)) <= 0)
+ my_yyabort_error((ER_BACKUP_UNKNOWN_STAGE, MYF(0), $2.str));
+ Lex->sql_command= SQLCOM_BACKUP;
+ Lex->backup_stage= (backup_stages) (type-1);
+ break;
+ }
+ | LOCK_SYM
+ {
+ if (unlikely(Lex->sphead))
+ my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "BACKUP LOCK"));
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ table_ident
+ {
+ if (unlikely(!Select->add_table_to_list(thd, $3, NULL, 0,
+ TL_READ, MDL_SHARED_HIGH_PRIO)))
+ MYSQL_YYABORT;
+ Lex->sql_command= SQLCOM_BACKUP_LOCK;
+ Lex->pop_select(); //main select
+ }
+ | UNLOCK_SYM
+ {
+ if (unlikely(Lex->sphead))
+ my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "BACKUP UNLOCK"));
+ /* Table list is empty for unlock */
+ Lex->sql_command= SQLCOM_BACKUP_LOCK;
+ }
+ ;
+
opt_delete_gtid_domain:
/* empty */ {}
| DELETE_DOMAIN_ID_SYM '=' '(' delete_domain_id_list ')'
@@ -14731,34 +14969,21 @@ master_reset_options:
;
purge:
- PURGE
- {
- LEX *lex=Lex;
- lex->type=0;
- lex->sql_command = SQLCOM_PURGE;
- }
- purge_options
- {}
- ;
-
-purge_options:
- master_or_binary LOGS_SYM purge_option
- ;
-
-purge_option:
- TO_SYM TEXT_STRING_sys
+ PURGE master_or_binary LOGS_SYM TO_SYM TEXT_STRING_sys
{
- Lex->to_log = $2.str;
+ Lex->stmt_purge_to($5);
}
- | BEFORE_SYM expr
+ | PURGE master_or_binary LOGS_SYM BEFORE_SYM
+ { Lex->clause_that_disallows_subselect= "PURGE..BEFORE"; }
+ expr
{
- LEX *lex= Lex;
- lex->value_list.empty();
- lex->value_list.push_front($2, thd->mem_root);
- lex->sql_command= SQLCOM_PURGE_BEFORE;
+ Lex->clause_that_disallows_subselect= NULL;
+ if (Lex->stmt_purge_before($6))
+ MYSQL_YYABORT;
}
;
+
/* kill threads */
kill:
@@ -14812,6 +15037,15 @@ kill_expr:
shutdown:
SHUTDOWN { Lex->sql_command= SQLCOM_SHUTDOWN; }
+ shutdown_option {}
+ ;
+
+shutdown_option:
+ /* Empty */ { Lex->is_shutdown_wait_for_slaves= false; }
+ | WAIT_SYM FOR_SYM ALL SLAVES
+ {
+ Lex->is_shutdown_wait_for_slaves= true;
+ }
;
/* change database */
@@ -14821,7 +15055,7 @@ use:
{
LEX *lex=Lex;
lex->sql_command=SQLCOM_CHANGE_DB;
- lex->select_lex.db= $2;
+ lex->first_select_lex()->db= $2;
}
;
@@ -14831,7 +15065,6 @@ load:
LOAD data_or_xml
{
LEX *lex= thd->lex;
- mysql_init_select(lex);
if (unlikely(lex->sphead))
{
@@ -14839,6 +15072,9 @@ load:
$2 == FILETYPE_CSV ? "LOAD DATA" : "LOAD XML");
MYSQL_YYABORT;
}
+ if (lex->main_select_push())
+ MYSQL_YYABORT;
+ mysql_init_select(lex);
}
load_data_lock opt_local INFILE TEXT_STRING_filesystem
{
@@ -14870,6 +15106,9 @@ load:
opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec
opt_load_data_set_spec
{
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
Lex->mark_first_table_as_inserting();
}
;
@@ -15070,11 +15309,6 @@ hex_or_bin_String:
$1.length);
if (unlikely(tmp == NULL))
MYSQL_YYABORT;
- /*
- it is OK only emulate fix_fields, because we need only
- value of constant
- */
- tmp->quick_fix_field();
$$= tmp->val_str((String*) 0);
}
| HEX_STRING
@@ -15083,7 +15317,6 @@ hex_or_bin_String:
$1.length);
if (unlikely(tmp == NULL))
MYSQL_YYABORT;
- tmp->quick_fix_field();
$$= tmp->val_str((String*) 0);
}
| BIN_NUM
@@ -15096,7 +15329,6 @@ hex_or_bin_String:
it is OK only emulate fix_fields, because we need only
value of constant
*/
- tmp->quick_fix_field();
$$= tmp->val_str((String*) 0);
}
;
@@ -15245,52 +15477,43 @@ NUM_literal:
temporal_literal:
DATE_SYM TEXT_STRING
{
- if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
- YYCSCL,
- MYSQL_TYPE_DATE,
- true))))
+ if (unlikely(!($$= type_handler_newdate.create_literal_item(thd,
+ $2.str, $2.length,
+ YYCSCL, true))))
MYSQL_YYABORT;
}
| TIME_SYM TEXT_STRING
{
- if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
- YYCSCL,
- MYSQL_TYPE_TIME,
- true))))
+ if (unlikely(!($$= type_handler_time2.create_literal_item(thd,
+ $2.str, $2.length,
+ YYCSCL, true))))
MYSQL_YYABORT;
}
| TIMESTAMP TEXT_STRING
{
- if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
- YYCSCL,
- MYSQL_TYPE_DATETIME,
- true))))
+ if (unlikely(!($$= type_handler_datetime.create_literal_item(thd,
+ $2.str, $2.length,
+ YYCSCL, true))))
MYSQL_YYABORT;
}
;
-
-opt_with_clause:
- /*empty */ { $$= 0; }
- | with_clause
- {
- $$= $1;
- }
- ;
-
-
with_clause:
- WITH opt_recursive
+ WITH opt_recursive
{
+ LEX *lex= Lex;
With_clause *with_clause=
new With_clause($2, Lex->curr_with_clause);
if (unlikely(with_clause == NULL))
MYSQL_YYABORT;
- Lex->derived_tables|= DERIVED_WITH;
- Lex->curr_with_clause= with_clause;
+ lex->derived_tables|= DERIVED_WITH;
+ lex->curr_with_clause= with_clause;
with_clause->add_to_list(Lex->with_clauses_list_last_next);
+ if (lex->current_select &&
+ lex->current_select->parsing_place == BEFORE_OPT_LIST)
+ lex->current_select->parsing_place= NO_MATTER;
}
- with_list
+ with_list
{
$$= Lex->curr_with_clause;
Lex->curr_with_clause= Lex->curr_with_clause->pop();
@@ -15319,17 +15542,16 @@ with_list_element:
MYSQL_YYABORT;
Lex->with_column_list.empty();
}
- AS '(' remember_tok_start subselect remember_tok_end ')'
+ AS '(' query_expression ')'
{
LEX *lex= thd->lex;
const char *query_start= lex->sphead ? lex->sphead->m_tmp_query
: thd->query();
- char *spec_start= $6 + 1;
- With_element *elem= new With_element($1, *$2, $7->master_unit());
- if (unlikely(elem == NULL) ||
- unlikely(Lex->curr_with_clause->add_with_element(elem)))
+ const char *spec_start= $5.pos() + 1;
+ With_element *elem= new With_element($1, *$2, $6);
+ if (elem == NULL || Lex->curr_with_clause->add_with_element(elem))
MYSQL_YYABORT;
- if (elem->set_unparsed_spec(thd, spec_start, $8,
+ if (elem->set_unparsed_spec(thd, spec_start, $7.pos(),
spec_start - query_start))
MYSQL_YYABORT;
}
@@ -15641,11 +15863,9 @@ ident_or_text:
user_maybe_role:
ident_or_text
{
- if (unlikely(!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))))
+ if (unlikely(!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
$$->user = $1;
- $$->host= null_clex_str; // User or Role, see get_current_user()
- $$->reset_auth();
if (unlikely(check_string_char_length(&$$->user, ER_USERNAME,
username_char_length,
@@ -15654,10 +15874,9 @@ user_maybe_role:
}
| ident_or_text '@' ident_or_text
{
- if (unlikely(!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))))
+ if (unlikely(!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
$$->user = $1; $$->host=$3;
- $$->reset_auth();
if (unlikely(check_string_char_length(&$$->user, ER_USERNAME,
username_char_length,
@@ -15687,8 +15906,7 @@ user_maybe_role:
if (unlikely(!($$=(LEX_USER*)thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
$$->user= current_user;
- $$->plugin= empty_clex_str;
- $$->auth= empty_clex_str;
+ $$->auth= new (thd->mem_root) USER_AUTH();
}
;
@@ -15974,6 +16192,7 @@ keyword_data_type:
*/
keyword_sp_var_and_label:
ACTION
+ | ACCOUNT_SYM
| ADDDATE_SYM
| ADMIN_SYM
| AFTER_SYM
@@ -16057,6 +16276,7 @@ keyword_sp_var_and_label:
| EXCEPTION_MARIADB_SYM
| EXCHANGE_SYM
| EXPANSION_SYM
+ | EXPIRE_SYM
| EXPORT_SYM
| EXTENDED_SYM
| EXTENT_SIZE_SYM
@@ -16147,6 +16367,7 @@ keyword_sp_var_and_label:
| MYSQL_SYM
| MYSQL_ERRNO_SYM
| NAME_SYM
+ | NEVER_SYM
| NEXT_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2
| NEXTVAL_SYM
| NEW_SYM
@@ -16235,6 +16456,7 @@ keyword_sp_var_and_label:
| SQL_BUFFER_RESULT
| SQL_NO_CACHE_SYM
| SQL_THREAD
+ | STAGE_SYM
| STARTS_SYM
| STATEMENT_SYM
| STATUS_SYM
@@ -16303,14 +16525,23 @@ set:
SET
{
LEX *lex=Lex;
+ if (lex->main_select_push(true))
+ MYSQL_YYABORT;
lex->set_stmt_init();
lex->var_list.empty();
- sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
}
start_option_value_list
- {}
+ {
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
+ }
| SET STATEMENT_SYM
{
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
Lex->set_stmt_init();
}
set_stmt_option_value_following_option_type_list
@@ -16320,6 +16551,9 @@ set:
my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), "SET STATEMENT"));
lex->stmt_var_list= lex->var_list;
lex->var_list.empty();
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
}
FOR_SYM verb_clause
{}
@@ -16384,7 +16618,8 @@ option_value_list_continued:
/* Repeating list of option values after first option value. */
option_value_list:
{
- sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
}
option_value
{
@@ -16393,7 +16628,8 @@ option_value_list:
}
| option_value_list ','
{
- sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
}
option_value
{
@@ -16659,21 +16895,29 @@ opt_for_user:
thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
lex->definer->user= current_user;
- lex->definer->plugin= empty_clex_str;
- lex->definer->auth= empty_clex_str;
+ lex->definer->auth= new (thd->mem_root) USER_AUTH();
}
| FOR_SYM user equal { Lex->definer= $2; }
;
text_or_password:
- TEXT_STRING { Lex->definer->pwhash= $1;}
- | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; }
+ TEXT_STRING
+ {
+ Lex->definer->auth= new (thd->mem_root) USER_AUTH();
+ Lex->definer->auth->auth_str= $1;
+ }
+ | PASSWORD_SYM '(' TEXT_STRING ')'
+ {
+ Lex->definer->auth= new (thd->mem_root) USER_AUTH();
+ Lex->definer->auth->pwtext= $3;
+ }
| OLD_PASSWORD_SYM '(' TEXT_STRING ')'
{
- Lex->definer->pwtext= $3;
- Lex->definer->pwhash.str= Item_func_password::alloc(thd,
+ Lex->definer->auth= new (thd->mem_root) USER_AUTH();
+ Lex->definer->auth->pwtext= $3;
+ Lex->definer->auth->auth_str.str= Item_func_password::alloc(thd,
$3.str, $3.length, Item_func_password::OLD);
- Lex->definer->pwhash.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+ Lex->definer->auth->auth_str.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
}
;
@@ -16743,7 +16987,7 @@ table_lock_list:
;
table_lock:
- table_ident opt_table_alias lock_option
+ table_ident opt_table_alias_clause lock_option
{
thr_lock_type lock_type= (thr_lock_type) $3;
bool lock_for_write= (lock_type >= TL_WRITE_ALLOW_WRITE);
@@ -16754,7 +16998,7 @@ table_lock:
? MDL_SHARED_WRITE
: MDL_SHARED_NO_READ_WRITE;
- if (unlikely(!Select->
+ if (unlikely(!Lex->current_select_or_default()->
add_table_to_list(thd, $1, $2, table_options,
lock_type, mdl_type)))
MYSQL_YYABORT;
@@ -16791,32 +17035,42 @@ unlock:
*/
handler:
- HANDLER_SYM table_ident OPEN_SYM opt_table_alias
+ HANDLER_SYM
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ handler_tail
+ {
+ Lex->pop_select(); //main select
+ }
+ ;
+
+handler_tail:
+ table_ident OPEN_SYM opt_table_alias_clause
{
LEX *lex= Lex;
if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER"));
lex->sql_command = SQLCOM_HA_OPEN;
- if (unlikely(!lex->current_select->add_table_to_list(thd, $2, $4,
- 0)))
+ if (!lex->current_select->add_table_to_list(thd, $1, $3, 0))
MYSQL_YYABORT;
}
- | HANDLER_SYM table_ident_nodb CLOSE_SYM
+ | table_ident_nodb CLOSE_SYM
{
LEX *lex= Lex;
if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER"));
lex->sql_command = SQLCOM_HA_CLOSE;
- if (unlikely(!lex->current_select->add_table_to_list(thd, $2, 0,
- 0)))
+ if (!lex->current_select->add_table_to_list(thd, $1, 0, 0))
MYSQL_YYABORT;
}
- | HANDLER_SYM table_ident_nodb READ_SYM
+ | table_ident_nodb READ_SYM
{
LEX *lex=Lex;
if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER"));
- lex->expr_allows_subselect= FALSE;
+ lex->clause_that_disallows_subselect= "HANDLER..READ";
lex->sql_command = SQLCOM_HA_READ;
lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */
Item *one= new (thd->mem_root) Item_int(thd, (int32) 1);
@@ -16825,15 +17079,24 @@ handler:
lex->current_select->select_limit= one;
lex->current_select->offset_limit= 0;
lex->limit_rows_examined= 0;
- if (unlikely(!lex->current_select->add_table_to_list(thd, $2, 0,
- 0)))
+ if (!lex->current_select->add_table_to_list(thd, $1, 0, 0))
MYSQL_YYABORT;
}
- handler_read_or_scan opt_where_clause opt_limit_clause
+ handler_read_or_scan opt_where_clause opt_global_limit_clause
{
- Lex->expr_allows_subselect= TRUE;
+ LEX *lex=Lex;
+ lex->clause_that_disallows_subselect= NULL;
+ if (!lex->current_select->explicit_limit)
+ {
+ Item *one= new (thd->mem_root) Item_int(thd, (int32) 1);
+ if (one == NULL)
+ MYSQL_YYABORT;
+ lex->current_select->select_limit= one;
+ lex->current_select->offset_limit= 0;
+ lex->limit_rows_examined= 0;
+ }
/* Stored functions are not supported for HANDLER READ. */
- if (unlikely(Lex->uses_stored_routines()))
+ if (lex->uses_stored_routines())
{
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
"stored functions in HANDLER ... READ");
@@ -17031,7 +17294,7 @@ current_role:
if (unlikely(!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
$$->user= current_role;
- $$->reset_auth();
+ $$->auth= NULL;
}
;
@@ -17048,7 +17311,7 @@ grant_role:
MYSQL_YYABORT;
$$->user= $1;
$$->host= empty_clex_str;
- $$->reset_auth();
+ $$->auth= NULL;
if (unlikely(check_string_char_length(&$$->user, ER_USERNAME,
username_char_length,
@@ -17138,23 +17401,23 @@ require_list_element:
SUBJECT_SYM TEXT_STRING
{
LEX *lex=Lex;
- if (unlikely(lex->x509_subject))
+ if (lex->account_options.x509_subject.str)
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SUBJECT"));
- lex->x509_subject=$2.str;
+ lex->account_options.x509_subject= $2;
}
| ISSUER_SYM TEXT_STRING
{
LEX *lex=Lex;
- if (unlikely(lex->x509_issuer))
+ if (lex->account_options.x509_issuer.str)
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "ISSUER"));
- lex->x509_issuer=$2.str;
+ lex->account_options.x509_issuer= $2;
}
| CIPHER_SYM TEXT_STRING
{
LEX *lex=Lex;
- if (unlikely(lex->ssl_cipher))
+ if (lex->account_options.ssl_cipher.str)
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CIPHER"));
- lex->ssl_cipher=$2.str;
+ lex->account_options.ssl_cipher= $2;
}
;
@@ -17162,7 +17425,7 @@ grant_ident:
'*'
{
LEX *lex= Lex;
- if (unlikely(lex->copy_db_to(&lex->current_select->db)))
+ if (unlikely(lex->copy_db_to(&lex->first_select_lex()->db)))
MYSQL_YYABORT;
if (lex->grant == GLOBAL_ACLS)
lex->grant = DB_ACLS & ~GRANT_ACL;
@@ -17172,7 +17435,7 @@ grant_ident:
| ident '.' '*'
{
LEX *lex= Lex;
- lex->current_select->db= $1;
+ lex->first_select_lex()->db= $1;
if (lex->grant == GLOBAL_ACLS)
lex->grant = DB_ACLS & ~GRANT_ACL;
else if (unlikely(lex->columns.elements))
@@ -17181,7 +17444,7 @@ grant_ident:
| '*' '.' '*'
{
LEX *lex= Lex;
- lex->current_select->db= null_clex_str;
+ lex->first_select_lex()->db= null_clex_str;
if (lex->grant == GLOBAL_ACLS)
lex->grant= GLOBAL_ACLS & ~GRANT_ACL;
else if (unlikely(lex->columns.elements))
@@ -17190,7 +17453,7 @@ grant_ident:
| table_ident
{
LEX *lex=Lex;
- if (unlikely(!lex->current_select->
+ if (unlikely(!lex->first_select_lex()->
add_table_to_list(thd, $1,NULL,
TL_OPTION_UPDATING)))
MYSQL_YYABORT;
@@ -17245,38 +17508,74 @@ grant_user:
user IDENTIFIED_SYM BY TEXT_STRING
{
$$= $1;
- $1->pwtext= $4;
- if (unlikely(Lex->sql_command == SQLCOM_REVOKE))
- MYSQL_YYABORT;
+ $1->auth= new (thd->mem_root) USER_AUTH();
+ $1->auth->pwtext= $4;
}
| user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING
{
$$= $1;
- $1->pwhash= $5;
+ $1->auth= new (thd->mem_root) USER_AUTH();
+ $1->auth->auth_str= $5;
}
- | user IDENTIFIED_SYM via_or_with ident_or_text
+ | user IDENTIFIED_SYM via_or_with auth_expression
{
$$= $1;
- $1->plugin= $4;
- $1->auth= empty_clex_str;
+ $1->auth= $4;
}
- | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as TEXT_STRING_sys
+ | user_or_role
{
$$= $1;
- $1->plugin= $4;
- $1->auth= $6;
}
- | user_or_role
- { $$= $1; }
;
+auth_expression:
+ auth_token OR_SYM auth_expression
+ {
+ $$= $1;
+ DBUG_ASSERT($$->next == NULL);
+ $$->next= $3;
+ }
+ | auth_token
+ {
+ $$= $1;
+ }
+ ;
+
+auth_token:
+ ident_or_text opt_auth_str
+ {
+ $$= $2;
+ $$->plugin= $1;
+ }
+ ;
+
+opt_auth_str:
+ /* empty */
+ {
+ if (!($$=(USER_AUTH*) thd->calloc(sizeof(USER_AUTH))))
+ MYSQL_YYABORT;
+ }
+ | using_or_as TEXT_STRING_sys
+ {
+ if (!($$=(USER_AUTH*) thd->calloc(sizeof(USER_AUTH))))
+ MYSQL_YYABORT;
+ $$->auth_str= $2;
+ }
+ | using_or_as PASSWORD_SYM '(' TEXT_STRING ')'
+ {
+ if (!($$=(USER_AUTH*) thd->calloc(sizeof(USER_AUTH))))
+ MYSQL_YYABORT;
+ $$->pwtext= $4;
+ }
+ ;
+
opt_column_list:
/* empty */
{
LEX *lex=Lex;
lex->grant |= lex->which_columns;
}
- | '(' column_list ')'
+ | '(' column_list ')' { }
;
column_list:
@@ -17317,52 +17616,47 @@ opt_require_clause:
/* empty */
| REQUIRE_SYM require_list
{
- Lex->ssl_type=SSL_TYPE_SPECIFIED;
+ Lex->account_options.ssl_type= SSL_TYPE_SPECIFIED;
}
| REQUIRE_SYM SSL_SYM
{
- Lex->ssl_type=SSL_TYPE_ANY;
+ Lex->account_options.ssl_type= SSL_TYPE_ANY;
}
| REQUIRE_SYM X509_SYM
{
- Lex->ssl_type=SSL_TYPE_X509;
+ Lex->account_options.ssl_type= SSL_TYPE_X509;
}
| REQUIRE_SYM NONE_SYM
{
- Lex->ssl_type=SSL_TYPE_NONE;
+ Lex->account_options.ssl_type= SSL_TYPE_NONE;
}
;
resource_option:
MAX_QUERIES_PER_HOUR ulong_num
{
- LEX *lex=Lex;
- lex->mqh.questions=$2;
- lex->mqh.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR;
+ Lex->account_options.questions=$2;
+ Lex->account_options.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR;
}
| MAX_UPDATES_PER_HOUR ulong_num
{
- LEX *lex=Lex;
- lex->mqh.updates=$2;
- lex->mqh.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR;
+ Lex->account_options.updates=$2;
+ Lex->account_options.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR;
}
| MAX_CONNECTIONS_PER_HOUR ulong_num
{
- LEX *lex=Lex;
- lex->mqh.conn_per_hour= $2;
- lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR;
+ Lex->account_options.conn_per_hour= $2;
+ Lex->account_options.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR;
}
| MAX_USER_CONNECTIONS_SYM int_num
{
- LEX *lex=Lex;
- lex->mqh.user_conn= $2;
- lex->mqh.specified_limits|= USER_RESOURCES::USER_CONNECTIONS;
+ Lex->account_options.user_conn= $2;
+ Lex->account_options.specified_limits|= USER_RESOURCES::USER_CONNECTIONS;
}
| MAX_STATEMENT_TIME_SYM NUM_literal
{
- LEX *lex=Lex;
- lex->mqh.max_statement_time= $2->val_real();
- lex->mqh.specified_limits|= USER_RESOURCES::MAX_STATEMENT_TIME;
+ Lex->account_options.max_statement_time= $2->val_real();
+ Lex->account_options.specified_limits|= USER_RESOURCES::MAX_STATEMENT_TIME;
}
;
@@ -17411,8 +17705,8 @@ compound_statement:
sp_proc_stmt_compound_ok
{
Lex->sql_command= SQLCOM_COMPOUND;
- Lex->sphead->set_stmt_end(thd);
- Lex->sphead->restore_thd_mem_root(thd);
+ if (Lex->sp_body_finalize_procedure(thd))
+ MYSQL_YYABORT;
}
;
@@ -17499,214 +17793,27 @@ release:
*/
unit_type_decl:
- UNION_SYM
- { $$= UNION_TYPE; }
+ UNION_SYM union_option
+ { $$.unit_type= UNION_TYPE; $$.distinct= $2; }
| INTERSECT_SYM
- { $$= INTERSECT_TYPE; }
+ { $$.unit_type= INTERSECT_TYPE; $$.distinct= 1; }
| EXCEPT_SYM
- { $$= EXCEPT_TYPE; }
- ;
-
-union_clause:
- /* empty */ {}
- | union_list
- ;
-
-union_list:
- unit_type_decl union_option
- {
- if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, TRUE)))
- MYSQL_YYABORT;
- }
- union_list_part2
- {
- /*
- Remove from the name resolution context stack the context of the
- last select in the union.
- */
- Lex->pop_context();
- }
- ;
-
-union_list_view:
- unit_type_decl union_option
- {
- if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, TRUE)))
- MYSQL_YYABORT;
- }
- query_expression_body_view
- {
- Lex->pop_context();
- }
- ;
-
-union_order_or_limit:
- {
- LEX *lex= thd->lex;
- DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE);
- SELECT_LEX *sel= lex->current_select;
- SELECT_LEX_UNIT *unit= sel->master_unit();
- SELECT_LEX *fake= unit->fake_select_lex;
- if (fake)
- {
- fake->no_table_names_allowed= 1;
- lex->current_select= fake;
- }
- thd->where= "global ORDER clause";
- }
- order_or_limit
- {
- thd->lex->current_select->no_table_names_allowed= 0;
- thd->where= "";
- }
- ;
-
-order_or_limit:
- order_clause opt_limit_clause
- | limit_clause
+ { $$.unit_type= EXCEPT_TYPE; $$.distinct= 1; }
;
/*
Start a UNION, for non-top level query expressions.
*/
-union_head_non_top:
- unit_type_decl union_option
- {
- if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, FALSE)))
- MYSQL_YYABORT;
- }
- ;
-
union_option:
/* empty */ { $$=1; }
| DISTINCT { $$=1; }
| ALL { $$=0; }
;
-simple_table:
- query_specification { $$= $1; }
- | table_value_constructor { $$= $1; }
- ;
-
-table_value_constructor:
- VALUES
- {
- Lex->tvc_start();
- }
- values_list
- {
- $$= Lex->current_select;
- if (Lex->tvc_finalize())
- MYSQL_YYABORT;
- }
- ;
-
-/*
- Corresponds to the SQL Standard
- <query specification> ::=
- SELECT [ <set quantifier> ] <select list> <table expression>
-
- Notes:
- - We allow more options in addition to <set quantifier>
- - <table expression> is optional in MariaDB
-*/
-query_specification:
- SELECT_SYM select_init2_derived opt_table_expression
- {
- $$= Lex->current_select->master_unit()->first_select();
- }
- ;
-
-query_term_union_not_ready:
- simple_table order_or_limit opt_select_lock_type { $$= $1; }
- | '(' select_paren_derived ')' union_order_or_limit { $$= $2; }
- ;
-
-query_term_union_ready:
- simple_table opt_select_lock_type { $$= $1; }
- | '(' select_paren_derived ')' { $$= $2; }
- ;
-
-query_expression_body:
- query_term_union_not_ready { $$= $1; }
- | query_term_union_ready { $$= $1; }
- | query_term_union_ready union_list_derived { $$= $1; }
- ;
-
-/* Corresponds to <query expression> in the SQL:2003 standard. */
-subselect:
- subselect_start opt_with_clause query_expression_body subselect_end
- {
- $3->set_with_clause($2);
- $$= $3;
- }
- ;
-
-subselect_start:
- {
- LEX *lex=Lex;
- if (unlikely(!lex->expr_allows_subselect ||
- lex->sql_command == (int)SQLCOM_PURGE))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- /*
- we are making a "derived table" for the parenthesis
- as we need to have a lex level to fit the union
- after the parenthesis, e.g.
- (SELECT .. ) UNION ... becomes
- SELECT * FROM ((SELECT ...) UNION ...)
- */
- if (unlikely(mysql_new_select(Lex, 1, NULL)))
- MYSQL_YYABORT;
- }
- ;
-
-subselect_end:
- {
- LEX *lex=Lex;
-
- lex->check_automatic_up(UNSPECIFIED_TYPE);
- lex->pop_context();
- SELECT_LEX *child= lex->current_select;
- lex->current_select = lex->current_select->return_after_parsing();
- lex->nest_level--;
- lex->current_select->n_child_sum_items += child->n_sum_items;
-
- /*
- A subquery (and all the subsequent query blocks in a UNION) can
- add columns to an outer query block. Reserve space for them.
- Aggregate functions in having clause can also add fields to an
- outer select.
- */
- for (SELECT_LEX *temp= child->master_unit()->first_select();
- temp != NULL; temp= temp->next_select())
- {
- lex->current_select->select_n_where_fields+=
- temp->select_n_where_fields;
- lex->current_select->select_n_having_items+=
- temp->select_n_having_items;
- }
- }
- ;
-
-opt_query_expression_options:
- /* empty */
- | query_expression_option_list
- ;
-
-query_expression_option_list:
- query_expression_option_list query_expression_option
- | query_expression_option
- ;
-
query_expression_option:
STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; }
| HIGH_PRIORITY
{
- if (unlikely(Lex->check_simple_select(&$1)))
- MYSQL_YYABORT;
YYPS->m_lock_type= TL_READ_HIGH_PRIORITY;
YYPS->m_mdl_type= MDL_SHARED_READ;
Select->options|= SELECT_HIGH_PRIORITY;
@@ -17714,18 +17821,8 @@ query_expression_option:
| DISTINCT { Select->options|= SELECT_DISTINCT; }
| SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; }
| SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; }
- | SQL_BUFFER_RESULT
- {
- if (unlikely(Lex->check_simple_select(&$1)))
- MYSQL_YYABORT;
- Select->options|= OPTION_BUFFER_RESULT;
- }
- | SQL_CALC_FOUND_ROWS
- {
- if (unlikely(Lex->check_simple_select(&$1)))
- MYSQL_YYABORT;
- Select->options|= OPTION_FOUND_ROWS;
- }
+ | SQL_BUFFER_RESULT { Select->options|= OPTION_BUFFER_RESULT; }
+ | SQL_CALC_FOUND_ROWS { Select->options|= OPTION_FOUND_ROWS; }
| ALL { Select->options|= SELECT_ALL; }
;
@@ -17758,9 +17855,7 @@ definer:
DEFINER_SYM '=' user_or_role
{
Lex->definer= $3;
- Lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
- Lex->ssl_cipher= Lex->x509_subject= Lex->x509_issuer= 0;
- bzero(&(Lex->mqh), sizeof(Lex->mqh));
+ Lex->account_options.reset();
}
;
@@ -17789,7 +17884,7 @@ view_suid:
view_list_opt:
/* empty */
{}
- | '(' view_list ')'
+ | '(' view_list ')' { }
;
view_list:
@@ -17813,35 +17908,14 @@ view_select:
lex->parsing_options.allows_variable= FALSE;
lex->create_view->select.str= (char *) YYLIP->get_cpp_ptr();
}
- opt_with_clause query_expression_body_view view_check_option
+ query_expression
+ view_check_option
{
- LEX *lex= Lex;
- size_t len= YYLIP->get_cpp_ptr() - lex->create_view->select.str;
- void *create_view_select= thd->memdup(lex->create_view->select.str, len);
- lex->create_view->select.length= len;
- lex->create_view->select.str= (char *) create_view_select;
- trim_whitespace(thd->charset(),
- &lex->create_view->select);
- lex->create_view->check= $4;
- lex->parsing_options.allows_variable= TRUE;
- lex->current_select->set_with_clause($2);
+ if (Lex->parsed_create_view($2, $3))
+ MYSQL_YYABORT;
}
;
-/*
- SQL Standard <query expression body> for VIEWs.
- Does not include INTO and PROCEDURE clauses.
-*/
-query_expression_body_view:
- SELECT_SYM select_options_and_item_list select_init3_view
- | table_value_constructor
- | table_value_constructor union_order_or_limit
- | table_value_constructor union_list_view
- | '(' select_paren_view ')'
- | '(' select_paren_view ')' union_order_or_limit
- | '(' select_paren_view ')' union_list_view
- ;
-
view_check_option:
/* empty */ { $$= VIEW_CHECK_NONE; }
| WITH CHECK_SYM OPTION { $$= VIEW_CHECK_CASCADED; }
@@ -17918,7 +17992,8 @@ trigger_tail:
(*static_cast<st_trg_execution_order*>(&lex->trg_chistics))= ($17);
lex->trg_chistics.ordering_clause_end= lip->get_cpp_ptr();
- if (unlikely(!lex->make_sp_head(thd, $4, &sp_handler_trigger)))
+ if (unlikely(!lex->make_sp_head(thd, $4, &sp_handler_trigger,
+ DEFAULT_AGGREGATE)))
MYSQL_YYABORT;
lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
@@ -17926,13 +18001,9 @@ trigger_tail:
sp_proc_stmt /* $19 */
{ /* $20 */
LEX *lex= Lex;
- sp_head *sp= lex->sphead;
lex->sql_command= SQLCOM_CREATE_TRIGGER;
- sp->set_stmt_end(thd);
- sp->restore_thd_mem_root(thd);
-
- if (unlikely(sp->is_not_allowed_in_function("trigger")))
+ if (lex->sp_body_finalize_trigger(thd))
MYSQL_YYABORT;
/*
@@ -17940,11 +18011,10 @@ trigger_tail:
sp_proc_stmt alternatives are not saving/restoring LEX, so
lex->query_tables can be wiped out.
*/
- if (unlikely(!lex->select_lex.
- add_table_to_list(thd, $10, (LEX_CSTRING*) 0,
- TL_OPTION_UPDATING,
- TL_READ_NO_INSERT,
- MDL_SHARED_NO_WRITE)))
+ if (!lex->first_select_lex()->
+ add_table_to_list(thd, $10, (LEX_CSTRING*) 0,
+ TL_OPTION_UPDATING, TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE))
MYSQL_YYABORT;
}
;
@@ -17955,22 +18025,6 @@ trigger_tail:
**************************************************************************/
-udf_tail:
- opt_if_not_exists ident
- RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
- {
- LEX *lex= thd->lex;
- if (unlikely(lex->add_create_options_with_check($1)))
- MYSQL_YYABORT;
- if (unlikely(is_native_function(thd, & $2)))
- my_yyabort_error((ER_NATIVE_FCT_NAME_COLLISION, MYF(0), $2.str));
- lex->sql_command= SQLCOM_CREATE_FUNCTION;
- lex->udf.name= $2;
- lex->udf.returns= (Item_result) $4;
- lex->udf.dl= $6.str;
- }
- ;
-
sf_return_type:
RETURNS_SYM
@@ -17988,24 +18042,12 @@ sf_return_type:
}
;
-sf_tail:
- opt_if_not_exists
- sp_name
- {
- Lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
- if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1, $2,
- &sp_handler_function)))
- MYSQL_YYABORT;
- }
- sp_parenthesized_fdparam_list
- sf_return_type
+sf_c_chistics_and_body:
sp_c_chistics
{
LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- lex->sphead->set_chistics(lex->sp_chistics);
- lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
+ lex->sphead->set_c_chistics(lex->sp_chistics);
+ lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
}
sp_proc_stmt_in_returns_clause
{
@@ -18014,18 +18056,19 @@ sf_tail:
}
;
+
sp_tail:
- opt_if_not_exists sp_name
+ sp_name
{
- Lex->sql_command= SQLCOM_CREATE_PROCEDURE;
- if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1, $2,
- &sp_handler_procedure)))
+ if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1,
+ &sp_handler_procedure,
+ DEFAULT_AGGREGATE)))
MYSQL_YYABORT;
}
sp_parenthesized_pdparam_list
sp_c_chistics
{
- Lex->sphead->set_chistics(Lex->sp_chistics);
+ Lex->sphead->set_c_chistics(Lex->sp_chistics);
Lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
}
sp_proc_stmt
@@ -18137,35 +18180,27 @@ opt_migrate:
;
install:
- INSTALL_SYM PLUGIN_SYM ident SONAME_SYM TEXT_STRING_sys
+ INSTALL_SYM PLUGIN_SYM opt_if_not_exists ident SONAME_SYM TEXT_STRING_sys
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_INSTALL_PLUGIN;
- lex->comment= $3;
- lex->ident= $5;
+ if (Lex->stmt_install_plugin($3, $4, $6))
+ MYSQL_YYABORT;
}
| INSTALL_SYM SONAME_SYM TEXT_STRING_sys
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_INSTALL_PLUGIN;
- lex->comment= null_clex_str;
- lex->ident= $3;
+ Lex->stmt_install_plugin($3);
}
;
uninstall:
- UNINSTALL_SYM PLUGIN_SYM ident
+ UNINSTALL_SYM PLUGIN_SYM opt_if_exists ident
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_UNINSTALL_PLUGIN;
- lex->comment= $3;
+ if (Lex->stmt_uninstall_plugin_by_name($3, $4))
+ MYSQL_YYABORT;
}
- | UNINSTALL_SYM SONAME_SYM TEXT_STRING_sys
+ | UNINSTALL_SYM SONAME_SYM opt_if_exists TEXT_STRING_sys
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_UNINSTALL_PLUGIN;
- lex->comment= null_clex_str;
- lex->ident= $3;
+ if (Lex->stmt_uninstall_plugin_by_soname($3, $4))
+ MYSQL_YYABORT;
}
;
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index 4af034db921..cb58c4aff43 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -68,6 +68,7 @@
#include "sql_lex.h"
#include "sql_sequence.h"
#include "my_base.h"
+#include "sql_type_json.h"
/* this is to get the bison compilation windows warnings out */
#ifdef _MSC_VER
@@ -192,6 +193,20 @@ void ORAerror(THD *thd, const char *s)
Lex_for_loop_bounds_st for_loop_bounds;
Lex_trim_st trim;
vers_history_point_t vers_history_point;
+ struct
+ {
+ enum sub_select_type unit_type;
+ bool distinct;
+ } unit_operation;
+ struct
+ {
+ SELECT_LEX *first;
+ SELECT_LEX *prev_last;
+ } select_list;
+ SQL_I_List<ORDER> *select_order;
+ Lex_select_lock select_lock;
+ Lex_select_limit select_limit;
+ Lex_order_limit_lock *order_limit_lock;
/* pointers */
Create_field *create_field;
@@ -212,6 +227,7 @@ void ORAerror(THD *thd, const char *s)
class sp_lex_cursor *sp_cursor_stmt;
LEX_CSTRING *lex_str_ptr;
LEX_USER *lex_user;
+ USER_AUTH *user_auth;
List<Condition_information_item> *cond_info_list;
List<DYNCALL_CREATE_DEF> *dyncol_def_list;
List<Item> *item_list;
@@ -237,6 +253,7 @@ void ORAerror(THD *thd, const char *s)
handlerton *db_type;
st_select_lex *select_lex;
+ st_select_lex_unit *select_lex_unit;
struct p_elem_val *p_elem_value;
class Window_frame *window_frame;
class Window_frame_bound *window_frame_bound;
@@ -245,8 +262,8 @@ void ORAerror(THD *thd, const char *s)
/* enums */
enum enum_sp_suid_behaviour sp_suid;
+ enum enum_sp_aggregate_type sp_aggregate_type;
enum enum_view_suid view_suid;
- enum sub_select_type unit_type;
enum Condition_information_item::Name cond_info_item_name;
enum enum_diag_condition_item_name diag_condition_item_name;
enum Diagnostics_information::Which_area diag_area;
@@ -288,7 +305,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
/*
We should not introduce any further shift/reduce conflicts.
*/
-%expect 83
+%expect 70
/*
Comments for TOKENS.
@@ -445,6 +462,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token LEADING /* SQL-2003-R */
%token LEAVE_SYM
%token LEFT /* SQL-2003-R */
+%token LEFT_PAREN_ALT /* INTERNAL */
+%token LEFT_PAREN_WITH /* INTERNAL */
+%token LEFT_PAREN_LIKE /* INTERNAL */
%token LEX_HOSTNAME
%token LIKE /* SQL-2003-R */
%token LIMIT
@@ -507,6 +527,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token PERCENT_RANK_SYM
%token PERCENTILE_CONT_SYM
%token PERCENTILE_DISC_SYM
+%token PORTION_SYM /* SQL-2016-R */
%token POSITION_SYM /* SQL-2003-N */
%token PRECISION /* SQL-2003-R */
%token PRIMARY_SYM /* SQL-2003-R */
@@ -635,6 +656,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
Non-reserved keywords
*/
+%token <kwd> ACCOUNT_SYM /* MYSQL */
%token <kwd> ACTION /* SQL-2003-N */
%token <kwd> ADMIN_SYM /* SQL-2003-N */
%token <kwd> ADDDATE_SYM /* MYSQL-FUNC */
@@ -752,6 +774,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> EXIT_MARIADB_SYM /* PLSQL-R */
%token <kwd> EXIT_ORACLE_SYM /* PLSQL-R */
%token <kwd> EXPANSION_SYM
+%token <kwd> EXPIRE_SYM /* MySQL */
%token <kwd> EXPORT_SYM
%token <kwd> EXTENDED_SYM
%token <kwd> EXTENT_SIZE_SYM
@@ -867,6 +890,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> NAME_SYM /* SQL-2003-N */
%token <kwd> NATIONAL_SYM /* SQL-2003-R */
%token <kwd> NCHAR_SYM /* SQL-2003-R */
+%token <kwd> NEVER_SYM /* MySQL */
%token <kwd> NEW_SYM /* SQL-2003-R */
%token <kwd> NEXT_SYM /* SQL-2003-N */
%token <kwd> NEXTVAL_SYM /* PostgreSQL sequence function */
@@ -990,6 +1014,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> SQL_CALC_FOUND_ROWS
%token <kwd> SQL_NO_CACHE_SYM
%token <kwd> SQL_THREAD
+%token <kwd> STAGE_SYM
%token <kwd> STARTS_SYM
%token <kwd> START_SYM /* SQL-2003-R */
%token <kwd> STATEMENT_SYM
@@ -1068,6 +1093,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
/* A dummy token to force the priority of table_ref production in a join. */
%left CONDITIONLESS_JOIN
%left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT ON_SYM USING
+
%left SET_VAR
%left OR_SYM OR2_SYM
%left XOR
@@ -1089,6 +1115,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%left MYSQL_CONCAT_SYM
%nonassoc NEG '~' NOT2_SYM BINARY
%nonassoc COLLATE_SYM
+%nonassoc SUBQUERY_AS_EXPR
/*
Tokens that can change their meaning from identifier to something else
@@ -1179,7 +1206,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
ALTER TABLE t1 ADD SYSTEM VERSIONING;
*/
%left PREC_BELOW_CONTRACTION_TOKEN2
-%left TEXT_STRING '(' VALUE_SYM VERSIONING_SYM
+%left TEXT_STRING '(' ')' VALUE_SYM VERSIONING_SYM
+%left EMPTY_FROM_CLAUSE
+%right INTO
%type <lex_str>
DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
@@ -1212,7 +1241,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
NCHAR_STRING
%type <lex_str_ptr>
- opt_table_alias
+ opt_table_alias_clause
+ table_alias_clause
%type <ident_cli>
IDENT
@@ -1246,7 +1276,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <simple_string>
remember_name remember_end remember_end_opt
- remember_tok_start remember_tok_end
+ remember_tok_start
wild_and_where
%type <const_simple_string>
@@ -1282,7 +1312,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
opt_temporary all_or_any opt_distinct opt_glimit_clause
opt_ignore_leaves fulltext_options union_option
opt_not
- select_derived_init transaction_access_mode_types
+ transaction_access_mode_types
opt_natural_language_mode opt_query_expansion
opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment
ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt
@@ -1291,7 +1321,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
opt_default_time_precision
case_stmt_body opt_bin_mod opt_for_system_time_clause
opt_if_exists_table_element opt_if_not_exists_table_element
- opt_recursive opt_format_xid
+ opt_recursive opt_format_xid opt_for_portion_of_time_clause
%type <object_ddl_options>
create_or_replace
@@ -1371,6 +1401,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
expr_list opt_udf_expr_list udf_expr_list when_list when_list_opt_else
ident_list ident_list_arg opt_expr_list
decode_when_list_oracle
+ execute_using
+ execute_params
%type <sp_cursor_stmt>
sp_cursor_stmt_lex
@@ -1403,12 +1435,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <table_list>
join_table_list join_table
table_factor table_ref esc_table_ref
- table_primary_ident table_primary_derived
- select_derived derived_table_list
- select_derived_union
- derived_simple_table
- derived_query_specification
- derived_table_value_constructor
+ table_primary_ident table_primary_ident_opt_parens
+ table_primary_derived table_primary_derived_opt_parens
+ derived_table_list table_reference_list_parens
+ nested_table_reference_list join_table_parens
+ update_table_list
%type <date_time_type> date_time_type;
%type <interval> interval
@@ -1431,6 +1462,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <lex_user> user grant_user grant_role user_or_role current_role
admin_option_for_role user_maybe_role
+%type <user_auth> opt_auth_str auth_expression auth_token
+
%type <charset>
opt_collate
charset_name
@@ -1444,14 +1477,21 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
UNDERSCORE_CHARSET
%type <select_lex> subselect
- get_select_lex get_select_lex_derived
- simple_table
query_specification
- query_term_union_not_ready
- query_term_union_ready
- query_expression_body
- select_paren_derived
table_value_constructor
+ simple_table
+ query_simple
+ query_primary
+ subquery
+ select_into_query_specification
+
+%type <select_lex_unit>
+ query_expression
+ query_expression_no_with_clause
+ query_expression_body_ext
+ query_expression_body_ext_parens
+ query_expression_body
+ query_specification_start
%type <boolfunc2creator> comp_op
@@ -1463,11 +1503,30 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <virtual_column> opt_check_constraint check_constraint virtual_column_func
column_default_expr
-%type <unit_type> unit_type_decl
+
+%type <unit_operation> unit_type_decl
+
+%type <select_lock>
+ opt_procedure_or_into
+ opt_select_lock_type
+ select_lock_type
+ opt_lock_wait_timeout_new
+
+%type <select_limit> opt_limit_clause limit_clause limit_options
+
+%type <order_limit_lock>
+ query_expression_tail
+ opt_query_expression_tail
+ order_or_limit
+ order_limit_lock
+ opt_order_limit_lock
+
+%type <select_order> opt_order_clause order_clause order_list
%type <NONE>
- analyze_stmt_command
- query verb_clause create change select do drop insert replace insert2
+ analyze_stmt_command backup backup_statements
+ query verb_clause create change select select_into
+ do drop insert replace insert2
insert_values update delete truncate rename compound_statement
show describe load alter optimize keycache preload flush
reset purge begin_stmt_mariadb commit rollback savepoint release
@@ -1483,7 +1542,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
assign_to_keycache_parts
preload_list preload_list_or_parts preload_keys preload_keys_parts
select_item_list select_item values_list no_braces
- opt_limit_clause delete_limit_clause fields opt_values values
+ delete_limit_clause fields opt_values values
no_braces_with_names opt_values_with_names values_with_names
procedure_list procedure_list2 procedure_item
field_def handler opt_generated_always
@@ -1507,18 +1566,16 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
table_to_table_list table_to_table opt_table_list opt_as
handler_rkey_function handler_read_or_scan
single_multi table_wild_list table_wild_one opt_wild
- union_clause union_list
- subselect_start opt_and charset
- subselect_end select_var_list select_var_list_init help
+ opt_and charset
+ select_var_list select_var_list_init help
opt_extended_describe shutdown
opt_format_json
- prepare prepare_src execute deallocate
+ prepare execute deallocate
statement
sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
view_list_opt view_list view_select
- trigger_tail sp_tail sf_tail event_tail
- udf_tail create_function_tail
+ trigger_tail event_tail
install uninstall partition_entry binlog_base64_event
normal_key_options normal_key_opts all_key_opt
spatial_key_options fulltext_key_options normal_key_opt
@@ -1527,6 +1584,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
key_using_alg
part_column_list
period_for_system_time
+ period_for_application_time
server_def server_options_list server_option
definer_opt no_definer definer get_diagnostics
parse_vcol_expr vcol_opt_specifier vcol_opt_attribute
@@ -1537,7 +1595,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
opt_delete_gtid_domain
asrow_attribute
set_assign
- sf_tail_standalone
sp_tail_standalone
opt_constraint_no_id
END_OF_INPUT
@@ -1563,6 +1620,7 @@ END_OF_INPUT
%type <plsql_cursor_attr> plsql_cursor_attr
%type <sp_suid> sp_suid
+%type <sp_aggregate_type> opt_aggregate
%type <num> sp_decl_idents sp_decl_idents_init_vars
%type <num> sp_handler_type sp_hcond_list
@@ -1620,13 +1678,16 @@ END_OF_INPUT
%type <frame_exclusion> opt_window_frame_exclusion;
%type <window_frame_bound> window_frame_start window_frame_bound;
-%type <NONE>
+%type <kwd>
'-' '+' '*' '/' '%' '(' ')'
- ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM BETWEEN_SYM CASE_SYM
+ ',' '!' '{' '}' '&' '|'
+
+%type <NONE>
+ AND_SYM OR_SYM BETWEEN_SYM CASE_SYM
THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM
MYSQL_CONCAT_SYM ORACLE_CONCAT_SYM
-%type <with_clause> opt_with_clause with_clause
+%type <with_clause> with_clause
%type <lex_str_ptr> query_name
@@ -1662,8 +1723,8 @@ rule: <-- starts at col 1
query:
END_OF_INPUT
{
- if (likely(!thd->bootstrap) &&
- unlikely(!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT)))
+ if (!thd->bootstrap &&
+ (!(thd->lex->lex_options & OPTION_LEX_FOUND_COMMENT)))
my_yyabort_error((ER_EMPTY_QUERY, MYF(0)));
thd->lex->sql_command= SQLCOM_EMPTY_QUERY;
@@ -1717,6 +1778,7 @@ statement:
alter
| analyze
| analyze_stmt_command
+ | backup
| binlog_base64_event
| call
| change
@@ -1759,6 +1821,7 @@ statement:
| rollback
| savepoint
| select
+ | select_into
| set
| set_assign
| signal_stmt
@@ -1777,9 +1840,7 @@ statement:
deallocate:
deallocate_or_drop PREPARE_SYM ident
{
- LEX *lex= thd->lex;
- lex->sql_command= SQLCOM_DEALLOCATE_PREPARE;
- lex->prepared_stmt_name= $3;
+ Lex->stmt_deallocate_prepare($3);
}
;
@@ -1789,72 +1850,58 @@ deallocate_or_drop:
;
prepare:
- PREPARE_SYM ident FROM prepare_src
- {
- LEX *lex= thd->lex;
- if (unlikely(lex->table_or_sp_used()))
- my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
- "PREPARE..FROM"));
- lex->sql_command= SQLCOM_PREPARE;
- lex->prepared_stmt_name= $2;
- }
- ;
-
-prepare_src:
- { Lex->expr_allows_subselect= false; }
+ PREPARE_SYM ident FROM
+ { Lex->clause_that_disallows_subselect= "PREPARE..FROM"; }
expr
{
- Lex->prepared_stmt_code= $2;
- Lex->expr_allows_subselect= true;
+ Lex->clause_that_disallows_subselect= NULL;
+ if (Lex->stmt_prepare($2, $5))
+ MYSQL_YYABORT;
}
;
execute:
- EXECUTE_SYM ident
+ EXECUTE_SYM ident execute_using
{
- LEX *lex= thd->lex;
- lex->sql_command= SQLCOM_EXECUTE;
- lex->prepared_stmt_name= $2;
+ if (Lex->stmt_execute($2, $3))
+ MYSQL_YYABORT;
}
+ | EXECUTE_SYM IMMEDIATE_SYM
+ { Lex->clause_that_disallows_subselect= "EXECUTE IMMEDIATE"; }
+ expr
+ { Lex->clause_that_disallows_subselect= NULL; }
execute_using
- {}
- | EXECUTE_SYM IMMEDIATE_SYM prepare_src
{
- if (unlikely(Lex->table_or_sp_used()))
- my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
- "EXECUTE IMMEDIATE"));
- Lex->sql_command= SQLCOM_EXECUTE_IMMEDIATE;
+ if (Lex->stmt_execute_immediate($4, $6))
+ MYSQL_YYABORT;
}
- execute_using
- {}
;
execute_using:
- /* nothing */
- | USING { Lex->expr_allows_subselect= false; }
- execute_var_list
+ /* nothing */ { $$= NULL; }
+ | USING
+ { Lex->clause_that_disallows_subselect= "EXECUTE..USING"; }
+ execute_params
{
- if (unlikely(Lex->table_or_sp_used()))
- my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
- "EXECUTE..USING"));
- Lex->expr_allows_subselect= true;
+ $$= $3;
+ Lex->clause_that_disallows_subselect= NULL;
}
;
-execute_var_list:
- execute_var_list ',' execute_var_ident
- | execute_var_ident
- ;
-
-execute_var_ident:
+execute_params:
expr_or_default
{
- if (unlikely(Lex->prepared_stmt_params.push_back($1,
- thd->mem_root)))
+ if (unlikely(!($$= List<Item>::make(thd->mem_root, $1))))
+ MYSQL_YYABORT;
+ }
+ | execute_params ',' expr_or_default
+ {
+ if (($$= $1)->push_back($3, thd->mem_root))
MYSQL_YYABORT;
}
;
+
/* help */
help:
@@ -2113,19 +2160,24 @@ connection_name:
/* create a table */
create:
- create_or_replace opt_temporary TABLE_SYM opt_if_not_exists table_ident
+ create_or_replace opt_temporary TABLE_SYM opt_if_not_exists
{
LEX *lex= thd->lex;
if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_create_table()))
MYSQL_YYABORT;
lex->create_info.init();
- if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2,
- $1 | $4)))
+ if (lex->main_select_push())
+ MYSQL_YYABORT;
+ lex->current_select->parsing_place= BEFORE_OPT_LIST;
+ if (lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2, $1 | $4))
MYSQL_YYABORT;
- if (unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL,
- TL_OPTION_UPDATING,
- TL_WRITE,
- MDL_EXCLUSIVE)))
+ }
+ table_ident
+ {
+ LEX *lex= thd->lex;
+ if (!lex->first_select_lex()->
+ add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING,
+ TL_WRITE, MDL_SHARED_UPGRADABLE))
MYSQL_YYABORT;
lex->alter_info.reset();
/*
@@ -2140,12 +2192,14 @@ create:
create_body
{
LEX *lex= thd->lex;
- lex->current_select= &lex->select_lex;
create_table_set_open_action_and_adjust_tables(lex);
+ Lex->pop_select(); //main select
}
| create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident
{
LEX *lex= thd->lex;
+ if (lex->main_select_push())
+ MYSQL_YYABORT;
if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_create_sequence()))
MYSQL_YYABORT;
lex->create_info.init();
@@ -2153,10 +2207,9 @@ create:
$1 | $4)))
MYSQL_YYABORT;
- if (unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL,
- TL_OPTION_UPDATING,
- TL_WRITE,
- MDL_EXCLUSIVE)))
+ if (!lex->first_select_lex()->
+ add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING,
+ TL_WRITE, MDL_EXCLUSIVE))
MYSQL_YYABORT;
/*
@@ -2179,8 +2232,9 @@ create:
if (unlikely(lex->create_info.seq_create_info->check_and_adjust(1)))
{
my_error(ER_SEQUENCE_INVALID_DATA, MYF(0),
- lex->select_lex.table_list.first->db.str,
- lex->select_lex.table_list.first->table_name.str);
+ lex->first_select_lex()->table_list.first->db.str,
+ lex->first_select_lex()->table_list.first->
+ table_name.str);
MYSQL_YYABORT;
}
@@ -2193,42 +2247,64 @@ create:
Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE;
Lex->create_info.sequence= 1;
- lex->current_select= &lex->select_lex;
create_table_set_open_action_and_adjust_tables(lex);
+ Lex->pop_select(); //main select
}
- | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident
+ | create_or_replace opt_unique INDEX_SYM opt_if_not_exists
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ ident
opt_key_algorithm_clause
ON table_ident
{
- if (unlikely(Lex->add_create_index_prepare($8)))
+ if (Lex->add_create_index_prepare($9))
MYSQL_YYABORT;
- if (unlikely(Lex->add_create_index($2, &$5, $6, $1 | $4)))
+ if (Lex->add_create_index($2, &$6, $7, $1 | $4))
MYSQL_YYABORT;
}
'(' key_list ')' opt_lock_wait_timeout normal_key_options
- opt_index_lock_algorithm { }
- | create_or_replace fulltext INDEX_SYM opt_if_not_exists ident
+ opt_index_lock_algorithm
+ {
+ Lex->pop_select(); //main select
+ }
+ | create_or_replace fulltext INDEX_SYM
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ opt_if_not_exists ident
ON table_ident
{
- if (unlikely(Lex->add_create_index_prepare($7)))
+ if (Lex->add_create_index_prepare($8))
MYSQL_YYABORT;
- if (unlikely(Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF,
- $1 | $4)))
+ if (Lex->add_create_index($2, &$6, HA_KEY_ALG_UNDEF, $1 | $5))
MYSQL_YYABORT;
}
'(' key_list ')' opt_lock_wait_timeout fulltext_key_options
- opt_index_lock_algorithm { }
- | create_or_replace spatial INDEX_SYM opt_if_not_exists ident
+ opt_index_lock_algorithm
+ {
+ Lex->pop_select(); //main select
+ }
+ | create_or_replace spatial INDEX_SYM
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ opt_if_not_exists ident
ON table_ident
{
- if (unlikely(Lex->add_create_index_prepare($7)))
+ if (Lex->add_create_index_prepare($8))
MYSQL_YYABORT;
- if (unlikely(Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF,
- $1 | $4)))
+ if (Lex->add_create_index($2, &$6, HA_KEY_ALG_UNDEF, $1 | $5))
MYSQL_YYABORT;
}
'(' key_list ')' opt_lock_wait_timeout spatial_key_options
- opt_index_lock_algorithm { }
+ opt_index_lock_algorithm
+ {
+ Lex->pop_select(); //main select
+ }
| create_or_replace DATABASE opt_if_not_exists ident
{
Lex->create_info.default_table_charset= NULL;
@@ -2245,50 +2321,120 @@ create:
| create_or_replace definer_opt opt_view_suid VIEW_SYM
opt_if_not_exists table_ident
{
- if (unlikely(Lex->add_create_view(thd, $1 | $5,
- DTYPE_ALGORITHM_UNDEFINED, $3,
- $6)))
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ if (Lex->add_create_view(thd, $1 | $5,
+ DTYPE_ALGORITHM_UNDEFINED, $3, $6))
MYSQL_YYABORT;
}
view_list_opt AS view_select
- { }
+ {
+ Lex->pop_select(); //main select
+ }
| create_or_replace view_algorithm definer_opt opt_view_suid VIEW_SYM
opt_if_not_exists table_ident
{
- if (unlikely(Lex->add_create_view(thd, $1 | $6, $2, $4, $7)))
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ if (Lex->add_create_view(thd, $1 | $6, $2, $4, $7))
MYSQL_YYABORT;
}
view_list_opt AS view_select
- { }
+ {
+ Lex->pop_select(); //main select
+ }
| create_or_replace definer_opt TRIGGER_SYM
- { Lex->create_info.set($1); }
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ Lex->create_info.set($1);
+ }
trigger_tail
- { }
- | create_or_replace definer_opt PROCEDURE_SYM
- { Lex->create_info.set($1); }
+ {
+ Lex->pop_select(); //main select
+ }
+ | create_or_replace definer_opt PROCEDURE_SYM opt_if_not_exists
+ {
+ if (Lex->stmt_create_procedure_start($1 | $4))
+ MYSQL_YYABORT;
+ }
sp_tail_standalone
- { }
+ {
+ Lex->stmt_create_routine_finalize();
+ }
| create_or_replace definer_opt EVENT_SYM
- { Lex->create_info.set($1); }
- event_tail
- { }
- | create_or_replace definer FUNCTION_SYM
- { Lex->create_info.set($1); }
- sf_tail_standalone
- { }
- | create_or_replace no_definer FUNCTION_SYM
- { Lex->create_info.set($1); }
- create_function_tail
- { }
- | create_or_replace no_definer AGGREGATE_SYM FUNCTION_SYM
{
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
Lex->create_info.set($1);
- Lex->udf.type= UDFTYPE_AGGREGATE;
}
- udf_tail
- { }
- | create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list
- opt_require_clause opt_resource_options
+ event_tail
+ {
+ Lex->pop_select(); //main select
+ }
+ | create_or_replace definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ sp_name RETURN_ORACLE_SYM
+ {
+ if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
+ MYSQL_YYABORT;
+ }
+ sf_return_type
+ sf_c_chistics_and_body_standalone
+ opt_sp_name
+ {
+ if (Lex->stmt_create_stored_function_finalize_standalone($11))
+ MYSQL_YYABORT;
+ }
+ | create_or_replace definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ sp_name '('
+ {
+ if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
+ MYSQL_YYABORT;
+ }
+ sp_fdparam_list ')'
+ RETURN_ORACLE_SYM sf_return_type
+ sf_c_chistics_and_body_standalone
+ opt_sp_name
+ {
+ if (Lex->stmt_create_stored_function_finalize_standalone($14))
+ MYSQL_YYABORT;
+ }
+ | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ sp_name RETURN_ORACLE_SYM
+ {
+ if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
+ MYSQL_YYABORT;
+ }
+ sf_return_type
+ sf_c_chistics_and_body_standalone
+ opt_sp_name
+ {
+ if (Lex->stmt_create_stored_function_finalize_standalone($11))
+ MYSQL_YYABORT;
+ }
+ | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ sp_name '('
+ {
+ if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
+ MYSQL_YYABORT;
+ }
+ sp_fdparam_list ')'
+ RETURN_ORACLE_SYM sf_return_type
+ sf_c_chistics_and_body_standalone
+ opt_sp_name
+ {
+ if (Lex->stmt_create_stored_function_finalize_standalone($14))
+ MYSQL_YYABORT;
+ }
+ | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ ident RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
+ {
+ if (Lex->stmt_create_udf_function($1 | $5, $3, $6,
+ (Item_result) $8, $10))
+ MYSQL_YYABORT;
+ }
+ | create_or_replace USER_SYM opt_if_not_exists clear_privileges
+ grant_list opt_require_clause opt_resource_options opt_account_locking_and_opt_password_expiration
{
if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_USER,
$1 | $3)))
@@ -2324,7 +2470,7 @@ create:
&sp_handler_package_spec,
$5, $1 | $4))))
MYSQL_YYABORT;
- pkg->set_chistics(Lex->sp_chistics);
+ pkg->set_c_chistics(Lex->sp_chistics);
}
opt_package_specification_element_list END
remember_end_opt opt_sp_name
@@ -2344,7 +2490,7 @@ create:
&sp_handler_package_body,
$6, $1 | $5))))
MYSQL_YYABORT;
- pkg->set_chistics(Lex->sp_chistics);
+ pkg->set_c_chistics(Lex->sp_chistics);
Lex->sp_block_init(thd);
}
package_implementation_declare_section
@@ -2421,13 +2567,14 @@ package_specification_function:
MYSQL_YYABORT;
thd->lex= $2;
if (unlikely(!$2->make_sp_head_no_recursive(thd, spname,
- &sp_handler_package_function)))
+ &sp_handler_package_function,
+ NOT_AGGREGATE)))
MYSQL_YYABORT;
$1->sphead->get_package()->m_current_routine= $2;
(void) is_native_function_with_warn(thd, &$3);
}
opt_sp_parenthesized_fdparam_list
- sf_return_type
+ RETURN_ORACLE_SYM sf_return_type
sp_c_chistics
{
sp_head *sp= thd->lex->sphead;
@@ -2447,7 +2594,8 @@ package_specification_procedure:
MYSQL_YYABORT;
thd->lex= $2;
if (unlikely(!$2->make_sp_head_no_recursive(thd, spname,
- &sp_handler_package_procedure)))
+ &sp_handler_package_procedure,
+ DEFAULT_AGGREGATE)))
MYSQL_YYABORT;
$1->sphead->get_package()->m_current_routine= $2;
}
@@ -2497,11 +2645,6 @@ package_implementation_function_body:
}
sp_body opt_package_routine_end_name
{
- if (unlikely(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR))
- {
- my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0)));
- }
- Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE);
if (unlikely(thd->lex->sp_body_finalize_function(thd) ||
thd->lex->sphead->check_package_routine_end_name($5)))
MYSQL_YYABORT;
@@ -2521,7 +2664,7 @@ package_implementation_procedure_body:
sp_body opt_package_routine_end_name
{
if (unlikely(thd->lex->sp_body_finalize_procedure(thd) ||
- thd->lex->sphead->check_package_routine_end_name($5)))
+ thd->lex->sphead->check_package_routine_end_name($5)))
MYSQL_YYABORT;
thd->lex= $2;
}
@@ -2559,10 +2702,6 @@ package_specification_element:
}
;
-create_function_tail:
- sf_tail_standalone { }
- | udf_tail { Lex->udf.type= UDFTYPE_FUNCTION; }
- ;
opt_sequence:
/* empty */ { }
@@ -2882,20 +3021,17 @@ ev_sql_stmt:
if (unlikely(!lex->make_sp_head(thd,
lex->event_parse_data->identifier,
- &sp_handler_procedure)))
+ &sp_handler_procedure,
+ DEFAULT_AGGREGATE)))
MYSQL_YYABORT;
lex->sphead->set_body_start(thd, lip->get_cpp_ptr());
}
sp_proc_stmt
{
- LEX *lex= thd->lex;
-
/* return back to the original memory root ASAP */
- lex->sphead->set_stmt_end(thd);
- lex->sphead->restore_thd_mem_root(thd);
-
- lex->event_parse_data->body_changed= TRUE;
+ if (Lex->sp_body_finalize_event(thd))
+ MYSQL_YYABORT;
}
;
@@ -2907,13 +3043,16 @@ clear_privileges:
lex->columns.empty();
lex->grant= lex->grant_tot_col= 0;
lex->all_privileges= 0;
- lex->select_lex.db= null_clex_str;
- lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
- lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0;
- bzero((char *)&(lex->mqh),sizeof(lex->mqh));
+ lex->first_select_lex()->db= null_clex_str;
+ lex->account_options.reset();
}
;
+opt_aggregate:
+ /* Empty */ { $$= NOT_AGGREGATE; }
+ | AGGREGATE_SYM { $$= GROUP_AGGREGATE; }
+ ;
+
sp_name:
ident '.' ident
{
@@ -3027,7 +3166,18 @@ sp_cparams:
/* Stored FUNCTION parameter declaration list */
sp_fdparam_list:
/* Empty */
- | sp_fdparams
+ {
+ Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start();
+ Lex->sphead->m_param_end= Lex->sphead->m_param_begin;
+ }
+ |
+ {
+ Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start();
+ }
+ sp_fdparams
+ {
+ Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
+ }
;
sp_fdparams:
@@ -3134,18 +3284,6 @@ sp_opt_inout:
| IN_SYM OUT_SYM { $$= sp_variable::MODE_INOUT; }
;
-sp_parenthesized_fdparam_list:
- '('
- {
- Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start() + 1;
- }
- sp_fdparam_list
- ')'
- {
- Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
- }
- ;
-
sp_parenthesized_pdparam_list:
'('
{
@@ -3168,7 +3306,7 @@ sp_no_param:
opt_sp_parenthesized_fdparam_list:
sp_no_param
- | sp_parenthesized_fdparam_list
+ | '(' sp_fdparam_list ')'
;
opt_sp_parenthesized_pdparam_list:
@@ -3263,12 +3401,8 @@ optionally_qualified_column_ident:
row_field_name:
ident_directly_assignable
{
- if (unlikely(check_string_char_length(&$1, 0, NAME_CHAR_LEN,
- system_charset_info, 1)))
- my_yyabort_error((ER_TOO_LONG_IDENT, MYF(0), $1.str));
- if (unlikely(!($$= new (thd->mem_root) Spvar_definition())))
+ if (!($$= Lex->row_field_name(thd, $1)))
MYSQL_YYABORT;
- Lex->init_last_field($$, &$1, thd->variables.collation_database);
}
;
@@ -3279,17 +3413,12 @@ row_field_definition:
row_field_definition_list:
row_field_definition
{
- if (unlikely(!($$= new (thd->mem_root) Row_definition_list())) ||
- unlikely($$->push_back($1, thd->mem_root)))
+ if (!($$= Row_definition_list::make(thd->mem_root, $1)))
MYSQL_YYABORT;
}
| row_field_definition_list ',' row_field_definition
{
- uint unused;
- if (unlikely($1->find_row_field_by_name(&$3->field_name, &unused)))
- my_yyabort_error((ER_DUP_FIELDNAME, MYF(0), $3->field_name.str));
- $$= $1;
- if (unlikely($$->push_back($3, thd->mem_root)))
+ if (($$= $1)->append_uniq(thd->mem_root, $3))
MYSQL_YYABORT;
}
;
@@ -3413,10 +3542,13 @@ sp_cursor_stmt:
{
DBUG_ASSERT(thd->free_list == NULL);
Lex->sphead->reset_lex(thd, $1);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
select
{
DBUG_ASSERT(Lex == $1);
+ Lex->pop_select(); //main select
if (unlikely($1->stmt_finalize(thd)) ||
unlikely($1->sphead->restore_lex(thd)))
MYSQL_YYABORT;
@@ -3545,7 +3677,7 @@ raise_stmt_oracle:
signal_stmt:
SIGNAL_SYM signal_value opt_set_signal_information
{
- if (unlikely(Lex->add_signal_statement(thd, $2)))
+ if (Lex->add_signal_statement(thd, $2))
MYSQL_YYABORT;
}
;
@@ -3927,48 +4059,17 @@ sp_proc_stmt_statement:
Lex_input_stream *lip= YYLIP;
lex->sphead->reset_lex(thd);
+ /*
+ We should not push main select here, it will be done or not
+ done by the statement, we just provide only new LEX for the
+ statement here as if it is start of parsing new statement.
+ */
lex->sphead->m_tmp_query= lip->get_tok_start();
}
sp_statement
{
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
- sp_head *sp= lex->sphead;
-
- sp->m_flags|= sp_get_flags_for_command(lex);
- /* "USE db" doesn't work in a procedure */
- if (unlikely(lex->sql_command == SQLCOM_CHANGE_DB))
- my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "USE"));
- /*
- Don't add an instruction for SET statements, since all
- instructions for them were already added during processing
- of "set" rule.
- */
- DBUG_ASSERT(lex->sql_command != SQLCOM_SET_OPTION ||
- lex->var_list.is_empty());
- if (lex->sql_command != SQLCOM_SET_OPTION)
- {
- sp_instr_stmt *i=new (thd->mem_root)
- sp_instr_stmt(sp->instructions(), lex->spcont, lex);
- if (unlikely(i == NULL))
- MYSQL_YYABORT;
-
- /*
- Extract the query statement from the tokenizer. The
- end is either lex->ptr, if there was no lookahead,
- lex->tok_end otherwise.
- */
- if (yychar == YYEMPTY)
- i->m_query.length= lip->get_ptr() - sp->m_tmp_query;
- else
- i->m_query.length= lip->get_tok_start() - sp->m_tmp_query;;
- if (unlikely(!(i->m_query.str= strmake_root(thd->mem_root,
- sp->m_tmp_query,
- i->m_query.length))) ||
- unlikely(sp->add_instr(i)))
- MYSQL_YYABORT;
- }
- if (unlikely(sp->restore_lex(thd)))
+ if (Lex->sp_proc_stmt_statement_finalize(thd, yychar == YYEMPTY) ||
+ Lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
;
@@ -3981,11 +4082,16 @@ RETURN_ALLMODES_SYM:
sp_proc_stmt_return:
RETURN_ALLMODES_SYM
- { Lex->sphead->reset_lex(thd); }
+ {
+ Lex->sphead->reset_lex(thd);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
expr
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
+ Lex->pop_select(); //main select
if (unlikely(sp->m_handler->add_instr_freturn(thd, sp, lex->spcont,
$3, lex)) ||
unlikely(sp->restore_lex(thd)))
@@ -4002,7 +4108,16 @@ sp_proc_stmt_return:
;
reset_lex_expr:
- { Lex->sphead->reset_lex(thd); } expr { $$= $2; }
+ {
+ Lex->sphead->reset_lex(thd);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
+ expr
+ {
+ $$= $2;
+ Lex->pop_select(); //main select
+ }
;
sp_proc_stmt_exit_oracle:
@@ -4018,14 +4133,14 @@ sp_proc_stmt_exit_oracle:
}
| EXIT_ORACLE_SYM WHEN_SYM reset_lex_expr
{
- if (unlikely(Lex->sp_exit_statement(thd, $3)) ||
- unlikely(Lex->sphead->restore_lex(thd)))
+ if (Lex->sp_exit_statement(thd, $3) ||
+ Lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
| EXIT_ORACLE_SYM label_ident WHEN_SYM reset_lex_expr
{
- if (unlikely(Lex->sp_exit_statement(thd, &$2, $4)) ||
- unlikely(Lex->sphead->restore_lex(thd)))
+ if (Lex->sp_exit_statement(thd, &$2, $4) ||
+ Lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
;
@@ -4043,14 +4158,14 @@ sp_proc_stmt_continue_oracle:
}
| CONTINUE_ORACLE_SYM WHEN_SYM reset_lex_expr
{
- if (unlikely(Lex->sp_continue_statement(thd, $3)) ||
- unlikely(Lex->sphead->restore_lex(thd)))
+ if (Lex->sp_continue_statement(thd, $3) ||
+ Lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
| CONTINUE_ORACLE_SYM label_ident WHEN_SYM reset_lex_expr
{
- if (unlikely(Lex->sp_continue_statement(thd, &$2, $4)) ||
- unlikely(Lex->sphead->restore_lex(thd)))
+ if (Lex->sp_continue_statement(thd, &$2, $4) ||
+ Lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
;
@@ -4101,6 +4216,8 @@ assignment_source_expr:
{
DBUG_ASSERT(thd->free_list == NULL);
Lex->sphead->reset_lex(thd, $1);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
expr
{
@@ -4109,7 +4226,8 @@ assignment_source_expr:
$$->sp_lex_in_use= true;
$$->set_item_and_free_list($3, thd->free_list);
thd->free_list= NULL;
- if (unlikely($$->sphead->restore_lex(thd)))
+ Lex->pop_select(); //main select
+ if ($$->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
;
@@ -4118,6 +4236,9 @@ for_loop_bound_expr:
assignment_source_lex
{
Lex->sphead->reset_lex(thd, $1);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ Lex->current_select->parsing_place= FOR_LOOP_BOUND;
}
expr
{
@@ -4125,8 +4246,10 @@ for_loop_bound_expr:
$$= $1;
$$->sp_lex_in_use= true;
$$->set_item_and_free_list($3, NULL);
+ Lex->pop_select(); //main select
if (unlikely($$->sphead->restore_lex(thd)))
MYSQL_YYABORT;
+ Lex->current_select->parsing_place= NO_MATTER;
}
;
@@ -4176,7 +4299,12 @@ sp_proc_stmt_fetch_head:
;
sp_proc_stmt_fetch:
- sp_proc_stmt_fetch_head sp_fetch_list { }
+ sp_proc_stmt_fetch_head sp_fetch_list { }
+ | FETCH_SYM GROUP_SYM NEXT_SYM ROW_SYM
+ {
+ if (unlikely(Lex->sp_add_agg_cfetch()))
+ MYSQL_YYABORT;
+ }
;
sp_proc_stmt_close:
@@ -4231,7 +4359,11 @@ sp_fetch_list:
;
sp_if:
- { Lex->sphead->reset_lex(thd); }
+ {
+ Lex->sphead->reset_lex(thd);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
expr THEN_SYM
{
LEX *lex= Lex;
@@ -4245,6 +4377,7 @@ sp_if:
unlikely(sp->add_cont_backpatch(i)) ||
unlikely(sp->add_instr(i)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
if (unlikely(sp->restore_lex(thd)))
MYSQL_YYABORT;
}
@@ -4345,12 +4478,18 @@ case_stmt_specification:
;
case_stmt_body:
- { Lex->sphead->reset_lex(thd); /* For expr $2 */ }
+ {
+ Lex->sphead->reset_lex(thd); /* For expr $2 */
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
expr
{
if (unlikely(Lex->case_stmt_action_expr($2)))
MYSQL_YYABORT;
- if (unlikely(Lex->sphead->restore_lex(thd)))
+
+ Lex->pop_select(); //main select
+ if (Lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
simple_when_clause_list
@@ -4373,6 +4512,8 @@ simple_when_clause:
WHEN_SYM
{
Lex->sphead->reset_lex(thd); /* For expr $3 */
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
expr
{
@@ -4381,6 +4522,7 @@ simple_when_clause:
LEX *lex= Lex;
if (unlikely(lex->case_stmt_action_when($3, true)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
/* For expr $3 */
if (unlikely(lex->sphead->restore_lex(thd)))
MYSQL_YYABORT;
@@ -4397,12 +4539,15 @@ searched_when_clause:
WHEN_SYM
{
Lex->sphead->reset_lex(thd); /* For expr $3 */
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
expr
{
LEX *lex= Lex;
if (unlikely(lex->case_stmt_action_when($3, false)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
/* For expr $3 */
if (unlikely(lex->sphead->restore_lex(thd)))
MYSQL_YYABORT;
@@ -4591,9 +4736,15 @@ opt_sp_for_loop_direction:
;
sp_for_loop_index_and_bounds:
- ident_directly_assignable sp_for_loop_bounds
+ ident_directly_assignable
+ {
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
+ sp_for_loop_bounds
{
- if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $2)))
+ Lex->pop_select(); //main select
+ if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $3)))
MYSQL_YYABORT;
}
;
@@ -4639,8 +4790,11 @@ while_body:
LEX *lex= Lex;
if (unlikely(lex->sp_while_loop_expression(thd, $1)))
MYSQL_YYABORT;
- if (unlikely(lex->sphead->restore_lex(thd)))
+ Lex->pop_select(); //main select
+ if (lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
+ if (lex->main_select_push(true))
+ MYSQL_YYABORT;
}
sp_proc_stmts1 END LOOP_SYM
{
@@ -4651,7 +4805,11 @@ while_body:
repeat_body:
sp_proc_stmts1 UNTIL_SYM
- { Lex->sphead->reset_lex(thd); }
+ {
+ Lex->sphead->reset_lex(thd);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
expr END REPEAT_SYM
{
LEX *lex= Lex;
@@ -4662,7 +4820,8 @@ repeat_body:
if (unlikely(i == NULL) ||
unlikely(lex->sphead->add_instr(i)))
MYSQL_YYABORT;
- if (unlikely(lex->sphead->restore_lex(thd)))
+ Lex->pop_select(); //main select
+ if (lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
/* We can shortcut the cont_backpatch here */
i->m_cont_dest= ip+1;
@@ -4690,6 +4849,8 @@ sp_labeled_control:
if (unlikely(Lex->sp_push_loop_label(thd, &$1)))
MYSQL_YYABORT;
Lex->sphead->reset_lex(thd);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
while_body pop_sp_loop_label
{ }
@@ -4741,6 +4902,8 @@ sp_unlabeled_control:
if (unlikely(Lex->sp_push_loop_empty_label(thd)))
MYSQL_YYABORT;
Lex->sphead->reset_lex(thd);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
while_body
{
@@ -5142,26 +5305,16 @@ size_number:
*/
create_body:
- '(' create_field_list ')'
+ create_field_list_parens
{ Lex->create_info.option_list= NULL; }
opt_create_table_options opt_create_partitioning opt_create_select {}
| opt_create_table_options opt_create_partitioning opt_create_select {}
- /*
- the following rule is redundant, but there's a shift/reduce
- conflict that prevents the rule above from parsing a syntax like
- CREATE TABLE t1 (SELECT 1);
- */
- | '(' create_select_query_specification ')'
- | '(' create_select_query_specification ')'
- { Select->set_braces(1);} union_list {}
- | '(' create_select_query_specification ')'
- { Select->set_braces(1);} union_order_or_limit {}
| create_like
{
Lex->create_info.add(DDL_options_st::OPT_LIKE);
- TABLE_LIST *src_table= Lex->select_lex.add_table_to_list(thd,
- $1, NULL, 0, TL_READ, MDL_SHARED_READ);
+ TABLE_LIST *src_table= Lex->first_select_lex()->
+ add_table_to_list(thd, $1, NULL, 0, TL_READ, MDL_SHARED_READ);
if (unlikely(! src_table))
MYSQL_YYABORT;
/* CREATE TABLE ... LIKE is not allowed for views. */
@@ -5171,7 +5324,7 @@ create_body:
create_like:
LIKE table_ident { $$= $2; }
- | '(' LIKE table_ident ')' { $$= $3; }
+ | LEFT_PAREN_LIKE LIKE table_ident ')' { $$= $3; }
;
opt_create_select:
@@ -5180,23 +5333,19 @@ opt_create_select:
;
create_select_query_expression:
- opt_with_clause SELECT_SYM create_select_part2 opt_table_expression
- create_select_part4
- {
- Select->set_braces(0);
- Select->set_with_clause($1);
+ query_expression
+ {
+ if (Lex->parsed_insert_select($1->first_select()))
+ MYSQL_YYABORT;
}
- union_clause
- | opt_with_clause SELECT_SYM create_select_part2
- create_select_part3_union_not_ready create_select_part4
+ | LEFT_PAREN_WITH with_clause query_expression_no_with_clause ')'
{
- Select->set_with_clause($1);
+ SELECT_LEX *first_select= $3->first_select();
+ $3->set_with_clause($2);
+ $2->attach_to(first_select);
+ if (Lex->parsed_insert_select(first_select))
+ MYSQL_YYABORT;
}
- | '(' create_select_query_specification ')'
- | '(' create_select_query_specification ')'
- { Select->set_braces(1);} union_list {}
- | '(' create_select_query_specification ')'
- { Select->set_braces(1);} union_order_or_limit {}
;
opt_create_partitioning:
@@ -5284,8 +5433,13 @@ partition_entry:
We enter here when opening the frm file to translate
partition info string into part_info data structure.
*/
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ partition
+ {
+ Lex->pop_select(); //main select
}
- partition {}
;
partition:
@@ -5934,56 +6088,6 @@ opt_versioning_interval_start:
End of partition parser part
*/
-create_select_query_specification:
- opt_with_clause SELECT_SYM create_select_part2 create_select_part3
- create_select_part4
- {
- Select->set_with_clause($1);
- }
- ;
-
-create_select_part2:
- {
- LEX *lex=Lex;
- if (lex->sql_command == SQLCOM_INSERT)
- lex->sql_command= SQLCOM_INSERT_SELECT;
- else if (lex->sql_command == SQLCOM_REPLACE)
- lex->sql_command= SQLCOM_REPLACE_SELECT;
- /*
- The following work only with the local list, the global list
- is created correctly in this case
- */
- lex->current_select->table_list.save_and_clear(&lex->save_list);
- mysql_init_select(lex);
- lex->current_select->parsing_place= SELECT_LIST;
- }
- select_options select_item_list
- {
- Select->parsing_place= NO_MATTER;
- }
- ;
-
-create_select_part3:
- opt_table_expression
- | create_select_part3_union_not_ready
- ;
-
-create_select_part3_union_not_ready:
- table_expression order_or_limit
- | order_or_limit
- ;
-
-create_select_part4:
- opt_select_lock_type
- {
- /*
- The following work only with the local list, the global list
- is created correctly in this case
- */
- Lex->current_select->table_list.push_front(&Lex->save_list);
- }
- ;
-
opt_as:
/* empty */ {}
| AS {}
@@ -6211,7 +6315,7 @@ create_table_option:
}
| UNION_SYM opt_equal
{
- Lex->select_lex.table_list.save_and_clear(&Lex->save_list);
+ Lex->first_select_lex()->table_list.save_and_clear(&Lex->save_list);
}
'(' opt_table_list ')'
{
@@ -6220,8 +6324,8 @@ create_table_option:
from the global list.
*/
LEX *lex=Lex;
- lex->create_info.merge_list= lex->select_lex.table_list.first;
- lex->select_lex.table_list= lex->save_list;
+ lex->create_info.merge_list= lex->first_select_lex()->table_list.first;
+ lex->first_select_lex()->table_list= lex->save_list;
/*
When excluding union list from the global list we assume that
elements of the former immediately follow elements which represent
@@ -6324,7 +6428,7 @@ versioning_option:
{
if (DBUG_EVALUATE_IF("sysvers_force", 0, 1))
{
- my_error(ER_VERS_TEMPORARY, MYF(0));
+ my_error(ER_VERS_NOT_SUPPORTED, MYF(0), "CREATE TEMPORARY TABLE");
MYSQL_YYABORT;
}
}
@@ -6411,6 +6515,13 @@ create_field_list:
}
;
+create_field_list_parens:
+ LEFT_PAREN_ALT field_list ')'
+ {
+ Lex->create_last_non_select_table= Lex->last_table();
+ }
+ ;
+
field_list:
field_list_item
| field_list ',' field_list_item
@@ -6421,6 +6532,7 @@ field_list_item:
| key_def
| constraint_def
| period_for_system_time
+ | PERIOD_SYM period_for_application_time { }
;
column_def:
@@ -6517,7 +6629,7 @@ key_def:
constraint_def:
opt_constraint check_constraint
{
- Lex->add_constraint(&$1, $2, FALSE);
+ Lex->add_constraint($1, $2, FALSE);
}
;
@@ -6526,7 +6638,15 @@ period_for_system_time:
PERIOD_SYM FOR_SYSTEM_TIME_SYM '(' ident ',' ident ')'
{
Vers_parse_info &info= Lex->vers_get_info();
- info.set_system_time($4, $6);
+ info.set_period($4, $6);
+ }
+ ;
+
+period_for_application_time:
+ FOR_SYM ident '(' ident ',' ident ')'
+ {
+ if (Lex->add_period($2, $4, $6))
+ MYSQL_YYABORT;
}
;
@@ -6715,6 +6835,8 @@ parse_vcol_expr:
Prevent the end user from invoking this command.
*/
MYSQL_YYABORT_UNLESS(Lex->parse_vcol_expr);
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
}
expr
{
@@ -6722,17 +6844,12 @@ parse_vcol_expr:
if (unlikely(!v))
MYSQL_YYABORT;
Lex->last_field->vcol_info= v;
+ Lex->pop_select(); //main select
}
;
parenthesized_expr:
- subselect
- {
- $$= new (thd->mem_root) Item_singlerow_subselect(thd, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | expr
+ expr
| expr ',' expr_list
{
$3->push_front($1, thd->mem_root);
@@ -6751,6 +6868,16 @@ virtual_column_func:
MYSQL_YYABORT;
$$= v;
}
+ | subquery
+ {
+ Item *item;
+ if (!(item= new (thd->mem_root) Item_singlerow_subselect(thd, $1)))
+ MYSQL_YYABORT;
+ Virtual_column_info *v= add_virtual_expression(thd, item);
+ if (unlikely(!v))
+ MYSQL_YYABORT;
+ $$= v;
+ }
;
expr_or_literal: column_default_non_parenthesized_expr | signed_literal ;
@@ -7042,7 +7169,7 @@ field_type_lob:
| JSON_SYM opt_compressed
{
Lex->charset= &my_charset_utf8mb4_bin;
- $$.set(&type_handler_long_blob);
+ $$.set(&type_handler_json_longtext);
}
;
@@ -7151,7 +7278,6 @@ opt_field_length_default_1:
| field_length { $$= $1; }
;
-
/*
In sql_mode=ORACLE, real size of VARCHAR and CHAR with no length
in SP parameters is fixed at runtime with the length of real args.
@@ -7806,23 +7932,25 @@ alter:
Lex->name= null_clex_str;
Lex->table_type= TABLE_TYPE_UNKNOWN;
Lex->sql_command= SQLCOM_ALTER_TABLE;
- Lex->duplicates= DUP_ERROR;
- Lex->select_lex.init_order();
+ Lex->duplicates= DUP_ERROR;
+ Lex->first_select_lex()->order_list.empty();
Lex->create_info.init();
Lex->create_info.row_type= ROW_TYPE_NOT_USED;
Lex->alter_info.reset();
Lex->no_write_to_binlog= 0;
Lex->create_info.storage_media= HA_SM_DEFAULT;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
DBUG_ASSERT(!Lex->m_sql_cmd);
}
alter_options TABLE_SYM table_ident opt_lock_wait_timeout
{
- if (unlikely(!Lex->select_lex.add_table_to_list(thd, $5, NULL,
- TL_OPTION_UPDATING,
- TL_READ_NO_INSERT,
- MDL_SHARED_UPGRADABLE)))
+ if (!Lex->first_select_lex()->
+ add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT, MDL_SHARED_UPGRADABLE))
MYSQL_YYABORT;
- Lex->select_lex.db= (Lex->select_lex.table_list.first)->db;
+ Lex->first_select_lex()->db=
+ (Lex->first_select_lex()->table_list.first)->db;
Lex->create_last_non_select_table= Lex->last_table();
Lex->mark_first_table_as_inserting();
}
@@ -7835,11 +7963,14 @@ alter:
if (unlikely(Lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
+ Lex->pop_select(); //main select
}
| ALTER DATABASE ident_or_empty
{
Lex->create_info.default_table_charset= NULL;
Lex->create_info.used_fields= 0;
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
}
create_database_options
{
@@ -7849,6 +7980,7 @@ alter:
if (lex->name.str == NULL &&
unlikely(lex->copy_db_to(&lex->name)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
}
| ALTER DATABASE ident UPGRADE_SYM DATA_SYM DIRECTORY_SYM NAME_SYM
{
@@ -7864,6 +7996,8 @@ alter:
if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"));
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
lex->sp_chistics.init();
}
sp_a_chistics
@@ -7872,6 +8006,9 @@ alter:
lex->sql_command= SQLCOM_ALTER_PROCEDURE;
lex->spname= $3;
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
}
| ALTER FUNCTION_SYM sp_name
{
@@ -7879,6 +8016,8 @@ alter:
if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"));
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
lex->sp_chistics.init();
}
sp_a_chistics
@@ -7887,14 +8026,23 @@ alter:
lex->sql_command= SQLCOM_ALTER_FUNCTION;
lex->spname= $3;
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
}
| ALTER view_algorithm definer_opt opt_view_suid VIEW_SYM table_ident
{
- if (unlikely(Lex->add_alter_view(thd, $2, $4, $6)))
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ if (Lex->add_alter_view(thd, $2, $4, $6))
MYSQL_YYABORT;
}
view_list_opt AS view_select
- {}
+ {
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
+ }
| ALTER definer_opt opt_view_suid VIEW_SYM table_ident
/*
We have two separate rules for ALTER VIEW rather that
@@ -7902,14 +8050,22 @@ alter:
with the ALTER EVENT below.
*/
{
- if (unlikely(Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5)))
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ if (Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5))
MYSQL_YYABORT;
}
view_list_opt AS view_select
- {}
+ {
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
+ }
| ALTER definer_opt remember_name EVENT_SYM sp_name
{
- /*
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ /*
It is safe to use Lex->spname because
ALTER EVENT xxx RENATE TO yyy DO ALTER EVENT RENAME TO
is not allowed. Lex->spname is used in the case of RENAME TO
@@ -7941,6 +8097,8 @@ alter:
*/
Lex->sql_command= SQLCOM_ALTER_EVENT;
Lex->stmt_definition_end= (char*)YYLIP->get_cpp_ptr();
+
+ Lex->pop_select(); //main select
}
| ALTER TABLESPACE alter_tablespace_info
{
@@ -7970,7 +8128,7 @@ alter:
} OPTIONS_SYM '(' server_options_list ')' { }
/* ALTER USER foo is allowed for MySQL compatibility. */
| ALTER USER_SYM opt_if_exists clear_privileges grant_list
- opt_require_clause opt_resource_options
+ opt_require_clause opt_resource_options opt_account_locking_and_opt_password_expiration
{
Lex->create_info.set($3);
Lex->sql_command= SQLCOM_ALTER_USER;
@@ -7984,16 +8142,17 @@ alter:
lex->create_info.init();
lex->no_write_to_binlog= 0;
DBUG_ASSERT(!lex->m_sql_cmd);
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
}
table_ident
{
LEX *lex= Lex;
- if (unlikely(!(lex->create_info.seq_create_info=
- new (thd->mem_root) sequence_definition())) ||
- unlikely(!lex->select_lex.add_table_to_list(thd, $5, NULL,
- TL_OPTION_SEQUENCE,
- TL_WRITE,
- MDL_EXCLUSIVE)))
+ if (!(lex->create_info.seq_create_info= new (thd->mem_root)
+ sequence_definition()) ||
+ !lex->first_select_lex()->
+ add_table_to_list(thd, $5, NULL, TL_OPTION_SEQUENCE,
+ TL_WRITE, MDL_EXCLUSIVE))
MYSQL_YYABORT;
}
sequence_defs
@@ -8002,9 +8161,52 @@ alter:
Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_sequence($3);
if (unlikely(Lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
+ }
+ ;
+
+account_locking_option:
+ LOCK_SYM
+ {
+ Lex->account_options.account_locked= ACCOUNTLOCK_LOCKED;
+ }
+ | UNLOCK_SYM
+ {
+ Lex->account_options.account_locked= ACCOUNTLOCK_UNLOCKED;
+ }
+ ;
+
+opt_password_expire_option:
+ /* empty */
+ {
+ Lex->account_options.password_expire= PASSWORD_EXPIRE_NOW;
+ }
+ | NEVER_SYM
+ {
+ Lex->account_options.password_expire= PASSWORD_EXPIRE_NEVER;
+ }
+ | DEFAULT
+ {
+ Lex->account_options.password_expire= PASSWORD_EXPIRE_DEFAULT;
+ }
+ | INTERVAL_SYM NUM DAY_SYM
+ {
+ Lex->account_options.password_expire= PASSWORD_EXPIRE_INTERVAL;
+ if (!(Lex->account_options.num_expiration_days= atoi($2.str)))
+ my_yyabort_error((ER_WRONG_VALUE, MYF(0), "DAY", $2.str));
}
;
+opt_account_locking_and_opt_password_expiration:
+ /* empty */
+ | ACCOUNT_SYM account_locking_option
+ | PASSWORD_SYM EXPIRE_SYM opt_password_expire_option
+ | ACCOUNT_SYM account_locking_option PASSWORD_SYM EXPIRE_SYM opt_password_expire_option
+ | PASSWORD_SYM EXPIRE_SYM opt_password_expire_option ACCOUNT_SYM account_locking_option
+ ;
+
ev_alter_on_schedule_completion:
/* empty */ { $$= 0;}
| ON SCHEDULE_SYM ev_schedule_time { $$= 1; }
@@ -8150,22 +8352,7 @@ alter_commands:
| EXCHANGE_SYM PARTITION_SYM alt_part_name_item
WITH TABLE_SYM table_ident have_partitioning
{
- LEX *lex= thd->lex;
- lex->select_lex.db= $6->db;
- if (lex->select_lex.db.str == NULL &&
- unlikely(lex->copy_db_to(&lex->select_lex.db)))
- MYSQL_YYABORT;
- lex->name= $6->table;
- lex->alter_info.partition_flags|= ALTER_PARTITION_EXCHANGE;
- if (unlikely(!lex->select_lex.add_table_to_list(thd, $6, NULL,
- TL_OPTION_UPDATING,
- TL_READ_NO_INSERT,
- MDL_SHARED_NO_WRITE)))
- MYSQL_YYABORT;
- DBUG_ASSERT(!lex->m_sql_cmd);
- lex->m_sql_cmd= new (thd->mem_root)
- Sql_cmd_alter_table_exchange_partition();
- if (unlikely(lex->m_sql_cmd == NULL))
+ if (Lex->stmt_alter_table_exchange_partition($6))
MYSQL_YYABORT;
}
;
@@ -8289,6 +8476,13 @@ alter_list_item:
{
Lex->alter_info.flags|= ALTER_ADD_PERIOD;
}
+ | ADD
+ PERIOD_SYM opt_if_not_exists_table_element period_for_application_time
+ {
+ Table_period_info &period= Lex->create_info.period_info;
+ period.create_if_not_exists= Lex->check_exists;
+ Lex->alter_info.flags|= ALTER_ADD_CHECK_CONSTRAINT;
+ }
| add_column '(' create_field_list ')'
{
LEX *lex=Lex;
@@ -8303,7 +8497,7 @@ alter_list_item:
| ADD CONSTRAINT IF_SYM not EXISTS field_ident check_constraint
{
Lex->alter_info.flags|= ALTER_ADD_CHECK_CONSTRAINT;
- Lex->add_constraint(&$6, $7, TRUE);
+ Lex->add_constraint($6, $7, TRUE);
}
| CHANGE opt_column opt_if_exists_table_element field_ident
field_spec opt_place
@@ -8401,9 +8595,9 @@ alter_list_item:
| RENAME opt_to table_ident
{
LEX *lex=Lex;
- lex->select_lex.db= $3->db;
- if (lex->select_lex.db.str == NULL &&
- unlikely(lex->copy_db_to(&lex->select_lex.db)))
+ lex->first_select_lex()->db= $3->db;
+ if (lex->first_select_lex()->db.str == NULL &&
+ lex->copy_db_to(&lex->first_select_lex()->db))
MYSQL_YYABORT;
if (unlikely(check_table_name($3->table.str,$3->table.length,
FALSE)) ||
@@ -8424,7 +8618,7 @@ alter_list_item:
$5->name, $4->csname));
if (unlikely(Lex->create_info.add_alter_list_item_convert_to_charset($5)))
MYSQL_YYABORT;
- Lex->alter_info.flags|= ALTER_OPTIONS;
+ Lex->alter_info.flags|= ALTER_CONVERT_TO;
}
| create_table_options_space_separated
{
@@ -8456,6 +8650,14 @@ alter_list_item:
{
Lex->alter_info.flags|= ALTER_DROP_PERIOD;
}
+ | DROP PERIOD_SYM opt_if_exists_table_element FOR_SYM ident
+ {
+ Alter_drop *ad= new Alter_drop(Alter_drop::PERIOD, $5.str, $3);
+ if (unlikely(ad == NULL))
+ MYSQL_YYABORT;
+ Lex->alter_info.drop_list.push_back(ad, thd->mem_root);
+ Lex->alter_info.flags|= ALTER_DROP_CHECK_CONSTRAINT;
+ }
;
opt_index_lock_algorithm:
@@ -8525,7 +8727,6 @@ alter_option:
}
;
-
opt_restrict:
/* empty */ { Lex->drop_mode= DROP_DEFAULT; }
| RESTRICT { Lex->drop_mode= DROP_RESTRICT; }
@@ -8804,6 +9005,7 @@ persistent_column_stat_spec:
}
table_column_list
')'
+ { }
;
persistent_index_stat_spec:
@@ -8817,6 +9019,7 @@ persistent_index_stat_spec:
}
table_index_list
')'
+ { }
;
table_column_list:
@@ -8959,9 +9162,13 @@ rename:
RENAME table_or_tables
{
Lex->sql_command= SQLCOM_RENAME_TABLE;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
}
table_to_table_list
- {}
+ {
+ Lex->pop_select(); //main select
+ }
| RENAME USER_SYM clear_privileges rename_list
{
Lex->sql_command = SQLCOM_RENAME_USER;
@@ -9059,9 +9266,13 @@ preload:
LEX *lex=Lex;
lex->sql_command=SQLCOM_PRELOAD_KEYS;
lex->alter_info.reset();
+ if (lex->main_select_push())
+ MYSQL_YYABORT;
}
preload_list_or_parts
- {}
+ {
+ Lex->pop_select(); //main select
+ }
;
preload_list_or_parts:
@@ -9106,9 +9317,9 @@ adm_partition:
cache_keys_spec:
{
- Lex->select_lex.alloc_index_hints(thd);
- Select->set_index_hint_type(INDEX_HINT_USE,
- INDEX_HINT_MASK_ALL);
+ Lex->first_select_lex()->alloc_index_hints(thd);
+ Lex->first_select_lex()->set_index_hint_type(INDEX_HINT_USE,
+ INDEX_HINT_MASK_ALL);
}
cache_key_list_or_empty
;
@@ -9128,245 +9339,390 @@ opt_ignore_leaves:
Select : retrieve data from table
*/
-
select:
- opt_with_clause select_init
+ query_expression_no_with_clause
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->current_select->set_with_clause($1);
+ if (Lex->push_select($1->fake_select_lex ?
+ $1->fake_select_lex :
+ $1->first_select()))
+ MYSQL_YYABORT;
+ }
+ opt_procedure_or_into
+ {
+ Lex->pop_select();
+ $1->set_with_clause(NULL);
+ if (Lex->select_finalize($1, $3))
+ MYSQL_YYABORT;
+ }
+ | with_clause query_expression_no_with_clause
+ {
+ if (Lex->push_select($2->fake_select_lex ?
+ $2->fake_select_lex :
+ $2->first_select()))
+ MYSQL_YYABORT;
+ }
+ opt_procedure_or_into
+ {
+ Lex->pop_select();
+ $2->set_with_clause($1);
+ $1->attach_to($2->first_select());
+ if (Lex->select_finalize($2, $4))
+ MYSQL_YYABORT;
}
;
-select_init:
- SELECT_SYM select_options_and_item_list select_init3
- | table_value_constructor
- | table_value_constructor union_list
- | table_value_constructor union_order_or_limit
- | '(' select_paren ')'
- | '(' select_paren ')' union_list
- | '(' select_paren ')' union_order_or_limit
- ;
-
-union_list_part2:
- SELECT_SYM select_options_and_item_list select_init3_union_query_term
- | table_value_constructor
- | table_value_constructor union_list
- | table_value_constructor union_order_or_limit
- | '(' select_paren_union_query_term ')'
- | '(' select_paren_union_query_term ')' union_list
- | '(' select_paren_union_query_term ')' union_order_or_limit
- ;
-select_paren:
+select_into:
+ select_into_query_specification
{
- Lex->current_select->set_braces(true);
+ if (Lex->push_select($1))
+ MYSQL_YYABORT;
}
- table_value_constructor select_part3
+ opt_order_limit_lock
{
- DBUG_ASSERT(Lex->current_select->braces);
- }
- |
+ SELECT_LEX_UNIT *unit;
+ if (!(unit = Lex->create_unit($1)))
+ MYSQL_YYABORT;
+ if ($3)
+ unit= Lex->add_tail_to_query_expression_body(unit, $3);
+ if (Lex->select_finalize(unit))
+ MYSQL_YYABORT;
+ }
+ | with_clause
+ select_into_query_specification
{
- /*
- In order to correctly parse UNION's global ORDER BY we need to
- set braces before parsing the clause.
- */
- Lex->current_select->set_braces(true);
+ if (Lex->push_select($2))
+ MYSQL_YYABORT;
}
- SELECT_SYM select_options_and_item_list select_part3
- opt_select_lock_type
+ opt_order_limit_lock
{
- DBUG_ASSERT(Lex->current_select->braces);
+ SELECT_LEX_UNIT *unit;
+ if (!(unit = Lex->create_unit($2)))
+ MYSQL_YYABORT;
+ if ($4)
+ unit= Lex->add_tail_to_query_expression_body(unit, $4);
+ unit->set_with_clause($1);
+ $1->attach_to($2);
+ if (Lex->select_finalize(unit))
+ MYSQL_YYABORT;
}
- | '(' select_paren ')'
- ;
+ ;
+
-select_parent_union_query_term_proper:
- SELECT_SYM select_options_and_item_list select_part3_union_query_term
- opt_select_lock_type
- | table_value_constructor select_part3_union_query_term
+simple_table:
+ query_specification { $$= $1; }
+ | table_value_constructor { $$= $1; }
;
-select_paren_union_query_term:
+table_value_constructor:
+ VALUES
+ {
+ if (Lex->parsed_TVC_start())
+ MYSQL_YYABORT;
+ }
+ values_list
+ {
+ if (!($$= Lex->parsed_TVC_end()))
+ MYSQL_YYABORT;
+ }
+ ;
+
+query_specification_start:
+ SELECT_SYM
{
- /*
- In order to correctly parse UNION's global ORDER BY we need to
- set braces before parsing the clause.
- */
- Lex->current_select->set_braces(true);
+ SELECT_LEX *sel;
+ LEX *lex= Lex;
+ if (!(sel= lex->alloc_select(TRUE)) ||
+ lex->push_select(sel))
+ MYSQL_YYABORT;
+ sel->init_select();
+ sel->braces= FALSE;
}
- select_parent_union_query_term_proper
+ select_options
{
- DBUG_ASSERT(Lex->current_select->braces);
+ Select->parsing_place= SELECT_LIST;
}
- | '(' select_paren_union_query_term ')'
- ;
-
-select_parent_view_proper:
- SELECT_SYM select_options_and_item_list select_part3_view
- opt_select_lock_type
- | table_value_constructor select_part3_view
- ;
+ select_item_list
+ {
+ Select->parsing_place= NO_MATTER;
+ }
+ ;
-select_paren_view:
+query_specification:
+ query_specification_start
+ opt_from_clause
+ opt_where_clause
+ opt_group_clause
+ opt_having_clause
+ opt_window_clause
{
- /*
- In order to correctly parse UNION's global ORDER BY we need to
- set braces before parsing the clause.
- */
- Lex->current_select->set_braces(true);
+ $$= Lex->pop_select();
}
- select_parent_view_proper
+ ;
+
+select_into_query_specification:
+ query_specification_start
+ into
+ opt_from_clause
+ opt_where_clause
+ opt_group_clause
+ opt_having_clause
+ opt_window_clause
{
- DBUG_ASSERT(Lex->current_select->braces);
+ $$= Lex->pop_select();
}
- | '(' select_paren_view ')'
;
-/* The equivalent of select_paren for nested queries. */
-select_paren_derived:
+/**
+
+ The following grammar for query expressions conformant to
+ the latest SQL Standard is supported:
+
+ <query expression> ::=
+ [ <with clause> ] <query expression body>
+ [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+
+ <with clause> ::=
+ WITH [ RECURSIVE ] <with_list
+
+ <with list> ::=
+ <with list element> [ { <comma> <with list element> }... ]
+
+ <with list element> ::=
+ <query name> [ '(' <with column list> ')' ]
+ AS <table subquery>
+
+ <with column list> ::=
+ <column name list>
+
+ <query expression body> ::
+ <query term>
+ | <query expression body> UNION [ ALL | DISTINCT ] <query term>
+ | <query expression body> EXCEPT [ DISTINCT ] <query term>
+
+ <query term> ::=
+ <query primary>
+ | <query term> INTERSECT [ DISTINCT ] <query primary>
+
+ <query primary> ::=
+ <simple table>
+ | '(' <query expression body>
+ [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+ ')'
+
+ <simple table>
+ <query specification>
+ | <table value constructor>
+
+ <subquery>
+ '(' <query_expression> ')'
+
+*/
+
+/*
+ query_expression produces the same expressions as
+ <query expression>
+*/
+
+query_expression:
+ query_expression_no_with_clause
{
- Lex->current_select->set_braces(true);
+ $1->set_with_clause(NULL);
+ $$= $1;
}
- table_value_constructor
+ | with_clause
+ query_expression_no_with_clause
{
- DBUG_ASSERT(Lex->current_select->braces);
- $$= Lex->current_select->master_unit()->first_select();
+ $2->set_with_clause($1);
+ $1->attach_to($2->first_select());
+ $$= $2;
}
- |
+ ;
+
+/*
+ query_expression_no_with_clause produces the same expressions as
+ <query expression> without [ <with clause> ]
+*/
+
+query_expression_no_with_clause:
+ query_expression_body_ext { $$= $1; }
+ | query_expression_body_ext_parens { $$= $1; }
+ ;
+
+/*
+ query_expression_body_ext produces the same expressions as
+ <query expression body>
+ [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+ | '('... <query expression body>
+ [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
+ ')'...
+ Note: number of ')' must be equal to the number of '(' in the rule above
+*/
+
+query_expression_body_ext:
+ query_expression_body
{
- Lex->current_select->set_braces(true);
+ if ($1->first_select()->next_select())
+ {
+ if (Lex->parsed_multi_operand_query_expression_body($1))
+ MYSQL_YYABORT;
+ }
}
- SELECT_SYM select_part2_derived
- opt_table_expression
- opt_order_clause
- opt_limit_clause
- opt_select_lock_type
+ opt_query_expression_tail
{
- DBUG_ASSERT(Lex->current_select->braces);
- $$= Lex->current_select->master_unit()->first_select();
+ if (!$3)
+ $$= $1;
+ else
+ $$= Lex->add_tail_to_query_expression_body($1, $3);
}
- | '(' select_paren_derived ')' { $$= $2; }
- ;
-
-select_init3:
- opt_table_expression
- opt_select_lock_type
+ | query_expression_body_ext_parens
{
- /* Parentheses carry no meaning here */
- Lex->current_select->set_braces(false);
+ Lex->push_select(!$1->first_select()->next_select() ?
+ $1->first_select() : $1->fake_select_lex);
}
- union_clause
- | select_part3_union_not_ready
- opt_select_lock_type
+ query_expression_tail
{
- /* Parentheses carry no meaning here */
- Lex->current_select->set_braces(false);
+ if (!($$= Lex->add_tail_to_query_expression_body_ext_parens($1, $3)))
+ MYSQL_YYABORT;
}
;
-
-select_init3_union_query_term:
- opt_table_expression
- opt_select_lock_type
- {
- /* Parentheses carry no meaning here */
- Lex->current_select->set_braces(false);
- }
- union_clause
- | select_part3_union_not_ready_noproc
- opt_select_lock_type
+query_expression_body_ext_parens:
+ '(' query_expression_body_ext_parens ')'
+ { $$= $2; }
+ | '(' query_expression_body_ext ')'
{
- /* Parentheses carry no meaning here */
- Lex->current_select->set_braces(false);
+ SELECT_LEX *sel= $2->first_select()->next_select() ?
+ $2->fake_select_lex : $2->first_select();
+ sel->braces= true;
+ $$= $2;
}
;
+/*
+ query_expression_body produces the same expressions as
+ <query expression body>
+*/
-select_init3_view:
- opt_table_expression opt_select_lock_type
+query_expression_body:
+ query_simple
{
- Lex->current_select->set_braces(false);
+ Lex->push_select($1);
+ if (!($$= Lex->create_unit($1)))
+ MYSQL_YYABORT;
}
- | opt_table_expression opt_select_lock_type
+ | query_expression_body
+ unit_type_decl
{
- Lex->current_select->set_braces(false);
+ if (!$1->first_select()->next_select())
+ {
+ Lex->pop_select();
+ }
}
- union_list_view
- | order_or_limit opt_select_lock_type
+ query_primary
{
- Lex->current_select->set_braces(false);
+ if (!($$= Lex->add_primary_to_query_expression_body($1, $4,
+ $2.unit_type,
+ $2.distinct,
+ TRUE)))
+ MYSQL_YYABORT;
}
- | table_expression order_or_limit opt_select_lock_type
+ | query_expression_body_ext_parens
+ unit_type_decl
+ query_primary
{
- Lex->current_select->set_braces(false);
+ if (!($$= Lex->add_primary_to_query_expression_body_ext_parens(
+ $1, $3,
+ $2.unit_type,
+ $2.distinct)))
+ MYSQL_YYABORT;
}
;
/*
- The SELECT parts after select_item_list that cannot be followed by UNION.
+ query_primary produces the same expressions as
+ <query primary>
*/
-select_part3:
- opt_table_expression
- | select_part3_union_not_ready
+query_primary:
+ query_simple
+ { $$= $1; }
+ | query_expression_body_ext_parens
+ { $$= $1->first_select(); }
;
-select_part3_union_query_term:
- opt_table_expression
- | select_part3_union_not_ready_noproc
- ;
+/*
+ query_simple produces the same expressions as
+ <simple table>
+*/
-select_part3_view:
- opt_table_expression
- | order_or_limit
- | table_expression order_or_limit
+query_simple:
+ simple_table { $$= $1;}
;
-select_part3_union_not_ready:
- select_part3_union_not_ready_noproc
- | table_expression procedure_clause
- | table_expression order_or_limit procedure_clause
+subselect:
+ query_expression
+ {
+ if (!($$= Lex->parsed_subselect($1)))
+ YYABORT;
+ }
;
-select_part3_union_not_ready_noproc:
- order_or_limit
- | into opt_table_expression opt_order_clause opt_limit_clause
- | table_expression into
- | table_expression order_or_limit
- | table_expression order_or_limit into
- ;
+/*
+ subquery produces the same expressions as
+ <subquery>
+
+ Consider the production rule of the SQL Standard
+ subquery:
+ '(' query_expression ')'
+
+ This rule is equivalent to the rule
+ subquery:
+ '(' query_expression_no_with_clause ')'
+ | '(' with_clause query_expression_no_with_clause ')'
+ that in its turn is equivalent to
+ subquery:
+ '(' query_expression_body_ext ')'
+ | query_expression_body_ext_parens
+ | '(' with_clause query_expression_no_with_clause ')'
+
+ The latter can be re-written into
+ subquery:
+ query_expression_body_ext_parens
+ | '(' with_clause query_expression_no_with_clause ')'
+
+ The last rule allows us to resolve properly the shift/reduce conflict
+ when subquery is used in expressions such as in the following queries
+ select (select * from t1 limit 1) + t2.a from t2
+ select * from t1 where t1.a [not] in (select t2.a from t2)
+
+ In the rule below %prec SUBQUERY_AS_EXPR forces the parser to perform a shift
+ operation rather then a reduce operation when ')' is encountered and can be
+ considered as the last symbol a query expression.
+*/
-select_options_and_item_list:
+subquery:
+ query_expression_body_ext_parens %prec SUBQUERY_AS_EXPR
{
- LEX *lex= Lex;
- SELECT_LEX *sel= lex->current_select;
- if (sel->linkage != UNION_TYPE)
- mysql_init_select(lex);
- lex->current_select->parsing_place= SELECT_LIST;
+ if (!$1->fake_select_lex)
+ $1->first_select()->braces= false;
+ else
+ $1->fake_select_lex->braces= false;
+ if (!($$= Lex->parsed_subselect($1)))
+ YYABORT;
}
- select_options select_item_list
+ | '(' with_clause query_expression_no_with_clause ')'
{
- Select->parsing_place= NO_MATTER;
+ $3->set_with_clause($2);
+ $2->attach_to($3->first_select());
+ if (!($$= Lex->parsed_subselect($3)))
+ YYABORT;
}
;
-
-/**
- <table expression>, as in the SQL standard.
-*/
-table_expression:
- from_clause
- opt_where_clause
- opt_group_clause
- opt_having_clause
- opt_window_clause
- ;
-
-opt_table_expression:
- /* Empty */
- | table_expression
+opt_from_clause:
+ /* empty */ %prec EMPTY_FROM_CLAUSE
+ | from_clause
;
from_clause:
@@ -9416,8 +9772,9 @@ history_point:
TIMESTAMP TEXT_STRING
{
Item *item;
- if (!(item= create_temporal_literal(thd, $2.str, $2.length, YYCSCL,
- MYSQL_TYPE_DATETIME, true)))
+ if (!(item= type_handler_datetime2.create_literal_item(thd,
+ $2.str, $2.length,
+ YYCSCL, true)))
MYSQL_YYABORT;
$$= Vers_history_point(VERS_TIMESTAMP, item);
}
@@ -9431,6 +9788,33 @@ history_point:
}
;
+for_portion_of_time_clause:
+ FOR_SYM PORTION_SYM OF_SYM remember_tok_start ident FROM
+ bit_expr TO_SYM bit_expr
+ {
+ if (unlikely(0 == strcasecmp($5.str, "SYSTEM_TIME")))
+ {
+ thd->parse_error(ER_SYNTAX_ERROR, $4);
+ MYSQL_YYABORT;
+ }
+ Lex->period_conditions.init(SYSTEM_TIME_FROM_TO,
+ Vers_history_point(VERS_TIMESTAMP, $7),
+ Vers_history_point(VERS_TIMESTAMP, $9),
+ $5);
+ }
+ ;
+
+opt_for_portion_of_time_clause:
+ /* empty */
+ {
+ $$= false;
+ }
+ | for_portion_of_time_clause
+ {
+ $$= true;
+ }
+ ;
+
opt_for_system_time_clause:
/* empty */
{
@@ -9470,59 +9854,68 @@ select_option:
query_expression_option
| SQL_NO_CACHE_SYM
{
- /*
- Allow this flag only on the first top-level SELECT statement, if
- SQL_CACHE wasn't specified, and only once per query.
- */
- if (unlikely(Lex->current_select != &Lex->select_lex))
- my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_NO_CACHE"));
- if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE))
- my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_CACHE", "SQL_NO_CACHE"));
- if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE))
+ /*
+ Allow this flag once per query.
+ */
+ if (Select->options & OPTION_NO_QUERY_CACHE)
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_NO_CACHE"));
-
- Lex->safe_to_cache_query=0;
- Lex->select_lex.options&= ~OPTION_TO_QUERY_CACHE;
- Lex->select_lex.sql_cache= SELECT_LEX::SQL_NO_CACHE;
+ Select->options|= OPTION_NO_QUERY_CACHE;
}
| SQL_CACHE_SYM
{
- /*
- Allow this flag only on the first top-level SELECT statement, if
- SQL_NO_CACHE wasn't specified, and only once per query.
- */
- if (unlikely(Lex->current_select != &Lex->select_lex))
- my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CACHE"));
- if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE))
- my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_NO_CACHE", "SQL_CACHE"));
- if (unlikely(Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE))
+ /*
+ Allow this flag once per query.
+ */
+ if (Select->options & OPTION_TO_QUERY_CACHE)
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_CACHE"));
-
- Lex->safe_to_cache_query=1;
- Lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
- Lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE;
+ Select->options|= OPTION_TO_QUERY_CACHE;
}
;
-opt_select_lock_type:
- /* empty */
- | FOR_SYM UPDATE_SYM opt_lock_wait_timeout
+
+select_lock_type:
+ FOR_SYM UPDATE_SYM opt_lock_wait_timeout_new
{
- LEX *lex=Lex;
- lex->current_select->lock_type= TL_WRITE;
- lex->current_select->set_lock_for_tables(TL_WRITE, false);
- lex->safe_to_cache_query=0;
+ $$= $3;
+ $$.defined_lock= TRUE;
+ $$.update_lock= TRUE;
}
- | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout
+ | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout_new
{
- LEX *lex=Lex;
- lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS;
- lex->current_select->
- set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS, false);
- lex->safe_to_cache_query=0;
+ $$= $5;
+ $$.defined_lock= TRUE;
+ $$.update_lock= FALSE;
}
;
+opt_select_lock_type:
+ /* empty */
+ {
+ $$.empty();
+ }
+ | select_lock_type
+ {
+ $$= $1;
+ }
+ ;
+
+opt_lock_wait_timeout_new:
+ /* empty */
+ {
+ $$.empty();
+ }
+ | WAIT_SYM ulong_num
+ {
+ $$.defined_timeout= TRUE;
+ $$.timeout= $2;
+ }
+ | NOWAIT_SYM
+ {
+ $$.defined_timeout= TRUE;
+ $$.timeout= 0;
+ }
+ ;
+
select_item_list:
select_item_list ',' select_item
| select_item
@@ -9572,12 +9965,6 @@ remember_tok_start:
}
;
-remember_tok_end:
- {
- $$= (char*) YYLIP->get_tok_end();
- }
- ;
-
remember_name:
{
$$= (char*) YYLIP->get_cpp_tok_start();
@@ -9803,19 +10190,19 @@ expr:
;
predicate:
- predicate IN_SYM '(' subselect ')'
+ predicate IN_SYM subquery
{
- $$= new (thd->mem_root) Item_in_subselect(thd, $1, $4);
- if (unlikely($$ == NULL))
+ $$= new (thd->mem_root) Item_in_subselect(thd, $1, $3);
+ if (unlikely(!$$))
MYSQL_YYABORT;
}
- | predicate not IN_SYM '(' subselect ')'
+ | predicate not IN_SYM subquery
{
- Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $5);
- if (unlikely(item == NULL))
+ Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $4);
+ if (unlikely(!item))
MYSQL_YYABORT;
$$= negate_expression(thd, item);
- if (unlikely($$ == NULL))
+ if (unlikely(!$$))
MYSQL_YYABORT;
}
| predicate IN_SYM '(' expr ')'
@@ -10213,7 +10600,21 @@ column_default_non_parenthesized_expr:
| param_marker { $$= $1; }
| variable
| sum_expr
+ {
+ if (!Lex->select_stack_top)
+ {
+ my_error(ER_INVALID_GROUP_FUNC_USE, MYF(0));
+ MYSQL_YYABORT;
+ }
+ }
| window_func_expr
+ {
+ if (!Lex->select_stack_top)
+ {
+ my_error(ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION, MYF(0));
+ MYSQL_YYABORT;
+ }
+ }
| inverse_distribution_function
| ROW_SYM '(' expr ',' expr_list ')'
{
@@ -10329,6 +10730,11 @@ primary_expr:
column_default_non_parenthesized_expr
| explicit_cursor_attr
| '(' parenthesized_expr ')' { $$= $2; }
+ | subquery
+ {
+ if (!($$= Lex->create_item_query_expression(thd, $1->master_unit())))
+ MYSQL_YYABORT;
+ }
;
string_factor_expr:
@@ -10392,7 +10798,7 @@ function_call_keyword_timestamp:
}
| TIMESTAMP '(' expr ',' expr ')'
{
- $$= new (thd->mem_root) Item_func_add_time(thd, $3, $5, 1, 0);
+ $$= new (thd->mem_root) Item_func_timestamp(thd, $3, $5);
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
@@ -11371,6 +11777,21 @@ window_func:
{
((Item_sum *) $1)->mark_as_window_func_sum_expr();
}
+ |
+ function_call_generic
+ {
+ Item* item = (Item*)$1;
+ /* Only UDF aggregate here possible */
+ if ((item == NULL) ||
+ (item->type() != Item::SUM_FUNC_ITEM)
+ || (((Item_sum *)item)->sum_func() != Item_sum::UDF_SUM_FUNC))
+ {
+ thd->parse_error();
+ MYSQL_YYABORT;
+ }
+
+ ((Item_sum *) $1)->mark_as_window_func_sum_expr();
+ }
;
simple_window_func:
@@ -11725,6 +12146,10 @@ cast_type_temporal:
DATE_SYM { $$.set(&type_handler_newdate); }
| TIME_SYM opt_field_length { $$.set(&type_handler_time2, 0, $2); }
| DATETIME opt_field_length { $$.set(&type_handler_datetime2, 0, $2); }
+ | INTERVAL_SYM DAY_SECOND_SYM field_length
+ {
+ $$.set(&type_handler_interval_DDhhmmssff, 0, $3);
+ }
;
opt_expr_list:
@@ -11735,9 +12160,7 @@ opt_expr_list:
expr_list:
expr
{
- $$= new (thd->mem_root) List<Item>;
- if (unlikely($$ == NULL) ||
- unlikely($$->push_back($1, thd->mem_root)))
+ if (unlikely(!($$= List<Item>::make(thd->mem_root, $1))))
MYSQL_YYABORT;
}
| expr_list ',' expr
@@ -11849,10 +12272,15 @@ esc_table_ref:
/* Equivalent to <table reference list> in the SQL:2003 standard. */
/* Warning - may return NULL in case of incomplete SELECT */
derived_table_list:
- esc_table_ref { $$=$1; }
+ esc_table_ref
+ {
+ $$=$1;
+ Select->add_joined_table($1);
+ }
| derived_table_list ',' esc_table_ref
{
MYSQL_YYABORT_UNLESS($1 && ($$=$3));
+ Select->add_joined_table($3);
}
;
@@ -11863,15 +12291,9 @@ derived_table_list:
*/
join_table:
/* INNER JOIN variants */
- /*
- Use %prec to evaluate production 'table_ref' before 'normal_join'
- so that [INNER | CROSS] JOIN is properly nested as other
- left-associative joins.
- */
table_ref normal_join table_ref %prec CONDITIONLESS_JOIN
{
MYSQL_YYABORT_UNLESS($1 && ($$=$3));
-
if (unlikely(Select->add_cross_joined_table($1, $3, $2)))
MYSQL_YYABORT;
}
@@ -11879,6 +12301,8 @@ join_table:
ON
{
MYSQL_YYABORT_UNLESS($1 && $3);
+ Select->add_joined_table($1);
+ Select->add_joined_table($3);
/* Change the current name resolution context to a local context. */
if (unlikely(push_new_name_resolution_context(thd, $1, $3)))
MYSQL_YYABORT;
@@ -11886,7 +12310,7 @@ join_table:
}
expr
{
- $3->straight=$2;
+ $3->straight=$2;
add_join_on(thd, $3, $6);
$3->on_context= Lex->pop_context();
Select->parsing_place= NO_MATTER;
@@ -11895,6 +12319,8 @@ join_table:
USING
{
MYSQL_YYABORT_UNLESS($1 && $3);
+ Select->add_joined_table($1);
+ Select->add_joined_table($3);
}
'(' using_list ')'
{
@@ -11905,6 +12331,8 @@ join_table:
| table_ref NATURAL inner_join table_factor
{
MYSQL_YYABORT_UNLESS($1 && ($$=$4));
+ Select->add_joined_table($1);
+ Select->add_joined_table($4);
$4->straight=$3;
add_join_natural($1,$4,NULL,Select);
}
@@ -11914,6 +12342,8 @@ join_table:
ON
{
MYSQL_YYABORT_UNLESS($1 && $5);
+ Select->add_joined_table($1);
+ Select->add_joined_table($5);
/* Change the current name resolution context to a local context. */
if (unlikely(push_new_name_resolution_context(thd, $1, $5)))
MYSQL_YYABORT;
@@ -11930,6 +12360,8 @@ join_table:
| table_ref LEFT opt_outer JOIN_SYM table_factor
{
MYSQL_YYABORT_UNLESS($1 && $5);
+ Select->add_joined_table($1);
+ Select->add_joined_table($5);
}
USING '(' using_list ')'
{
@@ -11940,6 +12372,8 @@ join_table:
| table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor
{
MYSQL_YYABORT_UNLESS($1 && $6);
+ Select->add_joined_table($1);
+ Select->add_joined_table($6);
add_join_natural($1,$6,NULL,Select);
$6->outer_join|=JOIN_TYPE_LEFT;
$$=$6;
@@ -11950,6 +12384,8 @@ join_table:
ON
{
MYSQL_YYABORT_UNLESS($1 && $5);
+ Select->add_joined_table($1);
+ Select->add_joined_table($5);
/* Change the current name resolution context to a local context. */
if (unlikely(push_new_name_resolution_context(thd, $1, $5)))
MYSQL_YYABORT;
@@ -11967,6 +12403,8 @@ join_table:
| table_ref RIGHT opt_outer JOIN_SYM table_factor
{
MYSQL_YYABORT_UNLESS($1 && $5);
+ Select->add_joined_table($1);
+ Select->add_joined_table($5);
}
USING '(' using_list ')'
{
@@ -11978,6 +12416,8 @@ join_table:
| table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor
{
MYSQL_YYABORT_UNLESS($1 && $6);
+ Select->add_joined_table($1);
+ Select->add_joined_table($6);
add_join_natural($6,$1,NULL,Select);
LEX *lex= Lex;
if (unlikely(!($$= lex->current_select->convert_right_join())))
@@ -12010,240 +12450,63 @@ use_partition:
PARTITION_SYM '(' using_list ')' have_partitioning
{
$$= $3;
+ Select->parsing_place= Select->save_parsing_place;
+ Select->save_parsing_place= NO_MATTER;
}
;
-
-/*
- This is a flattening of the rules <table factor> and <table primary>
- in the SQL:2003 standard, since we don't have <sample clause>
- I.e.
- <table factor> ::= <table primary> [ <sample clause> ]
-*/
-/* Warning - may return NULL in case of incomplete SELECT */
table_factor:
- table_primary_ident
- | table_primary_derived
- ;
-
-table_primary_ident:
- {
- DBUG_ASSERT(Select);
- SELECT_LEX *sel= Select;
- sel->table_join_options= 0;
- }
- table_ident opt_use_partition opt_for_system_time_clause opt_table_alias opt_key_definition
- {
- if (unlikely(!($$= Select->add_table_to_list(thd, $2, $5,
- Select->get_table_join_options(),
- YYPS->m_lock_type,
- YYPS->m_mdl_type,
- Select->
- pop_index_hints(),
- $3))))
- MYSQL_YYABORT;
- Select->add_joined_table($$);
- if ($4)
- $$->vers_conditions= Lex->vers_conditions;
+ table_primary_ident_opt_parens { $$= $1; }
+ | table_primary_derived_opt_parens { $$= $1; }
+ | join_table_parens
+ {
+ $1->nested_join->nest_type= 0;
+ $$= $1;
}
+ | table_reference_list_parens { $$= $1; }
;
+table_primary_ident_opt_parens:
+ table_primary_ident { $$= $1; }
+ | '(' table_primary_ident_opt_parens ')' { $$= $2; }
+ ;
-
-/*
- Represents a flattening of the following rules from the SQL:2003
- standard. This sub-rule corresponds to the sub-rule
- <table primary> ::= ... | <derived table> [ AS ] <correlation name>
-
- <derived table> ::= <table subquery>
- <table subquery> ::= <subquery>
- <subquery> ::= <left paren> <query expression> <right paren>
- <query expression> ::= [ <with clause> ] <query expression body>
-
- For the time being we use the non-standard rule
- select_derived_union which is a compromise between the standard
- and our parser. Possibly this rule could be replaced by our
- query_expression_body.
-*/
-
-table_primary_derived:
- '(' get_select_lex select_derived_union ')' opt_for_system_time_clause opt_table_alias
- {
- /* Use $2 instead of Lex->current_select as derived table will
- alter value of Lex->current_select. */
- if (!($3 || $6) && $2->embedding &&
- !$2->embedding->nested_join->join_list.elements)
- {
- /* we have a derived table ($3 == NULL) but no alias,
- Since we are nested in further parentheses so we
- can pass NULL to the outer level parentheses
- Permits parsing of "((((select ...))) as xyz)" */
- $$= 0;
- }
- else if (!$3)
- {
- /* Handle case of derived table, alias may be NULL if there
- are no outer parentheses, add_table_to_list() will throw
- error in this case */
- LEX *lex=Lex;
- lex->check_automatic_up(UNSPECIFIED_TYPE);
- SELECT_LEX *sel= lex->current_select;
- SELECT_LEX_UNIT *unit= sel->master_unit();
- lex->current_select= sel= unit->outer_select();
- Table_ident *ti= new (thd->mem_root) Table_ident(unit);
- if (unlikely(ti == NULL))
- MYSQL_YYABORT;
- if (unlikely(!($$= sel->add_table_to_list(thd,
- ti, $6, 0,
- TL_READ,
- MDL_SHARED_READ))))
- MYSQL_YYABORT;
- sel->add_joined_table($$);
- lex->pop_context();
- lex->nest_level--;
- }
- else if (unlikely($6 != NULL))
- {
- /*
- Tables with or without joins within parentheses cannot
- have aliases, and we ruled out derived tables above.
- */
- thd->parse_error();
- MYSQL_YYABORT;
- }
- else
- {
- /* nested join: FROM (t1 JOIN t2 ...),
- nest_level is the same as in the outer query */
- $$= $3;
- }
- /*
- Fields in derived table can be used in upper select in
- case of merge. We do not add HAVING fields because we do
- not merge such derived. We do not add union because
- also do not merge them
- */
- if ($$ && $$->derived &&
- !$$->derived->first_select()->next_select())
- $$->select_lex->add_where_field($$->derived->first_select());
- if ($5)
- {
- MYSQL_YYABORT_UNLESS(!$3);
- $$->vers_conditions= Lex->vers_conditions;
- }
- }
- /* Represents derived table with WITH clause */
- | '(' get_select_lex subselect_start
- with_clause query_expression_body
- subselect_end ')' opt_for_system_time_clause opt_table_alias
- {
- LEX *lex=Lex;
- SELECT_LEX *sel= $2;
- SELECT_LEX_UNIT *unit= $5->master_unit();
- Table_ident *ti= new (thd->mem_root) Table_ident(unit);
- if (unlikely(ti == NULL))
- MYSQL_YYABORT;
- $5->set_with_clause($4);
- lex->current_select= sel;
- if (unlikely(!($$= sel->add_table_to_list(lex->thd,
- ti, $9, 0,
- TL_READ,
- MDL_SHARED_READ))))
- MYSQL_YYABORT;
- sel->add_joined_table($$);
- if ($8)
- $$->vers_conditions= Lex->vers_conditions;
- }
+table_primary_derived_opt_parens:
+ table_primary_derived { $$= $1; }
+ | '(' table_primary_derived_opt_parens ')' { $$= $2; }
;
-/*
- This rule accepts just about anything. The reason is that we have
- empty-producing rules in the beginning of rules, in this case
- subselect_start. This forces bison to take a decision which rules to
- reduce by long before it has seen any tokens. This approach ties us
- to a very limited class of parseable languages, and unfortunately
- SQL is not one of them. The chosen 'solution' was this rule, which
- produces just about anything, even complete bogus statements, for
- instance ( table UNION SELECT 1 ).
- Fortunately, we know that the semantic value returned by
- select_derived is NULL if it contained a derived table, and a pointer to
- the base table's TABLE_LIST if it was a base table. So in the rule
- regarding union's, we throw a parse error manually and pretend it
- was bison that did it.
-
- Also worth noting is that this rule concerns query expressions in
- the from clause only. Top level select statements and other types of
- subqueries have their own union rules.
-*/
-select_derived_union:
- select_derived
- | select_derived union_order_or_limit
- {
- if (unlikely($1))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- }
- | select_derived union_head_non_top
+table_reference_list_parens:
+ '(' table_reference_list_parens ')' { $$= $2; }
+ | '(' nested_table_reference_list ')'
{
- if (unlikely($1))
- {
- thd->parse_error();
+ if (!($$= Select->end_nested_join(thd)))
MYSQL_YYABORT;
- }
- }
- union_list_derived_part2
- | derived_simple_table opt_select_lock_type
- | derived_simple_table order_or_limit opt_select_lock_type
- | derived_simple_table opt_select_lock_type union_list_derived
- ;
-
-union_list_derived_part2:
- query_term_union_not_ready { Lex->pop_context(); }
- | query_term_union_ready { Lex->pop_context(); }
- | query_term_union_ready { Lex->pop_context(); } union_list_derived
- ;
-
-union_list_derived:
- union_head_non_top union_list_derived_part2
- ;
-
-
-/* The equivalent of select_init2 for nested queries. */
-select_init2_derived:
- select_part2_derived
- {
- Select->set_braces(0);
}
;
-/* The equivalent of select_part2 for nested queries. */
-select_part2_derived:
+nested_table_reference_list:
+ table_ref ',' table_ref
{
- LEX *lex= Lex;
- SELECT_LEX *sel= lex->current_select;
- if (sel->linkage != UNION_TYPE)
- mysql_init_select(lex);
- lex->current_select->parsing_place= SELECT_LIST;
+ if (Select->init_nested_join(thd))
+ MYSQL_YYABORT;
+ Select->add_joined_table($1);
+ Select->add_joined_table($3);
+ $$= $1->embedding;
}
- opt_query_expression_options select_item_list
+ | nested_table_reference_list ',' table_ref
{
- Select->parsing_place= NO_MATTER;
+ Select->add_joined_table($3);
+ $$= $1;
}
;
-/* handle contents of parentheses in join expression */
-select_derived:
- get_select_lex_derived derived_table_list
+join_table_parens:
+ '(' join_table_parens ')' { $$= $2; }
+ | '(' join_table ')'
{
LEX *lex= Lex;
- /* for normal joins, $2 != NULL and end_nested_join() != NULL,
- for derived tables, both must equal NULL */
-
- if (unlikely(!($$= $1->end_nested_join(lex->thd)) && $2))
- MYSQL_YYABORT;
- if (unlikely(!$2 && $$))
+ if (!($$= lex->current_select->nest_last_join(thd)))
{
thd->parse_error();
MYSQL_YYABORT;
@@ -12251,85 +12514,33 @@ select_derived:
}
;
-derived_simple_table:
- derived_query_specification { $$= $1; }
- | derived_table_value_constructor { $$= $1; }
- ;
-/*
- Similar to query_specification, but for derived tables.
- Example: the inner parenthesized SELECT in this query:
- SELECT * FROM (SELECT * FROM t1);
-*/
-derived_query_specification:
- SELECT_SYM select_derived_init select_derived2
- {
- if ($2)
- Select->set_braces(1);
- $$= NULL;
- }
- ;
-derived_table_value_constructor:
- VALUES
- {
- if (Lex->tvc_start_derived())
- MYSQL_YYABORT;
- }
- values_list
+table_primary_ident:
+ table_ident opt_use_partition opt_for_system_time_clause
+ opt_table_alias_clause opt_key_definition
{
- if (Lex->tvc_finalize_derived())
+ SELECT_LEX *sel= Select;
+ sel->table_join_options= 0;
+ if (!($$= Select->add_table_to_list(thd, $1, $4,
+ Select->get_table_join_options(),
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type,
+ Select->pop_index_hints(),
+ $2)))
MYSQL_YYABORT;
- $$= NULL;
+ if ($3)
+ $$->vers_conditions= Lex->vers_conditions;
}
;
-
-select_derived2:
+table_primary_derived:
+ subquery
+ opt_for_system_time_clause table_alias_clause
{
- LEX *lex= Lex;
- lex->derived_tables|= DERIVED_SUBQUERY;
- if (unlikely(!lex->expr_allows_subselect ||
- lex->sql_command == (int)SQLCOM_PURGE))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE ||
- unlikely(mysql_new_select(lex, 1, NULL)))
+ if (!($$= Lex->parsed_derived_table($1->master_unit(), $2, $3)))
MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->current_select->linkage= DERIVED_TABLE_TYPE;
- lex->current_select->parsing_place= SELECT_LIST;
}
- select_options select_item_list
- {
- Select->parsing_place= NO_MATTER;
- }
- opt_table_expression
;
-
-get_select_lex:
- /* Empty */ { $$= Select; }
- ;
-
-get_select_lex_derived:
- get_select_lex
- {
- LEX *lex= Lex;
- if (unlikely($1->init_nested_join(lex->thd)))
- MYSQL_YYABORT;
- }
- ;
-
-select_derived_init:
- {
- LEX *lex= Lex;
-
- TABLE_LIST *embedding= lex->current_select->embedding;
- $$= embedding &&
- !embedding->nested_join->join_list.elements;
- /* return true if we are deeply nested */
- }
;
opt_outer:
@@ -12462,9 +12673,13 @@ table_alias:
| '='
;
-opt_table_alias:
+opt_table_alias_clause:
/* empty */ { $$=0; }
- | table_alias ident_table_alias
+ | table_alias_clause { $$= $1; }
+ ;
+
+table_alias_clause:
+ table_alias ident_table_alias
{
$$= (LEX_CSTRING*) thd->memdup(&$2,sizeof(LEX_STRING));
if (unlikely($$ == NULL))
@@ -12543,7 +12758,7 @@ olap_opt:
SQL-2003: GROUP BY ... CUBE(col1, col2, col3)
*/
LEX *lex=Lex;
- if (unlikely(lex->current_select->linkage == GLOBAL_OPTIONS_TYPE))
+ if (unlikely(lex->current_select->get_linkage() == GLOBAL_OPTIONS_TYPE))
my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH CUBE",
"global union parameters"));
lex->current_select->olap= CUBE_TYPE;
@@ -12560,7 +12775,7 @@ olap_opt:
SQL-2003: GROUP BY ... ROLLUP(col1, col2, col3)
*/
LEX *lex= Lex;
- if (unlikely(lex->current_select->linkage == GLOBAL_OPTIONS_TYPE))
+ if (unlikely(lex->current_select->get_linkage() == GLOBAL_OPTIONS_TYPE))
my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH ROLLUP",
"global union parameters"));
lex->current_select->olap= ROLLUP_TYPE;
@@ -12602,6 +12817,7 @@ window_spec:
opt_window_ref opt_window_partition_clause
opt_window_order_clause opt_window_frame_clause
')'
+ { }
;
opt_window_ref:
@@ -12621,7 +12837,7 @@ opt_window_partition_clause:
opt_window_order_clause:
/* empty */ { }
- | ORDER_SYM BY order_list
+ | ORDER_SYM BY order_list { Select->order_list= *($3); }
;
opt_window_frame_clause:
@@ -12747,70 +12963,35 @@ alter_order_item:
opt_order_clause:
/* empty */
+ { $$= NULL; }
| order_clause
+ { $$= $1; }
;
order_clause:
ORDER_SYM BY
{
- LEX *lex=Lex;
- SELECT_LEX *sel= lex->current_select;
- SELECT_LEX_UNIT *unit= sel-> master_unit();
- if (unlikely(sel->linkage != GLOBAL_OPTIONS_TYPE &&
- sel->olap != UNSPECIFIED_OLAP_TYPE &&
- (sel->linkage != UNION_TYPE || sel->braces)))
- {
- my_error(ER_WRONG_USAGE, MYF(0),
- "CUBE/ROLLUP", "ORDER BY");
- MYSQL_YYABORT;
- }
- if (lex->sql_command != SQLCOM_ALTER_TABLE &&
- !unit->fake_select_lex)
- {
- /*
- A query of the of the form (SELECT ...) ORDER BY order_list is
- executed in the same way as the query
- SELECT ... ORDER BY order_list
- unless the SELECT construct contains ORDER BY or LIMIT clauses.
- Otherwise we create a fake SELECT_LEX if it has not been
- created yet.
- */
- SELECT_LEX *first_sl= unit->first_select();
- if (unlikely(!unit->is_unit_op() &&
- (first_sl->order_list.elements ||
- first_sl->select_limit) &&
- unit->add_fake_select_lex(thd)))
- MYSQL_YYABORT;
- }
- if (sel->master_unit()->is_unit_op() && !sel->braces)
- {
- /*
- At this point we don't know yet whether this is the last
- select in union or not, but we move ORDER BY to
- fake_select_lex anyway. If there would be one more select
- in union mysql_new_select will correctly throw error.
- */
- DBUG_ASSERT(sel->master_unit()->fake_select_lex);
- lex->current_select= sel->master_unit()->fake_select_lex;
- }
+ thd->where= "ORDER clause";
}
order_list
{
-
+ $$= $4;
}
;
order_list:
order_list ',' order_ident order_dir
{
- if (unlikely(add_order_to_list(thd, $3,(bool) $4)))
- MYSQL_YYABORT;
- }
+ $$= $1;
+ if (add_to_list(thd, *$$, $3,(bool) $4))
+ MYSQL_YYABORT;
+ }
| order_ident order_dir
{
- if (unlikely(add_order_to_list(thd, $1,(bool) $2)))
+ $$= new (thd->mem_root) SQL_I_List<ORDER>();
+ if (add_to_list(thd, *$$, $1, (bool) $2))
MYSQL_YYABORT;
- }
+ }
;
order_dir:
@@ -12820,63 +13001,62 @@ order_dir:
;
opt_limit_clause:
- /* empty */ {}
- | limit_clause {}
+ /* empty */
+ { $$.empty(); }
+ | limit_clause
+ { $$= $1; }
;
-limit_clause_init:
- LIMIT
- {
- SELECT_LEX *sel= Select;
- if (sel->master_unit()->is_unit_op() && !sel->braces)
- {
- /* Move LIMIT that belongs to UNION to fake_select_lex */
- Lex->current_select= sel->master_unit()->fake_select_lex;
- DBUG_ASSERT(Select);
- }
- }
- ;
-
limit_clause:
- limit_clause_init limit_options
+ LIMIT limit_options
{
- SELECT_LEX *sel= Select;
- if (!sel->select_limit->basic_const_item() ||
- sel->select_limit->val_int() > 0)
+ $$= $2;
+ if (!$$.select_limit->basic_const_item() ||
+ $$.select_limit->val_int() > 0)
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
}
- | limit_clause_init limit_options
+ | LIMIT limit_options
ROWS_SYM EXAMINED_SYM limit_rows_option
{
+ $$= $2;
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
}
- | limit_clause_init ROWS_SYM EXAMINED_SYM limit_rows_option
+ | LIMIT ROWS_SYM EXAMINED_SYM limit_rows_option
{
+ $$.select_limit= 0;
+ $$.offset_limit= 0;
+ $$.explicit_limit= 1;
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
}
;
+opt_global_limit_clause:
+ opt_limit_clause
+ {
+ Select->explicit_limit= $1.explicit_limit;
+ Select->select_limit= $1.select_limit;
+ Select->offset_limit= $1.offset_limit;
+ }
+ ;
+
limit_options:
limit_option
{
- SELECT_LEX *sel= Select;
- sel->select_limit= $1;
- sel->offset_limit= 0;
- sel->explicit_limit= 1;
+ $$.select_limit= $1;
+ $$.offset_limit= 0;
+ $$.explicit_limit= 1;
}
| limit_option ',' limit_option
{
- SELECT_LEX *sel= Select;
- sel->select_limit= $3;
- sel->offset_limit= $1;
- sel->explicit_limit= 1;
+ $$.select_limit= $3;
+ $$.offset_limit= $1;
+ $$.explicit_limit= 1;
}
| limit_option OFFSET_SYM limit_option
{
- SELECT_LEX *sel= Select;
- sel->select_limit= $1;
- sel->offset_limit= $3;
- sel->explicit_limit= 1;
+ $$.select_limit= $1;
+ $$.offset_limit= $3;
+ $$.explicit_limit= 1;
}
;
@@ -12940,6 +13120,88 @@ delete_limit_clause:
| LIMIT limit_option ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; }
;
+order_limit_lock:
+ order_or_limit
+ {
+ $$= $1;
+ $$->lock.empty();
+ }
+ | order_or_limit select_lock_type
+ {
+ $$= $1;
+ $$->lock= $2;
+ }
+ | select_lock_type
+ {
+ $$= new(thd->mem_root) Lex_order_limit_lock;
+ if (!$$)
+ YYABORT;
+ $$->order_list= NULL;
+ $$->limit.empty();
+ $$->lock= $1;
+ }
+ ;
+opt_order_limit_lock:
+ /* empty */
+ {
+ Lex->pop_select();
+ $$= NULL;
+ }
+ | order_limit_lock { $$= $1; }
+ ;
+
+query_expression_tail:
+ order_limit_lock
+ ;
+
+opt_query_expression_tail:
+ opt_order_limit_lock
+ ;
+
+opt_procedure_or_into:
+ /* empty */
+ {
+ $$.empty();
+ }
+ | procedure_clause opt_select_lock_type
+ {
+ $$= $2;
+ }
+ | into opt_select_lock_type
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WARN_DEPRECATED_SYNTAX,
+ ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX),
+ "<select expression> INTO <destination>;",
+ "'SELECT <select list> INTO <destination>"
+ " FROM...'");
+ $$= $2;
+ }
+ ;
+
+
+order_or_limit:
+ order_clause opt_limit_clause
+ {
+ $$= new(thd->mem_root) Lex_order_limit_lock;
+ if (!$$)
+ YYABORT;
+ $$->order_list= $1;
+ $$->limit= $2;
+ }
+ | limit_clause
+ {
+ Lex_order_limit_lock *op= $$= new(thd->mem_root) Lex_order_limit_lock;
+ if (!$$)
+ YYABORT;
+ op->order_list= NULL;
+ op->limit= $1;
+ $$->order_list= NULL;
+ $$->limit= $1;
+ }
+ ;
+
+
opt_plus:
/* empty */
| '+'
@@ -13016,8 +13278,6 @@ procedure_clause:
{
LEX *lex=Lex;
- DBUG_ASSERT(&lex->select_lex == lex->current_select);
-
lex->proc_list.elements=0;
lex->proc_list.first=0;
lex->proc_list.next= &lex->proc_list.first;
@@ -13032,16 +13292,16 @@ procedure_clause:
/*
PROCEDURE CLAUSE cannot handle subquery as one of its parameter,
- so set expr_allows_subselect as false to disallow any subqueries
- further. Reset expr_allows_subselect back to true once the
- parameters are reduced.
+ so disallow any subqueries further.
+ Alow subqueries back once the parameters are reduced.
*/
- Lex->expr_allows_subselect= false;
+ Lex->clause_that_disallows_subselect= "PROCEDURE";
+ Select->options|= OPTION_PROCEDURE_CLAUSE;
}
'(' procedure_list ')'
{
/* Subqueries are allowed from now.*/
- Lex->expr_allows_subselect= true;
+ Lex->clause_that_disallows_subselect= NULL;
}
;
@@ -13120,6 +13380,7 @@ select_outvar:
into:
INTO into_destination
+ {}
;
into_destination:
@@ -13167,11 +13428,14 @@ do:
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_DO;
+ if (lex->main_select_push(true))
+ MYSQL_YYABORT;
mysql_init_select(lex);
}
expr_list
{
Lex->insert_list= $3;
+ Lex->pop_select(); //main select
}
;
@@ -13189,11 +13453,16 @@ drop:
}
table_list opt_lock_wait_timeout opt_restrict
{}
- | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident opt_lock_wait_timeout
+ | DROP INDEX_SYM
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ opt_if_exists_table_element ident ON table_ident opt_lock_wait_timeout
{
LEX *lex=Lex;
Alter_drop *ad= (new (thd->mem_root)
- Alter_drop(Alter_drop::KEY, $4.str, $3));
+ Alter_drop(Alter_drop::KEY, $5.str, $4));
if (unlikely(ad == NULL))
MYSQL_YYABORT;
lex->sql_command= SQLCOM_DROP_INDEX;
@@ -13201,10 +13470,11 @@ drop:
lex->alter_info.flags= ALTER_DROP_INDEX;
lex->alter_info.drop_list.push_back(ad, thd->mem_root);
if (unlikely(!lex->current_select->
- add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING,
+ add_table_to_list(thd, $7, NULL, TL_OPTION_UPDATING,
TL_READ_NO_INSERT,
MDL_SHARED_UPGRADABLE)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
}
| DROP DATABASE opt_if_exists ident
{
@@ -13329,10 +13599,11 @@ table_list:
table_name:
table_ident
{
- if (unlikely(!Select->add_table_to_list(thd, $1, NULL,
- TL_OPTION_UPDATING,
- YYPS->m_lock_type,
- YYPS->m_mdl_type)))
+ if (!thd->lex->current_select_or_default()->
+ add_table_to_list(thd, $1, NULL,
+ TL_OPTION_UPDATING,
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type))
MYSQL_YYABORT;
}
;
@@ -13405,17 +13676,23 @@ insert:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_INSERT;
- lex->duplicates= DUP_ERROR;
+ lex->duplicates= DUP_ERROR;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
mysql_init_select(lex);
+ lex->current_select->parsing_place= BEFORE_OPT_LIST;
}
insert_lock_option
opt_ignore insert2
{
Select->set_lock_for_tables($3, true);
- Lex->current_select= &Lex->select_lex;
+ Lex->current_select= Lex->first_select_lex();
}
insert_field_spec opt_insert_update
{
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
Lex->mark_first_table_as_inserting();
}
;
@@ -13426,15 +13703,21 @@ replace:
LEX *lex=Lex;
lex->sql_command = SQLCOM_REPLACE;
lex->duplicates= DUP_REPLACE;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
mysql_init_select(lex);
+ lex->current_select->parsing_place= BEFORE_OPT_LIST;
}
replace_lock_option insert2
{
Select->set_lock_for_tables($3, true);
- Lex->current_select= &Lex->select_lex;
+ Lex->current_select= Lex->first_select_lex();
}
insert_field_spec
{
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
Lex->mark_first_table_as_inserting();
}
;
@@ -13476,10 +13759,13 @@ insert2:
;
insert_table:
+ {
+ Select->save_parsing_place= Select->parsing_place;
+ }
table_name_with_opt_use_partition
{
LEX *lex=Lex;
- lex->field_list.empty();
+ //lex->field_list.empty();
lex->many_values.empty();
lex->insert_list=0;
}
@@ -13487,8 +13773,7 @@ insert_table:
insert_field_spec:
insert_values {}
- | '(' ')' insert_values {}
- | '(' fields ')' insert_values {}
+ | insert_field_list insert_values {}
| SET
{
LEX *lex=Lex;
@@ -13496,20 +13781,33 @@ insert_field_spec:
unlikely(lex->many_values.push_back(lex->insert_list,
thd->mem_root)))
MYSQL_YYABORT;
+ lex->current_select->parsing_place= NO_MATTER;
}
ident_eq_list
;
+insert_field_list:
+ LEFT_PAREN_ALT opt_fields ')'
+ {
+ Lex->current_select->parsing_place= AFTER_LIST;
+ }
+ ;
+
+opt_fields:
+ /* empty */
+ | fields
+ ;
+
fields:
fields ',' insert_ident
{ Lex->field_list.push_back($3, thd->mem_root); }
| insert_ident { Lex->field_list.push_back($1, thd->mem_root); }
;
+
+
insert_values:
- VALUES values_list {}
- | VALUE_SYM values_list {}
- | create_select_query_expression {}
+ create_select_query_expression {}
;
values_list:
@@ -13653,23 +13951,43 @@ opt_insert_update:
}
;
+update_table_list:
+ table_ident opt_use_partition for_portion_of_time_clause
+ opt_table_alias_clause opt_key_definition
+ {
+ SELECT_LEX *sel= Select;
+ sel->table_join_options= 0;
+ if (!($$= Select->add_table_to_list(thd, $1, $4,
+ Select->get_table_join_options(),
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type,
+ Select->pop_index_hints(),
+ $2)))
+ MYSQL_YYABORT;
+ $$->period_conditions= Lex->period_conditions;
+ }
+ | join_table_list { $$= $1; }
+ ;
+
/* Update rows in a table */
update:
UPDATE_SYM
{
LEX *lex= Lex;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
mysql_init_select(lex);
lex->sql_command= SQLCOM_UPDATE;
lex->duplicates= DUP_ERROR;
}
- opt_low_priority opt_ignore join_table_list
+ opt_low_priority opt_ignore update_table_list
SET update_list
{
- SELECT_LEX *slex= &Lex->select_lex;
+ SELECT_LEX *slex= Lex->first_select_lex();
if (slex->table_list.elements > 1)
Lex->sql_command= SQLCOM_UPDATE_MULTI;
- else if (unlikely(slex->get_table_list()->derived))
+ else if (slex->get_table_list()->derived)
{
/* it is single table update and it is update of derived table */
my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
@@ -13683,7 +14001,14 @@ update:
*/
slex->set_lock_for_tables($3, slex->table_list.elements == 1);
}
- opt_where_clause opt_order_clause delete_limit_clause {}
+ opt_where_clause opt_order_clause delete_limit_clause
+ {
+ if ($10)
+ Select->order_list= *($10);
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
+ }
;
update_list:
@@ -13727,12 +14052,13 @@ delete:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_DELETE;
- mysql_init_select(lex);
YYPS->m_lock_type= TL_WRITE_DEFAULT;
YYPS->m_mdl_type= MDL_SHARED_WRITE;
-
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ mysql_init_select(lex);
lex->ignore= 0;
- lex->select_lex.init_order();
+ lex->first_select_lex()->order_list.empty();
}
delete_part2
;
@@ -13753,6 +14079,7 @@ delete_part2:
| HISTORY_SYM delete_single_table opt_delete_system_time
{
Lex->last_table()->vers_conditions= Lex->vers_conditions;
+ Lex->pop_select(); //main select
}
;
@@ -13771,12 +14098,25 @@ delete_single_table:
}
;
+delete_single_table_for_period:
+ delete_single_table opt_for_portion_of_time_clause
+ {
+ if ($2)
+ Lex->last_table()->period_conditions= Lex->period_conditions;
+ }
+ ;
+
single_multi:
- delete_single_table
+ delete_single_table_for_period
opt_where_clause
opt_order_clause
delete_limit_clause
- opt_select_expressions {}
+ opt_select_expressions
+ {
+ if ($3)
+ Select->order_list= *($3);
+ Lex->pop_select(); //main select
+ }
| table_wild_list
{
mysql_init_multi_delete(Lex);
@@ -13787,6 +14127,9 @@ single_multi:
{
if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
}
| FROM table_alias_ref_list
{
@@ -13798,6 +14141,9 @@ single_multi:
{
if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex)))
MYSQL_YYABORT;
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
}
;
@@ -13866,9 +14212,9 @@ truncate:
LEX* lex= Lex;
lex->sql_command= SQLCOM_TRUNCATE;
lex->alter_info.reset();
- lex->select_lex.options= 0;
- lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
- lex->select_lex.init_order();
+ lex->first_select_lex()->options= 0;
+ lex->sql_cache= LEX::SQL_CACHE_UNSPECIFIED;
+ lex->first_select_lex()->order_list.empty();
YYPS->m_lock_type= TL_WRITE;
YYPS->m_mdl_type= MDL_EXCLUSIVE;
}
@@ -13960,6 +14306,8 @@ show:
LEX *lex=Lex;
lex->wild=0;
lex->ident= null_clex_str;
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
lex->create_info.init();
@@ -13967,6 +14315,7 @@ show:
show_param
{
Select->parsing_place= NO_MATTER;
+ Lex->pop_select(); //main select
}
;
@@ -13982,40 +14331,40 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TABLES;
- lex->select_lex.db= $3;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES)))
+ lex->first_select_lex()->db= $3;
+ if (prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES))
MYSQL_YYABORT;
}
| opt_full TRIGGERS_SYM opt_db wild_and_where
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TRIGGERS;
- lex->select_lex.db= $3;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TRIGGERS)))
+ lex->first_select_lex()->db= $3;
+ if (prepare_schema_table(thd, lex, 0, SCH_TRIGGERS))
MYSQL_YYABORT;
}
| EVENTS_SYM opt_db wild_and_where
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_EVENTS;
- lex->select_lex.db= $2;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_EVENTS)))
+ lex->first_select_lex()->db= $2;
+ if (prepare_schema_table(thd, lex, 0, SCH_EVENTS))
MYSQL_YYABORT;
}
| TABLE_SYM STATUS_SYM opt_db wild_and_where
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TABLE_STATUS;
- lex->select_lex.db= $3;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_TABLES)))
+ lex->first_select_lex()->db= $3;
+ if (prepare_schema_table(thd, lex, 0, SCH_TABLES))
MYSQL_YYABORT;
}
| OPEN_SYM TABLES opt_db wild_and_where
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_OPEN_TABLES;
- lex->select_lex.db= $3;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES)))
+ lex->first_select_lex()->db= $3;
+ if (prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES))
MYSQL_YYABORT;
}
| PLUGINS_SYM
@@ -14064,12 +14413,13 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS;
}
- opt_limit_clause
+ opt_global_limit_clause
| RELAYLOG_SYM optional_connection_name EVENTS_SYM binlog_in binlog_from
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_RELAYLOG_EVENTS;
- } opt_limit_clause
+ }
+ opt_global_limit_clause
| keys_or_index from_or_in table_ident opt_db opt_where_clause
{
LEX *lex= Lex;
@@ -14111,13 +14461,13 @@ show_param:
LEX_CSTRING var= {STRING_WITH_LEN("error_count")};
(void) create_select_for_variable(thd, &var);
}
- | WARNINGS opt_limit_clause
+ | WARNINGS opt_global_limit_clause
{ Lex->sql_command = SQLCOM_SHOW_WARNS;}
- | ERRORS opt_limit_clause
+ | ERRORS opt_global_limit_clause
{ Lex->sql_command = SQLCOM_SHOW_ERRORS;}
| PROFILES_SYM
{ Lex->sql_command = SQLCOM_SHOW_PROFILES; }
- | PROFILE_SYM opt_profile_defs opt_profile_args opt_limit_clause
+ | PROFILE_SYM opt_profile_defs opt_profile_args opt_global_limit_clause
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_PROFILE;
@@ -14179,7 +14529,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE;
- if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL,0)))
+ if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL,0))
MYSQL_YYABORT;
lex->create_info.storage_media= HA_SM_DEFAULT;
}
@@ -14187,7 +14537,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE;
- if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)))
+ if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL, 0))
MYSQL_YYABORT;
lex->table_type= TABLE_TYPE_VIEW;
}
@@ -14195,7 +14545,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE;
- if (unlikely(!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)))
+ if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL, 0))
MYSQL_YYABORT;
lex->table_type= TABLE_TYPE_SEQUENCE;
}
@@ -14409,10 +14759,12 @@ describe:
describe_command table_ident
{
LEX *lex= Lex;
+ if (lex->main_select_push())
+ MYSQL_YYABORT;
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
lex->sql_command= SQLCOM_SHOW_FIELDS;
- lex->select_lex.db= null_clex_str;
+ lex->first_select_lex()->db= null_clex_str;
lex->verbose= 0;
if (unlikely(prepare_schema_table(thd, lex, $2, SCH_COLUMNS)))
MYSQL_YYABORT;
@@ -14420,18 +14772,20 @@ describe:
opt_describe_column
{
Select->parsing_place= NO_MATTER;
+ Lex->pop_select(); //main select
}
| describe_command opt_extended_describe
{ Lex->describe|= DESCRIBE_NORMAL; }
explainable_command
{
LEX *lex=Lex;
- lex->select_lex.options|= SELECT_DESCRIBE;
+ lex->first_select_lex()->options|= SELECT_DESCRIBE;
}
;
explainable_command:
select
+ | select_into
| insert
| replace
| update
@@ -14452,6 +14806,8 @@ analyze_stmt_command:
opt_extended_describe:
EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; }
+ | EXTENDED_SYM ALL
+ { Lex->describe|= DESCRIBE_EXTENDED | DESCRIBE_EXTENDED2; }
| PARTITIONS_SYM { Lex->describe|= DESCRIBE_PARTITIONS; }
| opt_format_json {}
;
@@ -14494,8 +14850,7 @@ flush:
lex->type= 0;
lex->no_write_to_binlog= $2;
}
- flush_options
- {}
+ flush_options {}
;
flush_options:
@@ -14512,6 +14867,7 @@ flush_options:
opt_table_list opt_flush_lock
{}
| flush_options_list
+ {}
;
opt_flush_lock:
@@ -14597,6 +14953,8 @@ flush_option:
{ Lex->type|= REFRESH_DES_KEY_FILE; }
| RESOURCES
{ Lex->type|= REFRESH_USER_RESOURCES; }
+ | SSL_SYM
+ { Lex->type|= REFRESH_SSL;}
| IDENT_sys remember_tok_start
{
Lex->type|= REFRESH_GENERIC;
@@ -14618,6 +14976,43 @@ opt_table_list:
| table_list {}
;
+backup:
+ BACKUP_SYM backup_statements {}
+ ;
+
+backup_statements:
+ STAGE_SYM ident
+ {
+ int type;
+ if (unlikely(Lex->sphead))
+ my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "BACKUP STAGE"));
+ if ((type= find_type($2.str, &backup_stage_names,
+ FIND_TYPE_NO_PREFIX)) <= 0)
+ my_yyabort_error((ER_BACKUP_UNKNOWN_STAGE, MYF(0), $2.str));
+ Lex->sql_command= SQLCOM_BACKUP;
+ Lex->backup_stage= (backup_stages) (type-1);
+ break;
+ }
+ | LOCK_SYM
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ table_ident
+ {
+ if (unlikely(!Select->add_table_to_list(thd, $3, NULL, 0,
+ TL_READ, MDL_SHARED_HIGH_PRIO)))
+ MYSQL_YYABORT;
+ Lex->sql_command= SQLCOM_BACKUP_LOCK;
+ Lex->pop_select(); //main select
+ }
+ | UNLOCK_SYM
+ {
+ /* Table list is empty for unlock */
+ Lex->sql_command= SQLCOM_BACKUP_LOCK;
+ }
+ ;
+
opt_delete_gtid_domain:
/* empty */ {}
| DELETE_DOMAIN_ID_SYM '=' '(' delete_domain_id_list ')'
@@ -14692,34 +15087,21 @@ master_reset_options:
;
purge:
- PURGE
- {
- LEX *lex=Lex;
- lex->type=0;
- lex->sql_command = SQLCOM_PURGE;
- }
- purge_options
- {}
- ;
-
-purge_options:
- master_or_binary LOGS_SYM purge_option
- ;
-
-purge_option:
- TO_SYM TEXT_STRING_sys
+ PURGE master_or_binary LOGS_SYM TO_SYM TEXT_STRING_sys
{
- Lex->to_log = $2.str;
+ Lex->stmt_purge_to($5);
}
- | BEFORE_SYM expr
+ | PURGE master_or_binary LOGS_SYM BEFORE_SYM
+ { Lex->clause_that_disallows_subselect= "PURGE..BEFORE"; }
+ expr
{
- LEX *lex= Lex;
- lex->value_list.empty();
- lex->value_list.push_front($2, thd->mem_root);
- lex->sql_command= SQLCOM_PURGE_BEFORE;
+ Lex->clause_that_disallows_subselect= NULL;
+ if (Lex->stmt_purge_before($6))
+ MYSQL_YYABORT;
}
;
+
/* kill threads */
kill:
@@ -14773,8 +15155,16 @@ kill_expr:
shutdown:
SHUTDOWN { Lex->sql_command= SQLCOM_SHUTDOWN; }
+ shutdown_option {}
;
+shutdown_option:
+ /* Empty */ { Lex->is_shutdown_wait_for_slaves= false; }
+ | WAIT_SYM FOR_SYM ALL SLAVES
+ {
+ Lex->is_shutdown_wait_for_slaves= true;
+ }
+ ;
/* change database */
use:
@@ -14782,7 +15172,7 @@ use:
{
LEX *lex=Lex;
lex->sql_command=SQLCOM_CHANGE_DB;
- lex->select_lex.db= $2;
+ lex->first_select_lex()->db= $2;
}
;
@@ -14799,6 +15189,9 @@ load:
$2 == FILETYPE_CSV ? "LOAD DATA" : "LOAD XML");
MYSQL_YYABORT;
}
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ mysql_init_select(lex);
}
load_data_lock opt_local INFILE TEXT_STRING_filesystem
{
@@ -14830,6 +15223,9 @@ load:
opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec
opt_load_data_set_spec
{
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
Lex->mark_first_table_as_inserting();
}
;
@@ -15030,11 +15426,6 @@ hex_or_bin_String:
$1.length);
if (unlikely(tmp == NULL))
MYSQL_YYABORT;
- /*
- it is OK only emulate fix_fields, because we need only
- value of constant
- */
- tmp->quick_fix_field();
$$= tmp->val_str((String*) 0);
}
| HEX_STRING
@@ -15043,7 +15434,6 @@ hex_or_bin_String:
$1.length);
if (unlikely(tmp == NULL))
MYSQL_YYABORT;
- tmp->quick_fix_field();
$$= tmp->val_str((String*) 0);
}
| BIN_NUM
@@ -15056,7 +15446,6 @@ hex_or_bin_String:
it is OK only emulate fix_fields, because we need only
value of constant
*/
- tmp->quick_fix_field();
$$= tmp->val_str((String*) 0);
}
;
@@ -15205,52 +15594,43 @@ NUM_literal:
temporal_literal:
DATE_SYM TEXT_STRING
{
- if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
- YYCSCL,
- MYSQL_TYPE_DATE,
- true))))
+ if (unlikely(!($$= type_handler_newdate.create_literal_item(thd,
+ $2.str, $2.length,
+ YYCSCL, true))))
MYSQL_YYABORT;
}
| TIME_SYM TEXT_STRING
{
- if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
- YYCSCL,
- MYSQL_TYPE_TIME,
- true))))
+ if (unlikely(!($$= type_handler_time2.create_literal_item(thd,
+ $2.str, $2.length,
+ YYCSCL, true))))
MYSQL_YYABORT;
}
| TIMESTAMP TEXT_STRING
{
- if (unlikely(!($$= create_temporal_literal(thd, $2.str, $2.length,
- YYCSCL,
- MYSQL_TYPE_DATETIME,
- true))))
+ if (unlikely(!($$= type_handler_datetime2.create_literal_item(thd,
+ $2.str, $2.length,
+ YYCSCL, true))))
MYSQL_YYABORT;
}
;
-
-opt_with_clause:
- /*empty */ { $$= 0; }
- | with_clause
- {
- $$= $1;
- }
- ;
-
-
with_clause:
- WITH opt_recursive
+ WITH opt_recursive
{
+ LEX *lex= Lex;
With_clause *with_clause=
new With_clause($2, Lex->curr_with_clause);
if (unlikely(with_clause == NULL))
MYSQL_YYABORT;
- Lex->derived_tables|= DERIVED_WITH;
- Lex->curr_with_clause= with_clause;
+ lex->derived_tables|= DERIVED_WITH;
+ lex->curr_with_clause= with_clause;
with_clause->add_to_list(Lex->with_clauses_list_last_next);
+ if (lex->current_select &&
+ lex->current_select->parsing_place == BEFORE_OPT_LIST)
+ lex->current_select->parsing_place= NO_MATTER;
}
- with_list
+ with_list
{
$$= Lex->curr_with_clause;
Lex->curr_with_clause= Lex->curr_with_clause->pop();
@@ -15279,17 +15659,16 @@ with_list_element:
MYSQL_YYABORT;
Lex->with_column_list.empty();
}
- AS '(' remember_tok_start subselect remember_tok_end ')'
+ AS '(' query_expression ')'
{
LEX *lex= thd->lex;
const char *query_start= lex->sphead ? lex->sphead->m_tmp_query
: thd->query();
- char *spec_start= $6 + 1;
- With_element *elem= new With_element($1, *$2, $7->master_unit());
- if (unlikely(elem == NULL) ||
- unlikely(Lex->curr_with_clause->add_with_element(elem)))
+ const char *spec_start= $5.pos() + 1;
+ With_element *elem= new With_element($1, *$2, $6);
+ if (elem == NULL || Lex->curr_with_clause->add_with_element(elem))
MYSQL_YYABORT;
- if (elem->set_unparsed_spec(thd, spec_start, $8,
+ if (elem->set_unparsed_spec(thd, spec_start, $7.pos(),
spec_start - query_start))
MYSQL_YYABORT;
}
@@ -15625,11 +16004,9 @@ ident_or_text:
user_maybe_role:
ident_or_text
{
- if (unlikely(!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))))
+ if (unlikely(!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
$$->user = $1;
- $$->host= null_clex_str; // User or Role, see get_current_user()
- $$->reset_auth();
if (unlikely(check_string_char_length(&$$->user, ER_USERNAME,
username_char_length,
@@ -15638,10 +16015,9 @@ user_maybe_role:
}
| ident_or_text '@' ident_or_text
{
- if (unlikely(!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))))
+ if (unlikely(!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
$$->user = $1; $$->host=$3;
- $$->reset_auth();
if (unlikely(check_string_char_length(&$$->user, ER_USERNAME,
username_char_length,
@@ -15671,8 +16047,7 @@ user_maybe_role:
if (unlikely(!($$=(LEX_USER*)thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
$$->user= current_user;
- $$->plugin= empty_clex_str;
- $$->auth= empty_clex_str;
+ $$->auth= new (thd->mem_root) USER_AUTH();
}
;
@@ -15972,6 +16347,7 @@ keyword_data_type:
*/
keyword_sp_var_and_label:
ACTION
+ | ACCOUNT_SYM
| ADDDATE_SYM
| ADMIN_SYM
| AFTER_SYM
@@ -16055,6 +16431,7 @@ keyword_sp_var_and_label:
| EXCEPTION_MARIADB_SYM
| EXCHANGE_SYM
| EXPANSION_SYM
+ | EXPIRE_SYM
| EXPORT_SYM
| EXTENDED_SYM
| EXTENT_SIZE_SYM
@@ -16147,6 +16524,7 @@ keyword_sp_var_and_label:
| NAME_SYM
| NEXT_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2
| NEXTVAL_SYM
+ | NEVER_SYM
| NEW_SYM
| NOCACHE_SYM
| NOCYCLE_SYM
@@ -16233,6 +16611,7 @@ keyword_sp_var_and_label:
| SQL_BUFFER_RESULT
| SQL_NO_CACHE_SYM
| SQL_THREAD
+ | STAGE_SYM
| STARTS_SYM
| STATEMENT_SYM
| STATUS_SYM
@@ -16300,14 +16679,23 @@ set:
SET
{
LEX *lex=Lex;
+ if (lex->main_select_push(true))
+ MYSQL_YYABORT;
lex->set_stmt_init();
lex->var_list.empty();
- sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
}
start_option_value_list
- {}
+ {
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
+ }
| SET STATEMENT_SYM
{
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
Lex->set_stmt_init();
}
set_stmt_option_value_following_option_type_list
@@ -16317,6 +16705,9 @@ set:
my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), "SET STATEMENT"));
lex->stmt_var_list= lex->var_list;
lex->var_list.empty();
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
}
FOR_SYM verb_clause
{}
@@ -16328,7 +16719,8 @@ set_assign:
LEX *lex=Lex;
lex->set_stmt_init();
lex->var_list.empty();
- sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ if(sp_create_assignment_lex(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
}
set_expr_or_default
{
@@ -16341,7 +16733,8 @@ set_assign:
LEX *lex=Lex;
lex->set_stmt_init();
lex->var_list.empty();
- sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
}
set_expr_or_default
{
@@ -16361,7 +16754,8 @@ set_assign:
}
lex->set_stmt_init();
lex->var_list.empty();
- sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
}
set_expr_or_default
{
@@ -16431,7 +16825,8 @@ option_value_list_continued:
/* Repeating list of option values after first option value. */
option_value_list:
{
- sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
}
option_value
{
@@ -16440,7 +16835,8 @@ option_value_list:
}
| option_value_list ','
{
- sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
}
option_value
{
@@ -16706,21 +17102,29 @@ opt_for_user:
thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
lex->definer->user= current_user;
- lex->definer->plugin= empty_clex_str;
- lex->definer->auth= empty_clex_str;
+ lex->definer->auth= new (thd->mem_root) USER_AUTH();
}
| FOR_SYM user equal { Lex->definer= $2; }
;
text_or_password:
- TEXT_STRING { Lex->definer->pwhash= $1;}
- | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; }
+ TEXT_STRING
+ {
+ Lex->definer->auth= new (thd->mem_root) USER_AUTH();
+ Lex->definer->auth->auth_str= $1;
+ }
+ | PASSWORD_SYM '(' TEXT_STRING ')'
+ {
+ Lex->definer->auth= new (thd->mem_root) USER_AUTH();
+ Lex->definer->auth->pwtext= $3;
+ }
| OLD_PASSWORD_SYM '(' TEXT_STRING ')'
{
- Lex->definer->pwtext= $3;
- Lex->definer->pwhash.str= Item_func_password::alloc(thd,
+ Lex->definer->auth= new (thd->mem_root) USER_AUTH();
+ Lex->definer->auth->pwtext= $3;
+ Lex->definer->auth->auth_str.str= Item_func_password::alloc(thd,
$3.str, $3.length, Item_func_password::OLD);
- Lex->definer->pwhash.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+ Lex->definer->auth->auth_str.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
}
;
@@ -16790,7 +17194,7 @@ table_lock_list:
;
table_lock:
- table_ident opt_table_alias lock_option
+ table_ident opt_table_alias_clause lock_option
{
thr_lock_type lock_type= (thr_lock_type) $3;
bool lock_for_write= (lock_type >= TL_WRITE_ALLOW_WRITE);
@@ -16801,7 +17205,7 @@ table_lock:
? MDL_SHARED_WRITE
: MDL_SHARED_NO_READ_WRITE;
- if (unlikely(!Select->
+ if (unlikely(!Lex->current_select_or_default()->
add_table_to_list(thd, $1, $2, table_options,
lock_type, mdl_type)))
MYSQL_YYABORT;
@@ -16838,32 +17242,42 @@ unlock:
*/
handler:
- HANDLER_SYM table_ident OPEN_SYM opt_table_alias
+ HANDLER_SYM
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ handler_tail
+ {
+ Lex->pop_select(); //main select
+ }
+ ;
+
+handler_tail:
+ table_ident OPEN_SYM opt_table_alias_clause
{
LEX *lex= Lex;
if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER"));
lex->sql_command = SQLCOM_HA_OPEN;
- if (unlikely(!lex->current_select->add_table_to_list(thd, $2, $4,
- 0)))
+ if (!lex->current_select->add_table_to_list(thd, $1, $3, 0))
MYSQL_YYABORT;
}
- | HANDLER_SYM table_ident_nodb CLOSE_SYM
+ | table_ident_nodb CLOSE_SYM
{
LEX *lex= Lex;
if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER"));
lex->sql_command = SQLCOM_HA_CLOSE;
- if (unlikely(!lex->current_select->add_table_to_list(thd, $2, 0,
- 0)))
+ if (!lex->current_select->add_table_to_list(thd, $1, 0, 0))
MYSQL_YYABORT;
}
- | HANDLER_SYM table_ident_nodb READ_SYM
+ | table_ident_nodb READ_SYM
{
LEX *lex=Lex;
if (unlikely(lex->sphead))
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER"));
- lex->expr_allows_subselect= FALSE;
+ lex->clause_that_disallows_subselect= "HANDLER..READ";
lex->sql_command = SQLCOM_HA_READ;
lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */
Item *one= new (thd->mem_root) Item_int(thd, (int32) 1);
@@ -16872,15 +17286,24 @@ handler:
lex->current_select->select_limit= one;
lex->current_select->offset_limit= 0;
lex->limit_rows_examined= 0;
- if (unlikely(!lex->current_select->add_table_to_list(thd, $2, 0,
- 0)))
+ if (!lex->current_select->add_table_to_list(thd, $1, 0, 0))
MYSQL_YYABORT;
}
- handler_read_or_scan opt_where_clause opt_limit_clause
+ handler_read_or_scan opt_where_clause opt_global_limit_clause
{
- Lex->expr_allows_subselect= TRUE;
+ LEX *lex=Lex;
+ lex->clause_that_disallows_subselect= NULL;
+ if (!lex->current_select->explicit_limit)
+ {
+ Item *one= new (thd->mem_root) Item_int(thd, (int32) 1);
+ if (one == NULL)
+ MYSQL_YYABORT;
+ lex->current_select->select_limit= one;
+ lex->current_select->offset_limit= 0;
+ lex->limit_rows_examined= 0;
+ }
/* Stored functions are not supported for HANDLER READ. */
- if (unlikely(Lex->uses_stored_routines()))
+ if (lex->uses_stored_routines())
{
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
"stored functions in HANDLER ... READ");
@@ -17078,7 +17501,7 @@ current_role:
if (unlikely(!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))))
MYSQL_YYABORT;
$$->user= current_role;
- $$->reset_auth();
+ $$->auth= NULL;
}
;
@@ -17095,7 +17518,7 @@ grant_role:
MYSQL_YYABORT;
$$->user= $1;
$$->host= empty_clex_str;
- $$->reset_auth();
+ $$->auth= NULL;
if (unlikely(check_string_char_length(&$$->user, ER_USERNAME,
username_char_length,
@@ -17185,23 +17608,23 @@ require_list_element:
SUBJECT_SYM TEXT_STRING
{
LEX *lex=Lex;
- if (unlikely(lex->x509_subject))
+ if (lex->account_options.x509_subject.str)
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SUBJECT"));
- lex->x509_subject=$2.str;
+ lex->account_options.x509_subject= $2;
}
| ISSUER_SYM TEXT_STRING
{
LEX *lex=Lex;
- if (unlikely(lex->x509_issuer))
+ if (lex->account_options.x509_issuer.str)
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "ISSUER"));
- lex->x509_issuer=$2.str;
+ lex->account_options.x509_issuer= $2;
}
| CIPHER_SYM TEXT_STRING
{
LEX *lex=Lex;
- if (unlikely(lex->ssl_cipher))
+ if (lex->account_options.ssl_cipher.str)
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CIPHER"));
- lex->ssl_cipher=$2.str;
+ lex->account_options.ssl_cipher= $2;
}
;
@@ -17209,7 +17632,7 @@ grant_ident:
'*'
{
LEX *lex= Lex;
- if (unlikely(lex->copy_db_to(&lex->current_select->db)))
+ if (unlikely(lex->copy_db_to(&lex->first_select_lex()->db)))
MYSQL_YYABORT;
if (lex->grant == GLOBAL_ACLS)
lex->grant = DB_ACLS & ~GRANT_ACL;
@@ -17219,7 +17642,7 @@ grant_ident:
| ident '.' '*'
{
LEX *lex= Lex;
- lex->current_select->db= $1;
+ lex->first_select_lex()->db= $1;
if (lex->grant == GLOBAL_ACLS)
lex->grant = DB_ACLS & ~GRANT_ACL;
else if (unlikely(lex->columns.elements))
@@ -17228,7 +17651,7 @@ grant_ident:
| '*' '.' '*'
{
LEX *lex= Lex;
- lex->current_select->db= null_clex_str;
+ lex->first_select_lex()->db= null_clex_str;
if (lex->grant == GLOBAL_ACLS)
lex->grant= GLOBAL_ACLS & ~GRANT_ACL;
else if (unlikely(lex->columns.elements))
@@ -17237,7 +17660,7 @@ grant_ident:
| table_ident
{
LEX *lex=Lex;
- if (unlikely(!lex->current_select->
+ if (unlikely(!lex->first_select_lex()->
add_table_to_list(thd, $1,NULL,
TL_OPTION_UPDATING)))
MYSQL_YYABORT;
@@ -17292,29 +17715,65 @@ grant_user:
user IDENTIFIED_SYM BY TEXT_STRING
{
$$= $1;
- $1->pwtext= $4;
- if (unlikely(Lex->sql_command == SQLCOM_REVOKE))
- MYSQL_YYABORT;
+ $1->auth= new (thd->mem_root) USER_AUTH();
+ $1->auth->pwtext= $4;
}
| user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING
{
$$= $1;
- $1->pwhash= $5;
+ $1->auth= new (thd->mem_root) USER_AUTH();
+ $1->auth->auth_str= $5;
}
- | user IDENTIFIED_SYM via_or_with ident_or_text
+ | user IDENTIFIED_SYM via_or_with auth_expression
{
$$= $1;
- $1->plugin= $4;
- $1->auth= empty_clex_str;
+ $1->auth= $4;
}
- | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as TEXT_STRING_sys
+ | user_or_role
{
$$= $1;
- $1->plugin= $4;
- $1->auth= $6;
}
- | user_or_role
- { $$= $1; }
+ ;
+
+auth_expression:
+ auth_token OR_SYM auth_expression
+ {
+ $$= $1;
+ DBUG_ASSERT($$->next == NULL);
+ $$->next= $3;
+ }
+ | auth_token
+ {
+ $$= $1;
+ }
+ ;
+
+auth_token:
+ ident_or_text opt_auth_str
+ {
+ $$= $2;
+ $$->plugin= $1;
+ }
+ ;
+
+opt_auth_str:
+ /* empty */
+ {
+ if (!($$=(USER_AUTH*) thd->calloc(sizeof(USER_AUTH))))
+ MYSQL_YYABORT;
+ }
+ | using_or_as TEXT_STRING_sys
+ {
+ if (!($$=(USER_AUTH*) thd->calloc(sizeof(USER_AUTH))))
+ MYSQL_YYABORT;
+ $$->auth_str= $2;
+ }
+ | using_or_as PASSWORD_SYM '(' TEXT_STRING ')'
+ {
+ if (!($$=(USER_AUTH*) thd->calloc(sizeof(USER_AUTH))))
+ MYSQL_YYABORT;
+ $$->pwtext= $4;
+ }
;
opt_column_list:
@@ -17323,7 +17782,7 @@ opt_column_list:
LEX *lex=Lex;
lex->grant |= lex->which_columns;
}
- | '(' column_list ')'
+ | '(' column_list ')' { }
;
column_list:
@@ -17364,52 +17823,47 @@ opt_require_clause:
/* empty */
| REQUIRE_SYM require_list
{
- Lex->ssl_type=SSL_TYPE_SPECIFIED;
+ Lex->account_options.ssl_type= SSL_TYPE_SPECIFIED;
}
| REQUIRE_SYM SSL_SYM
{
- Lex->ssl_type=SSL_TYPE_ANY;
+ Lex->account_options.ssl_type= SSL_TYPE_ANY;
}
| REQUIRE_SYM X509_SYM
{
- Lex->ssl_type=SSL_TYPE_X509;
+ Lex->account_options.ssl_type= SSL_TYPE_X509;
}
| REQUIRE_SYM NONE_SYM
{
- Lex->ssl_type=SSL_TYPE_NONE;
+ Lex->account_options.ssl_type= SSL_TYPE_NONE;
}
;
resource_option:
MAX_QUERIES_PER_HOUR ulong_num
{
- LEX *lex=Lex;
- lex->mqh.questions=$2;
- lex->mqh.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR;
+ Lex->account_options.questions=$2;
+ Lex->account_options.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR;
}
| MAX_UPDATES_PER_HOUR ulong_num
{
- LEX *lex=Lex;
- lex->mqh.updates=$2;
- lex->mqh.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR;
+ Lex->account_options.updates=$2;
+ Lex->account_options.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR;
}
| MAX_CONNECTIONS_PER_HOUR ulong_num
{
- LEX *lex=Lex;
- lex->mqh.conn_per_hour= $2;
- lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR;
+ Lex->account_options.conn_per_hour= $2;
+ Lex->account_options.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR;
}
| MAX_USER_CONNECTIONS_SYM int_num
{
- LEX *lex=Lex;
- lex->mqh.user_conn= $2;
- lex->mqh.specified_limits|= USER_RESOURCES::USER_CONNECTIONS;
+ Lex->account_options.user_conn= $2;
+ Lex->account_options.specified_limits|= USER_RESOURCES::USER_CONNECTIONS;
}
| MAX_STATEMENT_TIME_SYM NUM_literal
{
- LEX *lex=Lex;
- lex->mqh.max_statement_time= $2->val_real();
- lex->mqh.specified_limits|= USER_RESOURCES::MAX_STATEMENT_TIME;
+ Lex->account_options.max_statement_time= $2->val_real();
+ Lex->account_options.specified_limits|= USER_RESOURCES::MAX_STATEMENT_TIME;
}
;
@@ -17458,8 +17912,8 @@ compound_statement:
sp_proc_stmt_compound_ok
{
Lex->sql_command= SQLCOM_COMPOUND;
- Lex->sphead->set_stmt_end(thd);
- Lex->sphead->restore_thd_mem_root(thd);
+ if (Lex->sp_body_finalize_procedure(thd))
+ MYSQL_YYABORT;
}
;
@@ -17546,214 +18000,27 @@ release:
*/
unit_type_decl:
- UNION_SYM
- { $$= UNION_TYPE; }
+ UNION_SYM union_option
+ { $$.unit_type= UNION_TYPE; $$.distinct= $2; }
| INTERSECT_SYM
- { $$= INTERSECT_TYPE; }
+ { $$.unit_type= INTERSECT_TYPE; $$.distinct= 1; }
| EXCEPT_SYM
- { $$= EXCEPT_TYPE; }
- ;
-
-union_clause:
- /* empty */ {}
- | union_list
- ;
-
-union_list:
- unit_type_decl union_option
- {
- if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, TRUE)))
- MYSQL_YYABORT;
- }
- union_list_part2
- {
- /*
- Remove from the name resolution context stack the context of the
- last select in the union.
- */
- Lex->pop_context();
- }
- ;
-
-union_list_view:
- unit_type_decl union_option
- {
- if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, TRUE)))
- MYSQL_YYABORT;
- }
- query_expression_body_view
- {
- Lex->pop_context();
- }
- ;
-
-union_order_or_limit:
- {
- LEX *lex= thd->lex;
- DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE);
- SELECT_LEX *sel= lex->current_select;
- SELECT_LEX_UNIT *unit= sel->master_unit();
- SELECT_LEX *fake= unit->fake_select_lex;
- if (fake)
- {
- fake->no_table_names_allowed= 1;
- lex->current_select= fake;
- }
- thd->where= "global ORDER clause";
- }
- order_or_limit
- {
- thd->lex->current_select->no_table_names_allowed= 0;
- thd->where= "";
- }
- ;
-
-order_or_limit:
- order_clause opt_limit_clause
- | limit_clause
+ { $$.unit_type= EXCEPT_TYPE; $$.distinct= 1; }
;
/*
Start a UNION, for non-top level query expressions.
*/
-union_head_non_top:
- unit_type_decl union_option
- {
- if (unlikely(Lex->add_select_to_union_list((bool)$2, $1, FALSE)))
- MYSQL_YYABORT;
- }
- ;
-
union_option:
/* empty */ { $$=1; }
| DISTINCT { $$=1; }
| ALL { $$=0; }
;
-simple_table:
- query_specification { $$= $1; }
- | table_value_constructor { $$= $1; }
- ;
-
-table_value_constructor:
- VALUES
- {
- Lex->tvc_start();
- }
- values_list
- {
- $$= Lex->current_select;
- if (Lex->tvc_finalize())
- MYSQL_YYABORT;
- }
- ;
-
-/*
- Corresponds to the SQL Standard
- <query specification> ::=
- SELECT [ <set quantifier> ] <select list> <table expression>
-
- Notes:
- - We allow more options in addition to <set quantifier>
- - <table expression> is optional in MariaDB
-*/
-query_specification:
- SELECT_SYM select_init2_derived opt_table_expression
- {
- $$= Lex->current_select->master_unit()->first_select();
- }
- ;
-
-query_term_union_not_ready:
- simple_table order_or_limit opt_select_lock_type { $$= $1; }
- | '(' select_paren_derived ')' union_order_or_limit { $$= $2; }
- ;
-
-query_term_union_ready:
- simple_table opt_select_lock_type { $$= $1; }
- | '(' select_paren_derived ')' { $$= $2; }
- ;
-
-query_expression_body:
- query_term_union_not_ready { $$= $1; }
- | query_term_union_ready { $$= $1; }
- | query_term_union_ready union_list_derived { $$= $1; }
- ;
-
-/* Corresponds to <query expression> in the SQL:2003 standard. */
-subselect:
- subselect_start opt_with_clause query_expression_body subselect_end
- {
- $3->set_with_clause($2);
- $$= $3;
- }
- ;
-
-subselect_start:
- {
- LEX *lex=Lex;
- if (unlikely(!lex->expr_allows_subselect ||
- lex->sql_command == (int)SQLCOM_PURGE))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- /*
- we are making a "derived table" for the parenthesis
- as we need to have a lex level to fit the union
- after the parenthesis, e.g.
- (SELECT .. ) UNION ... becomes
- SELECT * FROM ((SELECT ...) UNION ...)
- */
- if (unlikely(mysql_new_select(Lex, 1, NULL)))
- MYSQL_YYABORT;
- }
- ;
-
-subselect_end:
- {
- LEX *lex=Lex;
-
- lex->check_automatic_up(UNSPECIFIED_TYPE);
- lex->pop_context();
- SELECT_LEX *child= lex->current_select;
- lex->current_select = lex->current_select->return_after_parsing();
- lex->nest_level--;
- lex->current_select->n_child_sum_items += child->n_sum_items;
-
- /*
- A subquery (and all the subsequent query blocks in a UNION) can
- add columns to an outer query block. Reserve space for them.
- Aggregate functions in having clause can also add fields to an
- outer select.
- */
- for (SELECT_LEX *temp= child->master_unit()->first_select();
- temp != NULL; temp= temp->next_select())
- {
- lex->current_select->select_n_where_fields+=
- temp->select_n_where_fields;
- lex->current_select->select_n_having_items+=
- temp->select_n_having_items;
- }
- }
- ;
-
-opt_query_expression_options:
- /* empty */
- | query_expression_option_list
- ;
-
-query_expression_option_list:
- query_expression_option_list query_expression_option
- | query_expression_option
- ;
-
query_expression_option:
STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; }
| HIGH_PRIORITY
{
- if (unlikely(Lex->check_simple_select(&$1)))
- MYSQL_YYABORT;
YYPS->m_lock_type= TL_READ_HIGH_PRIORITY;
YYPS->m_mdl_type= MDL_SHARED_READ;
Select->options|= SELECT_HIGH_PRIORITY;
@@ -17762,18 +18029,8 @@ query_expression_option:
| UNIQUE_SYM { Select->options|= SELECT_DISTINCT; }
| SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; }
| SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; }
- | SQL_BUFFER_RESULT
- {
- if (unlikely(Lex->check_simple_select(&$1)))
- MYSQL_YYABORT;
- Select->options|= OPTION_BUFFER_RESULT;
- }
- | SQL_CALC_FOUND_ROWS
- {
- if (unlikely(Lex->check_simple_select(&$1)))
- MYSQL_YYABORT;
- Select->options|= OPTION_FOUND_ROWS;
- }
+ | SQL_BUFFER_RESULT { Select->options|= OPTION_BUFFER_RESULT; }
+ | SQL_CALC_FOUND_ROWS { Select->options|= OPTION_FOUND_ROWS; }
| ALL { Select->options|= SELECT_ALL; }
;
@@ -17806,9 +18063,7 @@ definer:
DEFINER_SYM '=' user_or_role
{
Lex->definer= $3;
- Lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
- Lex->ssl_cipher= Lex->x509_subject= Lex->x509_issuer= 0;
- bzero(&(Lex->mqh), sizeof(Lex->mqh));
+ Lex->account_options.reset();
}
;
@@ -17837,7 +18092,7 @@ view_suid:
view_list_opt:
/* empty */
{}
- | '(' view_list ')'
+ | '(' view_list ')' { }
;
view_list:
@@ -17861,35 +18116,14 @@ view_select:
lex->parsing_options.allows_variable= FALSE;
lex->create_view->select.str= (char *) YYLIP->get_cpp_ptr();
}
- opt_with_clause query_expression_body_view view_check_option
+ query_expression
+ view_check_option
{
- LEX *lex= Lex;
- size_t len= YYLIP->get_cpp_ptr() - lex->create_view->select.str;
- void *create_view_select= thd->memdup(lex->create_view->select.str, len);
- lex->create_view->select.length= len;
- lex->create_view->select.str= (char *) create_view_select;
- trim_whitespace(thd->charset(),
- &lex->create_view->select);
- lex->create_view->check= $4;
- lex->parsing_options.allows_variable= TRUE;
- lex->current_select->set_with_clause($2);
+ if (Lex->parsed_create_view($2, $3))
+ MYSQL_YYABORT;
}
;
-/*
- SQL Standard <query expression body> for VIEWs.
- Does not include INTO and PROCEDURE clauses.
-*/
-query_expression_body_view:
- SELECT_SYM select_options_and_item_list select_init3_view
- | table_value_constructor
- | table_value_constructor union_order_or_limit
- | table_value_constructor union_list_view
- | '(' select_paren_view ')'
- | '(' select_paren_view ')' union_order_or_limit
- | '(' select_paren_view ')' union_list_view
- ;
-
view_check_option:
/* empty */ { $$= VIEW_CHECK_NONE; }
| WITH CHECK_SYM OPTION { $$= VIEW_CHECK_CASCADED; }
@@ -17966,7 +18200,8 @@ trigger_tail:
(*static_cast<st_trg_execution_order*>(&lex->trg_chistics))= ($17);
lex->trg_chistics.ordering_clause_end= lip->get_cpp_ptr();
- if (unlikely(!lex->make_sp_head(thd, $4, &sp_handler_trigger)))
+ if (unlikely(!lex->make_sp_head(thd, $4, &sp_handler_trigger,
+ DEFAULT_AGGREGATE)))
MYSQL_YYABORT;
lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
@@ -17974,15 +18209,9 @@ trigger_tail:
sp_proc_stmt /* $19 */
{ /* $20 */
LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- if (unlikely(sp->check_unresolved_goto()))
- MYSQL_YYABORT;
lex->sql_command= SQLCOM_CREATE_TRIGGER;
- sp->set_stmt_end(thd);
- sp->restore_thd_mem_root(thd);
-
- if (unlikely(sp->is_not_allowed_in_function("trigger")))
+ if (lex->sp_body_finalize_trigger(thd))
MYSQL_YYABORT;
/*
@@ -17990,11 +18219,10 @@ trigger_tail:
sp_proc_stmt alternatives are not saving/restoring LEX, so
lex->query_tables can be wiped out.
*/
- if (unlikely(!lex->select_lex.
- add_table_to_list(thd, $10, (LEX_CSTRING*) 0,
- TL_OPTION_UPDATING,
- TL_READ_NO_INSERT,
- MDL_SHARED_NO_WRITE)))
+ if (!lex->first_select_lex()->
+ add_table_to_list(thd, $10, (LEX_CSTRING*) 0,
+ TL_OPTION_UPDATING, TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE))
MYSQL_YYABORT;
}
;
@@ -18005,25 +18233,7 @@ trigger_tail:
**************************************************************************/
-udf_tail:
- opt_if_not_exists ident
- RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
- {
- LEX *lex= thd->lex;
- if (unlikely(lex->add_create_options_with_check($1)))
- MYSQL_YYABORT;
- if (unlikely(is_native_function(thd, & $2)))
- my_yyabort_error((ER_NATIVE_FCT_NAME_COLLISION, MYF(0), $2.str));
- lex->sql_command= SQLCOM_CREATE_FUNCTION;
- lex->udf.name= $2;
- lex->udf.returns= (Item_result) $4;
- lex->udf.dl= $6.str;
- }
- ;
-
-
sf_return_type:
- RETURN_ORACLE_SYM
{
LEX *lex= Lex;
lex->init_last_field(&lex->sphead->m_return_field_def,
@@ -18038,79 +18248,44 @@ sf_return_type:
}
;
-sf_tail:
- opt_if_not_exists
- sp_name
- {
- Lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
- if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1, $2,
- &sp_handler_function)))
- MYSQL_YYABORT;
- }
- opt_sp_parenthesized_fdparam_list
- sf_return_type
+sf_c_chistics_and_body_standalone:
sp_c_chistics
{
LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- lex->sphead->set_chistics(lex->sp_chistics);
- lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
+ lex->sphead->set_c_chistics(lex->sp_chistics);
+ lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
}
sp_tail_is
sp_body
{
if (unlikely(Lex->sp_body_finalize_function(thd)))
MYSQL_YYABORT;
- if (unlikely(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR))
- {
- my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0)));
- }
- Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE);
}
;
-sp_tail:
- opt_if_not_exists sp_name
+sp_tail_standalone:
+ sp_name
{
- Lex->sql_command= SQLCOM_CREATE_PROCEDURE;
- if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1, $2,
- &sp_handler_procedure)))
+ if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1,
+ &sp_handler_procedure,
+ DEFAULT_AGGREGATE)))
MYSQL_YYABORT;
}
opt_sp_parenthesized_pdparam_list
sp_c_chistics
{
- Lex->sphead->set_chistics(Lex->sp_chistics);
+ Lex->sphead->set_c_chistics(Lex->sp_chistics);
Lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
}
sp_tail_is
sp_body
+ opt_sp_name
{
- if (unlikely(Lex->sp_body_finalize_procedure(thd)))
+ if (unlikely(Lex->sp_body_finalize_procedure_standalone(thd, $8)))
MYSQL_YYABORT;
}
;
-sf_tail_standalone:
- sf_tail opt_sp_name
- {
- if (unlikely($2 && !$2->eq(Lex->sphead)))
- my_yyabort_error((ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0),
- ErrConvDQName($2).ptr(),
- ErrConvDQName(Lex->sphead).ptr()));
- }
- ;
-
-sp_tail_standalone:
- sp_tail opt_sp_name
- {
- if (unlikely($2 && !$2->eq(Lex->sphead)))
- my_yyabort_error((ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0),
- ErrConvDQName($2).ptr(),
- ErrConvDQName(Lex->sphead).ptr()));
- }
- ;
opt_package_routine_end_name:
/* Empty */ { $$= null_clex_str; }
@@ -18224,35 +18399,27 @@ opt_migrate:
;
install:
- INSTALL_SYM PLUGIN_SYM ident SONAME_SYM TEXT_STRING_sys
+ INSTALL_SYM PLUGIN_SYM opt_if_not_exists ident SONAME_SYM TEXT_STRING_sys
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_INSTALL_PLUGIN;
- lex->comment= $3;
- lex->ident= $5;
+ if (Lex->stmt_install_plugin($3, $4, $6))
+ MYSQL_YYABORT;
}
| INSTALL_SYM SONAME_SYM TEXT_STRING_sys
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_INSTALL_PLUGIN;
- lex->comment= null_clex_str;
- lex->ident= $3;
+ Lex->stmt_install_plugin($3);
}
;
uninstall:
- UNINSTALL_SYM PLUGIN_SYM ident
+ UNINSTALL_SYM PLUGIN_SYM opt_if_exists ident
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_UNINSTALL_PLUGIN;
- lex->comment= $3;
+ if (Lex->stmt_uninstall_plugin_by_name($3, $4))
+ MYSQL_YYABORT;
}
- | UNINSTALL_SYM SONAME_SYM TEXT_STRING_sys
+ | UNINSTALL_SYM SONAME_SYM opt_if_exists TEXT_STRING_sys
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_UNINSTALL_PLUGIN;
- lex->comment= null_clex_str;
- lex->ident= $3;
+ if (Lex->stmt_uninstall_plugin_by_soname($3, $4))
+ MYSQL_YYABORT;
}
;
diff --git a/sql/strfunc.cc b/sql/strfunc.cc
index 99ff9c50588..58647c21e44 100644
--- a/sql/strfunc.cc
+++ b/sql/strfunc.cc
@@ -79,14 +79,17 @@ ulonglong find_set(TYPELIB *lib, const char *str, size_t length, CHARSET_INFO *c
var_len= (uint) (pos - start);
uint find= cs ? find_type2(lib, start, var_len, cs) :
find_type(lib, start, var_len, (bool) 0);
- if (unlikely(!find && *err_len == 0))
+ if (unlikely(!find))
{
- // report the first error with length > 0
- *err_pos= (char*) start;
- *err_len= var_len;
- *set_warning= 1;
+ if (*err_len == 0)
+ {
+ // report the first error with length > 0
+ *err_pos= (char*) start;
+ *err_len= var_len;
+ *set_warning= 1;
+ }
}
- else
+ else if (find <= sizeof(longlong) * 8)
found|= 1ULL << (find - 1);
if (pos >= end)
break;
@@ -400,4 +403,3 @@ const char *flagset_to_string(THD *thd, LEX_CSTRING *result, ulonglong set,
return result->str;
}
-
diff --git a/sql/structs.h b/sql/structs.h
index 46947b50502..215fe7b60ea 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -1,8 +1,8 @@
#ifndef STRUCTS_INCLUDED
#define STRUCTS_INCLUDED
-/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
- Copyright (c) 2017, MariaDB Corporation.
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
#include "thr_lock.h" /* thr_lock_type */
#include "my_base.h" /* ha_rows, ha_key_alg */
#include <mysql_com.h> /* USERNAME_LENGTH */
+#include "sql_bitmap.h"
struct TABLE;
class Type_handler;
@@ -110,6 +111,13 @@ typedef struct st_key {
ext_key_part_map.is_set(1) == false
*/
key_part_map ext_key_part_map;
+ /*
+ Bitmap of indexes having common parts with this index
+ (only key parts from key definitions are taken into account)
+ */
+ key_map overlapped;
+ /* Set of keys constraint correlated with this key */
+ key_map constraint_correlated;
LEX_CSTRING name;
uint block_size;
enum ha_key_alg algorithm;
@@ -200,6 +208,17 @@ extern const char *show_comp_option_name[];
typedef int *(*update_var)(THD *, struct st_mysql_show_var *);
+struct USER_AUTH : public Sql_alloc
+{
+ LEX_CSTRING plugin, auth_str, pwtext;
+ USER_AUTH *next;
+ USER_AUTH() : next(NULL)
+ {
+ plugin.str= auth_str.str= "";
+ pwtext.str= NULL;
+ plugin.length= auth_str.length= pwtext.length= 0;
+ }
+};
struct AUTHID
{
@@ -224,13 +243,10 @@ struct AUTHID
struct LEX_USER: public AUTHID
{
- LEX_CSTRING plugin, auth;
- LEX_CSTRING pwtext, pwhash;
- void reset_auth()
+ USER_AUTH *auth;
+ bool has_auth()
{
- pwtext.length= pwhash.length= plugin.length= auth.length= 0;
- pwtext.str= pwhash.str= 0;
- plugin.str= auth.str= "";
+ return auth && (auth->plugin.length || auth->auth_str.length || auth->pwtext.length);
}
};
@@ -774,6 +790,43 @@ public:
};
+class st_select_lex;
+
+class Lex_select_lock
+{
+public:
+ struct
+ {
+ uint defined_lock:1;
+ uint update_lock:1;
+ uint defined_timeout:1;
+ };
+ ulong timeout;
+
+
+ void empty()
+ {
+ defined_lock= update_lock= defined_timeout= FALSE;
+ timeout= 0;
+ }
+ void set_to(st_select_lex *sel);
+};
+
+class Lex_select_limit
+{
+public:
+ bool explicit_limit;
+ Item *select_limit, *offset_limit;
+
+ void empty()
+ {
+ explicit_limit= FALSE;
+ select_limit= offset_limit= NULL;
+ }
+};
+
+struct st_order;
+
class Load_data_param
{
protected:
@@ -810,4 +863,26 @@ public:
};
+class Timeval: public timeval
+{
+protected:
+ Timeval() { }
+public:
+ Timeval(my_time_t sec, ulong usec)
+ {
+ tv_sec= sec;
+ /*
+ Since tv_usec is not always of type ulong, cast usec parameter
+ explicitly to uint to avoid compiler warnings about losing
+ integer precision.
+ */
+ DBUG_ASSERT(usec < 1000000);
+ tv_usec= (uint)usec;
+ }
+ explicit Timeval(const timeval &tv)
+ :timeval(tv)
+ { }
+};
+
+
#endif /* STRUCTS_INCLUDED */
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 43e175887ef..c52a8f742a8 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2002, 2015, Oracle and/or its affiliates.
- Copyright (c) 2012, 2018, MariaDB Corporation.
+ Copyright (c) 2012, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -53,6 +53,7 @@
#include <myisam.h>
#include "debug_sync.h" // DEBUG_SYNC
#include "sql_show.h"
+#include "opt_trace_context.h"
#include "log_event.h"
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
@@ -368,6 +369,15 @@ static bool update_auto_increment_increment (sys_var *self, THD *thd, enum_var_t
#endif /* WITH_WSREP */
+static Sys_var_double Sys_analyze_sample_percentage(
+ "analyze_sample_percentage",
+ "Percentage of rows from the table ANALYZE TABLE will sample "
+ "to collect table statistics. Set to 0 to let MariaDB decide "
+ "what percentage of rows to sample.",
+ SESSION_VAR(sample_percentage),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 100),
+ DEFAULT(100));
+
static Sys_var_ulong Sys_auto_increment_increment(
"auto_increment_increment",
"Auto-increment columns are incremented by this",
@@ -521,7 +531,7 @@ bool check_has_super(sys_var *self, THD *thd, set_var *var)
static Sys_var_bit Sys_core_file("core_file", "write a core-file on crashes",
READ_ONLY GLOBAL_VAR(test_flags), NO_CMD_LINE,
- TEST_CORE_ON_SIGNAL, DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ TEST_CORE_ON_SIGNAL, DEFAULT(IF_WIN(TRUE,FALSE)), NO_MUTEX_GUARD, NOT_IN_BINLOG,
0,0,0);
static bool binlog_format_check(sys_var *self, THD *thd, set_var *var)
@@ -646,7 +656,7 @@ static Sys_var_mybool Sys_explicit_defaults_for_timestamp(
"as NULL with DEFAULT NULL attribute, Without this option, "
"TIMESTAMP columns are NOT NULL and have implicit DEFAULT clauses.",
READ_ONLY GLOBAL_VAR(opt_explicit_defaults_for_timestamp),
- CMD_LINE(OPT_ARG), DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG);
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
static Sys_var_ulonglong Sys_bulk_insert_buff_size(
@@ -1555,6 +1565,24 @@ static Sys_var_ulong Sys_max_connections(
DEFAULT(MAX_CONNECTIONS_DEFAULT), BLOCK_SIZE(1), NO_MUTEX_GUARD,
NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_max_connections));
+static Sys_var_uint Sys_default_password_lifetime(
+ "default_password_lifetime",
+ "This defines the global password expiration policy. 0 means "
+ "automatic password expiration is disabled. If the value is a "
+ "positive integer N, the passwords must be changed every N days. This "
+ "behavior can be overridden using the password expiration options in "
+ "ALTER USER.",
+ GLOBAL_VAR(default_password_lifetime), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
+
+static Sys_var_mybool Sys_disconnect_on_expired_password(
+ "disconnect_on_expired_password",
+ "This variable controls how the server handles clients that are not "
+ "aware of the sandbox mode. If enabled, the server disconnects the "
+ "client, otherwise the server puts the client in a sandbox mode.",
+ GLOBAL_VAR(disconnect_on_expired_password), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+
static Sys_var_ulong Sys_max_connect_errors(
"max_connect_errors",
"If there is more than this number of interrupted connections from "
@@ -1563,6 +1591,14 @@ static Sys_var_ulong Sys_max_connect_errors(
VALID_RANGE(1, UINT_MAX), DEFAULT(MAX_CONNECT_ERRORS),
BLOCK_SIZE(1));
+static Sys_var_uint Sys_max_password_errors(
+ "max_password_errors",
+ "If there is more than this number of failed connect attempts "
+ "due to invalid password, user will be blocked from further connections until FLUSH_PRIVILEGES.",
+ GLOBAL_VAR(max_password_errors), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, UINT_MAX), DEFAULT(UINT_MAX),
+ BLOCK_SIZE(1));
+
static Sys_var_uint Sys_max_digest_length(
"max_digest_length", "Maximum length considered for digest text.",
READ_ONLY GLOBAL_VAR(max_digest_length),
@@ -1990,6 +2026,19 @@ Sys_var_last_gtid::session_value_ptr(THD *thd, const LEX_CSTRING *base)
}
+static Sys_var_uint Sys_gtid_cleanup_batch_size(
+ "gtid_cleanup_batch_size",
+ "Normally does not need tuning. How many old rows must accumulate in "
+ "the mysql.gtid_slave_pos table before a background job will be run to "
+ "delete them. Can be increased to reduce number of commits if "
+ "using many different engines with --gtid_pos_auto_engines, or to "
+ "reduce CPU overhead if using a huge number of different "
+ "gtid_domain_ids. Can be decreased to reduce number of old rows in the "
+ "table.",
+ GLOBAL_VAR(opt_gtid_cleanup_batch_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0,2147483647), DEFAULT(64), BLOCK_SIZE(1));
+
+
static bool
check_slave_parallel_threads(sys_var *self, THD *thd, set_var *var)
{
@@ -2183,7 +2232,7 @@ static Sys_var_bit Sys_skip_parallel_replication(
"retry for transactions that are likely to cause a conflict if "
"replicated in parallel.",
SESSION_ONLY(option_bits), NO_CMD_LINE, OPTION_RPL_SKIP_PARALLEL,
- DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG);
+ DEFAULT(FALSE));
static bool
@@ -2279,7 +2328,9 @@ static Sys_var_ulong Sys_max_long_data_size(
READ_ONLY GLOBAL_VAR(max_long_data_size),
CMD_LINE(REQUIRED_ARG, OPT_MAX_LONG_DATA_SIZE),
VALID_RANGE(1024, UINT_MAX32), DEFAULT(1024*1024),
- BLOCK_SIZE(1));
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(0), ON_UPDATE(0),
+ DEPRECATED("'@@max_allowed_packet'"));
static PolyLock_mutex PLock_prepared_stmt_count(&LOCK_prepared_stmt_count);
static Sys_var_uint Sys_max_prepared_stmt_count(
@@ -2504,7 +2555,7 @@ static Sys_var_ulong Sys_optimizer_use_condition_selectivity(
"5 - additionally use selectivity of certain non-range predicates "
"calculated on record samples",
SESSION_VAR(optimizer_use_condition_selectivity), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(1, 5), DEFAULT(1), BLOCK_SIZE(1));
+ VALID_RANGE(1, 5), DEFAULT(4), BLOCK_SIZE(1));
static Sys_var_ulong Sys_optimizer_search_depth(
"optimizer_search_depth",
@@ -2544,6 +2595,9 @@ export const char *optimizer_switch_names[]=
"orderby_uses_equalities",
"condition_pushdown_for_derived",
"split_materialized",
+ "condition_pushdown_for_subquery",
+ "rowid_filter",
+ "condition_pushdown_from_having",
"default",
NullS
};
@@ -2577,6 +2631,23 @@ static Sys_var_flagset Sys_optimizer_switch(
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_legal_optimizer_switch),
ON_UPDATE(fix_optimizer_switch));
+static Sys_var_flagset Sys_optimizer_trace(
+ "optimizer_trace",
+ "Controls tracing of the Optimizer:"
+ " optimizer_trace=option=val[,option=val...], where option is one of"
+ " {enabled}"
+ " and val is one of {on, off, default}",
+ SESSION_VAR(optimizer_trace), CMD_LINE(REQUIRED_ARG),
+ Opt_trace_context::flag_names, DEFAULT(Opt_trace_context::FLAG_DEFAULT));
+ // @see set_var::is_var_optimizer_trace()
+export sys_var *Sys_optimizer_trace_ptr = &Sys_optimizer_trace;
+
+static Sys_var_ulong Sys_optimizer_trace_max_mem_size(
+ "optimizer_trace_max_mem_size",
+ "Maximum allowed size of an optimizer trace",
+ SESSION_VAR(optimizer_trace_max_mem_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, ULONG_MAX), DEFAULT(1024 * 1024), BLOCK_SIZE(1));
+
static Sys_var_charptr Sys_pid_file(
"pid_file", "Pid file used by safe_mysqld",
READ_ONLY GLOBAL_VAR(pidfile_name_ptr), CMD_LINE(REQUIRED_ARG),
@@ -2630,13 +2701,15 @@ static Sys_var_ulong Sys_read_buff_size(
static bool check_read_only(sys_var *self, THD *thd, set_var *var)
{
/* Prevent self dead-lock */
- if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
+ if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction() ||
+ thd->current_backup_stage != BACKUP_FINISHED)
{
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
return true;
}
return false;
}
+
static bool fix_read_only(sys_var *self, THD *thd, enum_var_type type)
{
bool result= true;
@@ -2737,7 +2810,7 @@ static Sys_var_uint Sys_eq_range_index_dive_limit(
"ranges for the index is larger than or equal to this number. "
"If set to 0, index dives are always used.",
SESSION_VAR(eq_range_index_dive_limit), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(0, UINT_MAX32), DEFAULT(0),
+ VALID_RANGE(0, UINT_MAX32), DEFAULT(200),
BLOCK_SIZE(1));
static Sys_var_ulong Sys_range_alloc_block_size(
@@ -2779,17 +2852,6 @@ static Sys_var_ulong Sys_query_prealloc_size(
BLOCK_SIZE(1024), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_thd_mem_root));
-#ifdef HAVE_SMEM
-static Sys_var_mybool Sys_shared_memory(
- "shared_memory", "Enable the shared memory",
- READ_ONLY GLOBAL_VAR(opt_enable_shared_memory), CMD_LINE(OPT_ARG),
- DEFAULT(FALSE));
-
-static Sys_var_charptr Sys_shared_memory_base_name(
- "shared_memory_base_name", "Base name of shared memory",
- READ_ONLY GLOBAL_VAR(shared_memory_base_name), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(0));
-#endif
// this has to be NO_CMD_LINE as the command-line option has a different name
static Sys_var_mybool Sys_skip_external_locking(
@@ -2937,7 +2999,7 @@ static Sys_var_ulong Sys_query_cache_limit(
"Don't cache results that are bigger than this",
GLOBAL_VAR(query_cache_limit), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, UINT_MAX), DEFAULT(1024*1024), BLOCK_SIZE(1),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_query_cache_limit));
static bool fix_qcache_min_res_unit(sys_var *self, THD *thd, enum_var_type type)
@@ -3424,6 +3486,7 @@ static const char *sql_mode_names[]=
"ALLOW_INVALID_DATES", "ERROR_FOR_DIVISION_BY_ZERO", "TRADITIONAL",
"NO_AUTO_CREATE_USER", "HIGH_NOT_PRECEDENCE", "NO_ENGINE_SUBSTITUTION",
"PAD_CHAR_TO_FULL_LENGTH", "EMPTY_STRING_IS_NULL", "SIMULTANEOUS_ASSIGNMENT",
+ "TIME_ROUND_FRACTIONAL",
0
};
@@ -3521,6 +3584,29 @@ static Sys_var_charptr Sys_ssl_crlpath(
READ_ONLY GLOBAL_VAR(opt_ssl_crlpath), SSL_OPT(OPT_SSL_CRLPATH),
IN_FS_CHARSET, DEFAULT(0));
+static const char *tls_version_names[]=
+{
+ "TLSv1.0",
+ "TLSv1.1",
+ "TLSv1.2",
+ "TLSv1.3",
+ 0
+};
+
+export bool tls_version_string_representation(THD *thd, sql_mode_t sql_mode,
+ LEX_CSTRING *ls)
+{
+ set_to_string(thd, ls, tls_version, tls_version_names);
+ return ls->str == 0;
+}
+
+static Sys_var_set Sys_tls_version(
+ "tls_version",
+ "TLS protocol version for secure connections.",
+ READ_ONLY GLOBAL_VAR(tls_version), CMD_LINE(REQUIRED_ARG),
+ tls_version_names,
+ DEFAULT(VIO_TLSv1_1 | VIO_TLSv1_2 | VIO_TLSv1_3));
+
static Sys_var_mybool Sys_standard_compliant_cte(
"standard_compliant_cte",
"Allow only CTEs compliant to SQL standard",
@@ -3821,7 +3907,7 @@ static Sys_var_mybool Sys_timed_mutexes(
"timed_mutexes",
"Specify whether to time mutexes. Deprecated, has no effect.",
GLOBAL_VAR(timed_mutexes), CMD_LINE(OPT_ARG), DEFAULT(0),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL), ON_UPDATE(NULL),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
DEPRECATED(""));
static Sys_var_charptr Sys_version(
@@ -4129,6 +4215,16 @@ static bool fix_sql_log_bin_after_update(sys_var *self, THD *thd,
return FALSE;
}
+static bool check_session_only_variable(sys_var *self, THD *,set_var *var)
+{
+ if (unlikely(var->type == OPT_GLOBAL))
+ {
+ my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), self->name.str, "SESSION");
+ return true;
+ }
+ return false;
+}
+
/**
This function checks if the sql_log_bin can be changed,
what is possible if:
@@ -4144,20 +4240,17 @@ static bool fix_sql_log_bin_after_update(sys_var *self, THD *thd,
static bool check_sql_log_bin(sys_var *self, THD *thd, set_var *var)
{
if (check_has_super(self, thd, var))
- return TRUE;
+ return true;
- if (unlikely(var->type == OPT_GLOBAL))
- {
- my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), self->name.str, "SESSION");
- return TRUE;
- }
+ if (check_session_only_variable(self, thd, var))
+ return true;
if (unlikely(error_if_in_trans_or_substatement(thd,
ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN,
ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN)))
- return TRUE;
+ return true;
- return FALSE;
+ return false;
}
static Sys_var_mybool Sys_log_binlog(
@@ -4741,6 +4834,24 @@ static Sys_var_have Sys_have_symlink(
"--skip-symbolic-links option.",
READ_ONLY GLOBAL_VAR(have_symlink), NO_CMD_LINE);
+#if defined(__SANITIZE_ADDRESS__) || defined(WITH_UBSAN)
+
+#ifdef __SANITIZE_ADDRESS__
+#define SANITIZER_MODE "ASAN"
+#else
+#define SANITIZER_MODE "UBSAN"
+#endif /* __SANITIZE_ADDRESS__ */
+
+static char *have_sanitizer;
+static Sys_var_charptr Sys_have_santitizer(
+ "have_sanitizer",
+ "If the server is compiled with sanitize (compiler option), this "
+ "variable is set to the sanitizer mode used. Possible values are "
+ "ASAN (Address sanitizer) or UBSAN (The Undefined Behavior Sanitizer).",
+ READ_ONLY GLOBAL_VAR(have_sanitizer), NO_CMD_LINE,
+ IN_FS_CHARSET, DEFAULT(SANITIZER_MODE));
+#endif /* defined(__SANITIZE_ADDRESS__) || defined(WITH_UBSAN) */
+
static bool fix_log_state(sys_var *self, THD *thd, enum_var_type type);
static Sys_var_mybool Sys_general_log(
@@ -5178,8 +5289,10 @@ static Sys_var_ulonglong Sys_read_binlog_speed_limit(
static Sys_var_charptr Sys_slave_transaction_retry_errors(
"slave_transaction_retry_errors", "Tells the slave thread to retry "
"transaction for replication when a query event returns an error from "
- "the provided list. Deadlock and elapsed lock wait timeout errors are "
- "automatically added to this list",
+ "the provided list. Deadlock error, elapsed lock wait timeout, "
+ "net read error, net read timeout, net write error, net write timeout, "
+ "connect error and 2 types of lost connection error are automatically "
+ "added to this list",
READ_ONLY GLOBAL_VAR(opt_slave_transaction_retry_errors), CMD_LINE(REQUIRED_ARG),
IN_SYSTEM_CHARSET, DEFAULT(0));
@@ -5350,13 +5463,12 @@ static Sys_var_charptr Sys_wsrep_cluster_name(
ON_CHECK(wsrep_cluster_name_check),
ON_UPDATE(wsrep_cluster_name_update));
-static PolyLock_mutex PLock_wsrep_slave_threads(&LOCK_wsrep_slave_threads);
static Sys_var_charptr Sys_wsrep_cluster_address (
"wsrep_cluster_address", "Address to initially connect to cluster",
PREALLOCATED GLOBAL_VAR(wsrep_cluster_address),
CMD_LINE(REQUIRED_ARG),
IN_SYSTEM_CHARSET, DEFAULT(""),
- &PLock_wsrep_slave_threads, NOT_IN_BINLOG,
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(wsrep_cluster_address_check),
ON_UPDATE(wsrep_cluster_address_update));
@@ -5387,8 +5499,8 @@ static Sys_var_ulong Sys_wsrep_slave_threads(
"wsrep_slave_threads", "Number of slave appliers to launch",
GLOBAL_VAR(wsrep_slave_threads), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1, 512), DEFAULT(1), BLOCK_SIZE(1),
- &PLock_wsrep_slave_threads, NOT_IN_BINLOG,
- ON_CHECK(NULL),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(0),
ON_UPDATE(wsrep_slave_threads_update));
static Sys_var_charptr Sys_wsrep_dbug_option(
@@ -5396,9 +5508,14 @@ static Sys_var_charptr Sys_wsrep_dbug_option(
GLOBAL_VAR(wsrep_dbug_option),CMD_LINE(REQUIRED_ARG),
IN_SYSTEM_CHARSET, DEFAULT(""));
-static Sys_var_mybool Sys_wsrep_debug(
- "wsrep_debug", "To enable debug level logging",
- GLOBAL_VAR(wsrep_debug), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+static const char *wsrep_debug_names[]=
+{ "NONE", "SERVER", "TRANSACTION", "STREAMING", "CLIENT", NullS };
+static Sys_var_enum Sys_wsrep_debug(
+ "wsrep_debug", "WSREP debug level logging",
+ GLOBAL_VAR(wsrep_debug), CMD_LINE(REQUIRED_ARG),
+ wsrep_debug_names, DEFAULT(0),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(0), ON_UPDATE(wsrep_debug_update));
static Sys_var_mybool Sys_wsrep_convert_LOCK_to_trx(
"wsrep_convert_LOCK_to_trx", "To convert locking sessions "
@@ -5626,9 +5743,10 @@ static Sys_var_ulong Sys_wsrep_mysql_replication_bundle(
static Sys_var_mybool Sys_wsrep_load_data_splitting(
"wsrep_load_data_splitting", "To commit LOAD DATA "
- "transaction after every 10K rows inserted",
+ "transaction after every 10K rows inserted (deprecated)",
GLOBAL_VAR(wsrep_load_data_splitting),
- CMD_LINE(OPT_ARG), DEFAULT(TRUE));
+ CMD_LINE(OPT_ARG), DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(0), ON_UPDATE(0), DEPRECATED(""));
static Sys_var_mybool Sys_wsrep_slave_FK_checks(
"wsrep_slave_FK_checks", "Should slave thread do "
@@ -5646,19 +5764,53 @@ static Sys_var_mybool Sys_wsrep_restart_slave(
"wsrep_restart_slave", "Should MariaDB slave be restarted automatically, when node joins back to cluster",
GLOBAL_VAR(wsrep_restart_slave), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+static Sys_var_ulonglong Sys_wsrep_trx_fragment_size(
+ "wsrep_trx_fragment_size",
+ "Size of transaction fragments for streaming replication (measured in "
+ "units of 'wsrep_trx_fragment_unit')",
+ SESSION_VAR(wsrep_trx_fragment_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, WSREP_MAX_WS_SIZE), DEFAULT(0), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(wsrep_trx_fragment_size_check),
+ ON_UPDATE(wsrep_trx_fragment_size_update));
+
+extern const char *wsrep_fragment_units[];
+
+static Sys_var_enum Sys_wsrep_trx_fragment_unit(
+ "wsrep_trx_fragment_unit",
+ "Unit for streaming replication transaction fragments' size: bytes, "
+ "rows, statements",
+ SESSION_VAR(wsrep_trx_fragment_unit), CMD_LINE(REQUIRED_ARG),
+ wsrep_fragment_units,
+ DEFAULT(WSREP_FRAG_BYTES),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(0),
+ ON_UPDATE(wsrep_trx_fragment_unit_update));
+
+extern const char *wsrep_SR_store_types[];
+static Sys_var_enum Sys_wsrep_SR_store(
+ "wsrep_SR_store", "Storage for streaming replication fragments",
+ READ_ONLY GLOBAL_VAR(wsrep_SR_store_type), CMD_LINE(REQUIRED_ARG),
+ wsrep_SR_store_types, DEFAULT(WSREP_SR_STORE_TABLE));
+
static Sys_var_mybool Sys_wsrep_dirty_reads(
"wsrep_dirty_reads",
"Allow reads even when the node is not in the primary component.",
SESSION_VAR(wsrep_dirty_reads), CMD_LINE(OPT_ARG),
- DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG);
+ DEFAULT(FALSE));
+
+static Sys_var_uint Sys_wsrep_ignore_apply_errors (
+ "wsrep_ignore_apply_errors", "Ignore replication errors",
+ GLOBAL_VAR(wsrep_ignore_apply_errors), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(WSREP_IGNORE_ERRORS_NONE, WSREP_IGNORE_ERRORS_MAX),
+ DEFAULT(7), BLOCK_SIZE(1));
static Sys_var_uint Sys_wsrep_gtid_domain_id(
"wsrep_gtid_domain_id", "When wsrep_gtid_mode is set, this value is "
"used as gtid_domain_id for galera transactions and also copied to the "
"joiner nodes during state transfer. It is ignored, otherwise.",
GLOBAL_VAR(wsrep_gtid_domain_id), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(0, UINT_MAX32), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD,
- NOT_IN_BINLOG);
+ VALID_RANGE(0, UINT_MAX32), DEFAULT(0), BLOCK_SIZE(1));
static Sys_var_mybool Sys_wsrep_gtid_mode(
"wsrep_gtid_mode", "Automatically update the (joiner) node's "
@@ -5689,7 +5841,7 @@ static Sys_var_ulong Sys_host_cache_size(
CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 65536),
DEFAULT(HOST_CACHE_SIZE),
BLOCK_SIZE(1),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_host_cache_size));
vio_keepalive_opts opt_vio_keepalive;
@@ -5699,30 +5851,45 @@ static Sys_var_int Sys_keepalive_time(
"Timeout, in seconds, with no activity until the first TCP keep-alive packet is sent."
"If set to 0, system dependent default is used.",
AUTO_SET GLOBAL_VAR(opt_vio_keepalive.idle),
- CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000),
- DEFAULT(0),
- BLOCK_SIZE(1),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000), DEFAULT(0),
+ BLOCK_SIZE(1));
static Sys_var_int Sys_keepalive_interval(
"tcp_keepalive_interval",
"The interval, in seconds, between when successive keep-alive packets are sent if no acknowledgement is received."
"If set to 0, system dependent default is used.",
AUTO_SET GLOBAL_VAR(opt_vio_keepalive.interval),
- CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000),
- DEFAULT(0),
- BLOCK_SIZE(1),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000), DEFAULT(0),
+ BLOCK_SIZE(1));
static Sys_var_int Sys_keepalive_probes(
"tcp_keepalive_probes",
"The number of unacknowledged probes to send before considering the connection dead and notifying the application layer."
"If set to 0, system dependent default is used.",
AUTO_SET GLOBAL_VAR(opt_vio_keepalive.probes),
- CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000),
- DEFAULT(0),
- BLOCK_SIZE(1),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000), DEFAULT(0),
+ BLOCK_SIZE(1));
+
+
+static bool update_tcp_nodelay(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ DBUG_ASSERT(thd);
+
+ Vio *vio = thd->net.vio;
+ if (vio)
+ return (MY_TEST(vio_nodelay(vio, thd->variables.tcp_nodelay)));
+
+ return false;
+}
+
+static Sys_var_mybool Sys_tcp_nodelay(
+ "tcp_nodelay",
+ "Set option TCP_NODELAY (disable Nagle's algorithm) on socket",
+ SESSION_VAR(tcp_nodelay), CMD_LINE(OPT_ARG),
+ DEFAULT(TRUE),NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_session_only_variable),
+ ON_UPDATE(update_tcp_nodelay));
static Sys_var_charptr Sys_ignore_db_dirs(
"ignore_db_dirs",
@@ -5955,19 +6122,20 @@ static Sys_var_ulong Sys_progress_report_time(
VALID_RANGE(0, UINT_MAX), DEFAULT(5), BLOCK_SIZE(1));
const char *use_stat_tables_modes[] =
- {"NEVER", "COMPLEMENTARY", "PREFERABLY", 0};
+ {"NEVER", "COMPLEMENTARY", "PREFERABLY",
+ "COMPLEMENTARY_FOR_QUERIES", "PREFERABLY_FOR_QUERIES", 0};
static Sys_var_enum Sys_optimizer_use_stat_tables(
"use_stat_tables",
"Specifies how to use system statistics tables",
SESSION_VAR(use_stat_tables), CMD_LINE(REQUIRED_ARG),
- use_stat_tables_modes, DEFAULT(0));
+ use_stat_tables_modes, DEFAULT(4));
static Sys_var_ulong Sys_histogram_size(
"histogram_size",
"Number of bytes used for a histogram. "
"If set to 0, no histograms are created by ANALYZE.",
SESSION_VAR(histogram_size), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(0, 255), DEFAULT(0), BLOCK_SIZE(1));
+ VALID_RANGE(0, 255), DEFAULT(254), BLOCK_SIZE(1));
extern const char *histogram_types[];
static Sys_var_enum Sys_histogram_type(
@@ -5977,7 +6145,7 @@ static Sys_var_enum Sys_histogram_type(
"SINGLE_PREC_HB - single precision height-balanced, "
"DOUBLE_PREC_HB - double precision height-balanced.",
SESSION_VAR(histogram_type), CMD_LINE(REQUIRED_ARG),
- histogram_types, DEFAULT(0));
+ histogram_types, DEFAULT(1));
static Sys_var_mybool Sys_no_thread_alarm(
"debug_no_thread_alarm",
@@ -6112,14 +6280,14 @@ static Sys_var_mybool Sys_mysql56_temporal_format(
"mysql56_temporal_format",
"Use MySQL-5.6 (instead of MariaDB-5.3) format for TIME, DATETIME, TIMESTAMP columns.",
GLOBAL_VAR(opt_mysql56_temporal_format),
- CMD_LINE(OPT_ARG), DEFAULT(TRUE), NO_MUTEX_GUARD, NOT_IN_BINLOG);
+ CMD_LINE(OPT_ARG), DEFAULT(TRUE));
static Sys_var_mybool Sys_strict_password_validation(
"strict_password_validation",
"When password validation plugins are enabled, reject passwords "
"that cannot be validated (passwords specified as a hash)",
GLOBAL_VAR(strict_password_validation),
- CMD_LINE(OPT_ARG), DEFAULT(TRUE), NO_MUTEX_GUARD, NOT_IN_BINLOG);
+ CMD_LINE(OPT_ARG), DEFAULT(TRUE));
#ifdef HAVE_MMAP
static Sys_var_ulong Sys_log_tc_size(
@@ -6146,8 +6314,7 @@ static Sys_var_sesvartrack Sys_track_session_sys_vars(
"Track changes in registered system variables. ",
CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
DEFAULT("autocommit,character_set_client,character_set_connection,"
- "character_set_results,time_zone"),
- NO_MUTEX_GUARD);
+ "character_set_results,time_zone"));
static bool update_session_track_schema(sys_var *self, THD *thd,
enum_var_type type)
@@ -6226,3 +6393,10 @@ static Sys_var_enum Sys_secure_timestamp(
"historical behavior, anyone can modify session timestamp",
READ_ONLY GLOBAL_VAR(opt_secure_timestamp), CMD_LINE(REQUIRED_ARG),
secure_timestamp_levels, DEFAULT(SECTIME_NO));
+
+static Sys_var_ulonglong Sys_max_rowid_filter_size(
+ "max_rowid_filter_size",
+ "The maximum size of the container of a rowid filter",
+ SESSION_VAR(max_rowid_filter_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1024, (ulonglong)~(intptr)0), DEFAULT(128*1024),
+ BLOCK_SIZE(1));
diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic
index 3e35820adef..50e2ec46222 100644
--- a/sql/sys_vars.ic
+++ b/sql/sys_vars.ic
@@ -599,7 +599,7 @@ public:
const char *comment,
CMD_LINE getopt,
enum charset_enum is_os_charset_arg,
- const char *def_val, PolyLock *lock) :
+ const char *def_val, PolyLock *lock= 0) :
Sys_var_charptr_base(name_arg, comment,
SESSION_VAR(session_track_system_variables),
getopt, is_os_charset_arg, def_val, lock,
@@ -2652,7 +2652,10 @@ public:
if (!Sys_var_enum::do_check(thd, var))
return false;
MYSQL_TIME ltime;
- bool res= var->value->get_date(&ltime, TIME_NO_ZERO_IN_DATE|TIME_NO_ZERO_DATE);
+ Datetime::Options opt(TIME_CONV_NONE |
+ TIME_NO_ZERO_IN_DATE |
+ TIME_NO_ZERO_DATE, thd);
+ bool res= var->value->get_date(thd, &ltime, opt);
if (!res)
{
var->save_result.ulonglong_value= SYSTEM_TIME_AS_OF;
@@ -2669,7 +2672,15 @@ private:
{
if (var->value)
{
- res= var->value->get_date(&out.ltime, TIME_NO_ZERO_IN_DATE|TIME_NO_ZERO_DATE);
+ THD *thd= current_thd;
+ Datetime::Options opt(TIME_CONV_NONE |
+ TIME_NO_ZERO_IN_DATE |
+ TIME_NO_ZERO_DATE, thd);
+ /*
+ var->value is allowed to return DATETIME and DATE
+ Make sure to convert DATE to DATETIME.
+ */
+ res= Datetime(thd, var->value, opt).copy_to_mysql_time(&out.ltime);
}
else // set DEFAULT from global var
{
diff --git a/sql/table.cc b/sql/table.cc
index cf5a95e0929..12299271ab3 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -44,6 +44,7 @@
#include "sql_cte.h"
#include "ha_sequence.h"
#include "sql_show.h"
+#include "opt_trace.h"
/* For MySQL 5.7 virtual fields */
#define MYSQL57_GENERATED_FIELD 128
@@ -60,6 +61,19 @@ public:
}
};
+struct extra2_fields
+{
+ LEX_CUSTRING version;
+ LEX_CUSTRING options;
+ Lex_ident engine;
+ LEX_CUSTRING gis;
+ LEX_CUSTRING field_flags;
+ LEX_CUSTRING system_period;
+ LEX_CUSTRING application_period;
+ void reset()
+ { bzero((void*)this, sizeof(*this)); }
+};
+
static Virtual_column_info * unpack_vcol_info_from_frm(THD *, MEM_ROOT *,
TABLE *, String *, Virtual_column_info **, bool *);
static bool check_vcol_forward_refs(Field *, Virtual_column_info *,
@@ -81,8 +95,6 @@ LEX_CSTRING GENERAL_LOG_NAME= {STRING_WITH_LEN("general_log")};
LEX_CSTRING SLOW_LOG_NAME= {STRING_WITH_LEN("slow_log")};
LEX_CSTRING TRANSACTION_REG_NAME= {STRING_WITH_LEN("transaction_registry")};
-LEX_CSTRING MYSQL_USER_NAME= {STRING_WITH_LEN("user")};
-LEX_CSTRING MYSQL_DB_NAME= {STRING_WITH_LEN("db")};
LEX_CSTRING MYSQL_PROC_NAME= {STRING_WITH_LEN("proc")};
/*
@@ -91,7 +103,7 @@ LEX_CSTRING MYSQL_PROC_NAME= {STRING_WITH_LEN("proc")};
*/
static LEX_CSTRING parse_vcol_keyword= { STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
-static int64 last_table_id;
+static std::atomic<ulong> last_table_id;
/* Functions defined in this file */
@@ -264,6 +276,13 @@ TABLE_CATEGORY get_table_category(const LEX_CSTRING *db,
DBUG_ASSERT(db != NULL);
DBUG_ASSERT(name != NULL);
+#ifdef WITH_WSREP
+ if (my_strcasecmp(system_charset_info, db->str, "mysql") == 0 &&
+ my_strcasecmp(system_charset_info, name->str, "wsrep_streaming_log") == 0)
+ {
+ return TABLE_CATEGORY_INFORMATION;
+ }
+#endif /* WITH_WSREP */
if (is_infoschema_db(db))
return TABLE_CATEGORY_INFORMATION;
@@ -362,8 +381,8 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
*/
do
{
- share->table_map_id=(ulong) my_atomic_add64_explicit(&last_table_id, 1,
- MY_MEMORY_ORDER_RELAXED);
+ share->table_map_id=
+ last_table_id.fetch_add(1, std::memory_order_relaxed);
} while (unlikely(share->table_map_id == ~0UL ||
share->table_map_id == 0));
}
@@ -712,7 +731,7 @@ err_not_open:
static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
uint keys, KEY *keyinfo,
- uint new_frm_ver, uint &ext_key_parts,
+ uint new_frm_ver, uint *ext_key_parts,
TABLE_SHARE *share, uint len,
KEY *first_keyinfo,
LEX_STRING *keynames)
@@ -771,8 +790,8 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
if (i == 0)
{
- ext_key_parts+= (share->use_ext_keys ? first_keyinfo->user_defined_key_parts*(keys-1) : 0);
- n_length=keys * sizeof(KEY) + ext_key_parts * sizeof(KEY_PART_INFO);
+ (*ext_key_parts)+= (share->use_ext_keys ? first_keyinfo->user_defined_key_parts*(keys-1) : 0);
+ n_length=keys * sizeof(KEY) + *ext_key_parts * sizeof(KEY_PART_INFO);
if (!(keyinfo= (KEY*) alloc_root(&share->mem_root,
n_length + len)))
return 1;
@@ -781,7 +800,7 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
key_part= reinterpret_cast<KEY_PART_INFO*> (keyinfo + keys);
if (!(rec_per_key= (ulong*) alloc_root(&share->mem_root,
- sizeof(ulong) * ext_key_parts)))
+ sizeof(ulong) * *ext_key_parts)))
return 1;
first_key_part= key_part;
first_key_parts= first_keyinfo->user_defined_key_parts;
@@ -799,7 +818,8 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
{
if (strpos + (new_frm_ver >= 1 ? 9 : 7) >= frm_image_end)
return 1;
- *rec_per_key++=0;
+ if (!(keyinfo->algorithm == HA_KEY_ALG_LONG_HASH))
+ *rec_per_key++=0;
key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK);
key_part->offset= (uint) uint2korr(strpos+2)-1;
key_part->key_type= (uint) uint2korr(strpos+5);
@@ -823,6 +843,12 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
}
key_part->store_length=key_part->length;
}
+ if (keyinfo->algorithm == HA_KEY_ALG_LONG_HASH)
+ {
+ keyinfo->key_length= HA_HASH_KEY_LENGTH_WITHOUT_NULL;
+ key_part++; // reserved for the hash value
+ *rec_per_key++=0;
+ }
/*
Add primary key to end of extended keys for non unique keys for
@@ -856,7 +882,9 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
if (j == first_key_parts)
keyinfo->ext_key_flags= keyinfo->flags | HA_EXT_NOSAME;
}
- share->ext_key_parts+= keyinfo->ext_key_parts;
+ if (keyinfo->algorithm == HA_KEY_ALG_LONG_HASH)
+ share->ext_key_parts++;
+ share->ext_key_parts+= keyinfo->ext_key_parts;
}
keynames->str= (char*) key_part;
keynames->length= strnmov(keynames->str, (char *) strpos,
@@ -943,6 +971,54 @@ static uint upgrade_collation(ulong mysql_version, uint cs_number)
}
+void Column_definition_attributes::frm_pack_basic(uchar *buff) const
+{
+ int2store(buff + 3, length);
+ int2store(buff + 8, pack_flag);
+ buff[10]= (uchar) unireg_check;
+}
+
+
+void Column_definition_attributes::frm_unpack_basic(const uchar *buff)
+{
+ length= uint2korr(buff + 3);
+ pack_flag= uint2korr(buff + 8);
+ unireg_check= (Field::utype) MTYP_TYPENR((uint) buff[10]);
+}
+
+
+void Column_definition_attributes::frm_pack_charset(uchar *buff) const
+{
+ buff[11]= (uchar) (charset->number >> 8);
+ buff[14]= (uchar) charset->number;
+}
+
+
+bool Column_definition_attributes::frm_unpack_charset(TABLE_SHARE *share,
+ const uchar *buff)
+{
+ uint cs_org= buff[14] + (((uint) buff[11]) << 8);
+ uint cs_new= upgrade_collation(share->mysql_version, cs_org);
+ if (cs_org != cs_new)
+ share->incompatible_version|= HA_CREATE_USED_CHARSET;
+ if (cs_new && !(charset= get_charset(cs_new, MYF(0))))
+ {
+ const char *csname= get_charset_name((uint) cs_new);
+ char tmp[10];
+ if (!csname || csname[0] =='?')
+ {
+ my_snprintf(tmp, sizeof(tmp), "#%u", cs_new);
+ csname= tmp;
+ }
+ my_printf_error(ER_UNKNOWN_COLLATION,
+ "Unknown collation '%s' in table '%-.64s' definition",
+ MYF(0), csname, share->table_name.str);
+ return true;
+ }
+ return false;
+}
+
+
/*
In MySQL 5.7 the null bits for not stored virtual fields are last.
Calculate the position for these bits
@@ -1098,12 +1174,21 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
vcol= unpack_vcol_info_from_frm(thd, mem_root, table, &expr_str,
&((*field_ptr)->vcol_info), error_reported);
*(vfield_ptr++)= *field_ptr;
+ DBUG_ASSERT(table->map == 0);
+ /*
+ We need Item_field::const_item() to return false, so
+ datetime_precision() and time_precision() do not try to calculate
+ field values, e.g. val_str().
+ Set table->map to non-zero temporarily.
+ */
+ table->map= 1;
if (vcol && field_ptr[0]->check_vcol_sql_mode_dependency(thd, mode))
{
DBUG_ASSERT(thd->is_error());
*error_reported= true;
goto end;
}
+ table->map= 0;
break;
case VCOL_DEFAULT:
vcol= unpack_vcol_info_from_frm(thd, mem_root, table, &expr_str,
@@ -1130,10 +1215,57 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
pos+= expr_length;
}
- /* Now, initialize CURRENT_TIMESTAMP fields */
+ /* Now, initialize CURRENT_TIMESTAMP and UNIQUE_INDEX_HASH_FIELD fields */
for (field_ptr= table->field; *field_ptr; field_ptr++)
{
Field *field= *field_ptr;
+ if (field->flags & LONG_UNIQUE_HASH_FIELD)
+ {
+ List<Item> *field_list= new (mem_root) List<Item>();
+ Item *list_item;
+ KEY *key= 0;
+ uint key_index, parts= 0;
+ for (key_index= 0; key_index < table->s->keys; key_index++)
+ {
+ key=table->key_info + key_index;
+ parts= key->user_defined_key_parts;
+ if (key->key_part[parts].fieldnr == field->field_index + 1)
+ break;
+ }
+ if (!key || key->algorithm != HA_KEY_ALG_LONG_HASH)
+ goto end;
+ KEY_PART_INFO *keypart;
+ for (uint i=0; i < parts; i++)
+ {
+ keypart= key->key_part + i;
+ if (keypart->key_part_flag & HA_PART_KEY_SEG)
+ {
+ int length= keypart->length/keypart->field->charset()->mbmaxlen;
+ list_item= new (mem_root) Item_func_left(thd,
+ new (mem_root) Item_field(thd, keypart->field),
+ new (mem_root) Item_int(thd, length));
+ list_item->fix_fields(thd, NULL);
+ keypart->field->vcol_info=
+ table->field[keypart->field->field_index]->vcol_info;
+ }
+ else
+ list_item= new (mem_root) Item_field(thd, keypart->field);
+ field_list->push_back(list_item, mem_root);
+ }
+ Item_func_hash *hash_item= new(mem_root)Item_func_hash(thd, *field_list);
+ Virtual_column_info *v= new (mem_root) Virtual_column_info();
+ field->vcol_info= v;
+ field->vcol_info->expr= hash_item;
+ key->user_defined_key_parts= key->ext_key_parts= key->usable_key_parts= 1;
+ key->key_part+= parts;
+
+ if (key->flags & HA_NULL_PART_KEY)
+ key->key_length= HA_HASH_KEY_LENGTH_WITH_NULL;
+ else
+ key->key_length= HA_HASH_KEY_LENGTH_WITHOUT_NULL;
+
+ *(vfield_ptr++)= *field_ptr;
+ }
if (field->has_default_now_unireg_check())
{
expr_str.length(parse_vcol_keyword.length);
@@ -1170,6 +1302,8 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
goto end;
}
+ table->find_constraint_correlated_indexes();
+
res=0;
end:
thd->restore_active_arena(table->expr_arena, &backup_arena);
@@ -1180,6 +1314,255 @@ end:
DBUG_RETURN(res);
}
+
+static const Type_handler *old_frm_type_handler(uint pack_flag,
+ uint interval_nr)
+{
+ enum_field_types field_type= (enum_field_types) f_packtype(pack_flag);
+ DBUG_ASSERT(field_type < 16);
+
+ if (!f_is_alpha(pack_flag))
+ return Type_handler::get_handler_by_real_type(field_type);
+
+ if (!f_is_packed(pack_flag))
+ {
+ if (field_type == MYSQL_TYPE_DECIMAL) // 3.23 or 4.0 string
+ return &type_handler_string;
+ if (field_type == MYSQL_TYPE_VARCHAR) // Since mysql-5.0
+ return &type_handler_varchar;
+ return NULL; // Error (bad frm?)
+ }
+
+ if (f_is_blob(pack_flag))
+ return &type_handler_blob; // QQ: exact type??
+
+ if (interval_nr)
+ {
+ if (f_is_enum(pack_flag))
+ return &type_handler_enum;
+ return &type_handler_set;
+ }
+ return Type_handler::get_handler_by_real_type(field_type);
+}
+
+/* Set overlapped bitmaps for each index */
+
+void TABLE_SHARE::set_overlapped_keys()
+{
+ KEY *key1= key_info;
+ for (uint i= 0; i < keys; i++, key1++)
+ {
+ key1->overlapped.clear_all();
+ key1->overlapped.set_bit(i);
+ }
+ key1= key_info;
+ for (uint i= 0; i < keys; i++, key1++)
+ {
+ KEY *key2= key1 + 1;
+ for (uint j= i+1; j < keys; j++, key2++)
+ {
+ KEY_PART_INFO *key_part1= key1->key_part;
+ uint n1= key1->user_defined_key_parts;
+ uint n2= key2->user_defined_key_parts;
+ for (uint k= 0; k < n1; k++, key_part1++)
+ {
+ KEY_PART_INFO *key_part2= key2->key_part;
+ for (uint l= 0; l < n2; l++, key_part2++)
+ {
+ if (key_part1->fieldnr == key_part2->fieldnr)
+ {
+ key1->overlapped.set_bit(j);
+ key2->overlapped.set_bit(i);
+ goto end_checking_overlap;
+ }
+ }
+ }
+ end_checking_overlap:
+ ;
+ }
+ }
+}
+
+
+bool Item_field::check_index_dependence(void *arg)
+{
+ TABLE *table= (TABLE *)arg;
+
+ KEY *key= table->key_info;
+ for (uint j= 0; j < table->s->keys; j++, key++)
+ {
+ if (table->constraint_dependent_keys.is_set(j))
+ continue;
+
+ KEY_PART_INFO *key_part= key->key_part;
+ uint n= key->user_defined_key_parts;
+
+ for (uint k= 0; k < n; k++, key_part++)
+ {
+ if (this->field == key_part->field)
+ {
+ table->constraint_dependent_keys.set_bit(j);
+ break;
+ }
+ }
+ }
+ return false;
+}
+
+
+/**
+ @brief
+ Find keys that occur in the same constraint on this table
+
+ @details
+ Constraints on this table are checked only.
+
+ The method goes through constraints list trying to find at
+ least two keys which parts participate in some constraint.
+ These keys are called constraint correlated.
+
+ Each key has its own key map with the information about with
+ which keys it is constraint correlated. Bit in this map is set
+ only if keys are constraint correlated.
+ This method fills each keys constraint correlated key map.
+*/
+
+void TABLE::find_constraint_correlated_indexes()
+{
+ if (s->keys == 0)
+ return;
+
+ KEY *key= key_info;
+ for (uint i= 0; i < s->keys; i++, key++)
+ {
+ key->constraint_correlated.clear_all();
+ key->constraint_correlated.set_bit(i);
+ }
+
+ if (!check_constraints)
+ return;
+
+ for (Virtual_column_info **chk= check_constraints ; *chk ; chk++)
+ {
+ constraint_dependent_keys.clear_all();
+ (*chk)->expr->walk(&Item::check_index_dependence, 0, this);
+
+ if (constraint_dependent_keys.bits_set() <= 1)
+ continue;
+
+ uint key_no= 0;
+ key_map::Iterator ki(constraint_dependent_keys);
+ while ((key_no= ki++) != key_map::Iterator::BITMAP_END)
+ key_info[key_no].constraint_correlated.merge(constraint_dependent_keys);
+ }
+}
+
+
+bool TABLE_SHARE::init_period_from_extra2(period_info_t *period,
+ const uchar *data, const uchar *end)
+{
+ if (data + 2*frm_fieldno_size > end)
+ return 1;
+ period->start_fieldno= read_frm_fieldno(data);
+ period->end_fieldno= read_frm_fieldno(data + frm_fieldno_size);
+ return period->start_fieldno >= fields || period->end_fieldno >= fields;
+}
+
+
+static size_t extra2_read_len(const uchar **extra2, const uchar *extra2_end)
+{
+ size_t length= *(*extra2)++;
+ if (length)
+ return length;
+
+ if ((*extra2) + 2 >= extra2_end)
+ return 0;
+ length= uint2korr(*extra2);
+ (*extra2)+= 2;
+ if (length < 256 || *extra2 + length > extra2_end)
+ return 0;
+ return length;
+}
+
+
+static
+bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields)
+{
+ const uchar *extra2= frm_image + 64;
+
+ DBUG_ENTER("read_extra2");
+
+ fields->reset();
+
+ if (*extra2 != '/') // old frm had '/' there
+ {
+ const uchar *e2end= extra2 + len;
+ while (extra2 + 3 <= e2end)
+ {
+ extra2_frm_value_type type= (extra2_frm_value_type)*extra2++;
+ size_t length= extra2_read_len(&extra2, e2end);
+ if (!length)
+ DBUG_RETURN(true);
+ switch (type) {
+ case EXTRA2_TABLEDEF_VERSION:
+ if (fields->version.str) // see init_from_sql_statement_string()
+ {
+ if (length != fields->version.length)
+ DBUG_RETURN(true);
+ }
+ else
+ {
+ fields->version.str= extra2;
+ fields->version.length= length;
+ }
+ break;
+ case EXTRA2_ENGINE_TABLEOPTS:
+ if (fields->options.str)
+ DBUG_RETURN(true);
+ fields->options.str= extra2;
+ fields->options.length= length;
+ break;
+ case EXTRA2_DEFAULT_PART_ENGINE:
+ fields->engine.set((const char*)extra2, length);
+ break;
+ case EXTRA2_GIS:
+ if (fields->gis.str)
+ DBUG_RETURN(true);
+ fields->gis.str= extra2;
+ fields->gis.length= length;
+ break;
+ case EXTRA2_PERIOD_FOR_SYSTEM_TIME:
+ if (fields->system_period.str || length != 2 * frm_fieldno_size)
+ DBUG_RETURN(true);
+ fields->system_period.str = extra2;
+ fields->system_period.length= length;
+ break;
+ case EXTRA2_FIELD_FLAGS:
+ if (fields->field_flags.str)
+ DBUG_RETURN(true);
+ fields->field_flags.str= extra2;
+ fields->field_flags.length= length;
+ break;
+ case EXTRA2_APPLICATION_TIME_PERIOD:
+ if (fields->application_period.str)
+ DBUG_RETURN(true);
+ fields->application_period.str= extra2;
+ fields->application_period.length= length;
+ break;
+ default:
+ /* abort frm parsing if it's an unknown but important extra2 value */
+ if (type >= EXTRA2_ENGINE_IMPORTANT)
+ DBUG_RETURN(true);
+ }
+ extra2+= length;
+ }
+ if (extra2 != e2end)
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+}
+
+
/**
Read data from a binary .frm file image into a TABLE_SHARE
@@ -1206,11 +1589,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
uint total_typelib_value_count;
uint db_create_options, keys, key_parts, n_length;
uint com_length, null_bit_pos, UNINIT_VAR(mysql57_vcol_null_bit_pos), bitmap_count;
- uint i;
+ uint i, hash_fields= 0;
bool use_hash, mysql57_null_bits= 0;
LEX_STRING keynames= {NULL, 0};
char *names, *comment_pos;
- const uchar *forminfo, *extra2;
+ const uchar *forminfo;
const uchar *frm_image_end = frm_image + frm_length;
uchar *record, *null_flags, *null_pos, *UNINIT_VAR(mysql57_vcol_null_pos);
const uchar *disk_buff, *strpos;
@@ -1226,26 +1609,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
my_bitmap_map *bitmaps;
bool null_bits_are_used;
uint vcol_screen_length;
- size_t UNINIT_VAR(options_len);
uchar *vcol_screen_pos;
- const uchar *options= 0;
- size_t UNINIT_VAR(gis_options_len);
- const uchar *gis_options= 0;
+ LEX_CUSTRING options;
KEY first_keyinfo;
uint len;
uint ext_key_parts= 0;
plugin_ref se_plugin= 0;
- const uchar *system_period= 0;
bool vers_can_native= false;
- const uchar *extra2_field_flags= 0;
- size_t extra2_field_flags_length= 0;
MEM_ROOT *old_root= thd->mem_root;
Virtual_column_info **table_check_constraints;
+ extra2_fields extra2;
+
DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image");
keyinfo= &first_keyinfo;
- share->ext_key_parts= 0;
thd->mem_root= &share->mem_root;
if (write && write_frm_image(frm_image, frm_length))
@@ -1270,90 +1648,27 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
/* Length of the MariaDB extra2 segment in the form file. */
len = uint2korr(frm_image+4);
- extra2= frm_image + 64;
- if (*extra2 != '/') // old frm had '/' there
- {
- const uchar *e2end= extra2 + len;
- while (extra2 + 3 <= e2end)
- {
- uchar type= *extra2++;
- size_t length= *extra2++;
- if (!length)
- {
- if (extra2 + 2 >= e2end)
- goto err;
- length= uint2korr(extra2);
- extra2+= 2;
- if (length < 256)
- goto err;
- }
- if (extra2 + length > e2end)
- goto err;
- switch (type) {
- case EXTRA2_TABLEDEF_VERSION:
- if (tabledef_version.str) // see init_from_sql_statement_string()
- {
- if (length != tabledef_version.length ||
- memcmp(extra2, tabledef_version.str, length))
- goto err;
- }
- else
- {
- tabledef_version.length= length;
- tabledef_version.str= (uchar*)memdup_root(&mem_root, extra2, length);
- if (!tabledef_version.str)
- goto err;
- }
- break;
- case EXTRA2_ENGINE_TABLEOPTS:
- if (options)
- goto err;
- /* remember but delay parsing until we have read fields and keys */
- options= extra2;
- options_len= length;
- break;
- case EXTRA2_DEFAULT_PART_ENGINE:
+ if (read_extra2(frm_image, len, &extra2))
+ goto err;
+
+ tabledef_version.length= extra2.version.length;
+ tabledef_version.str= (uchar*)memdup_root(&mem_root, extra2.version.str,
+ extra2.version.length);
+ if (!tabledef_version.str)
+ goto err;
+
+ /* remember but delay parsing until we have read fields and keys */
+ options= extra2.options;
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
- {
- LEX_CSTRING name= { (char*)extra2, length };
- share->default_part_plugin= ha_resolve_by_name(NULL, &name, false);
- if (!share->default_part_plugin)
- goto err;
- }
-#endif
- break;
- case EXTRA2_GIS:
-#ifdef HAVE_SPATIAL
- {
- if (gis_options)
- goto err;
- gis_options= extra2;
- gis_options_len= length;
- }
-#endif /*HAVE_SPATIAL*/
- break;
- case EXTRA2_PERIOD_FOR_SYSTEM_TIME:
- if (system_period || length != 2 * sizeof(uint16))
- goto err;
- system_period = extra2;
- break;
- case EXTRA2_FIELD_FLAGS:
- if (extra2_field_flags)
- goto err;
- extra2_field_flags= extra2;
- extra2_field_flags_length= length;
- break;
- default:
- /* abort frm parsing if it's an unknown but important extra2 value */
- if (type >= EXTRA2_ENGINE_IMPORTANT)
- goto err;
- }
- extra2+= length;
- }
- if (extra2 != e2end)
+ if (extra2.engine)
+ {
+ share->default_part_plugin= ha_resolve_by_name(NULL, &extra2.engine, false);
+ if (!share->default_part_plugin)
goto err;
}
+#endif
if (frm_length < FRM_HEADER_SIZE + len ||
!(pos= uint4korr(frm_image + FRM_HEADER_SIZE + len)))
@@ -1549,7 +1864,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
share->set_use_ext_keys_flag(plugin_hton(se_plugin)->flags & HTON_SUPPORTS_EXTENDED_KEYS);
if (create_key_infos(disk_buff + 6, frm_image_end, keys, keyinfo,
- new_frm_ver, ext_key_parts,
+ new_frm_ver, &ext_key_parts,
share, len, &first_keyinfo, &keynames))
goto err;
@@ -1631,23 +1946,26 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS_legacy)
{
- if (options)
+ if (options.str)
goto err;
- options_len= uint4korr(next_chunk);
- options= next_chunk + 4;
- next_chunk+= options_len + 4;
+ options.length= uint4korr(next_chunk);
+ options.str= next_chunk + 4;
+ next_chunk+= options.length + 4;
}
DBUG_ASSERT(next_chunk <= buff_end);
}
else
{
if (create_key_infos(disk_buff + 6, frm_image_end, keys, keyinfo,
- new_frm_ver, ext_key_parts,
+ new_frm_ver, &ext_key_parts,
share, len, &first_keyinfo, &keynames))
goto err;
}
-
share->key_block_size= uint2korr(frm_image+62);
+ keyinfo= share->key_info;
+ for (uint i= 0; i < share->keys; i++, keyinfo++)
+ if (keyinfo->algorithm == HA_KEY_ALG_LONG_HASH)
+ hash_fields++;
if (share->db_plugin && !plugin_equals(share->db_plugin, se_plugin))
goto err; // wrong engine (someone changed the frm under our feet?)
@@ -1663,7 +1981,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
disk_buff= frm_image + pos + FRM_FORMINFO_SIZE;
share->fields= uint2korr(forminfo+258);
- if (extra2_field_flags && extra2_field_flags_length != share->fields)
+ if (extra2.field_flags.str && extra2.field_flags.length != share->fields)
goto err;
pos= uint2korr(forminfo+260); /* Length of all screens */
n_length= uint2korr(forminfo+268);
@@ -1809,106 +2127,59 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
}
/* Set system versioning information. */
- if (system_period == NULL)
+ vers.name= Lex_ident(STRING_WITH_LEN("SYSTEM_TIME"));
+ if (extra2.system_period.str == NULL)
{
versioned= VERS_UNDEFINED;
- row_start_field= 0;
- row_end_field= 0;
+ vers.start_fieldno= 0;
+ vers.end_fieldno= 0;
}
else
{
DBUG_PRINT("info", ("Setting system versioning informations"));
- uint16 row_start= uint2korr(system_period);
- uint16 row_end= uint2korr(system_period + sizeof(uint16));
- if (row_start >= share->fields || row_end >= share->fields)
+ if (init_period_from_extra2(&vers, extra2.system_period.str,
+ extra2.system_period.str + extra2.system_period.length))
goto err;
- DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end));
+ DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]",
+ vers.start_fieldno, vers.end_fieldno));
versioned= VERS_TIMESTAMP;
vers_can_native= handler_file->vers_can_native(thd);
- row_start_field= row_start;
- row_end_field= row_end;
status_var_increment(thd->status_var.feature_system_versioning);
} // if (system_period == NULL)
+ if (extra2.application_period.str)
+ {
+ const uchar *pos= extra2.application_period.str;
+ const uchar *end= pos + extra2.application_period.length;
+ period.name.length= extra2_read_len(&pos, end);
+ period.name.str= strmake_root(&mem_root, (char*)pos, period.name.length);
+ pos+= period.name.length;
+
+ period.constr_name.length= extra2_read_len(&pos, end);
+ period.constr_name.str= strmake_root(&mem_root, (char*)pos,
+ period.constr_name.length);
+ pos+= period.constr_name.length;
+
+ if (init_period_from_extra2(&period, pos, end))
+ goto err;
+ status_var_increment(thd->status_var.feature_application_time_periods);
+ }
+
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;
+ uint interval_nr= 0, recpos;
LEX_CSTRING comment;
LEX_CSTRING name;
Virtual_column_info *vcol_info= 0;
- uint gis_length, gis_decimals, srid= 0;
- Field::utype unireg_check;
const Type_handler *handler;
uint32 flags= 0;
+ Column_definition_attributes attr;
if (new_frm_ver >= 3)
{
/* new frm file in 4.1 */
- field_length= uint2korr(strpos+3);
recpos= uint3korr(strpos+5);
- pack_flag= uint2korr(strpos+8);
- unireg_type= (uint) strpos[10];
- interval_nr= (uint) strpos[12];
uint comment_length=uint2korr(strpos+15);
- field_type=(enum_field_types) (uint) strpos[13];
-
- /* charset and geometry_type share the same byte in frm */
- if (field_type == MYSQL_TYPE_GEOMETRY)
- {
-#ifdef HAVE_SPATIAL
- uint gis_opt_read;
- Field_geom::storage_type st_type;
- geom_type= (Field::geometry_type) strpos[14];
- charset= &my_charset_bin;
- gis_opt_read= gis_field_options_read(gis_options, gis_options_len,
- &st_type, &gis_length, &gis_decimals, &srid);
- gis_options+= gis_opt_read;
- gis_options_len-= gis_opt_read;
-#else
- goto err;
-#endif
- }
- else
- {
- uint cs_org= strpos[14] + (((uint) strpos[11]) << 8);
- uint cs_new= upgrade_collation(share->mysql_version, cs_org);
- if (cs_org != cs_new)
- share->incompatible_version|= HA_CREATE_USED_CHARSET;
- if (!cs_new)
- charset= &my_charset_bin;
- else if (!(charset= get_charset(cs_new, MYF(0))))
- {
- const char *csname= get_charset_name((uint) cs_new);
- char tmp[10];
- if (!csname || csname[0] =='?')
- {
- my_snprintf(tmp, sizeof(tmp), "#%u", cs_new);
- csname= tmp;
- }
- my_printf_error(ER_UNKNOWN_COLLATION,
- "Unknown collation '%s' in table '%-.64s' definition",
- MYF(0), csname, share->table_name.str);
- goto err;
- }
- }
-
- if ((uchar)field_type == (uchar)MYSQL_TYPE_VIRTUAL)
- {
- if (!interval_nr) // Expect non-null expression
- goto err;
- /*
- MariaDB version 10.0 version.
- 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)
{
@@ -1922,33 +2193,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
comment_pos+= comment_length;
}
- if (unireg_type & MYSQL57_GENERATED_FIELD)
+ if ((uchar) strpos[13] == (uchar) MYSQL_TYPE_VIRTUAL)
{
- unireg_type&= MYSQL57_GENERATED_FIELD;
-
/*
- MySQL 5.7 generated fields
-
- byte 1 = 1
- byte 2,3 = expr length
- byte 4 = stored_in_db
- byte 5.. = expr
+ MariaDB version 10.0 version.
+ The interval_id byte in the .frm file stores the length of the
+ expression statement for a virtual column.
*/
- if ((uint)(vcol_screen_pos)[0] != 1)
- goto err;
- vcol_info= new (&share->mem_root) Virtual_column_info();
- vcol_info_length= uint2korr(vcol_screen_pos + 1);
- if (!vcol_info_length) // Expect non-empty expression
+ uint vcol_info_length= (uint) strpos[12];
+
+ if (!vcol_info_length) // Expect non-null expression
goto err;
- vcol_info->stored_in_db= vcol_screen_pos[3];
- vcol_info->utf8= 0;
- vcol_screen_pos+= vcol_info_length + MYSQL57_GCOL_HEADER_SIZE;;
- share->virtual_fields++;
- vcol_info_length= 0;
- }
- if (vcol_info_length)
- {
+ attr.frm_unpack_basic(strpos);
+ if (attr.frm_unpack_charset(share, strpos))
+ goto err;
/*
Old virtual field information before 10.2
@@ -1962,7 +2221,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
vcol_info= new (&share->mem_root) Virtual_column_info();
bool opt_interval_id= (uint)vcol_screen_pos[0] == 2;
- field_type= (enum_field_types) (uchar) vcol_screen_pos[1];
+ enum_field_types ftype= (enum_field_types) (uchar) vcol_screen_pos[1];
+ if (!(handler= Type_handler::get_handler_by_real_type(ftype)))
+ goto err;
if (opt_interval_id)
interval_nr= (uint)vcol_screen_pos[3];
else if ((uint)vcol_screen_pos[0] != 1)
@@ -1970,26 +2231,64 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
bool stored= vcol_screen_pos[2] & 1;
vcol_info->stored_in_db= stored;
vcol_info->set_vcol_type(stored ? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL);
- vcol_expr_length= vcol_info_length -
- (uint)(FRM_VCOL_OLD_HEADER_SIZE(opt_interval_id));
+ uint vcol_expr_length= vcol_info_length -
+ (uint)(FRM_VCOL_OLD_HEADER_SIZE(opt_interval_id));
vcol_info->utf8= 0; // before 10.2.1 the charset was unknown
int2store(vcol_screen_pos+1, vcol_expr_length); // for parse_vcol_defs()
vcol_screen_pos+= vcol_info_length;
share->virtual_fields++;
}
+ else
+ {
+ interval_nr= (uint) strpos[12];
+ enum_field_types field_type= (enum_field_types) strpos[13];
+ if (!(handler= Type_handler::get_handler_by_real_type(field_type)))
+ goto err; // Not supported field type
+ if (handler->Column_definition_attributes_frm_unpack(&attr, share,
+ strpos,
+ &extra2.gis))
+ goto err;
+ }
+
+ if (((uint) strpos[10]) & MYSQL57_GENERATED_FIELD)
+ {
+ attr.unireg_check= Field::NONE;
+
+ /*
+ MySQL 5.7 generated fields
+
+ byte 1 = 1
+ byte 2,3 = expr length
+ byte 4 = stored_in_db
+ byte 5.. = expr
+ */
+ if ((uint)(vcol_screen_pos)[0] != 1)
+ goto err;
+ vcol_info= new (&share->mem_root) Virtual_column_info();
+ uint vcol_info_length= uint2korr(vcol_screen_pos + 1);
+ if (!vcol_info_length) // Expect non-empty expression
+ goto err;
+ vcol_info->stored_in_db= vcol_screen_pos[3];
+ vcol_info->utf8= 0;
+ vcol_screen_pos+= vcol_info_length + MYSQL57_GCOL_HEADER_SIZE;;
+ share->virtual_fields++;
+ }
}
else
{
- field_length= (uint) strpos[3];
+ attr.length= (uint) strpos[3];
recpos= uint2korr(strpos+4),
- pack_flag= uint2korr(strpos+6);
- pack_flag&= ~FIELDFLAG_NO_DEFAULT; // Safety for old files
- unireg_type= (uint) strpos[8];
+ attr.pack_flag= uint2korr(strpos+6);
+ attr.pack_flag&= ~FIELDFLAG_NO_DEFAULT; // Safety for old files
+ attr.unireg_check= (Field::utype) MTYP_TYPENR((uint) strpos[8]);
interval_nr= (uint) strpos[10];
/* old frm file */
- field_type= (enum_field_types) f_packtype(pack_flag);
- if (f_is_binary(pack_flag))
+ enum_field_types ftype= (enum_field_types) f_packtype(attr.pack_flag);
+ if (!(handler= Type_handler::get_handler_by_real_type(ftype)))
+ goto err; // Not supported field type
+
+ if (f_is_binary(attr.pack_flag))
{
/*
Try to choose the best 4.1 type:
@@ -1997,26 +2296,26 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
try to find a binary collation for character set.
- for other types (e.g. BLOB) just use my_charset_bin.
*/
- if (!f_is_blob(pack_flag))
+ if (!f_is_blob(attr.pack_flag))
{
// 3.23 or 4.0 string
- if (!(charset= get_charset_by_csname(share->table_charset->csname,
- MY_CS_BINSORT, MYF(0))))
- charset= &my_charset_bin;
+ if (!(attr.charset= get_charset_by_csname(share->table_charset->csname,
+ MY_CS_BINSORT, MYF(0))))
+ attr.charset= &my_charset_bin;
}
- else
- charset= &my_charset_bin;
}
else
- charset= share->table_charset;
+ attr.charset= share->table_charset;
bzero((char*) &comment, sizeof(comment));
+ if ((!(handler= old_frm_type_handler(attr.pack_flag, interval_nr))))
+ goto err; // Not supported field type
}
/* Remove >32 decimals from old files */
if (share->mysql_version < 100200)
- pack_flag&= ~FIELDFLAG_LONG_DECIMAL;
+ attr.pack_flag&= ~FIELDFLAG_LONG_DECIMAL;
- if (interval_nr && charset->mbminlen > 1)
+ if (interval_nr && attr.charset->mbminlen > 1)
{
/* Unescape UCS2 intervals from HEX notation */
TYPELIB *interval= share->intervals + interval_nr - 1;
@@ -2024,17 +2323,18 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
}
#ifndef TO_BE_DELETED_ON_PRODUCTION
- if (field_type == MYSQL_TYPE_NEWDECIMAL && !share->mysql_version)
+ if (handler->real_field_type() == MYSQL_TYPE_NEWDECIMAL &&
+ !share->mysql_version)
{
/*
Fix pack length of old decimal values from 5.0.3 -> 5.0.4
The difference is that in the old version we stored precision
in the .frm table while we now store the display_length
*/
- uint decimals= f_decimals(pack_flag);
- field_length= my_decimal_precision_to_length(field_length,
- decimals,
- f_is_dec(pack_flag) == 0);
+ uint decimals= f_decimals(attr.pack_flag);
+ attr.length=
+ my_decimal_precision_to_length((uint) attr.length, decimals,
+ f_is_dec(attr.pack_flag) == 0);
sql_print_error("Found incompatible DECIMAL field '%s' in %s; "
"Please do \"ALTER TABLE '%s' FORCE\" to fix it!",
share->fieldnames.type_names[i], share->table_name.str,
@@ -2058,14 +2358,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (versioned)
{
- if (i == row_start_field)
+ if (i == vers.start_fieldno)
flags|= VERS_SYS_START_FLAG;
- else if (i == row_end_field)
+ else if (i == vers.end_fieldno)
flags|= VERS_SYS_END_FLAG;
if (flags & VERS_SYSTEM_FIELD)
{
- switch (field_type)
+ switch (handler->real_field_type())
{
case MYSQL_TYPE_TIMESTAMP2:
break;
@@ -2086,22 +2386,17 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
}
/* Convert pre-10.2.2 timestamps to use Field::default_value */
- unireg_check= (Field::utype) MTYP_TYPENR(unireg_type);
name.str= fieldnames.type_names[i];
name.length= strlen(name.str);
- if (!(handler= Type_handler::get_handler_by_real_type(field_type)))
- goto err; // Not supported field type
+ attr.interval= interval_nr ? share->intervals + interval_nr - 1 : NULL;
+ Record_addr addr(record + recpos, null_pos, null_bit_pos);
*field_ptr= reg_field=
- make_field(share, &share->mem_root, record+recpos, (uint32) field_length,
- null_pos, null_bit_pos, pack_flag, handler, charset,
- geom_type, srid, unireg_check,
- (interval_nr ? share->intervals+interval_nr-1 : NULL),
- &name, flags);
+ attr.make_field(share, &share->mem_root, &addr, handler, &name, flags);
if (!reg_field) // Not supported field type
goto err;
- if (unireg_check == Field::TIMESTAMP_DNUN_FIELD ||
- unireg_check == Field::TIMESTAMP_DN_FIELD)
+ if (attr.unireg_check == Field::TIMESTAMP_DNUN_FIELD ||
+ attr.unireg_check == Field::TIMESTAMP_DN_FIELD)
{
reg_field->default_value= new (&share->mem_root) Virtual_column_info();
reg_field->default_value->set_vcol_type(VCOL_DEFAULT);
@@ -2113,9 +2408,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
reg_field->comment=comment;
reg_field->vcol_info= vcol_info;
reg_field->flags|= flags;
- if (extra2_field_flags)
+ if (extra2.field_flags.str)
{
- uchar flags= *extra2_field_flags++;
+ uchar flags= *extra2.field_flags.str++;
if (flags & VERS_OPTIMIZED_UPDATE)
reg_field->flags|= VERS_UPDATE_UNVERSIONED_FLAG;
@@ -2125,10 +2420,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
status_var_increment(thd->status_var.feature_invisible_columns);
if (!reg_field->invisible)
share->visible_fields++;
- if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag))
+ if (handler->real_field_type() == MYSQL_TYPE_BIT &&
+ !f_bit_as_char(attr.pack_flag))
{
null_bits_are_used= 1;
- if ((null_bit_pos+= field_length & 7) > 7)
+ if ((null_bit_pos+= (uint) (attr.length & 7)) > 7)
{
null_pos++;
null_bit_pos-= 8;
@@ -2151,7 +2447,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
}
}
- if (f_no_default(pack_flag))
+ if (f_no_default(attr.pack_flag))
reg_field->flags|= NO_DEFAULT_VALUE_FLAG;
if (reg_field->unireg_check == Field::NEXT_NUMBER)
@@ -2188,6 +2484,37 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
/* Fix key->name and key_part->field */
if (key_parts)
{
+ keyinfo= share->key_info;
+ uint hash_field_used_no= share->fields - hash_fields;
+ KEY_PART_INFO *hash_keypart;
+ Field *hash_field;
+ uint offset= share->reclength - HA_HASH_FIELD_LENGTH * hash_fields;
+ for (uint i= 0; i < share->keys; i++, keyinfo++)
+ {
+ /* We need set value in hash key_part */
+ if (keyinfo->algorithm == HA_KEY_ALG_LONG_HASH)
+ {
+ share->long_unique_table= 1;
+ hash_keypart= keyinfo->key_part + keyinfo->user_defined_key_parts;
+ hash_keypart->length= HA_HASH_KEY_LENGTH_WITHOUT_NULL;
+ hash_keypart->store_length= hash_keypart->length;
+ hash_keypart->type= HA_KEYTYPE_ULONGLONG;
+ hash_keypart->key_part_flag= 0;
+ hash_keypart->key_type= 32834;
+ /* Last n fields are unique_index_hash fields*/
+ hash_keypart->offset= offset;
+ hash_keypart->fieldnr= hash_field_used_no + 1;
+ hash_field= share->field[hash_field_used_no];
+ hash_field->flags|= LONG_UNIQUE_HASH_FIELD;//Used in parse_vcol_defs
+ keyinfo->flags|= HA_NOSAME;
+ share->virtual_fields++;
+ share->stored_fields--;
+ if (record + share->stored_rec_length >= hash_field->ptr)
+ share->stored_rec_length= (ulong)(hash_field->ptr - record - 1);
+ hash_field_used_no++;
+ offset+= HA_HASH_FIELD_LENGTH;
+ }
+ }
uint add_first_key_parts= 0;
longlong ha_option= handler_file->ha_table_flags();
keyinfo= share->key_info;
@@ -2195,7 +2522,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
primary_key_name) ? MAX_KEY : 0;
KEY* key_first_info= NULL;
- if (primary_key >= MAX_KEY && keyinfo->flags & HA_NOSAME)
+ if (primary_key >= MAX_KEY && keyinfo->flags & HA_NOSAME &&
+ keyinfo->algorithm != HA_KEY_ALG_LONG_HASH)
{
/*
If the UNIQUE key doesn't have NULL columns and is not a part key
@@ -2230,7 +2558,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
}
if (share->use_ext_keys)
- {
+ {
if (primary_key >= MAX_KEY)
{
add_first_key_parts= 0;
@@ -2282,6 +2610,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
uint length_bytes= 0, len_null_byte= 0, ext_key_length= 0;
Field *field;
+ if ((keyinfo-1)->algorithm == HA_KEY_ALG_LONG_HASH)
+ new_key_part++; // reserved for the hash value
+
/*
Do not extend the key that contains a component
defined over the beginning of a field.
@@ -2289,22 +2620,22 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
for (i= 0; i < keyinfo->user_defined_key_parts; i++)
{
uint fieldnr= keyinfo->key_part[i].fieldnr;
- field= share->field[keyinfo->key_part[i].fieldnr-1];
+ field= share->field[fieldnr-1];
if (field->null_ptr)
len_null_byte= HA_KEY_NULL_LENGTH;
- if (field->type() == MYSQL_TYPE_BLOB ||
+ if ((field->type() == MYSQL_TYPE_BLOB ||
field->real_type() == MYSQL_TYPE_VARCHAR ||
- field->type() == MYSQL_TYPE_GEOMETRY)
+ field->type() == MYSQL_TYPE_GEOMETRY) &&
+ keyinfo->algorithm != HA_KEY_ALG_LONG_HASH )
{
length_bytes= HA_KEY_BLOB_LENGTH;
}
ext_key_length+= keyinfo->key_part[i].length + len_null_byte
+ length_bytes;
- if (share->field[fieldnr-1]->key_length() !=
- keyinfo->key_part[i].length)
+ if (field->key_length() != keyinfo->key_part[i].length)
{
add_keyparts_for_this_key= 0;
break;
@@ -2361,6 +2692,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
key_part= keyinfo->key_part;
uint key_parts= share->use_ext_keys ? keyinfo->ext_key_parts :
keyinfo->user_defined_key_parts;
+ if (keyinfo->algorithm == HA_KEY_ALG_LONG_HASH)
+ key_parts++;
for (i=0; i < key_parts; key_part++, i++)
{
Field *field;
@@ -2374,8 +2707,10 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
field= key_part->field= share->field[key_part->fieldnr-1];
key_part->type= field->key_type();
+
if (field->invisible > INVISIBLE_USER && !field->vers_sys_field())
- keyinfo->flags |= HA_INVISIBLE_KEY;
+ if (keyinfo->algorithm != HA_KEY_ALG_LONG_HASH)
+ keyinfo->flags |= HA_INVISIBLE_KEY;
if (field->null_ptr)
{
key_part->null_offset=(uint) ((uchar*) field->null_ptr -
@@ -2383,12 +2718,6 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
key_part->null_bit= field->null_bit;
key_part->store_length+=HA_KEY_NULL_LENGTH;
keyinfo->flags|=HA_NULL_PART_KEY;
-
- /*
- This branch is executed only for user defined key parts of the
- secondary indexes.
- */
- DBUG_ASSERT(i < keyinfo->user_defined_key_parts);
keyinfo->key_length+= HA_KEY_NULL_LENGTH;
}
if (field->type() == MYSQL_TYPE_BLOB ||
@@ -2408,13 +2737,15 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
key_part->key_part_flag|= HA_BIT_PART;
if (i == 0 && key != primary_key)
- field->flags |= (((keyinfo->flags & HA_NOSAME) &&
+ field->flags |= (((keyinfo->flags & HA_NOSAME ||
+ keyinfo->algorithm == HA_KEY_ALG_LONG_HASH) &&
(keyinfo->user_defined_key_parts == 1)) ?
UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG);
if (i == 0)
field->key_start.set_bit(key);
if (field->key_length() == key_part->length &&
- !(field->flags & BLOB_FLAG))
+ !(field->flags & BLOB_FLAG) &&
+ keyinfo->algorithm != HA_KEY_ALG_LONG_HASH)
{
if (handler_file->index_flags(key, i, 0) & HA_KEYREAD_ONLY)
{
@@ -2449,7 +2780,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (field->key_length() != key_part->length)
{
#ifndef TO_BE_DELETED_ON_PRODUCTION
- if (field->type() == MYSQL_TYPE_NEWDECIMAL)
+ if (field->type() == MYSQL_TYPE_NEWDECIMAL &&
+ keyinfo->algorithm != HA_KEY_ALG_LONG_HASH)
{
/*
Fix a fatal error in decimal key handling that causes crashes
@@ -2488,7 +2820,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (!(key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART |
HA_BIT_PART)) &&
key_part->type != HA_KEYTYPE_FLOAT &&
- key_part->type != HA_KEYTYPE_DOUBLE)
+ key_part->type == HA_KEYTYPE_DOUBLE &&
+ keyinfo->algorithm != HA_KEY_ALG_LONG_HASH)
key_part->key_part_flag|= HA_CAN_MEMCMP;
}
keyinfo->usable_key_parts= usable_parts; // Filesort
@@ -2535,6 +2868,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
null_length, 255);
}
+ set_overlapped_keys();
+
/* Handle virtual expressions */
if (vcol_screen_length && share->frm_version >= FRM_VER_EXPRESSSIONS)
{
@@ -2627,10 +2962,10 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
(uint) (share->table_check_constraints -
share->field_check_constraints));
- if (options)
+ if (options.str)
{
- DBUG_ASSERT(options_len);
- if (engine_table_options_frm_read(options, options_len, share))
+ DBUG_ASSERT(options.length);
+ if (engine_table_options_frm_read(options.str, options.length, share))
goto err;
}
if (parse_engine_table_options(thd, handler_file->partition_ht(), share))
@@ -2749,7 +3084,7 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine,
if (lex->create_info.like())
return 1;
// ... create select
- if (lex->select_lex.item_list.elements)
+ if (lex->first_select_lex()->item_list.elements)
return 1;
// ... temporary
if (create_info->tmp_table())
@@ -2849,7 +3184,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
thd->lex->create_info.tabledef_version= tabledef_version;
promote_first_timestamp_column(&thd->lex->alter_info.create_list);
- file= mysql_create_frm_image(thd, &db, &table_name,
+ file= mysql_create_frm_image(thd, db, table_name,
&thd->lex->create_info, &thd->lex->alter_info,
C_ORDINARY_CREATE, &unused1, &unused2, &frm);
error|= file == 0;
@@ -2951,7 +3286,7 @@ bool fix_session_vcol_expr(THD *thd, Virtual_column_info *vcol)
DBUG_RETURN(0);
vcol->expr->walk(&Item::cleanup_excluding_fields_processor, 0, 0);
- DBUG_ASSERT(!vcol->expr->fixed);
+ DBUG_ASSERT(!vcol->expr->is_fixed());
DBUG_RETURN(fix_vcol_expr(thd, vcol));
}
@@ -3006,7 +3341,7 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
DBUG_PRINT("info", ("vcol: %p", vcol));
DBUG_ASSERT(func_expr);
- if (func_expr->fixed)
+ if (func_expr->is_fixed())
DBUG_RETURN(0); // nothing to do
if (fix_vcol_expr(thd, vcol))
@@ -3050,7 +3385,7 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
of the statement because the field item does not have a field
pointer at that time
*/
- myf warn= table->s->frm_version < FRM_VER_EXPRESSSIONS ? ME_JUST_WARNING : 0;
+ myf warn= table->s->frm_version < FRM_VER_EXPRESSSIONS ? ME_WARNING : 0;
my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(warn),
"AUTO_INCREMENT", vcol->get_vcol_type_name(), res.name);
if (!warn)
@@ -3166,6 +3501,86 @@ static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol,
return res;
}
+#ifndef DBUG_OFF
+static void print_long_unique_table(TABLE *table)
+{
+ char buff[256];
+ String str;
+ KEY *key_info_table, *key_info_share;
+ KEY_PART_INFO *key_part;
+ Field *field;
+ my_snprintf(buff, sizeof(buff), "Printing Table state, It will print table fields,"
+ " fields->offset,field->null_bit, field->null_pos and key_info ... \n"
+ "\nPrinting Table keyinfo\n");
+ str.append(buff, strlen(buff));
+ my_snprintf(buff, sizeof(buff), "\ntable->s->reclength %d\n"
+ "table->s->fields %d\n",
+ table->s->reclength, table->s->fields);
+ str.append(buff, strlen(buff));
+ for (uint i= 0; i < table->s->keys; i++)
+ {
+ key_info_table= table->key_info + i;
+ key_info_share= table->s->key_info + i;
+ my_snprintf(buff, sizeof(buff), "\ntable->key_info[%d] user_defined_key_parts = %d\n"
+ "table->key_info[%d] algorithm == HA_KEY_ALG_LONG_HASH = %d\n"
+ "table->key_info[%d] flags & HA_NOSAME = %d\n",
+ i, key_info_table->user_defined_key_parts,
+ i, key_info_table->algorithm == HA_KEY_ALG_LONG_HASH,
+ i, key_info_table->flags & HA_NOSAME);
+ str.append(buff, strlen(buff));
+ my_snprintf(buff, sizeof(buff), "\ntable->s->key_info[%d] user_defined_key_parts = %d\n"
+ "table->s->key_info[%d] algorithm == HA_KEY_ALG_LONG_HASH = %d\n"
+ "table->s->key_info[%d] flags & HA_NOSAME = %d\n",
+ i, key_info_share->user_defined_key_parts,
+ i, key_info_share->algorithm == HA_KEY_ALG_LONG_HASH,
+ i, key_info_share->flags & HA_NOSAME);
+ str.append(buff, strlen(buff));
+ key_part = key_info_table->key_part;
+ my_snprintf(buff, sizeof(buff), "\nPrinting table->key_info[%d].key_part[0] info\n"
+ "key_part->offset = %d\n"
+ "key_part->field_name = %s\n"
+ "key_part->length = %d\n"
+ "key_part->null_bit = %d\n"
+ "key_part->null_offset = %d\n",
+ i, key_part->offset, key_part->field->field_name.str, key_part->length,
+ key_part->null_bit, key_part->null_offset);
+ str.append(buff, strlen(buff));
+
+ for (uint j= 0; j < key_info_share->user_defined_key_parts; j++)
+ {
+ key_part= key_info_share->key_part + j;
+ my_snprintf(buff, sizeof(buff), "\nPrinting share->key_info[%d].key_part[%d] info\n"
+ "key_part->offset = %d\n"
+ "key_part->field_name = %s\n"
+ "key_part->length = %d\n"
+ "key_part->null_bit = %d\n"
+ "key_part->null_offset = %d\n",
+ i,j,key_part->offset, key_part->field->field_name.str, key_part->length,
+ key_part->null_bit, key_part->null_offset);
+ str.append(buff, strlen(buff));
+ }
+ }
+ my_snprintf(buff, sizeof(buff), "\nPrinting table->fields\n");
+ str.append(buff, strlen(buff));
+ for(uint i= 0; i < table->s->fields; i++)
+ {
+ field= table->field[i];
+ my_snprintf(buff, sizeof(buff), "\ntable->field[%d]->field_name %s\n"
+ "table->field[%d]->offset = %d\n"
+ "table->field[%d]->field_length = %d\n"
+ "table->field[%d]->null_pos wrt to record 0 = %d\n"
+ "table->field[%d]->null_bit_pos = %d\n",
+ i, field->field_name.str,
+ i, field->ptr- table->record[0],
+ i, field->pack_length(),
+ i, field->null_bit ? field->null_ptr - table->record[0] : -1,
+ i, field->null_bit);
+ str.append(buff, strlen(buff));
+ }
+ (*error_handler_hook)(1, str.ptr(), ME_NOTE);
+}
+#endif
+
/*
Open a table based on a TABLE_SHARE
@@ -3236,10 +3651,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
goto err;
outparam->alias.set(tmp_alias, alias->length, table_alias_charset);
- outparam->quick_keys.init();
- outparam->covering_keys.init();
- outparam->intersect_keys.init();
- outparam->keys_in_use_for_query.init();
/* Allocate handler */
outparam->file= 0;
@@ -3276,7 +3687,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
if (prgflag & (READ_ALL + EXTRA_RECORD))
{
records++;
- if (share->versioned)
+ if (share->versioned || share->period.name)
records++;
}
@@ -3360,10 +3771,14 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
key_part_end= key_part + (share->use_ext_keys ? key_info->ext_key_parts :
key_info->user_defined_key_parts) ;
+ if (key_info->algorithm == HA_KEY_ALG_LONG_HASH)
+ {
+ key_part_end++;
+ key_info->flags&= ~HA_NOSAME;
+ }
for ( ; key_part < key_part_end; key_part++)
{
Field *field= key_part->field= outparam->field[key_part->fieldnr - 1];
-
if (field->key_length() != key_part->length &&
!(field->flags & BLOB_FLAG))
{
@@ -3373,7 +3788,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
*/
field= key_part->field=field->make_new_field(&outparam->mem_root,
outparam, 0);
- field->field_length= key_part->length;
+ const_cast<uint32_t&>(field->field_length)= key_part->length;
}
}
if (!share->use_ext_keys)
@@ -3534,17 +3949,6 @@ partititon_err:
(my_bitmap_map*) bitmaps, share->fields, FALSE);
bitmaps+= bitmap_size;
- /* Don't allocate vcol_bitmap if we don't need it */
- if (share->virtual_fields)
- {
- if (!(outparam->def_vcol_set= (MY_BITMAP*)
- alloc_root(&outparam->mem_root, sizeof(*outparam->def_vcol_set))))
- goto err;
- my_bitmap_init(outparam->def_vcol_set,
- (my_bitmap_map*) bitmaps, share->fields, FALSE);
- bitmaps+= bitmap_size;
- }
-
my_bitmap_init(&outparam->has_value_set,
(my_bitmap_map*) bitmaps, share->fields, FALSE);
bitmaps+= bitmap_size;
@@ -3625,6 +4029,8 @@ partititon_err:
share->no_replicate= TRUE;
if (outparam->file->table_cache_type() & HA_CACHE_TBL_NOCACHE)
share->not_usable_by_query_cache= TRUE;
+ if (outparam->file->ha_table_flags() & HA_CAN_ONLINE_BACKUPS)
+ share->online_backup= 1;
}
if (share->no_replicate || !binlog_filter->db_ok(share->db.str))
@@ -3635,6 +4041,8 @@ partititon_err:
thd->status_var.opened_tables++;
thd->lex->context_analysis_only= save_context_analysis_only;
+ DBUG_EXECUTE_IF("print_long_unique_internal_state",
+ print_long_unique_table(outparam););
DBUG_RETURN (OPEN_FRM_OK);
err:
@@ -3747,7 +4155,7 @@ void open_table_error(TABLE_SHARE *share, enum open_frm_error error,
int db_errno)
{
char buff[FN_REFLEN];
- const myf errortype= ME_ERROR+ME_WAITTANG; // Write fatals error to log
+ const myf errortype= ME_ERROR_LOG; // Write fatals error to log
DBUG_ENTER("open_table_error");
DBUG_PRINT("info", ("error: %d db_errno: %d", error, db_errno));
@@ -3974,6 +4382,8 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
{
size_t key_comment_total_bytes= 0;
uint i;
+ uchar frm_format= create_info->expression_length ? FRM_VER_EXPRESSSIONS
+ : FRM_VER_TRUE_VARCHAR;
DBUG_ENTER("prepare_frm_header");
/* Fix this when we have new .frm files; Current limit is 4G rows (TODO) */
@@ -3982,17 +4392,6 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
if (create_info->min_rows > UINT_MAX32)
create_info->min_rows= UINT_MAX32;
- size_t key_length, tmp_key_length, tmp, csid;
- bzero((char*) fileinfo, FRM_HEADER_SIZE);
- /* header */
- fileinfo[0]=(uchar) 254;
- fileinfo[1]= 1;
- fileinfo[2]= (create_info->expression_length == 0 ? FRM_VER_TRUE_VARCHAR :
- FRM_VER_EXPRESSSIONS);
-
- DBUG_ASSERT(ha_storage_engine_is_enabled(create_info->db_type));
- fileinfo[3]= (uchar) ha_legacy_type(create_info->db_type);
-
/*
Keep in sync with pack_keys() in unireg.cc
For each key:
@@ -4011,8 +4410,20 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
(key_info[i].comment.length > 0));
if (key_info[i].flags & HA_USES_COMMENT)
key_comment_total_bytes += 2 + key_info[i].comment.length;
+ if (key_info[i].algorithm == HA_KEY_ALG_LONG_HASH)
+ frm_format= FRM_VER_EXPRESSSIONS;
}
+ size_t key_length, tmp_key_length, tmp, csid;
+ bzero((char*) fileinfo, FRM_HEADER_SIZE);
+ /* header */
+ fileinfo[0]=(uchar) 254;
+ fileinfo[1]= 1;
+ fileinfo[2]= frm_format;
+
+ DBUG_ASSERT(ha_storage_engine_is_enabled(create_info->db_type));
+ fileinfo[3]= (uchar) ha_legacy_type(create_info->db_type);
+
key_length= keys * (8 + MAX_REF_PARTS * 9 + NAME_LEN + 1) + 16
+ key_comment_total_bytes;
@@ -4068,7 +4479,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
create_info->row_type= share->row_type;
create_info->key_block_size= share->key_block_size;
create_info->default_table_charset= share->table_charset;
- create_info->table_charset= 0;
+ create_info->alter_table_convert_to_charset= 0;
create_info->comment= share->comment;
create_info->transactional= share->transactional;
create_info->page_checksum= share->page_checksum;
@@ -4158,7 +4569,7 @@ uint calculate_key_len(TABLE *table, uint key, const uchar *buf,
/* works only with key prefixes */
DBUG_ASSERT(((keypart_map + 1) & keypart_map) == 0);
- KEY *key_info= table->s->key_info+key;
+ KEY *key_info= table->key_info+key;
KEY_PART_INFO *key_part= key_info->key_part;
KEY_PART_INFO *end_key_part= key_part + table->actual_n_key_parts(key_info);
uint length= 0;
@@ -4761,9 +5172,16 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
fulltext_searched= 0;
file->ft_handler= 0;
reginfo.impossible_range= 0;
+ reginfo.join_tab= NULL;
+ reginfo.not_exists_optimize= FALSE;
created= TRUE;
cond_selectivity= 1.0;
cond_selectivity_sampling_explain= NULL;
+ range_rowid_filter_cost_info_elems= 0;
+ range_rowid_filter_cost_info_ptr= NULL;
+ range_rowid_filter_cost_info= NULL;
+ update_handler= NULL;
+ check_unique_buf= NULL;
vers_write= s->versioned;
quick_condition_rows=0;
no_cache= false;
@@ -5051,7 +5469,7 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds,
if (where)
{
- if (where->fixed)
+ if (where->is_fixed())
where->update_used_tables();
else if (where->fix_fields(thd, &where))
DBUG_RETURN(TRUE);
@@ -5111,13 +5529,13 @@ bool TABLE_LIST::single_table_updatable()
{
if (!updatable)
return false;
- if (view && view->select_lex.table_list.elements == 1)
+ if (view && view->first_select_lex()->table_list.elements == 1)
{
/*
We need to check deeply only single table views. Multi-table views
will be turned to multi-table updates and then checked by leaf tables
*/
- return (((TABLE_LIST *)view->select_lex.table_list.first)->
+ return (((TABLE_LIST *)view->first_select_lex()->table_list.first)->
single_table_updatable());
}
return true;
@@ -5154,7 +5572,8 @@ merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded)
cond= table->on_expr->copy_andor_structure(thd);
if (!table->view)
DBUG_RETURN(cond);
- for (TABLE_LIST *tbl= (TABLE_LIST*)table->view->select_lex.table_list.first;
+ for (TABLE_LIST *tbl=
+ (TABLE_LIST*)table->view->first_select_lex()->table_list.first;
tbl;
tbl= tbl->next_local)
{
@@ -5196,7 +5615,7 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
{
DBUG_ENTER("TABLE_LIST::prep_check_option");
bool is_cascaded= check_opt_type == VIEW_CHECK_CASCADED;
- TABLE_LIST *merge_underlying_list= view->select_lex.get_table_list();
+ TABLE_LIST *merge_underlying_list= view->first_select_lex()->get_table_list();
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
/* see comment of check_opt_type parameter */
@@ -5314,7 +5733,7 @@ TABLE_LIST *TABLE_LIST::find_underlying_table(TABLE *table_to_find)
if (!view)
return 0;
- for (TABLE_LIST *tbl= view->select_lex.get_table_list();
+ for (TABLE_LIST *tbl= view->first_select_lex()->get_table_list();
tbl;
tbl= tbl->next_local)
{
@@ -5376,7 +5795,7 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure)
main_view->db.str);
const char *name_table= (main_view->view ? main_view->view_name.str :
main_view->table_name.str);
- my_error(ER_VIEW_CHECK_FAILED, MYF(ignore_failure ? ME_JUST_WARNING : 0),
+ my_error(ER_VIEW_CHECK_FAILED, MYF(ignore_failure ? ME_WARNING : 0),
name_db, name_table);
return ignore_failure ? VIEW_CHECK_SKIP : VIEW_CHECK_ERROR;
}
@@ -5420,7 +5839,7 @@ int TABLE::verify_constraints(bool ignore_failure)
}
field_error.append((*chk)->name.str);
my_error(ER_CONSTRAINT_FAILED,
- MYF(ignore_failure ? ME_JUST_WARNING : 0), field_error.c_ptr(),
+ MYF(ignore_failure ? ME_WARNING : 0), field_error.c_ptr(),
s->db.str, s->table_name.str);
return ignore_failure ? VIEW_CHECK_SKIP : VIEW_CHECK_ERROR;
}
@@ -5512,7 +5931,8 @@ bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root)
{
DBUG_PRINT("info", ("setting insert_value for view"));
DBUG_ASSERT(is_view_or_derived() && is_merged_derived());
- for (TABLE_LIST *tbl= (TABLE_LIST*)view->select_lex.table_list.first;
+ for (TABLE_LIST *tbl=
+ (TABLE_LIST*)view->first_select_lex()->table_list.first;
tbl;
tbl= tbl->next_local)
if (tbl->set_insert_values(mem_root))
@@ -5679,7 +6099,7 @@ void TABLE_LIST::register_want_access(ulong want_access)
}
if (!view)
return;
- for (TABLE_LIST *tbl= view->select_lex.get_table_list();
+ for (TABLE_LIST *tbl= view->first_select_lex()->get_table_list();
tbl;
tbl= tbl->next_local)
tbl->register_want_access(want_access);
@@ -5813,6 +6233,7 @@ bool TABLE_LIST::prepare_security(THD *thd)
if (prepare_view_security_context(thd))
DBUG_RETURN(TRUE);
thd->security_ctx= find_view_security_context(thd);
+ opt_trace_disable_if_no_security_context_access(thd);
while ((tbl= tb++))
{
DBUG_ASSERT(tbl->referencing_view);
@@ -5871,6 +6292,7 @@ void TABLE_LIST::set_check_materialized()
The subtree should be already excluded
*/
DBUG_ASSERT(!derived->first_select()->first_inner_unit() ||
+ derived->first_select()->first_inner_unit()->with_element ||
derived->first_select()->first_inner_unit()->first_select()->
exclude_from_table_unique_test);
}
@@ -5887,14 +6309,14 @@ TABLE *TABLE_LIST::get_real_join_table()
break;
/* we do not support merging of union yet */
DBUG_ASSERT(tbl->view == NULL ||
- tbl->view->select_lex.next_select() == NULL);
+ tbl->view->first_select_lex()->next_select() == NULL);
DBUG_ASSERT(tbl->derived == NULL ||
tbl->derived->first_select()->next_select() == NULL);
{
List_iterator_fast<TABLE_LIST>
ti(tbl->view != NULL ?
- tbl->view->select_lex.top_join_list :
+ tbl->view->first_select_lex()->top_join_list :
tbl->derived->first_select()->top_join_list);
for (;;)
{
@@ -6065,7 +6487,7 @@ Item *Field_iterator_view::create_item(THD *thd)
Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
LEX_CSTRING *name)
{
- bool save_wrapper= thd->lex->select_lex.no_wrap_view_item;
+ bool save_wrapper= thd->lex->first_select_lex()->no_wrap_view_item;
Item *field= *field_ref;
DBUG_ENTER("create_view_field");
@@ -6076,13 +6498,13 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
('mysql_schema_table' function). So we can return directly the
field. This case happens only for 'show & where' commands.
*/
- DBUG_ASSERT(field && field->fixed);
+ DBUG_ASSERT(field && field->is_fixed());
DBUG_RETURN(field);
}
DBUG_ASSERT(field);
thd->lex->current_select->no_wrap_view_item= TRUE;
- if (!field->fixed)
+ if (!field->is_fixed())
{
if (field->fix_fields(thd, field_ref))
{
@@ -6096,8 +6518,9 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
{
DBUG_RETURN(field);
}
- Name_resolution_context *context= view->view ? &view->view->select_lex.context :
- &thd->lex->select_lex.context;
+ Name_resolution_context *context= (view->view ?
+ &view->view->first_select_lex()->context:
+ &thd->lex->first_select_lex()->context);
Item *item= (new (thd->mem_root)
Item_direct_view_ref(thd, context, field_ref, view->alias.str,
name, view));
@@ -6421,13 +6844,12 @@ void 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);
- if (s->virtual_fields) bitmap_clear_all(table->def_vcol_set);
The code assumes that the bitmaps are allocated after each other, as
guaranteed by open_table_from_share()
*/
bzero((char*) def_read_set.bitmap,
s->column_bitmap_size * (s->virtual_fields ? 3 : 2));
- column_bitmaps_set(&def_read_set, &def_write_set, def_vcol_set);
+ column_bitmaps_set(&def_read_set, &def_write_set);
rpl_write_set= 0; // Safety
}
@@ -6574,13 +6996,8 @@ void TABLE::mark_columns_needed_for_delete()
Field **reg_field;
for (reg_field= field ; *reg_field ; reg_field++)
{
- Field *cur_field= *reg_field;
- if (cur_field->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG))
- {
- bitmap_set_bit(read_set, cur_field->field_index);
- if (cur_field->vcol_info)
- bitmap_set_bit(vcol_set, cur_field->field_index);
- }
+ if ((*reg_field)->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG))
+ mark_column_with_deps(*reg_field);
}
need_signal= true;
}
@@ -6602,9 +7019,9 @@ void TABLE::mark_columns_needed_for_delete()
if (s->versioned)
{
- bitmap_set_bit(read_set, s->vers_start_field()->field_index);
- bitmap_set_bit(read_set, s->vers_end_field()->field_index);
- bitmap_set_bit(write_set, s->vers_end_field()->field_index);
+ bitmap_set_bit(read_set, s->vers.start_fieldno);
+ bitmap_set_bit(read_set, s->vers.end_fieldno);
+ bitmap_set_bit(write_set, s->vers.end_fieldno);
need_signal= true;
}
@@ -6636,7 +7053,6 @@ void TABLE::mark_columns_needed_for_update()
DBUG_ENTER("TABLE::mark_columns_needed_for_update");
bool need_signal= false;
- mark_columns_per_binlog_row_image();
if (triggers)
triggers->mark_fields_used(TRG_EVENT_UPDATE);
@@ -6660,13 +7076,7 @@ void TABLE::mark_columns_needed_for_update()
if (any_written && !all_read)
{
for (KEY_PART_INFO *kp= k->key_part; kp < kpend; kp++)
- {
- int idx= kp->fieldnr - 1;
- if (bitmap_fast_test_and_set(read_set, idx))
- continue;
- if (field[idx]->vcol_info)
- mark_virtual_col(field[idx]);
- }
+ mark_column_with_deps(field[kp->fieldnr - 1]);
}
}
need_signal= true;
@@ -6718,6 +7128,7 @@ void TABLE::mark_columns_needed_for_update()
bitmap_union(read_set, write_set);
need_signal= true;
}
+ mark_columns_per_binlog_row_image();
if (need_signal)
file->column_bitmaps_signal();
DBUG_VOID_RETURN;
@@ -6734,7 +7145,6 @@ void TABLE::mark_columns_needed_for_update()
void TABLE::mark_columns_needed_for_insert()
{
DBUG_ENTER("mark_columns_needed_for_insert");
- mark_columns_per_binlog_row_image();
if (triggers)
{
@@ -6754,6 +7164,7 @@ void TABLE::mark_columns_needed_for_insert()
/* Mark virtual columns for insert */
if (vfield)
mark_virtual_columns_for_write(TRUE);
+ mark_columns_per_binlog_row_image();
if (check_constraints)
mark_check_constraint_columns_for_read();
DBUG_VOID_RETURN;
@@ -6877,49 +7288,12 @@ void TABLE::mark_columns_per_binlog_row_image()
DBUG_ASSERT(FALSE);
}
}
- /*
- We have to ensure that all virtual columns that are part of read set
- are calculated.
- */
- if (vcol_set)
- bitmap_union(vcol_set, read_set);
file->column_bitmaps_signal();
}
DBUG_VOID_RETURN;
}
-/*
- @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 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;
- DBUG_ASSERT(vcol_item);
- vcol_item->walk(&Item::register_field_in_read_map, 1, 0);
- }
- return res;
-}
-
/*
@brief Mark virtual columns for update/insert commands
@@ -6961,13 +7335,13 @@ bool TABLE::mark_virtual_columns_for_write(bool insert_fl
{
tmp_vfield= *vfield_ptr;
if (bitmap_is_set(write_set, tmp_vfield->field_index))
- bitmap_updated|= mark_virtual_col(tmp_vfield);
+ bitmap_updated|= mark_virtual_column_with_deps(tmp_vfield);
else if (tmp_vfield->vcol_info->stored_in_db ||
(tmp_vfield->flags & (PART_KEY_FLAG | FIELD_IN_PART_FUNC_FLAG |
PART_INDIRECT_KEY_FLAG)))
{
bitmap_set_bit(write_set, tmp_vfield->field_index);
- mark_virtual_col(tmp_vfield);
+ mark_virtual_column_with_deps(tmp_vfield);
bitmap_updated= true;
}
}
@@ -7098,8 +7472,6 @@ void TABLE::mark_columns_used_by_virtual_fields(void)
void TABLE::mark_check_constraint_columns_for_read(void)
{
bitmap_union(read_set, s->check_set);
- if (vcol_set)
- bitmap_union(vcol_set, s->check_set);
}
@@ -7873,6 +8245,20 @@ public:
}
};
+
+/*
+ to satisfy marked_for_write_or_computed() Field's assert we temporarily
+ mark field for write before storing the generated value in it
+*/
+#ifdef DBUG_ASSERT_EXISTS
+#define DBUG_FIX_WRITE_SET(f) bool _write_set_fixed= !bitmap_fast_test_and_set(write_set, (f)->field_index)
+#define DBUG_RESTORE_WRITE_SET(f) if (_write_set_fixed) bitmap_clear_bit(write_set, (f)->field_index)
+#else
+#define DBUG_FIX_WRITE_SET(f)
+#define DBUG_RESTORE_WRITE_SET(f)
+#endif
+
+
/*
@brief Compute values for virtual columns used in query
@@ -7881,28 +8267,39 @@ public:
@details
The function computes the values of the virtual columns of the table and
stores them in the table record buffer.
+ This will be done even if is_error() is set either when function was called
+ or by calculating the virtual function, as most calls to this
+ function doesn't check the result. We also want to ensure that as many
+ fields as possible has the right value so that we can optionally
+ return the partly-faulty-row from a storage engine with a virtual
+ field that gives an error on storage for an existing row.
+
+ @todo
+ Ensure that all caller checks the value of this function and
+ either properly ignores it (and resets the error) or sends the
+ error forward to the caller.
@retval
0 Success
@retval
- >0 Error occurred when storing a virtual field value
+ >0 Error occurred when storing a virtual field value or potentially
+ is_error() was set when function was called.
*/
int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
{
DBUG_ENTER("TABLE::update_virtual_fields");
- DBUG_PRINT("enter", ("update_mode: %d", update_mode));
+ DBUG_PRINT("enter", ("update_mode: %d is_error: %d", update_mode,
+ in_use->is_error()));
Field **vfield_ptr, *vf;
Query_arena backup_arena;
Turn_errors_to_warnings_handler Suppress_errors;
- int error;
bool handler_pushed= 0, update_all_columns= 1;
DBUG_ASSERT(vfield);
if (h->keyread_enabled())
DBUG_RETURN(0);
- error= 0;
in_use->set_n_backup_active_arena(expr_arena, &backup_arena);
/* When reading or deleting row, ignore errors from virtual columns */
@@ -7925,7 +8322,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
}
/* Iterate over virtual fields in the table */
- for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
+ for (vfield_ptr= vfield; *vfield_ptr ; vfield_ptr++)
{
vf= (*vfield_ptr);
Virtual_column_info *vcol_info= vf->vcol_info;
@@ -7936,17 +8333,17 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
switch (update_mode) {
case VCOL_UPDATE_FOR_READ:
update= (!vcol_info->stored_in_db &&
- bitmap_is_set(vcol_set, vf->field_index));
+ bitmap_is_set(read_set, vf->field_index));
swap_values= 1;
break;
case VCOL_UPDATE_FOR_DELETE:
case VCOL_UPDATE_FOR_WRITE:
- update= bitmap_is_set(vcol_set, vf->field_index);
+ update= bitmap_is_set(read_set, vf->field_index);
break;
case VCOL_UPDATE_FOR_REPLACE:
update= ((!vcol_info->stored_in_db &&
(vf->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG)) &&
- bitmap_is_set(vcol_set, vf->field_index)) ||
+ bitmap_is_set(read_set, vf->field_index)) ||
update_all_columns);
if (update && (vf->flags & BLOB_FLAG))
{
@@ -7966,7 +8363,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
/* Read indexed fields that was not updated in VCOL_UPDATE_FOR_READ */
update= (!vcol_info->stored_in_db &&
(vf->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG)) &&
- !bitmap_is_set(vcol_set, vf->field_index));
+ !bitmap_is_set(read_set, vf->field_index));
swap_values= 1;
break;
}
@@ -7975,8 +8372,9 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
{
int field_error __attribute__((unused)) = 0;
/* Compute the actual value of the virtual fields */
- if (vcol_info->expr->save_in_field(vf, 0))
- field_error= error= 1;
+ DBUG_FIX_WRITE_SET(vf);
+ field_error= vcol_info->expr->save_in_field(vf, 0);
+ DBUG_RESTORE_WRITE_SET(vf);
DBUG_PRINT("info", ("field '%s' - updated error: %d",
vf->field_name.str, field_error));
if (swap_values && (vf->flags & BLOB_FLAG))
@@ -8012,7 +8410,9 @@ int TABLE::update_virtual_field(Field *vf)
in_use->set_n_backup_active_arena(expr_arena, &backup_arena);
bitmap_clear_all(&tmp_set);
vf->vcol_info->expr->walk(&Item::update_vcol_processor, 0, &tmp_set);
+ DBUG_FIX_WRITE_SET(vf);
vf->vcol_info->expr->save_in_field(vf, 0);
+ DBUG_RESTORE_WRITE_SET(vf);
in_use->restore_active_arena(expr_arena, &backup_arena);
in_use->pop_internal_handler();
DBUG_RETURN(count_errors.errors);
@@ -8074,6 +8474,81 @@ int TABLE::update_default_fields(bool ignore_errors)
DBUG_RETURN(res);
}
+int TABLE::update_generated_fields()
+{
+ int res= 0;
+ if (found_next_number_field)
+ {
+ next_number_field= found_next_number_field;
+ res= found_next_number_field->set_default();
+ if (likely(!res))
+ res= file->update_auto_increment();
+ }
+
+ if (likely(!res) && vfield)
+ res= update_virtual_fields(file, VCOL_UPDATE_FOR_WRITE);
+ if (likely(!res) && versioned())
+ vers_update_fields();
+ if (likely(!res))
+ res= verify_constraints(false) == VIEW_CHECK_ERROR;
+ return res;
+}
+
+int TABLE::period_make_insert(Item *src, Field *dst)
+{
+ THD *thd= in_use;
+
+ ulonglong prev_insert_id= file->next_insert_id;
+ store_record(this, record[1]);
+ int res= src->save_in_field(dst, true);
+
+ if (likely(!res))
+ res= update_generated_fields();
+
+ if (likely(!res) && triggers)
+ res= triggers->process_triggers(thd, TRG_EVENT_INSERT,
+ TRG_ACTION_BEFORE, true);
+
+ if (likely(!res))
+ res = file->ha_write_row(record[0]);
+
+ if (likely(!res) && triggers)
+ res= triggers->process_triggers(thd, TRG_EVENT_INSERT,
+ TRG_ACTION_AFTER, true);
+
+ restore_record(this, record[1]);
+ if (res)
+ file->restore_auto_increment(prev_insert_id);
+ return res;
+}
+
+int TABLE::insert_portion_of_time(THD *thd,
+ const vers_select_conds_t &period_conds,
+ ha_rows *rows_inserted)
+{
+ bool lcond= period_conds.field_start->val_datetime_packed(thd)
+ < period_conds.start.item->val_datetime_packed(thd);
+ bool rcond= period_conds.field_end->val_datetime_packed(thd)
+ > period_conds.end.item->val_datetime_packed(thd);
+
+ int res= 0;
+ if (lcond)
+ {
+ res= period_make_insert(period_conds.start.item,
+ field[s->period.end_fieldno]);
+ if (likely(!res))
+ ++*rows_inserted;
+ }
+ if (likely(!res) && rcond)
+ {
+ res= period_make_insert(period_conds.end.item,
+ field[s->period.start_fieldno]);
+ if (likely(!res))
+ ++*rows_inserted;
+ }
+
+ return res;
+}
void TABLE::evaluate_update_default_function()
{
@@ -8689,199 +9164,72 @@ double KEY::actual_rec_per_key(uint i)
read_stats->get_avg_frequency(i) : (double) rec_per_key[i]);
}
+/*
+ find total number of field in hash expr
+*/
+int fields_in_hash_keyinfo(KEY *keyinfo)
+{
+ Item_func_hash * temp= (Item_func_hash *)
+ keyinfo->key_part->field->vcol_info->expr;
+ return temp->argument_count();
+}
+/*
+ setup_keyinfo_hash changes the key_info->key_part
+ to be same as defined by user
+ */
+void setup_keyinfo_hash(KEY *key_info)
+{
+ DBUG_ASSERT(key_info->algorithm == HA_KEY_ALG_LONG_HASH);
+ DBUG_ASSERT(key_info->key_part->field->flags & LONG_UNIQUE_HASH_FIELD);
+ uint no_of_keyparts= fields_in_hash_keyinfo(key_info);
+ key_info->key_part-= no_of_keyparts;
+ key_info->user_defined_key_parts= key_info->usable_key_parts=
+ key_info->ext_key_parts= no_of_keyparts;
+ key_info->flags|= HA_NOSAME;
+}
+/*
+ re_setup_keyinfo_hash reverts th setup_keyinfo_hash and this type of
+ arrangement is expected by storage engine
+ */
+void re_setup_keyinfo_hash(KEY *key_info)
+{
+ DBUG_ASSERT(key_info->algorithm == HA_KEY_ALG_LONG_HASH);
+ DBUG_ASSERT(!(key_info->key_part->field->flags & LONG_UNIQUE_HASH_FIELD));
+ while(!(key_info->key_part->field->flags & LONG_UNIQUE_HASH_FIELD))
+ key_info->key_part++;
+ key_info->user_defined_key_parts= key_info->usable_key_parts=
+ key_info->ext_key_parts= 1;
+ key_info->flags&= ~HA_NOSAME;
+}
/**
- @brief
- Mark subformulas of a condition unusable for the condition pushed into table
-
- @param cond The condition whose subformulas are to be marked
-
- @details
- This method recursively traverses the AND-OR condition cond and for each subformula
- of the codition it checks whether it can be usable for the extraction of a condition
- that can be pushed into this table. The subformulas that are not usable are
- marked with the flag NO_EXTRACTION_FL.
- @note
- This method is called before any call of TABLE_LIST::build_pushable_cond_for_table.
- The flag NO_EXTRACTION_FL set in a subformula allows to avoid building clone
- for the subformula when extracting the pushable condition.
-*/
-
-void TABLE_LIST::check_pushable_cond_for_table(Item *cond)
+ @brief clone of current handler.
+ Creates a clone of handler used in update for
+ unique hash key.
+*/
+void TABLE::clone_handler_for_update()
{
- table_map tab_map= table->map;
- cond->clear_extraction_flag();
- if (cond->type() == Item::COND_ITEM)
- {
- bool and_cond= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC;
- List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
- uint count= 0;
- Item *item;
- while ((item=li++))
- {
- check_pushable_cond_for_table(item);
- if (item->get_extraction_flag() != NO_EXTRACTION_FL)
- count++;
- else if (!and_cond)
- break;
- }
- if ((and_cond && count == 0) || item)
- {
- cond->set_extraction_flag(NO_EXTRACTION_FL);
- if (and_cond)
- li.rewind();
- while ((item= li++))
- item->clear_extraction_flag();
- }
- }
- else if (!cond->excl_dep_on_table(tab_map))
- cond->set_extraction_flag(NO_EXTRACTION_FL);
+ if (this->update_handler)
+ return;
+ handler *update_handler= NULL;
+ if (!s->long_unique_table)
+ return;
+ update_handler= file->clone(s->normalized_path.str,
+ in_use->mem_root);
+ update_handler->ha_external_lock(in_use, F_RDLCK);
+ this->update_handler= update_handler;
+ return;
}
-
/**
- @brief
- Build condition extractable from the given one depended only on this table
-
- @param thd The thread handle
- @param cond The condition from which the pushable one is to be extracted
-
- @details
- For the given condition cond this method finds out what condition depended
- only on this table can be extracted from cond. If such condition C exists
- the method builds the item for it.
- The method uses the flag NO_EXTRACTION_FL set by the preliminary call of
- the method TABLE_LIST::check_pushable_cond_for_table to figure out whether
- a subformula depends only on this table or not.
- @note
- The built condition C is always implied by the condition cond
- (cond => C). The method tries to build the most restictive such
- condition (i.e. for any other condition C' such that cond => C'
- we have C => C').
- @note
- The build item is not ready for usage: substitution for the field items
- has to be done and it has to be re-fixed.
-
- @retval
- the built condition pushable into this table if such a condition exists
- NULL if there is no such a condition
-*/
-
-Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond)
+ @brief Deletes update handler object
+*/
+void TABLE::delete_update_handler()
{
- table_map tab_map= table->map;
- bool is_multiple_equality= cond->type() == Item::FUNC_ITEM &&
- ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC;
- if (cond->get_extraction_flag() == NO_EXTRACTION_FL)
- return 0;
- if (cond->type() == Item::COND_ITEM)
- {
- bool cond_and= false;
- Item_cond *new_cond;
- if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
- {
- cond_and= true;
- new_cond=new (thd->mem_root) Item_cond_and(thd);
- }
- else
- new_cond= new (thd->mem_root) Item_cond_or(thd);
- if (!new_cond)
- return 0;
- List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
- Item *item;
- bool is_fix_needed= false;
- while ((item=li++))
- {
- if (item->get_extraction_flag() == NO_EXTRACTION_FL)
- {
- if (!cond_and)
- return 0;
- continue;
- }
- Item *fix= build_pushable_cond_for_table(thd, item);
- if (!fix && !cond_and)
- return 0;
- if (!fix)
- continue;
-
- if (fix->type() == Item::COND_ITEM &&
- ((Item_cond*) fix)->functype() == Item_func::COND_AND_FUNC)
- is_fix_needed= true;
-
- new_cond->argument_list()->push_back(fix, thd->mem_root);
- }
- if (is_fix_needed && new_cond->fix_fields(thd, 0))
- return 0;
-
- switch (new_cond->argument_list()->elements)
- {
- case 0:
- return 0;
- case 1:
- return new_cond->argument_list()->head();
- default:
- return new_cond;
- }
- }
- else if (is_multiple_equality)
- {
- if (!(cond->used_tables() & tab_map))
- return 0;
- Item *new_cond= NULL;
- int i= 0;
- Item_equal *item_equal= (Item_equal *) cond;
- Item *left_item = item_equal->get_const();
- Item_equal_fields_iterator it(*item_equal);
- Item *item;
- if (!left_item)
- {
- while ((item=it++))
- if (item->used_tables() == tab_map)
- {
- left_item= item;
- break;
- }
- }
- if (!left_item)
- return 0;
- while ((item=it++))
- {
- if (!(item->used_tables() == tab_map))
- continue;
- Item_func_eq *eq= 0;
- Item *left_item_clone= left_item->build_clone(thd);
- Item *right_item_clone= item->build_clone(thd);
- if (left_item_clone && right_item_clone)
- {
- left_item_clone->set_item_equal(NULL);
- right_item_clone->set_item_equal(NULL);
- eq= new (thd->mem_root) Item_func_eq(thd, right_item_clone,
- left_item_clone);
- }
- if (eq)
- {
- i++;
- switch (i)
- {
- case 1:
- new_cond= eq;
- break;
- case 2:
- new_cond= new (thd->mem_root) Item_cond_and(thd, new_cond, eq);
- break;
- default:
- ((Item_cond_and*)new_cond)->argument_list()->push_back(eq,
- thd->mem_root);
- }
- }
- }
- if (new_cond)
- new_cond->fix_fields(thd, &new_cond);
- return new_cond;
- }
- else if (cond->get_extraction_flag() != NO_EXTRACTION_FL)
- return cond->build_clone(thd);
- return 0;
+ update_handler->ha_external_lock(in_use, F_UNLCK);
+ update_handler->ha_close();
+ delete update_handler;
+ this->update_handler= NULL;
}
LEX_CSTRING *fk_option_name(enum_fk_option opt)
@@ -8992,7 +9340,7 @@ bool TR_table::query(ulonglong trx_id)
READ_RECORD info;
int error;
List<TABLE_LIST> dummy;
- SELECT_LEX &slex= thd->lex->select_lex;
+ SELECT_LEX &slex= *(thd->lex->first_select_lex());
Name_resolution_context_backup backup(slex.context, *this);
Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_TRX_ID]);
Item *value= newx Item_int(thd, trx_id);
@@ -9026,10 +9374,11 @@ bool TR_table::query(MYSQL_TIME &commit_time, bool backwards)
READ_RECORD info;
int error;
List<TABLE_LIST> dummy;
- SELECT_LEX &slex= thd->lex->select_lex;
+ SELECT_LEX &slex= *(thd->lex->first_select_lex());
Name_resolution_context_backup backup(slex.context, *this);
Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_COMMIT_TS]);
- Item *value= newx Item_datetime_literal(thd, &commit_time, 6);
+ Datetime dt(&commit_time);
+ Item *value= newx Item_datetime_literal(thd, &dt, 6);
COND *conds;
if (backwards)
conds= newx Item_func_ge(thd, field, value);
@@ -9056,7 +9405,7 @@ bool TR_table::query(MYSQL_TIME &commit_time, bool backwards)
if (res > 0)
{
MYSQL_TIME commit_ts;
- if ((*this)[FLD_COMMIT_TS]->get_date(&commit_ts, 0))
+ if ((*this)[FLD_COMMIT_TS]->get_date(&commit_ts, date_mode_t(0)))
{
found= false;
break;
@@ -9277,7 +9626,8 @@ bool Vers_history_point::resolve_unit(THD *thd)
return false;
if (item->fix_fields_if_needed(thd, &item))
return true;
- return item->this_item()->type_handler_for_system_time()->
+ return item->this_item()->real_type_handler()->
+ type_handler_for_system_time()->
Vers_history_point_resolve_unit(thd, this);
}
diff --git a/sql/table.h b/sql/table.h
index 6a4c6eb2a03..4a739ed1f9f 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -25,6 +25,7 @@
#ifndef MYSQL_CLIENT
+#include "my_cpu.h" /* LF_BACKOFF() */
#include "hash.h" /* HASH */
#include "handler.h" /* row_type, ha_choice, handler */
#include "mysql_com.h" /* enum_field_types */
@@ -55,6 +56,9 @@ class Virtual_column_info;
class Table_triggers_list;
class TMP_TABLE_PARAM;
class SEQUENCE;
+class Range_rowid_filter_cost_info;
+class derived_handler;
+class Pushdown_derived;
struct Name_resolution_context;
/*
@@ -361,9 +365,18 @@ enum field_visibility_t {
INVISIBLE_FULL
};
-#define INVISIBLE_MAX_BITS 3
+#define INVISIBLE_MAX_BITS 3
+#define HA_HASH_FIELD_LENGTH 8
+#define HA_HASH_KEY_LENGTH_WITHOUT_NULL 8
+#define HA_HASH_KEY_LENGTH_WITH_NULL 9
+int fields_in_hash_keyinfo(KEY *keyinfo);
+
+void setup_keyinfo_hash(KEY *key_info);
+
+void re_setup_keyinfo_hash(KEY *key_info);
+
/**
Category of table found in the table share.
*/
@@ -410,28 +423,6 @@ enum enum_table_category
TABLE_CATEGORY_SYSTEM=3,
/**
- Information schema tables.
- These tables are an interface provided by the system
- to inspect the system metadata.
- These tables do *not* honor:
- - LOCK TABLE t FOR READ/WRITE
- - FLUSH TABLES WITH READ LOCK
- - SET GLOBAL READ_ONLY = ON
- as there is no point in locking explicitly
- an INFORMATION_SCHEMA table.
- Nothing is directly written to information schema tables.
- Note that this value is not used currently,
- since information schema tables are not shared,
- but implemented as session specific temporary tables.
- */
- /*
- TODO: Fixing the performance issues of I_S will lead
- to I_S tables in the table cache, which should use
- this table type.
- */
- TABLE_CATEGORY_INFORMATION=4,
-
- /**
Log tables.
These tables are an interface provided by the system
to inspect the system logs.
@@ -451,7 +442,33 @@ enum enum_table_category
The server implementation perform writes.
Log tables are cached in the table cache.
*/
- TABLE_CATEGORY_LOG=5,
+ TABLE_CATEGORY_LOG=4,
+
+ /*
+ Types below are read only tables, not affected by FLUSH TABLES or
+ MDL locks.
+ */
+ /**
+ Information schema tables.
+ These tables are an interface provided by the system
+ to inspect the system metadata.
+ These tables do *not* honor:
+ - LOCK TABLE t FOR READ/WRITE
+ - FLUSH TABLES WITH READ LOCK
+ - SET GLOBAL READ_ONLY = ON
+ as there is no point in locking explicitly
+ an INFORMATION_SCHEMA table.
+ Nothing is directly written to information schema tables.
+ Note that this value is not used currently,
+ since information schema tables are not shared,
+ but implemented as session specific temporary tables.
+ */
+ /*
+ TODO: Fixing the performance issues of I_S will lead
+ to I_S tables in the table cache, which should use
+ this table type.
+ */
+ TABLE_CATEGORY_INFORMATION=5,
/**
Performance schema tables.
@@ -475,6 +492,7 @@ enum enum_table_category
*/
TABLE_CATEGORY_PERFORMANCE=6
};
+
typedef enum enum_table_category TABLE_CATEGORY;
TABLE_CATEGORY get_table_category(const LEX_CSTRING *db,
@@ -811,6 +829,7 @@ struct TABLE_SHARE
bool null_field_first;
bool system; /* Set if system table (one record) */
bool not_usable_by_query_cache;
+ bool online_backup; /* Set if on-line backup supported */
/*
This is used by log tables, for tables that have their own internal
binary logging or for tables that doesn't support statement or row logging
@@ -825,6 +844,8 @@ struct TABLE_SHARE
bool vcols_need_refixing;
bool has_update_default_function;
bool can_do_row_logging; /* 1 if table supports RBR */
+ bool long_unique_table;
+
ulong table_map_id; /* for row-based replication */
/*
@@ -854,20 +875,38 @@ struct TABLE_SHARE
/**
System versioning support.
- */
+ */
+ struct period_info_t
+ {
+ uint16 start_fieldno;
+ uint16 end_fieldno;
+ Lex_ident name;
+ Lex_ident constr_name;
+ Field *start_field(TABLE_SHARE *s) const
+ {
+ return s->field[start_fieldno];
+ }
+ Field *end_field(TABLE_SHARE *s) const
+ {
+ return s->field[end_fieldno];
+ }
+ };
vers_sys_type_t versioned;
- uint16 row_start_field;
- uint16 row_end_field;
+ period_info_t vers;
+ period_info_t period;
+
+ bool init_period_from_extra2(period_info_t *period, const uchar *data,
+ const uchar *end);
Field *vers_start_field()
{
- return field[row_start_field];
+ return field[vers.start_fieldno];
}
Field *vers_end_field()
{
- return field[row_end_field];
+ return field[vers.end_fieldno];
}
/**
@@ -1082,8 +1121,12 @@ struct TABLE_SHARE
/* frees the memory allocated in read_frm_image */
void free_frm_image(const uchar *frm);
+
+ void set_overlapped_keys();
};
+/* not NULL, but cannot be dereferenced */
+#define UNUSABLE_TABLE_SHARE ((TABLE_SHARE*)1)
/**
Class is used as a BLOB field value storage for
@@ -1155,6 +1198,8 @@ typedef Bitmap<MAX_FIELDS> Field_map;
class SplM_opt_info;
+struct vers_select_conds_t;
+
struct TABLE
{
TABLE() {} /* Remove gcc warning */
@@ -1180,6 +1225,9 @@ public:
THD *in_use; /* Which thread uses this */
uchar *record[3]; /* Pointer to records */
+ /* record buf to resolve hash collisions for long UNIQUE constraints */
+ uchar *check_unique_buf;
+ handler *update_handler; /* Handler used in case of update */
uchar *write_row_record; /* Used as optimisation in
THD::write_row */
uchar *insert_values; /* used by INSERT ... UPDATE */
@@ -1205,6 +1253,8 @@ public:
key_map keys_in_use_for_group_by;
/* Map of keys that can be used to calculate ORDER BY without sorting */
key_map keys_in_use_for_order_by;
+ /* Map of keys dependent on some constraint */
+ key_map constraint_dependent_keys;
KEY *key_info; /* data of keys in database */
Field **field; /* Pointer to fields */
@@ -1236,8 +1286,6 @@ public:
MY_BITMAP cond_set; /* used to mark fields from sargable conditions*/
/* Active column sets */
MY_BITMAP *read_set, *write_set, *rpl_write_set;
- /* Set if using virtual fields */
- MY_BITMAP *vcol_set, *def_vcol_set;
/* On INSERT: fields that the user specified a value for */
MY_BITMAP has_value_set;
@@ -1275,7 +1323,14 @@ public:
and max #key parts that range access would use.
*/
ha_rows quick_rows[MAX_KEY];
+ uint quick_key_parts[MAX_KEY];
+
double quick_costs[MAX_KEY];
+ /*
+ If there is a range access by i-th index then the cost of
+ index only access for it is stored in quick_index_only_costs[i]
+ */
+ double quick_index_only_costs[MAX_KEY];
/*
Bitmaps of key parts that =const for the duration of join execution. If
@@ -1284,8 +1339,7 @@ public:
*/
key_part_map const_key_parts[MAX_KEY];
- uint quick_key_parts[MAX_KEY];
- uint quick_n_ranges[MAX_KEY];
+ uint quick_n_ranges[MAX_KEY];
/*
Estimate of number of records that satisfy SARGable part of the table
@@ -1467,7 +1521,9 @@ public:
void mark_columns_needed_for_delete(void);
void mark_columns_needed_for_insert(void);
void mark_columns_per_binlog_row_image(void);
- bool mark_virtual_col(Field *field);
+ inline bool mark_column_with_deps(Field *field);
+ inline bool mark_virtual_column_with_deps(Field *field);
+ inline void mark_virtual_column_deps(Field *field);
bool mark_virtual_columns_for_write(bool insert_fl);
bool check_virtual_columns_marked_for_read();
bool check_virtual_columns_marked_for_write();
@@ -1489,39 +1545,21 @@ public:
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);
}
+ inline void use_all_stored_columns();
inline void default_column_bitmaps()
{
read_set= &def_read_set;
write_set= &def_write_set;
- vcol_set= def_vcol_set; /* Note that this may be 0 */
rpl_write_set= 0;
}
/** Should this instance of the table be reopened? */
@@ -1615,6 +1653,23 @@ public:
double get_materialization_cost(); // Now used only if is_splittable()==true
void add_splitting_info_for_key_field(struct KEY_FIELD *key_field);
+ key_map with_impossible_ranges;
+
+ /* Number of cost info elements for possible range filters */
+ uint range_rowid_filter_cost_info_elems;
+ /* Pointer to the array of cost info elements for range filters */
+ Range_rowid_filter_cost_info *range_rowid_filter_cost_info;
+ /* The array of pointers to cost info elements for range filters */
+ Range_rowid_filter_cost_info **range_rowid_filter_cost_info_ptr;
+
+ void init_cost_info_for_usable_range_rowid_filters(THD *thd);
+ void prune_range_rowid_filters();
+ void trace_range_rowid_filters(THD *thd) const;
+ Range_rowid_filter_cost_info *
+ best_range_rowid_filter_for_partial_join(uint access_key_no,
+ double records,
+ double access_cost_factor);
+
/**
System Versioning support
*/
@@ -1649,23 +1704,30 @@ public:
Field *vers_start_field() const
{
DBUG_ASSERT(s && s->versioned);
- return field[s->row_start_field];
+ return field[s->vers.start_fieldno];
}
Field *vers_end_field() const
{
DBUG_ASSERT(s && s->versioned);
- return field[s->row_end_field];
+ return field[s->vers.end_fieldno];
}
ulonglong vers_start_id() const;
ulonglong vers_end_id() const;
+ int update_generated_fields();
+ int period_make_insert(Item *src, Field *dst);
+ int insert_portion_of_time(THD *thd, const vers_select_conds_t &period_conds,
+ ha_rows *rows_inserted);
bool vers_check_update(List<Item> &items);
int delete_row();
void vers_update_fields();
void vers_update_end();
+ void find_constraint_correlated_indexes();
+ void clone_handler_for_update();
+ void delete_update_handler();
/** Number of additional fields used in versioned tables */
#define VERSIONING_FIELDS 2
@@ -1862,6 +1924,13 @@ class IS_table_read_plan;
/** The threshold size a blob field buffer before it is freed */
#define MAX_TDC_BLOB_SIZE 65536
+/** number of bytes used by field positional indexes in frm */
+constexpr uint frm_fieldno_size= 2;
+static inline uint16 read_frm_fieldno(const uchar *data)
+{ return uint2korr(data); }
+static inline void store_frm_fieldno(const uchar *data, uint16 fieldno)
+{ int2store(data, fieldno); }
+
class select_unit;
class TMP_TABLE_PARAM;
@@ -1973,6 +2042,12 @@ struct vers_select_conds_t
bool delete_history:1;
Vers_history_point start;
Vers_history_point end;
+ Lex_ident name;
+
+ Item_field *field_start;
+ Item_field *field_end;
+
+ const TABLE_SHARE::period_info_t *period;
void empty()
{
@@ -1986,7 +2061,8 @@ struct vers_select_conds_t
void init(vers_system_time_t _type,
Vers_history_point _start= Vers_history_point(),
- Vers_history_point _end= Vers_history_point())
+ Vers_history_point _end= Vers_history_point(),
+ Lex_ident _name= "SYSTEM_TIME")
{
type= _type;
orig_type= _type;
@@ -1995,6 +2071,13 @@ struct vers_select_conds_t
type == SYSTEM_TIME_BEFORE);
start= _start;
end= _end;
+ name= _name;
+ }
+
+ void set_all()
+ {
+ type= SYSTEM_TIME_ALL;
+ name= "SYSTEM_TIME";
}
void print(String *str, enum_query_type query_type) const;
@@ -2102,6 +2185,7 @@ struct TABLE_LIST
init_one_table(&table_arg->s->db, &table_arg->s->table_name,
NULL, lock_type);
table= table_arg;
+ vers_conditions.name= table->s->vers.name;
}
inline void init_one_table_for_prelocking(const LEX_CSTRING *db_arg,
@@ -2281,6 +2365,15 @@ struct TABLE_LIST
TABLE_LIST * next_with_rec_ref;
bool is_derived_with_recursive_reference;
bool block_handle_derived;
+ /* The interface employed to materialize the table by a foreign engine */
+ derived_handler *dt_handler;
+ /* The text of the query specifying the derived table */
+ LEX_CSTRING derived_spec;
+ /*
+ The object used to organize execution of the query that specifies
+ the derived table by a foreign engine
+ */
+ Pushdown_derived *pushdown_derived;
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
st_select_lex *schema_select_lex;
/*
@@ -2545,6 +2638,12 @@ struct TABLE_LIST
/* System Versioning */
vers_select_conds_t vers_conditions;
+ vers_select_conds_t period_conditions;
+
+ bool has_period() const
+ {
+ return period_conditions.is_set();
+ }
my_bool for_insert_data;
@@ -2749,8 +2848,9 @@ struct TABLE_LIST
return false;
}
void set_lock_type(THD* thd, enum thr_lock_type lock);
- void check_pushable_cond_for_table(Item *cond);
- Item *build_pushable_cond_for_table(THD *thd, Item *cond);
+
+ derived_handler *find_derived_handler(THD *thd);
+ TABLE_LIST *get_first_table();
void remove_join_columns()
{
@@ -3128,7 +3228,7 @@ extern LEX_CSTRING INFORMATION_SCHEMA_NAME;
extern LEX_CSTRING MYSQL_SCHEMA_NAME;
/* table names */
-extern LEX_CSTRING MYSQL_USER_NAME, MYSQL_DB_NAME, MYSQL_PROC_NAME;
+extern LEX_CSTRING MYSQL_PROC_NAME;
inline bool is_infoschema_db(const LEX_CSTRING *name)
{
diff --git a/sql/table_cache.cc b/sql/table_cache.cc
index c1ae89f85ed..15255c56083 100644
--- a/sql/table_cache.cc
+++ b/sql/table_cache.cc
@@ -56,8 +56,8 @@
ulong tdc_size; /**< Table definition cache threshold for LRU eviction. */
ulong tc_size; /**< Table cache threshold for LRU eviction. */
uint32 tc_instances;
-uint32 tc_active_instances= 1;
-static uint32 tc_contention_warning_reported;
+static std::atomic<uint32_t> tc_active_instances(1);
+static std::atomic<bool> tc_contention_warning_reported;
/** Data collections. */
static LF_HASH tdc_hash; /**< Collection of TABLE_SHARE objects. */
@@ -163,7 +163,7 @@ struct Table_cache_instance
overhead on TABLE object release. All other table cache mutex acquistions
are considered out of hot path and are not instrumented either.
*/
- void lock_and_check_contention(uint32 n_instances, uint32 instance)
+ void lock_and_check_contention(uint32_t n_instances, uint32_t instance)
{
if (mysql_mutex_trylock(&LOCK_table_cache))
{
@@ -172,11 +172,10 @@ struct Table_cache_instance
{
if (n_instances < tc_instances)
{
- if (my_atomic_cas32_weak_explicit((int32*) &tc_active_instances,
- (int32*) &n_instances,
- (int32) n_instances + 1,
- MY_MEMORY_ORDER_RELAXED,
- MY_MEMORY_ORDER_RELAXED))
+ if (tc_active_instances.
+ compare_exchange_weak(n_instances, n_instances + 1,
+ std::memory_order_relaxed,
+ std::memory_order_relaxed))
{
sql_print_information("Detected table cache mutex contention at instance %d: "
"%d%% waits. Additional table cache instance "
@@ -187,8 +186,8 @@ struct Table_cache_instance
n_instances + 1);
}
}
- else if (!my_atomic_fas32_explicit((int32*) &tc_contention_warning_reported,
- 1, MY_MEMORY_ORDER_RELAXED))
+ else if (!tc_contention_warning_reported.exchange(true,
+ std::memory_order_relaxed))
{
sql_print_warning("Detected table cache mutex contention at instance %d: "
"%d%% waits. Additional table cache instance "
@@ -232,7 +231,7 @@ static void intern_close_table(TABLE *table)
uint tc_records(void)
{
ulong total= 0;
- for (ulong i= 0; i < tc_instances; i++)
+ for (uint32 i= 0; i < tc_instances; i++)
{
mysql_mutex_lock(&tc[i].LOCK_table_cache);
total+= tc[i].records;
@@ -277,7 +276,7 @@ static void tc_remove_all_unused_tables(TDC_element *element,
*/
if (mark_flushed)
element->flushed= true;
- for (ulong i= 0; i < tc_instances; i++)
+ for (uint32 i= 0; i < tc_instances; i++)
{
mysql_mutex_lock(&tc[i].LOCK_table_cache);
while ((table= element->free_tables[i].list.pop_front()))
@@ -354,8 +353,8 @@ void tc_purge(bool mark_flushed)
void tc_add_table(THD *thd, TABLE *table)
{
- uint32 i= thd->thread_id % my_atomic_load32_explicit((int32*) &tc_active_instances,
- MY_MEMORY_ORDER_RELAXED);
+ uint32_t i=
+ thd->thread_id % tc_active_instances.load(std::memory_order_relaxed);
TABLE *LRU_table= 0;
TDC_element *element= table->s->tdc;
@@ -406,12 +405,10 @@ void tc_add_table(THD *thd, TABLE *table)
@return TABLE object, or NULL if no unused objects.
*/
-static TABLE *tc_acquire_table(THD *thd, TDC_element *element)
+TABLE *tc_acquire_table(THD *thd, TDC_element *element)
{
- uint32 n_instances=
- my_atomic_load32_explicit((int32*) &tc_active_instances,
- MY_MEMORY_ORDER_RELAXED);
- uint32 i= thd->thread_id % n_instances;
+ uint32_t n_instances= tc_active_instances.load(std::memory_order_relaxed);
+ uint32_t i= thd->thread_id % n_instances;
TABLE *table;
tc[i].lock_and_check_contention(n_instances, i);
@@ -491,7 +488,7 @@ static void tdc_assert_clean_share(TDC_element *element)
DBUG_ASSERT(element->m_flush_tickets.is_empty());
DBUG_ASSERT(element->all_tables.is_empty());
#ifndef DBUG_OFF
- for (ulong i= 0; i < tc_instances; i++)
+ for (uint32 i= 0; i < tc_instances; i++)
DBUG_ASSERT(element->free_tables[i].list.is_empty());
#endif
DBUG_ASSERT(element->all_tables_refs == 0);
@@ -564,7 +561,7 @@ static void lf_alloc_constructor(uchar *arg)
mysql_cond_init(key_TABLE_SHARE_COND_release, &element->COND_release, 0);
element->m_flush_tickets.empty();
element->all_tables.empty();
- for (ulong i= 0; i < tc_instances; i++)
+ for (uint32 i= 0; i < tc_instances; i++)
element->free_tables[i].list.empty();
element->all_tables_refs= 0;
element->share= 0;
@@ -645,7 +642,7 @@ bool tdc_init(void)
void tdc_start_shutdown(void)
{
- DBUG_ENTER("table_def_start_shutdown");
+ DBUG_ENTER("tdc_start_shutdown");
if (tdc_inited)
{
/*
@@ -657,7 +654,7 @@ void tdc_start_shutdown(void)
tdc_size= 0;
tc_size= 0;
/* Free all cached but unused TABLEs and TABLE_SHAREs. */
- close_cached_tables(NULL, NULL, FALSE, LONG_TIMEOUT);
+ purge_tables(true);
}
DBUG_VOID_RETURN;
}
@@ -689,7 +686,7 @@ void tdc_deinit(void)
ulong tdc_records(void)
{
- return my_atomic_load32_explicit(&tdc_hash.count, MY_MEMORY_ORDER_RELAXED);
+ return lf_hash_size(&tdc_hash);
}
@@ -945,7 +942,7 @@ end:
table existed?
Let's return an invalid pointer here to catch dereferencing attempts.
*/
- share= (TABLE_SHARE*) 1;
+ share= UNUSABLE_TABLE_SHARE;
}
DBUG_RETURN(share);
@@ -1094,6 +1091,7 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
TABLE *table;
TDC_element *element;
uint my_refs= 1;
+ bool res= false;
DBUG_ENTER("tdc_remove_table");
DBUG_PRINT("enter",("name: %s remove_type: %d", table_name, remove_type));
@@ -1101,7 +1099,6 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
MDL_EXCLUSIVE));
-
mysql_mutex_lock(&LOCK_unused_shares);
if (!(element= tdc_lock_share(thd, db, table_name)))
{
@@ -1123,7 +1120,7 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
mysql_mutex_unlock(&LOCK_unused_shares);
tdc_delete_share_from_hash(element);
- DBUG_RETURN(true);
+ DBUG_RETURN(false);
}
mysql_mutex_unlock(&LOCK_unused_shares);
@@ -1189,10 +1186,16 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
#endif
mysql_mutex_unlock(&element->LOCK_table_share);
}
+ else
+ {
+ mysql_mutex_lock(&element->LOCK_table_share);
+ res= element->ref_count > 1;
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ }
tdc_release_share(element->share);
- DBUG_RETURN(true);
+ DBUG_RETURN(res);
}
@@ -1336,3 +1339,14 @@ int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
}
return res;
}
+
+
+int show_tc_active_instances(THD *thd, SHOW_VAR *var, char *buff,
+ enum enum_var_type scope)
+{
+ var->type= SHOW_UINT;
+ var->value= buff;
+ *(reinterpret_cast<uint32_t*>(buff))=
+ tc_active_instances.load(std::memory_order_relaxed);
+ return 0;
+}
diff --git a/sql/table_cache.h b/sql/table_cache.h
index b2790e44b6e..3be7b5fe413 100644
--- a/sql/table_cache.h
+++ b/sql/table_cache.h
@@ -71,7 +71,6 @@ enum enum_tdc_remove_table_type
extern ulong tdc_size;
extern ulong tc_size;
extern uint32 tc_instances;
-extern uint32 tc_active_instances;
extern bool tdc_init(void);
extern void tdc_start_shutdown(void);
@@ -88,7 +87,6 @@ extern bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
const char *db, const char *table_name,
bool kill_delayed_threads);
-
extern int tdc_wait_for_old_version(THD *thd, const char *db,
const char *table_name,
ulong wait_timeout, uint deadlock_weight,
@@ -99,9 +97,12 @@ extern int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
bool no_dups= false);
extern uint tc_records(void);
+int show_tc_active_instances(THD *thd, SHOW_VAR *var, char *buff,
+ enum enum_var_type scope);
extern void tc_purge(bool mark_flushed= false);
extern void tc_add_table(THD *thd, TABLE *table);
extern void tc_release_table(TABLE *table);
+extern TABLE *tc_acquire_table(THD *thd, TDC_element *element);
/**
Create a table cache key for non-temporary table.
diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc
index e957488e3db..5e4b5f7a713 100644
--- a/sql/temporary_tables.cc
+++ b/sql/temporary_tables.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2016, 2019, MariaDB Corporation.
+ Copyright (c) 2016, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -49,23 +49,18 @@ bool THD::has_thd_temporary_tables()
/**
Create a temporary table, open it and return the TABLE handle.
- @param hton [IN] Handlerton
@param frm [IN] Binary frm image
@param path [IN] File path (without extension)
@param db [IN] Schema name
@param table_name [IN] Table name
- @param open_in_engine [IN] Whether open table in SE
-
@return Success A pointer to table object
Failure NULL
*/
-TABLE *THD::create_and_open_tmp_table(handlerton *hton,
- LEX_CUSTRING *frm,
+TABLE *THD::create_and_open_tmp_table(LEX_CUSTRING *frm,
const char *path,
const char *db,
const char *table_name,
- bool open_in_engine,
bool open_internal_tables)
{
DBUG_ENTER("THD::create_and_open_tmp_table");
@@ -73,10 +68,10 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton,
TMP_TABLE_SHARE *share;
TABLE *table= NULL;
- if ((share= create_temporary_table(hton, frm, path, db, table_name)))
+ if ((share= create_temporary_table(frm, path, db, table_name)))
{
open_options|= HA_OPEN_FOR_CREATE;
- table= open_temporary_table(share, table_name, open_in_engine);
+ table= open_temporary_table(share, table_name);
open_options&= ~HA_OPEN_FOR_CREATE;
/*
@@ -96,7 +91,7 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton,
/* Open any related tables */
if (open_internal_tables && table->internal_tables &&
- open_and_lock_internal_tables(table, open_in_engine))
+ open_and_lock_internal_tables(table, true))
{
drop_temporary_table(table, NULL, false);
DBUG_RETURN(0);
@@ -388,7 +383,7 @@ bool THD::open_temporary_table(TABLE_LIST *tl)
*/
if (!table && (share= find_tmp_table_share(tl)))
{
- table= open_temporary_table(share, tl->get_table_name(), true);
+ table= open_temporary_table(share, tl->get_table_name());
/*
Temporary tables are not safe for parallel replication. They were
designed to be visible to one thread only, so have no table locking.
@@ -754,6 +749,8 @@ void THD::mark_tmp_tables_as_free_for_reuse()
{
if ((table->query_id == query_id) && !table->open_by_handler)
{
+ if (table->update_handler)
+ table->delete_update_handler();
mark_tmp_table_as_free_for_reuse(table);
}
}
@@ -878,10 +875,12 @@ void THD::restore_tmp_table_share(TMP_TABLE_SHARE *share)
inline bool THD::has_temporary_tables()
{
DBUG_ENTER("THD::has_temporary_tables");
- bool result= (rgi_slave
- ? (rgi_slave->rli->save_temporary_tables &&
- !rgi_slave->rli->save_temporary_tables->is_empty())
- : (has_thd_temporary_tables()));
+ bool result=
+#ifdef HAVE_REPLICATION
+ rgi_slave ? (rgi_slave->rli->save_temporary_tables &&
+ !rgi_slave->rli->save_temporary_tables->is_empty()) :
+#endif
+ has_thd_temporary_tables();
DBUG_RETURN(result);
}
@@ -926,7 +925,6 @@ uint THD::create_tmp_table_def_key(char *key, const char *db,
/**
Create a temporary table.
- @param hton [IN] Handlerton
@param frm [IN] Binary frm image
@param path [IN] File path (without extension)
@param db [IN] Schema name
@@ -935,8 +933,7 @@ uint THD::create_tmp_table_def_key(char *key, const char *db,
@return Success A pointer to table share object
Failure NULL
*/
-TMP_TABLE_SHARE *THD::create_temporary_table(handlerton *hton,
- LEX_CUSTRING *frm,
+TMP_TABLE_SHARE *THD::create_temporary_table(LEX_CUSTRING *frm,
const char *path,
const char *db,
const char *table_name)
@@ -974,8 +971,6 @@ TMP_TABLE_SHARE *THD::create_temporary_table(handlerton *hton,
init_tmp_table_share(this, share, saved_key_cache, key_length,
strend(saved_key_cache) + 1, tmp_path);
- share->db_plugin= ha_lock_engine(this, hton);
-
/*
Prefer using frm image over file. The image might not be available in
ALTER TABLE, when the discovering engine took over the ownership (see
@@ -1102,14 +1097,12 @@ TABLE *THD::find_temporary_table(const char *key, uint key_length,
@param share [IN] Table share
@param alias [IN] Table alias
- @param open_in_engine [IN] Whether open table in SE
@return Success A pointer to table object
Failure NULL
*/
TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share,
- const char *alias_arg,
- bool open_in_engine)
+ const char *alias_arg)
{
TABLE *table;
LEX_CSTRING alias= {alias_arg, strlen(alias_arg) };
@@ -1122,11 +1115,11 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share,
}
if (open_table_from_share(this, share, &alias,
- open_in_engine ? (uint)HA_OPEN_KEYFILE : 0,
+ (uint) HA_OPEN_KEYFILE,
EXTRA_RECORD,
(ha_open_options |
(open_options & HA_OPEN_FOR_CREATE)),
- table, open_in_engine ? false : true))
+ table, false))
{
my_free(table);
DBUG_RETURN(NULL);
@@ -1146,9 +1139,7 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share,
/* Increment Slave_open_temp_table_definitions status variable count. */
if (rgi_slave)
- {
- thread_safe_increment32(&slave_open_temp_tables);
- }
+ slave_open_temp_tables++;
DBUG_PRINT("tmptable", ("Opened table: '%s'.'%s table: %p",
table->s->db.str,
@@ -1254,7 +1245,7 @@ void THD::close_temporary_table(TABLE *table)
/* Natural invariant of temporary_tables */
DBUG_ASSERT(slave_open_temp_tables || !temporary_tables);
/* Decrement Slave_open_temp_table_definitions status variable count. */
- thread_safe_decrement32(&slave_open_temp_tables);
+ slave_open_temp_tables--;
}
DBUG_VOID_RETURN;
@@ -1519,12 +1510,14 @@ bool THD::lock_temporary_tables()
DBUG_RETURN(false);
}
+#ifdef HAVE_REPLICATION
if (rgi_slave)
{
mysql_mutex_lock(&rgi_slave->rli->data_lock);
temporary_tables= rgi_slave->rli->save_temporary_tables;
m_tmp_tables_locked= true;
}
+#endif
DBUG_RETURN(m_tmp_tables_locked);
}
@@ -1545,6 +1538,7 @@ void THD::unlock_temporary_tables()
DBUG_VOID_RETURN;
}
+#ifdef HAVE_REPLICATION
if (rgi_slave)
{
rgi_slave->rli->save_temporary_tables= temporary_tables;
@@ -1552,6 +1546,7 @@ void THD::unlock_temporary_tables()
mysql_mutex_unlock(&rgi_slave->rli->data_lock);
m_tmp_tables_locked= false;
}
+#endif
DBUG_VOID_RETURN;
}
diff --git a/sql/threadpool.h b/sql/threadpool.h
index ec320c6945c..817681e174f 100644
--- a/sql/threadpool.h
+++ b/sql/threadpool.h
@@ -1,3 +1,7 @@
+#ifndef THREADPOOL_H_INCLUDED
+#define THREADPOOL_H_INCLUDED
+
+#ifdef HAVE_POOL_OF_THREADS
/* Copyright (C) 2012 Monty Program Ab
This program is free software; you can redistribute it and/or modify
@@ -43,7 +47,7 @@ extern void tp_timeout_handler(TP_connection *c);
struct TP_STATISTICS
{
/* Current number of worker thread. */
- volatile int32 num_worker_threads;
+ Atomic_counter<uint32_t> num_worker_threads;
};
extern TP_STATISTICS tp_stats;
@@ -155,3 +159,6 @@ struct TP_pool_generic :TP_pool
virtual int set_stall_limit(uint);
virtual int get_idle_thread_count();
};
+
+#endif /* HAVE_POOL_OF_THREADS */
+#endif /* THREADPOOL_H_INCLUDED */
diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc
index 7de7866e015..3320b9fc0cc 100644
--- a/sql/threadpool_common.cc
+++ b/sql/threadpool_common.cc
@@ -23,7 +23,9 @@
#include <sql_audit.h>
#include <debug_sync.h>
#include <threadpool.h>
-
+#ifdef WITH_WSREP
+#include "wsrep_trans_observer.h"
+#endif /* WITH_WSREP */
/* Threadpool parameters */
@@ -136,6 +138,11 @@ static inline void set_thd_idle(THD *thd)
*/
static void thread_attach(THD* thd)
{
+#ifdef WITH_WSREP
+ /* Wait until possible background rollback has finished before
+ attaching the thd. */
+ wsrep_wait_rollback_complete_and_acquire_ownership(thd);
+#endif /* WITH_WSREP */
set_mysys_var(thd->mysys_var);
thd->thread_stack=(char*)&thd;
thd->store_globals();
@@ -241,7 +248,7 @@ static THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data)
return NULL;
}
delete connect;
- add_to_active_threads(thd);
+ server_threads.insert(thd);
thd->set_mysys_var(mysys_var);
thd->event_scheduler.data= scheduler_data;
diff --git a/sql/threadpool_generic.cc b/sql/threadpool_generic.cc
index 8b424591ba6..3f4997b8238 100644
--- a/sql/threadpool_generic.cc
+++ b/sql/threadpool_generic.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2012 Monty Program Ab
+/* Copyright (C) 2012, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -200,7 +200,7 @@ struct MY_ALIGNED(CPU_LEVEL1_DCACHE_LINESIZE) thread_group_t
static thread_group_t *all_groups;
static uint group_count;
-static int32 shutdown_group_count;
+static Atomic_counter<uint32_t> shutdown_group_count;
/**
Used for printing "pool blocked" message, see
@@ -214,7 +214,7 @@ struct pool_timer_t
mysql_mutex_t mutex;
mysql_cond_t cond;
volatile uint64 current_microtime;
- volatile uint64 next_timeout_check;
+ std::atomic<uint64_t> next_timeout_check;
int tick_interval;
bool shutdown;
pthread_t timer_thread_id;
@@ -578,40 +578,34 @@ static void queue_put(thread_group_t *thread_group, native_event *ev, int cnt)
Also, recalculate time when next timeout check should run.
*/
-static void timeout_check(pool_timer_t *timer)
+static my_bool timeout_check(THD *thd, pool_timer_t *timer)
{
DBUG_ENTER("timeout_check");
-
- mysql_mutex_lock(&LOCK_thread_count);
- I_List_iterator<THD> it(threads);
-
- /* Reset next timeout check, it will be recalculated in the loop below */
- my_atomic_fas64((volatile int64*)&timer->next_timeout_check, ULONGLONG_MAX);
-
- THD *thd;
- while ((thd=it++))
+ if (thd->net.reading_or_writing == 1)
{
TP_connection_generic *connection= (TP_connection_generic *)thd->event_scheduler.data;
if (!connection || connection->state != TP_STATE_IDLE)
{
- /*
+ /*
Connection does not have scheduler data. This happens for example
if THD belongs to a different scheduler, that is listening to extra_port.
*/
- continue;
+ DBUG_RETURN(0);
}
if(connection->abs_wait_timeout < timer->current_microtime)
{
tp_timeout_handler(connection);
}
- else
+ else
{
- set_next_timeout_check(connection->abs_wait_timeout);
+ if (connection->abs_wait_timeout < timer->current_microtime)
+ tp_timeout_handler(connection);
+ else
+ set_next_timeout_check(connection->abs_wait_timeout);
}
}
- mysql_mutex_unlock(&LOCK_thread_count);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
}
@@ -639,7 +633,8 @@ static void* timer_thread(void *param)
my_thread_init();
DBUG_ENTER("timer_thread");
- timer->next_timeout_check= ULONGLONG_MAX;
+ timer->next_timeout_check.store(std::numeric_limits<uint64_t>::max(),
+ std::memory_order_relaxed);
timer->current_microtime= microsecond_interval_timer();
for(;;)
@@ -667,8 +662,14 @@ static void* timer_thread(void *param)
}
/* Check if any client exceeded wait_timeout */
- if (timer->next_timeout_check <= timer->current_microtime)
- timeout_check(timer);
+ if (timer->next_timeout_check.load(std::memory_order_relaxed) <=
+ timer->current_microtime)
+ {
+ /* Reset next timeout check, it will be recalculated below */
+ timer->next_timeout_check.store(std::numeric_limits<uint64_t>::max(),
+ std::memory_order_relaxed);
+ server_threads.iterate(timeout_check, timer);
+ }
}
mysql_mutex_unlock(&timer->mutex);
}
@@ -917,7 +918,7 @@ static void add_thread_count(thread_group_t *thread_group, int32 count)
thread_group->thread_count += count;
/* worker starts out and end in "active" state */
thread_group->active_thread_count += count;
- my_atomic_add32(&tp_stats.num_worker_threads, count);
+ tp_stats.num_worker_threads+= count;
}
@@ -937,7 +938,7 @@ static int create_worker(thread_group_t *thread_group)
int err;
DBUG_ENTER("create_worker");
- if (tp_stats.num_worker_threads >= (int)threadpool_max_threads
+ if (tp_stats.num_worker_threads >= threadpool_max_threads
&& thread_group->thread_count >= 2)
{
err= 1;
@@ -1077,7 +1078,7 @@ void thread_group_destroy(thread_group_t *thread_group)
}
#endif
- if (my_atomic_add32(&shutdown_group_count, -1) == 1)
+ if (!--shutdown_group_count)
{
my_free(all_groups);
all_groups= 0;
@@ -1425,12 +1426,15 @@ void TP_connection_generic::wait_end()
static void set_next_timeout_check(ulonglong abstime)
{
+ auto old= pool_timer.next_timeout_check.load(std::memory_order_relaxed);
DBUG_ENTER("set_next_timeout_check");
- while(abstime < pool_timer.next_timeout_check)
+ while (abstime < old)
{
- longlong old= (longlong)pool_timer.next_timeout_check;
- my_atomic_cas64((volatile int64*)&pool_timer.next_timeout_check,
- &old, abstime);
+ if (pool_timer.next_timeout_check.
+ compare_exchange_weak(old, abstime,
+ std::memory_order_relaxed,
+ std::memory_order_relaxed))
+ break;
}
DBUG_VOID_RETURN;
}
@@ -1702,7 +1706,7 @@ int TP_pool_generic::set_pool_size(uint size)
success= (group->pollfd != INVALID_HANDLE_VALUE);
if(!success)
{
- sql_print_error("io_poll_create() failed, errno=%d\n", errno);
+ sql_print_error("io_poll_create() failed, errno=%d", errno);
}
}
mysql_mutex_unlock(&group->mutex);
diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc
index ce0ea2252c1..7d721c88f96 100644
--- a/sql/threadpool_win.cc
+++ b/sql/threadpool_win.cc
@@ -70,12 +70,16 @@ static DWORD fls;
static bool skip_completion_port_on_success = false;
+PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ()
+{
+ return pool? &callback_environ: 0;
+}
+
/*
Threadpool callbacks.
io_completion_callback - handle client request
timer_callback - handle wait timeout (kill connection)
- shm_read_callback, shm_close_callback - shared memory stuff
login_callback - user login (submitted as threadpool work)
*/
@@ -89,9 +93,6 @@ static void CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance,
static void CALLBACK work_callback(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WORK work);
-static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance,
- PVOID Context, PTP_WAIT wait,TP_WAIT_RESULT wait_result);
-
static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance);
/* Get current time as Windows time */
@@ -120,7 +121,6 @@ public:
PTP_CALLBACK_INSTANCE callback_instance;
PTP_IO io;
PTP_TIMER timer;
- PTP_WAIT shm_read;
PTP_WORK work;
bool long_callback;
@@ -139,7 +139,15 @@ struct TP_connection *new_TP_connection(CONNECT *connect)
void TP_pool_win::add(TP_connection *c)
{
- SubmitThreadpoolWork(((TP_connection_win *)c)->work);
+ if(FlsGetValue(fls))
+ {
+ /* Inside threadpool(), execute callback directly. */
+ tp_callback(c);
+ }
+ else
+ {
+ SubmitThreadpoolWork(((TP_connection_win *)c)->work);
+ }
}
@@ -149,7 +157,6 @@ TP_connection_win::TP_connection_win(CONNECT *c) :
callback_instance(0),
io(0),
timer(0),
- shm_read(0),
work(0)
{
}
@@ -170,30 +177,20 @@ int TP_connection_win::init()
case VIO_TYPE_NAMEDPIPE:
handle= (HANDLE)vio->hPipe;
break;
- case VIO_TYPE_SHARED_MEMORY:
- handle= vio->event_server_wrote;
- break;
default:
abort();
}
- if (vio_type == VIO_TYPE_SHARED_MEMORY)
- {
- CHECK_ALLOC_ERROR(shm_read= CreateThreadpoolWait(shm_read_callback, this, &callback_environ));
- }
- else
+
+ /* Performance tweaks (s. MSDN documentation)*/
+ UCHAR flags= FILE_SKIP_SET_EVENT_ON_HANDLE;
+ if (skip_completion_port_on_success)
{
- /* Performance tweaks (s. MSDN documentation)*/
- UCHAR flags= FILE_SKIP_SET_EVENT_ON_HANDLE;
- if (skip_completion_port_on_success)
- {
- flags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS;
- }
- (void)SetFileCompletionNotificationModes(handle, flags);
- /* Assign io completion callback */
- CHECK_ALLOC_ERROR(io= CreateThreadpoolIo(handle, io_completion_callback, this, &callback_environ));
+ flags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS;
}
-
+ (void)SetFileCompletionNotificationModes(handle, flags);
+ /* Assign io completion callback */
+ CHECK_ALLOC_ERROR(io= CreateThreadpoolIo(handle, io_completion_callback, this, &callback_environ));
CHECK_ALLOC_ERROR(timer= CreateThreadpoolTimer(timer_callback, this, &callback_environ));
CHECK_ALLOC_ERROR(work= CreateThreadpoolWork(work_callback, this, &callback_environ));
return 0;
@@ -214,11 +211,6 @@ int TP_connection_win::start_io()
DWORD last_error= 0;
int retval;
- if (shm_read)
- {
- SetThreadpoolWait(shm_read, handle, NULL);
- return 0;
- }
StartThreadpoolIo(io);
if (vio_type == VIO_TYPE_TCPIP || vio_type == VIO_TYPE_SSL)
@@ -297,9 +289,6 @@ TP_connection_win::~TP_connection_win()
if (io)
CloseThreadpoolIo(io);
- if (shm_read)
- CloseThreadpoolWait(shm_read);
-
if (work)
CloseThreadpoolWork(work);
@@ -313,14 +302,13 @@ TP_connection_win::~TP_connection_win()
void TP_connection_win::wait_begin(int type)
{
-
/*
Signal to the threadpool whenever callback can run long. Currently, binlog
waits are a good candidate, its waits are really long
*/
if (type == THD_WAIT_BINLOG)
{
- if (!long_callback)
+ if (!long_callback && callback_instance)
{
CallbackMayRunLong(callback_instance);
long_callback= true;
@@ -333,21 +321,26 @@ void TP_connection_win::wait_end()
/* Do we need to do anything ? */
}
-/*
- This function should be called first whenever a callback is invoked in the
+/*
+ This function should be called first whenever a callback is invoked in the
threadpool, does my_thread_init() if not yet done
*/
-extern ulong thread_created;
-static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance)
+void tp_win_callback_prolog()
{
if (FlsGetValue(fls) == NULL)
{
/* Running in new worker thread*/
FlsSetValue(fls, (void *)1);
statistic_increment(thread_created, &LOCK_status);
- InterlockedIncrement((volatile long *)&tp_stats.num_worker_threads);
+ tp_stats.num_worker_threads++;
my_thread_init();
}
+}
+
+extern ulong thread_created;
+static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance)
+{
+ tp_win_callback_prolog();
TP_connection_win *c = (TP_connection_win *)context;
c->callback_instance = instance;
c->long_callback = false;
@@ -362,7 +355,7 @@ static VOID WINAPI thread_destructor(void *data)
{
if(data)
{
- InterlockedDecrement((volatile long *)&tp_stats.num_worker_threads);
+ tp_stats.num_worker_threads--;
my_thread_end();
}
}
@@ -421,29 +414,6 @@ static VOID CALLBACK timer_callback(PTP_CALLBACK_INSTANCE instance,
}
}
-
-/*
- Shared memory read callback.
- Invoked when read event is set on connection.
-*/
-
-static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance,
- PVOID context, PTP_WAIT wait,TP_WAIT_RESULT wait_result)
-{
- TP_connection_win *c= (TP_connection_win *)context;
- /* Disarm wait. */
- SetThreadpoolWait(wait, NULL, NULL);
-
- /*
- This is an autoreset event, and one wakeup is eaten already by threadpool,
- and the current state is "not set". Thus we need to reset the event again,
- or vio_read will hang.
- */
- SetEvent(c->handle);
- tp_callback(instance, context);
-}
-
-
static void CALLBACK work_callback(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WORK work)
{
tp_callback(instance, context);
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 7f94d98179e..135d274e6c6 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -24,19 +24,20 @@
#include "debug_sync.h" // DEBUG_SYNC
#include "sql_acl.h"
#include "semisync_master.h"
+#ifdef WITH_WSREP
+#include "wsrep_trans_observer.h"
+#endif /* WITH_WSREP */
-#ifndef EMBEDDED_LIBRARY
/**
Helper: Tell tracker (if any) that transaction ended.
*/
-static void trans_track_end_trx(THD *thd)
+void trans_track_end_trx(THD *thd)
{
+#ifndef EMBEDDED_LIBRARY
if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
thd->session_tracker.transaction_info.end_trx(thd);
-}
-#else
-#define trans_track_end_trx(A) do{}while(0)
#endif //EMBEDDED_LIBRARY
+}
/**
@@ -59,7 +60,6 @@ void trans_reset_one_shot_chistics(THD *thd)
/* Conditions under which the transaction state must not change. */
static bool trans_check(THD *thd)
{
- enum xa_states xa_state= thd->transaction.xid_state.xa_state;
DBUG_ENTER("trans_check");
/*
@@ -70,8 +70,8 @@ static bool trans_check(THD *thd)
if (unlikely(thd->in_sub_stmt))
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
- if (xa_state != XA_NOTR)
- my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
+ if (thd->transaction.xid_state.is_explicit_XA())
+ thd->transaction.xid_state.er_xaer_rmfail();
else
DBUG_RETURN(FALSE);
@@ -80,67 +80,6 @@ static bool trans_check(THD *thd)
/**
- Mark a XA transaction as rollback-only if the RM unilaterally
- rolled back the transaction branch.
-
- @note If a rollback was requested by the RM, this function sets
- the appropriate rollback error code and transits the state
- to XA_ROLLBACK_ONLY.
-
- @return TRUE if transaction was rolled back or if the transaction
- state is XA_ROLLBACK_ONLY. FALSE otherwise.
-*/
-static bool xa_trans_rolled_back(XID_STATE *xid_state)
-{
- if (xid_state->rm_error)
- {
- switch (xid_state->rm_error) {
- case ER_LOCK_WAIT_TIMEOUT:
- my_error(ER_XA_RBTIMEOUT, MYF(0));
- break;
- case ER_LOCK_DEADLOCK:
- my_error(ER_XA_RBDEADLOCK, MYF(0));
- break;
- default:
- my_error(ER_XA_RBROLLBACK, MYF(0));
- }
- xid_state->xa_state= XA_ROLLBACK_ONLY;
- }
-
- return (xid_state->xa_state == XA_ROLLBACK_ONLY);
-}
-
-
-/**
- Rollback the active XA transaction.
-
- @note Resets rm_error before calling ha_rollback(), so
- the thd->transaction.xid structure gets reset
- by ha_rollback() / THD::transaction::cleanup().
-
- @return TRUE if the rollback failed, FALSE otherwise.
-*/
-
-static bool xa_trans_force_rollback(THD *thd)
-{
- /*
- We must reset rm_error before calling ha_rollback(),
- so thd->transaction.xid structure gets reset
- by ha_rollback()/THD::transaction::cleanup().
- */
- thd->transaction.xid_state.rm_error= 0;
- if (WSREP_ON)
- wsrep_register_hton(thd, TRUE);
- if (ha_rollback_trans(thd, true))
- {
- my_error(ER_XAER_RMERR, MYF(0));
- return true;
- }
- return false;
-}
-
-
-/**
Begin a new transaction.
@note Beginning a transaction implicitly commits any current
@@ -169,14 +108,16 @@ bool trans_begin(THD *thd, uint flags)
(thd->variables.option_bits & OPTION_TABLE_LOCK))
{
thd->variables.option_bits&= ~OPTION_TABLE_LOCK;
- if (WSREP_ON)
- wsrep_register_hton(thd, TRUE);
thd->server_status&=
~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
res= MY_TEST(ha_commit_trans(thd, TRUE));
- if (WSREP_ON)
- wsrep_post_commit(thd, TRUE);
+#ifdef WITH_WSREP
+ if (wsrep_thd_is_local(thd))
+ {
+ res= res || wsrep_after_statement(thd);
+ }
+#endif /* WITH_WSREP */
}
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
@@ -237,9 +178,14 @@ bool trans_begin(THD *thd, uint flags)
}
#ifdef WITH_WSREP
- thd->wsrep_PA_safe= true;
- if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd))
- DBUG_RETURN(TRUE);
+ if (wsrep_thd_is_local(thd))
+ {
+ if (wsrep_sync_wait(thd))
+ DBUG_RETURN(TRUE);
+ if (!thd->tx_read_only &&
+ wsrep_start_transaction(thd, thd->wsrep_next_trx_id()))
+ DBUG_RETURN(TRUE);
+ }
#endif /* WITH_WSREP */
thd->variables.option_bits|= OPTION_BEGIN;
@@ -284,8 +230,6 @@ bool trans_commit(THD *thd)
if (trans_check(thd))
DBUG_RETURN(TRUE);
- if (WSREP_ON)
- wsrep_register_hton(thd, TRUE);
thd->server_status&=
~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
@@ -296,8 +240,6 @@ bool trans_commit(THD *thd)
mysql_mutex_assert_not_owner(&LOCK_after_binlog_sync);
mysql_mutex_assert_not_owner(&LOCK_commit_ordered);
- if (WSREP_ON)
- wsrep_post_commit(thd, TRUE);
/*
if res is non-zero, then ha_commit_trans has rolled back the
transaction, so the hooks for rollback will be called.
@@ -353,14 +295,10 @@ bool trans_commit_implicit(THD *thd)
/* Safety if one did "drop table" on locked tables */
if (!thd->locked_tables_mode)
thd->variables.option_bits&= ~OPTION_TABLE_LOCK;
- if (WSREP_ON)
- wsrep_register_hton(thd, TRUE);
thd->server_status&=
~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
res= MY_TEST(ha_commit_trans(thd, TRUE));
- if (WSREP_ON)
- wsrep_post_commit(thd, TRUE);
}
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
@@ -394,14 +332,9 @@ bool trans_rollback(THD *thd)
int res;
DBUG_ENTER("trans_rollback");
-#ifdef WITH_WSREP
- thd->wsrep_PA_safe= true;
-#endif /* WITH_WSREP */
if (trans_check(thd))
DBUG_RETURN(TRUE);
- if (WSREP_ON)
- wsrep_register_hton(thd, TRUE);
thd->server_status&=
~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
@@ -500,14 +433,10 @@ bool trans_commit_stmt(THD *thd)
if (thd->transaction.stmt.ha_list)
{
- if (WSREP_ON)
- wsrep_register_hton(thd, FALSE);
res= ha_commit_trans(thd, FALSE);
if (! thd->in_active_multi_stmt_transaction())
{
trans_reset_one_shot_chistics(thd);
- if (WSREP_ON)
- wsrep_post_commit(thd, FALSE);
}
}
@@ -563,8 +492,6 @@ bool trans_rollback_stmt(THD *thd)
if (thd->transaction.stmt.ha_list)
{
- if (WSREP_ON)
- wsrep_register_hton(thd, FALSE);
ha_rollback_trans(thd, FALSE);
if (! thd->in_active_multi_stmt_transaction())
trans_reset_one_shot_chistics(thd);
@@ -619,9 +546,6 @@ bool trans_savepoint(THD *thd, LEX_CSTRING name)
if (thd->transaction.xid_state.check_has_uncommitted_xa())
DBUG_RETURN(TRUE);
- if (WSREP_ON)
- wsrep_register_hton(thd, thd->in_multi_stmt_transaction_mode());
-
sv= find_savepoint(thd, name);
if (*sv) /* old savepoint of the same name exists */
@@ -698,9 +622,6 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_CSTRING name)
if (thd->transaction.xid_state.check_has_uncommitted_xa())
DBUG_RETURN(TRUE);
- if (WSREP_ON)
- wsrep_register_hton(thd, thd->in_multi_stmt_transaction_mode());
-
/**
Checking whether it is safe to release metadata locks acquired after
savepoint, if rollback to savepoint is successful.
@@ -724,7 +645,8 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_CSTRING name)
logging is off.
*/
bool mdl_can_safely_rollback_to_savepoint=
- (!(mysql_bin_log.is_open() && thd->variables.sql_log_bin) ||
+ (!((WSREP_EMULATE_BINLOG_NNULL(thd) || mysql_bin_log.is_open())
+ && thd->variables.sql_log_bin) ||
ha_rollback_to_savepoint_can_release_mdl(thd));
if (ha_rollback_to_savepoint(thd, sv))
@@ -778,279 +700,3 @@ bool trans_release_savepoint(THD *thd, LEX_CSTRING name)
DBUG_RETURN(MY_TEST(res));
}
-
-
-/**
- Starts an XA transaction with the given xid value.
-
- @param thd Current thread
-
- @retval FALSE Success
- @retval TRUE Failure
-*/
-
-bool trans_xa_start(THD *thd)
-{
- enum xa_states xa_state= thd->transaction.xid_state.xa_state;
- DBUG_ENTER("trans_xa_start");
-
- if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_RESUME)
- {
- bool not_equal= !thd->transaction.xid_state.xid.eq(thd->lex->xid);
- if (not_equal)
- my_error(ER_XAER_NOTA, MYF(0));
- else
- thd->transaction.xid_state.xa_state= XA_ACTIVE;
- DBUG_RETURN(not_equal);
- }
-
- /* TODO: JOIN is not supported yet. */
- if (thd->lex->xa_opt != XA_NONE)
- my_error(ER_XAER_INVAL, MYF(0));
- else if (xa_state != XA_NOTR)
- my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
- else if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
- my_error(ER_XAER_OUTSIDE, MYF(0));
- else if (!trans_begin(thd))
- {
- DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
- thd->transaction.xid_state.xa_state= XA_ACTIVE;
- thd->transaction.xid_state.rm_error= 0;
- thd->transaction.xid_state.xid.set(thd->lex->xid);
- if (xid_cache_insert(thd, &thd->transaction.xid_state))
- {
- thd->transaction.xid_state.xa_state= XA_NOTR;
- thd->transaction.xid_state.xid.null();
- trans_rollback(thd);
- DBUG_RETURN(true);
- }
- DBUG_RETURN(FALSE);
- }
-
- DBUG_RETURN(TRUE);
-}
-
-
-/**
- Put a XA transaction in the IDLE state.
-
- @param thd Current thread
-
- @retval FALSE Success
- @retval TRUE Failure
-*/
-
-bool trans_xa_end(THD *thd)
-{
- DBUG_ENTER("trans_xa_end");
-
- /* TODO: SUSPEND and FOR MIGRATE are not supported yet. */
- if (thd->lex->xa_opt != XA_NONE)
- my_error(ER_XAER_INVAL, MYF(0));
- else if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
- my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xid_state.xa_state]);
- else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
- my_error(ER_XAER_NOTA, MYF(0));
- else if (!xa_trans_rolled_back(&thd->transaction.xid_state))
- thd->transaction.xid_state.xa_state= XA_IDLE;
-
- DBUG_RETURN(thd->is_error() ||
- thd->transaction.xid_state.xa_state != XA_IDLE);
-}
-
-
-/**
- Put a XA transaction in the PREPARED state.
-
- @param thd Current thread
-
- @retval FALSE Success
- @retval TRUE Failure
-*/
-
-bool trans_xa_prepare(THD *thd)
-{
- DBUG_ENTER("trans_xa_prepare");
-
- if (thd->transaction.xid_state.xa_state != XA_IDLE)
- my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xid_state.xa_state]);
- else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
- my_error(ER_XAER_NOTA, MYF(0));
- else if (ha_prepare(thd))
- {
- xid_cache_delete(thd, &thd->transaction.xid_state);
- thd->transaction.xid_state.xa_state= XA_NOTR;
- my_error(ER_XA_RBROLLBACK, MYF(0));
- }
- else
- thd->transaction.xid_state.xa_state= XA_PREPARED;
-
- DBUG_RETURN(thd->is_error() ||
- thd->transaction.xid_state.xa_state != XA_PREPARED);
-}
-
-
-/**
- Commit and terminate the a XA transaction.
- Transactional locks are released if transaction ended
-
- @param thd Current thread
-
- @retval FALSE Success
- @retval TRUE Failure
-
-*/
-
-bool trans_xa_commit(THD *thd)
-{
- bool res= TRUE;
- enum xa_states xa_state= thd->transaction.xid_state.xa_state;
- DBUG_ENTER("trans_xa_commit");
-
- if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
- {
- if (thd->fix_xid_hash_pins())
- {
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- DBUG_RETURN(TRUE);
- }
-
- XID_STATE *xs= xid_cache_search(thd, thd->lex->xid);
- res= !xs;
- if (res)
- my_error(ER_XAER_NOTA, MYF(0));
- else
- {
- res= xa_trans_rolled_back(xs);
- ha_commit_or_rollback_by_xid(thd->lex->xid, !res);
- xid_cache_delete(thd, xs);
- }
- DBUG_RETURN(res);
- }
-
- if (xa_trans_rolled_back(&thd->transaction.xid_state))
- {
- xa_trans_force_rollback(thd);
- res= thd->is_error();
- }
- else if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE)
- {
- if (WSREP_ON)
- wsrep_register_hton(thd, TRUE);
- int r= ha_commit_trans(thd, TRUE);
- if ((res= MY_TEST(r)))
- my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
- if (WSREP_ON)
- wsrep_post_commit(thd, TRUE);
- }
- else if (xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE)
- {
- MDL_request mdl_request;
-
- /*
- Acquire metadata lock which will ensure that COMMIT is blocked
- by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
- progress blocks FTWRL).
-
- We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
- */
- mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE,
- MDL_TRANSACTION);
-
- if (thd->mdl_context.acquire_lock(&mdl_request,
- thd->variables.lock_wait_timeout))
- {
- if (WSREP_ON)
- wsrep_register_hton(thd, TRUE);
- ha_rollback_trans(thd, TRUE);
- my_error(ER_XAER_RMERR, MYF(0));
- }
- else
- {
- DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
-
- res= MY_TEST(ha_commit_one_phase(thd, 1));
- if (res)
- my_error(ER_XAER_RMERR, MYF(0));
- }
- }
- else
- {
- my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
- DBUG_RETURN(TRUE);
- }
-
- thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
- thd->transaction.all.reset();
- thd->server_status&=
- ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
- DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
- xid_cache_delete(thd, &thd->transaction.xid_state);
- thd->transaction.xid_state.xa_state= XA_NOTR;
-
- trans_track_end_trx(thd);
- thd->mdl_context.release_transactional_locks();
-
- DBUG_RETURN(res);
-}
-
-
-/**
- Roll back and terminate a XA transaction.
- Transactional locks are released if transaction ended
-
- @param thd Current thread
-
- @retval FALSE Success
- @retval TRUE Failure
-*/
-
-bool trans_xa_rollback(THD *thd)
-{
- bool res= TRUE;
- enum xa_states xa_state= thd->transaction.xid_state.xa_state;
- DBUG_ENTER("trans_xa_rollback");
-
- if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
- {
- if (thd->fix_xid_hash_pins())
- {
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- DBUG_RETURN(TRUE);
- }
-
- XID_STATE *xs= xid_cache_search(thd, thd->lex->xid);
- if (!xs)
- my_error(ER_XAER_NOTA, MYF(0));
- else
- {
- xa_trans_rolled_back(xs);
- ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
- xid_cache_delete(thd, xs);
- }
- DBUG_RETURN(thd->get_stmt_da()->is_error());
- }
-
- if (xa_state != XA_IDLE && xa_state != XA_PREPARED && xa_state != XA_ROLLBACK_ONLY)
- {
- my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
- DBUG_RETURN(TRUE);
- }
-
- res= xa_trans_force_rollback(thd);
-
- thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
- thd->transaction.all.reset();
- thd->server_status&=
- ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
- DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
- xid_cache_delete(thd, &thd->transaction.xid_state);
- thd->transaction.xid_state.xa_state= XA_NOTR;
-
- trans_track_end_trx(thd);
- thd->mdl_context.release_transactional_locks();
-
- DBUG_RETURN(res);
-}
diff --git a/sql/transaction.h b/sql/transaction.h
index 56cc6f8f011..fe0129fa8bc 100644
--- a/sql/transaction.h
+++ b/sql/transaction.h
@@ -24,6 +24,8 @@
class THD;
+void trans_track_end_trx(THD *thd);
+
bool trans_begin(THD *thd, uint flags= 0);
bool trans_commit(THD *thd);
bool trans_commit_implicit(THD *thd);
@@ -37,12 +39,6 @@ bool trans_savepoint(THD *thd, LEX_CSTRING name);
bool trans_rollback_to_savepoint(THD *thd, LEX_CSTRING name);
bool trans_release_savepoint(THD *thd, LEX_CSTRING name);
-bool trans_xa_start(THD *thd);
-bool trans_xa_end(THD *thd);
-bool trans_xa_prepare(THD *thd);
-bool trans_xa_commit(THD *thd);
-bool trans_xa_rollback(THD *thd);
-
void trans_reset_one_shot_chistics(THD *thd);
#endif /* TRANSACTION_H */
diff --git a/sql/tztime.cc b/sql/tztime.cc
index 4ef20d79d5d..05b8389a521 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -2453,7 +2453,7 @@ print_tz_leaps_as_sql(const TIME_ZONE_INFO *sp)
printf("\\d |\n"
"IF (select count(*) from information_schema.global_variables where\n"
"variable_name='wsrep_on' and variable_value='ON') = 1 THEN\n"
- "ALTER TABLE time_zone_leap_second ENGINE=MyISAM;\n"
+ "ALTER TABLE time_zone_leap_second ENGINE=Aria;\n"
"END IF|\n"
"\\d ;\n");
@@ -2781,14 +2781,14 @@ main(int argc, char **argv)
if(!opt_skip_write_binlog)
{
- // Fall back to MyISAM
+ // Fall back to Aria
printf("\\d |\n"
"IF (select count(*) from information_schema.global_variables where\n"
"variable_name='wsrep_on' and variable_value='ON') = 1 THEN\n"
- "ALTER TABLE time_zone ENGINE=MyISAM;\n"
- "ALTER TABLE time_zone_name ENGINE=MyISAM;\n"
- "ALTER TABLE time_zone_transition ENGINE=MyISAM;\n"
- "ALTER TABLE time_zone_transition_type ENGINE=MyISAM;\n"
+ "ALTER TABLE time_zone ENGINE=Aria;\n"
+ "ALTER TABLE time_zone_name ENGINE=Aria;\n"
+ "ALTER TABLE time_zone_transition ENGINE=Aria;\n"
+ "ALTER TABLE time_zone_transition_type ENGINE=Aria;\n"
"END IF|\n"
"\\d ;\n");
}
diff --git a/sql/udf_example.c b/sql/udf_example.c
index 26afa538e88..e4757de880d 100644
--- a/sql/udf_example.c
+++ b/sql/udf_example.c
@@ -173,6 +173,13 @@ void avgcost_reset( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error
void avgcost_clear( UDF_INIT* initid, char* is_null, char *error );
void avgcost_add( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error );
double avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error );
+my_bool avg2_init( UDF_INIT* initid, UDF_ARGS* args, char* message );
+void avg2_deinit( UDF_INIT* initid );
+void avg2_reset( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error );
+void avg2_clear( UDF_INIT* initid, char* is_null, char *error );
+void avg2_add( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error );
+void avg2_remove( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error );
+double avg2( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error );
my_bool is_const_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
char *is_const(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long
*length, char *is_null, char *error);
@@ -1049,6 +1056,138 @@ avgcost( UDF_INIT* initid, UDF_ARGS* args __attribute__((unused)),
return data->totalprice/(double)data->totalquantity;
}
+
+/*
+** Average 2 (number, sum)*/
+struct avg2_data
+{
+ ulonglong count;
+ double sum;
+};
+
+
+my_bool
+avg2_init( UDF_INIT* initid, UDF_ARGS* args, char* message )
+{
+ struct avg2_data* data;
+
+ if (args->arg_count != 2)
+ {
+ strcpy(
+ message,
+ "wrong number of arguments: AVG2() requires two arguments"
+ );
+ return 1;
+ }
+
+ if ((args->arg_type[0] != INT_RESULT) || (args->arg_type[1] != REAL_RESULT) )
+ {
+ strcpy(
+ message,
+ "wrong argument type: AVG2() requires an INT and a REAL"
+ );
+ return 1;
+ }
+
+ /*
+ ** force arguments to double.
+ */
+ /*args->arg_type[0] = REAL_RESULT;
+ args->arg_type[1] = REAL_RESULT;*/
+
+ initid->maybe_null = 0; /* The result may be null */
+ initid->decimals = 4; /* We want 4 decimals in the result */
+ initid->max_length = 20; /* 6 digits + . + 10 decimals */
+
+ if (!(data = (struct avg2_data*) malloc(sizeof(struct avg2_data))))
+ {
+ strmov(message,"Couldn't allocate memory");
+ return 1;
+ }
+ data->count = 0;
+ data->sum = 0.0;
+
+ initid->ptr = (char*)data;
+
+ return 0;
+}
+
+void
+avg2_deinit( UDF_INIT* initid )
+{
+ free(initid->ptr);
+}
+
+
+/* This is only for MySQL 4.0 compability */
+void
+avg2_reset(UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message)
+{
+ avgcost_clear(initid, is_null, message);
+ avgcost_add(initid, args, is_null, message);
+}
+
+/* This is needed to get things to work in MySQL 4.1.1 and above */
+
+void
+avg2_clear(UDF_INIT* initid, char* is_null __attribute__((unused)),
+ char* message __attribute__((unused)))
+{
+ struct avg2_data* data = (struct avg2_data*)initid->ptr;
+ data->sum= 0.0;
+ data->count= 0;
+}
+
+
+void
+avg2_add(UDF_INIT* initid, UDF_ARGS* args,
+ char* is_null __attribute__((unused)),
+ char* message __attribute__((unused)))
+{
+ if (args->args[0] && args->args[1])
+ {
+ struct avg2_data* data = (struct avg2_data*)initid->ptr;
+ longlong quantity = *((longlong*)args->args[0]);
+ double sum = *((double*)args->args[1]);
+
+ data->count += quantity;
+ data->sum += sum;
+ }
+}
+
+
+void
+avg2_remove(UDF_INIT* initid, UDF_ARGS* args,
+ char* is_null __attribute__((unused)),
+ char* message __attribute__((unused)))
+{
+ if (args->args[0] && args->args[1])
+ {
+ struct avg2_data* data = (struct avg2_data*)initid->ptr;
+ longlong quantity = *((longlong*)args->args[0]);
+ double sum = *((double*)args->args[1]);
+
+ data->count -= quantity;
+ data->sum -= sum;
+ }
+}
+
+
+double
+avg2( UDF_INIT* initid, UDF_ARGS* args __attribute__((unused)),
+ char* is_null, char* error __attribute__((unused)))
+{
+ struct avg2_data* data = (struct avg2_data*)initid->ptr;
+ if (!data->count)
+ {
+ *is_null = 1;
+ return 0.0;
+ }
+
+ *is_null = 0;
+ return data->sum/(double)data->count;
+}
+
my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args,
char *message);
char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result,
diff --git a/sql/udf_example.def b/sql/udf_example.def
index 74230b638bf..903c2b74893 100644
--- a/sql/udf_example.def
+++ b/sql/udf_example.def
@@ -23,6 +23,13 @@ EXPORTS
avgcost_add
avgcost_clear
avgcost
+ avg2_init
+ avg2_deinit
+ avg2_reset
+ avg2_add
+ avg2_remove
+ avg2_clear
+ avg2
is_const
is_const_init
check_const_len
diff --git a/sql/unireg.cc b/sql/unireg.cc
index 86a58db2f9d..8340901175a 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2009, 2018, MariaDB Corporation
+ Copyright (c) 2009, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,7 +39,7 @@
/* threshold for safe_alloca */
#define ALLOCA_THRESHOLD 2048
-static uint pack_keys(uchar *,uint, KEY *, ulong);
+static uint pack_keys(uchar *,uint, KEY *, ulong, uint);
static bool pack_header(THD *, uchar *, List<Create_field> &, HA_CREATE_INFO *,
ulong, handler *);
static bool pack_vcols(String *, List<Create_field> &, List<Virtual_column_info> *);
@@ -57,6 +57,7 @@ static bool make_empty_rec(THD *, uchar *, uint, List<Create_field> &, uint,
*/
static uchar *extra2_write_len(uchar *pos, size_t len)
{
+ DBUG_ASSERT(len);
if (len <= 255)
*pos++= (uchar)len;
else
@@ -72,19 +73,24 @@ static uchar *extra2_write_len(uchar *pos, size_t len)
return pos;
}
+static uchar* extra2_write_str(uchar *pos, const LEX_CSTRING &str)
+{
+ pos= extra2_write_len(pos, str.length);
+ memcpy(pos, str.str, str.length);
+ return pos + str.length;
+}
+
static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type,
- const LEX_CSTRING *str)
+ const LEX_CSTRING &str)
{
*pos++ = type;
- pos= extra2_write_len(pos, str->length);
- memcpy(pos, str->str, str->length);
- return pos + str->length;
+ return extra2_write_str(pos, str);
}
static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type,
- LEX_CUSTRING *str)
+ const LEX_CUSTRING &str)
{
- return extra2_write(pos, type, reinterpret_cast<LEX_CSTRING *>(str));
+ return extra2_write(pos, type, *reinterpret_cast<const LEX_CSTRING*>(&str));
}
static uchar *extra2_write_field_properties(uchar *pos,
@@ -106,25 +112,18 @@ static uchar *extra2_write_field_properties(uchar *pos,
return pos;
}
-static const bool ROW_START = true;
-static const bool ROW_END = false;
-
-static inline
-uint16
-vers_get_field(HA_CREATE_INFO *create_info, List<Create_field> &create_fields, bool row_start)
+static uint16
+get_fieldno_by_name(HA_CREATE_INFO *create_info, List<Create_field> &create_fields,
+ const Lex_ident &field_name)
{
- DBUG_ASSERT(create_info->versioned());
-
List_iterator<Create_field> it(create_fields);
Create_field *sql_field = NULL;
- const Lex_ident row_field= row_start ? create_info->vers_info.as_row.start
- : create_info->vers_info.as_row.end;
- DBUG_ASSERT(row_field);
+ DBUG_ASSERT(field_name);
for (unsigned field_no = 0; (sql_field = it++); ++field_no)
{
- if (row_field.streq(sql_field->field_name))
+ if (field_name.streq(sql_field->field_name))
{
DBUG_ASSERT(field_no <= uint16(~0U));
return uint16(field_no);
@@ -149,6 +148,11 @@ bool has_extra2_field_flags(List<Create_field> &create_fields)
return false;
}
+static size_t extra2_str_size(size_t len)
+{
+ return (len > 255 ? 3 : 1) + len;
+}
+
/**
Create a frm (table definition) file
@@ -164,7 +168,7 @@ bool has_extra2_field_flags(List<Create_field> &create_fields)
or null LEX_CUSTRING (str==0) in case of an error.
*/
-LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table,
+LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table,
HA_CREATE_INFO *create_info,
List<Create_field> &create_fields,
uint keys, KEY *key_info, handler *db_file)
@@ -176,6 +180,12 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table,
ulong data_offset;
uint options_len;
uint gis_extra2_len= 0;
+ size_t period_info_len= create_info->period_info.name
+ ? extra2_str_size(create_info->period_info.name.length)
+ + extra2_str_size(create_info->period_info.constr->name.length)
+ + 2 * frm_fieldno_size
+ : 0;
+ uint e_unique_hash_extra_parts= 0;
uchar fileinfo[FRM_HEADER_SIZE],forminfo[FRM_FORMINFO_SIZE];
const partition_info *part_info= IF_PARTITIONING(thd->work_part_info, 0);
bool error;
@@ -238,7 +248,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table,
DBUG_PRINT("info", ("Options length: %u", options_len));
if (validate_comment_length(thd, &create_info->comment, TABLE_COMMENT_MAXLEN,
- ER_TOO_LONG_TABLE_COMMENT, table->str))
+ ER_TOO_LONG_TABLE_COMMENT, table.str))
DBUG_RETURN(frm);
/*
If table comment is longer than TABLE_COMMENT_INLINE_MAXLEN bytes,
@@ -272,28 +282,35 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table,
prepare_frm_header(thd, reclength, fileinfo, create_info, keys, key_info);
/* one byte for a type, one or three for a length */
- size_t extra2_size= 1 + 1 + create_info->tabledef_version.length;
+ size_t extra2_size= 1 + extra2_str_size(create_info->tabledef_version.length);
if (options_len)
- extra2_size+= 1 + (options_len > 255 ? 3 : 1) + options_len;
+ extra2_size+= 1 + extra2_str_size(options_len);
if (part_info)
- extra2_size+= 1 + 1 + hton_name(part_info->default_engine_type)->length;
+ extra2_size+= 1 + extra2_str_size(hton_name(part_info->default_engine_type)->length);
if (gis_extra2_len)
- extra2_size+= 1 + (gis_extra2_len > 255 ? 3 : 1) + gis_extra2_len;
+ extra2_size+= 1 + extra2_str_size(gis_extra2_len);
if (create_info->versioned())
{
- extra2_size+= 1 + 1 + 2 * sizeof(uint16);
+ extra2_size+= 1 + extra2_str_size(2 * frm_fieldno_size);
+ }
+
+ if (create_info->period_info.name)
+ {
+ extra2_size+= 1 + extra2_str_size(period_info_len);
}
bool has_extra2_field_flags_= has_extra2_field_flags(create_fields);
if (has_extra2_field_flags_)
{
- extra2_size+= 1 + (create_fields.elements > 255 ? 3 : 1) +
- create_fields.elements;
+ extra2_size+= 1 + extra2_str_size(create_fields.elements);
}
+ for (i= 0; i < keys; i++)
+ if (key_info[i].algorithm == HA_KEY_ALG_LONG_HASH)
+ e_unique_hash_extra_parts++;
key_buff_length= uint4korr(fileinfo+47);
frm.length= FRM_HEADER_SIZE; // fileinfo;
@@ -313,7 +330,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table,
if (frm.length > FRM_MAX_SIZE ||
create_info->expression_length > UINT_MAX32)
{
- my_error(ER_TABLE_DEFINITION_TOO_BIG, MYF(0), table->str);
+ my_error(ER_TABLE_DEFINITION_TOO_BIG, MYF(0), table.str);
DBUG_RETURN(frm);
}
@@ -326,11 +343,11 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table,
pos = frm_ptr + 64;
compile_time_assert(EXTRA2_TABLEDEF_VERSION != '/');
pos= extra2_write(pos, EXTRA2_TABLEDEF_VERSION,
- &create_info->tabledef_version);
+ create_info->tabledef_version);
if (part_info)
pos= extra2_write(pos, EXTRA2_DEFAULT_PART_ENGINE,
- hton_name(part_info->default_engine_type));
+ *hton_name(part_info->default_engine_type));
if (options_len)
{
@@ -349,14 +366,32 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table,
}
#endif /*HAVE_SPATIAL*/
+ // PERIOD
+ if (create_info->period_info.is_set())
+ {
+ *pos++= EXTRA2_APPLICATION_TIME_PERIOD;
+ pos= extra2_write_len(pos, period_info_len);
+ pos= extra2_write_str(pos, create_info->period_info.name);
+ pos= extra2_write_str(pos, create_info->period_info.constr->name);
+
+ store_frm_fieldno(pos, get_fieldno_by_name(create_info, create_fields,
+ create_info->period_info.period.start));
+ pos+= frm_fieldno_size;
+ store_frm_fieldno(pos, get_fieldno_by_name(create_info, create_fields,
+ create_info->period_info.period.end));
+ pos+= frm_fieldno_size;
+ }
+
if (create_info->versioned())
{
*pos++= EXTRA2_PERIOD_FOR_SYSTEM_TIME;
- *pos++= 2 * sizeof(uint16);
- int2store(pos, vers_get_field(create_info, create_fields, ROW_START));
- pos+= sizeof(uint16);
- int2store(pos, vers_get_field(create_info, create_fields, ROW_END));
- pos+= sizeof(uint16);
+ *pos++= 2 * frm_fieldno_size;
+ store_frm_fieldno(pos, get_fieldno_by_name(create_info, create_fields,
+ create_info->vers_info.as_row.start));
+ pos+= frm_fieldno_size;
+ store_frm_fieldno(pos, get_fieldno_by_name(create_info, create_fields,
+ create_info->vers_info.as_row.end));
+ pos+= frm_fieldno_size;
}
if (has_extra2_field_flags_)
@@ -366,13 +401,13 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table,
pos+= 4;
DBUG_ASSERT(pos == frm_ptr + uint2korr(fileinfo+6));
- key_info_length= pack_keys(pos, keys, key_info, data_offset);
+ key_info_length= pack_keys(pos, keys, key_info, data_offset, e_unique_hash_extra_parts);
if (key_info_length > UINT_MAX16)
{
my_printf_error(ER_CANT_CREATE_TABLE,
"Cannot create table %`s: index information is too long. "
"Decrease number of indexes or use shorter index names or shorter comments.",
- MYF(0), table->str);
+ MYF(0), table.str);
goto err;
}
@@ -476,60 +511,10 @@ err:
}
-/**
- Create a frm (table definition) file and the tables
-
- @param thd Thread handler
- @param frm Binary frm image of the table to create
- @param path Name of file (including database, without .frm)
- @param db Data base name
- @param table_name Table name
- @param create_info create info parameters
- @param file Handler to use or NULL if only frm needs to be created
-
- @retval 0 ok
- @retval 1 error
-*/
-
-int rea_create_table(THD *thd, LEX_CUSTRING *frm,
- const char *path, const char *db, const char *table_name,
- HA_CREATE_INFO *create_info, handler *file,
- bool no_ha_create_table)
-{
- DBUG_ENTER("rea_create_table");
-
- if (no_ha_create_table)
- {
- if (writefrm(path, db, table_name, true, frm->str, frm->length))
- goto err_frm;
- }
-
- if (thd->variables.keep_files_on_create)
- create_info->options|= HA_CREATE_KEEP_FILES;
-
- if (file->ha_create_partitioning_metadata(path, NULL, CHF_CREATE_FLAG))
- goto err_part;
-
- if (!no_ha_create_table)
- {
- if (ha_create_table(thd, path, db, table_name, create_info, frm))
- goto err_part;
- }
-
- DBUG_RETURN(0);
-
-err_part:
- file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG);
-err_frm:
- deletefrm(path);
- DBUG_RETURN(1);
-} /* rea_create_table */
-
-
/* Pack keyinfo and keynames to keybuff for save in form-file. */
static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
- ulong data_offset)
+ ulong data_offset, uint e_unique_hash_extra_parts)
{
uint key_parts,length;
uchar *pos, *keyname_pos;
@@ -591,6 +576,7 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
}
}
+ key_parts+= e_unique_hash_extra_parts;
if (key_count > 127 || key_parts > 127)
{
keybuff[0]= (key_count & 0x7f) | 0x80;
@@ -657,7 +643,7 @@ static bool pack_vcols(String *buf, List<Create_field> &create_fields,
for (uint field_nr=0; (field= it++); field_nr++)
{
- if (field->vcol_info)
+ if (field->vcol_info && field->vcol_info->expr)
if (pack_expression(buf, field->vcol_info, field_nr,
field->vcol_info->stored_in_db
? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL))
@@ -905,32 +891,12 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
while ((field=it++))
{
uint recpos;
- int2store(buff+3, field->length);
/* The +1 is here becasue the col offset in .frm file have offset 1 */
recpos= field->offset+1 + (uint) data_offset;
int3store(buff+5,recpos);
- int2store(buff+8,field->pack_flag);
- buff[10]= (uchar) field->unireg_check;
buff[12]= (uchar) field->interval_id;
- buff[13]= (uchar) field->real_field_type();
- if (field->real_field_type() == MYSQL_TYPE_GEOMETRY)
- {
- buff[11]= 0;
- buff[14]= (uchar) field->geom_type;
-#ifndef HAVE_SPATIAL
- DBUG_ASSERT(0); // Should newer happen
-#endif
- }
- else if (field->charset)
- {
- buff[11]= (uchar) (field->charset->number >> 8);
- buff[14]= (uchar) field->charset->number;
- }
- else
- {
- buff[11]= buff[14]= 0; // Numerical
- }
-
+ buff[13]= (uchar) field->type_handler()->real_field_type();
+ field->type_handler()->Column_definition_attributes_frm_pack(field, buff);
int2store(buff+15, field->comment.length);
comment_length+= field->comment.length;
set_if_bigger(int_count,field->interval_id);
@@ -1020,13 +986,37 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
DBUG_RETURN(0);
}
+
+static bool make_empty_rec_store_default(THD *thd, Field *regfield,
+ Create_field *field)
+{
+ Virtual_column_info *default_value= field->default_value;
+ if (!field->vers_sys_field() && default_value && !default_value->flags)
+ {
+ Item *expr= default_value->expr;
+ // may be already fixed if ALTER TABLE
+ if (expr->fix_fields_if_needed(thd, &expr))
+ return true;
+ DBUG_ASSERT(expr == default_value->expr); // Should not change
+ if (regfield->make_empty_rec_store_default_value(thd, expr))
+ {
+ my_error(ER_INVALID_DEFAULT, MYF(0), regfield->field_name.str);
+ return true;
+ }
+ return false;
+ }
+ regfield->make_empty_rec_reset(thd);
+ return false;
+}
+
+
/* save an empty record on start of formfile */
static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
List<Create_field> &create_fields,
uint reclength, ulong data_offset)
{
- int error= 0;
+ int error= false;
uint null_count;
uchar *null_pos;
TABLE table;
@@ -1054,24 +1044,19 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
List_iterator<Create_field> it(create_fields);
while ((field=it++))
{
+ Record_addr addr(buff + field->offset + data_offset,
+ null_pos + null_count / 8, null_count & 7);
+ Column_definition_attributes tmp(*field);
+ tmp.interval= field->save_interval ?
+ field->save_interval : field->interval;
/* regfield don't have to be deleted as it's allocated on THD::mem_root */
- Field *regfield= make_field(&share, thd->mem_root,
- buff+field->offset + data_offset,
- (uint32)field->length,
- null_pos + null_count / 8,
- null_count & 7,
- field->pack_flag,
- field->type_handler(),
- field->charset,
- field->geom_type, field->srid,
- field->unireg_check,
- field->save_interval ? field->save_interval
- : field->interval,
- &field->field_name,
- field->flags);
+ Field *regfield= tmp.make_field(&share, thd->mem_root, &addr,
+ field->type_handler(),
+ &field->field_name,
+ field->flags);
if (!regfield)
{
- error= 1;
+ error= true;
goto err; // End of memory
}
@@ -1088,38 +1073,10 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
!f_bit_as_char(field->pack_flag))
null_count+= field->length & 7;
- if (field->default_value && !field->default_value->flags &&
- !field->vers_sys_field() &&
- (!(field->flags & BLOB_FLAG) ||
- field->real_field_type() == MYSQL_TYPE_GEOMETRY))
- {
- Item *expr= field->default_value->expr;
- // may be already fixed if ALTER TABLE
- int res= expr->fix_fields_if_needed(thd, &expr);
- if (!res)
- res= expr->save_in_field(regfield, 1);
- if (!res && (field->flags & BLOB_FLAG))
- regfield->reset();
-
- /* If not ok or warning of level 'note' */
- if (res != 0 && res != 3)
- {
- my_error(ER_INVALID_DEFAULT, MYF(0), regfield->field_name.str);
- error= 1;
- delete regfield; //To avoid memory leak
- goto err;
- }
- delete regfield; //To avoid memory leak
- }
- else if (regfield->real_type() == MYSQL_TYPE_ENUM &&
- !field->vers_sys_field() &&
- (field->flags & NOT_NULL_FLAG))
- {
- regfield->set_notnull();
- regfield->store((longlong) 1, TRUE);
- }
- else
- regfield->reset();
+ error= make_empty_rec_store_default(thd, regfield, field);
+ delete regfield; // Avoid memory leaks
+ if (error)
+ goto err;
}
DBUG_ASSERT(data_offset == ((null_count + 7) / 8));
diff --git a/sql/unireg.h b/sql/unireg.h
index e9c62334e86..cac290c7806 100644
--- a/sql/unireg.h
+++ b/sql/unireg.h
@@ -56,11 +56,6 @@
#endif
#define ER_THD_OR_DEFAULT(thd,X) ((thd) ? ER_THD(thd, (X)) : ER_DEFAULT(X))
-
-#define ME_INFO (ME_HOLDTANG | ME_NOREFRESH)
-#define ME_ERROR (ME_BELL | ME_NOREFRESH)
-#define MYF_RW MYF(MY_WME+MY_NABP) /* Vid my_read & my_write */
-
#define SPECIAL_USE_LOCKS 1 /* Lock used databases */
#define SPECIAL_NO_NEW_FUNC 2 /* Skip new functions */
#define SPECIAL_SKIP_SHOW_DB 4 /* Don't allow 'show db' */
@@ -176,6 +171,7 @@ enum extra2_frm_value_type {
EXTRA2_TABLEDEF_VERSION=0,
EXTRA2_DEFAULT_PART_ENGINE=1,
EXTRA2_GIS=2,
+ EXTRA2_APPLICATION_TIME_PERIOD=3,
EXTRA2_PERIOD_FOR_SYSTEM_TIME=4,
#define EXTRA2_ENGINE_IMPORTANT 128
@@ -185,14 +181,10 @@ enum extra2_frm_value_type {
};
enum extra2_field_flags {
- VERS_OPTIMIZED_UPDATE= 1 << INVISIBLE_MAX_BITS
+ VERS_OPTIMIZED_UPDATE= 1 << INVISIBLE_MAX_BITS,
};
-int rea_create_table(THD *thd, LEX_CUSTRING *frm,
- const char *path, const char *db, const char *table_name,
- HA_CREATE_INFO *create_info, handler *file,
- bool no_ha_create_table);
-LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table,
+LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table,
HA_CREATE_INFO *create_info,
List<Create_field> &create_fields,
uint keys, KEY *key_info, handler *db_file);
diff --git a/sql/vers_string.h b/sql/vers_string.h
index 3709cdbf786..2349cc0cac1 100644
--- a/sql/vers_string.h
+++ b/sql/vers_string.h
@@ -58,6 +58,12 @@ class Lex_cstring : public LEX_CSTRING
str= _str;
length= _len;
}
+ Lex_cstring(const char *start, const char *end)
+ {
+ DBUG_ASSERT(start <= end);
+ str= start;
+ length= end - start;
+ }
void set(const char *_str, size_t _len)
{
str= _str;
diff --git a/sql/vers_utils.h b/sql/vers_utils.h
index e896f84135e..2bea191da9e 100644
--- a/sql/vers_utils.h
+++ b/sql/vers_utils.h
@@ -5,43 +5,4 @@
#include "sql_class.h"
#include "vers_string.h"
-class MDL_auto_lock
-{
- THD *thd;
- TABLE_LIST &table;
- bool error;
-
-public:
- MDL_auto_lock(THD *_thd, TABLE_LIST &_table) :
- thd(_thd), table(_table)
- {
- DBUG_ASSERT(thd);
- MDL_request protection_request;
- if (thd->global_read_lock.can_acquire_protection())
- {
- error= true;
- return;
- }
- protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
- MDL_EXPLICIT);
- error= thd->mdl_context.acquire_lock(&protection_request, thd->variables.lock_wait_timeout);
- if (error)
- return;
-
- table.mdl_request.init(MDL_key::TABLE, table.db.str, table.table_name.str, MDL_EXCLUSIVE, MDL_EXPLICIT);
- error= thd->mdl_context.acquire_lock(&table.mdl_request, thd->variables.lock_wait_timeout);
- thd->mdl_context.release_lock(protection_request.ticket);
- }
- ~MDL_auto_lock()
- {
- if (!error)
- {
- DBUG_ASSERT(table.mdl_request.ticket);
- thd->mdl_context.release_lock(table.mdl_request.ticket);
- table.mdl_request.ticket= NULL;
- }
- }
- bool acquire_error() const { return error; }
-};
-
#endif // VERS_UTILS_INCLUDED
diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc
index bc9b68400f4..fd51dbf9439 100644
--- a/sql/wsrep_applier.cc
+++ b/sql/wsrep_applier.cc
@@ -14,12 +14,17 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. */
#include "mariadb.h"
+#include "mysql/service_wsrep.h"
+#include "wsrep_applier.h"
+
#include "wsrep_priv.h"
#include "wsrep_binlog.h" // wsrep_dump_rbr_buf()
#include "wsrep_xid.h"
+#include "wsrep_thd.h"
+#include "wsrep_trans_observer.h"
+#include "slave.h" // opt_log_slave_updates
#include "log_event.h" // class THD, EVENT_LEN_OFFSET, etc.
-#include "wsrep_applier.h"
#include "debug_sync.h"
/*
@@ -27,7 +32,6 @@
At the end (*buf) is shitfed to point to the following event or NULL and
(*buf_len) will be changed to account just being read bytes of the 1st event.
*/
-
static Log_event* wsrep_read_log_event(
char **arg_buf, size_t *arg_buf_len,
const Format_description_log_event *description_event)
@@ -35,7 +39,7 @@ static Log_event* wsrep_read_log_event(
DBUG_ENTER("wsrep_read_log_event");
char *head= (*arg_buf);
- uint data_len = uint4korr(head + EVENT_LEN_OFFSET);
+ uint data_len= uint4korr(head + EVENT_LEN_OFFSET);
char *buf= (*arg_buf);
const char *error= 0;
Log_event *res= 0;
@@ -62,12 +66,13 @@ void wsrep_set_apply_format(THD* thd, Format_description_log_event* ev)
{
if (thd->wsrep_apply_format)
{
- delete (Format_description_log_event*)thd->wsrep_apply_format;
+ delete (Format_description_log_event*)thd->wsrep_apply_format;
}
thd->wsrep_apply_format= ev;
}
-Format_description_log_event* wsrep_get_apply_format(THD* thd)
+Format_description_log_event*
+wsrep_get_apply_format(THD* thd)
{
if (thd->wsrep_apply_format)
{
@@ -79,45 +84,77 @@ Format_description_log_event* wsrep_get_apply_format(THD* thd)
return thd->wsrep_rgi->rli->relay_log.description_event_for_exec;
}
-static wsrep_cb_status_t wsrep_apply_events(THD* thd,
- const void* events_buf,
- size_t buf_len)
+void wsrep_apply_error::store(const THD* const thd)
{
- char *buf= (char *)events_buf;
- int rcode= 0;
- int event= 1;
- Log_event_type typ;
+ Diagnostics_area::Sql_condition_iterator it=
+ thd->get_stmt_da()->sql_conditions();
+ const Sql_condition* cond;
- DBUG_ENTER("wsrep_apply_events");
+ static size_t const max_len= 2*MAX_SLAVE_ERRMSG; // 2x so that we have enough
+
+ if (NULL == str_)
+ {
+ // this must be freeable by standard free()
+ str_= static_cast<char*>(malloc(max_len));
+ if (NULL == str_)
+ {
+ WSREP_ERROR("Failed to allocate %zu bytes for error buffer.", max_len);
+ len_= 0;
+ return;
+ }
+ }
+ else
+ {
+ /* This is possible when we invoke rollback after failed applying.
+ * In this situation DA should not be reset yet and should contain
+ * all previous errors from applying and new ones from rollbacking,
+ * so we just overwrite is from scratch */
+ }
- if (thd->killed == KILL_CONNECTION &&
- thd->wsrep_conflict_state != REPLAYING)
+ char* slider= str_;
+ const char* const buf_end= str_ + max_len - 1; // -1: leave space for \0
+
+ for (cond= it++; cond && slider < buf_end; cond= it++)
{
- WSREP_INFO("applier has been aborted, skipping apply_rbr: %lld",
- (long long) wsrep_thd_trx_seqno(thd));
- DBUG_RETURN(WSREP_CB_FAILURE);
+ uint const err_code= cond->get_sql_errno();
+ const char* const err_str= cond->get_message_text();
+
+ slider+= my_snprintf(slider, buf_end - slider, " %s, Error_code: %d;",
+ err_str, err_code);
}
- mysql_mutex_lock(&thd->LOCK_thd_data);
- wsrep_thd_set_query_state(thd, QUERY_EXEC);
- if (thd->wsrep_conflict_state!= REPLAYING)
- thd->wsrep_conflict_state= NO_CONFLICT;
- mysql_mutex_unlock(&thd->LOCK_thd_data);
+ *slider= '\0';
+ len_= slider - str_ + 1; // +1: add \0
+
+ WSREP_DEBUG("Error buffer for thd %llu seqno %lld, %zu bytes: %s",
+ thd->thread_id, (long long)wsrep_thd_trx_seqno(thd),
+ len_, str_ ? str_ : "(null)");
+}
+
+int wsrep_apply_events(THD* thd,
+ Relay_log_info* rli,
+ const void* events_buf,
+ size_t buf_len)
+{
+ char *buf= (char *)events_buf;
+ int rcode= 0;
+ int event= 1;
+ Log_event_type typ;
+ DBUG_ENTER("wsrep_apply_events");
if (!buf_len) WSREP_DEBUG("empty rbr buffer to apply: %lld",
(long long) wsrep_thd_trx_seqno(thd));
- while(buf_len)
+ while (buf_len)
{
int exec_res;
Log_event* ev= wsrep_read_log_event(&buf, &buf_len,
- wsrep_get_apply_format(thd));
-
+ wsrep_get_apply_format(thd));
if (!ev)
{
WSREP_ERROR("applier could not read binlog event, seqno: %lld, len: %zu",
(long long)wsrep_thd_trx_seqno(thd), buf_len);
- rcode= 1;
+ rcode= WSREP_ERR_BAD_EVENT;
goto error;
}
@@ -147,9 +184,6 @@ static wsrep_cb_status_t wsrep_apply_events(THD* thd,
thd->set_server_id(ev->server_id);
thd->set_time(); // time the query
thd->transaction.start_time.reset(thd);
- wsrep_xid_init(&thd->transaction.xid_state.xid,
- thd->wsrep_trx_meta.gtid.uuid,
- thd->wsrep_trx_meta.gtid.seqno);
thd->lex->current_select= 0;
if (!ev->when)
{
@@ -162,13 +196,13 @@ static wsrep_cb_status_t wsrep_apply_events(THD* thd,
(thd->variables.option_bits & ~OPTION_SKIP_REPLICATION) |
(ev->flags & LOG_EVENT_SKIP_REPLICATION_F ? OPTION_SKIP_REPLICATION : 0);
- ev->thd = thd;
- exec_res = ev->apply_event(thd->wsrep_rgi);
+ ev->thd= thd;
+ exec_res= ev->apply_event(thd->wsrep_rgi);
DBUG_PRINT("info", ("exec_event result: %d", exec_res));
if (exec_res)
{
- WSREP_WARN("RBR event %d %s apply warning: %d, %lld",
+ WSREP_WARN("Event %d %s apply failed: %d, seqno %lld",
event, ev->get_type_str(), exec_res,
(long long) wsrep_thd_trx_seqno(thd));
rcode= exec_res;
@@ -178,230 +212,14 @@ static wsrep_cb_status_t wsrep_apply_events(THD* thd,
}
event++;
- if (thd->wsrep_conflict_state!= NO_CONFLICT &&
- thd->wsrep_conflict_state!= REPLAYING)
- WSREP_WARN("conflict state after RBR event applying: %d, %lld",
- thd->wsrep_query_state, (long long)wsrep_thd_trx_seqno(thd));
-
- if (thd->wsrep_conflict_state == MUST_ABORT) {
- WSREP_WARN("RBR event apply failed, rolling back: %lld",
- (long long) wsrep_thd_trx_seqno(thd));
- trans_rollback(thd);
- thd->locked_tables_list.unlock_locked_tables(thd);
- /* Release transactional metadata locks. */
- thd->release_transactional_locks();
- thd->wsrep_conflict_state= NO_CONFLICT;
- DBUG_RETURN(WSREP_CB_FAILURE);
- }
-
delete_or_keep_event_post_apply(thd->wsrep_rgi, typ, ev);
}
- error:
- mysql_mutex_lock(&thd->LOCK_thd_data);
- wsrep_thd_set_query_state(thd, QUERY_IDLE);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
-
- assert(thd->wsrep_exec_mode== REPL_RECV);
-
+error:
if (thd->killed == KILL_CONNECTION)
WSREP_INFO("applier aborted: %lld", (long long)wsrep_thd_trx_seqno(thd));
- if (rcode) DBUG_RETURN(WSREP_CB_FAILURE);
- DBUG_RETURN(WSREP_CB_SUCCESS);
-}
-
-wsrep_cb_status_t wsrep_apply_cb(void* const ctx,
- const void* const buf,
- size_t const buf_len,
- uint32_t const flags,
- const wsrep_trx_meta_t* meta)
-{
- THD* const thd((THD*)ctx);
-
- assert(thd->wsrep_apply_toi == false);
-
- // Allow tests to block the applier thread using the DBUG facilities.
- DBUG_EXECUTE_IF("sync.wsrep_apply_cb",
- {
- const char act[]=
- "now "
- "SIGNAL sync.wsrep_apply_cb_reached "
- "WAIT_FOR signal.wsrep_apply_cb";
- DBUG_ASSERT(!debug_sync_set_action(thd,
- STRING_WITH_LEN(act)));
- };);
-
- thd->wsrep_trx_meta = *meta;
-
-#ifdef WSREP_PROC_INFO
- snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "Applying write set %lld: %p, %zu",
- (long long)wsrep_thd_trx_seqno(thd), buf, buf_len);
- thd_proc_info(thd, thd->wsrep_info);
-#else
- thd_proc_info(thd, "Applying write set");
-#endif /* WSREP_PROC_INFO */
-
- /* tune FK and UK checking policy */
- if (wsrep_slave_UK_checks == FALSE)
- thd->variables.option_bits|= OPTION_RELAXED_UNIQUE_CHECKS;
- else
- thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS;
-
- if (wsrep_slave_FK_checks == FALSE)
- thd->variables.option_bits|= OPTION_NO_FOREIGN_KEY_CHECKS;
- else
- thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
-
- /* With galera we assume that the master has done the constraint checks */
- thd->variables.option_bits|= OPTION_NO_CHECK_CONSTRAINT_CHECKS;
-
- if (flags & WSREP_FLAG_ISOLATION)
- {
- thd->wsrep_apply_toi= true;
- /*
- Don't run in transaction mode with TOI actions.
- */
- thd->variables.option_bits&= ~OPTION_BEGIN;
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- }
- wsrep_cb_status_t rcode(wsrep_apply_events(thd, buf, buf_len));
-
-#ifdef WSREP_PROC_INFO
- snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "Applied write set %lld", (long long)wsrep_thd_trx_seqno(thd));
- thd_proc_info(thd, thd->wsrep_info);
-#else
- thd_proc_info(thd, "Applied write set");
-#endif /* WSREP_PROC_INFO */
-
- if (WSREP_CB_SUCCESS != rcode)
- {
- wsrep_dump_rbr_buf_with_header(thd, buf, buf_len);
- }
-
- if (thd->has_thd_temporary_tables())
- {
- WSREP_DEBUG("Applier %lld has temporary tables. Closing them now..",
- thd->thread_id);
- thd->close_temporary_tables();
- }
-
- return rcode;
-}
-
-static wsrep_cb_status_t wsrep_commit(THD* const thd)
-{
-#ifdef WSREP_PROC_INFO
- snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "Committing %lld", (long long)wsrep_thd_trx_seqno(thd));
- thd_proc_info(thd, thd->wsrep_info);
-#else
- thd_proc_info(thd, "Committing");
-#endif /* WSREP_PROC_INFO */
-
- wsrep_cb_status_t const rcode(trans_commit(thd) ?
- WSREP_CB_FAILURE : WSREP_CB_SUCCESS);
-
- if (WSREP_CB_SUCCESS == rcode)
- {
- thd->wsrep_rgi->cleanup_context(thd, false);
-#ifdef GTID_SUPPORT
- thd->variables.gtid_next.set_automatic();
-#endif /* GTID_SUPPORT */
- if (thd->wsrep_apply_toi)
- {
- wsrep_set_SE_checkpoint(thd->wsrep_trx_meta.gtid.uuid,
- thd->wsrep_trx_meta.gtid.seqno);
- }
- }
-
-#ifdef WSREP_PROC_INFO
- snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "Committed %lld", (long long) wsrep_thd_trx_seqno(thd));
- thd_proc_info(thd, thd->wsrep_info);
-#else
- thd_proc_info(thd, "Committed");
-#endif /* WSREP_PROC_INFO */
-
- return rcode;
-}
-
-static wsrep_cb_status_t wsrep_rollback(THD* const thd)
-{
-#ifdef WSREP_PROC_INFO
- snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "Rolling back %lld", (long long)wsrep_thd_trx_seqno(thd));
- thd_proc_info(thd, thd->wsrep_info);
-#else
- thd_proc_info(thd, "Rolling back");
-#endif /* WSREP_PROC_INFO */
-
- wsrep_cb_status_t const rcode(trans_rollback(thd) ?
- WSREP_CB_FAILURE : WSREP_CB_SUCCESS);
-
-#ifdef WSREP_PROC_INFO
- snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "Rolled back %lld", (long long)wsrep_thd_trx_seqno(thd));
- thd_proc_info(thd, thd->wsrep_info);
-#else
- thd_proc_info(thd, "Rolled back");
-#endif /* WSREP_PROC_INFO */
-
- return rcode;
-}
-
-wsrep_cb_status_t wsrep_commit_cb(void* const ctx,
- uint32_t const flags,
- const wsrep_trx_meta_t* meta,
- wsrep_bool_t* const exit,
- bool const commit)
-{
- THD* const thd((THD*)ctx);
-
- assert(meta->gtid.seqno >= wsrep_thd_trx_seqno(thd));
-
- wsrep_cb_status_t rcode;
-
- if (commit)
- rcode = wsrep_commit(thd);
- else
- rcode = wsrep_rollback(thd);
-
- /* Cleanup */
wsrep_set_apply_format(thd, NULL);
- thd->release_transactional_locks();
- thd->reset_query(); /* Mutex protected */
- free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
- thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
- if (wsrep_slave_count_change < 0 && commit && WSREP_CB_SUCCESS == rcode)
- {
- mysql_mutex_lock(&LOCK_wsrep_slave_threads);
- if (wsrep_slave_count_change < 0)
- {
- wsrep_slave_count_change++;
- *exit = true;
- }
- mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
- }
-
- if (thd->wsrep_applier)
- {
- /* From trans_begin() */
- thd->variables.option_bits|= OPTION_BEGIN;
- thd->server_status|= SERVER_STATUS_IN_TRANS;
- thd->wsrep_apply_toi= false;
- }
-
- return rcode;
-}
-
-
-wsrep_cb_status_t wsrep_unordered_cb(void* const ctx,
- const void* const data,
- size_t const size)
-{
- return WSREP_CB_SUCCESS;
+ DBUG_RETURN(rcode);
}
diff --git a/sql/wsrep_applier.h b/sql/wsrep_applier.h
index 3cfcd556ae8..70361987cc7 100644
--- a/sql/wsrep_applier.h
+++ b/sql/wsrep_applier.h
@@ -1,4 +1,4 @@
-/* Copyright 2013 Codership Oy <http://www.codership.com>
+/* Copyright 2013-2015 Codership Oy <http://www.codership.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,28 +17,57 @@
#define WSREP_APPLIER_H
#include <my_config.h>
-#include "../wsrep/wsrep_api.h"
-void wsrep_set_apply_format(THD* thd, Format_description_log_event* ev);
-Format_description_log_event* wsrep_get_apply_format(THD* thd);
+#include "sql_class.h" // THD class
+
+int wsrep_apply_events(THD* thd,
+ Relay_log_info* rli,
+ const void* events_buf,
+ size_t buf_len);
-/* wsrep callback prototypes */
-extern "C" {
-wsrep_cb_status_t wsrep_apply_cb(void *ctx,
- const void* buf, size_t buf_len,
- uint32_t flags,
- const wsrep_trx_meta_t* meta);
+/* Applier error codes, when nothing better is available. */
+#define WSREP_RET_SUCCESS 0 // Success
+#define WSREP_ERR_GENERIC 1 // When in doubt (MySQL default error code)
+#define WSREP_ERR_BAD_EVENT 2 // Can't parse event
+#define WSREP_ERR_NOT_FOUND 3 // Key. table, schema not found
+#define WSREP_ERR_EXISTS 4 // Key, table, schema already exists
+#define WSREP_ERR_WRONG_TYPE 5 // Incompatible data type
+#define WSREP_ERR_FAILED 6 // Operation failed for some internal reason
+#define WSREP_ERR_ABORTED 7 // Operation was aborted externally
-wsrep_cb_status_t wsrep_commit_cb(void *ctx,
- uint32_t flags,
- const wsrep_trx_meta_t* meta,
- wsrep_bool_t* exit,
- bool commit);
+class wsrep_apply_error
+{
+public:
+ wsrep_apply_error() : str_(NULL), len_(0) {};
+ ~wsrep_apply_error() { ::free(str_); }
+ /* stores the current THD error info from the diagnostic area. Works only
+ * once, subsequent invocations are ignored in order to preserve the original
+ * condition. */
+ void store(const THD* thd);
+ const char* c_str() const { return str_; }
+ size_t length() const { return len_; }
+ bool is_null() const { return (c_str() == NULL && length() == 0); }
+ wsrep_buf_t get_buf() const
+ {
+ wsrep_buf_t ret= { c_str(), length() };
+ return ret;
+ }
+private:
+ char* str_;
+ size_t len_;
+};
+
+class Format_description_log_event;
+void wsrep_set_apply_format(THD*, Format_description_log_event*);
+Format_description_log_event* wsrep_get_apply_format(THD* thd);
+int wsrep_apply(void* ctx,
+ uint32_t flags,
+ const wsrep_buf_t* buf,
+ const wsrep_trx_meta_t* meta,
+ wsrep_apply_error& err);
-wsrep_cb_status_t wsrep_unordered_cb(void* ctx,
- const void* data,
- size_t size);
+wsrep_cb_status_t wsrep_unordered_cb(void* ctx,
+ const wsrep_buf_t* data);
-} /* extern "C" */
#endif /* WSREP_APPLIER_H */
diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc
index 85c1deb0d71..d04d9989e99 100644
--- a/sql/wsrep_binlog.cc
+++ b/sql/wsrep_binlog.cc
@@ -14,12 +14,16 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. */
#include "mariadb.h"
+#include "mysql/service_wsrep.h"
#include "wsrep_binlog.h"
#include "wsrep_priv.h"
#include "log.h"
+#include "slave.h"
#include "log_event.h"
#include "wsrep_applier.h"
+#include "transaction.h"
+
extern handlerton *binlog_hton;
/*
Write the contents of a cache to a memory buffer.
@@ -40,10 +44,10 @@ int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len)
DBUG_RETURN(ER_ERROR_ON_WRITE);
}
- uint length = my_b_bytes_in_cache(cache);
- if (unlikely(0 == length)) length = my_b_fill(cache);
+ uint length= my_b_bytes_in_cache(cache);
+ if (unlikely(0 == length)) length= my_b_fill(cache);
- size_t total_length = 0;
+ size_t total_length= 0;
if (likely(length > 0)) do
{
@@ -60,7 +64,7 @@ int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len)
wsrep_max_ws_size, total_length);
goto error;
}
- uchar* tmp = (uchar *)my_realloc(*buf, total_length,
+ uchar* tmp= (uchar *)my_realloc(*buf, total_length,
MYF(MY_ALLOW_ZERO_PTR));
if (!tmp)
{
@@ -68,17 +72,17 @@ int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len)
*buf_len, length);
goto error;
}
- *buf = tmp;
+ *buf= tmp;
memcpy(*buf + *buf_len, cache->read_pos, length);
- *buf_len = total_length;
+ *buf_len= total_length;
if (cache->file < 0)
{
cache->read_pos= cache->read_end;
break;
}
- } while ((length = my_b_fill(cache)));
+ } while ((length= my_b_fill(cache)));
if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
{
@@ -104,137 +108,6 @@ cleanup:
* many transactions would fit in there
* so there is no need to reach for the heap */
-/* Returns minimum multiple of HEAP_PAGE_SIZE that is >= length */
-static inline size_t
-heap_size(size_t length)
-{
- return (length + HEAP_PAGE_SIZE - 1)/HEAP_PAGE_SIZE*HEAP_PAGE_SIZE;
-}
-
-/* append data to writeset */
-static inline wsrep_status_t
-wsrep_append_data(wsrep_t* const wsrep,
- wsrep_ws_handle_t* const ws,
- const void* const data,
- size_t const len)
-{
- struct wsrep_buf const buff = { data, len };
- wsrep_status_t const rc(wsrep->append_data(wsrep, ws, &buff, 1,
- WSREP_DATA_ORDERED, true));
- DBUG_DUMP("buff", (uchar*) data, len);
- if (rc != WSREP_OK)
- {
- WSREP_WARN("append_data() returned %d", rc);
- }
-
- return rc;
-}
-
-/*
- Write the contents of a cache to wsrep provider.
-
- This function quite the same as MYSQL_BIN_LOG::write_cache(),
- with the exception that here we write in buffer instead of log file.
-
- This version reads all of cache into single buffer and then appends to a
- writeset at once.
- */
-static int wsrep_write_cache_once(wsrep_t* const wsrep,
- THD* const thd,
- IO_CACHE* const cache,
- size_t* const len)
-{
- my_off_t const saved_pos(my_b_tell(cache));
- DBUG_ENTER("wsrep_write_cache_once");
-
- if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
- {
- WSREP_ERROR("failed to initialize io-cache");
- DBUG_RETURN(ER_ERROR_ON_WRITE);
- }
-
- int err(WSREP_OK);
-
- size_t total_length(0);
- uchar stack_buf[STACK_SIZE]; /* to avoid dynamic allocations for few data*/
- uchar* heap_buf(NULL);
- uchar* buf(stack_buf);
- size_t allocated(sizeof(stack_buf));
- size_t used(0);
-
- uint length(my_b_bytes_in_cache(cache));
- if (unlikely(0 == length)) length = my_b_fill(cache);
-
- if (likely(length > 0)) do
- {
- total_length += length;
- /*
- Bail out if buffer grows too large.
- A temporary fix to avoid allocating indefinitely large buffer,
- not a real limit on a writeset size which includes other things
- like header and keys.
- */
- if (unlikely(total_length > wsrep_max_ws_size))
- {
- WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
- wsrep_max_ws_size, total_length);
- err = WSREP_TRX_SIZE_EXCEEDED;
- goto cleanup;
- }
-
- if (total_length > allocated)
- {
- size_t const new_size(heap_size(total_length));
- uchar* tmp = (uchar *)my_realloc(heap_buf, new_size,
- MYF(MY_ALLOW_ZERO_PTR));
- if (!tmp)
- {
- WSREP_ERROR("could not (re)allocate buffer: %zu + %u",
- allocated, length);
- err = WSREP_TRX_SIZE_EXCEEDED;
- goto cleanup;
- }
-
- heap_buf = tmp;
- buf = heap_buf;
- allocated = new_size;
-
- if (used <= STACK_SIZE && used > 0) // there's data in stack_buf
- {
- DBUG_ASSERT(buf == stack_buf);
- memcpy(heap_buf, stack_buf, used);
- }
- }
-
- memcpy(buf + used, cache->read_pos, length);
- used = total_length;
- if (cache->file < 0)
- {
- cache->read_pos= cache->read_end;
- break;
- }
- } while ((length = my_b_fill(cache)));
-
- if (used > 0)
- err = wsrep_append_data(wsrep, &thd->wsrep_ws_handle, buf, used);
-
- if (WSREP_OK == err) *len = total_length;
-
-cleanup:
- if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
- {
- WSREP_ERROR("failed to reinitialize io-cache");
- }
-
- if (unlikely(WSREP_OK != err))
- {
- wsrep_dump_rbr_buf_with_header(thd, buf, used);
- }
-
- my_free(heap_buf);
- DBUG_RETURN(err);
-}
-
/*
Write the contents of a cache to wsrep provider.
@@ -243,62 +116,58 @@ cleanup:
This version uses incremental data appending as it reads it from cache.
*/
-static int wsrep_write_cache_inc(wsrep_t* const wsrep,
- THD* const thd,
+static int wsrep_write_cache_inc(THD* const thd,
IO_CACHE* const cache,
size_t* const len)
{
- my_off_t const saved_pos(my_b_tell(cache));
- DBUG_ENTER("wsrep_write_cache_inc");
-
- if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
- {
- WSREP_ERROR("failed to initialize io-cache");
- DBUG_RETURN(WSREP_TRX_ERROR);
- }
+ DBUG_ENTER("wsrep_write_cache_inc");
+ my_off_t const saved_pos(my_b_tell(cache));
- int err(WSREP_OK);
+ if (reinit_io_cache(cache, READ_CACHE, thd->wsrep_sr().log_position(), 0, 0))
+ {
+ WSREP_ERROR("failed to initialize io-cache");
+ DBUG_RETURN(1);;
+ }
- size_t total_length(0);
+ int ret= 0;
+ size_t total_length(0);
- uint length(my_b_bytes_in_cache(cache));
- if (unlikely(0 == length)) length = my_b_fill(cache);
+ uint length(my_b_bytes_in_cache(cache));
+ if (unlikely(0 == length)) length= my_b_fill(cache);
- if (likely(length > 0)) do
+ if (likely(length > 0))
+ {
+ do
{
- total_length += length;
- /* bail out if buffer grows too large
- not a real limit on a writeset size which includes other things
- like header and keys.
- */
- if (unlikely(total_length > wsrep_max_ws_size))
- {
- WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
- wsrep_max_ws_size, total_length);
- err = WSREP_TRX_SIZE_EXCEEDED;
- goto cleanup;
- }
-
- if(WSREP_OK != (err=wsrep_append_data(wsrep, &thd->wsrep_ws_handle,
- cache->read_pos, length)))
- goto cleanup;
-
- if (cache->file < 0)
- {
- cache->read_pos= cache->read_end;
- break;
- }
- } while ((length = my_b_fill(cache)));
-
- if (WSREP_OK == err) *len = total_length;
+ total_length += length;
+ /* bail out if buffer grows too large
+ not a real limit on a writeset size which includes other things
+ like header and keys.
+ */
+ if (unlikely(total_length > wsrep_max_ws_size))
+ {
+ WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
+ wsrep_max_ws_size, total_length);
+ ret= 1;
+ goto cleanup;
+ }
+ if (thd->wsrep_cs().append_data(wsrep::const_buffer(cache->read_pos, length)))
+ goto cleanup;
+ cache->read_pos= cache->read_end;
+ } while ((cache->file >= 0) && (length= my_b_fill(cache)));
+ }
+ if (ret == 0)
+ {
+ assert(total_length + thd->wsrep_sr().log_position() == saved_pos);
+ }
cleanup:
- if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
- {
- WSREP_ERROR("failed to reinitialize io-cache");
- }
-
- DBUG_RETURN(err);
+ *len= total_length;
+ if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
+ {
+ WSREP_ERROR("failed to reinitialize io-cache");
+ }
+ DBUG_RETURN(ret);
}
/*
@@ -307,17 +176,11 @@ cleanup:
This function quite the same as MYSQL_BIN_LOG::write_cache(),
with the exception that here we write in buffer instead of log file.
*/
-int wsrep_write_cache(wsrep_t* const wsrep,
- THD* const thd,
+int wsrep_write_cache(THD* const thd,
IO_CACHE* const cache,
size_t* const len)
{
- if (wsrep_incremental_data_collection) {
- return wsrep_write_cache_inc(wsrep, thd, cache, len);
- }
- else {
- return wsrep_write_cache_once(wsrep, thd, cache, len);
- }
+ return wsrep_write_cache_inc(thd, cache, len);
}
void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len)
@@ -365,89 +228,6 @@ void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len)
free(filename);
}
-/*
- wsrep exploits binlog's caches even if binlogging itself is not
- activated. In such case connection close needs calling
- actual binlog's method.
- Todo: split binlog hton from its caches to use ones by wsrep
- without referring to binlog's stuff.
-*/
-int wsrep_binlog_close_connection(THD* thd)
-{
- DBUG_ENTER("wsrep_binlog_close_connection");
- if (thd_get_ha_data(thd, binlog_hton) != NULL)
- binlog_hton->close_connection (binlog_hton, thd);
- DBUG_RETURN(0);
-}
-
-#if 0
-void wsrep_dump_rbr_direct(THD* thd, IO_CACHE* cache)
-{
- char filename[PATH_MAX]= {0};
- int len= snprintf(filename, PATH_MAX, "%s/GRA_%lld_%lld.log",
- wsrep_data_home_dir, (longlong) thd->thread_id,
- (longlong) wsrep_thd_trx_seqno(thd));
- size_t bytes_in_cache = 0;
- // check path
- if (len >= PATH_MAX)
- {
- WSREP_ERROR("RBR dump path too long: %d, skipping dump.", len);
- return ;
- }
- // init cache
- my_off_t const saved_pos(my_b_tell(cache));
- if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
- {
- WSREP_ERROR("failed to initialize io-cache");
- return ;
- }
- // open file
- FILE* of = fopen(filename, "wb");
- if (!of)
- {
- WSREP_ERROR("Failed to open file '%s': %d (%s)",
- filename, errno, strerror(errno));
- goto cleanup;
- }
- // ready to write
- bytes_in_cache= my_b_bytes_in_cache(cache);
- if (unlikely(bytes_in_cache == 0)) bytes_in_cache = my_b_fill(cache);
- if (likely(bytes_in_cache > 0)) do
- {
- if (my_fwrite(of, cache->read_pos, bytes_in_cache,
- MYF(MY_WME | MY_NABP)) == (size_t) -1)
- {
- WSREP_ERROR("Failed to write file '%s'", filename);
- goto cleanup;
- }
-
- if (cache->file < 0)
- {
- cache->read_pos= cache->read_end;
- break;
- }
- } while ((bytes_in_cache= my_b_fill(cache)));
- if (cache->error == -1)
- {
- WSREP_ERROR("RBR inconsistent");
- goto cleanup;
- }
-cleanup:
- // init back
- if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
- {
- WSREP_ERROR("failed to reinitialize io-cache");
- }
- // close file
- if (of) fclose(of);
-}
-#endif
-
-void thd_binlog_flush_pending_rows_event(THD *thd, bool stmt_end)
-{
- thd->binlog_flush_pending_rows_event(stmt_end);
-}
-
/* Dump replication buffer along with header to a file. */
void wsrep_dump_rbr_buf_with_header(THD *thd, const void *rbr_buf,
size_t buf_len)
@@ -529,3 +309,92 @@ cleanup1:
DBUG_VOID_RETURN;
}
+int wsrep_write_skip_event(THD* thd)
+{
+ DBUG_ENTER("wsrep_write_skip_event");
+ Ignorable_log_event skip_event(thd);
+ int ret= mysql_bin_log.write_event(&skip_event);
+ if (ret)
+ {
+ WSREP_WARN("wsrep_write_skip_event: write to binlog failed: %d", ret);
+ }
+ if (!ret && (ret= trans_commit_stmt(thd)))
+ {
+ WSREP_WARN("wsrep_write_skip_event: statt commit failed");
+ }
+ DBUG_RETURN(ret);
+}
+
+int wsrep_write_dummy_event_low(THD *thd, const char *msg)
+{
+ ::abort();
+ return 0;
+}
+
+int wsrep_write_dummy_event(THD *orig_thd, const char *msg)
+{
+ return 0;
+}
+
+bool wsrep_commit_will_write_binlog(THD *thd)
+{
+ return (!wsrep_emulate_bin_log && /* binlog enabled*/
+ (wsrep_thd_is_local(thd) || /* local thd*/
+ (thd->wsrep_applier_service && /* applier and log-slave-updates */
+ opt_log_slave_updates)));
+}
+
+/*
+ The last THD/commit_for_wait registered for group commit.
+*/
+static wait_for_commit *commit_order_tail= NULL;
+
+void wsrep_register_for_group_commit(THD *thd)
+{
+ DBUG_ENTER("wsrep_register_for_group_commit");
+ if (wsrep_emulate_bin_log)
+ {
+ /* Binlog is off, no need to maintain group commit queue */
+ DBUG_VOID_RETURN;
+ }
+
+ DBUG_ASSERT(thd->wsrep_trx().ordered());
+
+ wait_for_commit *wfc= thd->wait_for_commit_ptr= &thd->wsrep_wfc;
+
+ mysql_mutex_lock(&LOCK_wsrep_group_commit);
+ if (commit_order_tail)
+ {
+ wfc->register_wait_for_prior_commit(commit_order_tail);
+ }
+ commit_order_tail= thd->wait_for_commit_ptr;
+ mysql_mutex_unlock(&LOCK_wsrep_group_commit);
+
+ /*
+ Now we have queued for group commit. If the commit will go
+ through TC log_and_order(), the commit ordering is done
+ by TC group commit. Otherwise the wait for prior
+ commits to complete is done in ha_commit_one_phase().
+ */
+ DBUG_VOID_RETURN;
+}
+
+void wsrep_unregister_from_group_commit(THD *thd)
+{
+ DBUG_ASSERT(thd->wsrep_trx().ordered());
+ wait_for_commit *wfc= thd->wait_for_commit_ptr;
+
+ if (wfc)
+ {
+ mysql_mutex_lock(&LOCK_wsrep_group_commit);
+ wfc->unregister_wait_for_prior_commit();
+ thd->wakeup_subsequent_commits(0);
+
+ /* The last one queued for group commit has completed commit, it is
+ safe to set tail to NULL. */
+ if (wfc == commit_order_tail)
+ commit_order_tail= NULL;
+ mysql_mutex_unlock(&LOCK_wsrep_group_commit);
+ thd->wait_for_commit_ptr= NULL;
+ }
+}
diff --git a/sql/wsrep_binlog.h b/sql/wsrep_binlog.h
index ed8e19ed651..252fbe602d2 100644
--- a/sql/wsrep_binlog.h
+++ b/sql/wsrep_binlog.h
@@ -16,6 +16,7 @@
#ifndef WSREP_BINLOG_H
#define WSREP_BINLOG_H
+#include "my_global.h"
#include "sql_class.h" // THD, IO_CACHE
#define HEAP_PAGE_SIZE 65536 /* 64K */
@@ -38,21 +39,65 @@ int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len);
@param len total amount of data written
@return wsrep error status
*/
-int wsrep_write_cache (wsrep_t* const wsrep,
- THD* const thd,
- IO_CACHE* const cache,
- size_t* const len);
+int wsrep_write_cache(THD* thd,
+ IO_CACHE* cache,
+ size_t* len);
/* Dump replication buffer to disk */
void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len);
-/* Dump replication buffer to disk without intermediate buffer */
-void wsrep_dump_rbr_direct(THD* thd, IO_CACHE* cache);
-
/* Dump replication buffer along with header to a file */
void wsrep_dump_rbr_buf_with_header(THD *thd, const void *rbr_buf,
size_t buf_len);
-int wsrep_binlog_close_connection(THD* thd);
+/**
+ Write a skip event into binlog.
+
+ @param thd Thread object pointer
+ @return Zero in case of success, non-zero on failure.
+*/
+int wsrep_write_skip_event(THD* thd);
+
+/*
+ Write dummy event into binlog in place of unused GTID.
+ The binlog write is done in thd context.
+*/
+int wsrep_write_dummy_event_low(THD *thd, const char *msg);
+/*
+ Write dummy event to binlog in place of unused GTID and
+ commit. The binlog write and commit are done in temporary
+ thd context, the original thd state is not altered.
+*/
+int wsrep_write_dummy_event(THD* thd, const char *msg);
+
+void wsrep_register_binlog_handler(THD *thd, bool trx);
+
+/**
+ Return true if committing THD will write to binlog during commit.
+ This is the case for:
+ - Local THD, binlog is open
+ - Replaying THD, binlog is open
+ - Applier THD, log-slave-updates is enabled
+*/
+bool wsrep_commit_will_write_binlog(THD *thd);
+
+/**
+ Register THD for group commit. The wsrep_trx must be in committing state,
+ i.e. the call must be done after wsrep_before_commit() but before
+ commit order is released.
+
+ This call will release commit order critical section if it is
+ determined that the commit will go through binlog group commit.
+ */
+void wsrep_register_for_group_commit(THD *thd);
+
+/**
+ Deregister THD from group commit. The wsrep_trx must be in committing state,
+ as for wsrep_register_for_group_commit() above.
+
+ This call must be used only for THDs which will not go through
+ binlog group commit.
+*/
+void wsrep_unregister_from_group_commit(THD *thd);
#endif /* WSREP_BINLOG_H */
diff --git a/sql/wsrep_check_opts.cc b/sql/wsrep_check_opts.cc
index 075546d827a..e5a0dcb2ede 100644
--- a/sql/wsrep_check_opts.cc
+++ b/sql/wsrep_check_opts.cc
@@ -33,7 +33,7 @@ int wsrep_check_opts()
autoinc_lock_mode->val_int(&is_null, 0, OPT_GLOBAL, 0) != 2)
{
WSREP_ERROR("Parallel applying (wsrep_slave_threads > 1) requires"
- " innodb_autoinc_lock_mode = 2.");
+ " innodb_autoinc_lock_mode= 2.");
return 1;
}
}
@@ -63,7 +63,7 @@ int wsrep_check_opts()
else
{
// non-mysqldump SST requires wsrep_cluster_address on startup
- if (!wsrep_cluster_address || !wsrep_cluster_address[0])
+ if (!wsrep_cluster_address_exists())
{
WSREP_ERROR ("%s SST method requires wsrep_cluster_address to be "
"configured on startup.", wsrep_sst_method);
@@ -88,7 +88,7 @@ int wsrep_check_opts()
{
if (global_system_variables.binlog_format != BINLOG_FORMAT_ROW)
{
- WSREP_ERROR("Only binlog_format = 'ROW' is currently supported. "
+ WSREP_ERROR("Only binlog_format= 'ROW' is currently supported. "
"Configured value: '%s'. Please adjust your "
"configuration.",
binlog_format_names[global_system_variables.binlog_format]);
diff --git a/sql/wsrep_client_service.cc b/sql/wsrep_client_service.cc
new file mode 100644
index 00000000000..89621619a23
--- /dev/null
+++ b/sql/wsrep_client_service.cc
@@ -0,0 +1,345 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "wsrep_client_service.h"
+#include "wsrep_high_priority_service.h"
+#include "wsrep_applier.h" /* wsrep_apply_events() */
+#include "wsrep_binlog.h" /* wsrep_dump_rbr_buf() */
+#include "wsrep_schema.h" /* remove_fragments() */
+#include "wsrep_thd.h"
+#include "wsrep_xid.h"
+#include "wsrep_trans_observer.h"
+
+#include "sql_base.h" /* close_temporary_table() */
+#include "sql_class.h" /* THD */
+#include "sql_parse.h" /* stmt_causes_implicit_commit() */
+#include "rpl_filter.h" /* binlog_filter */
+#include "rpl_rli.h" /* Relay_log_info */
+#include "slave.h" /* opt_log_slave_updates */
+#include "transaction.h" /* trans_commit()... */
+#include "log.h" /* stmt_has_updated_trans_table() */
+#include "mysql/service_debug_sync.h"
+#include "mysql/psi/mysql_thread.h" /* mysql_mutex_assert_owner() */
+
+namespace
+{
+
+void debug_sync_caller(THD* thd, const char* sync_point)
+{
+#ifdef ENABLED_DEBUG_SYNC_OUT
+ debug_sync_set_action(thd, sync_point, strlen(sync_point));
+#endif
+#ifdef ENABLED_DEBUG_SYNC
+ if (debug_sync_service) debug_sync_service(thd,sync_point,strlen(sync_point));
+#endif
+
+}
+}
+
+Wsrep_client_service::Wsrep_client_service(THD* thd,
+ Wsrep_client_state& client_state)
+ : wsrep::client_service()
+ , m_thd(thd)
+ , m_client_state(client_state)
+{ }
+
+void Wsrep_client_service::store_globals()
+{
+ wsrep_store_threadvars(m_thd);
+}
+
+void Wsrep_client_service::reset_globals()
+{
+ wsrep_reset_threadvars(m_thd);
+}
+
+bool Wsrep_client_service::interrupted(
+ wsrep::unique_lock<wsrep::mutex>& lock WSREP_UNUSED) const
+{
+ DBUG_ASSERT(m_thd == current_thd);
+ mysql_mutex_assert_owner(static_cast<mysql_mutex_t*>(lock.mutex()->native()));
+ bool ret= (m_thd->killed != NOT_KILLED);
+ if (ret)
+ {
+ WSREP_DEBUG("wsrep state is interrupted, THD::killed %d trx state %d",
+ m_thd->killed, m_thd->wsrep_trx().state());
+ }
+ return ret;
+}
+
+int Wsrep_client_service::prepare_data_for_replication()
+{
+ DBUG_ASSERT(m_thd == current_thd);
+ DBUG_ENTER("Wsrep_client_service::prepare_data_for_replication");
+ size_t data_len= 0;
+ IO_CACHE* cache= wsrep_get_trans_cache(m_thd);
+
+ if (cache)
+ {
+ m_thd->binlog_flush_pending_rows_event(true);
+ if (wsrep_write_cache(m_thd, cache, &data_len))
+ {
+ WSREP_ERROR("rbr write fail, data_len: %zu",
+ data_len);
+ // wsrep_override_error(m_thd, ER_ERROR_DURING_COMMIT);
+ DBUG_RETURN(1);
+ }
+ }
+
+ if (data_len == 0)
+ {
+ if (m_thd->get_stmt_da()->is_ok() &&
+ m_thd->get_stmt_da()->affected_rows() > 0 &&
+ !binlog_filter->is_on() &&
+ !m_thd->wsrep_trx().is_streaming())
+ {
+ WSREP_DEBUG("empty rbr buffer, query: %s, "
+ "affected rows: %llu, "
+ "changed tables: %d, "
+ "sql_log_bin: %d",
+ WSREP_QUERY(m_thd),
+ m_thd->get_stmt_da()->affected_rows(),
+ stmt_has_updated_trans_table(m_thd),
+ m_thd->variables.sql_log_bin);
+ }
+ else
+ {
+ WSREP_DEBUG("empty rbr buffer, query: %s", WSREP_QUERY(m_thd));
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+void Wsrep_client_service::cleanup_transaction()
+{
+ DBUG_ASSERT(m_thd == current_thd);
+ if (WSREP_EMULATE_BINLOG(m_thd)) wsrep_thd_binlog_trx_reset(m_thd);
+ m_thd->wsrep_affected_rows= 0;
+}
+
+
+int Wsrep_client_service::prepare_fragment_for_replication(
+ wsrep::mutable_buffer& buffer, size_t& log_position)
+{
+ DBUG_ASSERT(m_thd == current_thd);
+ THD* thd= m_thd;
+ DBUG_ENTER("Wsrep_client_service::prepare_fragment_for_replication");
+ IO_CACHE* cache= wsrep_get_trans_cache(thd);
+ thd->binlog_flush_pending_rows_event(true);
+
+ if (!cache)
+ {
+ DBUG_RETURN(0);
+ }
+
+ const my_off_t saved_pos(my_b_tell(cache));
+ if (reinit_io_cache(cache, READ_CACHE, thd->wsrep_sr().log_position(), 0, 0))
+ {
+ DBUG_RETURN(1);
+ }
+
+ int ret= 0;
+ size_t total_length= 0;
+ size_t length= my_b_bytes_in_cache(cache);
+
+ if (!length)
+ {
+ length= my_b_fill(cache);
+ }
+
+ if (length > 0)
+ {
+ do
+ {
+ total_length+= length;
+ if (total_length > wsrep_max_ws_size)
+ {
+ WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
+ wsrep_max_ws_size, total_length);
+ ret= 1;
+ goto cleanup;
+ }
+
+ buffer.push_back(reinterpret_cast<const char*>(cache->read_pos),
+ reinterpret_cast<const char*>(cache->read_pos + length));
+ cache->read_pos= cache->read_end;
+ }
+ while (cache->file >= 0 && (length= my_b_fill(cache)));
+ }
+ DBUG_ASSERT(total_length == buffer.size());
+ log_position= saved_pos;
+cleanup:
+ if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
+ {
+ WSREP_WARN("Failed to reinitialize IO cache");
+ ret= 1;
+ }
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_client_service::remove_fragments()
+{
+ DBUG_ENTER("Wsrep_client_service::remove_fragments");
+ if (wsrep_schema->remove_fragments(m_thd,
+ Wsrep_server_state::instance().id(),
+ m_thd->wsrep_trx().id(),
+ m_thd->wsrep_sr().fragments()))
+ {
+ WSREP_DEBUG("Failed to remove fragments from SR storage for transaction "
+ "%llu, %llu",
+ m_thd->thread_id, m_thd->wsrep_trx().id().get());
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+bool Wsrep_client_service::statement_allowed_for_streaming() const
+{
+ /*
+ Todo: Decide if implicit commit is allowed with streaming
+ replication.
+ !stmt_causes_implicit_commit(m_thd, CF_IMPLICIT_COMMIT_BEGIN);
+ */
+ return true;
+}
+
+size_t Wsrep_client_service::bytes_generated() const
+{
+ IO_CACHE* cache= wsrep_get_trans_cache(m_thd);
+ if (cache)
+ {
+ size_t pending_rows_event_length= 0;
+ if (Rows_log_event* ev= m_thd->binlog_get_pending_rows_event(true))
+ {
+ pending_rows_event_length= ev->get_data_size();
+ }
+ return my_b_tell(cache) + pending_rows_event_length;
+ }
+ return 0;
+}
+
+void Wsrep_client_service::will_replay()
+{
+ DBUG_ASSERT(m_thd == current_thd);
+ mysql_mutex_lock(&LOCK_wsrep_replaying);
+ ++wsrep_replaying;
+ mysql_mutex_unlock(&LOCK_wsrep_replaying);
+}
+
+void Wsrep_client_service::signal_replayed()
+{
+ DBUG_ASSERT(m_thd == current_thd);
+ mysql_mutex_lock(&LOCK_wsrep_replaying);
+ --wsrep_replaying;
+ DBUG_ASSERT(wsrep_replaying >= 0);
+ mysql_cond_broadcast(&COND_wsrep_replaying);
+ mysql_mutex_unlock(&LOCK_wsrep_replaying);
+}
+
+enum wsrep::provider::status Wsrep_client_service::replay()
+{
+
+ DBUG_ASSERT(m_thd == current_thd);
+ DBUG_ENTER("Wsrep_client_service::replay");
+
+ /*
+ Allocate separate THD for replaying to avoid tampering
+ original THD state during replication event applying.
+ */
+ THD *replayer_thd= new THD(true, true);
+ replayer_thd->thread_stack= m_thd->thread_stack;
+ replayer_thd->real_id= pthread_self();
+ replayer_thd->prior_thr_create_utime=
+ replayer_thd->start_utime= microsecond_interval_timer();
+ replayer_thd->set_command(COM_SLEEP);
+ replayer_thd->reset_for_next_command(true);
+
+ enum wsrep::provider::status ret;
+ {
+ Wsrep_replayer_service replayer_service(replayer_thd, m_thd);
+ wsrep::provider& provider(replayer_thd->wsrep_cs().provider());
+ ret= provider.replay(replayer_thd->wsrep_trx().ws_handle(),
+ &replayer_service);
+ replayer_service.replay_status(ret);
+ }
+
+ delete replayer_thd;
+ DBUG_RETURN(ret);
+}
+
+enum wsrep::provider::status Wsrep_client_service::replay_unordered()
+{
+ DBUG_ASSERT(0);
+ return wsrep::provider::error_not_implemented;
+}
+
+void Wsrep_client_service::wait_for_replayers(wsrep::unique_lock<wsrep::mutex>& lock)
+{
+ DBUG_ASSERT(m_thd == current_thd);
+ lock.unlock();
+ mysql_mutex_lock(&LOCK_wsrep_replaying);
+ /* We need to check if the THD is BF aborted during condition wait.
+ Because the aborter does not know which condition this thread is waiting,
+ use timed wait and check if the THD is BF aborted in the loop. */
+ while (wsrep_replaying > 0 && !wsrep_is_bf_aborted(m_thd))
+ {
+ struct timespec wait_time;
+ set_timespec_nsec(wait_time, 10000000L);
+ mysql_cond_timedwait(&COND_wsrep_replaying, &LOCK_wsrep_replaying,
+ &wait_time);
+ }
+ mysql_mutex_unlock(&LOCK_wsrep_replaying);
+ lock.lock();
+}
+
+enum wsrep::provider::status Wsrep_client_service::commit_by_xid()
+{
+ DBUG_ASSERT(0);
+ return wsrep::provider::error_not_implemented;
+}
+
+void Wsrep_client_service::debug_sync(const char* sync_point)
+{
+ DBUG_ASSERT(m_thd == current_thd);
+ debug_sync_caller(m_thd, sync_point);
+}
+
+void Wsrep_client_service::debug_crash(const char* crash_point)
+{
+ // DBUG_ASSERT(m_thd == current_thd);
+ DBUG_EXECUTE_IF(crash_point, DBUG_SUICIDE(); );
+}
+
+int Wsrep_client_service::bf_rollback()
+{
+ DBUG_ASSERT(m_thd == current_thd);
+ DBUG_ENTER("Wsrep_client_service::rollback");
+
+ int ret= (trans_rollback_stmt(m_thd) || trans_rollback(m_thd));
+ if (m_thd->locked_tables_mode && m_thd->lock)
+ {
+ m_thd->locked_tables_list.unlock_locked_tables(m_thd);
+ m_thd->variables.option_bits&= ~OPTION_TABLE_LOCK;
+ }
+ if (m_thd->global_read_lock.is_acquired())
+ {
+ m_thd->global_read_lock.unlock_global_read_lock(m_thd);
+ }
+ m_thd->release_transactional_locks();
+ m_thd->mdl_context.release_explicit_locks();
+
+ DBUG_RETURN(ret);
+}
diff --git a/sql/wsrep_client_service.h b/sql/wsrep_client_service.h
new file mode 100644
index 00000000000..253d2f43ac3
--- /dev/null
+++ b/sql/wsrep_client_service.h
@@ -0,0 +1,74 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/** @file wsrep_client_service.h
+
+ This file provides declaratios for client service implementation.
+ See wsrep/client_service.hpp for interface documentation.
+*/
+
+#ifndef WSREP_CLIENT_SERVICE_H
+#define WSREP_CLIENT_SERVICE_H
+
+/* wsrep-lib */
+#include "wsrep/client_service.hpp"
+#include "wsrep/client_state.hpp"
+#include "wsrep/exception.hpp" /* not_implemented_error, remove when finished */
+
+class THD;
+class Wsrep_client_state;
+class Wsrep_high_priority_context;
+
+class Wsrep_client_service : public wsrep::client_service
+{
+public:
+ Wsrep_client_service(THD*, Wsrep_client_state&);
+
+ bool interrupted(wsrep::unique_lock<wsrep::mutex>&) const;
+ void reset_globals();
+ void store_globals();
+ int prepare_data_for_replication();
+ void cleanup_transaction();
+ bool statement_allowed_for_streaming() const;
+ size_t bytes_generated() const;
+ int prepare_fragment_for_replication(wsrep::mutable_buffer&, size_t&);
+ int remove_fragments();
+ void emergency_shutdown()
+ { throw wsrep::not_implemented_error(); }
+ void will_replay();
+ void signal_replayed();
+ enum wsrep::provider::status replay();
+ enum wsrep::provider::status replay_unordered();
+ void wait_for_replayers(wsrep::unique_lock<wsrep::mutex>&);
+ enum wsrep::provider::status commit_by_xid();
+ bool is_explicit_xa()
+ {
+ return false;
+ }
+ bool is_xa_rollback()
+ {
+ return false;
+ }
+ void debug_sync(const char*);
+ void debug_crash(const char*);
+ int bf_rollback();
+private:
+ friend class Wsrep_server_service;
+ THD* m_thd;
+ Wsrep_client_state& m_client_state;
+};
+
+
+#endif /* WSREP_CLIENT_SERVICE_H */
diff --git a/sql/wsrep_client_state.h b/sql/wsrep_client_state.h
new file mode 100644
index 00000000000..403bfa81365
--- /dev/null
+++ b/sql/wsrep_client_state.h
@@ -0,0 +1,47 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef WSREP_CLIENT_STATE_H
+#define WSREP_CLIENT_STATE_H
+
+/* wsrep-lib */
+#include "wsrep/client_state.hpp"
+#include "my_global.h"
+
+class THD;
+
+class Wsrep_client_state : public wsrep::client_state
+{
+public:
+ Wsrep_client_state(THD* thd,
+ wsrep::mutex& mutex,
+ wsrep::condition_variable& cond,
+ wsrep::server_state& server_state,
+ wsrep::client_service& client_service,
+ const wsrep::client_id& id)
+ : wsrep::client_state(mutex,
+ cond,
+ server_state,
+ client_service,
+ id,
+ wsrep::client_state::m_local)
+ , m_thd(thd)
+ { }
+ THD* thd() { return m_thd; }
+private:
+ THD* m_thd;
+};
+
+#endif /* WSREP_CLIENT_STATE_H */
diff --git a/sql/wsrep_condition_variable.h b/sql/wsrep_condition_variable.h
new file mode 100644
index 00000000000..6ad53a3086c
--- /dev/null
+++ b/sql/wsrep_condition_variable.h
@@ -0,0 +1,54 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef WSREP_CONDITION_VARIABLE_H
+#define WSREP_CONDITION_VARIABLE_H
+
+/* wsrep-lib */
+#include "wsrep/condition_variable.hpp"
+
+/* implementation */
+#include "my_pthread.h"
+
+class Wsrep_condition_variable : public wsrep::condition_variable
+{
+public:
+
+ Wsrep_condition_variable(mysql_cond_t& cond)
+ : m_cond(cond)
+ { }
+ ~Wsrep_condition_variable()
+ { }
+
+ void notify_one()
+ {
+ mysql_cond_signal(&m_cond);
+ }
+
+ void notify_all()
+ {
+ mysql_cond_broadcast(&m_cond);
+ }
+
+ void wait(wsrep::unique_lock<wsrep::mutex>& lock)
+ {
+ mysql_mutex_t* mutex= static_cast<mysql_mutex_t*>(lock.mutex()->native());
+ mysql_cond_wait(&m_cond, mutex);
+ }
+private:
+ mysql_cond_t& m_cond;
+};
+
+#endif /* WSREP_CONDITION_VARIABLE_H */
diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc
index b00b2f134b1..129df8e1577 100644
--- a/sql/wsrep_dummy.cc
+++ b/sql/wsrep_dummy.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2014, 2020, MariaDB
+/* Copyright (C) 2014, 2021, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,16 +17,10 @@
#include <sql_class.h>
#include <mysql/service_wsrep.h>
-my_bool wsrep_thd_is_BF(THD *, my_bool)
+my_bool wsrep_thd_is_BF(const THD *, my_bool)
{ return 0; }
-int wsrep_trx_order_before(THD *, THD *)
-{ return 0; }
-
-enum wsrep_conflict_state wsrep_thd_conflict_state(THD *, my_bool)
-{ return NO_CONFLICT; }
-
-int wsrep_is_wsrep_xid(const XID*)
+int wsrep_is_wsrep_xid(const void* xid)
{ return 0; }
long long wsrep_xid_seqno(const XID* x)
@@ -34,134 +28,122 @@ long long wsrep_xid_seqno(const XID* x)
const unsigned char* wsrep_xid_uuid(const XID*)
{
- static const unsigned char uuid[16] = {0};
+ static const unsigned char uuid[16]= {0};
return uuid;
}
+bool wsrep_prepare_key_for_innodb(THD* thd, const uchar*, size_t, const uchar*, size_t, struct wsrep_buf*, size_t*)
+{ return 1; }
+
bool wsrep_prepare_key(const uchar*, size_t, const uchar*, size_t, struct wsrep_buf*, size_t*)
{ return 0; }
struct wsrep *get_wsrep()
{ return 0; }
-my_bool get_wsrep_certify_nonPK()
-{ return 0; }
-
-my_bool get_wsrep_debug()
-{ return 0; }
-
-my_bool get_wsrep_drupal_282555_workaround()
-{ return 0; }
-
-my_bool get_wsrep_load_data_splitting()
-{ return 0; }
-
my_bool get_wsrep_recovery()
{ return 0; }
-my_bool get_wsrep_log_conflicts()
-{ return 0; }
-
-long get_wsrep_protocol_version()
-{ return 0; }
-
-my_bool wsrep_aborting_thd_contains(THD *)
-{ return 0; }
-
-void wsrep_aborting_thd_enqueue(THD *)
-{ }
-
bool wsrep_consistency_check(THD *)
{ return 0; }
void wsrep_lock_rollback()
{ }
-int wsrep_on(THD *thd)
+my_bool wsrep_on(const THD *)
{ return 0; }
-void wsrep_post_commit(THD*, bool)
+void wsrep_thd_LOCK(const THD *)
{ }
-enum wsrep_trx_status wsrep_run_wsrep_commit(THD *, bool)
-{ return WSREP_TRX_ERROR; }
-
-void wsrep_thd_LOCK(THD *)
-{ }
-
-void wsrep_thd_UNLOCK(THD *)
+void wsrep_thd_UNLOCK(const THD *)
{ }
-void wsrep_thd_kill_LOCK(THD *)
+void wsrep_thd_kill_LOCK(const THD *)
{ }
-void wsrep_thd_kill_UNLOCK(THD *)
-{ }
-
-void wsrep_thd_awake(THD *, my_bool)
+void wsrep_thd_kill_UNLOCK(const THD *)
{ }
const char *wsrep_thd_conflict_state_str(THD *)
{ return 0; }
-enum wsrep_exec_mode wsrep_thd_exec_mode(THD *)
-{ return LOCAL_STATE; }
-
const char *wsrep_thd_exec_mode_str(THD *)
{ return NULL; }
-enum wsrep_conflict_state wsrep_thd_get_conflict_state(THD *)
-{ return NO_CONFLICT; }
+const char *wsrep_thd_query(const THD *)
+{ return "NULL"; }
-my_bool wsrep_thd_is_wsrep(THD *)
+const char *wsrep_thd_query_state_str(THD *)
{ return 0; }
-const char *wsrep_thd_query(THD *)
-{ return "NULL"; }
+int wsrep_thd_retry_counter(const THD *)
+{ return 0; }
-enum wsrep_query_state wsrep_thd_query_state(THD *)
-{ return QUERY_IDLE; }
+bool wsrep_thd_ignore_table(THD *)
+{ return 0; }
-const char *wsrep_thd_query_state_str(THD *)
+long long wsrep_thd_trx_seqno(const THD *)
+{ return -1; }
+
+my_bool wsrep_thd_is_aborting(const THD *)
{ return 0; }
-int wsrep_thd_retry_counter(THD *)
+void wsrep_set_data_home_dir(const char *)
+{ }
+
+my_bool wsrep_thd_is_local(const THD *)
{ return 0; }
-void wsrep_thd_set_conflict_state(THD *, enum wsrep_conflict_state)
+void wsrep_thd_self_abort(THD *)
{ }
-bool wsrep_thd_ignore_table(THD *)
+int wsrep_thd_append_key(THD *, const struct wsrep_key*, int, enum Wsrep_service_key_type)
{ return 0; }
-longlong wsrep_thd_trx_seqno(THD *)
-{ return -1; }
+const char* wsrep_thd_client_state_str(const THD*)
+{ return 0; }
-struct wsrep_ws_handle* wsrep_thd_ws_handle(THD *)
+const char* wsrep_thd_client_mode_str(const THD*)
{ return 0; }
-void wsrep_set_load_multi_commit(THD *thd, bool split)
-{ }
+const char* wsrep_thd_transaction_state_str(const THD*)
+{ return 0; }
-bool wsrep_is_load_multi_commit(THD *thd)
-{ return false; }
+query_id_t wsrep_thd_transaction_id(const THD *)
+{ return 0; }
+
+my_bool wsrep_thd_bf_abort(THD *, THD *, my_bool)
+{ return 0; }
-int wsrep_trx_is_aborting(THD *)
+my_bool wsrep_thd_order_before(const THD*, const THD *)
{ return 0; }
-void wsrep_unlock_rollback()
+void wsrep_handle_SR_rollback(THD*, THD*)
{ }
-void wsrep_set_data_home_dir(const char *)
+my_bool wsrep_thd_skip_locking(const THD*)
+{ return 0;}
+
+const char* wsrep_get_sr_table_name()
+{ return 0; }
+
+my_bool wsrep_get_debug()
+{ return 0;}
+
+void wsrep_commit_ordered(THD* )
{ }
void wsrep_log(void (*)(const char *, ...), const char *, ...)
{
}
-my_bool wsrep_thd_is_applier(MYSQL_THD thd)
-{ return false; }
+my_bool wsrep_thd_is_applying(const THD*)
+{ return 0;}
+
+bool wsrep_thd_set_wsrep_aborter(THD*, THD*)
+{ return 0;}
-void wsrep_report_bf_lock_wait(MYSQL_THD thd,
- unsigned long long id)
+void wsrep_report_bf_lock_wait(const THD*,
+ unsigned long long)
{}
diff --git a/sql/wsrep_high_priority_service.cc b/sql/wsrep_high_priority_service.cc
new file mode 100644
index 00000000000..0da71c3eda5
--- /dev/null
+++ b/sql/wsrep_high_priority_service.cc
@@ -0,0 +1,714 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "wsrep_high_priority_service.h"
+#include "wsrep_applier.h"
+#include "wsrep_binlog.h"
+#include "wsrep_schema.h"
+#include "wsrep_xid.h"
+#include "wsrep_trans_observer.h"
+
+#include "sql_class.h" /* THD */
+#include "transaction.h"
+#include "debug_sync.h"
+/* RLI */
+#include "rpl_rli.h"
+#define NUMBER_OF_FIELDS_TO_IDENTIFY_COORDINATOR 1
+#define NUMBER_OF_FIELDS_TO_IDENTIFY_WORKER 2
+#include "slave.h"
+#include "rpl_mi.h"
+
+namespace
+{
+/*
+ Scoped mode for applying non-transactional write sets (TOI)
+ */
+class Wsrep_non_trans_mode
+{
+public:
+ Wsrep_non_trans_mode(THD* thd, const wsrep::ws_meta& ws_meta)
+ : m_thd(thd)
+ , m_option_bits(thd->variables.option_bits)
+ , m_server_status(thd->server_status)
+ {
+ m_thd->variables.option_bits&= ~OPTION_BEGIN;
+ m_thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ m_thd->wsrep_cs().enter_toi_mode(ws_meta);
+ }
+ ~Wsrep_non_trans_mode()
+ {
+ m_thd->variables.option_bits= m_option_bits;
+ m_thd->server_status= m_server_status;
+ m_thd->wsrep_cs().leave_toi_mode();
+ }
+private:
+ Wsrep_non_trans_mode(const Wsrep_non_trans_mode&);
+ Wsrep_non_trans_mode& operator=(const Wsrep_non_trans_mode&);
+ THD* m_thd;
+ ulonglong m_option_bits;
+ uint m_server_status;
+};
+}
+
+static rpl_group_info* wsrep_relay_group_init(THD* thd, const char* log_fname)
+{
+ Relay_log_info* rli= new Relay_log_info(false);
+
+ if (!rli->relay_log.description_event_for_exec)
+ {
+ rli->relay_log.description_event_for_exec=
+ new Format_description_log_event(4);
+ }
+
+ static LEX_CSTRING connection_name= { STRING_WITH_LEN("wsrep") };
+
+ /*
+ Master_info's constructor initializes rpl_filter by either an already
+ constructed Rpl_filter object from global 'rpl_filters' list if the
+ specified connection name is same, or it constructs a new Rpl_filter
+ object and adds it to rpl_filters. This object is later destructed by
+ Mater_info's destructor by looking it up based on connection name in
+ rpl_filters list.
+
+ However, since all Master_info objects created here would share same
+ connection name ("wsrep"), destruction of any of the existing Master_info
+ objects (in wsrep_return_from_bf_mode()) would free rpl_filter referenced
+ by any/all existing Master_info objects.
+
+ In order to avoid that, we have added a check in Master_info's destructor
+ to not free the "wsrep" rpl_filter. It will eventually be freed by
+ free_all_rpl_filters() when server terminates.
+ */
+ rli->mi= new Master_info(&connection_name, false);
+
+ struct rpl_group_info *rgi= new rpl_group_info(rli);
+ rgi->thd= rli->sql_driver_thd= thd;
+
+ if ((rgi->deferred_events_collecting= rli->mi->rpl_filter->is_on()))
+ {
+ rgi->deferred_events= new Deferred_log_events(rli);
+ }
+
+ return rgi;
+}
+
+static void wsrep_setup_uk_and_fk_checks(THD* thd)
+{
+ /* Tune FK and UK checking policy. These are reset back to original
+ in Wsrep_high_priority_service destructor. */
+ if (wsrep_slave_UK_checks == FALSE)
+ thd->variables.option_bits|= OPTION_RELAXED_UNIQUE_CHECKS;
+ else
+ thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+
+ if (wsrep_slave_FK_checks == FALSE)
+ thd->variables.option_bits|= OPTION_NO_FOREIGN_KEY_CHECKS;
+ else
+ thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
+}
+
+/****************************************************************************
+ High priority service
+*****************************************************************************/
+
+Wsrep_high_priority_service::Wsrep_high_priority_service(THD* thd)
+ : wsrep::high_priority_service(Wsrep_server_state::instance())
+ , wsrep::high_priority_context(thd->wsrep_cs())
+ , m_thd(thd)
+ , m_rli()
+{
+ LEX_CSTRING db_str= { NULL, 0 };
+ m_shadow.option_bits = thd->variables.option_bits;
+ m_shadow.server_status= thd->server_status;
+ m_shadow.vio = thd->net.vio;
+ m_shadow.tx_isolation = thd->variables.tx_isolation;
+ m_shadow.db = (char *)thd->db.str;
+ m_shadow.db_length = thd->db.length;
+ m_shadow.user_time = thd->user_time;
+ m_shadow.row_count_func= thd->get_row_count_func();
+ m_shadow.wsrep_applier= thd->wsrep_applier;
+
+ /* Disable general logging on applier threads */
+ thd->variables.option_bits |= OPTION_LOG_OFF;
+
+ /* enable binlogging regardless of log_slave_updates setting
+ this is for ensuring that both local and applier transaction go through
+ same commit ordering algorithm in group commit control
+ */
+ thd->variables.option_bits|= OPTION_BIN_LOG;
+
+ thd->net.vio= 0;
+ thd->reset_db(&db_str);
+ thd->clear_error();
+ thd->variables.tx_isolation= ISO_READ_COMMITTED;
+ thd->tx_isolation = ISO_READ_COMMITTED;
+
+ /* From trans_begin() */
+ thd->variables.option_bits|= OPTION_BEGIN;
+ thd->server_status|= SERVER_STATUS_IN_TRANS;
+
+ /* Make THD wsrep_applier so that it cannot be killed */
+ thd->wsrep_applier= true;
+
+ if (!thd->wsrep_rgi) thd->wsrep_rgi= wsrep_relay_group_init(thd, "wsrep_relay");
+
+ m_rgi= thd->wsrep_rgi;
+ m_rgi->thd= thd;
+ m_rli= m_rgi->rli;
+ thd_proc_info(thd, "wsrep applier idle");
+}
+
+Wsrep_high_priority_service::~Wsrep_high_priority_service()
+{
+ THD* thd= m_thd;
+ thd->variables.option_bits = m_shadow.option_bits;
+ thd->server_status = m_shadow.server_status;
+ thd->net.vio = m_shadow.vio;
+ thd->variables.tx_isolation= m_shadow.tx_isolation;
+ LEX_CSTRING db_str= { m_shadow.db, m_shadow.db_length };
+ thd->reset_db(&db_str);
+ thd->user_time = m_shadow.user_time;
+
+ if (thd->wsrep_rgi && thd->wsrep_rgi->rli)
+ delete thd->wsrep_rgi->rli->mi;
+ if (thd->wsrep_rgi)
+ delete thd->wsrep_rgi->rli;
+ delete thd->wsrep_rgi;
+ thd->wsrep_rgi= NULL;
+
+ thd->set_row_count_func(m_shadow.row_count_func);
+ thd->wsrep_applier = m_shadow.wsrep_applier;
+}
+
+int Wsrep_high_priority_service::start_transaction(
+ const wsrep::ws_handle& ws_handle, const wsrep::ws_meta& ws_meta)
+{
+ DBUG_ENTER(" Wsrep_high_priority_service::start_transaction");
+ DBUG_RETURN(m_thd->wsrep_cs().start_transaction(ws_handle, ws_meta) ||
+ trans_begin(m_thd));
+}
+
+const wsrep::transaction& Wsrep_high_priority_service::transaction() const
+{
+ DBUG_ENTER(" Wsrep_high_priority_service::transaction");
+ DBUG_RETURN(m_thd->wsrep_trx());
+}
+
+int Wsrep_high_priority_service::next_fragment(const wsrep::ws_meta& ws_meta)
+{
+ DBUG_ENTER(" Wsrep_high_priority_service::next_fragment");
+ DBUG_RETURN(m_thd->wsrep_cs().next_fragment(ws_meta));
+}
+
+int Wsrep_high_priority_service::adopt_transaction(
+ const wsrep::transaction& transaction)
+{
+ DBUG_ENTER(" Wsrep_high_priority_service::adopt_transaction");
+ /* Adopt transaction first to set up transaction meta data for
+ trans begin. If trans_begin() fails for some reason, roll back
+ the wsrep transaction before return. */
+ m_thd->wsrep_cs().adopt_transaction(transaction);
+ int ret= trans_begin(m_thd);
+ if (ret)
+ {
+ m_thd->wsrep_cs().before_rollback();
+ m_thd->wsrep_cs().after_rollback();
+ }
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_high_priority_service::append_fragment_and_commit(
+ const wsrep::ws_handle& ws_handle,
+ const wsrep::ws_meta& ws_meta,
+ const wsrep::const_buffer& data,
+ const wsrep::xid& xid WSREP_UNUSED)
+{
+ DBUG_ENTER("Wsrep_high_priority_service::append_fragment_and_commit");
+ int ret= start_transaction(ws_handle, ws_meta);
+ /*
+ Start transaction explicitly to avoid early commit via
+ trans_commit_stmt() in append_fragment()
+ */
+ ret= ret || trans_begin(m_thd);
+ ret= ret || wsrep_schema->append_fragment(m_thd,
+ ws_meta.server_id(),
+ ws_meta.transaction_id(),
+ ws_meta.seqno(),
+ ws_meta.flags(),
+ data);
+
+ /*
+ Note: The commit code below seems to be identical to
+ Wsrep_storage_service::commit(). Consider implementing
+ common utility function to deal with commit.
+ */
+ const bool do_binlog_commit= (opt_log_slave_updates &&
+ wsrep_gtid_mode &&
+ m_thd->variables.gtid_seq_no);
+ /*
+ Write skip event into binlog if gtid_mode is on. This is to
+ maintain gtid continuity.
+ */
+ if (do_binlog_commit)
+ {
+ ret= wsrep_write_skip_event(m_thd);
+ }
+
+ if (!ret)
+ {
+ ret= m_thd->wsrep_cs().prepare_for_ordering(ws_handle,
+ ws_meta, true);
+ }
+
+ ret= ret || trans_commit(m_thd);
+
+ m_thd->wsrep_cs().after_applying();
+ m_thd->release_transactional_locks();
+
+ free_root(m_thd->mem_root, MYF(MY_KEEP_PREALLOC));
+
+ thd_proc_info(m_thd, "wsrep applier committed");
+
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_high_priority_service::remove_fragments(const wsrep::ws_meta& ws_meta)
+{
+ DBUG_ENTER("Wsrep_high_priority_service::remove_fragments");
+ int ret= wsrep_schema->remove_fragments(m_thd,
+ ws_meta.server_id(),
+ ws_meta.transaction_id(),
+ m_thd->wsrep_sr().fragments());
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_high_priority_service::commit(const wsrep::ws_handle& ws_handle,
+ const wsrep::ws_meta& ws_meta)
+{
+ DBUG_ENTER("Wsrep_high_priority_service::commit");
+ THD* thd= m_thd;
+ DBUG_ASSERT(thd->wsrep_trx().active());
+ thd->wsrep_cs().prepare_for_ordering(ws_handle, ws_meta, true);
+ thd_proc_info(thd, "committing");
+ int ret=0;
+
+ const bool is_ordered= !ws_meta.seqno().is_undefined();
+
+ if (!thd->transaction.stmt.is_empty())
+ ret= trans_commit_stmt(thd);
+
+ if (ret == 0)
+ ret= trans_commit(thd);
+
+ if (ret == 0)
+ {
+ m_rgi->cleanup_context(thd, 0);
+ }
+
+ m_thd->release_transactional_locks();
+
+ thd_proc_info(thd, "wsrep applier committed");
+
+ if (!is_ordered)
+ {
+ m_thd->wsrep_cs().before_rollback();
+ m_thd->wsrep_cs().after_rollback();
+ }
+ else if (m_thd->wsrep_trx().state() == wsrep::transaction::s_executing)
+ {
+ /*
+ Wsrep commit was ordered but it did not go through commit time
+ hooks and remains active. Cycle through commit hooks to release
+ commit order and to make cleanup happen in after_applying() call.
+
+ This is a workaround for CTAS with empty result set.
+ */
+ WSREP_DEBUG("Commit not finished for applier %llu", thd->thread_id);
+ ret= ret || m_thd->wsrep_cs().before_commit() ||
+ m_thd->wsrep_cs().ordered_commit() ||
+ m_thd->wsrep_cs().after_commit();
+ }
+
+ thd->lex->sql_command= SQLCOM_END;
+
+ free_root(thd->mem_root, MYF(MY_KEEP_PREALLOC));
+
+ must_exit_= check_exit_status();
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_high_priority_service::rollback(const wsrep::ws_handle& ws_handle,
+ const wsrep::ws_meta& ws_meta)
+{
+ DBUG_ENTER("Wsrep_high_priority_service::rollback");
+ m_thd->wsrep_cs().prepare_for_ordering(ws_handle, ws_meta, false);
+ int ret= (trans_rollback_stmt(m_thd) || trans_rollback(m_thd));
+ m_thd->release_transactional_locks();
+ m_thd->mdl_context.release_explicit_locks();
+
+ free_root(m_thd->mem_root, MYF(MY_KEEP_PREALLOC));
+
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_high_priority_service::apply_toi(const wsrep::ws_meta& ws_meta,
+ const wsrep::const_buffer& data,
+ wsrep::mutable_buffer&)
+{
+ DBUG_ENTER("Wsrep_high_priority_service::apply_toi");
+ THD* thd= m_thd;
+ Wsrep_non_trans_mode non_trans_mode(thd, ws_meta);
+
+ wsrep::client_state& client_state(thd->wsrep_cs());
+ DBUG_ASSERT(client_state.in_toi());
+
+ thd_proc_info(thd, "wsrep applier toi");
+
+ WSREP_DEBUG("Wsrep_high_priority_service::apply_toi: %lld",
+ client_state.toi_meta().seqno().get());
+
+ int ret= wsrep_apply_events(thd, m_rli, data.data(), data.size());
+ if (ret != 0 || thd->wsrep_has_ignored_error)
+ {
+ wsrep_dump_rbr_buf_with_header(thd, data.data(), data.size());
+ thd->wsrep_has_ignored_error= false;
+ /* todo: error voting */
+ }
+ trans_commit(thd);
+
+ thd->close_temporary_tables();
+ thd->lex->sql_command= SQLCOM_END;
+
+ wsrep_set_SE_checkpoint(client_state.toi_meta().gtid());
+
+ must_exit_= check_exit_status();
+
+ DBUG_RETURN(ret);
+}
+
+void Wsrep_high_priority_service::store_globals()
+{
+ wsrep_store_threadvars(m_thd);
+ m_thd->wsrep_cs().acquire_ownership();
+}
+
+void Wsrep_high_priority_service::reset_globals()
+{
+ wsrep_reset_threadvars(m_thd);
+}
+
+void Wsrep_high_priority_service::switch_execution_context(wsrep::high_priority_service& orig_high_priority_service)
+{
+ DBUG_ENTER("Wsrep_high_priority_service::switch_execution_context");
+ Wsrep_high_priority_service&
+ orig_hps= static_cast<Wsrep_high_priority_service&>(orig_high_priority_service);
+ m_thd->thread_stack= orig_hps.m_thd->thread_stack;
+ DBUG_VOID_RETURN;
+}
+
+int Wsrep_high_priority_service::log_dummy_write_set(const wsrep::ws_handle& ws_handle,
+ const wsrep::ws_meta& ws_meta,
+ wsrep::mutable_buffer& err)
+{
+ DBUG_ENTER("Wsrep_high_priority_service::log_dummy_write_set");
+ int ret= 0;
+ DBUG_PRINT("info",
+ ("Wsrep_high_priority_service::log_dummy_write_set: seqno=%lld",
+ ws_meta.seqno().get()));
+ if (ws_meta.ordered())
+ {
+ wsrep::client_state& cs(m_thd->wsrep_cs());
+ if (!cs.transaction().active())
+ {
+ cs.start_transaction(ws_handle, ws_meta);
+ }
+ adopt_apply_error(err);
+ WSREP_DEBUG("Log dummy write set %lld", ws_meta.seqno().get());
+ ret= cs.provider().commit_order_enter(ws_handle, ws_meta);
+ if (!(ret && opt_log_slave_updates && wsrep_gtid_mode &&
+ m_thd->variables.gtid_seq_no))
+ {
+ cs.before_rollback();
+ cs.after_rollback();
+ }
+
+ if (!WSREP_EMULATE_BINLOG(m_thd))
+ {
+ wsrep_register_for_group_commit(m_thd);
+ ret = ret || cs.provider().commit_order_leave(ws_handle, ws_meta, err);
+ m_thd->wait_for_prior_commit();
+ }
+
+ wsrep_set_SE_checkpoint(ws_meta.gtid());
+
+ if (!WSREP_EMULATE_BINLOG(m_thd))
+ {
+ wsrep_unregister_from_group_commit(m_thd);
+ }
+ else
+ {
+ ret= ret || cs.provider().commit_order_leave(ws_handle, ws_meta, err);
+ }
+ cs.after_applying();
+ }
+ DBUG_RETURN(ret);
+}
+
+void Wsrep_high_priority_service::debug_crash(const char* crash_point)
+{
+ DBUG_ASSERT(m_thd == current_thd);
+ DBUG_EXECUTE_IF(crash_point, DBUG_SUICIDE(););
+}
+
+/****************************************************************************
+ Applier service
+*****************************************************************************/
+
+Wsrep_applier_service::Wsrep_applier_service(THD* thd)
+ : Wsrep_high_priority_service(thd)
+{
+ thd->wsrep_applier_service= this;
+ thd->wsrep_cs().open(wsrep::client_id(thd->thread_id));
+ thd->wsrep_cs().before_command();
+ thd->wsrep_cs().debug_log_level(wsrep_debug);
+ if (!thd->slave_thread)
+ thd->system_thread_info.rpl_sql_info=
+ new rpl_sql_thread_info(thd->wsrep_rgi->rli->mi->rpl_filter);
+}
+
+Wsrep_applier_service::~Wsrep_applier_service()
+{
+ if (!m_thd->slave_thread)
+ delete m_thd->system_thread_info.rpl_sql_info;
+ m_thd->wsrep_cs().after_command_before_result();
+ m_thd->wsrep_cs().after_command_after_result();
+ m_thd->wsrep_cs().close();
+ m_thd->wsrep_cs().cleanup();
+ m_thd->wsrep_applier_service= NULL;
+}
+
+int Wsrep_applier_service::apply_write_set(const wsrep::ws_meta& ws_meta,
+ const wsrep::const_buffer& data,
+ wsrep::mutable_buffer&)
+{
+ DBUG_ENTER("Wsrep_applier_service::apply_write_set");
+ THD* thd= m_thd;
+
+ thd->variables.option_bits |= OPTION_BEGIN;
+ thd->variables.option_bits |= OPTION_NOT_AUTOCOMMIT;
+ DBUG_ASSERT(thd->wsrep_trx().active());
+ DBUG_ASSERT(thd->wsrep_trx().state() == wsrep::transaction::s_executing);
+
+ thd_proc_info(thd, "applying write set");
+ /* moved dbug sync point here, after possible THD switch for SR transactions
+ has ben done
+ */
+ /* Allow tests to block the applier thread using the DBUG facilities */
+ DBUG_EXECUTE_IF("sync.wsrep_apply_cb",
+ {
+ const char act[]=
+ "now "
+ "SIGNAL sync.wsrep_apply_cb_reached "
+ "WAIT_FOR signal.wsrep_apply_cb";
+ DBUG_ASSERT(!debug_sync_set_action(thd,
+ STRING_WITH_LEN(act)));
+ };);
+
+ wsrep_setup_uk_and_fk_checks(thd);
+
+ int ret= wsrep_apply_events(thd, m_rli, data.data(), data.size());
+
+ if (ret || thd->wsrep_has_ignored_error)
+ {
+ wsrep_dump_rbr_buf_with_header(thd, data.data(), data.size());
+ }
+
+ thd->close_temporary_tables();
+ if (!ret && !(ws_meta.flags() & wsrep::provider::flag::commit))
+ {
+ thd->wsrep_cs().fragment_applied(ws_meta.seqno());
+ }
+ thd_proc_info(thd, "wsrep applied write set");
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_applier_service::apply_nbo_begin(const wsrep::ws_meta& ws_meta,
+ const wsrep::const_buffer& data,
+ wsrep::mutable_buffer& err)
+{
+ DBUG_ENTER("Wsrep_applier_service::apply_nbo_begin");
+ DBUG_RETURN(0);
+}
+
+void Wsrep_applier_service::after_apply()
+{
+ DBUG_ENTER("Wsrep_applier_service::after_apply");
+ wsrep_after_apply(m_thd);
+ DBUG_VOID_RETURN;
+}
+
+bool Wsrep_applier_service::check_exit_status() const
+{
+ bool ret= false;
+ mysql_mutex_lock(&LOCK_wsrep_slave_threads);
+ if (wsrep_slave_count_change < 0)
+ {
+ ++wsrep_slave_count_change;
+ ret= true;
+ }
+ mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
+ return ret;
+}
+
+/****************************************************************************
+ Replayer service
+*****************************************************************************/
+
+Wsrep_replayer_service::Wsrep_replayer_service(THD* replayer_thd, THD* orig_thd)
+ : Wsrep_high_priority_service(replayer_thd)
+ , m_orig_thd(orig_thd)
+ , m_da_shadow()
+ , m_replay_status()
+{
+ /* Response must not have been sent to client */
+ DBUG_ASSERT(!orig_thd->get_stmt_da()->is_sent());
+ /* PS reprepare observer should have been removed already
+ open_table() will fail if we have dangling observer here */
+ DBUG_ASSERT(!orig_thd->m_reprepare_observer);
+ /* Replaying should happen always from after_statement() hook
+ after rollback, which should guarantee that there are no
+ transactional locks */
+ DBUG_ASSERT(!orig_thd->mdl_context.has_transactional_locks());
+
+ /* Make a shadow copy of diagnostics area and reset */
+ m_da_shadow.status= orig_thd->get_stmt_da()->status();
+ if (m_da_shadow.status == Diagnostics_area::DA_OK)
+ {
+ m_da_shadow.affected_rows= orig_thd->get_stmt_da()->affected_rows();
+ m_da_shadow.last_insert_id= orig_thd->get_stmt_da()->last_insert_id();
+ strmake(m_da_shadow.message, orig_thd->get_stmt_da()->message(),
+ sizeof(m_da_shadow.message) - 1);
+ }
+ orig_thd->get_stmt_da()->reset_diagnostics_area();
+
+ /* Release explicit locks */
+ if (orig_thd->locked_tables_mode && orig_thd->lock)
+ {
+ WSREP_WARN("releasing table lock for replaying (%llu)",
+ orig_thd->thread_id);
+ orig_thd->locked_tables_list.unlock_locked_tables(orig_thd);
+ orig_thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
+ }
+
+ thd_proc_info(orig_thd, "wsrep replaying trx");
+
+ /*
+ Switch execution context to replayer_thd and prepare it for
+ replay execution.
+ */
+ /* Copy thd vars from orig_thd before reset, otherwise reset
+ for orig thd clears thread local storage before copy. */
+ wsrep_assign_from_threadvars(replayer_thd);
+ wsrep_reset_threadvars(orig_thd);
+ wsrep_store_threadvars(replayer_thd);
+ wsrep_open(replayer_thd);
+ wsrep_before_command(replayer_thd);
+ replayer_thd->wsrep_cs().clone_transaction_for_replay(orig_thd->wsrep_trx());
+}
+
+Wsrep_replayer_service::~Wsrep_replayer_service()
+{
+ THD* replayer_thd= m_thd;
+ THD* orig_thd= m_orig_thd;
+
+ /* Switch execution context back to original. */
+ wsrep_after_apply(replayer_thd);
+ wsrep_after_command_ignore_result(replayer_thd);
+ wsrep_close(replayer_thd);
+ wsrep_reset_threadvars(replayer_thd);
+ wsrep_store_threadvars(orig_thd);
+
+ DBUG_ASSERT(!orig_thd->get_stmt_da()->is_sent());
+ DBUG_ASSERT(!orig_thd->get_stmt_da()->is_set());
+
+ if (m_replay_status == wsrep::provider::success)
+ {
+ DBUG_ASSERT(replayer_thd->wsrep_cs().current_error() == wsrep::e_success);
+ orig_thd->reset_kill_query();
+ my_ok(orig_thd, m_da_shadow.affected_rows, m_da_shadow.last_insert_id);
+ }
+ else if (m_replay_status == wsrep::provider::error_certification_failed)
+ {
+ wsrep_override_error(orig_thd, ER_LOCK_DEADLOCK);
+ }
+ else
+ {
+ DBUG_ASSERT(0);
+ WSREP_ERROR("trx_replay failed for: %d, schema: %s, query: %s",
+ m_replay_status,
+ orig_thd->db.str, WSREP_QUERY(orig_thd));
+ unireg_abort(1);
+ }
+}
+
+int Wsrep_replayer_service::apply_write_set(const wsrep::ws_meta& ws_meta,
+ const wsrep::const_buffer& data,
+ wsrep::mutable_buffer&)
+{
+ DBUG_ENTER("Wsrep_replayer_service::apply_write_set");
+ THD* thd= m_thd;
+
+ DBUG_ASSERT(thd->wsrep_trx().active());
+ DBUG_ASSERT(thd->wsrep_trx().state() == wsrep::transaction::s_replaying);
+
+ /* Allow tests to block the replayer thread using the DBUG facilities */
+ DBUG_EXECUTE_IF("sync.wsrep_replay_cb",
+ {
+ const char act[]=
+ "now "
+ "SIGNAL sync.wsrep_replay_cb_reached "
+ "WAIT_FOR signal.wsrep_replay_cb";
+ DBUG_ASSERT(!debug_sync_set_action(thd,
+ STRING_WITH_LEN(act)));
+ };);
+
+ wsrep_setup_uk_and_fk_checks(thd);
+
+ int ret= 0;
+ if (!wsrep::starts_transaction(ws_meta.flags()))
+ {
+ DBUG_ASSERT(thd->wsrep_trx().is_streaming());
+ ret= wsrep_schema->replay_transaction(thd,
+ m_rli,
+ ws_meta,
+ thd->wsrep_sr().fragments());
+ }
+
+ ret= ret || wsrep_apply_events(thd, m_rli, data.data(), data.size());
+
+ if (ret || thd->wsrep_has_ignored_error)
+ {
+ wsrep_dump_rbr_buf_with_header(thd, data.data(), data.size());
+ }
+
+ thd->close_temporary_tables();
+ if (!ret && !(ws_meta.flags() & wsrep::provider::flag::commit))
+ {
+ thd->wsrep_cs().fragment_applied(ws_meta.seqno());
+ }
+
+ thd_proc_info(thd, "wsrep replayed write set");
+ DBUG_RETURN(ret);
+}
diff --git a/sql/wsrep_high_priority_service.h b/sql/wsrep_high_priority_service.h
new file mode 100644
index 00000000000..2c61f0126af
--- /dev/null
+++ b/sql/wsrep_high_priority_service.h
@@ -0,0 +1,135 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef WSREP_HIGH_PRIORITY_SERVICE_H
+#define WSREP_HIGH_PRIORITY_SERVICE_H
+
+#include "wsrep/high_priority_service.hpp"
+#include "wsrep/client_state.hpp"
+#include "my_global.h"
+#include "sql_error.h" /* Diagnostics area */
+#include "sql_class.h" /* rpl_group_info */
+
+class THD;
+class Relay_log_info;
+class Wsrep_server_service;
+
+class Wsrep_high_priority_service :
+ public wsrep::high_priority_service,
+ public wsrep::high_priority_context
+{
+public:
+ Wsrep_high_priority_service(THD*);
+ ~Wsrep_high_priority_service();
+ int start_transaction(const wsrep::ws_handle&,
+ const wsrep::ws_meta&);
+ int next_fragment(const wsrep::ws_meta&);
+ const wsrep::transaction& transaction() const;
+ int adopt_transaction(const wsrep::transaction&);
+ int apply_write_set(const wsrep::ws_meta&, const wsrep::const_buffer&,
+ wsrep::mutable_buffer&) = 0;
+ int append_fragment_and_commit(const wsrep::ws_handle&,
+ const wsrep::ws_meta&,
+ const wsrep::const_buffer&,
+ const wsrep::xid&);
+ int remove_fragments(const wsrep::ws_meta&);
+ int commit(const wsrep::ws_handle&, const wsrep::ws_meta&);
+ int rollback(const wsrep::ws_handle&, const wsrep::ws_meta&);
+ int apply_toi(const wsrep::ws_meta&, const wsrep::const_buffer&,
+ wsrep::mutable_buffer&);
+ void store_globals();
+ void reset_globals();
+ void switch_execution_context(wsrep::high_priority_service&);
+ int log_dummy_write_set(const wsrep::ws_handle&,
+ const wsrep::ws_meta&,
+ wsrep::mutable_buffer&);
+ void adopt_apply_error(wsrep::mutable_buffer& err) {}
+
+ virtual bool check_exit_status() const = 0;
+ void debug_crash(const char*);
+protected:
+ friend Wsrep_server_service;
+ THD* m_thd;
+ Relay_log_info* m_rli;
+ rpl_group_info* m_rgi;
+ struct shadow
+ {
+ ulonglong option_bits;
+ uint server_status;
+ struct st_vio* vio;
+ ulong tx_isolation;
+ char* db;
+ size_t db_length;
+ //struct timeval user_time;
+ my_hrtime_t user_time;
+ longlong row_count_func;
+ bool wsrep_applier;
+} m_shadow;
+};
+
+class Wsrep_applier_service : public Wsrep_high_priority_service
+{
+public:
+ Wsrep_applier_service(THD*);
+ ~Wsrep_applier_service();
+ int apply_write_set(const wsrep::ws_meta&, const wsrep::const_buffer&,
+ wsrep::mutable_buffer&);
+ int apply_nbo_begin(const wsrep::ws_meta&, const wsrep::const_buffer& data,
+ wsrep::mutable_buffer& err);
+ void after_apply();
+ bool is_replaying() const { return false; }
+ bool check_exit_status() const;
+};
+
+class Wsrep_replayer_service : public Wsrep_high_priority_service
+{
+public:
+ Wsrep_replayer_service(THD* replayer_thd, THD* orig_thd);
+ ~Wsrep_replayer_service();
+ int apply_write_set(const wsrep::ws_meta&, const wsrep::const_buffer&,
+ wsrep::mutable_buffer&);
+ int apply_nbo_begin(const wsrep::ws_meta&, const wsrep::const_buffer& data,
+ wsrep::mutable_buffer& err)
+ {
+ DBUG_ASSERT(0); /* DDL should never cause replaying */
+ return 0;
+ }
+ void after_apply() { }
+ bool is_replaying() const { return true; }
+ void replay_status(enum wsrep::provider::status status)
+ { m_replay_status = status; }
+ enum wsrep::provider::status replay_status() const
+ { return m_replay_status; }
+ /* Replayer should never be forced to exit */
+ bool check_exit_status() const { return false; }
+private:
+ THD* m_orig_thd;
+ struct da_shadow
+ {
+ enum Diagnostics_area::enum_diagnostics_status status;
+ ulonglong affected_rows;
+ ulonglong last_insert_id;
+ char message[MYSQL_ERRMSG_SIZE];
+ da_shadow()
+ : status()
+ , affected_rows()
+ , last_insert_id()
+ , message()
+ { }
+ } m_da_shadow;
+ enum wsrep::provider::status m_replay_status;
+};
+
+#endif /* WSREP_HIGH_PRIORITY_SERVICE_H */
diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc
deleted file mode 100644
index 05be257cbcb..00000000000
--- a/sql/wsrep_hton.cc
+++ /dev/null
@@ -1,675 +0,0 @@
-/* Copyright 2008-2015 Codership Oy <http://www.codership.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-
-#include "mariadb.h"
-#include <mysqld.h>
-#include "sql_base.h"
-#include "rpl_filter.h"
-#include <sql_class.h>
-#include "wsrep_mysqld.h"
-#include "wsrep_binlog.h"
-#include "wsrep_xid.h"
-#include <cstdio>
-#include <cstdlib>
-#include "debug_sync.h"
-
-extern handlerton *binlog_hton;
-extern int binlog_close_connection(handlerton *hton, THD *thd);
-extern ulonglong thd_to_trx_id(THD *thd);
-
-extern "C" int thd_binlog_format(const MYSQL_THD thd);
-// todo: share interface with ha_innodb.c
-
-/*
- Cleanup after local transaction commit/rollback, replay or TOI.
-*/
-void wsrep_cleanup_transaction(THD *thd)
-{
- if (!WSREP(thd)) return;
- DBUG_ASSERT(thd->wsrep_conflict_state != MUST_REPLAY &&
- thd->wsrep_conflict_state != REPLAYING);
-
- if (wsrep_emulate_bin_log) thd_binlog_trx_reset(thd);
- thd->wsrep_ws_handle.trx_id= WSREP_UNDEFINED_TRX_ID;
- thd->wsrep_trx_meta.gtid= WSREP_GTID_UNDEFINED;
- thd->wsrep_trx_meta.depends_on= WSREP_SEQNO_UNDEFINED;
- thd->wsrep_exec_mode= LOCAL_STATE;
- thd->wsrep_affected_rows= 0;
- thd->wsrep_skip_wsrep_GTID= false;
- thd->wsrep_split_flag= false;
- return;
-}
-
-/*
- wsrep hton
-*/
-handlerton *wsrep_hton;
-
-
-/*
- Registers wsrep hton at commit time if transaction has registered htons
- for supported engine types.
-
- Hton should not be registered for TOTAL_ORDER operations.
-
- Registration is needed for both LOCAL_MODE and REPL_RECV transactions to run
- commit in 2pc so that wsrep position gets properly recorded in storage
- engines.
-
- Note that all hton calls should immediately return for threads that are
- in REPL_RECV mode as their states are controlled by wsrep appliers or
- replaying code. Only threads in LOCAL_MODE should run wsrep callbacks
- from hton methods.
-*/
-void wsrep_register_hton(THD* thd, bool all)
-{
- if (WSREP(thd) && thd->wsrep_exec_mode != TOTAL_ORDER &&
- !thd->wsrep_apply_toi)
- {
- if (thd->wsrep_exec_mode == LOCAL_STATE &&
- (thd_sql_command(thd) == SQLCOM_OPTIMIZE ||
- thd_sql_command(thd) == SQLCOM_ANALYZE ||
- thd_sql_command(thd) == SQLCOM_REPAIR) &&
- thd->lex->no_write_to_binlog == 1)
- {
- WSREP_DEBUG("Skipping wsrep_register_hton for LOCAL sql admin command : %s",
- thd->query());
- return;
- }
-
- THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
- for (Ha_trx_info *i= trans->ha_list; i; i = i->next())
- {
- if ((i->ht()->db_type == DB_TYPE_INNODB) ||
- (i->ht()->db_type == DB_TYPE_TOKUDB))
- {
- trans_register_ha(thd, all, wsrep_hton);
-
- /* follow innodb read/write setting
- * but, as an exception: CTAS with empty result set will not be
- * replicated unless we declare wsrep hton as read/write here
- */
- if (i->is_trx_read_write() ||
- ((thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
- thd->lex->sql_command == SQLCOM_CREATE_SEQUENCE) &&
- thd->wsrep_exec_mode == LOCAL_STATE))
- {
- thd->ha_data[wsrep_hton->slot].ha_info[all].set_trx_read_write();
- }
- break;
- }
- }
- }
-}
-
-/*
- Calls wsrep->post_commit() for locally executed transactions that have
- got seqno from provider (must commit) and don't require replaying.
- */
-void wsrep_post_commit(THD* thd, bool all)
-{
- if (!WSREP(thd)) return;
-
- switch (thd->wsrep_exec_mode)
- {
- case LOCAL_COMMIT:
- {
- DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno != WSREP_SEQNO_UNDEFINED);
- if (wsrep && wsrep->post_commit(wsrep, &thd->wsrep_ws_handle))
- {
- DBUG_PRINT("wsrep", ("set committed fail"));
- WSREP_WARN("set committed fail: %llu %d",
- (long long)thd->real_id, thd->get_stmt_da()->status());
- }
- wsrep_cleanup_transaction(thd);
- break;
- }
- case LOCAL_STATE:
- {
- /* non-InnoDB statements may have populated events in stmt cache
- => cleanup
- */
- if (thd->wsrep_conflict_state != MUST_REPLAY)
- {
- WSREP_DEBUG("cleanup transaction for LOCAL_STATE: %s",
- WSREP_QUERY(thd));
- }
- /*
- Run post-rollback hook to clean up in the case if
- some keys were populated for the transaction in provider
- but during commit time there was no write set to replicate.
- This may happen when client sets the SAVEPOINT and immediately
- rolls back to savepoint after first operation.
- */
- if (all && thd->wsrep_conflict_state != MUST_REPLAY &&
- thd->wsrep_conflict_state != REPLAYING &&
- wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle))
- {
- WSREP_WARN("post_rollback fail: %llu %d",
- (long long)thd->thread_id, thd->get_stmt_da()->status());
- }
- if (thd->wsrep_conflict_state != MUST_REPLAY &&
- thd->wsrep_conflict_state != REPLAYING)
- {
- wsrep_cleanup_transaction(thd);
- }
- break;
- }
- default: break;
- }
-}
-
-/*
- wsrep exploits binlog's caches even if binlogging itself is not
- activated. In such case connection close needs calling
- actual binlog's method.
- Todo: split binlog hton from its caches to use ones by wsrep
- without referring to binlog's stuff.
-*/
-static int
-wsrep_close_connection(handlerton* hton, THD* thd)
-{
- DBUG_ENTER("wsrep_close_connection");
-
- if (thd->wsrep_exec_mode == REPL_RECV)
- {
- DBUG_RETURN(0);
- }
-
- if (wsrep_emulate_bin_log && thd_get_ha_data(thd, binlog_hton) != NULL)
- binlog_hton->close_connection (binlog_hton, thd);
- DBUG_RETURN(0);
-}
-
-/*
- prepare/wsrep_run_wsrep_commit can fail in two ways
- - certification test or an equivalent. As a result,
- the current transaction just rolls back
- Error codes:
- WSREP_TRX_CERT_FAIL, WSREP_TRX_SIZE_EXCEEDED, WSREP_TRX_ERROR
- - a post-certification failure makes this server unable to
- commit its own WS and therefore the server must abort
-*/
-static int wsrep_prepare(handlerton *hton, THD *thd, bool all)
-{
- DBUG_ENTER("wsrep_prepare");
-
- if (thd->wsrep_exec_mode == REPL_RECV)
- {
- DBUG_RETURN(0);
- }
-
- DBUG_ASSERT(thd->ha_data[wsrep_hton->slot].ha_info[all].is_trx_read_write());
- DBUG_ASSERT(thd->wsrep_exec_mode == LOCAL_STATE);
- DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno == WSREP_SEQNO_UNDEFINED);
-
- if ((all ||
- !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
- (thd->variables.wsrep_on && !wsrep_trans_cache_is_empty(thd)))
- {
- int res= wsrep_run_wsrep_commit(thd, all);
- if (res != 0)
- {
- if (res == WSREP_TRX_SIZE_EXCEEDED)
- res= EMSGSIZE;
- else
- res= EDEADLK; // for a better error message
- }
- DBUG_RETURN (res);
- }
- DBUG_RETURN(0);
-}
-
-/*
- Empty callbacks to support SAVEPOINT callbacks.
-*/
-
-static int wsrep_savepoint_set(handlerton *hton, THD *thd, void *sv)
-{
- DBUG_ENTER("wsrep_savepoint_set");
- DBUG_RETURN(0);
-}
-
-static int wsrep_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
-{
- DBUG_ENTER("wsrep_savepoint_rollback");
- DBUG_RETURN(0);
-}
-
-static int wsrep_rollback(handlerton *hton, THD *thd, bool all)
-{
- DBUG_ENTER("wsrep_rollback");
-
- if (thd->wsrep_exec_mode == REPL_RECV)
- {
- DBUG_RETURN(0);
- }
-
- mysql_mutex_lock(&thd->LOCK_thd_data);
- switch (thd->wsrep_exec_mode)
- {
- case TOTAL_ORDER:
- case REPL_RECV:
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- WSREP_DEBUG("Avoiding wsrep rollback for failed DDL: %s", thd->query());
- DBUG_RETURN(0);
- default: break;
- }
-
- if ((all || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
- thd->variables.wsrep_on && thd->wsrep_conflict_state != MUST_REPLAY)
- {
- if (wsrep && wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle))
- {
- DBUG_PRINT("wsrep", ("setting rollback fail"));
- WSREP_ERROR("setting rollback fail: thd: %llu, schema: %s, SQL: %s",
- (long long)thd->real_id, thd->get_db(), thd->query());
- }
- wsrep_cleanup_transaction(thd);
- }
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- DBUG_RETURN(0);
-}
-
-int wsrep_commit(handlerton *hton, THD *thd, bool all)
-{
- DBUG_ENTER("wsrep_commit");
-
- if (thd->wsrep_exec_mode == REPL_RECV)
- {
- DBUG_RETURN(0);
- }
-
- mysql_mutex_lock(&thd->LOCK_thd_data);
- if ((all || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
- (thd->variables.wsrep_on && thd->wsrep_conflict_state != MUST_REPLAY))
- {
- if (thd->wsrep_exec_mode == LOCAL_COMMIT)
- {
- DBUG_ASSERT(thd->ha_data[wsrep_hton->slot].ha_info[all].is_trx_read_write());
- /*
- Call to wsrep->post_commit() (moved to wsrep_post_commit()) must
- be done only after commit has done for all involved htons.
- */
- DBUG_PRINT("wsrep", ("commit"));
- }
- else
- {
- /*
- Transaction didn't go through wsrep->pre_commit() so just roll back
- possible changes to clean state.
- */
- if (WSREP_PROVIDER_EXISTS) {
- if (wsrep && wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle))
- {
- DBUG_PRINT("wsrep", ("setting rollback fail"));
- WSREP_ERROR("setting rollback fail: thd: %llu, schema: %s, SQL: %s",
- (long long)thd->real_id, thd->get_db(),
- thd->query());
- }
- }
- wsrep_cleanup_transaction(thd);
- }
- }
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- DBUG_RETURN(0);
-}
-
-
-extern Rpl_filter* binlog_filter;
-extern my_bool opt_log_slave_updates;
-
-enum wsrep_trx_status
-wsrep_run_wsrep_commit(THD *thd, bool all)
-{
- int rcode= -1;
- size_t data_len= 0;
- IO_CACHE *cache;
- int replay_round= 0;
- DBUG_ENTER("wsrep_run_wsrep_commit");
-
- if (thd->get_stmt_da()->is_error()) {
- WSREP_DEBUG("commit issue, error: %d %s",
- thd->get_stmt_da()->sql_errno(), thd->get_stmt_da()->message());
- }
-
- DEBUG_SYNC(thd, "wsrep_before_replication");
-
- if (thd->slave_thread && !opt_log_slave_updates) DBUG_RETURN(WSREP_TRX_OK);
-
- if (thd->wsrep_exec_mode == REPL_RECV) {
-
- mysql_mutex_lock(&thd->LOCK_thd_data);
- if (thd->wsrep_conflict_state == MUST_ABORT) {
- if (wsrep_debug)
- WSREP_INFO("WSREP: must abort for BF");
- DBUG_PRINT("wsrep", ("BF apply commit fail"));
- thd->wsrep_conflict_state = NO_CONFLICT;
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- //
- // TODO: test all calls of the rollback.
- // rollback must happen automagically innobase_rollback(hton, thd, 1);
- //
- DBUG_RETURN(WSREP_TRX_ERROR);
- }
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- }
-
- if (thd->wsrep_exec_mode != LOCAL_STATE) DBUG_RETURN(WSREP_TRX_OK);
-
- if (thd->wsrep_consistency_check == CONSISTENCY_CHECK_RUNNING) {
- WSREP_DEBUG("commit for consistency check: %s", thd->query());
- DBUG_RETURN(WSREP_TRX_OK);
- }
-
- DBUG_PRINT("wsrep", ("replicating commit"));
-
- mysql_mutex_lock(&thd->LOCK_thd_data);
- if (thd->wsrep_conflict_state == MUST_ABORT) {
- DBUG_PRINT("wsrep", ("replicate commit fail"));
- thd->wsrep_conflict_state = ABORTED;
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- if (wsrep_debug) {
- WSREP_INFO("innobase_commit, abort %s",
- (thd->query()) ? thd->query() : "void");
- }
- DBUG_RETURN(WSREP_TRX_CERT_FAIL);
- }
-
- mysql_mutex_lock(&LOCK_wsrep_replaying);
-
- DBUG_PRINT("info", ("wsrep_replaying: %d wsrep_conflict_state: %d killed: %d shutdown_in_progress: %d",
- (int) wsrep_replaying, (int) thd->wsrep_conflict_state,
- (int) thd->killed,
- (int) shutdown_in_progress));
-
- while (wsrep_replaying > 0 &&
- thd->wsrep_conflict_state == NO_CONFLICT &&
- thd->killed == NOT_KILLED &&
- !shutdown_in_progress)
- {
-
- mysql_mutex_unlock(&LOCK_wsrep_replaying);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
-
- mysql_mutex_lock(&thd->mysys_var->mutex);
- thd_proc_info(thd, "WSREP waiting on replaying");
- thd->mysys_var->current_mutex= &LOCK_wsrep_replaying;
- thd->mysys_var->current_cond= &COND_wsrep_replaying;
- mysql_mutex_unlock(&thd->mysys_var->mutex);
-
- mysql_mutex_lock(&LOCK_wsrep_replaying);
- // Using timedwait is a hack to avoid deadlock in case if BF victim
- // misses the signal.
- struct timespec wtime = {0, 1000000};
- mysql_cond_timedwait(&COND_wsrep_replaying, &LOCK_wsrep_replaying,
- &wtime);
-
- if (replay_round++ % 100000 == 0)
- WSREP_DEBUG("commit waiting for replaying: replayers %d, thd: %lld "
- "conflict: %d (round: %d)",
- wsrep_replaying, (longlong) thd->thread_id,
- thd->wsrep_conflict_state, replay_round);
-
- mysql_mutex_unlock(&LOCK_wsrep_replaying);
-
- mysql_mutex_lock(&thd->mysys_var->mutex);
- thd->mysys_var->current_mutex= 0;
- thd->mysys_var->current_cond= 0;
- mysql_mutex_unlock(&thd->mysys_var->mutex);
-
- mysql_mutex_lock(&thd->LOCK_thd_data);
- mysql_mutex_lock(&LOCK_wsrep_replaying);
- }
- mysql_mutex_unlock(&LOCK_wsrep_replaying);
-
- if (thd->wsrep_conflict_state == MUST_ABORT) {
- DBUG_PRINT("wsrep", ("replicate commit fail"));
- thd->wsrep_conflict_state = ABORTED;
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- WSREP_DEBUG("innobase_commit abort after replaying wait %s",
- (thd->query()) ? thd->query() : "void");
- DBUG_RETURN(WSREP_TRX_CERT_FAIL);
- }
-
- wsrep_thd_set_query_state(thd, QUERY_COMMITTING);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
-
- cache = get_trans_log(thd);
- rcode = 0;
- if (cache) {
- thd->binlog_flush_pending_rows_event(true);
- rcode = wsrep_write_cache(wsrep, thd, cache, &data_len);
- if (WSREP_OK != rcode) {
- WSREP_ERROR("rbr write fail, data_len: %zu, %d", data_len, rcode);
- DBUG_RETURN(WSREP_TRX_SIZE_EXCEEDED);
- }
- }
-
- DBUG_PRINT("info", ("rcode: %d wsrep_conflict_state: %d",
- rcode, thd->wsrep_conflict_state));
-
- if (data_len == 0)
- {
- if (thd->get_stmt_da()->is_ok() &&
- thd->get_stmt_da()->affected_rows() > 0 &&
- !binlog_filter->is_on())
- {
- WSREP_DEBUG("empty rbr buffer, query: %s, "
- "affected rows: %llu, "
- "changed tables: %d, "
- "sql_log_bin: %d, "
- "wsrep status (%d %d %d)",
- thd->query(), thd->get_stmt_da()->affected_rows(),
- stmt_has_updated_trans_table(thd), thd->variables.sql_log_bin,
- thd->wsrep_exec_mode, thd->wsrep_query_state,
- thd->wsrep_conflict_state);
- }
- else
- {
- WSREP_DEBUG("empty rbr buffer, query: %s", thd->query());
- }
- wsrep_thd_set_query_state(thd, QUERY_EXEC);
- DBUG_RETURN(WSREP_TRX_OK);
- }
-
- if (WSREP_UNDEFINED_TRX_ID == thd->wsrep_ws_handle.trx_id)
- {
- /*
- Async replication slave may have applied some non-innodb workload,
- and then has written replication "meta data" into gtid_slave_pos
- innodb table. Writes to gtid_slave_pos must not be replicated,
- but this activity has caused that innodb hton is registered for this
- transaction, but no wsrep keys have been appended.
- We enter in this code path, because IO cache has events for non-innodb
- tables.
- => we should not treat it an error if trx is not introduced for provider
- */
- if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL)
- {
- WSREP_DEBUG("skipping wsrep replication for async slave, error not raised");
- DBUG_RETURN(WSREP_TRX_OK);
- }
-
- WSREP_WARN("SQL statement was ineffective thd: %llu buf: %zu\n"
- "schema: %s \n"
- "QUERY: %s\n"
- " => Skipping replication",
- (ulonglong) thd->thread_id, data_len,
- thd->get_db(), thd->query());
- rcode = WSREP_TRX_FAIL;
- }
- else if (!rcode)
- {
- if (WSREP_OK == rcode && wsrep)
- rcode = wsrep->pre_commit(wsrep,
- (wsrep_conn_id_t)thd->thread_id,
- &thd->wsrep_ws_handle,
- WSREP_FLAG_COMMIT |
- ((thd->wsrep_PA_safe) ?
- 0ULL : WSREP_FLAG_PA_UNSAFE),
- &thd->wsrep_trx_meta);
-
- DBUG_PRINT("info", ("rcode after pre_commit: %d", rcode));
-
- if (rcode == WSREP_TRX_MISSING) {
- WSREP_WARN("Transaction missing in provider, thd: %lld schema: %s SQL: %s",
- (longlong) thd->thread_id,
- thd->get_db(), thd->query());
- rcode = WSREP_TRX_FAIL;
- } else if (rcode == WSREP_BF_ABORT) {
- WSREP_DEBUG("thd: %lld seqno: %lld BF aborted by provider, will replay",
- (longlong) thd->thread_id,
- (longlong) thd->wsrep_trx_meta.gtid.seqno);
- mysql_mutex_lock(&thd->LOCK_thd_data);
- thd->wsrep_conflict_state = MUST_REPLAY;
- DBUG_ASSERT(wsrep_thd_trx_seqno(thd) > 0);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- mysql_mutex_lock(&LOCK_wsrep_replaying);
- wsrep_replaying++;
- WSREP_DEBUG("replaying increased: %d, thd: %lld",
- wsrep_replaying, (longlong) thd->thread_id);
- mysql_mutex_unlock(&LOCK_wsrep_replaying);
- }
- } else {
- WSREP_ERROR("I/O error reading from thd's binlog iocache: "
- "errno=%d, io cache code=%d", my_errno, cache->error);
- DBUG_ASSERT(0); // failure like this can not normally happen
- DBUG_RETURN(WSREP_TRX_ERROR);
- }
-
- mysql_mutex_lock(&thd->LOCK_thd_data);
-
- DEBUG_SYNC(thd, "wsrep_after_replication");
-
- DBUG_PRINT("info", ("rcode: %d wsrep_conflict_state: %d",
- rcode, thd->wsrep_conflict_state));
-
- switch(rcode) {
- case 0:
- /*
- About MUST_ABORT: We assume that even if thd conflict state was set
- to MUST_ABORT, underlying transaction was not rolled back or marked
- as deadlock victim in QUERY_COMMITTING state. Conflict state is
- set to NO_CONFLICT and commit proceeds as usual.
- */
- if (thd->wsrep_conflict_state == MUST_ABORT)
- thd->wsrep_conflict_state= NO_CONFLICT;
-
- if (thd->wsrep_conflict_state != NO_CONFLICT)
- {
- WSREP_WARN("thd: %llu seqno: %lld conflict state %d after post commit",
- (longlong) thd->thread_id,
- (longlong) thd->wsrep_trx_meta.gtid.seqno,
- thd->wsrep_conflict_state);
- }
- thd->wsrep_exec_mode= LOCAL_COMMIT;
- DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno != WSREP_SEQNO_UNDEFINED);
- /* Override XID iff it was generated by mysql */
- if (thd->transaction.xid_state.xid.get_my_xid())
- {
- wsrep_xid_init(&thd->transaction.xid_state.xid,
- thd->wsrep_trx_meta.gtid.uuid,
- thd->wsrep_trx_meta.gtid.seqno);
- }
- DBUG_PRINT("wsrep", ("replicating commit success"));
- break;
- case WSREP_BF_ABORT:
- DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno != WSREP_SEQNO_UNDEFINED);
- /* fall through */
- case WSREP_TRX_FAIL:
- WSREP_DEBUG("commit failed for reason: %d conf %d",
- rcode, thd->wsrep_conflict_state);
- DBUG_PRINT("wsrep", ("replicating commit fail"));
-
- wsrep_thd_set_query_state(thd, QUERY_EXEC);
-
- if (thd->wsrep_conflict_state == MUST_ABORT) {
- thd->wsrep_conflict_state= ABORTED;
- }
- else
- {
- WSREP_DEBUG("conflict state: %d", thd->wsrep_conflict_state);
- if (thd->wsrep_conflict_state == NO_CONFLICT)
- {
- thd->wsrep_conflict_state = CERT_FAILURE;
- WSREP_LOG_CONFLICT(NULL, thd, FALSE);
- }
- }
- mysql_mutex_unlock(&thd->LOCK_thd_data);
-
- DBUG_RETURN(WSREP_TRX_CERT_FAIL);
-
- case WSREP_SIZE_EXCEEDED:
- WSREP_ERROR("transaction size exceeded");
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- DBUG_RETURN(WSREP_TRX_SIZE_EXCEEDED);
- case WSREP_CONN_FAIL:
- WSREP_ERROR("connection failure");
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- DBUG_RETURN(WSREP_TRX_ERROR);
- default:
- WSREP_ERROR("unknown connection failure");
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- DBUG_RETURN(WSREP_TRX_ERROR);
- }
-
- wsrep_thd_set_query_state(thd, QUERY_EXEC);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
-
- DBUG_RETURN(WSREP_TRX_OK);
-}
-
-
-static int wsrep_hton_init(void *p)
-{
- wsrep_hton= (handlerton *)p;
- //wsrep_hton->state=opt_bin_log ? SHOW_OPTION_YES : SHOW_OPTION_NO;
- wsrep_hton->state= SHOW_OPTION_YES;
- wsrep_hton->db_type=(legacy_db_type)0;
- wsrep_hton->savepoint_offset= sizeof(my_off_t);
- wsrep_hton->close_connection= wsrep_close_connection;
- wsrep_hton->savepoint_set= wsrep_savepoint_set;
- wsrep_hton->savepoint_rollback= wsrep_savepoint_rollback;
- wsrep_hton->commit= wsrep_commit;
- wsrep_hton->rollback= wsrep_rollback;
- wsrep_hton->prepare= wsrep_prepare;
- wsrep_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN; // todo: fix flags
- return 0;
-}
-
-
-struct st_mysql_storage_engine wsrep_storage_engine=
-{ MYSQL_HANDLERTON_INTERFACE_VERSION };
-
-
-maria_declare_plugin(wsrep)
-{
- MYSQL_STORAGE_ENGINE_PLUGIN,
- &wsrep_storage_engine,
- "wsrep",
- "Codership Oy",
- "A pseudo storage engine to represent transactions in multi-master "
- "synchornous replication",
- PLUGIN_LICENSE_GPL,
- wsrep_hton_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/wsrep_mutex.h b/sql/wsrep_mutex.h
new file mode 100644
index 00000000000..3454b44e0ec
--- /dev/null
+++ b/sql/wsrep_mutex.h
@@ -0,0 +1,50 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef WSREP_MUTEX_H
+#define WSREP_MUTEX_H
+
+/* wsrep-lib */
+#include "wsrep/mutex.hpp"
+
+/* implementation */
+#include "my_pthread.h"
+
+class Wsrep_mutex : public wsrep::mutex
+{
+public:
+ Wsrep_mutex(mysql_mutex_t& mutex)
+ : m_mutex(mutex)
+ { }
+
+ void lock()
+ {
+ mysql_mutex_lock(&m_mutex);
+ }
+
+ void unlock()
+ {
+ mysql_mutex_unlock(&m_mutex);
+ }
+
+ void* native()
+ {
+ return &m_mutex;
+ }
+private:
+ mysql_mutex_t& m_mutex;
+};
+
+#endif /* WSREP_MUTEX_H */
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
index ce798a918e3..195b882a3fb 100644
--- a/sql/wsrep_mysqld.cc
+++ b/sql/wsrep_mysqld.cc
@@ -14,7 +14,12 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
#include "sql_plugin.h" /* wsrep_plugins_pre_init() */
+#include "my_global.h"
+#include "wsrep_server_state.h"
+
+#include "mariadb.h"
#include <mysqld.h>
+#include <transaction.h>
#include <sql_class.h>
#include <sql_parse.h>
#include <sql_base.h> /* find_temporary_table() */
@@ -33,25 +38,32 @@
#include "wsrep_var.h"
#include "wsrep_binlog.h"
#include "wsrep_applier.h"
+#include "wsrep_schema.h"
#include "wsrep_xid.h"
+#include "wsrep_trans_observer.h"
+#include "mysql/service_wsrep.h"
#include <cstdio>
#include <cstdlib>
+#include <string>
#include "log_event.h"
+#include "sql_connect.h"
-wsrep_t *wsrep = NULL;
-/*
- wsrep_emulate_bin_log is a flag to tell that binlog has not been configured.
- wsrep needs to get binlog events from transaction cache even when binlog is
- not enabled, wsrep_emulate_bin_log opens needed code paths to make this
- possible
-*/
-my_bool wsrep_emulate_bin_log = FALSE; // activating parts of binlog interface
+#include <sstream>
+
+/* wsrep-lib */
+Wsrep_server_state* Wsrep_server_state::m_instance;
+
+my_bool wsrep_emulate_bin_log = FALSE; // activating parts of binlog interface
#ifdef GTID_SUPPORT
/* Sidno in global_sid_map corresponding to group uuid */
rpl_sidno wsrep_sidno= -1;
#endif /* GTID_SUPPORT */
my_bool wsrep_preordered_opt= FALSE;
+/* Streaming Replication */
+const char *wsrep_fragment_units[]= { "bytes", "rows", "statements", NullS };
+const char *wsrep_SR_store_types[]= { "none", "table", NullS };
+
/*
* Begin configuration options
*/
@@ -73,7 +85,7 @@ const char *wsrep_data_home_dir;
const char *wsrep_dbug_option;
const char *wsrep_notify_cmd;
-my_bool wsrep_debug; // Enable debug level logging
+ulong wsrep_debug; // Debug level logging
my_bool wsrep_convert_LOCK_to_trx; // Convert locking sessions to trx
my_bool wsrep_auto_increment_control; // Control auto increment variables
my_bool wsrep_drupal_282555_workaround; // Retry autoinc insert after dupkey
@@ -82,7 +94,7 @@ ulong wsrep_certification_rules = WSREP_CERTIFICATION_RULES_STRICT;
my_bool wsrep_recovery; // Recovery
my_bool wsrep_replicate_myisam; // Enable MyISAM replication
my_bool wsrep_log_conflicts;
-my_bool wsrep_load_data_splitting; // Commit load data every 10K intervals
+my_bool wsrep_load_data_splitting= 0; // Commit load data every 10K intervals
my_bool wsrep_slave_UK_checks; // Slave thread does UK checks
my_bool wsrep_slave_FK_checks; // Slave thread does FK checks
my_bool wsrep_restart_slave; // Should mysql slave thread be
@@ -107,13 +119,31 @@ my_bool wsrep_restart_slave_activated= 0; // Node has dropped, and slave
bool wsrep_new_cluster= false; // Bootstrap the cluster?
int wsrep_slave_count_change= 0; // No. of appliers to stop/start
int wsrep_to_isolation= 0; // No. of active TO isolation threads
-long wsrep_max_protocol_version= 3; // Maximum protocol version to use
+long wsrep_max_protocol_version= 4; // Maximum protocol version to use
+long int wsrep_protocol_version= wsrep_max_protocol_version;
+ulong wsrep_trx_fragment_unit= WSREP_FRAG_BYTES;
+ // unit for fragment size
+ulong wsrep_SR_store_type= WSREP_SR_STORE_TABLE;
+uint wsrep_ignore_apply_errors= 0;
+
/*
* End configuration options
*/
/*
+ * Cached variables
+ */
+
+// Whether the Galera write-set replication provider is set
+// wsrep_provider && strcmp(wsrep_provider, WSREP_NONE)
+bool WSREP_PROVIDER_EXISTS_;
+
+// Whether the Galera write-set replication is enabled
+// global_system_variables.wsrep_on && WSREP_PROVIDER_EXISTS_
+bool WSREP_ON_;
+
+/*
* Other wsrep global variables.
*/
@@ -123,14 +153,20 @@ mysql_mutex_t LOCK_wsrep_sst;
mysql_cond_t COND_wsrep_sst;
mysql_mutex_t LOCK_wsrep_sst_init;
mysql_cond_t COND_wsrep_sst_init;
-mysql_mutex_t LOCK_wsrep_rollback;
-mysql_cond_t COND_wsrep_rollback;
-wsrep_aborting_thd_t wsrep_aborting_thd= NULL;
mysql_mutex_t LOCK_wsrep_replaying;
mysql_cond_t COND_wsrep_replaying;
mysql_mutex_t LOCK_wsrep_slave_threads;
+mysql_cond_t COND_wsrep_slave_threads;
+mysql_mutex_t LOCK_wsrep_cluster_config;
mysql_mutex_t LOCK_wsrep_desync;
mysql_mutex_t LOCK_wsrep_config_state;
+mysql_mutex_t LOCK_wsrep_group_commit;
+mysql_mutex_t LOCK_wsrep_SR_pool;
+mysql_mutex_t LOCK_wsrep_SR_store;
+mysql_mutex_t LOCK_wsrep_joiner_monitor;
+mysql_mutex_t LOCK_wsrep_donor_monitor;
+mysql_cond_t COND_wsrep_joiner_monitor;
+mysql_cond_t COND_wsrep_donor_monitor;
int wsrep_replaying= 0;
ulong wsrep_running_threads = 0; // # of currently running wsrep
@@ -141,15 +177,23 @@ ulong wsrep_running_rollbacker_threads = 0; // # of running
ulong my_bind_addr;
#ifdef HAVE_PSI_INTERFACE
-PSI_mutex_key key_LOCK_wsrep_rollback,
+PSI_mutex_key
key_LOCK_wsrep_replaying, key_LOCK_wsrep_ready, key_LOCK_wsrep_sst,
key_LOCK_wsrep_sst_thread, key_LOCK_wsrep_sst_init,
key_LOCK_wsrep_slave_threads, key_LOCK_wsrep_desync,
- key_LOCK_wsrep_config_state;
-
-PSI_cond_key key_COND_wsrep_rollback,
+ key_LOCK_wsrep_config_state, key_LOCK_wsrep_cluster_config,
+ key_LOCK_wsrep_group_commit,
+ key_LOCK_wsrep_SR_pool,
+ key_LOCK_wsrep_SR_store,
+ key_LOCK_wsrep_thd_queue,
+ key_LOCK_wsrep_joiner_monitor,
+ key_LOCK_wsrep_donor_monitor;
+
+PSI_cond_key key_COND_wsrep_thd,
key_COND_wsrep_replaying, key_COND_wsrep_ready, key_COND_wsrep_sst,
- key_COND_wsrep_sst_init, key_COND_wsrep_sst_thread;
+ key_COND_wsrep_sst_init, key_COND_wsrep_sst_thread,
+ key_COND_wsrep_thd_queue, key_COND_wsrep_slave_threads,
+ key_COND_wsrep_joiner_monitor, key_COND_wsrep_donor_monitor;
PSI_file_key key_file_wsrep_gra_log;
@@ -160,11 +204,16 @@ static PSI_mutex_info wsrep_mutexes[]=
{ &key_LOCK_wsrep_sst_thread, "wsrep_sst_thread", 0},
{ &key_LOCK_wsrep_sst_init, "LOCK_wsrep_sst_init", PSI_FLAG_GLOBAL},
{ &key_LOCK_wsrep_sst, "LOCK_wsrep_sst", PSI_FLAG_GLOBAL},
- { &key_LOCK_wsrep_rollback, "LOCK_wsrep_rollback", PSI_FLAG_GLOBAL},
{ &key_LOCK_wsrep_replaying, "LOCK_wsrep_replaying", PSI_FLAG_GLOBAL},
{ &key_LOCK_wsrep_slave_threads, "LOCK_wsrep_slave_threads", PSI_FLAG_GLOBAL},
+ { &key_LOCK_wsrep_cluster_config, "LOCK_wsrep_cluster_config", PSI_FLAG_GLOBAL},
{ &key_LOCK_wsrep_desync, "LOCK_wsrep_desync", PSI_FLAG_GLOBAL},
- { &key_LOCK_wsrep_config_state, "LOCK_wsrep_config_state", PSI_FLAG_GLOBAL}
+ { &key_LOCK_wsrep_config_state, "LOCK_wsrep_config_state", PSI_FLAG_GLOBAL},
+ { &key_LOCK_wsrep_group_commit, "LOCK_wsrep_group_commit", PSI_FLAG_GLOBAL},
+ { &key_LOCK_wsrep_SR_pool, "LOCK_wsrep_SR_pool", PSI_FLAG_GLOBAL},
+ { &key_LOCK_wsrep_SR_store, "LOCK_wsrep_SR_store", PSI_FLAG_GLOBAL},
+ { &key_LOCK_wsrep_joiner_monitor, "LOCK_wsrep_joiner_monitor", PSI_FLAG_GLOBAL},
+ { &key_LOCK_wsrep_donor_monitor, "LOCK_wsrep_donor_monitor", PSI_FLAG_GLOBAL}
};
static PSI_cond_info wsrep_conds[]=
@@ -173,8 +222,11 @@ static PSI_cond_info wsrep_conds[]=
{ &key_COND_wsrep_sst, "COND_wsrep_sst", PSI_FLAG_GLOBAL},
{ &key_COND_wsrep_sst_init, "COND_wsrep_sst_init", PSI_FLAG_GLOBAL},
{ &key_COND_wsrep_sst_thread, "wsrep_sst_thread", 0},
- { &key_COND_wsrep_rollback, "COND_wsrep_rollback", PSI_FLAG_GLOBAL},
- { &key_COND_wsrep_replaying, "COND_wsrep_replaying", PSI_FLAG_GLOBAL}
+ { &key_COND_wsrep_thd, "THD::COND_wsrep_thd", 0},
+ { &key_COND_wsrep_replaying, "COND_wsrep_replaying", PSI_FLAG_GLOBAL},
+ { &key_COND_wsrep_slave_threads, "COND_wsrep_wsrep_slave_threads", PSI_FLAG_GLOBAL},
+ { &key_COND_wsrep_joiner_monitor, "COND_wsrep_joiner_monitor", PSI_FLAG_GLOBAL},
+ { &key_COND_wsrep_donor_monitor, "COND_wsrep_donor_monitor", PSI_FLAG_GLOBAL}
};
static PSI_file_info wsrep_files[]=
@@ -183,322 +235,274 @@ static PSI_file_info wsrep_files[]=
};
PSI_thread_key key_wsrep_sst_joiner, key_wsrep_sst_donor,
- key_wsrep_rollbacker, key_wsrep_applier;
+ key_wsrep_rollbacker, key_wsrep_applier,
+ key_wsrep_sst_joiner_monitor, key_wsrep_sst_donor_monitor;
static PSI_thread_info wsrep_threads[]=
{
{&key_wsrep_sst_joiner, "wsrep_sst_joiner_thread", PSI_FLAG_GLOBAL},
{&key_wsrep_sst_donor, "wsrep_sst_donor_thread", PSI_FLAG_GLOBAL},
{&key_wsrep_rollbacker, "wsrep_rollbacker_thread", PSI_FLAG_GLOBAL},
- {&key_wsrep_applier, "wsrep_applier_thread", PSI_FLAG_GLOBAL}
+ {&key_wsrep_applier, "wsrep_applier_thread", PSI_FLAG_GLOBAL},
+ {&key_wsrep_sst_joiner_monitor, "wsrep_sst_joiner_monitor", PSI_FLAG_GLOBAL},
+ {&key_wsrep_sst_donor_monitor, "wsrep_sst_donor_monitor", PSI_FLAG_GLOBAL}
};
#endif /* HAVE_PSI_INTERFACE */
-my_bool wsrep_inited = 0; // initialized ?
+my_bool wsrep_inited= 0; // initialized ?
-static wsrep_uuid_t cluster_uuid = WSREP_UUID_UNDEFINED;
+static wsrep_uuid_t node_uuid= WSREP_UUID_UNDEFINED;
static char cluster_uuid_str[40]= { 0, };
-static const char* cluster_status_str[WSREP_VIEW_MAX] =
-{
- "Primary",
- "non-Primary",
- "Disconnected"
-};
static char provider_name[256]= { 0, };
static char provider_version[256]= { 0, };
static char provider_vendor[256]= { 0, };
/*
- * wsrep status variables
+ * Wsrep status variables. LOCK_status must be locked When modifying
+ * these variables,
*/
-my_bool wsrep_connected = FALSE;
-my_bool wsrep_ready = FALSE; // node can accept queries
-const char* wsrep_cluster_state_uuid = cluster_uuid_str;
-long long wsrep_cluster_conf_id = WSREP_SEQNO_UNDEFINED;
-const char* wsrep_cluster_status = cluster_status_str[WSREP_VIEW_DISCONNECTED];
-long wsrep_cluster_size = 0;
-long wsrep_local_index = -1;
-long long wsrep_local_bf_aborts = 0;
-const char* wsrep_provider_name = provider_name;
-const char* wsrep_provider_version = provider_version;
-const char* wsrep_provider_vendor = provider_vendor;
+my_bool wsrep_connected = FALSE;
+my_bool wsrep_ready = FALSE;
+const char* wsrep_cluster_state_uuid= cluster_uuid_str;
+long long wsrep_cluster_conf_id = WSREP_SEQNO_UNDEFINED;
+const char* wsrep_cluster_status = "Disconnected";
+long wsrep_cluster_size = 0;
+long wsrep_local_index = -1;
+long long wsrep_local_bf_aborts = 0;
+const char* wsrep_provider_name = provider_name;
+const char* wsrep_provider_version = provider_version;
+const char* wsrep_provider_vendor = provider_vendor;
+char* wsrep_provider_capabilities = NULL;
+char* wsrep_cluster_capabilities = NULL;
/* End wsrep status variables */
-wsrep_uuid_t local_uuid = WSREP_UUID_UNDEFINED;
-wsrep_seqno_t local_seqno = WSREP_SEQNO_UNDEFINED;
-long wsrep_protocol_version = 3;
-
wsp::Config_state *wsrep_config_state;
-// Boolean denoting if server is in initial startup phase. This is needed
-// to make sure that main thread waiting in wsrep_sst_wait() is signaled
-// if there was no state gap on receiving first view event.
-static my_bool wsrep_startup = TRUE;
+void WSREP_LOG(void (*fun)(const char* fmt, ...), const char* fmt, ...)
+{
+ /* Allocate short buffer from stack. If the vsnprintf() return value
+ indicates that the message was truncated, a new buffer will be allocated
+ dynamically and the message will be reprinted. */
+ char msg[128] = {'\0'};
+ va_list arglist;
+ va_start(arglist, fmt);
+ int n= vsnprintf(msg, sizeof(msg) - 1, fmt, arglist);
+ va_end(arglist);
+ if (n < 0)
+ {
+ sql_print_warning("WSREP: Printing message failed");
+ }
+ else if (n < (int)sizeof(msg))
+ {
+ fun("WSREP: %s", msg);
+ }
+ else
+ {
+ size_t dynbuf_size= std::max(n, 4096);
+ char* dynbuf= (char*) my_malloc(dynbuf_size, MYF(0));
+ if (dynbuf)
+ {
+ va_start(arglist, fmt);
+ (void)vsnprintf(&dynbuf[0], dynbuf_size - 1, fmt, arglist);
+ va_end(arglist);
+ dynbuf[dynbuf_size - 1] = '\0';
+ fun("WSREP: %s", &dynbuf[0]);
+ my_free(dynbuf);
+ }
+ else
+ {
+ /* Memory allocation for vector failed, print truncated message. */
+ fun("WSREP: %s", msg);
+ }
+ }
+}
+
+
+wsrep_uuid_t local_uuid = WSREP_UUID_UNDEFINED;
+wsrep_seqno_t local_seqno = WSREP_SEQNO_UNDEFINED;
+wsp::node_status local_status;
+
+/*
+ */
+Wsrep_schema *wsrep_schema= 0;
+static void wsrep_log_cb(wsrep::log::level level,
+ const char*, const char *msg)
+{
+ /*
+ Silence all wsrep related logging from lib and provider if
+ wsrep is not enabled.
+ */
+ if (!WSREP_ON) return;
-static void wsrep_log_cb(wsrep_log_level_t level, const char *msg) {
switch (level) {
- case WSREP_LOG_INFO:
- sql_print_information("WSREP: %s", msg);
+ case wsrep::log::info:
+ WSREP_INFO("%s", msg);
break;
- case WSREP_LOG_WARN:
- sql_print_warning("WSREP: %s", msg);
+ case wsrep::log::warning:
+ WSREP_WARN("%s", msg);
break;
- case WSREP_LOG_ERROR:
- case WSREP_LOG_FATAL:
- sql_print_error("WSREP: %s", msg);
+ case wsrep::log::error:
+ WSREP_ERROR("%s", msg);
break;
- case WSREP_LOG_DEBUG:
- if (wsrep_debug) sql_print_information ("[Debug] WSREP: %s", msg);
- default:
+ case wsrep::log::debug:
+ WSREP_DEBUG("%s", msg);
+ break;
+ case wsrep::log::unknown:
+ WSREP_UNKNOWN("%s", msg);
break;
}
}
-void wsrep_log(void (*fun)(const char *, ...), const char *format, ...)
-{
- va_list args;
- char msg[1024];
- va_start(args, format);
- vsnprintf(msg, sizeof(msg) - 1, format, args);
- va_end(args);
- (fun)("WSREP: %s", msg);
-}
-
-
-static void wsrep_log_states (wsrep_log_level_t const level,
- const wsrep_uuid_t* const group_uuid,
- wsrep_seqno_t const group_seqno,
- const wsrep_uuid_t* const node_uuid,
- wsrep_seqno_t const node_seqno)
-{
- char uuid_str[37];
- char msg[256];
-
- wsrep_uuid_print (group_uuid, uuid_str, sizeof(uuid_str));
- snprintf (msg, 255, "WSREP: Group state: %s:%lld",
- uuid_str, (long long)group_seqno);
- wsrep_log_cb (level, msg);
-
- wsrep_uuid_print (node_uuid, uuid_str, sizeof(uuid_str));
- snprintf (msg, 255, "WSREP: Local state: %s:%lld",
- uuid_str, (long long)node_seqno);
- wsrep_log_cb (level, msg);
-}
-
-#ifdef GTID_SUPPORT
-void wsrep_init_sidno(const wsrep_uuid_t& wsrep_uuid)
+void wsrep_init_sidno(const wsrep::id& uuid)
{
- /* generate new Sid map entry from inverted uuid */
- rpl_sid sid;
- wsrep_uuid_t ltid_uuid;
-
- for (size_t i= 0; i < sizeof(ltid_uuid.data); ++i)
+ /*
+ Protocol versions starting from 4 use group gtid as it is.
+ For lesser protocol versions generate new Sid map entry from inverted
+ uuid.
+ */
+ rpl_gtid sid;
+ if (wsrep_protocol_version >= 4)
{
- ltid_uuid.data[i] = ~wsrep_uuid.data[i];
+ memcpy((void*)&sid, (const uchar*)uuid.data(),16);
}
-
- sid.copy_from(ltid_uuid.data);
+ else
+ {
+ wsrep_uuid_t ltid_uuid;
+ for (size_t i= 0; i < sizeof(ltid_uuid.data); ++i)
+ {
+ ltid_uuid.data[i]= ~((const uchar*)uuid.data())[i];
+ }
+ memcpy((void*)&sid, (const uchar*)ltid_uuid.data,16);
+ }
+#ifdef GTID_SUPPORT
global_sid_lock->wrlock();
wsrep_sidno= global_sid_map->add_sid(sid);
WSREP_INFO("Initialized wsrep sidno %d", wsrep_sidno);
global_sid_lock->unlock();
+#endif
}
-#endif /* GTID_SUPPORT */
-static wsrep_cb_status_t
-wsrep_view_handler_cb (void* app_ctx,
- void* recv_ctx,
- const wsrep_view_info_t* view,
- const char* state,
- size_t state_len,
- void** sst_req,
- size_t* sst_req_len)
+void wsrep_init_schema()
{
- *sst_req = NULL;
- *sst_req_len = 0;
+ DBUG_ASSERT(!wsrep_schema);
- wsrep_member_status_t memb_status= wsrep_config_state->get_status();
-
- if (memcmp(&cluster_uuid, &view->state_id.uuid, sizeof(wsrep_uuid_t)))
- {
- memcpy(&cluster_uuid, &view->state_id.uuid, sizeof(cluster_uuid));
-
- wsrep_uuid_print (&cluster_uuid, cluster_uuid_str,
- sizeof(cluster_uuid_str));
- }
-
- wsrep_cluster_conf_id= view->view;
- wsrep_cluster_status= cluster_status_str[view->status];
- wsrep_cluster_size= view->memb_num;
- wsrep_local_index= view->my_idx;
-
- WSREP_INFO("New cluster view: global state: %s:%lld, view# %lld: %s, "
- "number of nodes: %ld, my index: %ld, protocol version %d",
- wsrep_cluster_state_uuid, (long long)view->state_id.seqno,
- (long long)wsrep_cluster_conf_id, wsrep_cluster_status,
- wsrep_cluster_size, wsrep_local_index, view->proto_ver);
-
- /* Proceed further only if view is PRIMARY */
- if (WSREP_VIEW_PRIMARY != view->status)
+ WSREP_INFO("wsrep_init_schema_and_SR %p", wsrep_schema);
+ if (!wsrep_schema)
{
-#ifdef HAVE_QUERY_CACHE
- // query cache must be initialised by now
- query_cache.flush();
-#endif /* HAVE_QUERY_CACHE */
-
- wsrep_ready_set(FALSE);
- memb_status= WSREP_MEMBER_UNDEFINED;
- /* Always record local_uuid and local_seqno in non-prim since this
- * may lead to re-initializing provider and start position is
- * determined according to these variables */
- // WRONG! local_uuid should be the last primary configuration uuid we were
- // a member of. local_seqno should be updated in commit calls.
- // local_uuid= cluster_uuid;
- // local_seqno= view->first - 1;
- goto out;
- }
-
- switch (view->proto_ver)
- {
- case 0:
- case 1:
- case 2:
- case 3:
- // version change
- if (view->proto_ver != wsrep_protocol_version)
- {
- my_bool wsrep_ready_saved= wsrep_ready_get();
- wsrep_ready_set(FALSE);
- WSREP_INFO("closing client connections for "
- "protocol change %ld -> %d",
- wsrep_protocol_version, view->proto_ver);
- wsrep_close_client_connections(TRUE);
- wsrep_protocol_version= view->proto_ver;
- wsrep_ready_set(wsrep_ready_saved);
- }
- break;
- default:
- WSREP_ERROR("Unsupported application protocol version: %d",
- view->proto_ver);
- unireg_abort(1);
- }
-
- if (view->state_gap)
- {
- WSREP_WARN("Gap in state sequence. Need state transfer.");
-
- /* After that wsrep will call wsrep_sst_prepare. */
- /* keep ready flag 0 until we receive the snapshot */
- wsrep_ready_set(FALSE);
-
- /* Close client connections to ensure that they don't interfere
- * with SST. Necessary only if storage engines are initialized
- * before SST.
- * TODO: Just killing all ongoing transactions should be enough
- * since wsrep_ready is OFF and no new transactions can start.
- */
- if (!wsrep_before_SE())
+ wsrep_schema= new Wsrep_schema();
+ if (wsrep_schema->init())
{
- WSREP_DEBUG("[debug]: closing client connections for PRIM");
- wsrep_close_client_connections(FALSE);
+ WSREP_ERROR("Failed to init wsrep schema");
+ unireg_abort(1);
}
+ }
+}
- ssize_t const req_len= wsrep_sst_prepare (sst_req);
+void wsrep_deinit_schema()
+{
+ delete wsrep_schema;
+ wsrep_schema= 0;
+}
- if (req_len < 0)
+void wsrep_recover_sr_from_storage(THD *orig_thd)
+{
+ switch (wsrep_SR_store_type)
+ {
+ case WSREP_SR_STORE_TABLE:
+ if (!wsrep_schema)
{
- WSREP_ERROR("SST preparation failed: %zd (%s)", -req_len,
- strerror(-req_len));
- memb_status= WSREP_MEMBER_UNDEFINED;
+ WSREP_ERROR("Wsrep schema not initialized when trying to recover "
+ "streaming transactions");
+ unireg_abort(1);
}
- else
+ if (wsrep_schema->recover_sr_transactions(orig_thd))
{
- assert(sst_req != NULL);
- *sst_req_len= req_len;
- memb_status= WSREP_MEMBER_JOINER;
+ WSREP_ERROR("Failed to recover SR transactions from schema");
+ unireg_abort(1);
}
+ break;
+ default:
+ /* */
+ WSREP_ERROR("Unsupported wsrep SR store type: %lu", wsrep_SR_store_type);
+ unireg_abort(1);
+ break;
}
- else
- {
- /*
- * NOTE: Initialize wsrep_group_uuid here only if it wasn't initialized
- * before - OR - it was reinitilized on startup (lp:992840)
- */
- if (wsrep_startup)
+}
+
+/** Export the WSREP provider's capabilities as a human readable string.
+ * The result is saved in a dynamically allocated string of the form:
+ * :cap1:cap2:cap3:
+ */
+static void wsrep_capabilities_export(wsrep_cap_t const cap, char** str)
+{
+ static const char* names[] =
+ {
+ /* Keep in sync with wsrep/wsrep_api.h WSREP_CAP_* macros. */
+ "MULTI_MASTER",
+ "CERTIFICATION",
+ "PARALLEL_APPLYING",
+ "TRX_REPLAY",
+ "ISOLATION",
+ "PAUSE",
+ "CAUSAL_READS",
+ "CAUSAL_TRX",
+ "INCREMENTAL_WRITESET",
+ "SESSION_LOCKS",
+ "DISTRIBUTED_LOCKS",
+ "CONSISTENCY_CHECK",
+ "UNORDERED",
+ "ANNOTATION",
+ "PREORDERED",
+ "STREAMING",
+ "SNAPSHOT",
+ "NBO",
+ };
+
+ std::string s;
+ for (size_t i= 0; i < sizeof(names) / sizeof(names[0]); ++i)
+ {
+ if (cap & (1ULL << i))
{
- if (wsrep_before_SE())
+ if (s.empty())
{
- wsrep_SE_init_grab();
- // Signal mysqld init thread to continue
- wsrep_sst_complete (&cluster_uuid, view->state_id.seqno, false);
- // and wait for SE initialization
- wsrep_SE_init_wait();
+ s= ":";
}
- else
- {
- local_uuid= cluster_uuid;
- local_seqno= view->state_id.seqno;
- }
- /* Init storage engine XIDs from first view */
- wsrep_set_SE_checkpoint(local_uuid, local_seqno);
-#ifdef GTID_SUPPORT
- wsrep_init_sidno(local_uuid);
-#endif /* GTID_SUPPORT */
- memb_status= WSREP_MEMBER_JOINED;
+ s += names[i];
+ s += ":";
}
-
- // just some sanity check
- if (memcmp (&local_uuid, &cluster_uuid, sizeof (wsrep_uuid_t)))
- {
- WSREP_ERROR("Undetected state gap. Can't continue.");
- wsrep_log_states(WSREP_LOG_FATAL, &cluster_uuid, view->state_id.seqno,
- &local_uuid, -1);
- unireg_abort(1);
- }
- }
-
- if (wsrep_auto_increment_control)
- {
- global_system_variables.auto_increment_offset= view->my_idx + 1;
- global_system_variables.auto_increment_increment= view->memb_num;
}
- { /* capabilities may be updated on new configuration */
- uint64_t const caps(wsrep->capabilities (wsrep));
+ /* A read from the string pointed to by *str may be started at any time,
+ * so it must never point to free(3)d memory or non '\0' terminated string. */
- my_bool const idc((caps & WSREP_CAP_INCREMENTAL_WRITESET) != 0);
- if (TRUE == wsrep_incremental_data_collection && FALSE == idc)
- {
- WSREP_WARN("Unsupported protocol downgrade: "
- "incremental data collection disabled. Expect abort.");
- }
- wsrep_incremental_data_collection = idc;
- }
+ char* const previous= *str;
-out:
- if (view->status == WSREP_VIEW_PRIMARY) wsrep_startup= FALSE;
- wsrep_config_state->set(memb_status, view);
+ *str= strdup(s.c_str());
- return WSREP_CB_SUCCESS;
+ if (previous != NULL)
+ {
+ free(previous);
+ }
}
-my_bool wsrep_ready_set (my_bool x)
+/* Verifies that SE position is consistent with the group position
+ * and initializes other variables */
+void wsrep_verify_SE_checkpoint(const wsrep_uuid_t& uuid,
+ wsrep_seqno_t const seqno)
{
- WSREP_DEBUG("Setting wsrep_ready to %d", x);
- if (mysql_mutex_lock (&LOCK_wsrep_ready)) abort();
- my_bool ret= (wsrep_ready != x);
- if (ret)
- {
- wsrep_ready= x;
- mysql_cond_signal (&COND_wsrep_ready);
- }
- mysql_mutex_unlock (&LOCK_wsrep_ready);
- return ret;
}
+/*
+ Wsrep is considered ready if
+ 1) Provider is not loaded (native mode)
+ 2) Server has reached synced state
+ 3) Server is in joiner mode and mysqldump SST method has been
+ specified
+ See Wsrep_server_service::log_state_change() for further details.
+ */
my_bool wsrep_ready_get (void)
{
if (mysql_mutex_lock (&LOCK_wsrep_ready)) abort();
@@ -515,182 +519,67 @@ int wsrep_show_ready(THD *thd, SHOW_VAR *var, char *buff)
return 0;
}
-// Wait until wsrep has reached ready state
-void wsrep_ready_wait ()
+void wsrep_update_cluster_state_uuid(const char* uuid)
{
- if (mysql_mutex_lock (&LOCK_wsrep_ready)) abort();
- while (!wsrep_ready)
- {
- WSREP_INFO("Waiting to reach ready state");
- mysql_cond_wait (&COND_wsrep_ready, &LOCK_wsrep_ready);
- }
- WSREP_INFO("ready state reached");
- mysql_mutex_unlock (&LOCK_wsrep_ready);
+ strncpy(cluster_uuid_str, uuid, sizeof(cluster_uuid_str) - 1);
}
-static void wsrep_synced_cb(void* app_ctx)
+static void wsrep_init_position()
{
- WSREP_INFO("Synchronized with group, ready for connections");
- my_bool signal_main= wsrep_ready_set(TRUE);
- wsrep_config_state->set(WSREP_MEMBER_SYNCED);
-
- if (signal_main)
- {
- wsrep_SE_init_grab();
- // Signal mysqld init thread to continue
- wsrep_sst_complete (&local_uuid, local_seqno, false);
- // and wait for SE initialization
- wsrep_SE_init_wait();
- }
- if (wsrep_restart_slave_activated)
- {
- int rcode;
- WSREP_INFO("MariaDB slave restart");
- wsrep_restart_slave_activated= FALSE;
-
- mysql_mutex_lock(&LOCK_active_mi);
- if ((rcode = start_slave_threads(0,
- 1 /* need mutex */,
- 0 /* no wait for start*/,
- active_mi,
- master_info_file,
- relay_log_info_file,
- SLAVE_SQL)))
- {
- WSREP_WARN("Failed to create slave threads: %d", rcode);
- }
- mysql_mutex_unlock(&LOCK_active_mi);
-
- }
}
-static void wsrep_init_position()
+/****************************************************************************
+ Helpers for wsrep_init()
+ ****************************************************************************/
+static std::string wsrep_server_name()
{
- /* read XIDs from storage engines */
- wsrep_uuid_t uuid;
- wsrep_seqno_t seqno;
- wsrep_get_SE_checkpoint(uuid, seqno);
-
- if (!memcmp(&uuid, &WSREP_UUID_UNDEFINED, sizeof(wsrep_uuid_t)))
- {
- WSREP_INFO("Read nil XID from storage engines, skipping position init");
- return;
- }
-
- char uuid_str[40] = {0, };
- wsrep_uuid_print(&uuid, uuid_str, sizeof(uuid_str));
- WSREP_INFO("Initial position: %s:%lld", uuid_str, (long long)seqno);
-
- if (!memcmp(&local_uuid, &WSREP_UUID_UNDEFINED, sizeof(local_uuid)) &&
- local_seqno == WSREP_SEQNO_UNDEFINED)
- {
- // Initial state
- local_uuid= uuid;
- local_seqno= seqno;
- }
- else if (memcmp(&local_uuid, &uuid, sizeof(local_uuid)) ||
- local_seqno != seqno)
- {
- WSREP_WARN("Initial position was provided by configuration or SST, "
- "avoiding override");
- }
+ std::string ret(wsrep_node_name ? wsrep_node_name : "");
+ return ret;
}
-extern char* my_bind_addr_str;
-
-int wsrep_init()
+static std::string wsrep_server_id()
{
- int rcode= -1;
- DBUG_ASSERT(wsrep_inited == 0);
-
- if (strcmp(wsrep_start_position, WSREP_START_POSITION_ZERO) &&
- wsrep_start_position_init(wsrep_start_position))
- {
- return 1;
- }
-
- wsrep_sst_auth_init();
-
- wsrep_ready_set(FALSE);
- assert(wsrep_provider);
-
- wsrep_init_position();
-
- if ((rcode= wsrep_load(wsrep_provider, &wsrep, wsrep_log_cb)) != WSREP_OK)
- {
- if (strcasecmp(wsrep_provider, WSREP_NONE))
- {
- WSREP_ERROR("wsrep_load(%s) failed: %s (%d). Reverting to no provider.",
- wsrep_provider, strerror(rcode), rcode);
- strcpy((char*)wsrep_provider, WSREP_NONE); // damn it's a dirty hack
- return wsrep_init();
- }
- else /* this is for recursive call above */
- {
- WSREP_ERROR("Could not revert to no provider: %s (%d). Need to abort.",
- strerror(rcode), rcode);
- unireg_abort(1);
- }
- }
+ /* using empty server_id, which enables view change handler to
+ set final server_id later on
+ */
+ std::string ret("");
+ return ret;
+}
- if (!WSREP_PROVIDER_EXISTS)
- {
- // enable normal operation in case no provider is specified
- wsrep_ready_set(TRUE);
- global_system_variables.wsrep_on = 0;
- wsrep_init_args args;
- args.logger_cb = wsrep_log_cb;
- args.options = (wsrep_provider_options) ?
- wsrep_provider_options : "";
- rcode = wsrep->init(wsrep, &args);
- if (rcode)
- {
- DBUG_PRINT("wsrep",("wsrep::init() failed: %d", rcode));
- WSREP_ERROR("wsrep::init() failed: %d, must shutdown", rcode);
- wsrep_ready_set(FALSE);
- wsrep->free(wsrep);
- free(wsrep);
- wsrep = NULL;
- }
- else
- {
- wsrep_inited= 1;
- }
- return rcode;
- }
- else
- {
- global_system_variables.wsrep_on = 1;
- strncpy(provider_name,
- wsrep->provider_name, sizeof(provider_name) - 1);
- strncpy(provider_version,
- wsrep->provider_version, sizeof(provider_version) - 1);
- strncpy(provider_vendor,
- wsrep->provider_vendor, sizeof(provider_vendor) - 1);
- }
+static std::string wsrep_server_node_address()
+{
+ std::string ret;
if (!wsrep_data_home_dir || strlen(wsrep_data_home_dir) == 0)
- wsrep_data_home_dir = mysql_real_data_home;
+ wsrep_data_home_dir= mysql_real_data_home;
/* Initialize node address */
- char node_addr[512]= { 0, };
- size_t const node_addr_max= sizeof(node_addr) - 1;
if (!wsrep_node_address || !strcmp(wsrep_node_address, ""))
{
- size_t const ret= wsrep_guess_ip(node_addr, node_addr_max);
- if (!(ret > 0 && ret < node_addr_max))
+ char node_addr[512]= {0, };
+ const size_t node_addr_max= sizeof(node_addr) - 1;
+ size_t guess_ip_ret= wsrep_guess_ip(node_addr, node_addr_max);
+ if (!(guess_ip_ret > 0 && guess_ip_ret < node_addr_max))
{
WSREP_WARN("Failed to guess base node address. Set it explicitly via "
"wsrep_node_address.");
- node_addr[0]= '\0';
+ }
+ else
+ {
+ ret= node_addr;
}
}
else
{
- strncpy(node_addr, wsrep_node_address, node_addr_max);
+ ret= wsrep_node_address;
}
+ return ret;
+}
- /* Initialize node's incoming address */
+static std::string wsrep_server_incoming_address()
+{
+ std::string ret;
+ const std::string node_addr(wsrep_server_node_address());
char inc_addr[512]= { 0, };
size_t const inc_addr_max= sizeof (inc_addr);
@@ -705,7 +594,8 @@ int wsrep_init()
bool is_ipv6= false;
unsigned int my_bind_ip= INADDR_ANY; // default if not set
- if (my_bind_addr_str && strlen(my_bind_addr_str))
+ if (my_bind_addr_str && strlen(my_bind_addr_str) &&
+ strcmp(my_bind_addr_str, "*") != 0)
{
my_bind_ip= wsrep_check_ip(my_bind_addr_str, &is_ipv6);
}
@@ -724,22 +614,28 @@ int wsrep_init()
}
else /* mysqld binds to 0.0.0.0, try taking IP from wsrep_node_address. */
{
- size_t const node_addr_len= strlen(node_addr);
- if (node_addr_len > 0)
+ if (node_addr.size())
{
- wsp::Address addr(node_addr);
-
- if (!addr.is_valid())
+ size_t const ip_len_mdb= wsrep_host_len(node_addr.c_str(), node_addr.size());
+ if (ip_len_mdb + 7 /* :55555\0 */ < inc_addr_max)
{
- WSREP_DEBUG("Could not parse node address : %s", node_addr);
- WSREP_WARN("Guessing address for incoming client connections failed. "
- "Try setting wsrep_node_incoming_address explicitly.");
- goto done;
+ memcpy (inc_addr, node_addr.c_str(), ip_len_mdb);
+ snprintf(inc_addr + ip_len_mdb, inc_addr_max - ip_len_mdb, ":%u",
+ (int)mysqld_port);
}
+ else
+ {
+ WSREP_WARN("Guessing address for incoming client connections: "
+ "address too long.");
+ inc_addr[0]= '\0';
+ }
+ }
- const char *fmt= (addr.is_ipv6()) ? "[%s]:%u" : "%s:%u";
- snprintf(inc_addr, inc_addr_max, fmt, addr.get_address(),
- (int) mysqld_port);
+ if (!strlen(inc_addr))
+ {
+ WSREP_WARN("Guessing address for incoming client connections failed. "
+ "Try setting wsrep_node_incoming_address explicitly.");
+ WSREP_INFO("Node addr: %s", node_addr.c_str());
}
}
}
@@ -763,52 +659,180 @@ int wsrep_init()
snprintf(inc_addr, inc_addr_max, fmt, addr.get_address(), port);
}
+
+ done:
+ ret= wsrep_node_incoming_address;
+ return ret;
+}
-done:
- struct wsrep_init_args wsrep_args;
+static std::string wsrep_server_working_dir()
+{
+ std::string ret;
+ if (!wsrep_data_home_dir || strlen(wsrep_data_home_dir) == 0)
+ {
+ ret= mysql_real_data_home;
+ }
+ else
+ {
+ ret= wsrep_data_home_dir;
+ }
+ return ret;
+}
- struct wsrep_gtid const state_id = { local_uuid, local_seqno };
+static wsrep::gtid wsrep_server_initial_position()
+{
+ wsrep::gtid ret;
+ WSREP_DEBUG("Server initial position: %s", wsrep_start_position);
+ std::istringstream is(wsrep_start_position);
+ is >> ret;
+ return ret;
+}
- wsrep_args.data_dir = wsrep_data_home_dir;
- wsrep_args.node_name = (wsrep_node_name) ? wsrep_node_name : "";
- wsrep_args.node_address = node_addr;
- wsrep_args.node_incoming = inc_addr;
- wsrep_args.options = (wsrep_provider_options) ?
- wsrep_provider_options : "";
- wsrep_args.proto_ver = wsrep_max_protocol_version;
+/*
+ Intitialize provider specific status variables
+ */
+static void wsrep_init_provider_status_variables()
+{
+ wsrep_inited= 1;
+ const wsrep::provider& provider=
+ Wsrep_server_state::instance().provider();
+ strncpy(provider_name,
+ provider.name().c_str(), sizeof(provider_name) - 1);
+ strncpy(provider_version,
+ provider.version().c_str(), sizeof(provider_version) - 1);
+ strncpy(provider_vendor,
+ provider.vendor().c_str(), sizeof(provider_vendor) - 1);
+}
+
+int wsrep_init_server()
+{
+ wsrep::log::logger_fn(wsrep_log_cb);
+ try
+ {
+ std::string server_name;
+ std::string server_id;
+ std::string node_address;
+ std::string incoming_address;
+ std::string working_dir;
+ wsrep::gtid initial_position;
+
+ server_name= wsrep_server_name();
+ server_id= wsrep_server_id();
+ node_address= wsrep_server_node_address();
+ incoming_address= wsrep_server_incoming_address();
+ working_dir= wsrep_server_working_dir();
+ initial_position= wsrep_server_initial_position();
+
+ Wsrep_server_state::init_once(server_name,
+ incoming_address,
+ node_address,
+ working_dir,
+ initial_position,
+ wsrep_max_protocol_version);
+ Wsrep_server_state::instance().debug_log_level(wsrep_debug);
+ }
+ catch (const wsrep::runtime_error& e)
+ {
+ WSREP_ERROR("Failed to init wsrep server %s", e.what());
+ return 1;
+ }
+ catch (const std::exception& e)
+ {
+ WSREP_ERROR("Failed to init wsrep server %s", e.what());
+ }
+ return 0;
+}
- wsrep_args.state_id = &state_id;
+void wsrep_init_globals()
+{
+ wsrep_init_sidno(Wsrep_server_state::instance().connected_gtid().id());
+ wsrep_init_schema();
- wsrep_args.logger_cb = wsrep_log_cb;
- wsrep_args.view_handler_cb = wsrep_view_handler_cb;
- wsrep_args.apply_cb = wsrep_apply_cb;
- wsrep_args.commit_cb = wsrep_commit_cb;
- wsrep_args.unordered_cb = wsrep_unordered_cb;
- wsrep_args.sst_donate_cb = wsrep_sst_donate_cb;
- wsrep_args.synced_cb = wsrep_synced_cb;
+ if (WSREP_ON)
+ {
+ Wsrep_server_state::instance().initialized();
+ }
+}
- rcode = wsrep->init(wsrep, &wsrep_args);
+void wsrep_deinit_server()
+{
+ wsrep_deinit_schema();
+ Wsrep_server_state::destroy();
+}
+
+int wsrep_init()
+{
+ assert(wsrep_provider);
- if (rcode)
+ wsrep_init_position();
+ wsrep_sst_auth_init();
+
+ if (strlen(wsrep_provider)== 0 ||
+ !strcmp(wsrep_provider, WSREP_NONE))
{
- DBUG_PRINT("wsrep",("wsrep::init() failed: %d", rcode));
- WSREP_ERROR("wsrep::init() failed: %d, must shutdown", rcode);
- wsrep->free(wsrep);
- free(wsrep);
- wsrep = NULL;
- } else {
- wsrep_inited= 1;
+ // enable normal operation in case no provider is specified
+ global_system_variables.wsrep_on= 0;
+ int err= Wsrep_server_state::instance().load_provider(wsrep_provider, wsrep_provider_options ? wsrep_provider_options : "");
+ if (err)
+ {
+ DBUG_PRINT("wsrep",("wsrep::init() failed: %d", err));
+ WSREP_ERROR("wsrep::init() failed: %d, must shutdown", err);
+ }
+ else
+ wsrep_init_provider_status_variables();
+ return err;
}
- return rcode;
-}
+ global_system_variables.wsrep_on= 1;
+
+ WSREP_ON_= wsrep_provider && strcmp(wsrep_provider, WSREP_NONE);
+
+ if (wsrep_gtid_mode && opt_bin_log && !opt_log_slave_updates)
+ {
+ WSREP_ERROR("Option --log-slave-updates is required if "
+ "binlog is enabled, GTID mode is on and wsrep provider "
+ "is specified");
+ return 1;
+ }
+
+ if (!wsrep_data_home_dir || strlen(wsrep_data_home_dir) == 0)
+ wsrep_data_home_dir= mysql_real_data_home;
+
+ if (Wsrep_server_state::instance().load_provider(wsrep_provider,
+ wsrep_provider_options))
+ {
+ WSREP_ERROR("Failed to load provider");
+ return 1;
+ }
+ if (!wsrep_provider_is_SR_capable() &&
+ global_system_variables.wsrep_trx_fragment_size > 0)
+ {
+ WSREP_ERROR("The WSREP provider (%s) does not support streaming "
+ "replication but wsrep_trx_fragment_size is set to a "
+ "value other than 0 (%llu). Cannot continue. Either set "
+ "wsrep_trx_fragment_size to 0 or use wsrep_provider that "
+ "supports streaming replication.",
+ wsrep_provider, global_system_variables.wsrep_trx_fragment_size);
+ Wsrep_server_state::instance().unload_provider();
+ return 1;
+ }
+
+ wsrep_init_provider_status_variables();
+ wsrep_capabilities_export(Wsrep_server_state::instance().provider().capabilities(),
+ &wsrep_provider_capabilities);
+
+ WSREP_DEBUG("SR storage init for: %s",
+ (wsrep_SR_store_type == WSREP_SR_STORE_TABLE) ? "table" : "void");
+
+ return 0;
+}
/* Initialize wsrep thread LOCKs and CONDs */
void wsrep_thr_init()
{
DBUG_ENTER("wsrep_thr_init");
- wsrep_config_state = new wsp::Config_state;
+ wsrep_config_state= new wsp::Config_state;
#ifdef HAVE_PSI_INTERFACE
mysql_mutex_register("sql", wsrep_mutexes, array_elements(wsrep_mutexes));
mysql_cond_register("sql", wsrep_conds, array_elements(wsrep_conds));
@@ -822,27 +846,32 @@ void wsrep_thr_init()
mysql_cond_init(key_COND_wsrep_sst, &COND_wsrep_sst, NULL);
mysql_mutex_init(key_LOCK_wsrep_sst_init, &LOCK_wsrep_sst_init, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_COND_wsrep_sst_init, &COND_wsrep_sst_init, NULL);
- mysql_mutex_init(key_LOCK_wsrep_rollback, &LOCK_wsrep_rollback, MY_MUTEX_INIT_FAST);
- mysql_cond_init(key_COND_wsrep_rollback, &COND_wsrep_rollback, NULL);
mysql_mutex_init(key_LOCK_wsrep_replaying, &LOCK_wsrep_replaying, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_COND_wsrep_replaying, &COND_wsrep_replaying, NULL);
mysql_mutex_init(key_LOCK_wsrep_slave_threads, &LOCK_wsrep_slave_threads, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_wsrep_slave_threads, &COND_wsrep_slave_threads, NULL);
+ mysql_mutex_init(key_LOCK_wsrep_cluster_config, &LOCK_wsrep_cluster_config, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_wsrep_desync, &LOCK_wsrep_desync, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_wsrep_config_state, &LOCK_wsrep_config_state, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_wsrep_group_commit, &LOCK_wsrep_group_commit, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_wsrep_SR_pool,
+ &LOCK_wsrep_SR_pool, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_wsrep_SR_store,
+ &LOCK_wsrep_SR_store, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_wsrep_joiner_monitor,
+ &LOCK_wsrep_joiner_monitor, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_wsrep_donor_monitor,
+ &LOCK_wsrep_donor_monitor, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_wsrep_joiner_monitor, &COND_wsrep_joiner_monitor, NULL);
+ mysql_cond_init(key_COND_wsrep_donor_monitor, &COND_wsrep_donor_monitor, NULL);
DBUG_VOID_RETURN;
}
-void wsrep_init_startup (bool first)
+void wsrep_init_startup (bool sst_first)
{
if (wsrep_init()) unireg_abort(1);
- wsrep_thr_lock_init(
- (wsrep_thd_is_brute_force_fun)wsrep_thd_is_BF,
- (wsrep_abort_thd_fun)wsrep_abort_thd,
- wsrep_debug, wsrep_convert_LOCK_to_trx,
- (wsrep_on_fun)wsrep_on);
-
/*
Pre-initialize global_system_variables.table_plugin with a dummy engine
(placeholder) required during the initialization of wsrep threads (THDs).
@@ -858,30 +887,56 @@ void wsrep_init_startup (bool first)
if (!strcmp(wsrep_provider, WSREP_NONE)) return;
/* Skip replication start if no cluster address */
- if (!wsrep_cluster_address || wsrep_cluster_address[0] == 0) return;
+ if (!wsrep_cluster_address_exists()) return;
- if (first) wsrep_sst_grab(); // do it so we can wait for SST below
-
- if (!wsrep_start_replication()) unireg_abort(1);
+ /*
+ Read value of wsrep_new_cluster before wsrep_start_replication(),
+ the value is reset to FALSE inside wsrep_start_replication.
+ */
+ if (!wsrep_start_replication(wsrep_cluster_address)) unireg_abort(1);
wsrep_create_rollbacker();
wsrep_create_appliers(1);
- if (first && !wsrep_sst_wait()) unireg_abort(1);// wait until SST is completed
+ Wsrep_server_state& server_state= Wsrep_server_state::instance();
+ /*
+ If the SST happens before server initialization, wait until the server
+ state reaches initializing. This indicates that
+ either SST was not necessary or SST has been delivered.
+
+ With mysqldump SST (!sst_first) wait until the server reaches
+ joiner state and procedd to accepting connections.
+ */
+ if (sst_first)
+ {
+ server_state.wait_until_state(Wsrep_server_state::s_initializing);
+ }
+ else
+ {
+ server_state.wait_until_state(Wsrep_server_state::s_joiner);
+ }
}
void wsrep_deinit(bool free_options)
{
DBUG_ASSERT(wsrep_inited == 1);
- wsrep_unload(wsrep);
- wsrep= 0;
+ WSREP_DEBUG("wsrep_deinit");
+
+ Wsrep_server_state::instance().unload_provider();
provider_name[0]= '\0';
provider_version[0]= '\0';
provider_vendor[0]= '\0';
wsrep_inited= 0;
+ if (wsrep_provider_capabilities != NULL)
+ {
+ char* p= wsrep_provider_capabilities;
+ wsrep_provider_capabilities= NULL;
+ free(p);
+ }
+
if (free_options)
{
wsrep_sst_auth_free();
@@ -893,28 +948,44 @@ void wsrep_thr_deinit()
{
if (!wsrep_config_state)
return; // Never initialized
+ WSREP_DEBUG("wsrep_thr_deinit");
mysql_mutex_destroy(&LOCK_wsrep_ready);
mysql_cond_destroy(&COND_wsrep_ready);
mysql_mutex_destroy(&LOCK_wsrep_sst);
mysql_cond_destroy(&COND_wsrep_sst);
mysql_mutex_destroy(&LOCK_wsrep_sst_init);
mysql_cond_destroy(&COND_wsrep_sst_init);
- mysql_mutex_destroy(&LOCK_wsrep_rollback);
- mysql_cond_destroy(&COND_wsrep_rollback);
mysql_mutex_destroy(&LOCK_wsrep_replaying);
mysql_cond_destroy(&COND_wsrep_replaying);
mysql_mutex_destroy(&LOCK_wsrep_slave_threads);
+ mysql_cond_destroy(&COND_wsrep_slave_threads);
+ mysql_mutex_destroy(&LOCK_wsrep_cluster_config);
mysql_mutex_destroy(&LOCK_wsrep_desync);
mysql_mutex_destroy(&LOCK_wsrep_config_state);
+ mysql_mutex_destroy(&LOCK_wsrep_group_commit);
+ mysql_mutex_destroy(&LOCK_wsrep_SR_pool);
+ mysql_mutex_destroy(&LOCK_wsrep_SR_store);
+ mysql_mutex_destroy(&LOCK_wsrep_joiner_monitor);
+ mysql_mutex_destroy(&LOCK_wsrep_donor_monitor);
+ mysql_cond_destroy(&COND_wsrep_joiner_monitor);
+ mysql_cond_destroy(&COND_wsrep_donor_monitor);
+
delete wsrep_config_state;
wsrep_config_state= 0; // Safety
+
+ if (wsrep_cluster_capabilities != NULL)
+ {
+ char* p= wsrep_cluster_capabilities;
+ wsrep_cluster_capabilities= NULL;
+ free(p);
+ }
}
void wsrep_recover()
{
char uuid_str[40];
- if (!memcmp(&local_uuid, &WSREP_UUID_UNDEFINED, sizeof(wsrep_uuid_t)) &&
+ if (wsrep_uuid_compare(&local_uuid, &WSREP_UUID_UNDEFINED) == 0 &&
local_seqno == -2)
{
wsrep_uuid_print(&local_uuid, uuid_str, sizeof(uuid_str));
@@ -922,43 +993,60 @@ void wsrep_recover()
uuid_str, (long long)local_seqno);
return;
}
- wsrep_uuid_t uuid;
- wsrep_seqno_t seqno;
- wsrep_get_SE_checkpoint(uuid, seqno);
- wsrep_uuid_print(&uuid, uuid_str, sizeof(uuid_str));
- WSREP_INFO("Recovered position: %s:%lld", uuid_str, (long long)seqno);
+ wsrep::gtid gtid= wsrep_get_SE_checkpoint();
+ std::ostringstream oss;
+ oss << gtid;
+ WSREP_INFO("Recovered position: %s", oss.str().c_str());
}
void wsrep_stop_replication(THD *thd)
{
- WSREP_INFO("Stop replication");
- if (!wsrep)
+ WSREP_INFO("Stop replication by %llu", (thd) ? thd->thread_id : 0);
+ if (Wsrep_server_state::instance().state() !=
+ Wsrep_server_state::s_disconnected)
{
- WSREP_INFO("Provider was not loaded, in stop replication");
- return;
+ WSREP_DEBUG("Disconnect provider");
+ Wsrep_server_state::instance().disconnect();
+ Wsrep_server_state::instance().wait_until_state(Wsrep_server_state::s_disconnected);
}
- /* disconnect from group first to get wsrep_ready == FALSE */
- WSREP_DEBUG("Provider disconnect");
- wsrep->disconnect(wsrep);
+ /* my connection, should not terminate with wsrep_close_client_connection(),
+ make transaction to rollback
+ */
+ if (thd && !thd->wsrep_applier) trans_rollback(thd);
+ wsrep_close_client_connections(TRUE, thd);
+
+ /* wait until appliers have stopped */
+ wsrep_wait_appliers_close(thd);
- wsrep_connected= FALSE;
+ node_uuid= WSREP_UUID_UNDEFINED;
+}
+
+void wsrep_shutdown_replication()
+{
+ WSREP_INFO("Shutdown replication");
+ if (Wsrep_server_state::instance().state() != wsrep::server_state::s_disconnected)
+ {
+ WSREP_DEBUG("Disconnect provider");
+ Wsrep_server_state::instance().disconnect();
+ Wsrep_server_state::instance().wait_until_state(Wsrep_server_state::s_disconnected);
+ }
wsrep_close_client_connections(TRUE);
/* wait until appliers have stopped */
- wsrep_wait_appliers_close(thd);
+ wsrep_wait_appliers_close(NULL);
+ node_uuid= WSREP_UUID_UNDEFINED;
- return;
+ /* Undocking the thread specific data. */
+ my_pthread_setspecific_ptr(THR_THD, NULL);
}
-bool wsrep_start_replication()
+bool wsrep_start_replication(const char *wsrep_cluster_address)
{
- wsrep_status_t rcode;
-
- /* wsrep provider must be loaded. */
- DBUG_ASSERT(wsrep);
+ int rcode;
+ WSREP_DEBUG("wsrep_start_replication");
/*
if provider is trivial, don't even try to connect,
@@ -967,34 +1055,23 @@ bool wsrep_start_replication()
if (!WSREP_PROVIDER_EXISTS)
{
// enable normal operation in case no provider is specified
- wsrep_ready_set(TRUE);
return true;
}
- if (!wsrep_cluster_address || wsrep_cluster_address[0]== 0)
- {
- // if provider is non-trivial, but no address is specified, wait for address
- wsrep_ready_set(FALSE);
- return true;
- }
+ DBUG_ASSERT(wsrep_cluster_address[0]);
- bool const bootstrap= wsrep_new_cluster;
+ bool const bootstrap(TRUE == wsrep_new_cluster);
+ wsrep_new_cluster= FALSE;
WSREP_INFO("Start replication");
- if (wsrep_new_cluster)
+ if ((rcode= Wsrep_server_state::instance().connect(
+ wsrep_cluster_name,
+ wsrep_cluster_address,
+ wsrep_sst_donor,
+ bootstrap)))
{
- WSREP_INFO("'wsrep-new-cluster' option used, bootstrapping the cluster");
- wsrep_new_cluster= false;
- }
-
- if ((rcode = wsrep->connect(wsrep,
- wsrep_cluster_name,
- wsrep_cluster_address,
- wsrep_sst_donor,
- bootstrap)))
- {
- DBUG_PRINT("wsrep",("wsrep->connect(%s) failed: %d",
+ DBUG_PRINT("wsrep",("wsrep_ptr->connect(%s) failed: %d",
wsrep_cluster_address, rcode));
WSREP_ERROR("wsrep::connect(%s) failed: %d",
wsrep_cluster_address, rcode);
@@ -1002,15 +1079,12 @@ bool wsrep_start_replication()
}
else
{
- wsrep_connected= TRUE;
-
- char* opts= wsrep->options_get(wsrep);
- if (opts)
+ try
{
- wsrep_provider_options_init(opts);
- free(opts);
+ std::string opts= Wsrep_server_state::instance().provider().options();
+ wsrep_provider_options_init(opts.c_str());
}
- else
+ catch (const wsrep::runtime_error&)
{
WSREP_WARN("Failed to get wsrep options");
}
@@ -1021,40 +1095,50 @@ bool wsrep_start_replication()
bool wsrep_must_sync_wait (THD* thd, uint mask)
{
- return (thd->variables.wsrep_sync_wait & mask) &&
- thd->variables.wsrep_on &&
+ bool ret;
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ ret= (thd->variables.wsrep_sync_wait & mask) &&
+ thd->wsrep_client_thread &&
+ WSREP_ON && thd->variables.wsrep_on &&
!(thd->variables.wsrep_dirty_reads &&
!is_update_query(thd->lex->sql_command)) &&
!thd->in_active_multi_stmt_transaction() &&
- thd->wsrep_conflict_state != REPLAYING &&
- thd->wsrep_sync_wait_gtid.seqno == WSREP_SEQNO_UNDEFINED;
+ thd->wsrep_trx().state() !=
+ wsrep::transaction::s_replaying &&
+ thd->wsrep_cs().sync_wait_gtid().is_undefined();
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ return ret;
}
bool wsrep_sync_wait (THD* thd, uint mask)
{
if (wsrep_must_sync_wait(thd, mask))
{
- WSREP_DEBUG("wsrep_sync_wait: thd->variables.wsrep_sync_wait = %u, mask = %u",
- thd->variables.wsrep_sync_wait, mask);
- // This allows autocommit SELECTs and a first SELECT after SET AUTOCOMMIT=0
- // TODO: modify to check if thd has locked any rows.
- wsrep_status_t ret= wsrep->causal_read (wsrep, &thd->wsrep_sync_wait_gtid);
-
- if (unlikely(WSREP_OK != ret))
+ WSREP_DEBUG("wsrep_sync_wait: thd->variables.wsrep_sync_wait= %u, "
+ "mask= %u, thd->variables.wsrep_on= %d",
+ thd->variables.wsrep_sync_wait, mask,
+ thd->variables.wsrep_on);
+ /*
+ This allows autocommit SELECTs and a first SELECT after SET AUTOCOMMIT=0
+ TODO: modify to check if thd has locked any rows.
+ */
+ if (thd->wsrep_cs().sync_wait(-1))
{
const char* msg;
int err;
- // Possibly relevant error codes:
- // ER_CHECKREAD, ER_ERROR_ON_READ, ER_INVALID_DEFAULT, ER_EMPTY_QUERY,
- // ER_FUNCTION_NOT_DEFINED, ER_NOT_ALLOWED_COMMAND, ER_NOT_SUPPORTED_YET,
- // ER_FEATURE_DISABLED, ER_QUERY_INTERRUPTED
+ /*
+ Possibly relevant error codes:
+ ER_CHECKREAD, ER_ERROR_ON_READ, ER_INVALID_DEFAULT, ER_EMPTY_QUERY,
+ ER_FUNCTION_NOT_DEFINED, ER_NOT_ALLOWED_COMMAND, ER_NOT_SUPPORTED_YET,
+ ER_FEATURE_DISABLED, ER_QUERY_INTERRUPTED
+ */
- switch (ret)
+ switch (thd->wsrep_cs().current_error())
{
- case WSREP_NOT_IMPLEMENTED:
+ case wsrep::e_not_supported_error:
msg= "synchronous reads by wsrep backend. "
- "Please unset wsrep_causal_reads variable.";
+ "Please unset wsrep_causal_reads variable.";
err= ER_NOT_SUPPORTED_YET;
break;
default:
@@ -1072,6 +1156,27 @@ bool wsrep_sync_wait (THD* thd, uint mask)
return false;
}
+enum wsrep::provider::status
+wsrep_sync_wait_upto (THD* thd,
+ wsrep_gtid_t* upto,
+ int timeout)
+{
+ DBUG_ASSERT(upto);
+ enum wsrep::provider::status ret;
+ if (upto)
+ {
+ wsrep::gtid upto_gtid(wsrep::id(upto->uuid.data, sizeof(upto->uuid.data)),
+ wsrep::seqno(upto->seqno));
+ ret= Wsrep_server_state::instance().wait_for_gtid(upto_gtid, timeout);
+ }
+ else
+ {
+ ret= Wsrep_server_state::instance().causal_read(timeout).second;
+ }
+ WSREP_DEBUG("wsrep_sync_wait_upto: %d", ret);
+ return ret;
+}
+
void wsrep_keys_free(wsrep_key_arr_t* key_arr)
{
for (size_t i= 0; i < key_arr->keys_len; ++i)
@@ -1083,6 +1188,89 @@ void wsrep_keys_free(wsrep_key_arr_t* key_arr)
key_arr->keys_len= 0;
}
+/*!
+ * @param thd thread
+ * @param tables list of tables
+ * @param keys prepared keys
+
+ * @return true if parent table append was successfull, otherwise false.
+*/
+bool
+wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* keys)
+{
+ bool fail= false;
+ TABLE_LIST *table;
+
+ thd->release_transactional_locks();
+ uint counter;
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
+
+ if (thd->open_temporary_tables(tables) ||
+ open_tables(thd, &tables, &counter, MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL))
+ {
+ WSREP_DEBUG("unable to open table for FK checks for %s", thd->query());
+ fail= true;
+ goto exit;
+ }
+
+ for (table= tables; table; table= table->next_local)
+ {
+ if (!is_temporary_table(table) && table->table)
+ {
+ FOREIGN_KEY_INFO *f_key_info;
+ List<FOREIGN_KEY_INFO> f_key_list;
+
+ table->table->file->get_foreign_key_list(thd, &f_key_list);
+ List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list);
+ while ((f_key_info=it++))
+ {
+ WSREP_DEBUG("appended fkey %s", f_key_info->referenced_table->str);
+ keys->push_back(wsrep_prepare_key_for_toi(f_key_info->referenced_db->str,
+ f_key_info->referenced_table->str,
+ wsrep::key::shared));
+ }
+ }
+ }
+
+exit:
+ /* close the table and release MDL locks */
+ close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ for (table= tables; table; table= table->next_local)
+ {
+ table->table= NULL;
+ table->next_global= NULL;
+ table->mdl_request.ticket= NULL;
+ }
+
+ return fail;
+}
+
+bool wsrep_reload_ssl()
+{
+ try
+ {
+ std::string opts= Wsrep_server_state::instance().provider().options();
+ if (opts.find("socket.ssl_reload") == std::string::npos)
+ {
+ WSREP_DEBUG("Option `socket.ssl_reload` not found in parameters.");
+ return false;
+ }
+ const std::string reload_ssl_param("socket.ssl_reload=1");
+ enum wsrep::provider::status ret= Wsrep_server_state::instance().provider().options(reload_ssl_param);
+ if (ret)
+ {
+ WSREP_ERROR("Set options returned %d", ret);
+ return true;
+ }
+ return false;
+ }
+ catch (...)
+ {
+ WSREP_ERROR("Failed to get provider options");
+ return true;
+ }
+}
/*!
* @param db Database string
@@ -1095,9 +1283,9 @@ void wsrep_keys_free(wsrep_key_arr_t* key_arr)
*/
static bool wsrep_prepare_key_for_isolation(const char* db,
- const char* table,
- wsrep_buf_t* key,
- size_t* key_len)
+ const char* table,
+ wsrep_buf_t* key,
+ size_t* key_len)
{
if (*key_len < 2) return false;
@@ -1109,11 +1297,11 @@ static bool wsrep_prepare_key_for_isolation(const char* db,
case 1:
case 2:
case 3:
+ case 4:
{
*key_len= 0;
if (db)
{
- // sql_print_information("%s.%s", db, table);
key[*key_len].ptr= db;
key[*key_len].len= strlen(db);
++(*key_len);
@@ -1127,26 +1315,23 @@ static bool wsrep_prepare_key_for_isolation(const char* db,
break;
}
default:
+ assert(0);
+ WSREP_ERROR("Unsupported protocol version: %ld", wsrep_protocol_version);
+ unireg_abort(1);
return false;
}
- return true;
-}
+ return true;
+}
static bool wsrep_prepare_key_for_isolation(const char* db,
const char* table,
wsrep_key_arr_t* ka)
{
wsrep_key_t* tmp;
-
- if (!ka->keys)
- tmp= (wsrep_key_t*)my_malloc((ka->keys_len + 1) * sizeof(wsrep_key_t),
- MYF(0));
- else
- tmp= (wsrep_key_t*)my_realloc(ka->keys,
- (ka->keys_len + 1) * sizeof(wsrep_key_t),
- MYF(0));
-
+ tmp= (wsrep_key_t*)my_realloc(ka->keys,
+ (ka->keys_len + 1) * sizeof(wsrep_key_t),
+ MYF(MY_ALLOW_ZERO_PTR));
if (!tmp)
{
WSREP_ERROR("Can't allocate memory for key_array");
@@ -1172,7 +1357,6 @@ static bool wsrep_prepare_key_for_isolation(const char* db,
return true;
}
-
static bool wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db,
Alter_info* alter_info,
wsrep_key_arr_t* ka)
@@ -1199,7 +1383,6 @@ static bool wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db,
return true;
}
-
static bool wsrep_prepare_keys_for_isolation(THD* thd,
const char* db,
const char* table,
@@ -1227,16 +1410,19 @@ static bool wsrep_prepare_keys_for_isolation(THD* thd,
if (!wsrep_prepare_keys_for_alter_add_fk(table_list->db.str, alter_info, ka))
goto err;
}
-
return false;
err:
- wsrep_keys_free(ka);
- return true;
+ wsrep_keys_free(ka);
+ return true;
}
+/*
+ * Prepare key list from db/table and table_list
+ *
+ * Return zero in case of success, 1 in case of failure.
+ */
-/* Prepare key list from db/table and table_list */
bool wsrep_prepare_keys_for_isolation(THD* thd,
const char* db,
const char* table,
@@ -1246,7 +1432,6 @@ bool wsrep_prepare_keys_for_isolation(THD* thd,
return wsrep_prepare_keys_for_isolation(thd, db, table, table_list, NULL, ka);
}
-
bool wsrep_prepare_key(const uchar* cache_key, size_t cache_key_len,
const uchar* row_id, size_t row_id_len,
wsrep_buf_t* key, size_t* key_len)
@@ -1258,36 +1443,115 @@ bool wsrep_prepare_key(const uchar* cache_key, size_t cache_key_len,
{
case 0:
{
- key[0].ptr = cache_key;
- key[0].len = cache_key_len;
+ key[0].ptr= cache_key;
+ key[0].len= cache_key_len;
- *key_len = 1;
+ *key_len= 1;
break;
}
case 1:
case 2:
case 3:
+ case 4:
{
- key[0].ptr = cache_key;
- key[0].len = strlen( (char*)cache_key );
+ key[0].ptr= cache_key;
+ key[0].len= strlen( (char*)cache_key );
- key[1].ptr = cache_key + strlen( (char*)cache_key ) + 1;
- key[1].len = strlen( (char*)(key[1].ptr) );
+ key[1].ptr= cache_key + strlen( (char*)cache_key ) + 1;
+ key[1].len= strlen( (char*)(key[1].ptr) );
- *key_len = 2;
+ *key_len= 2;
break;
}
default:
return false;
}
- key[*key_len].ptr = row_id;
- key[*key_len].len = row_id_len;
+ key[*key_len].ptr= row_id;
+ key[*key_len].len= row_id_len;
++(*key_len);
return true;
}
+bool wsrep_prepare_key_for_innodb(THD* thd,
+ const uchar* cache_key,
+ size_t cache_key_len,
+ const uchar* row_id,
+ size_t row_id_len,
+ wsrep_buf_t* key,
+ size_t* key_len)
+{
+
+ return wsrep_prepare_key(cache_key, cache_key_len, row_id, row_id_len, key, key_len);
+}
+
+wsrep::key wsrep_prepare_key_for_toi(const char* db, const char* table,
+ enum wsrep::key::type type)
+{
+ wsrep::key ret(type);
+ DBUG_ASSERT(db);
+ ret.append_key_part(db, strlen(db));
+ if (table) ret.append_key_part(table, strlen(table));
+ return ret;
+}
+
+wsrep::key_array
+wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db,
+ Alter_info* alter_info)
+
+{
+ wsrep::key_array ret;
+ Key *key;
+ List_iterator<Key> key_iterator(alter_info->key_list);
+ while ((key= key_iterator++))
+ {
+ if (key->type == Key::FOREIGN_KEY)
+ {
+ Foreign_key *fk_key= (Foreign_key *)key;
+ const char *db_name= fk_key->ref_db.str;
+ const char *table_name= fk_key->ref_table.str;
+ if (!db_name)
+ {
+ db_name= child_table_db;
+ }
+ ret.push_back(wsrep_prepare_key_for_toi(db_name, table_name,
+ wsrep::key::exclusive));
+ }
+ }
+ return ret;
+}
+
+wsrep::key_array wsrep_prepare_keys_for_toi(const char* db,
+ const char* table,
+ const TABLE_LIST* table_list,
+ Alter_info* alter_info,
+ wsrep::key_array* fk_tables)
+{
+ wsrep::key_array ret;
+ if (db || table)
+ {
+ ret.push_back(wsrep_prepare_key_for_toi(db, table, wsrep::key::exclusive));
+ }
+ for (const TABLE_LIST* table= table_list; table; table= table->next_global)
+ {
+ ret.push_back(wsrep_prepare_key_for_toi(table->db.str, table->table_name.str,
+ wsrep::key::exclusive));
+ }
+ if (alter_info && (alter_info->flags & ALTER_ADD_FOREIGN_KEY))
+ {
+ wsrep::key_array fk(wsrep_prepare_keys_for_alter_add_fk(table_list->db.str, alter_info));
+ if (!fk.empty())
+ {
+ ret.insert(ret.end(), fk.begin(), fk.end());
+ }
+ }
+ if (fk_tables && !fk_tables->empty())
+ {
+ ret.insert(ret.end(), fk_tables->begin(), fk_tables->end());
+ }
+ return ret;
+}
/*
* Construct Query_log_Event from thd query and serialize it
@@ -1299,7 +1563,7 @@ int wsrep_to_buf_helper(
THD* thd, const char *query, uint query_len, uchar** buf, size_t* buf_len)
{
IO_CACHE tmp_io_cache;
- Log_event_writer writer(&tmp_io_cache,0);
+ Log_event_writer writer(&tmp_io_cache, 0);
if (open_cached_file(&tmp_io_cache, mysql_tmpdir, TEMP_PREFIX,
65536, MYF(MY_WME)))
return 1;
@@ -1385,9 +1649,9 @@ static int
create_view_query(THD *thd, uchar** buf, size_t* buf_len)
{
LEX *lex= thd->lex;
- SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
TABLE_LIST *first_table= select_lex->table_list.first;
- TABLE_LIST *views = first_table;
+ TABLE_LIST *views= first_table;
LEX_USER *definer;
String buff;
const LEX_CSTRING command[3]=
@@ -1412,16 +1676,16 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len)
if (definer)
{
- views->definer.user = definer->user;
- views->definer.host = definer->host;
+ views->definer.user= definer->user;
+ views->definer.host= definer->host;
} else {
WSREP_ERROR("Failed to get DEFINER for VIEW.");
return 1;
}
- views->algorithm = lex->create_view->algorithm;
- views->view_suid = lex->create_view->suid;
- views->with_check = lex->create_view->check;
+ views->algorithm = lex->create_view->algorithm;
+ views->view_suid = lex->create_view->suid;
+ views->with_check = lex->create_view->check;
view_store_options(thd, views, &buff);
buff.append(STRING_WITH_LEN("VIEW "));
@@ -1447,12 +1711,8 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len)
buff.append(')');
}
buff.append(STRING_WITH_LEN(" AS "));
- //buff.append(views->source.str, views->source.length);
buff.append(thd->lex->create_view->select.str,
thd->lex->create_view->select.length);
- //int errcode= query_error_code(thd, TRUE);
- //if (thd->binlog_query(THD::STMT_QUERY_TYPE,
- // buff.ptr(), buff.length(), FALSE, FALSE, FALSE, errcod
return wsrep_to_buf_helper(thd, buff.ptr(), buff.length(), buf, buf_len);
}
@@ -1467,7 +1727,7 @@ static int wsrep_drop_table_query(THD* thd, uchar** buf, size_t* buf_len)
{
LEX* lex= thd->lex;
- SELECT_LEX* select_lex= &lex->select_lex;
+ SELECT_LEX* select_lex= lex->first_select_lex();
TABLE_LIST* first_table= select_lex->table_list.first;
String buff;
@@ -1518,8 +1778,7 @@ static int wsrep_drop_table_query(THD* thd, uchar** buf, size_t* buf_len)
/* Forward declarations. */
-static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len);
-static int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len);
+int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len);
/*
Decide if statement should run in TOI.
@@ -1538,7 +1797,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
DBUG_ASSERT(table_list || db);
LEX* lex= thd->lex;
- SELECT_LEX* select_lex= &lex->select_lex;
+ SELECT_LEX* select_lex= lex->first_select_lex();
TABLE_LIST* first_table= select_lex->table_list.first;
switch (lex->sql_command)
@@ -1639,51 +1898,63 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
}
}
-/*
- returns:
- 0: statement was replicated as TOI
- 1: TOI replication was skipped
- -1: TOI replication failed
- */
-static int wsrep_TOI_begin(THD *thd, const char *db_, const char *table_,
- const TABLE_LIST* table_list,
- Alter_info* alter_info)
+
+static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len)
{
- wsrep_status_t ret(WSREP_WARNING);
- uchar* buf(0);
- size_t buf_len(0);
- int buf_err;
- int rc= 0;
+ String log_query;
+ sp_head *sp= thd->lex->sphead;
+ sql_mode_t saved_mode= thd->variables.sql_mode;
+ String retstr(64);
+ LEX_CSTRING returns= empty_clex_str;
+ retstr.set_charset(system_charset_info);
- if (wsrep_can_run_in_toi(thd, db_, table_, table_list) == false)
+ log_query.set_charset(system_charset_info);
+
+ if (sp->m_handler->type() == TYPE_ENUM_FUNCTION)
{
- WSREP_DEBUG("No TOI for %s", WSREP_QUERY(thd));
+ sp_returns_type(thd, retstr, sp);
+ returns= retstr.lex_cstring();
+ }
+ if (sp->m_handler->
+ show_create_sp(thd, &log_query,
+ sp->m_explicit_name ? sp->m_db : null_clex_str,
+ sp->m_name, sp->m_params, returns,
+ sp->m_body, sp->chistics(),
+ thd->lex->definer[0],
+ thd->lex->create_info,
+ saved_mode))
+ {
+ WSREP_WARN("SP create string failed: schema: %s, query: %s",
+ thd->get_db(), thd->query());
return 1;
}
- WSREP_DEBUG("TO BEGIN: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
- thd->wsrep_exec_mode, wsrep_thd_query(thd));
+ return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len);
+}
+static int wsrep_TOI_event_buf(THD* thd, uchar** buf, size_t* buf_len)
+{
+ int err;
switch (thd->lex->sql_command)
{
case SQLCOM_CREATE_VIEW:
- buf_err= create_view_query(thd, &buf, &buf_len);
+ err= create_view_query(thd, buf, buf_len);
break;
case SQLCOM_CREATE_PROCEDURE:
case SQLCOM_CREATE_SPFUNCTION:
- buf_err= wsrep_create_sp(thd, &buf, &buf_len);
+ err= wsrep_create_sp(thd, buf, buf_len);
break;
case SQLCOM_CREATE_TRIGGER:
- buf_err= wsrep_create_trigger_query(thd, &buf, &buf_len);
+ err= wsrep_create_trigger_query(thd, buf, buf_len);
break;
case SQLCOM_CREATE_EVENT:
- buf_err= wsrep_create_event_query(thd, &buf, &buf_len);
+ err= wsrep_create_event_query(thd, buf, buf_len);
break;
case SQLCOM_ALTER_EVENT:
- buf_err= wsrep_alter_event_query(thd, &buf, &buf_len);
+ err= wsrep_alter_event_query(thd, buf, buf_len);
break;
case SQLCOM_DROP_TABLE:
- buf_err= wsrep_drop_table_query(thd, &buf, &buf_len);
+ err= wsrep_drop_table_query(thd, buf, buf_len);
break;
case SQLCOM_CREATE_ROLE:
if (sp_process_definer(thd))
@@ -1692,169 +1963,197 @@ static int wsrep_TOI_begin(THD *thd, const char *db_, const char *table_,
}
/* fallthrough */
default:
- buf_err= wsrep_to_buf_helper(thd, thd->query(), thd->query_length(),
- &buf, &buf_len);
+ err= wsrep_to_buf_helper(thd, thd->query(), thd->query_length(), buf,
+ buf_len);
break;
}
- wsrep_key_arr_t key_arr= {0, 0};
- struct wsrep_buf buff = { buf, buf_len };
- if (!buf_err &&
- !wsrep_prepare_keys_for_isolation(thd, db_, table_,
- table_list, alter_info, &key_arr) &&
- key_arr.keys_len > 0 &&
- WSREP_OK == (ret = wsrep->to_execute_start(wsrep, thd->thread_id,
- key_arr.keys, key_arr.keys_len,
- &buff, 1,
- &thd->wsrep_trx_meta)))
- {
- thd->wsrep_exec_mode= TOTAL_ORDER;
- wsrep_to_isolation++;
- wsrep_keys_free(&key_arr);
- WSREP_DEBUG("TO BEGIN: %lld, %d",(long long)wsrep_thd_trx_seqno(thd),
- thd->wsrep_exec_mode);
- }
- else if (key_arr.keys_len > 0) {
+ return err;
+}
+
+static void wsrep_TOI_begin_failed(THD* thd, const wsrep_buf_t* /* const err */)
+{
+ if (wsrep_thd_trx_seqno(thd) > 0)
+ {
+ /* GTID was granted and TO acquired - need to log event and release TO */
+ if (wsrep_emulate_bin_log) wsrep_thd_binlog_trx_reset(thd);
+ if (wsrep_write_dummy_event(thd, "TOI begin failed")) { goto fail; }
+ wsrep::client_state& cs(thd->wsrep_cs());
+ int const ret= cs.leave_toi_local(wsrep::mutable_buffer());
+ if (ret)
+ {
+ WSREP_ERROR("Leaving critical section for failed TOI failed: thd: %lld, "
+ "schema: %s, SQL: %s, rcode: %d wsrep_error: %s",
+ (long long)thd->real_id, thd->db.str,
+ thd->query(), ret, wsrep::to_c_string(cs.current_error()));
+ goto fail;
+ }
+ }
+ return;
+fail:
+ WSREP_ERROR("Failed to release TOI resources. Need to abort.");
+ unireg_abort(1);
+}
+
+
+/*
+ returns:
+ 0: statement was replicated as TOI
+ 1: TOI replication was skipped
+ -1: TOI replication failed
+ */
+static int wsrep_TOI_begin(THD *thd, const char *db, const char *table,
+ const TABLE_LIST* table_list,
+ Alter_info* alter_info, wsrep::key_array* fk_tables)
+{
+ DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_TOI);
+
+ WSREP_DEBUG("TOI Begin for %s", WSREP_QUERY(thd));
+ if (wsrep_can_run_in_toi(thd, db, table, table_list) == false)
+ {
+ WSREP_DEBUG("No TOI for %s", WSREP_QUERY(thd));
+ return 1;
+ }
+
+ uchar* buf= 0;
+ size_t buf_len(0);
+ int buf_err;
+ int rc;
+
+ buf_err= wsrep_TOI_event_buf(thd, &buf, &buf_len);
+ if (buf_err) {
+ WSREP_ERROR("Failed to create TOI event buf: %d", buf_err);
+ my_message(ER_UNKNOWN_ERROR,
+ "WSREP replication failed to prepare TOI event buffer. "
+ "Check your query.",
+ MYF(0));
+ return -1;
+ }
+ struct wsrep_buf buff= { buf, buf_len };
+
+ wsrep::key_array key_array=
+ wsrep_prepare_keys_for_toi(db, table, table_list, alter_info, fk_tables);
+
+ if (thd->has_read_only_protection())
+ {
+ /* non replicated DDL, affecting temporary tables only */
+ WSREP_DEBUG("TO isolation skipped, sql: %s."
+ "Only temporary tables affected.",
+ WSREP_QUERY(thd));
+ if (buf) my_free(buf);
+ return -1;
+ }
+
+ thd_proc_info(thd, "acquiring total order isolation");
+
+ wsrep::client_state& cs(thd->wsrep_cs());
+ int ret= cs.enter_toi_local(key_array,
+ wsrep::const_buffer(buff.ptr, buff.len));
+
+ if (ret)
+ {
+ DBUG_ASSERT(cs.current_error());
+ WSREP_DEBUG("to_execute_start() failed for %llu: %s, seqno: %lld",
+ thd->thread_id, WSREP_QUERY(thd),
+ (long long)wsrep_thd_trx_seqno(thd));
+
/* jump to error handler in mysql_execute_command() */
- WSREP_WARN("TO isolation failed for: %d, schema: %s, sql: %s. Check wsrep "
- "connection state and retry the query.",
- ret,
- thd->get_db(),
- (thd->query()) ? thd->query() : "void");
- my_message(ER_LOCK_DEADLOCK, "WSREP replication failed. Check "
- "your wsrep connection state and retry the query.", MYF(0));
- wsrep_keys_free(&key_arr);
+ switch (cs.current_error())
+ {
+ case wsrep::e_size_exceeded_error:
+ WSREP_WARN("TO isolation failed for: %d, schema: %s, sql: %s. "
+ "Maximum size exceeded.",
+ ret,
+ (thd->db.str ? thd->db.str : "(null)"),
+ WSREP_QUERY(thd));
+ my_error(ER_ERROR_DURING_COMMIT, MYF(0), WSREP_SIZE_EXCEEDED);
+ break;
+ default:
+ WSREP_WARN("TO isolation failed for: %d, schema: %s, sql: %s. "
+ "Check wsrep connection state and retry the query.",
+ ret,
+ (thd->db.str ? thd->db.str : "(null)"),
+ WSREP_QUERY(thd));
+ if (!thd->is_error())
+ {
+ my_error(ER_LOCK_DEADLOCK, MYF(0), "WSREP replication failed. Check "
+ "your wsrep connection state and retry the query.");
+ }
+ }
rc= -1;
}
else {
- /* non replicated DDL, affecting temporary tables only */
- WSREP_DEBUG("TO isolation skipped for: %d, sql: %s."
- "Only temporary tables affected.",
- ret, (thd->query()) ? thd->query() : "void");
- rc= 1;
+ ++wsrep_to_isolation;
+ rc= 0;
}
+
if (buf) my_free(buf);
+
+ if (rc) wsrep_TOI_begin_failed(thd, NULL);
+
return rc;
}
static void wsrep_TOI_end(THD *thd) {
- wsrep_status_t ret;
wsrep_to_isolation--;
+ wsrep::client_state& client_state(thd->wsrep_cs());
+ DBUG_ASSERT(wsrep_thd_is_local_toi(thd));
- WSREP_DEBUG("TO END: %lld, %d: %s", (long long)wsrep_thd_trx_seqno(thd),
- thd->wsrep_exec_mode, wsrep_thd_query(thd));
+ wsrep_set_SE_checkpoint(client_state.toi_meta().gtid());
- wsrep_set_SE_checkpoint(thd->wsrep_trx_meta.gtid.uuid,
- thd->wsrep_trx_meta.gtid.seqno);
- WSREP_DEBUG("TO END: %lld, update seqno",
- (long long)wsrep_thd_trx_seqno(thd));
+ int ret= client_state.leave_toi_local(wsrep::mutable_buffer());
- if (WSREP_OK == (ret = wsrep->to_execute_end(wsrep, thd->thread_id))) {
- WSREP_DEBUG("TO END: %lld", (long long)wsrep_thd_trx_seqno(thd));
+ if (!ret)
+ {
+ WSREP_DEBUG("TO END: %lld: %s",
+ client_state.toi_meta().seqno().get(), WSREP_QUERY(thd));
}
- else {
- WSREP_WARN("TO isolation end failed for: %d, schema: %s, sql: %s",
- ret,
- thd->get_db(),
- (thd->query()) ? thd->query() : "void");
+ else
+ {
+ WSREP_WARN("TO isolation end failed for: %d, sql: %s",
+ ret, WSREP_QUERY(thd));
}
}
static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_)
{
- wsrep_status_t ret(WSREP_WARNING);
- WSREP_DEBUG("RSU BEGIN: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
- thd->wsrep_exec_mode, thd->query() );
-
- ret = wsrep->desync(wsrep);
- if (ret != WSREP_OK)
+ WSREP_DEBUG("RSU BEGIN: %lld, : %s", wsrep_thd_trx_seqno(thd),
+ WSREP_QUERY(thd));
+ if (thd->wsrep_cs().begin_rsu(5000))
{
- WSREP_WARN("RSU desync failed %d for schema: %s, query: %s",
- ret, thd->get_db(), thd->query());
- my_error(ER_LOCK_DEADLOCK, MYF(0));
- return(ret);
+ WSREP_WARN("RSU begin failed");
}
-
- mysql_mutex_lock(&LOCK_wsrep_replaying);
- wsrep_replaying++;
- mysql_mutex_unlock(&LOCK_wsrep_replaying);
-
- if (wsrep_wait_committing_connections_close(5000))
- {
- /* no can do, bail out from DDL */
- WSREP_WARN("RSU failed due to pending transactions, schema: %s, query %s",
- thd->get_db(), thd->query());
- mysql_mutex_lock(&LOCK_wsrep_replaying);
- wsrep_replaying--;
- mysql_mutex_unlock(&LOCK_wsrep_replaying);
-
- ret = wsrep->resync(wsrep);
- if (ret != WSREP_OK)
- {
- WSREP_WARN("resync failed %d for schema: %s, query: %s",
- ret, thd->get_db(), thd->query());
- }
-
- my_error(ER_LOCK_DEADLOCK, MYF(0));
- return(-1);
- }
-
- wsrep_seqno_t seqno = wsrep->pause(wsrep);
- if (seqno == WSREP_SEQNO_UNDEFINED)
+ else
{
- WSREP_WARN("pause failed %lld for schema: %s, query: %s", (long long)seqno,
- thd->get_db(), thd->query());
- return(1);
+ thd->variables.wsrep_on= 0;
}
- WSREP_DEBUG("paused at %lld", (long long)seqno);
- thd->variables.wsrep_on = 0;
return 0;
}
static void wsrep_RSU_end(THD *thd)
{
- wsrep_status_t ret(WSREP_WARNING);
- WSREP_DEBUG("RSU END: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
- thd->wsrep_exec_mode, thd->query() );
-
-
- mysql_mutex_lock(&LOCK_wsrep_replaying);
- wsrep_replaying--;
- mysql_mutex_unlock(&LOCK_wsrep_replaying);
-
- ret = wsrep->resume(wsrep);
- if (ret != WSREP_OK)
+ WSREP_DEBUG("RSU END: %lld : %s", wsrep_thd_trx_seqno(thd),
+ WSREP_QUERY(thd));
+ if (thd->wsrep_cs().end_rsu())
{
- WSREP_WARN("resume failed %d for schema: %s, query: %s", ret,
- thd->get_db(), thd->query());
+ WSREP_WARN("Failed to end RSU, server may need to be restarted");
}
-
- ret = wsrep->resync(wsrep);
- if (ret != WSREP_OK)
- {
- WSREP_WARN("resync failed %d for schema: %s, query: %s", ret,
- thd->get_db(), thd->query());
- return;
- }
-
- thd->variables.wsrep_on = 1;
+ thd->variables.wsrep_on= 1;
}
int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_,
const TABLE_LIST* table_list,
- Alter_info* alter_info)
+ Alter_info* alter_info, wsrep::key_array* fk_tables)
{
- int ret= 0;
-
/*
No isolation for applier or replaying threads.
*/
- if (thd->wsrep_exec_mode == REPL_RECV)
- return 0;
+ if (!wsrep_thd_is_local(thd)) return 0;
+ int ret= 0;
mysql_mutex_lock(&thd->LOCK_thd_data);
- if (thd->wsrep_conflict_state == MUST_ABORT)
+ if (thd->wsrep_trx().state() == wsrep::transaction::s_must_abort)
{
WSREP_INFO("thread: %lld schema: %s query: %s has been aborted due to multi-master conflict",
(longlong) thd->thread_id, thd->get_db(), thd->query());
@@ -1863,20 +2162,20 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_,
}
mysql_mutex_unlock(&thd->LOCK_thd_data);
- DBUG_ASSERT(thd->wsrep_exec_mode == LOCAL_STATE);
- DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno == WSREP_SEQNO_UNDEFINED);
+ DBUG_ASSERT(wsrep_thd_is_local(thd));
+ DBUG_ASSERT(thd->wsrep_trx().ws_meta().seqno().is_undefined());
- if (thd->global_read_lock.can_acquire_protection())
+ if (Wsrep_server_state::instance().desynced_on_pause())
{
- WSREP_DEBUG("Aborting TOI: Global Read-Lock (FTWRL) in place: %s %lld",
- thd->query(), (longlong) thd->thread_id);
+ my_message(ER_UNKNOWN_COM_ERROR,
+ "Aborting TOI: Global Read-Lock (FTWRL) in place.", MYF(0));
return -1;
}
if (wsrep_debug && thd->mdl_context.has_locks())
{
- WSREP_DEBUG("thread holds MDL locks at TI begin: %s %lld",
- thd->query(), (longlong) thd->thread_id);
+ WSREP_DEBUG("thread holds MDL locks at TI begin: %s %llu",
+ WSREP_QUERY(thd), thd->thread_id);
}
/*
@@ -1888,15 +2187,22 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_,
*/
if (wsrep_auto_increment_control)
{
- thd->variables.auto_increment_offset = 1;
- thd->variables.auto_increment_increment = 1;
+ thd->variables.auto_increment_offset= 1;
+ thd->variables.auto_increment_increment= 1;
}
- if (thd->variables.wsrep_on && thd->wsrep_exec_mode==LOCAL_STATE)
+ /*
+ TOI operations will ignore provided lock_wait_timeout and restore it
+ after operation is done.
+ */
+ thd->variables.saved_lock_wait_timeout= thd->variables.lock_wait_timeout;
+ thd->variables.lock_wait_timeout= LONG_TIMEOUT;
+
+ if (thd->variables.wsrep_on && wsrep_thd_is_local(thd))
{
switch (thd->variables.wsrep_OSU_method) {
case WSREP_OSU_TOI:
- ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info);
+ ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info, fk_tables);
break;
case WSREP_OSU_RSU:
ret= wsrep_RSU_begin(thd, db_, table_);
@@ -1908,48 +2214,56 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_,
break;
}
switch (ret) {
- case 0: thd->wsrep_exec_mode= TOTAL_ORDER; break;
+ case 0: /* wsrep_TOI_begin sould set toi mode */ break;
case 1:
/* TOI replication skipped, treat as success */
- ret = 0;
+ ret= 0;
break;
case -1:
/* TOI replication failed, treat as error */
break;
}
}
+
return ret;
}
void wsrep_to_isolation_end(THD *thd)
{
- if (thd->wsrep_exec_mode == TOTAL_ORDER)
+ DBUG_ASSERT(wsrep_thd_is_local_toi(thd) ||
+ wsrep_thd_is_in_rsu(thd));
+
+ thd->variables.lock_wait_timeout= thd->variables.saved_lock_wait_timeout;
+
+ if (wsrep_thd_is_local_toi(thd))
{
- switch(thd->variables.wsrep_OSU_method)
- {
- case WSREP_OSU_TOI: wsrep_TOI_end(thd); break;
- case WSREP_OSU_RSU: wsrep_RSU_end(thd); break;
- default:
- WSREP_WARN("Unsupported wsrep OSU method at isolation end: %lu",
- thd->variables.wsrep_OSU_method);
- break;
- }
- wsrep_cleanup_transaction(thd);
+ DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_TOI);
+ wsrep_TOI_end(thd);
+ }
+ else if (wsrep_thd_is_in_rsu(thd))
+ {
+ DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_RSU);
+ wsrep_RSU_end(thd);
}
+ else
+ {
+ DBUG_ASSERT(0);
+ }
+ if (wsrep_emulate_bin_log) wsrep_thd_binlog_trx_reset(thd);
}
#define WSREP_MDL_LOG(severity, msg, schema, schema_len, req, gra) \
WSREP_##severity( \
"%s\n" \
"schema: %.*s\n" \
- "request: (%lld \tseqno %lld \twsrep (%d, %d, %d) cmd %d %d \t%s)\n" \
- "granted: (%lld \tseqno %lld \twsrep (%d, %d, %d) cmd %d %d \t%s)", \
+ "request: (%llu \tseqno %lld \twsrep (%s, %s, %s) cmd %d %d \t%s)\n" \
+ "granted: (%llu \tseqno %lld \twsrep (%s, %s, %s) cmd %d %d \t%s)", \
msg, schema_len, schema, \
- (longlong) req->thread_id, (long long)wsrep_thd_trx_seqno(req), \
- req->wsrep_exec_mode, req->wsrep_query_state, req->wsrep_conflict_state, \
+ req->thread_id, (long long)wsrep_thd_trx_seqno(req), \
+ wsrep_thd_client_mode_str(req), wsrep_thd_client_state_str(req), wsrep_thd_transaction_state_str(req), \
req->get_command(), req->lex->sql_command, req->query(), \
- (longlong) gra->thread_id, (long long)wsrep_thd_trx_seqno(gra), \
- gra->wsrep_exec_mode, gra->wsrep_query_state, gra->wsrep_conflict_state, \
+ gra->thread_id, (long long)wsrep_thd_trx_seqno(gra), \
+ wsrep_thd_client_mode_str(gra), wsrep_thd_client_state_str(gra), wsrep_thd_transaction_state_str(gra), \
gra->get_command(), gra->lex->sql_command, gra->query());
/**
@@ -1962,58 +2276,53 @@ void wsrep_to_isolation_end(THD *thd)
@retval FALSE Lock request cannot be granted
*/
-bool wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
+void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
MDL_ticket *ticket,
const MDL_key *key)
{
/* Fallback to the non-wsrep behaviour */
- if (!WSREP_ON) return FALSE;
+ if (!WSREP_ON) return;
THD *request_thd= requestor_ctx->get_thd();
THD *granted_thd= ticket->get_ctx()->get_thd();
- bool ret= false;
const char* schema= key->db_name();
int schema_len= key->db_name_length();
mysql_mutex_lock(&request_thd->LOCK_thd_data);
+ if (wsrep_thd_is_toi(request_thd) ||
+ wsrep_thd_is_applying(request_thd)) {
- /*
- We consider granting MDL exceptions only for appliers (BF THD) and ones
- executing under TOI mode.
-
- Rules:
- 1. If granted/owner THD is also an applier (BF THD) or one executing
- under TOI mode, then we grant the requested lock to the requester
- THD.
- @return true
-
- 2. If granted/owner THD is executing a FLUSH command or already has an
- explicit lock, then do not grant the requested lock to the requester
- THD and it has to wait.
- @return false
-
- 3. In all other cases the granted/owner THD is aborted and the requested
- lock is not granted to the requester THD, thus it has to wait.
- @return false
- */
- if (request_thd->wsrep_exec_mode == TOTAL_ORDER ||
- request_thd->wsrep_exec_mode == REPL_RECV)
- {
mysql_mutex_unlock(&request_thd->LOCK_thd_data);
WSREP_MDL_LOG(DEBUG, "MDL conflict ", schema, schema_len,
request_thd, granted_thd);
ticket->wsrep_report(wsrep_debug);
mysql_mutex_lock(&granted_thd->LOCK_thd_data);
- if (granted_thd->wsrep_exec_mode == TOTAL_ORDER ||
- granted_thd->wsrep_exec_mode == REPL_RECV)
+ if (wsrep_thd_is_toi(granted_thd) ||
+ wsrep_thd_is_applying(granted_thd))
{
- WSREP_MDL_LOG(INFO, "MDL BF-BF conflict", schema, schema_len,
- request_thd, granted_thd);
- ticket->wsrep_report(true);
- mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
- ret= true;
+ if (wsrep_thd_is_aborting(granted_thd))
+ {
+ WSREP_DEBUG("BF thread waiting for SR in aborting state");
+ ticket->wsrep_report(wsrep_debug);
+ mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
+ }
+ else if (wsrep_thd_is_SR(granted_thd) && !wsrep_thd_is_SR(request_thd))
+ {
+ WSREP_MDL_LOG(INFO, "MDL conflict, DDL vs SR",
+ schema, schema_len, request_thd, granted_thd);
+ mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
+ wsrep_abort_thd(request_thd, granted_thd, 1);
+ }
+ else
+ {
+ WSREP_MDL_LOG(INFO, "MDL BF-BF conflict", schema, schema_len,
+ request_thd, granted_thd);
+ ticket->wsrep_report(true);
+ mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
+ unireg_abort(1);
+ }
}
else if (granted_thd->lex->sql_command == SQLCOM_FLUSH ||
granted_thd->mdl_context.has_explicit_locks())
@@ -2021,207 +2330,57 @@ bool wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
WSREP_DEBUG("BF thread waiting for FLUSH");
ticket->wsrep_report(wsrep_debug);
mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
- ret= false;
+ }
+ else if (request_thd->lex->sql_command == SQLCOM_DROP_TABLE)
+ {
+ WSREP_DEBUG("DROP caused BF abort, conf %s",
+ wsrep_thd_transaction_state_str(granted_thd));
+ ticket->wsrep_report(wsrep_debug);
+ mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
+ wsrep_abort_thd(request_thd, granted_thd, 1);
}
else
{
- /* Print some debug information. */
- if (wsrep_debug)
+ WSREP_MDL_LOG(DEBUG, "MDL conflict-> BF abort", schema, schema_len,
+ request_thd, granted_thd);
+ ticket->wsrep_report(wsrep_debug);
+ if (granted_thd->wsrep_trx().active())
{
- if (request_thd->lex->sql_command == SQLCOM_DROP_TABLE ||
- request_thd->lex->sql_command == SQLCOM_DROP_SEQUENCE)
- {
- WSREP_DEBUG("DROP caused BF abort, conf %d", granted_thd->wsrep_conflict_state);
- }
- else if (granted_thd->wsrep_query_state == QUERY_COMMITTING)
+ mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
+ wsrep_abort_thd(request_thd, granted_thd, 1);
+ }
+ else
+ {
+ /*
+ Granted_thd is likely executing with wsrep_on=0. If the requesting
+ thd is BF, BF abort and wait.
+ */
+ mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
+ if (wsrep_thd_is_BF(request_thd, FALSE))
{
- WSREP_DEBUG("MDL granted, but committing thd abort scheduled");
+ ha_abort_transaction(request_thd, granted_thd, TRUE);
}
else
{
- WSREP_MDL_LOG(DEBUG, "MDL conflict-> BF abort", schema, schema_len,
- request_thd, granted_thd);
+ WSREP_MDL_LOG(INFO, "MDL unknown BF-BF conflict", schema, schema_len,
+ request_thd, granted_thd);
+ ticket->wsrep_report(true);
+ unireg_abort(1);
}
- ticket->wsrep_report(true);
}
-
- mysql_mutex_unlock(&granted_thd->LOCK_thd_data);
- wsrep_abort_thd((void *) request_thd, (void *) granted_thd, 1);
- ret= false;
}
}
else
{
mysql_mutex_unlock(&request_thd->LOCK_thd_data);
}
-
- return ret;
}
-
-pthread_handler_t start_wsrep_THD(void *arg)
-{
- THD *thd;
- wsrep_thread_args* args= (wsrep_thread_args*)arg;
- wsrep_thd_processor_fun processor= args->processor;
-
- if (my_thread_init() || (!(thd= new THD(next_thread_id(), true))))
- {
- goto error;
- }
-
- mysql_mutex_lock(&LOCK_thread_count);
-
- if (wsrep_gtid_mode)
- {
- /* Adjust domain_id. */
- thd->variables.gtid_domain_id= wsrep_gtid_domain_id;
- }
-
- thd->real_id=pthread_self(); // Keep purify happy
- thread_created++;
- threads.append(thd);
-
- my_net_init(&thd->net,(st_vio*) 0, thd, MYF(0));
-
- DBUG_PRINT("wsrep",(("creating thread %lld"), (long long)thd->thread_id));
- thd->prior_thr_create_utime= thd->start_utime= microsecond_interval_timer();
- (void) mysql_mutex_unlock(&LOCK_thread_count);
-
- /* from bootstrap()... */
- thd->bootstrap=1;
- thd->max_client_packet_length= thd->net.max_packet;
- thd->security_ctx->master_access= ~(ulong)0;
-
- /* from handle_one_connection... */
- pthread_detach_this_thread();
-
- mysql_thread_set_psi_id(thd->thread_id);
- thd->thr_create_utime= microsecond_interval_timer();
- if (MYSQL_CALLBACK_ELSE(thread_scheduler, init_new_connection_thread, (), 0))
- {
- close_connection(thd, ER_OUT_OF_RESOURCES);
- statistic_increment(aborted_connects,&LOCK_status);
- MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
- goto error;
- }
-
-// </5.1.17>
- /*
- handle_one_connection() is normally the only way a thread would
- start and would always be on the very high end of the stack ,
- therefore, the thread stack always starts at the address of the
- first local variable of handle_one_connection, which is thd. We
- need to know the start of the stack so that we could check for
- stack overruns.
- */
- DBUG_PRINT("wsrep", ("handle_one_connection called by thread %lld",
- (long long)thd->thread_id));
- /* now that we've called my_thread_init(), it is safe to call DBUG_* */
-
- thd->thread_stack= (char*) &thd;
- if (thd->store_globals())
- {
- close_connection(thd, ER_OUT_OF_RESOURCES);
- statistic_increment(aborted_connects,&LOCK_status);
- MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
- goto error;
- }
-
- thd->system_thread= SYSTEM_THREAD_SLAVE_SQL;
- thd->security_ctx->skip_grants();
-
- /* handle_one_connection() again... */
- //thd->version= refresh_version;
- thd->proc_info= 0;
- thd->set_command(COM_SLEEP);
- thd->init_for_queries();
-
- mysql_mutex_lock(&LOCK_thread_count);
- wsrep_running_threads++;
-
- switch (args->thread_type) {
- case WSREP_APPLIER_THREAD:
- wsrep_running_applier_threads++;
- break;
- case WSREP_ROLLBACKER_THREAD:
- wsrep_running_rollbacker_threads++;
- break;
- default:
- WSREP_ERROR("Incorrect wsrep thread type: %d", args->thread_type);
- break;
- }
-
- mysql_cond_broadcast(&COND_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
-
- processor(thd);
-
- close_connection(thd, 0);
-
- mysql_mutex_lock(&LOCK_thread_count);
- DBUG_ASSERT(wsrep_running_threads > 0);
- wsrep_running_threads--;
-
- switch (args->thread_type) {
- case WSREP_APPLIER_THREAD:
- DBUG_ASSERT(wsrep_running_applier_threads > 0);
- wsrep_running_applier_threads--;
- break;
- case WSREP_ROLLBACKER_THREAD:
- DBUG_ASSERT(wsrep_running_rollbacker_threads > 0);
- wsrep_running_rollbacker_threads--;
- break;
- default:
- WSREP_ERROR("Incorrect wsrep thread type: %d", args->thread_type);
- break;
- }
-
- my_free(args);
-
- WSREP_DEBUG("wsrep running threads now: %lu", wsrep_running_threads);
- mysql_cond_broadcast(&COND_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
-
- // Note: We can't call THD destructor without crashing
- // if plugins have not been initialized. However, in most of the
- // cases this means that pre SE initialization SST failed and
- // we are going to exit anyway.
- if (plugins_are_initialized)
- {
- net_end(&thd->net);
- MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 1));
- }
- else
- {
- // TODO: lightweight cleanup to get rid of:
- // 'Error in my_thread_global_end(): 2 threads didn't exit'
- // at server shutdown
- }
-
- unlink_not_visible_thd(thd);
- delete thd;
- my_thread_end();
- return(NULL);
-
-error:
- WSREP_ERROR("Failed to create/initialize system thread");
-
- my_free(args);
-
- /* Abort if its the first applier/rollbacker thread. */
- if (!mysqld_server_initialized)
- unireg_abort(1);
- else
- return NULL;
-}
-
-
/**/
static bool abort_replicated(THD *thd)
{
bool ret_code= false;
- if (thd->wsrep_query_state== QUERY_COMMITTING)
+ if (thd->wsrep_trx().state() == wsrep::transaction::s_committing)
{
WSREP_DEBUG("aborting replicated trx: %llu", (ulonglong)(thd->real_id));
@@ -2231,198 +2390,138 @@ static bool abort_replicated(THD *thd)
return ret_code;
}
-
/**/
static inline bool is_client_connection(THD *thd)
{
return (thd->wsrep_client_thread && thd->variables.wsrep_on);
}
-
static inline bool is_replaying_connection(THD *thd)
{
bool ret;
mysql_mutex_lock(&thd->LOCK_thd_data);
- ret= (thd->wsrep_conflict_state == REPLAYING) ? true : false;
+ ret= (thd->wsrep_trx().state() == wsrep::transaction::s_replaying) ? true : false;
mysql_mutex_unlock(&thd->LOCK_thd_data);
return ret;
}
-
static inline bool is_committing_connection(THD *thd)
{
bool ret;
mysql_mutex_lock(&thd->LOCK_thd_data);
- ret= (thd->wsrep_query_state == QUERY_COMMITTING) ? true : false;
+ ret= (thd->wsrep_trx().state() == wsrep::transaction::s_committing) ? true : false;
mysql_mutex_unlock(&thd->LOCK_thd_data);
return ret;
}
-
-static bool have_client_connections()
+static my_bool have_client_connections(THD *thd, void*)
{
- THD *tmp;
-
- I_List_iterator<THD> it(threads);
- while ((tmp=it++))
+ DBUG_PRINT("quit",("Informing thread %lld that it's time to die",
+ (longlong) thd->thread_id));
+ if (is_client_connection(thd) && thd->killed == KILL_CONNECTION)
{
- DBUG_PRINT("quit",("Informing thread %lld that it's time to die",
- (longlong) tmp->thread_id));
- if (is_client_connection(tmp) && tmp->killed == KILL_CONNECTION)
- {
- (void)abort_replicated(tmp);
- return true;
- }
+ (void)abort_replicated(thd);
+ return 1;
}
- return false;
+ return 0;
}
static void wsrep_close_thread(THD *thd)
{
thd->set_killed(KILL_CONNECTION);
MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (thd));
- if (thd->mysys_var)
- {
- thd->mysys_var->abort=1;
- mysql_mutex_lock(&thd->mysys_var->mutex);
- if (thd->mysys_var->current_cond)
- {
- mysql_mutex_lock(thd->mysys_var->current_mutex);
- mysql_cond_broadcast(thd->mysys_var->current_cond);
- mysql_mutex_unlock(thd->mysys_var->current_mutex);
- }
- mysql_mutex_unlock(&thd->mysys_var->mutex);
- }
+ mysql_mutex_lock(&thd->LOCK_thd_kill);
+ thd->abort_current_cond_wait(true);
+ mysql_mutex_unlock(&thd->LOCK_thd_kill);
}
-
-static my_bool have_committing_connections()
+static my_bool have_committing_connections(THD *thd, void *)
{
- THD *tmp;
- mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
-
- I_List_iterator<THD> it(threads);
- while ((tmp=it++))
- {
- if (!is_client_connection(tmp))
- continue;
-
- if (is_committing_connection(tmp))
- {
- mysql_mutex_unlock(&LOCK_thread_count);
- return TRUE;
- }
- }
- mysql_mutex_unlock(&LOCK_thread_count);
- return FALSE;
+ return is_client_connection(thd) && is_committing_connection(thd) ? 1 : 0;
}
-
int wsrep_wait_committing_connections_close(int wait_time)
{
int sleep_time= 100;
- while (have_committing_connections() && wait_time > 0)
+ WSREP_DEBUG("wait for committing transaction to close: %d sleep: %d", wait_time, sleep_time);
+ while (server_threads.iterate(have_committing_connections) && wait_time > 0)
{
WSREP_DEBUG("wait for committing transaction to close: %d", wait_time);
my_sleep(sleep_time);
wait_time -= sleep_time;
}
- if (have_committing_connections())
- {
- return 1;
- }
- return 0;
+ return server_threads.iterate(have_committing_connections);
}
-
-void wsrep_close_client_connections(my_bool wait_to_end, THD *except_caller_thd)
+static my_bool kill_all_threads(THD *thd, THD *caller_thd)
{
- /*
- First signal all threads that it's time to die
- */
-
- THD *tmp;
- mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
-
- bool kill_cached_threads_saved= kill_cached_threads;
- kill_cached_threads= true; // prevent future threads caching
- mysql_cond_broadcast(&COND_thread_cache); // tell cached threads to die
-
- I_List_iterator<THD> it(threads);
- while ((tmp=it++))
+ DBUG_PRINT("quit", ("Informing thread %lld that it's time to die",
+ (longlong) thd->thread_id));
+ /* We skip slave threads & scheduler on this first loop through. */
+ if (is_client_connection(thd) && thd != caller_thd)
{
- DBUG_PRINT("quit",("Informing thread %lld that it's time to die",
- (longlong) tmp->thread_id));
- /* We skip slave threads & scheduler on this first loop through. */
- if (!is_client_connection(tmp))
- continue;
-
- if (tmp == except_caller_thd)
+ if (is_replaying_connection(thd))
+ thd->set_killed(KILL_CONNECTION);
+ else if (!abort_replicated(thd))
{
- DBUG_ASSERT(is_client_connection(tmp));
- continue;
+ /* replicated transactions must be skipped */
+ WSREP_DEBUG("closing connection %lld", (longlong) thd->thread_id);
+ /* instead of wsrep_close_thread() we do now soft kill by THD::awake */
+ thd->awake(KILL_CONNECTION);
}
+ }
+ return 0;
+}
- if (is_replaying_connection(tmp))
- {
- tmp->set_killed(KILL_CONNECTION);
- continue;
- }
-
- /* replicated transactions must be skipped */
- if (abort_replicated(tmp))
- continue;
-
- WSREP_DEBUG("closing connection %lld", (longlong) tmp->thread_id);
+static my_bool kill_remaining_threads(THD *thd, THD *caller_thd)
+{
+#ifndef __bsdi__ // Bug in BSDI kernel
+ if (is_client_connection(thd) &&
+ !abort_replicated(thd) &&
+ !is_replaying_connection(thd) &&
+ thd_is_connection_alive(thd) &&
+ thd != caller_thd)
+ {
- /*
- instead of wsrep_close_thread() we do now soft kill by THD::awake
- */
- tmp->awake(KILL_CONNECTION);
+ WSREP_INFO("killing local connection: %lld", (longlong) thd->thread_id);
+ close_connection(thd);
}
- mysql_mutex_unlock(&LOCK_thread_count);
+#endif
+ return 0;
+}
- if (thread_count)
- sleep(2); // Give threads time to die
+void wsrep_close_client_connections(my_bool wait_to_end, THD* except_caller_thd)
+{
+ /* Clear thread cache */
+ kill_cached_threads++;
+ flush_thread_cache();
+
+ /*
+ First signal all threads that it's time to die
+ */
+ server_threads.iterate(kill_all_threads, except_caller_thd);
- mysql_mutex_lock(&LOCK_thread_count);
/*
Force remaining threads to die by closing the connection to the client
*/
+ server_threads.iterate(kill_remaining_threads, except_caller_thd);
- I_List_iterator<THD> it2(threads);
- while ((tmp=it2++))
- {
-#ifndef __bsdi__ // Bug in BSDI kernel
- if (is_client_connection(tmp) &&
- !abort_replicated(tmp) &&
- !is_replaying_connection(tmp) &&
- tmp != except_caller_thd)
- {
- WSREP_INFO("killing local connection: %lld", (longlong) tmp->thread_id);
- close_connection(tmp,0);
- }
-#endif
- }
-
- DBUG_PRINT("quit",("Waiting for threads to die (count=%u)",thread_count));
- WSREP_DEBUG("waiting for client connections to close: %u", thread_count);
+ DBUG_PRINT("quit", ("Waiting for threads to die (count=%u)",
+ uint32_t(thread_count)));
+ WSREP_DEBUG("waiting for client connections to close: %u",
+ uint32_t(thread_count));
- while (wait_to_end && have_client_connections())
+ while (wait_to_end && server_threads.iterate(have_client_connections))
{
- mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
- DBUG_PRINT("quit",("One thread died (count=%u)", thread_count));
+ sleep(1);
+ DBUG_PRINT("quit",("One thread died (count=%u)", uint32_t(thread_count)));
}
- kill_cached_threads= kill_cached_threads_saved;
-
- mysql_mutex_unlock(&LOCK_thread_count);
-
/* All client connection threads have now been aborted */
}
@@ -2433,393 +2532,152 @@ void wsrep_close_applier(THD *thd)
wsrep_close_thread(thd);
}
-
-void wsrep_close_threads(THD *thd)
+static my_bool wsrep_close_threads_callback(THD *thd, THD *caller_thd)
{
- THD *tmp;
- mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
-
- I_List_iterator<THD> it(threads);
- while ((tmp=it++))
+ DBUG_PRINT("quit",("Informing thread %lld that it's time to die",
+ (longlong) thd->thread_id));
+ /* We skip slave threads & scheduler on this first loop through. */
+ if (thd->wsrep_applier && thd != caller_thd)
{
- DBUG_PRINT("quit",("Informing thread %lld that it's time to die",
- (longlong) tmp->thread_id));
- /* We skip slave threads & scheduler on this first loop through. */
- if (tmp->wsrep_applier && tmp != thd)
- {
- WSREP_DEBUG("closing wsrep thread %lld", (longlong) tmp->thread_id);
- wsrep_close_thread (tmp);
- }
+ WSREP_DEBUG("closing wsrep thread %lld", (longlong) thd->thread_id);
+ wsrep_close_thread(thd);
}
+ return 0;
+}
- mysql_mutex_unlock(&LOCK_thread_count);
+void wsrep_close_threads(THD *thd)
+{
+ server_threads.iterate(wsrep_close_threads_callback, thd);
}
void wsrep_wait_appliers_close(THD *thd)
{
/* Wait for wsrep appliers to gracefully exit */
- mysql_mutex_lock(&LOCK_thread_count);
- while (wsrep_running_threads > 1)
- // 1 is for rollbacker thread which needs to be killed explicitly.
- // This gotta be fixed in a more elegant manner if we gonna have arbitrary
- // number of non-applier wsrep threads.
+ mysql_mutex_lock(&LOCK_wsrep_slave_threads);
+ while (wsrep_running_threads > 2)
+ /*
+ 2 is for rollbacker thread which needs to be killed explicitly.
+ This gotta be fixed in a more elegant manner if we gonna have arbitrary
+ number of non-applier wsrep threads.
+ */
{
- if (thread_handling > SCHEDULER_ONE_THREAD_PER_CONNECTION)
- {
- mysql_mutex_unlock(&LOCK_thread_count);
- my_sleep(100);
- mysql_mutex_lock(&LOCK_thread_count);
- }
- else
- mysql_cond_wait(&COND_thread_count,&LOCK_thread_count);
- DBUG_PRINT("quit",("One applier died (count=%u)",thread_count));
+ mysql_cond_wait(&COND_wsrep_slave_threads, &LOCK_wsrep_slave_threads);
}
- mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
+ DBUG_PRINT("quit",("applier threads have died (count=%u)",
+ uint32_t(wsrep_running_threads)));
+
/* Now kill remaining wsrep threads: rollbacker */
wsrep_close_threads (thd);
/* and wait for them to die */
- mysql_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_wsrep_slave_threads);
while (wsrep_running_threads > 0)
{
- if (thread_handling > SCHEDULER_ONE_THREAD_PER_CONNECTION)
- {
- mysql_mutex_unlock(&LOCK_thread_count);
- my_sleep(100);
- mysql_mutex_lock(&LOCK_thread_count);
- }
- else
- mysql_cond_wait(&COND_thread_count,&LOCK_thread_count);
- DBUG_PRINT("quit",("One thread died (count=%u)",thread_count));
+ mysql_cond_wait(&COND_wsrep_slave_threads, &LOCK_wsrep_slave_threads);
}
- mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
+ DBUG_PRINT("quit",("all wsrep system threads have died"));
/* All wsrep applier threads have now been aborted. However, if this thread
is also applier, we are still running...
*/
}
-
-void wsrep_kill_mysql(THD *thd)
+void
+wsrep_last_committed_id(wsrep_gtid_t* gtid)
{
- if (mysqld_server_started)
- {
- if (!shutdown_in_progress)
- {
- WSREP_INFO("starting shutdown");
- kill_mysql();
- }
- }
- else
- {
- unireg_abort(1);
- }
+ wsrep::gtid ret= Wsrep_server_state::instance().last_committed_gtid();
+ memcpy(gtid->uuid.data, ret.id().data(), sizeof(gtid->uuid.data));
+ gtid->seqno= ret.seqno().get();
}
-
-static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len)
+void
+wsrep_node_uuid(wsrep_uuid_t& uuid)
{
- String log_query;
- sp_head *sp = thd->lex->sphead;
- sql_mode_t saved_mode= thd->variables.sql_mode;
- String retstr(64);
- LEX_CSTRING returns= empty_clex_str;
- retstr.set_charset(system_charset_info);
-
- log_query.set_charset(system_charset_info);
-
- if (sp->m_handler->type() == TYPE_ENUM_FUNCTION)
- {
- sp_returns_type(thd, retstr, sp);
- returns= retstr.lex_cstring();
- }
- if (sp->m_handler->
- show_create_sp(thd, &log_query,
- sp->m_explicit_name ? sp->m_db : null_clex_str,
- sp->m_name, sp->m_params, returns,
- sp->m_body, sp->chistics(),
- thd->lex->definer[0],
- thd->lex->create_info,
- saved_mode))
- {
- WSREP_WARN("SP create string failed: schema: %s, query: %s",
- thd->get_db(), thd->query());
- return 1;
- }
-
- return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len);
-}
-
-
-extern int wsrep_on(THD *thd)
-{
- return (int)(WSREP(thd));
-}
-
-
-extern "C" bool wsrep_thd_is_wsrep_on(THD *thd)
-{
- return thd->variables.wsrep_on;
-}
-
-
-bool wsrep_consistency_check(THD *thd)
-{
- return thd->wsrep_consistency_check == CONSISTENCY_CHECK_RUNNING;
-}
-
-
-extern "C" void wsrep_thd_set_exec_mode(THD *thd, enum wsrep_exec_mode mode)
-{
- thd->wsrep_exec_mode= mode;
-}
-
-
-extern "C" void wsrep_thd_set_query_state(
- THD *thd, enum wsrep_query_state state)
-{
- /* async slave thread should never flag IDLE state, as it may
- give rollbacker thread chance to interfere and rollback async slave
- transaction.
- in fact, async slave thread is never idle as it reads complete
- transactions from relay log and applies them, as a whole.
- BF abort happens voluntarily by async slave thread.
- */
- if (thd->slave_thread && state == QUERY_IDLE) {
- WSREP_DEBUG("Skipping IDLE state change for slave SQL");
- return;
- }
- thd->wsrep_query_state= state;
-}
-
-
-void wsrep_thd_set_conflict_state(THD *thd, enum wsrep_conflict_state state)
-{
- if (WSREP(thd)) thd->wsrep_conflict_state= state;
-}
-
-
-enum wsrep_exec_mode wsrep_thd_exec_mode(THD *thd)
-{
- return thd->wsrep_exec_mode;
-}
-
-
-const char *wsrep_thd_exec_mode_str(THD *thd)
-{
- return
- (!thd) ? "void" :
- (thd->wsrep_exec_mode == LOCAL_STATE) ? "local" :
- (thd->wsrep_exec_mode == REPL_RECV) ? "applier" :
- (thd->wsrep_exec_mode == TOTAL_ORDER) ? "total order" :
- (thd->wsrep_exec_mode == LOCAL_COMMIT) ? "local commit" : "void";
-}
-
-
-enum wsrep_query_state wsrep_thd_query_state(THD *thd)
-{
- return thd->wsrep_query_state;
-}
-
-
-const char *wsrep_thd_query_state_str(THD *thd)
-{
- return
- (!thd) ? "void" :
- (thd->wsrep_query_state == QUERY_IDLE) ? "idle" :
- (thd->wsrep_query_state == QUERY_EXEC) ? "executing" :
- (thd->wsrep_query_state == QUERY_COMMITTING) ? "committing" :
- (thd->wsrep_query_state == QUERY_EXITING) ? "exiting" :
- (thd->wsrep_query_state == QUERY_ROLLINGBACK) ? "rolling back" : "void";
-}
-
-
-enum wsrep_conflict_state wsrep_thd_get_conflict_state(THD *thd)
-{
- return thd->wsrep_conflict_state;
-}
-
-
-const char *wsrep_thd_conflict_state_str(THD *thd)
-{
- return
- (!thd) ? "void" :
- (thd->wsrep_conflict_state == NO_CONFLICT) ? "no conflict" :
- (thd->wsrep_conflict_state == MUST_ABORT) ? "must abort" :
- (thd->wsrep_conflict_state == ABORTING) ? "aborting" :
- (thd->wsrep_conflict_state == MUST_REPLAY) ? "must replay" :
- (thd->wsrep_conflict_state == REPLAYING) ? "replaying" :
- (thd->wsrep_conflict_state == RETRY_AUTOCOMMIT) ? "retrying" :
- (thd->wsrep_conflict_state == CERT_FAILURE) ? "cert failure" : "void";
-}
-
-
-wsrep_ws_handle_t* wsrep_thd_ws_handle(THD *thd)
-{
- return &thd->wsrep_ws_handle;
-}
-
-
-void wsrep_thd_LOCK(THD *thd)
-{
- mysql_mutex_lock(&thd->LOCK_thd_data);
-}
-
-
-void wsrep_thd_UNLOCK(THD *thd)
-{
- mysql_mutex_unlock(&thd->LOCK_thd_data);
+ uuid= node_uuid;
}
-
-void wsrep_thd_kill_LOCK(THD *thd)
-{
- mysql_mutex_lock(&thd->LOCK_thd_kill);
-}
-
-
-void wsrep_thd_kill_UNLOCK(THD *thd)
-{
- mysql_mutex_unlock(&thd->LOCK_thd_kill);
-}
-
-
-extern "C" time_t wsrep_thd_query_start(THD *thd)
+int wsrep_must_ignore_error(THD* thd)
{
- return thd->query_start();
-}
+ const int error= thd->get_stmt_da()->sql_errno();
+ const uint flags= sql_command_flags[thd->lex->sql_command];
+ DBUG_ASSERT(error);
+ DBUG_ASSERT(wsrep_thd_is_toi(thd) || wsrep_thd_is_applying(thd));
-extern "C" uint32 wsrep_thd_wsrep_rand(THD *thd)
-{
- return thd->wsrep_rand;
-}
-
-longlong wsrep_thd_trx_seqno(THD *thd)
-{
- return (thd) ? thd->wsrep_trx_meta.gtid.seqno : WSREP_SEQNO_UNDEFINED;
-}
-
+ if ((wsrep_ignore_apply_errors & WSREP_IGNORE_ERRORS_ON_DDL))
+ goto ignore_error;
-extern "C" query_id_t wsrep_thd_query_id(THD *thd)
-{
- return thd->query_id;
-}
-
-
-const char *wsrep_thd_query(THD *thd)
-{
- if (thd)
+ if ((flags & CF_WSREP_MAY_IGNORE_ERRORS) &&
+ (wsrep_ignore_apply_errors & WSREP_IGNORE_ERRORS_ON_RECONCILING_DDL))
{
- switch(thd->lex->sql_command)
+ switch (error)
{
- case SQLCOM_CREATE_USER:
- return "CREATE USER";
- case SQLCOM_GRANT:
- return "GRANT";
- case SQLCOM_REVOKE:
- return "REVOKE";
- case SQLCOM_SET_OPTION:
- if (thd->lex->definer)
- return "SET PASSWORD";
- /* fallthrough */
- default:
- if (thd->query())
- return thd->query();
+ case ER_DB_DROP_EXISTS:
+ case ER_BAD_TABLE_ERROR:
+ case ER_CANT_DROP_FIELD_OR_KEY:
+ goto ignore_error;
}
}
- return "NULL";
-}
+ return 0;
-extern "C" query_id_t wsrep_thd_wsrep_last_query_id(THD *thd)
-{
- return thd->wsrep_last_query_id;
+ignore_error:
+ WSREP_WARN("Ignoring error '%s' on query. "
+ "Default database: '%s'. Query: '%s', Error_code: %d",
+ thd->get_stmt_da()->message(),
+ print_slave_db_safe(thd->db.str),
+ thd->query(),
+ error);
+ return 1;
}
-
-extern "C" void wsrep_thd_set_wsrep_last_query_id(THD *thd, query_id_t id)
+int wsrep_ignored_error_code(Log_event* ev, int error)
{
- thd->wsrep_last_query_id= id;
-}
+ const THD* thd= ev->thd;
+ DBUG_ASSERT(error);
+ DBUG_ASSERT(wsrep_thd_is_applying(thd) &&
+ !wsrep_thd_is_local_toi(thd));
-extern "C" void wsrep_thd_awake(THD *thd, my_bool signal)
-{
- if (signal)
- {
- thd->awake_no_mutex(KILL_QUERY);
- }
- else
+ if ((wsrep_ignore_apply_errors & WSREP_IGNORE_ERRORS_ON_RECONCILING_DML))
{
- mysql_mutex_lock(&LOCK_wsrep_replaying);
- mysql_cond_broadcast(&COND_wsrep_replaying);
- mysql_mutex_unlock(&LOCK_wsrep_replaying);
+ const int ev_type= ev->get_type_code();
+ if ((ev_type == DELETE_ROWS_EVENT || ev_type == DELETE_ROWS_EVENT_V1)
+ && error == ER_KEY_NOT_FOUND)
+ goto ignore_error;
}
-}
-
-
-int wsrep_thd_retry_counter(THD *thd)
-{
- return(thd->wsrep_retry_counter);
-}
+ return 0;
-extern "C" bool wsrep_thd_ignore_table(THD *thd)
-{
- return thd->wsrep_ignore_table;
+ignore_error:
+ WSREP_WARN("Ignoring error '%s' on %s event. Error_code: %d",
+ thd->get_stmt_da()->message(),
+ ev->get_type_str(),
+ error);
+ return 1;
}
-
-extern int
-wsrep_trx_order_before(THD *thd1, THD *thd2)
+bool wsrep_provider_is_SR_capable()
{
- const longlong trx1_seqno= wsrep_thd_trx_seqno(thd1);
- const longlong trx2_seqno= wsrep_thd_trx_seqno(thd2);
- WSREP_DEBUG("BF conflict, order: %lld %lld\n",
- trx1_seqno, trx2_seqno);
-
- if (trx1_seqno == WSREP_SEQNO_UNDEFINED ||
- trx2_seqno == WSREP_SEQNO_UNDEFINED)
- return 1; /* trx is not yet replicated */
- else if (trx1_seqno < trx2_seqno)
- return 1;
-
- return 0;
+ return Wsrep_server_state::has_capability(wsrep::provider::capability::streaming);
}
-
-int wsrep_trx_is_aborting(THD *thd_ptr)
+int wsrep_thd_retry_counter(const THD *thd)
{
- if (thd_ptr) {
- if ((((THD *)thd_ptr)->wsrep_conflict_state == MUST_ABORT) ||
- (((THD *)thd_ptr)->wsrep_conflict_state == ABORTING)) {
- return 1;
- }
- }
- return 0;
+ return thd->wsrep_retry_counter;
}
-
-void wsrep_copy_query(THD *thd)
+extern bool wsrep_thd_ignore_table(THD *thd)
{
- thd->wsrep_retry_command = thd->get_command();
- thd->wsrep_retry_query_len = thd->query_length();
- if (thd->wsrep_retry_query) {
- my_free(thd->wsrep_retry_query);
- }
- thd->wsrep_retry_query = (char *)my_malloc(
- thd->wsrep_retry_query_len + 1, MYF(0));
- strncpy(thd->wsrep_retry_query, thd->query(), thd->wsrep_retry_query_len);
- thd->wsrep_retry_query[thd->wsrep_retry_query_len] = '\0';
+ return thd->wsrep_ignore_table;
}
-
bool wsrep_is_show_query(enum enum_sql_command command)
{
DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
return (sql_command_flags[command] & CF_STATUS_COMMAND) != 0;
}
-
bool wsrep_create_like_table(THD* thd, TABLE_LIST* table,
TABLE_LIST* src_table,
HA_CREATE_INFO *create_info)
@@ -2863,14 +2721,14 @@ bool wsrep_create_like_table(THD* thd, TABLE_LIST* table,
}
return(false);
-
-WSREP_ERROR_LABEL:
+#ifdef WITH_WSREP
+wsrep_error_label:
thd->wsrep_TOI_pre_query= NULL;
return (true);
+#endif
}
-
-static int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len)
+int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len)
{
LEX *lex= thd->lex;
String stmt_query;
@@ -2932,88 +2790,194 @@ static int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len)
buf, buf_len);
}
-/***** callbacks for wsrep service ************/
-
-my_bool get_wsrep_debug()
+void* start_wsrep_THD(void *arg)
{
- return wsrep_debug;
-}
+ THD *thd;
-my_bool get_wsrep_load_data_splitting()
-{
- return wsrep_load_data_splitting;
-}
+ Wsrep_thd_args* thd_args= (Wsrep_thd_args*) arg;
-long get_wsrep_protocol_version()
-{
- return wsrep_protocol_version;
-}
+ if (my_thread_init() || (!(thd= new THD(next_thread_id(), true))))
+ {
+ goto error;
+ }
-my_bool get_wsrep_drupal_282555_workaround()
-{
- return wsrep_drupal_282555_workaround;
-}
+ statistic_increment(thread_created, &LOCK_status);
-my_bool get_wsrep_recovery()
-{
- return wsrep_recovery;
-}
+ if (wsrep_gtid_mode)
+ {
+ /* Adjust domain_id. */
+ thd->variables.gtid_domain_id= wsrep_gtid_domain_id;
+ }
-my_bool get_wsrep_log_conflicts()
-{
- return wsrep_log_conflicts;
-}
+ thd->real_id=pthread_self(); // Keep purify happy
-wsrep_t *get_wsrep()
-{
- return wsrep;
-}
+ my_net_init(&thd->net,(st_vio*) 0, thd, MYF(0));
-my_bool get_wsrep_certify_nonPK()
-{
- return wsrep_certify_nonPK;
-}
+ DBUG_PRINT("wsrep",(("creating thread %lld"), (long long)thd->thread_id));
+ thd->prior_thr_create_utime= thd->start_utime= microsecond_interval_timer();
-void wsrep_lock_rollback()
-{
- mysql_mutex_lock(&LOCK_wsrep_rollback);
-}
+ server_threads.insert(thd);
-void wsrep_unlock_rollback()
-{
- mysql_cond_signal(&COND_wsrep_rollback);
- mysql_mutex_unlock(&LOCK_wsrep_rollback);
-}
+ /* from bootstrap()... */
+ thd->bootstrap=1;
+ thd->max_client_packet_length= thd->net.max_packet;
+ thd->security_ctx->master_access= ~(ulong)0;
-my_bool wsrep_aborting_thd_contains(THD *thd)
-{
- mysql_mutex_assert_owner(&LOCK_wsrep_rollback);
- wsrep_aborting_thd_t abortees = wsrep_aborting_thd;
- while (abortees)
+ /* from handle_one_connection... */
+ pthread_detach_this_thread();
+
+ mysql_thread_set_psi_id(thd->thread_id);
+ thd->thr_create_utime= microsecond_interval_timer();
+ if (MYSQL_CALLBACK_ELSE(thread_scheduler, init_new_connection_thread, (), 0))
{
- if (abortees->aborting_thd == thd)
- return true;
- abortees = abortees->next;
+ close_connection(thd, ER_OUT_OF_RESOURCES);
+ statistic_increment(aborted_connects,&LOCK_status);
+ MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
+ goto error;
}
- return false;
+
+// </5.1.17>
+ /*
+ handle_one_connection() is normally the only way a thread would
+ start and would always be on the very high end of the stack ,
+ therefore, the thread stack always starts at the address of the
+ first local variable of handle_one_connection, which is thd. We
+ need to know the start of the stack so that we could check for
+ stack overruns.
+ */
+ DBUG_PRINT("wsrep", ("handle_one_connection called by thread %lld",
+ (long long)thd->thread_id));
+ /* now that we've called my_thread_init(), it is safe to call DBUG_* */
+
+ thd->thread_stack= (char*) &thd;
+ wsrep_assign_from_threadvars(thd);
+ if (wsrep_store_threadvars(thd))
+ {
+ close_connection(thd, ER_OUT_OF_RESOURCES);
+ statistic_increment(aborted_connects,&LOCK_status);
+ MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
+ delete thd;
+ delete thd_args;
+ goto error;
+ }
+
+ thd->system_thread= SYSTEM_THREAD_SLAVE_SQL;
+ thd->security_ctx->skip_grants();
+
+ /* handle_one_connection() again... */
+ thd->proc_info= 0;
+ thd->set_command(COM_SLEEP);
+ thd->init_for_queries();
+ mysql_mutex_lock(&LOCK_wsrep_slave_threads);
+
+ wsrep_running_threads++;
+
+ switch (thd_args->thread_type()) {
+ case WSREP_APPLIER_THREAD:
+ wsrep_running_applier_threads++;
+ break;
+ case WSREP_ROLLBACKER_THREAD:
+ wsrep_running_rollbacker_threads++;
+ break;
+ default:
+ WSREP_ERROR("Incorrect wsrep thread type: %d", thd_args->thread_type());
+ break;
+ }
+
+ mysql_cond_broadcast(&COND_wsrep_slave_threads);
+ mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
+
+ WSREP_DEBUG("wsrep system thread %llu, %p starting",
+ thd->thread_id, thd);
+ thd_args->fun()(thd, static_cast<void *>(thd_args));
+
+ WSREP_DEBUG("wsrep system thread: %llu, %p closing",
+ thd->thread_id, thd);
+
+ /* Wsrep may reset globals during thread context switches, store globals
+ before cleanup. */
+ wsrep_store_threadvars(thd);
+
+ close_connection(thd, 0);
+
+ mysql_mutex_lock(&LOCK_wsrep_slave_threads);
+ DBUG_ASSERT(wsrep_running_threads > 0);
+ wsrep_running_threads--;
+
+ switch (thd_args->thread_type()) {
+ case WSREP_APPLIER_THREAD:
+ DBUG_ASSERT(wsrep_running_applier_threads > 0);
+ wsrep_running_applier_threads--;
+ break;
+ case WSREP_ROLLBACKER_THREAD:
+ DBUG_ASSERT(wsrep_running_rollbacker_threads > 0);
+ wsrep_running_rollbacker_threads--;
+ break;
+ default:
+ WSREP_ERROR("Incorrect wsrep thread type: %d", thd_args->thread_type());
+ break;
+ }
+
+ delete thd_args;
+ WSREP_DEBUG("wsrep running threads now: %lu", wsrep_running_threads);
+ mysql_cond_broadcast(&COND_wsrep_slave_threads);
+ mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
+ /*
+ Note: We can't call THD destructor without crashing
+ if plugins have not been initialized. However, in most of the
+ cases this means that pre SE initialization SST failed and
+ we are going to exit anyway.
+ */
+ if (plugins_are_initialized)
+ {
+ net_end(&thd->net);
+ MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 1));
+ }
+ else
+ {
+ /*
+ TODO: lightweight cleanup to get rid of:
+ 'Error in my_thread_global_end(): 2 threads didn't exit'
+ at server shutdown
+ */
+ }
+
+ server_threads.erase(thd);
+ delete thd;
+ my_thread_end();
+ return(NULL);
+
+error:
+ WSREP_ERROR("Failed to create/initialize system thread");
+
+ /* Abort if its the first applier/rollbacker thread. */
+ if (!mysqld_server_initialized)
+ unireg_abort(1);
+ else
+ return NULL;
}
-void wsrep_aborting_thd_enqueue(THD *thd)
+enum wsrep::streaming_context::fragment_unit wsrep_fragment_unit(ulong unit)
{
- mysql_mutex_assert_owner(&LOCK_wsrep_rollback);
- wsrep_aborting_thd_t aborting = (wsrep_aborting_thd_t)
- my_malloc(sizeof(struct wsrep_aborting_thd), MYF(0));
- aborting->aborting_thd = thd;
- aborting->next = wsrep_aborting_thd;
- wsrep_aborting_thd = aborting;
+ switch (unit)
+ {
+ case WSREP_FRAG_BYTES: return wsrep::streaming_context::bytes;
+ case WSREP_FRAG_ROWS: return wsrep::streaming_context::row;
+ case WSREP_FRAG_STATEMENTS: return wsrep::streaming_context::statement;
+ default:
+ DBUG_ASSERT(0);
+ return wsrep::streaming_context::bytes;
+ }
}
-bool wsrep_node_is_donor()
+/***** callbacks for wsrep service ************/
+
+my_bool get_wsrep_recovery()
{
- return (WSREP_ON) ? (wsrep_config_state->get_status() == 2) : false;
+ return wsrep_recovery;
}
-bool wsrep_node_is_synced()
+bool wsrep_consistency_check(THD *thd)
{
- return (WSREP_ON) ? (wsrep_config_state->get_status() == 4) : false;
+ return thd->wsrep_consistency_check == CONSISTENCY_CHECK_RUNNING;
}
diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h
index 39b6267299c..748d93c72aa 100644
--- a/sql/wsrep_mysqld.h
+++ b/sql/wsrep_mysqld.h
@@ -1,4 +1,4 @@
-/* Copyright 2008-2015 Codership Oy <http://www.codership.com>
+/* Copyright 2008-2017 Codership Oy <http://www.codership.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,25 +13,34 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <wsrep.h>
-
#ifndef WSREP_MYSQLD_H
#define WSREP_MYSQLD_H
-#include <mysql/plugin.h>
-#include <mysql/service_wsrep.h>
+#include <wsrep.h>
#ifdef WITH_WSREP
+extern bool WSREP_ON_;
+extern bool WSREP_PROVIDER_EXISTS_;
+
+#include <mysql/plugin.h>
+#include "mysql/service_wsrep.h"
+
+#include <my_global.h>
+#include <my_pthread.h>
+#include "log.h"
+#include "mysqld.h"
typedef struct st_mysql_show_var SHOW_VAR;
#include <sql_priv.h>
-//#include "rpl_gtid.h"
-#include "../wsrep/wsrep_api.h"
#include "mdl.h"
-#include "mysqld.h"
#include "sql_table.h"
#include "wsrep_mysqld_c.h"
+
+#include "wsrep/provider.hpp"
+#include "wsrep/streaming_context.hpp"
+#include "wsrep_api.h"
#include <vector>
+#include "wsrep_server_state.h"
#define WSREP_UNDEFINED_TRX_ID ULONGLONG_MAX
@@ -44,20 +53,7 @@ enum wsrep_consistency_check_mode {
CONSISTENCY_CHECK_RUNNING,
};
-struct wsrep_thd_shadow {
- ulonglong options;
- uint server_status;
- enum wsrep_exec_mode wsrep_exec_mode;
- Vio *vio;
- ulong tx_isolation;
- const char *db;
- size_t db_length;
- my_hrtime_t user_time;
- longlong row_count_func;
-};
-
// Global wsrep parameters
-extern wsrep_t* wsrep;
// MySQL wsrep options
extern const char* wsrep_provider;
@@ -71,24 +67,33 @@ extern const char* wsrep_data_home_dir;
extern const char* wsrep_dbug_option;
extern long wsrep_slave_threads;
extern int wsrep_slave_count_change;
+extern ulong wsrep_debug;
extern my_bool wsrep_convert_LOCK_to_trx;
extern ulong wsrep_retry_autocommit;
extern my_bool wsrep_auto_increment_control;
+extern my_bool wsrep_drupal_282555_workaround;
extern my_bool wsrep_incremental_data_collection;
extern const char* wsrep_start_position;
extern ulong wsrep_max_ws_size;
extern ulong wsrep_max_ws_rows;
extern const char* wsrep_notify_cmd;
-extern long wsrep_max_protocol_version;
+extern my_bool wsrep_certify_nonPK;
+extern long int wsrep_protocol_version;
extern ulong wsrep_forced_binlog_format;
extern my_bool wsrep_desync;
extern ulong wsrep_reject_queries;
+extern my_bool wsrep_recovery;
extern my_bool wsrep_replicate_myisam;
+extern my_bool wsrep_log_conflicts;
extern ulong wsrep_mysql_replication_bundle;
+extern my_bool wsrep_load_data_splitting;
extern my_bool wsrep_restart_slave;
extern my_bool wsrep_restart_slave_activated;
extern my_bool wsrep_slave_FK_checks;
extern my_bool wsrep_slave_UK_checks;
+extern ulong wsrep_trx_fragment_unit;
+extern ulong wsrep_SR_store_type;
+extern uint wsrep_ignore_apply_errors;
extern ulong wsrep_running_threads;
extern ulong wsrep_running_applier_threads;
extern ulong wsrep_running_rollbacker_threads;
@@ -109,15 +114,34 @@ enum enum_wsrep_OSU_method {
};
enum enum_wsrep_sync_wait {
- WSREP_SYNC_WAIT_NONE = 0x0,
+ WSREP_SYNC_WAIT_NONE= 0x0,
// select, begin
- WSREP_SYNC_WAIT_BEFORE_READ = 0x1,
- WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE = 0x2,
- WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE = 0x4,
- WSREP_SYNC_WAIT_BEFORE_SHOW = 0x8,
- WSREP_SYNC_WAIT_MAX = 0xF
+ WSREP_SYNC_WAIT_BEFORE_READ= 0x1,
+ WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE= 0x2,
+ WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE= 0x4,
+ WSREP_SYNC_WAIT_BEFORE_SHOW= 0x8,
+ WSREP_SYNC_WAIT_MAX= 0xF
+};
+
+enum enum_wsrep_ignore_apply_error {
+ WSREP_IGNORE_ERRORS_NONE= 0x0,
+ WSREP_IGNORE_ERRORS_ON_RECONCILING_DDL= 0x1,
+ WSREP_IGNORE_ERRORS_ON_RECONCILING_DML= 0x2,
+ WSREP_IGNORE_ERRORS_ON_DDL= 0x4,
+ WSREP_IGNORE_ERRORS_MAX= 0x7
};
+// Streaming Replication
+#define WSREP_FRAG_BYTES 0
+#define WSREP_FRAG_ROWS 1
+#define WSREP_FRAG_STATEMENTS 2
+
+#define WSREP_SR_STORE_NONE 0
+#define WSREP_SR_STORE_TABLE 1
+
+extern const char *wsrep_fragment_units[];
+extern const char *wsrep_SR_store_types[];
+
// MySQL status variables
extern my_bool wsrep_connected;
extern my_bool wsrep_ready;
@@ -130,9 +154,19 @@ extern long long wsrep_local_bf_aborts;
extern const char* wsrep_provider_name;
extern const char* wsrep_provider_version;
extern const char* wsrep_provider_vendor;
+extern char* wsrep_provider_capabilities;
+extern char* wsrep_cluster_capabilities;
int wsrep_show_status(THD *thd, SHOW_VAR *var, void *buff,
system_status_var *status_var, enum_var_type scope);
+int wsrep_show_ready(THD *thd, SHOW_VAR *var, char *buff);
+void wsrep_free_status(THD *thd);
+void wsrep_update_cluster_state_uuid(const char* str);
+
+/* Filters out --wsrep-new-cluster oprtion from argv[]
+ * should be called in the very beginning of main() */
+void wsrep_filter_new_cluster (int* argc, char* argv[]);
+
int wsrep_init();
void wsrep_deinit(bool free_options);
@@ -148,19 +182,17 @@ bool wsrep_before_SE(); // initialize wsrep before storage
* @param before wsrep_before_SE() value */
void wsrep_init_startup(bool before);
+/* Recover streaming transactions from fragment storage */
+void wsrep_recover_sr_from_storage(THD *);
+
// Other wsrep global variables
extern my_bool wsrep_inited; // whether wsrep is initialized ?
-
-extern "C" void wsrep_thd_set_exec_mode(THD *thd, enum wsrep_exec_mode mode);
-extern "C" void wsrep_thd_set_query_state(
- THD *thd, enum wsrep_query_state state);
-
-extern "C" void wsrep_thd_set_trx_to_replay(THD *thd, uint64 trx_id);
-
+extern "C" void wsrep_fire_rollbacker(THD *thd);
extern "C" uint32 wsrep_thd_wsrep_rand(THD *thd);
extern "C" time_t wsrep_thd_query_start(THD *thd);
-extern "C" query_id_t wsrep_thd_query_id(THD *thd);
+extern void wsrep_close_client_connections(my_bool wait_to_end,
+ THD *except_caller_thd= NULL);
extern "C" query_id_t wsrep_thd_wsrep_last_query_id(THD *thd);
extern "C" void wsrep_thd_set_wsrep_last_query_id(THD *thd, query_id_t id);
@@ -168,60 +200,94 @@ extern int wsrep_wait_committing_connections_close(int wait_time);
extern void wsrep_close_applier(THD *thd);
extern void wsrep_wait_appliers_close(THD *thd);
extern void wsrep_close_applier_threads(int count);
-extern void wsrep_kill_mysql(THD *thd);
+
/* new defines */
extern void wsrep_stop_replication(THD *thd);
-extern bool wsrep_start_replication();
-extern bool wsrep_must_sync_wait(THD* thd, uint mask = WSREP_SYNC_WAIT_BEFORE_READ);
-extern bool wsrep_sync_wait(THD* thd, uint mask = WSREP_SYNC_WAIT_BEFORE_READ);
+extern bool wsrep_start_replication(const char *wsrep_cluster_address);
+extern void wsrep_shutdown_replication();
+extern bool wsrep_must_sync_wait (THD* thd, uint mask= WSREP_SYNC_WAIT_BEFORE_READ);
+extern bool wsrep_sync_wait (THD* thd, uint mask= WSREP_SYNC_WAIT_BEFORE_READ);
+extern enum wsrep::provider::status
+wsrep_sync_wait_upto (THD* thd, wsrep_gtid_t* upto, int timeout);
+extern void wsrep_last_committed_id (wsrep_gtid_t* gtid);
extern int wsrep_check_opts();
extern void wsrep_prepend_PATH (const char* path);
+extern bool wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* table, wsrep::key_array* keys);
+extern bool wsrep_reload_ssl();
/* Other global variables */
extern wsrep_seqno_t wsrep_locked_seqno;
+#define WSREP_ON unlikely(WSREP_ON_)
+
+/* use xxxxxx_NNULL macros when thd pointer is guaranteed to be non-null to
+ * avoid compiler warnings (GCC 6 and later) */
-#define WSREP_ON unlikely(global_system_variables.wsrep_on)
+#define WSREP_NNULL(thd) \
+ (WSREP_PROVIDER_EXISTS_ && thd->variables.wsrep_on)
#define WSREP(thd) \
- (WSREP_ON && thd->variables.wsrep_on)
+ (thd && WSREP_NNULL(thd))
+
+#define WSREP_CLIENT_NNULL(thd) \
+ (WSREP_NNULL(thd) && thd->wsrep_client_thread)
#define WSREP_CLIENT(thd) \
(WSREP(thd) && thd->wsrep_client_thread)
+#define WSREP_EMULATE_BINLOG_NNULL(thd) \
+ (WSREP_NNULL(thd) && wsrep_emulate_bin_log)
+
#define WSREP_EMULATE_BINLOG(thd) \
(WSREP(thd) && wsrep_emulate_bin_log)
-#define WSREP_FORMAT(my_format) \
- ((wsrep_forced_binlog_format != BINLOG_FORMAT_UNSPEC) \
- ? wsrep_forced_binlog_format : (ulong)(my_format))
-
-// prefix all messages with "WSREP"
-void wsrep_log(void (*fun)(const char *, ...), const char *format, ...);
-#define WSREP_LOG(fun, ...) wsrep_log(fun, ## __VA_ARGS__)
-#define WSREP_LOG_CONFLICT_THD(thd, role) \
- WSREP_LOG(sql_print_information, \
- "%s: \n " \
- " THD: %lu, mode: %s, state: %s, conflict: %s, seqno: %lld\n " \
- " SQL: %s", \
- role, thd_get_thread_id(thd), wsrep_thd_exec_mode_str(thd), \
- wsrep_thd_query_state_str(thd), \
- wsrep_thd_conflict_state_str(thd), (long long)wsrep_thd_trx_seqno(thd), \
- wsrep_thd_query(thd) \
- );
-
-#define WSREP_LOG_CONFLICT(bf_thd, victim_thd, bf_abort) \
- if (wsrep_debug || wsrep_log_conflicts) \
- { \
- WSREP_LOG(sql_print_information, "cluster conflict due to %s for threads:",\
- (bf_abort) ? "high priority abort" : "certification failure" \
- ); \
- if (bf_thd != NULL) WSREP_LOG_CONFLICT_THD(bf_thd, "Winning thread"); \
- if (victim_thd) WSREP_LOG_CONFLICT_THD(victim_thd, "Victim thread"); \
+#define WSREP_BINLOG_FORMAT(my_format) \
+ ((wsrep_forced_binlog_format != BINLOG_FORMAT_UNSPEC) ? \
+ wsrep_forced_binlog_format : my_format)
+
+/* A wrapper function for MySQL log functions. The call will prefix
+ the log message with WSREP and forward the result buffer to fun. */
+void WSREP_LOG(void (*fun)(const char* fmt, ...), const char* fmt, ...);
+
+#define WSREP_DEBUG(...) \
+ if (wsrep_debug) WSREP_LOG(sql_print_information, ##__VA_ARGS__)
+#define WSREP_INFO(...) WSREP_LOG(sql_print_information, ##__VA_ARGS__)
+#define WSREP_WARN(...) WSREP_LOG(sql_print_warning, ##__VA_ARGS__)
+#define WSREP_ERROR(...) WSREP_LOG(sql_print_error, ##__VA_ARGS__)
+#define WSREP_UNKNOWN(fmt, ...) WSREP_ERROR("UNKNOWN: " fmt, ##__VA_ARGS__)
+
+#define WSREP_LOG_CONFLICT_THD(thd, role) \
+ WSREP_INFO("%s: \n " \
+ " THD: %lu, mode: %s, state: %s, conflict: %s, seqno: %lld\n " \
+ " SQL: %s", \
+ role, \
+ thd_get_thread_id(thd), \
+ wsrep_thd_client_mode_str(thd), \
+ wsrep_thd_client_state_str(thd), \
+ wsrep_thd_transaction_state_str(thd), \
+ wsrep_thd_trx_seqno(thd), \
+ wsrep_thd_query(thd) \
+ );
+
+#define WSREP_LOG_CONFLICT(bf_thd, victim_thd, bf_abort) \
+ if (wsrep_debug || wsrep_log_conflicts) \
+ { \
+ WSREP_INFO("cluster conflict due to %s for threads:", \
+ (bf_abort) ? "high priority abort" : "certification failure" \
+ ); \
+ if (bf_thd) WSREP_LOG_CONFLICT_THD(bf_thd, "Winning thread"); \
+ if (victim_thd) WSREP_LOG_CONFLICT_THD(victim_thd, "Victim thread"); \
+ WSREP_INFO("context: %s:%d", __FILE__, __LINE__); \
}
-#define WSREP_PROVIDER_EXISTS \
- (wsrep_provider && strncasecmp(wsrep_provider, WSREP_NONE, FN_REFLEN))
+#define WSREP_PROVIDER_EXISTS (WSREP_PROVIDER_EXISTS_)
+
+static inline bool wsrep_cluster_address_exists()
+{
+ if (mysqld_server_started)
+ mysql_mutex_assert_owner(&LOCK_global_system_variables);
+ return wsrep_cluster_address && wsrep_cluster_address[0];
+}
#define WSREP_QUERY(thd) (thd->query())
@@ -230,15 +296,6 @@ extern void wsrep_ready_wait();
class Ha_trx_info;
struct THD_TRANS;
-void wsrep_register_hton(THD* thd, bool all);
-void wsrep_brute_force_killer(THD *thd);
-int wsrep_hire_brute_force_killer(THD *thd, uint64_t trx_id);
-
-/* this is visible for client build so that innodb plugin gets this */
-typedef struct wsrep_aborting_thd {
- struct wsrep_aborting_thd *next;
- THD *aborting_thd;
-} *wsrep_aborting_thd_t;
extern mysql_mutex_t LOCK_wsrep_ready;
extern mysql_cond_t COND_wsrep_ready;
@@ -246,24 +303,33 @@ extern mysql_mutex_t LOCK_wsrep_sst;
extern mysql_cond_t COND_wsrep_sst;
extern mysql_mutex_t LOCK_wsrep_sst_init;
extern mysql_cond_t COND_wsrep_sst_init;
-extern mysql_mutex_t LOCK_wsrep_rollback;
-extern mysql_cond_t COND_wsrep_rollback;
extern int wsrep_replaying;
extern mysql_mutex_t LOCK_wsrep_replaying;
extern mysql_cond_t COND_wsrep_replaying;
extern mysql_mutex_t LOCK_wsrep_slave_threads;
+extern mysql_cond_t COND_wsrep_slave_threads;
+extern mysql_mutex_t LOCK_wsrep_cluster_config;
extern mysql_mutex_t LOCK_wsrep_desync;
+extern mysql_mutex_t LOCK_wsrep_SR_pool;
+extern mysql_mutex_t LOCK_wsrep_SR_store;
extern mysql_mutex_t LOCK_wsrep_config_state;
-extern wsrep_aborting_thd_t wsrep_aborting_thd;
+extern mysql_mutex_t LOCK_wsrep_group_commit;
+extern mysql_mutex_t LOCK_wsrep_joiner_monitor;
+extern mysql_mutex_t LOCK_wsrep_donor_monitor;
+extern mysql_cond_t COND_wsrep_joiner_monitor;
+extern mysql_cond_t COND_wsrep_donor_monitor;
+
extern my_bool wsrep_emulate_bin_log;
extern int wsrep_to_isolation;
#ifdef GTID_SUPPORT
extern rpl_sidno wsrep_sidno;
#endif /* GTID_SUPPORT */
extern my_bool wsrep_preordered_opt;
-extern handlerton *wsrep_hton;
#ifdef HAVE_PSI_INTERFACE
+
+extern PSI_cond_key key_COND_wsrep_thd;
+
extern PSI_mutex_key key_LOCK_wsrep_ready;
extern PSI_mutex_key key_COND_wsrep_ready;
extern PSI_mutex_key key_LOCK_wsrep_sst;
@@ -272,12 +338,19 @@ extern PSI_mutex_key key_LOCK_wsrep_sst_init;
extern PSI_cond_key key_COND_wsrep_sst_init;
extern PSI_mutex_key key_LOCK_wsrep_sst_thread;
extern PSI_cond_key key_COND_wsrep_sst_thread;
-extern PSI_mutex_key key_LOCK_wsrep_rollback;
-extern PSI_cond_key key_COND_wsrep_rollback;
extern PSI_mutex_key key_LOCK_wsrep_replaying;
extern PSI_cond_key key_COND_wsrep_replaying;
extern PSI_mutex_key key_LOCK_wsrep_slave_threads;
+extern PSI_cond_key key_COND_wsrep_slave_threads;
+extern PSI_mutex_key key_LOCK_wsrep_cluster_config;
extern PSI_mutex_key key_LOCK_wsrep_desync;
+extern PSI_mutex_key key_LOCK_wsrep_SR_pool;
+extern PSI_mutex_key key_LOCK_wsrep_SR_store;
+extern PSI_mutex_key key_LOCK_wsrep_global_seqno;
+extern PSI_mutex_key key_LOCK_wsrep_thd_queue;
+extern PSI_cond_key key_COND_wsrep_thd_queue;
+extern PSI_mutex_key key_LOCK_wsrep_joiner_monitor;
+extern PSI_mutex_key key_LOCK_wsrep_donor_monitor;
extern PSI_file_key key_file_wsrep_gra_log;
@@ -285,6 +358,8 @@ extern PSI_thread_key key_wsrep_sst_joiner;
extern PSI_thread_key key_wsrep_sst_donor;
extern PSI_thread_key key_wsrep_rollbacker;
extern PSI_thread_key key_wsrep_applier;
+extern PSI_thread_key key_wsrep_sst_joiner_monitor;
+extern PSI_thread_key key_wsrep_sst_donor_monitor;
#endif /* HAVE_PSI_INTERFACE */
@@ -292,55 +367,30 @@ struct TABLE_LIST;
class Alter_info;
int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_,
const TABLE_LIST* table_list,
- Alter_info* alter_info = NULL);
+ Alter_info* alter_info= NULL, wsrep::key_array *fk_tables=NULL);
+
void wsrep_to_isolation_end(THD *thd);
-void wsrep_cleanup_transaction(THD *thd);
+
+bool wsrep_append_SR_keys(THD *thd);
int wsrep_to_buf_helper(
THD* thd, const char *query, uint query_len, uchar** buf, size_t* buf_len);
+int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len);
int wsrep_create_event_query(THD *thd, uchar** buf, size_t* buf_len);
-extern bool
-wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
- MDL_ticket *ticket,
- const MDL_key *key);
-IO_CACHE * get_trans_log(THD * thd);
-bool wsrep_trans_cache_is_empty(THD *thd);
-void thd_binlog_flush_pending_rows_event(THD *thd, bool stmt_end);
-void thd_binlog_rollback_stmt(THD * thd);
-void thd_binlog_trx_reset(THD * thd);
-
-enum wsrep_thread_type {
- WSREP_APPLIER_THREAD=1,
- WSREP_ROLLBACKER_THREAD=2
-};
-
-typedef void (*wsrep_thd_processor_fun)(THD *);
+void wsrep_init_sidno(const wsrep_uuid_t&);
+bool wsrep_node_is_donor();
+bool wsrep_node_is_synced();
-typedef struct {
- pthread_t thread_id;
- wsrep_thd_processor_fun processor;
- enum wsrep_thread_type thread_type;
-} wsrep_thread_args;
+void wsrep_init_SR();
+void wsrep_verify_SE_checkpoint(const wsrep_uuid_t& uuid, wsrep_seqno_t seqno);
+int wsrep_replay_from_SR_store(THD*, const wsrep_trx_meta_t&);
+void wsrep_node_uuid(wsrep_uuid_t&);
-extern std::vector<wsrep_thread_args*> wsrep_thread_arg;
+class Log_event;
+int wsrep_ignored_error_code(Log_event* ev, int error);
+int wsrep_must_ignore_error(THD* thd);
-pthread_handler_t start_wsrep_THD(void *arg);
-int wsrep_wait_committing_connections_close(int wait_time);
-extern void wsrep_close_client_connections(my_bool wait_to_end,
- THD *except_caller_thd = NULL);
-void wsrep_close_applier(THD *thd);
-void wsrep_close_applier_threads(int count);
-void wsrep_wait_appliers_close(THD *thd);
-void wsrep_kill_mysql(THD *thd);
-void wsrep_close_threads(THD *thd);
-void wsrep_copy_query(THD *thd);
-bool wsrep_is_show_query(enum enum_sql_command command);
-void wsrep_replay_transaction(THD *thd);
-bool wsrep_create_like_table(THD* thd, TABLE_LIST* table,
- TABLE_LIST* src_table,
- HA_CREATE_INFO *create_info);
-bool wsrep_node_is_donor();
-bool wsrep_node_is_synced();
+bool wsrep_replicate_GTID(THD* thd);
typedef struct wsrep_key_arr
{
@@ -354,38 +404,112 @@ bool wsrep_prepare_keys_for_isolation(THD* thd,
wsrep_key_arr_t* ka);
void wsrep_keys_free(wsrep_key_arr_t* key_arr);
-#define WSREP_BINLOG_FORMAT(my_format) \
- ((wsrep_forced_binlog_format != BINLOG_FORMAT_UNSPEC) ? \
- wsrep_forced_binlog_format : my_format)
+extern void
+wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
+ MDL_ticket *ticket,
+ const MDL_key *key);
+
+enum wsrep_thread_type {
+ WSREP_APPLIER_THREAD=1,
+ WSREP_ROLLBACKER_THREAD=2
+};
+
+typedef void (*wsrep_thd_processor_fun)(THD*, void *);
+class Wsrep_thd_args
+{
+ public:
+ Wsrep_thd_args(wsrep_thd_processor_fun fun,
+ wsrep_thread_type thread_type,
+ pthread_t thread_id)
+ :
+ fun_ (fun),
+ thread_type_ (thread_type),
+ thread_id_ (thread_id)
+ { }
+
+ wsrep_thd_processor_fun fun() { return fun_; }
+ pthread_t* thread_id() {return &thread_id_; }
+ enum wsrep_thread_type thread_type() {return thread_type_;}
+
+ private:
+
+ Wsrep_thd_args(const Wsrep_thd_args&);
+ Wsrep_thd_args& operator=(const Wsrep_thd_args&);
+
+ wsrep_thd_processor_fun fun_;
+ enum wsrep_thread_type thread_type_;
+ pthread_t thread_id_;
+};
-#else /* WITH_WSREP */
+void* start_wsrep_THD(void*);
+void wsrep_close_threads(THD *thd);
+bool wsrep_is_show_query(enum enum_sql_command command);
+void wsrep_replay_transaction(THD *thd);
+bool wsrep_create_like_table(THD* thd, TABLE_LIST* table,
+ TABLE_LIST* src_table,
+ HA_CREATE_INFO *create_info);
+bool wsrep_node_is_donor();
+bool wsrep_node_is_synced();
+
+/**
+ * Check if the wsrep provider (ie the Galera library) is capable of
+ * doing streaming replication.
+ * @return true if SR capable
+ */
+bool wsrep_provider_is_SR_capable();
+
+/**
+ * Initialize WSREP server instance.
+ *
+ * @return Zero on success, non-zero on error.
+ */
+int wsrep_init_server();
+
+/**
+ * Initialize WSREP globals. This should be done after server initialization
+ * is complete and the server has joined to the cluster.
+ *
+ */
+void wsrep_init_globals();
+
+/**
+ * Deinit and release WSREP resources.
+ */
+void wsrep_deinit_server();
+
+/**
+ * Convert streaming fragment unit (WSREP_FRAG_BYTES, WSREP_FRAG_ROWS...)
+ * to corresponding wsrep-lib fragment_unit
+ */
+enum wsrep::streaming_context::fragment_unit wsrep_fragment_unit(ulong unit);
+
+wsrep::key wsrep_prepare_key_for_toi(const char* db, const char* table,
+ enum wsrep::key::type type);
+
+#else /* !WITH_WSREP */
+
+/* These macros are needed to compile MariaDB without WSREP support
+ * (e.g. embedded) */
+
+#define WSREP_ON false
#define WSREP(T) (0)
-#define WSREP_ON (0)
+#define WSREP_NNULL(T) (0)
#define WSREP_EMULATE_BINLOG(thd) (0)
-#define WSREP_CLIENT(thd) (0)
-#define WSREP_FORMAT(my_format) ((ulong)my_format)
+#define WSREP_EMULATE_BINLOG_NNULL(thd) (0)
+#define WSREP_BINLOG_FORMAT(my_format) ((ulong)my_format)
#define WSREP_PROVIDER_EXISTS (0)
#define wsrep_emulate_bin_log (0)
#define wsrep_to_isolation (0)
-#define wsrep_init() (1)
-#define wsrep_prepend_PATH(X)
#define wsrep_before_SE() (0)
#define wsrep_init_startup(X)
-#define wsrep_must_sync_wait(...) (0)
-#define wsrep_sync_wait(...) (0)
-#define wsrep_to_isolation_begin(...) (0)
-#define wsrep_register_hton(...) do { } while(0)
#define wsrep_check_opts() (0)
-#define wsrep_stop_replication(X) do { } while(0)
-#define wsrep_inited (0)
-#define wsrep_deinit(X) do { } while(0)
-#define wsrep_recover() do { } while(0)
-#define wsrep_slave_threads (1)
-#define wsrep_replicate_myisam (0)
#define wsrep_thr_init() do {} while(0)
#define wsrep_thr_deinit() do {} while(0)
-#define wsrep_running_threads (0)
-#define WSREP_BINLOG_FORMAT(my_format) my_format
+#define wsrep_init_globals() do {} while(0)
+#define wsrep_create_appliers(X) do {} while(0)
+#define wsrep_cluster_address_exists() (false)
+
#endif /* WITH_WSREP */
+
#endif /* WSREP_MYSQLD_H */
diff --git a/sql/wsrep_notify.cc b/sql/wsrep_notify.cc
index ae85e4e7092..d2d08e92ae7 100644
--- a/sql/wsrep_notify.cc
+++ b/sql/wsrep_notify.cc
@@ -18,22 +18,8 @@
#include "wsrep_priv.h"
#include "wsrep_utils.h"
-
-static const char* _status_str(wsrep_member_status_t status)
-{
- switch (status)
- {
- case WSREP_MEMBER_UNDEFINED: return "Undefined";
- case WSREP_MEMBER_JOINER: return "Joiner";
- case WSREP_MEMBER_DONOR: return "Donor";
- case WSREP_MEMBER_JOINED: return "Joined";
- case WSREP_MEMBER_SYNCED: return "Synced";
- default: return "Error(?)";
- }
-}
-
-void wsrep_notify_status (wsrep_member_status_t status,
- const wsrep_view_info_t* view)
+void wsrep_notify_status(enum wsrep::server_state::state status,
+ const wsrep::view* view)
{
if (!wsrep_notify_cmd || 0 == strlen(wsrep_notify_cmd))
{
@@ -51,44 +37,37 @@ void wsrep_notify_status (wsrep_member_status_t status,
cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off, "%s",
wsrep_notify_cmd);
- if (status >= WSREP_MEMBER_UNDEFINED && status < WSREP_MEMBER_ERROR)
- {
- cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off, " --status %s",
- _status_str(status));
- }
- else
- {
- /* here we preserve provider error codes */
- cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off,
- " --status 'Error(%d)'", status);
- }
+ cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off, " --status %s",
+ to_c_string(status));
- if (0 != view)
+ if (view != NULL)
{
- char uuid_str[40];
-
- wsrep_uuid_print (&view->state_id.uuid, uuid_str, sizeof(uuid_str));
+ std::ostringstream uuid;
+ uuid << view->state_id().id();
cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off,
- " --uuid %s", uuid_str);
+ " --uuid %s", uuid.str().c_str());
cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off,
- " --primary %s", view->view >= 0 ? "yes" : "no");
+ " --primary %s", view->view_seqno().get() >= 0 ? "yes" : "no");
cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off,
- " --index %d", view->my_idx);
+ " --index %zd", view->own_index());
- if (view->memb_num)
+ const std::vector<wsrep::view::member>& members(view->members());
+ if (members.size())
{
- cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off, " --members");
-
- for (int i = 0; i < view->memb_num; i++)
- {
- wsrep_uuid_print (&view->members[i].id, uuid_str, sizeof(uuid_str));
- cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off,
- "%c%s/%s/%s", i > 0 ? ',' : ' ',
- uuid_str, view->members[i].name,
- view->members[i].incoming);
- }
+ cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off, " --members");
+
+ for (unsigned int i= 0; i < members.size(); i++)
+ {
+ std::ostringstream id;
+ id << members[i].id();
+ cmd_off += snprintf(cmd_ptr + cmd_off, cmd_len - cmd_off,
+ "%c%s/%s/%s", i > 0 ? ',' : ' ',
+ id.str().c_str(),
+ members[i].name().c_str(),
+ members[i].incoming().c_str());
+ }
}
}
@@ -103,7 +82,7 @@ void wsrep_notify_status (wsrep_member_status_t status,
wsp::process p(cmd_ptr, "r", NULL);
p.wait();
- int err = p.error();
+ int err= p.error();
if (err)
{
diff --git a/sql/wsrep_plugin.cc b/sql/wsrep_plugin.cc
new file mode 100644
index 00000000000..743b8a593b8
--- /dev/null
+++ b/sql/wsrep_plugin.cc
@@ -0,0 +1,53 @@
+/* Copyright 2016 Codership Oy <http://www.codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "wsrep_trans_observer.h"
+#include "wsrep_mysqld.h"
+
+#include <mysql/plugin.h>
+
+static int wsrep_plugin_init(void *p)
+{
+ WSREP_DEBUG("wsrep_plugin_init()");
+ return 0;
+}
+
+static int wsrep_plugin_deinit(void *p)
+{
+ WSREP_DEBUG("wsrep_plugin_deinit()");
+ return 0;
+}
+
+struct Mysql_replication wsrep_plugin= {
+ MYSQL_REPLICATION_INTERFACE_VERSION
+};
+
+maria_declare_plugin(wsrep)
+{
+ MYSQL_REPLICATION_PLUGIN,
+ &wsrep_plugin,
+ "wsrep",
+ "Codership Oy",
+ "Wsrep replication plugin",
+ PLUGIN_LICENSE_GPL,
+ wsrep_plugin_init,
+ wsrep_plugin_deinit,
+ 0x0100,
+ NULL, /* Status variables */
+ NULL, /* System variables */
+ "1.0", /* Version (string) */
+ MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */
+}
+maria_declare_plugin_end;
diff --git a/sql/wsrep_priv.h b/sql/wsrep_priv.h
index e84ecfc5e2c..fb8467adc9d 100644
--- a/sql/wsrep_priv.h
+++ b/sql/wsrep_priv.h
@@ -19,8 +19,9 @@
#ifndef WSREP_PRIV_H
#define WSREP_PRIV_H
+#include <my_global.h>
#include "wsrep_mysqld.h"
-#include "../wsrep/wsrep_api.h"
+#include "wsrep_schema.h"
#include <log.h>
#include <pthread.h>
@@ -31,25 +32,20 @@ my_bool wsrep_ready_set (my_bool x);
ssize_t wsrep_sst_prepare (void** msg);
wsrep_cb_status wsrep_sst_donate_cb (void* app_ctx,
void* recv_ctx,
- const void* msg, size_t msg_len,
+ const wsrep_buf_t* msg,
const wsrep_gtid_t* state_id,
- const char* state, size_t state_len,
+ const wsrep_buf_t* state,
bool bypass);
extern wsrep_uuid_t local_uuid;
extern wsrep_seqno_t local_seqno;
+extern Wsrep_schema* wsrep_schema;
// a helper function
-bool wsrep_sst_received (wsrep_t* const wsrep,
- const wsrep_uuid_t& uuid,
- const wsrep_seqno_t seqno,
- const void* const state,
- const size_t state_len,
- const bool implicit);
-/*! SST thread signals init thread about sst completion */
-void wsrep_sst_complete(const wsrep_uuid_t*, wsrep_seqno_t, bool);
-
-void wsrep_notify_status (wsrep_member_status_t new_status,
- const wsrep_view_info_t* view = 0);
+bool wsrep_sst_received(THD*, const wsrep_uuid_t&, wsrep_seqno_t,
+ const void*, size_t);
+
+void wsrep_notify_status(enum wsrep::server_state::state status,
+ const wsrep::view* view= 0);
#endif /* WSREP_PRIV_H */
diff --git a/sql/wsrep_schema.cc b/sql/wsrep_schema.cc
new file mode 100644
index 00000000000..5ee6468e9c1
--- /dev/null
+++ b/sql/wsrep_schema.cc
@@ -0,0 +1,1428 @@
+/* Copyright (C) 2015-2019 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "mariadb.h"
+
+#include "table.h"
+#include "key.h"
+#include "sql_base.h"
+#include "sql_parse.h"
+#include "sql_update.h"
+#include "transaction.h"
+
+#include "mysql/service_wsrep.h"
+#include "wsrep_schema.h"
+#include "wsrep_applier.h"
+#include "wsrep_xid.h"
+#include "wsrep_binlog.h"
+#include "wsrep_high_priority_service.h"
+#include "wsrep_storage_service.h"
+#include "wsrep_thd.h"
+
+#include <string>
+#include <sstream>
+
+#define WSREP_SCHEMA "mysql"
+#define WSREP_STREAMING_TABLE "wsrep_streaming_log"
+#define WSREP_CLUSTER_TABLE "wsrep_cluster"
+#define WSREP_MEMBERS_TABLE "wsrep_cluster_members"
+
+const char* wsrep_sr_table_name_full= WSREP_SCHEMA "/" WSREP_STREAMING_TABLE;
+
+static const std::string wsrep_schema_str= WSREP_SCHEMA;
+static const std::string sr_table_str= WSREP_STREAMING_TABLE;
+static const std::string cluster_table_str= WSREP_CLUSTER_TABLE;
+static const std::string members_table_str= WSREP_MEMBERS_TABLE;
+
+static const std::string create_cluster_table_str=
+ "CREATE TABLE IF NOT EXISTS " + wsrep_schema_str + "." + cluster_table_str +
+ "("
+ "cluster_uuid CHAR(36) PRIMARY KEY,"
+ "view_id BIGINT NOT NULL,"
+ "view_seqno BIGINT NOT NULL,"
+ "protocol_version INT NOT NULL,"
+ "capabilities INT NOT NULL"
+ ") ENGINE=InnoDB";
+
+static const std::string create_members_table_str=
+ "CREATE TABLE IF NOT EXISTS " + wsrep_schema_str + "." + members_table_str +
+ "("
+ "node_uuid CHAR(36) PRIMARY KEY,"
+ "cluster_uuid CHAR(36) NOT NULL,"
+ "node_name CHAR(32) NOT NULL,"
+ "node_incoming_address VARCHAR(256) NOT NULL"
+ ") ENGINE=InnoDB";
+
+#ifdef WSREP_SCHEMA_MEMBERS_HISTORY
+static const std::string cluster_member_history_table_str= "wsrep_cluster_member_history";
+static const std::string create_members_history_table_str=
+ "CREATE TABLE IF NOT EXISTS " + wsrep_schema_str + "." + cluster_member_history_table_str +
+ "("
+ "node_uuid CHAR(36) PRIMARY KEY,"
+ "cluster_uuid CHAR(36) NOT NULL,"
+ "last_view_id BIGINT NOT NULL,"
+ "last_view_seqno BIGINT NOT NULL,"
+ "node_name CHAR(32) NOT NULL,"
+ "node_incoming_address VARCHAR(256) NOT NULL"
+ ") ENGINE=InnoDB";
+#endif /* WSREP_SCHEMA_MEMBERS_HISTORY */
+
+static const std::string create_frag_table_str=
+ "CREATE TABLE IF NOT EXISTS " + wsrep_schema_str + "." + sr_table_str +
+ "("
+ "node_uuid CHAR(36), "
+ "trx_id BIGINT, "
+ "seqno BIGINT, "
+ "flags INT NOT NULL, "
+ "frag LONGBLOB NOT NULL, "
+ "PRIMARY KEY (node_uuid, trx_id, seqno)"
+ ") ENGINE=InnoDB";
+
+static const std::string delete_from_cluster_table=
+ "DELETE FROM " + wsrep_schema_str + "." + cluster_table_str;
+
+static const std::string delete_from_members_table=
+ "DELETE FROM " + wsrep_schema_str + "." + members_table_str;
+
+namespace Wsrep_schema_impl
+{
+
+class binlog_off
+{
+public:
+ binlog_off(THD* thd)
+ : m_thd(thd)
+ , m_option_bits(thd->variables.option_bits)
+ , m_sql_log_bin(thd->variables.sql_log_bin)
+ {
+ thd->variables.option_bits&= ~OPTION_BIN_LOG;
+ thd->variables.sql_log_bin= 0;
+ }
+ ~binlog_off()
+ {
+ m_thd->variables.option_bits= m_option_bits;
+ m_thd->variables.sql_log_bin= m_sql_log_bin;
+ }
+private:
+ THD* m_thd;
+ ulonglong m_option_bits;
+ my_bool m_sql_log_bin;
+};
+
+class wsrep_off
+{
+public:
+ wsrep_off(THD* thd)
+ : m_thd(thd)
+ , m_wsrep_on(thd->variables.wsrep_on)
+ {
+ thd->variables.wsrep_on= 0;
+ }
+ ~wsrep_off()
+ {
+ m_thd->variables.wsrep_on= m_wsrep_on;
+ }
+private:
+ THD* m_thd;
+ my_bool m_wsrep_on;
+};
+
+class thd_context_switch
+{
+public:
+ thd_context_switch(THD *orig_thd, THD *cur_thd)
+ : m_orig_thd(orig_thd)
+ , m_cur_thd(cur_thd)
+ {
+ wsrep_reset_threadvars(m_orig_thd);
+ wsrep_store_threadvars(m_cur_thd);
+ }
+ ~thd_context_switch()
+ {
+ wsrep_reset_threadvars(m_cur_thd);
+ wsrep_store_threadvars(m_orig_thd);
+ }
+private:
+ THD *m_orig_thd;
+ THD *m_cur_thd;
+};
+
+class sql_safe_updates
+{
+public:
+ sql_safe_updates(THD* thd)
+ : m_thd(thd)
+ , m_option_bits(thd->variables.option_bits)
+ {
+ thd->variables.option_bits&= ~OPTION_SAFE_UPDATES;
+ }
+ ~sql_safe_updates()
+ {
+ m_thd->variables.option_bits= m_option_bits;
+ }
+private:
+ THD* m_thd;
+ ulonglong m_option_bits;
+};
+
+static int execute_SQL(THD* thd, const char* sql, uint length) {
+ DBUG_ENTER("Wsrep_schema::execute_SQL()");
+ int err= 0;
+
+ PSI_statement_locker *parent_locker= thd->m_statement_psi;
+ Parser_state parser_state;
+
+ WSREP_DEBUG("SQL: %d %s thd: %lld", length, sql, (long long)thd->thread_id);
+
+ if (parser_state.init(thd, (char*)sql, length) == 0) {
+ thd->reset_for_next_command();
+ lex_start(thd);
+
+ thd->m_statement_psi= NULL;
+
+ thd->set_query((char*)sql, length);
+ thd->set_query_id(next_query_id());
+
+ mysql_parse(thd, (char*)sql, length, & parser_state, FALSE, FALSE);
+
+ if (thd->is_error()) {
+ WSREP_WARN("Wsrep_schema::execute_sql() failed, %d %s\nSQL: %s",
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message(),
+ sql);
+ err= 1;
+ }
+ thd->m_statement_psi= parent_locker;
+ thd->end_statement();
+ thd->reset_query();
+ close_thread_tables(thd);
+ delete_explain_query(thd->lex);
+ }
+ else {
+ WSREP_WARN("SR init failure");
+ }
+ thd->cleanup_after_query();
+ DBUG_RETURN(err);
+}
+
+/*
+ Initialize thd for next "statement"
+ */
+static void init_stmt(THD* thd) {
+ thd->reset_for_next_command();
+}
+
+static void finish_stmt(THD* thd) {
+ trans_commit_stmt(thd);
+ close_thread_tables(thd);
+}
+
+static int open_table(THD* thd,
+ const LEX_CSTRING *schema_name,
+ const LEX_CSTRING *table_name,
+ enum thr_lock_type const lock_type,
+ TABLE** table) {
+ assert(table);
+ *table= NULL;
+
+ DBUG_ENTER("Wsrep_schema::open_table()");
+
+ TABLE_LIST tables;
+ uint flags= (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
+ MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |
+ MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_LOCK_IGNORE_TIMEOUT);
+
+ tables.init_one_table(schema_name,
+ table_name,
+ NULL, lock_type);
+ thd->lex->query_tables_own_last= 0;
+
+ if (!open_n_lock_single_table(thd, &tables, tables.lock_type, flags)) {
+ if (thd->is_error()) {
+ WSREP_WARN("Can't lock table %s.%s : %d (%s)",
+ schema_name->str, table_name->str,
+ thd->get_stmt_da()->sql_errno(), thd->get_stmt_da()->message());
+ }
+ close_thread_tables(thd);
+ my_error(ER_NO_SUCH_TABLE, MYF(0), schema_name->str, table_name->str);
+ DBUG_RETURN(1);
+ }
+
+ *table= tables.table;
+ (*table)->use_all_columns();
+
+ DBUG_RETURN(0);
+}
+
+
+static int open_for_write(THD* thd, const char* table_name, TABLE** table) {
+ LEX_CSTRING schema_str= { wsrep_schema_str.c_str(), wsrep_schema_str.length() };
+ LEX_CSTRING table_str= { table_name, strlen(table_name) };
+ if (Wsrep_schema_impl::open_table(thd, &schema_str, &table_str, TL_WRITE,
+ table)) {
+ WSREP_ERROR("Failed to open table %s.%s for writing",
+ schema_str.str, table_name);
+ return 1;
+ }
+ empty_record(*table);
+ (*table)->use_all_columns();
+ restore_record(*table, s->default_values);
+ return 0;
+}
+
+static void store(TABLE* table, uint field, const Wsrep_id& id) {
+ assert(field < table->s->fields);
+ std::ostringstream os;
+ os << id;
+ table->field[field]->store(os.str().c_str(),
+ os.str().size(),
+ &my_charset_bin);
+}
+
+
+template <typename INTTYPE>
+static void store(TABLE* table, uint field, const INTTYPE val) {
+ assert(field < table->s->fields);
+ table->field[field]->store(val);
+}
+
+template <typename CHARTYPE>
+static void store(TABLE* table, uint field, const CHARTYPE* str, size_t str_len) {
+ assert(field < table->s->fields);
+ table->field[field]->store((const char*)str,
+ str_len,
+ &my_charset_bin);
+}
+
+static void store(TABLE* table, uint field, const std::string& str)
+{
+ store(table, field, str.c_str(), str.size());
+}
+
+static int update_or_insert(TABLE* table) {
+ DBUG_ENTER("Wsrep_schema::update_or_insert()");
+ int ret= 0;
+ char* key;
+ int error;
+
+ /*
+ Verify that the table has primary key defined.
+ */
+ if (table->s->primary_key >= MAX_KEY ||
+ !table->s->keys_in_use.is_set(table->s->primary_key)) {
+ WSREP_ERROR("No primary key for %s.%s",
+ table->s->db.str, table->s->table_name.str);
+ DBUG_RETURN(1);
+ }
+
+ /*
+ Find the record and update or insert a new one if not found.
+ */
+ if (!(key= (char*) my_safe_alloca(table->s->max_unique_length))) {
+ WSREP_ERROR("Error allocating %ud bytes for key",
+ table->s->max_unique_length);
+ DBUG_RETURN(1);
+ }
+
+ key_copy((uchar*) key, table->record[0],
+ table->key_info + table->s->primary_key, 0);
+
+ if ((error= table->file->ha_index_read_idx_map(table->record[1],
+ table->s->primary_key,
+ (uchar*) key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))) {
+ /*
+ Row not found, insert a new one.
+ */
+ if ((error= table->file->ha_write_row(table->record[0]))) {
+ WSREP_ERROR("Error writing into %s.%s: %d",
+ table->s->db.str,
+ table->s->table_name.str,
+ error);
+ ret= 1;
+ }
+ }
+ else if (!records_are_comparable(table) || compare_record(table)) {
+ /*
+ Record has changed
+ */
+ if ((error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME) {
+ WSREP_ERROR("Error updating record in %s.%s: %d",
+ table->s->db.str,
+ table->s->table_name.str,
+ error);
+ ret= 1;
+ }
+ }
+
+ my_safe_afree(key, table->s->max_unique_length);
+
+ DBUG_RETURN(ret);
+}
+
+static int insert(TABLE* table) {
+ DBUG_ENTER("Wsrep_schema::insert()");
+ int ret= 0;
+ int error;
+
+ /*
+ Verify that the table has primary key defined.
+ */
+ if (table->s->primary_key >= MAX_KEY ||
+ !table->s->keys_in_use.is_set(table->s->primary_key)) {
+ WSREP_ERROR("No primary key for %s.%s",
+ table->s->db.str, table->s->table_name.str);
+ DBUG_RETURN(1);
+ }
+
+ if ((error= table->file->ha_write_row(table->record[0]))) {
+ WSREP_ERROR("Error writing into %s.%s: %d",
+ table->s->db.str,
+ table->s->table_name.str,
+ error);
+ ret= 1;
+ }
+
+ DBUG_RETURN(ret);
+}
+
+static int delete_row(TABLE* table) {
+ int error;
+ int retry= 3;
+
+ do {
+ error= table->file->ha_delete_row(table->record[0]);
+ retry--;
+ } while (error && retry);
+
+ if (error) {
+ WSREP_ERROR("Error deleting row from %s.%s: %d",
+ table->s->db.str,
+ table->s->table_name.str,
+ error);
+ return 1;
+ }
+ return 0;
+}
+
+static int open_for_read(THD* thd, const char* table_name, TABLE** table) {
+
+ LEX_CSTRING schema_str= { wsrep_schema_str.c_str(), wsrep_schema_str.length() };
+ LEX_CSTRING table_str= { table_name, strlen(table_name) };
+ if (Wsrep_schema_impl::open_table(thd, &schema_str, &table_str, TL_READ,
+ table)) {
+ WSREP_ERROR("Failed to open table %s.%s for reading",
+ schema_str.str, table_name);
+ return 1;
+ }
+ empty_record(*table);
+ (*table)->use_all_columns();
+ restore_record(*table, s->default_values);
+ return 0;
+}
+
+/*
+ Init table for sequential scan.
+
+ @return 0 in case of success, 1 in case of error.
+ */
+static int init_for_scan(TABLE* table) {
+ int error;
+ if ((error= table->file->ha_rnd_init(TRUE))) {
+ WSREP_ERROR("Failed to init table for scan: %d", error);
+ return 1;
+ }
+ return 0;
+}
+/*
+ Scan next record. For return codes see handler::ha_rnd_next()
+
+ @return 0 in case of success, error code in case of error
+ */
+static int next_record(TABLE* table) {
+ int error;
+ if ((error= table->file->ha_rnd_next(table->record[0])) &&
+ error != HA_ERR_END_OF_FILE) {
+ WSREP_ERROR("Failed to read next record: %d", error);
+ }
+ return error;
+}
+
+/*
+ End scan.
+
+ @return 0 in case of success, 1 in case of error.
+ */
+static int end_scan(TABLE* table) {
+ int error;
+ if ((error= table->file->ha_rnd_end())) {
+ WSREP_ERROR("Failed to end scan: %d", error);
+ return 1;
+ }
+ return 0;
+}
+
+static int scan(TABLE* table, uint field, wsrep::id& id)
+{
+ assert(field < table->s->fields);
+ String uuid_str;
+ (void)table->field[field]->val_str(&uuid_str);
+ id= wsrep::id(std::string(uuid_str.c_ptr(), uuid_str.length()));
+ return 0;
+}
+
+template <typename INTTYPE>
+static int scan(TABLE* table, uint field, INTTYPE& val)
+{
+ assert(field < table->s->fields);
+ val= table->field[field]->val_int();
+ return 0;
+}
+
+static int scan(TABLE* table, uint field, char* strbuf, uint strbuf_len)
+{
+ String str;
+ (void)table->field[field]->val_str(&str);
+ LEX_CSTRING tmp= str.lex_cstring();
+ uint len = tmp.length;
+ strncpy(strbuf, tmp.str, std::min(len, strbuf_len));
+ strbuf[strbuf_len - 1]= '\0';
+ return 0;
+}
+
+/*
+ Scan member
+ TODO: filter members by cluster UUID
+ */
+static int scan_member(TABLE* table,
+ const Wsrep_id& cluster_uuid,
+ std::vector<Wsrep_view::member>& members)
+{
+ Wsrep_id member_id;
+ char member_name[128]= { 0, };
+ char member_incoming[128]= { 0, };
+
+ if (scan(table, 0, member_id) ||
+ scan(table, 2, member_name, sizeof(member_name)) ||
+ scan(table, 3, member_incoming, sizeof(member_incoming))) {
+ return 1;
+ }
+
+ if (members.empty() == false) {
+ assert(members.rbegin()->id() < member_id);
+ }
+
+ try {
+ members.push_back(Wsrep_view::member(member_id,
+ member_name,
+ member_incoming));
+ }
+ catch (...) {
+ WSREP_ERROR("Caught exception while scanning members table");
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ Init table for index scan and retrieve first record
+
+ @return 0 in case of success, error code in case of error.
+ */
+static int init_for_index_scan(TABLE* table, const uchar* key,
+ key_part_map map) {
+ int error;
+ if ((error= table->file->ha_index_init(table->s->primary_key, true))) {
+ WSREP_ERROR("Failed to init table for index scan: %d", error);
+ return error;
+ }
+
+ error= table->file->ha_index_read_map(table->record[0],
+ key, map, HA_READ_KEY_EXACT);
+ switch(error) {
+ case 0:
+ case HA_ERR_END_OF_FILE:
+ case HA_ERR_KEY_NOT_FOUND:
+ case HA_ERR_ABORTED_BY_USER:
+ break;
+ case -1:
+ WSREP_DEBUG("init_for_index_scan interrupted");
+ break;
+ default:
+ WSREP_ERROR("init_for_index_scan failed to read first record, error %d", error);
+ }
+ return error;
+}
+
+/*
+ End index scan.
+
+ @return 0 in case of success, 1 in case of error.
+ */
+static int end_index_scan(TABLE* table) {
+ int error;
+ if ((error= table->file->ha_index_end())) {
+ WSREP_ERROR("Failed to end scan: %d", error);
+ return 1;
+ }
+ return 0;
+}
+
+static void make_key(TABLE* table, uchar** key, key_part_map* map, int parts) {
+ uint prefix_length= 0;
+ KEY_PART_INFO* key_part= table->key_info->key_part;
+
+ for (int i=0; i < parts; i++)
+ prefix_length += key_part[i].store_length;
+
+ *map= make_prev_keypart_map(parts);
+
+ if (!(*key= (uchar *) my_malloc(prefix_length + 1, MYF(MY_WME))))
+ {
+ WSREP_ERROR("Failed to allocate memory for key prefix_length %u", prefix_length);
+ assert(0);
+ }
+
+ key_copy(*key, table->record[0], table->key_info, prefix_length);
+}
+
+} /* namespace Wsrep_schema_impl */
+
+
+Wsrep_schema::Wsrep_schema()
+{
+}
+
+Wsrep_schema::~Wsrep_schema()
+{ }
+
+static void wsrep_init_thd_for_schema(THD *thd)
+{
+ thd->security_ctx->skip_grants();
+ thd->system_thread= SYSTEM_THREAD_GENERIC;
+
+ thd->real_id=pthread_self(); // Keep purify happy
+
+ thd->prior_thr_create_utime= thd->start_utime= thd->thr_create_utime;
+
+ /* No Galera replication */
+ thd->variables.wsrep_on= 0;
+ /* No binlogging */
+ thd->variables.sql_log_bin= 0;
+ thd->variables.option_bits&= ~OPTION_BIN_LOG;
+ /* No safe updates */
+ thd->variables.option_bits&= ~OPTION_SAFE_UPDATES;
+ /* No general log */
+ thd->variables.option_bits|= OPTION_LOG_OFF;
+ /* Read committed isolation to avoid gap locking */
+ thd->variables.tx_isolation= ISO_READ_COMMITTED;
+ wsrep_assign_from_threadvars(thd);
+ wsrep_store_threadvars(thd);
+}
+
+int Wsrep_schema::init()
+{
+ DBUG_ENTER("Wsrep_schema::init()");
+ int ret;
+ THD* thd= new THD(next_thread_id());
+ if (!thd) {
+ WSREP_ERROR("Unable to get thd");
+ DBUG_RETURN(1);
+ }
+ thd->thread_stack= (char*)&thd;
+ wsrep_init_thd_for_schema(thd);
+
+ if (Wsrep_schema_impl::execute_SQL(thd, create_cluster_table_str.c_str(),
+ create_cluster_table_str.size()) ||
+ Wsrep_schema_impl::execute_SQL(thd, create_members_table_str.c_str(),
+ create_members_table_str.size()) ||
+#ifdef WSREP_SCHEMA_MEMBERS_HISTORY
+ Wsrep_schema_impl::execute_SQL(thd,
+ create_members_history_table_str.c_str(),
+ create_members_history_table_str.size()) ||
+#endif /* WSREP_SCHEMA_MEMBERS_HISTORY */
+ Wsrep_schema_impl::execute_SQL(thd,
+ create_frag_table_str.c_str(),
+ create_frag_table_str.size())) {
+ ret= 1;
+ }
+ else {
+ ret= 0;
+ }
+
+ delete thd;
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_schema::store_view(THD* thd, const Wsrep_view& view)
+{
+ DBUG_ENTER("Wsrep_schema::store_view()");
+ assert(view.status() == Wsrep_view::primary);
+ int ret= 1;
+ int error;
+ TABLE* cluster_table= 0;
+ TABLE* members_table= 0;
+#ifdef WSREP_SCHEMA_MEMBERS_HISTORY
+ TABLE* members_history_table= 0;
+#endif /* WSREP_SCHEMA_MEMBERS_HISTORY */
+
+ Wsrep_schema_impl::wsrep_off wsrep_off(thd);
+ Wsrep_schema_impl::binlog_off binlog_off(thd);
+ Wsrep_schema_impl::sql_safe_updates sql_safe_updates(thd);
+
+ /*
+ Clean up cluster table and members table.
+ */
+ if (Wsrep_schema_impl::execute_SQL(thd,
+ delete_from_cluster_table.c_str(),
+ delete_from_cluster_table.size()) ||
+ Wsrep_schema_impl::execute_SQL(thd,
+ delete_from_members_table.c_str(),
+ delete_from_members_table.size())) {
+ goto out;
+ }
+
+ /*
+ Store cluster view info
+ */
+ Wsrep_schema_impl::init_stmt(thd);
+ if (Wsrep_schema_impl::open_for_write(thd, cluster_table_str.c_str(), &cluster_table))
+ {
+ goto out;
+ }
+
+ Wsrep_schema_impl::store(cluster_table, 0, view.state_id().id());
+ Wsrep_schema_impl::store(cluster_table, 1, view.view_seqno().get());
+ Wsrep_schema_impl::store(cluster_table, 2, view.state_id().seqno().get());
+ Wsrep_schema_impl::store(cluster_table, 3, view.protocol_version());
+ Wsrep_schema_impl::store(cluster_table, 4, view.capabilities());
+
+ if ((error= Wsrep_schema_impl::update_or_insert(cluster_table)))
+ {
+ WSREP_ERROR("failed to write to cluster table: %d", error);
+ goto out;
+ }
+
+ Wsrep_schema_impl::finish_stmt(thd);
+
+ /*
+ Store info about current members
+ */
+ Wsrep_schema_impl::init_stmt(thd);
+ if (Wsrep_schema_impl::open_for_write(thd, members_table_str.c_str(),
+ &members_table))
+ {
+ WSREP_ERROR("failed to open wsrep.members table");
+ goto out;
+ }
+
+ for (size_t i= 0; i < view.members().size(); ++i)
+ {
+ Wsrep_schema_impl::store(members_table, 0, view.members()[i].id());
+ Wsrep_schema_impl::store(members_table, 1, view.state_id().id());
+ Wsrep_schema_impl::store(members_table, 2, view.members()[i].name());
+ Wsrep_schema_impl::store(members_table, 3, view.members()[i].incoming());
+ if ((error= Wsrep_schema_impl::update_or_insert(members_table)))
+ {
+ WSREP_ERROR("failed to write wsrep.members table: %d", error);
+ goto out;
+ }
+ }
+ Wsrep_schema_impl::finish_stmt(thd);
+
+#ifdef WSREP_SCHEMA_MEMBERS_HISTORY
+ /*
+ Store members history
+ */
+ Wsrep_schema_impl::init_stmt(thd);
+ if (Wsrep_schema_impl::open_for_write(thd, cluster_member_history.c_str(),
+ &members_history_table)) {
+ WSREP_ERROR("failed to open wsrep.members table");
+ goto out;
+ }
+
+ for (size_t i= 0; i < view.members().size(); ++i) {
+ Wsrep_schema_impl::store(members_history_table, 0, view.members()[i].id());
+ Wsrep_schema_impl::store(members_history_table, 1, view.state_id().id());
+ Wsrep_schema_impl::store(members_history_table, 2, view.view_seqno());
+ Wsrep_schema_impl::store(members_history_table, 3, view.state_id().seqno());
+ Wsrep_schema_impl::store(members_history_table, 4,
+ view.members()[i].name());
+ Wsrep_schema_impl::store(members_history_table, 5,
+ view.members()[i].incoming());
+ if ((error= Wsrep_schema_impl::update_or_insert(members_history_table))) {
+ WSREP_ERROR("failed to write wsrep_cluster_member_history table: %d", error);
+ goto out;
+ }
+ }
+ Wsrep_schema_impl::finish_stmt(thd);
+#endif /* WSREP_SCHEMA_MEMBERS_HISTORY */
+ ret= 0;
+ out:
+
+ DBUG_RETURN(ret);
+}
+
+Wsrep_view Wsrep_schema::restore_view(THD* thd, const Wsrep_id& own_id) const {
+ DBUG_ENTER("Wsrep_schema::restore_view()");
+
+ int ret= 1;
+ int error;
+
+ TABLE* cluster_table= 0;
+ bool end_cluster_scan= false;
+ TABLE* members_table= 0;
+ bool end_members_scan= false;
+
+ /* variables below need to be initialized in case cluster table is empty */
+ Wsrep_id cluster_uuid;
+ wsrep_seqno_t view_id= -1;
+ wsrep_seqno_t view_seqno= -1;
+ int my_idx= -1;
+ int proto_ver= 0;
+ wsrep_cap_t capabilities= 0;
+ std::vector<Wsrep_view::member> members;
+
+ // we don't want causal waits for reading non-replicated private data
+ int const wsrep_sync_wait_saved= thd->variables.wsrep_sync_wait;
+ thd->variables.wsrep_sync_wait= 0;
+
+ if (trans_begin(thd, MYSQL_START_TRANS_OPT_READ_ONLY)) {
+ WSREP_ERROR("wsrep_schema::restore_view(): Failed to start transaction");
+ goto out;
+ }
+
+ /*
+ Read cluster info from cluster table
+ */
+ Wsrep_schema_impl::init_stmt(thd);
+ if (Wsrep_schema_impl::open_for_read(thd, cluster_table_str.c_str(), &cluster_table) ||
+ Wsrep_schema_impl::init_for_scan(cluster_table)) {
+ goto out;
+ }
+
+ if (((error= Wsrep_schema_impl::next_record(cluster_table)) != 0 ||
+ Wsrep_schema_impl::scan(cluster_table, 0, cluster_uuid) ||
+ Wsrep_schema_impl::scan(cluster_table, 1, view_id) ||
+ Wsrep_schema_impl::scan(cluster_table, 2, view_seqno) ||
+ Wsrep_schema_impl::scan(cluster_table, 3, proto_ver) ||
+ Wsrep_schema_impl::scan(cluster_table, 4, capabilities)) &&
+ error != HA_ERR_END_OF_FILE) {
+ end_cluster_scan= true;
+ goto out;
+ }
+
+ if (Wsrep_schema_impl::end_scan(cluster_table)) {
+ goto out;
+ }
+ Wsrep_schema_impl::finish_stmt(thd);
+
+ /*
+ Read members from members table
+ */
+ Wsrep_schema_impl::init_stmt(thd);
+ if (Wsrep_schema_impl::open_for_read(thd, members_table_str.c_str(), &members_table) ||
+ Wsrep_schema_impl::init_for_scan(members_table)) {
+ goto out;
+ }
+ end_members_scan= true;
+
+ while (true) {
+ if ((error= Wsrep_schema_impl::next_record(members_table)) == 0) {
+ if (Wsrep_schema_impl::scan_member(members_table,
+ cluster_uuid,
+ members)) {
+ goto out;
+ }
+ }
+ else if (error == HA_ERR_END_OF_FILE) {
+ break;
+ }
+ else {
+ goto out;
+ }
+ }
+
+ end_members_scan= false;
+ if (Wsrep_schema_impl::end_scan(members_table)) {
+ goto out;
+ }
+ Wsrep_schema_impl::finish_stmt(thd);
+
+ if (own_id.is_undefined() == false) {
+ for (uint i= 0; i < members.size(); ++i) {
+ if (members[i].id() == own_id) {
+ my_idx= i;
+ break;
+ }
+ }
+ }
+
+ (void)trans_commit(thd);
+ ret= 0; /* Success*/
+ out:
+
+ if (end_cluster_scan) Wsrep_schema_impl::end_scan(cluster_table);
+ if (end_members_scan) Wsrep_schema_impl::end_scan(members_table);
+
+ if (0 != ret) {
+ trans_rollback_stmt(thd);
+ if (!trans_rollback(thd)) {
+ close_thread_tables(thd);
+ }
+ }
+ thd->release_transactional_locks();
+
+ thd->variables.wsrep_sync_wait= wsrep_sync_wait_saved;
+
+ if (0 == ret) {
+ Wsrep_view ret_view(
+ wsrep::gtid(cluster_uuid, Wsrep_seqno(view_seqno)),
+ Wsrep_seqno(view_id),
+ wsrep::view::primary,
+ capabilities,
+ my_idx,
+ proto_ver,
+ members
+ );
+
+ if (wsrep_debug) {
+ std::ostringstream os;
+ os << "Restored cluster view:\n" << ret_view;
+ WSREP_INFO("%s", os.str().c_str());
+ }
+ DBUG_RETURN(ret_view);
+ }
+ else
+ {
+ WSREP_ERROR("wsrep_schema::restore_view() failed.");
+ Wsrep_view ret_view;
+ DBUG_RETURN(ret_view);
+ }
+}
+
+int Wsrep_schema::append_fragment(THD* thd,
+ const wsrep::id& server_id,
+ wsrep::transaction_id transaction_id,
+ wsrep::seqno seqno,
+ int flags,
+ const wsrep::const_buffer& data)
+{
+ DBUG_ENTER("Wsrep_schema::append_fragment");
+ std::ostringstream os;
+ os << server_id;
+ WSREP_DEBUG("Append fragment(%llu) %s, %llu",
+ thd->thread_id,
+ os.str().c_str(),
+ transaction_id.get());
+ /* use private query table list for the duration of fragment storing,
+ populated query table list from "parent DML" may cause problems .e.g
+ for virtual column handling
+ */
+ Query_tables_list query_tables_list_backup;
+ thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
+
+ Wsrep_schema_impl::binlog_off binlog_off(thd);
+ Wsrep_schema_impl::sql_safe_updates sql_safe_updates(thd);
+ Wsrep_schema_impl::init_stmt(thd);
+
+ TABLE* frag_table= 0;
+ if (Wsrep_schema_impl::open_for_write(thd, sr_table_str.c_str(), &frag_table))
+ {
+ trans_rollback_stmt(thd);
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
+ DBUG_RETURN(1);
+ }
+
+ Wsrep_schema_impl::store(frag_table, 0, server_id);
+ Wsrep_schema_impl::store(frag_table, 1, transaction_id.get());
+ Wsrep_schema_impl::store(frag_table, 2, seqno.get());
+ Wsrep_schema_impl::store(frag_table, 3, flags);
+ Wsrep_schema_impl::store(frag_table, 4, data.data(), data.size());
+
+ int error;
+ if ((error= Wsrep_schema_impl::insert(frag_table))) {
+ WSREP_ERROR("Failed to write to frag table: %d", error);
+ trans_rollback_stmt(thd);
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
+ DBUG_RETURN(1);
+ }
+ Wsrep_schema_impl::finish_stmt(thd);
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
+ DBUG_RETURN(0);
+}
+
+int Wsrep_schema::update_fragment_meta(THD* thd,
+ const wsrep::ws_meta& ws_meta)
+{
+ DBUG_ENTER("Wsrep_schema::update_fragment_meta");
+ std::ostringstream os;
+ os << ws_meta.server_id();
+ WSREP_DEBUG("update_frag_seqno(%llu) %s, %llu, seqno %lld",
+ thd->thread_id,
+ os.str().c_str(),
+ ws_meta.transaction_id().get(),
+ ws_meta.seqno().get());
+ DBUG_ASSERT(ws_meta.seqno().is_undefined() == false);
+
+ /* use private query table list for the duration of fragment storing,
+ populated query table list from "parent DML" may cause problems .e.g
+ for virtual column handling
+ */
+ Query_tables_list query_tables_list_backup;
+ thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
+
+ Wsrep_schema_impl::binlog_off binlog_off(thd);
+ Wsrep_schema_impl::sql_safe_updates sql_safe_updates(thd);
+ int error;
+ uchar *key=NULL;
+ key_part_map key_map= 0;
+ TABLE* frag_table= 0;
+
+ Wsrep_schema_impl::init_stmt(thd);
+ if (Wsrep_schema_impl::open_for_write(thd, sr_table_str.c_str(), &frag_table))
+ {
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
+ DBUG_RETURN(1);
+ }
+
+ /* Find record with the given uuid, trx id, and seqno -1 */
+ Wsrep_schema_impl::store(frag_table, 0, ws_meta.server_id());
+ Wsrep_schema_impl::store(frag_table, 1, ws_meta.transaction_id().get());
+ Wsrep_schema_impl::store(frag_table, 2, -1);
+ Wsrep_schema_impl::make_key(frag_table, &key, &key_map, 3);
+
+ if ((error= Wsrep_schema_impl::init_for_index_scan(frag_table,
+ key, key_map)))
+ {
+ if (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND)
+ {
+ WSREP_WARN("Record not found in %s.%s: %d",
+ frag_table->s->db.str,
+ frag_table->s->table_name.str,
+ error);
+ }
+ Wsrep_schema_impl::finish_stmt(thd);
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
+ my_free(key);
+ DBUG_RETURN(1);
+ }
+
+ my_free(key);
+ /* Copy the original record to frag_table->record[1] */
+ store_record(frag_table, record[1]);
+
+ /* Store seqno in frag_table->record[0] and update the row */
+ Wsrep_schema_impl::store(frag_table, 2, ws_meta.seqno().get());
+ if ((error= frag_table->file->ha_update_row(frag_table->record[1],
+ frag_table->record[0]))) {
+ WSREP_ERROR("Error updating record in %s.%s: %d",
+ frag_table->s->db.str,
+ frag_table->s->table_name.str,
+ error);
+ Wsrep_schema_impl::finish_stmt(thd);
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
+ DBUG_RETURN(1);
+ }
+
+ int ret= Wsrep_schema_impl::end_index_scan(frag_table);
+ Wsrep_schema_impl::finish_stmt(thd);
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
+ DBUG_RETURN(ret);
+}
+
+static int remove_fragment(THD* thd,
+ TABLE* frag_table,
+ const wsrep::id& server_id,
+ wsrep::transaction_id transaction_id,
+ wsrep::seqno seqno)
+{
+ WSREP_DEBUG("remove_fragment(%llu) trx %llu, seqno %lld",
+ thd->thread_id,
+ transaction_id.get(),
+ seqno.get());
+ int ret= 0;
+ int error;
+ uchar *key= NULL;
+ key_part_map key_map= 0;
+
+ DBUG_ASSERT(server_id.is_undefined() == false);
+ DBUG_ASSERT(transaction_id.is_undefined() == false);
+ DBUG_ASSERT(seqno.is_undefined() == false);
+
+ /*
+ Remove record with the given uuid, trx id, and seqno.
+ Using a complete key here avoids gap locks.
+ */
+ Wsrep_schema_impl::store(frag_table, 0, server_id);
+ Wsrep_schema_impl::store(frag_table, 1, transaction_id.get());
+ Wsrep_schema_impl::store(frag_table, 2, seqno.get());
+ Wsrep_schema_impl::make_key(frag_table, &key, &key_map, 3);
+
+ if ((error= Wsrep_schema_impl::init_for_index_scan(frag_table,
+ key,
+ key_map)))
+ {
+ if (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND)
+ {
+ WSREP_DEBUG("Record not found in %s.%s:trx %llu, seqno %lld, error %d",
+ frag_table->s->db.str,
+ frag_table->s->table_name.str,
+ transaction_id.get(),
+ seqno.get(),
+ error);
+ }
+ ret= error;
+ }
+ else if (Wsrep_schema_impl::delete_row(frag_table))
+ {
+ ret= 1;
+ }
+
+ if (key)
+ my_free(key);
+ Wsrep_schema_impl::end_index_scan(frag_table);
+ return ret;
+}
+
+int Wsrep_schema::remove_fragments(THD* thd,
+ const wsrep::id& server_id,
+ wsrep::transaction_id transaction_id,
+ const std::vector<wsrep::seqno>& fragments)
+{
+ DBUG_ENTER("Wsrep_schema::remove_fragments");
+ int ret= 0;
+
+ WSREP_DEBUG("Removing %zu fragments", fragments.size());
+ Wsrep_schema_impl::wsrep_off wsrep_off(thd);
+ Wsrep_schema_impl::binlog_off binlog_off(thd);
+ Wsrep_schema_impl::sql_safe_updates sql_safe_updates(thd);
+
+ Query_tables_list query_tables_list_backup;
+ Open_tables_backup open_tables_backup;
+ thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
+ thd->reset_n_backup_open_tables_state(&open_tables_backup);
+
+ TABLE* frag_table= 0;
+ if (Wsrep_schema_impl::open_for_write(thd, sr_table_str.c_str(), &frag_table))
+ {
+ ret= 1;
+ }
+ else
+ {
+ for (std::vector<wsrep::seqno>::const_iterator i= fragments.begin();
+ i != fragments.end(); ++i)
+ {
+ if (remove_fragment(thd,
+ frag_table,
+ server_id,
+ transaction_id, *i))
+ {
+ ret= 1;
+ break;
+ }
+ }
+ }
+ close_thread_tables(thd);
+ thd->restore_backup_open_tables_state(&open_tables_backup);
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
+
+ if (thd->wsrep_cs().mode() == wsrep::client_state::m_local &&
+ !thd->in_multi_stmt_transaction_mode())
+ {
+ /*
+ The ugly part: Locally executing autocommit statement is
+ committing and it has removed a fragment from stable storage.
+ Now calling finish_stmt() will call trans_commit_stmt(), which will
+ actually commit the transaction, what we really don't want
+ to do at this point.
+
+ Doing nothing at this point seems to work ok, this block is
+ intentionally no-op and for documentation purposes only.
+ */
+ }
+ else
+ {
+ Wsrep_schema_impl::finish_stmt(thd);
+ }
+
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_schema::replay_transaction(THD* orig_thd,
+ Relay_log_info* rli,
+ const wsrep::ws_meta& ws_meta,
+ const std::vector<wsrep::seqno>& fragments)
+{
+ DBUG_ENTER("Wsrep_schema::replay_transaction");
+ DBUG_ASSERT(!fragments.empty());
+
+ THD thd(next_thread_id(), true);
+ thd.thread_stack= (orig_thd ? orig_thd->thread_stack :
+ (char*) &thd);
+ wsrep_assign_from_threadvars(&thd);
+
+ Wsrep_schema_impl::wsrep_off wsrep_off(&thd);
+ Wsrep_schema_impl::binlog_off binlog_off(&thd);
+ Wsrep_schema_impl::sql_safe_updates sql_safe_updates(&thd);
+ Wsrep_schema_impl::thd_context_switch thd_context_switch(orig_thd, &thd);
+
+ int ret= 1;
+ int error;
+ TABLE* frag_table= 0;
+ uchar *key=NULL;
+ key_part_map key_map= 0;
+
+ for (std::vector<wsrep::seqno>::const_iterator i= fragments.begin();
+ i != fragments.end(); ++i)
+ {
+ Wsrep_schema_impl::init_stmt(&thd);
+ if ((error= Wsrep_schema_impl::open_for_read(&thd, sr_table_str.c_str(), &frag_table)))
+ {
+ WSREP_WARN("Could not open SR table for read: %d", error);
+ Wsrep_schema_impl::finish_stmt(&thd);
+ DBUG_RETURN(1);
+ }
+
+ Wsrep_schema_impl::store(frag_table, 0, ws_meta.server_id());
+ Wsrep_schema_impl::store(frag_table, 1, ws_meta.transaction_id().get());
+ Wsrep_schema_impl::store(frag_table, 2, i->get());
+ Wsrep_schema_impl::make_key(frag_table, &key, &key_map, 3);
+
+ int error= Wsrep_schema_impl::init_for_index_scan(frag_table,
+ key,
+ key_map);
+ if (error)
+ {
+ WSREP_WARN("Failed to init streaming log table for index scan: %d",
+ error);
+ Wsrep_schema_impl::end_index_scan(frag_table);
+ ret= 1;
+ break;
+ }
+
+ int flags;
+ Wsrep_schema_impl::scan(frag_table, 3, flags);
+ WSREP_DEBUG("replay_fragment(%llu): seqno: %lld flags: %x",
+ ws_meta.transaction_id().get(),
+ i->get(),
+ flags);
+ String buf;
+ frag_table->field[4]->val_str(&buf);
+
+ {
+ Wsrep_schema_impl::thd_context_switch thd_context_switch(&thd, orig_thd);
+
+ ret= wsrep_apply_events(orig_thd, rli, buf.c_ptr_quick(), buf.length());
+ if (ret)
+ {
+ WSREP_WARN("Wsrep_schema::replay_transaction: failed to apply fragments");
+ break;
+ }
+ }
+
+ Wsrep_schema_impl::end_index_scan(frag_table);
+ Wsrep_schema_impl::finish_stmt(&thd);
+
+ Wsrep_schema_impl::init_stmt(&thd);
+
+ if ((error= Wsrep_schema_impl::open_for_write(&thd,
+ sr_table_str.c_str(),
+ &frag_table)))
+ {
+ WSREP_WARN("Could not open SR table for write: %d", error);
+ Wsrep_schema_impl::finish_stmt(&thd);
+ DBUG_RETURN(1);
+ }
+
+ error= Wsrep_schema_impl::init_for_index_scan(frag_table,
+ key,
+ key_map);
+ if (error)
+ {
+ WSREP_WARN("Failed to init streaming log table for index scan: %d",
+ error);
+ Wsrep_schema_impl::end_index_scan(frag_table);
+ ret= 1;
+ break;
+ }
+
+ error= Wsrep_schema_impl::delete_row(frag_table);
+
+ if (error)
+ {
+ WSREP_WARN("Could not delete row from streaming log table: %d", error);
+ Wsrep_schema_impl::end_index_scan(frag_table);
+ ret= 1;
+ break;
+ }
+ Wsrep_schema_impl::end_index_scan(frag_table);
+ Wsrep_schema_impl::finish_stmt(&thd);
+ my_free(key);
+ key= NULL;
+ }
+
+ if (key)
+ my_free(key);
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_schema::recover_sr_transactions(THD *orig_thd)
+{
+ DBUG_ENTER("Wsrep_schema::recover_sr_transactions");
+ THD storage_thd(next_thread_id(), true);
+ storage_thd.thread_stack= (orig_thd ? orig_thd->thread_stack :
+ (char*) &storage_thd);
+ wsrep_assign_from_threadvars(&storage_thd);
+ TABLE* frag_table= 0;
+ TABLE* cluster_table= 0;
+ Wsrep_storage_service storage_service(&storage_thd);
+ Wsrep_schema_impl::binlog_off binlog_off(&storage_thd);
+ Wsrep_schema_impl::wsrep_off wsrep_off(&storage_thd);
+ Wsrep_schema_impl::sql_safe_updates sql_safe_updates(&storage_thd);
+ Wsrep_schema_impl::thd_context_switch thd_context_switch(orig_thd,
+ &storage_thd);
+ Wsrep_server_state& server_state(Wsrep_server_state::instance());
+
+ int ret= 1;
+ int error;
+ wsrep::id cluster_id;
+
+ Wsrep_schema_impl::init_stmt(&storage_thd);
+ storage_thd.wsrep_skip_locking= FALSE;
+ if (Wsrep_schema_impl::open_for_read(&storage_thd,
+ cluster_table_str.c_str(),
+ &cluster_table) ||
+ Wsrep_schema_impl::init_for_scan(cluster_table))
+ {
+ Wsrep_schema_impl::finish_stmt(&storage_thd);
+ DBUG_RETURN(1);
+ }
+
+ if ((error= Wsrep_schema_impl::next_record(cluster_table)))
+ {
+ Wsrep_schema_impl::end_scan(cluster_table);
+ Wsrep_schema_impl::finish_stmt(&storage_thd);
+ trans_commit(&storage_thd);
+ if (error == HA_ERR_END_OF_FILE)
+ {
+ WSREP_INFO("Cluster table is empty, not recovering transactions");
+ DBUG_RETURN(0);
+ }
+ else
+ {
+ WSREP_ERROR("Failed to read cluster table: %d", error);
+ DBUG_RETURN(1);
+ }
+ }
+
+ Wsrep_schema_impl::scan(cluster_table, 0, cluster_id);
+ Wsrep_schema_impl::end_scan(cluster_table);
+ Wsrep_schema_impl::finish_stmt(&storage_thd);
+
+ std::ostringstream os;
+ os << cluster_id;
+ WSREP_INFO("Recovered cluster id %s", os.str().c_str());
+
+ storage_thd.wsrep_skip_locking= TRUE;
+ Wsrep_schema_impl::init_stmt(&storage_thd);
+
+ /*
+ Open the table for reading and writing so that fragments without
+ valid seqno can be deleted.
+ */
+ if (Wsrep_schema_impl::open_for_write(&storage_thd, sr_table_str.c_str(), &frag_table) ||
+ Wsrep_schema_impl::init_for_scan(frag_table))
+ {
+ WSREP_ERROR("Failed to open SR table for write");
+ goto out;
+ }
+
+ while (true)
+ {
+ if ((error= Wsrep_schema_impl::next_record(frag_table)) == 0)
+ {
+ wsrep::id server_id;
+ Wsrep_schema_impl::scan(frag_table, 0, server_id);
+ wsrep::client_id client_id;
+ unsigned long long transaction_id_ull;
+ Wsrep_schema_impl::scan(frag_table, 1, transaction_id_ull);
+ wsrep::transaction_id transaction_id(transaction_id_ull);
+ long long seqno_ll;
+ Wsrep_schema_impl::scan(frag_table, 2, seqno_ll);
+ wsrep::seqno seqno(seqno_ll);
+
+ /* This is possible if the server crashes between inserting the
+ fragment into table and updating the fragment seqno after
+ certification. */
+ if (seqno.is_undefined())
+ {
+ Wsrep_schema_impl::delete_row(frag_table);
+ continue;
+ }
+
+ wsrep::gtid gtid(cluster_id, seqno);
+ int flags;
+ Wsrep_schema_impl::scan(frag_table, 3, flags);
+ String data_str;
+
+ (void)frag_table->field[4]->val_str(&data_str);
+ wsrep::const_buffer data(data_str.c_ptr_quick(), data_str.length());
+ wsrep::ws_meta ws_meta(gtid,
+ wsrep::stid(server_id,
+ transaction_id,
+ client_id),
+ wsrep::seqno::undefined(),
+ flags);
+
+ wsrep::high_priority_service* applier;
+ if (!(applier= server_state.find_streaming_applier(server_id,
+ transaction_id)))
+ {
+ DBUG_ASSERT(wsrep::starts_transaction(flags));
+ applier = wsrep_create_streaming_applier(&storage_thd, "recovery");
+ server_state.start_streaming_applier(server_id, transaction_id,
+ applier);
+ applier->start_transaction(wsrep::ws_handle(transaction_id, 0),
+ ws_meta);
+ }
+ applier->store_globals();
+ wsrep::mutable_buffer unused;
+ applier->apply_write_set(ws_meta, data, unused);
+ applier->after_apply();
+ storage_service.store_globals();
+ }
+ else if (error == HA_ERR_END_OF_FILE)
+ {
+ ret= 0;
+ break;
+ }
+ else
+ {
+ WSREP_ERROR("SR table scan returned error %d", error);
+ break;
+ }
+ }
+ Wsrep_schema_impl::end_scan(frag_table);
+ Wsrep_schema_impl::finish_stmt(&storage_thd);
+ trans_commit(&storage_thd);
+ storage_thd.set_mysys_var(0);
+out:
+ DBUG_RETURN(ret);
+}
diff --git a/sql/wsrep_schema.h b/sql/wsrep_schema.h
new file mode 100644
index 00000000000..36e23998d19
--- /dev/null
+++ b/sql/wsrep_schema.h
@@ -0,0 +1,144 @@
+/* Copyright (C) 2015-2019 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+
+#ifndef WSREP_SCHEMA_H
+#define WSREP_SCHEMA_H
+
+/* wsrep-lib */
+#include "wsrep_types.h"
+
+#include "mysqld.h"
+#include "wsrep_mysqld.h"
+
+/*
+ Forward decls
+*/
+class THD;
+class Relay_log_info;
+struct TABLE;
+struct TABLE_LIST;
+struct st_mysql_lex_string;
+typedef struct st_mysql_lex_string LEX_STRING;
+
+/** Name of the table in `wsrep_schema_str` used for storing streaming
+replication data. In an InnoDB full format, e.g. "database/tablename". */
+extern const char* wsrep_sr_table_name_full;
+
+class Wsrep_schema
+{
+ public:
+
+ Wsrep_schema();
+ ~Wsrep_schema();
+
+ /*
+ Initialize wsrep schema. Storage engines must be running before
+ calling this function.
+ */
+ int init();
+
+ /*
+ Store wsrep view info into wsrep schema.
+ */
+ int store_view(THD*, const Wsrep_view& view);
+
+ /*
+ Restore view info from stable storage.
+ */
+ Wsrep_view restore_view(THD* thd, const Wsrep_id& own_id) const;
+
+ /**
+ Append transaction fragment to fragment storage.
+ Transaction must have been started for THD before this call.
+ In order to make changes durable, transaction must be committed
+ separately after this call.
+
+ @param thd THD object
+ @param server_id Wsrep server identifier
+ @param transaction_id Transaction identifier
+ @param flags Flags for the fragment
+ @param data Fragment data buffer
+
+ @return Zero in case of success, non-zero on failure.
+ */
+ int append_fragment(THD* thd,
+ const wsrep::id& server_id,
+ wsrep::transaction_id transaction_id,
+ wsrep::seqno seqno,
+ int flags,
+ const wsrep::const_buffer& data);
+ /**
+ Update existing fragment meta data. The fragment must have been
+ inserted before using append_fragment().
+
+ @param thd THD object
+ @param ws_meta Wsrep meta data
+
+ @return Zero in case of success, non-zero on failure.
+ */
+ int update_fragment_meta(THD* thd,
+ const wsrep::ws_meta& ws_meta);
+
+ /**
+ Remove fragments from storage. This method must be called
+ inside active transaction. Fragment removal will be committed
+ once the transaction commits.
+
+ @param thd Pointer to THD object
+ @param server_id Identifier of the running server
+ @param transaction_id Identifier of the current transaction
+ @param fragments Vector of fragment seqnos to be removed
+ */
+ int remove_fragments(THD* thd,
+ const wsrep::id& server_id,
+ wsrep::transaction_id transaction_id,
+ const std::vector<wsrep::seqno>& fragments);
+
+ /**
+ Replay a transaction from stored fragments. The caller must have
+ started a transaction for a thd.
+
+ @param thd Pointer to THD object
+ @param ws_meta Write set meta data for commit fragment.
+ @param fragments Vector of fragments to be replayed
+
+ @return Zero on success, non-zero on failure.
+ */
+ int replay_transaction(THD* thd,
+ Relay_log_info* rli,
+ const wsrep::ws_meta& ws_meta,
+ const std::vector<wsrep::seqno>& fragments);
+
+ /**
+ Recover streaming transactions from SR table.
+ This method should be called after storage enignes are initialized.
+ It will scan SR table and replay found streaming transactions.
+
+ @param orig_thd The THD object of the calling thread.
+
+ @return Zero on success, non-zero on failure.
+ */
+ int recover_sr_transactions(THD* orig_thd);
+
+ private:
+ /* Non-copyable */
+ Wsrep_schema(const Wsrep_schema&);
+ Wsrep_schema& operator=(const Wsrep_schema&);
+};
+
+extern Wsrep_schema* wsrep_schema;
+
+#endif /* !WSREP_SCHEMA_H */
diff --git a/sql/wsrep_server_service.cc b/sql/wsrep_server_service.cc
new file mode 100644
index 00000000000..19259a43925
--- /dev/null
+++ b/sql/wsrep_server_service.cc
@@ -0,0 +1,393 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "my_global.h"
+#include "wsrep_server_service.h"
+#include "wsrep_server_state.h"
+#include "wsrep_client_state.h"
+#include "wsrep_client_service.h"
+#include "wsrep_storage_service.h"
+#include "wsrep_high_priority_service.h"
+
+#include "wsrep_sst.h"
+#include "wsrep_xid.h"
+#include "wsrep_mysqld.h"
+#include "wsrep_schema.h"
+#include "wsrep_utils.h"
+#include "wsrep_thd.h"
+
+#include "log.h" /* sql_print_xxx() */
+#include "sql_class.h" /* system variables */
+#include "transaction.h" /* trans_xxx */
+#include "sql_base.h" /* close_thread_tables */
+
+static void init_service_thd(THD* thd, char* thread_stack)
+{
+ thd->thread_stack= thread_stack;
+ thd->real_id= pthread_self();
+ thd->prior_thr_create_utime= thd->start_utime= microsecond_interval_timer();
+ thd->set_command(COM_SLEEP);
+ thd->reset_for_next_command(true);
+ server_threads.insert(thd); // as wsrep_innobase_kill_one_trx() uses find_thread_by_id()
+}
+
+Wsrep_storage_service*
+wsrep_create_storage_service(THD* orig_THD, const char* ctx)
+{
+ THD* thd= new THD(true, true);
+ init_service_thd(thd, orig_THD->thread_stack);
+ WSREP_DEBUG("Created storage service in %s context with thread id %llu",
+ ctx, thd->thread_id);
+ /* Use variables from the current thd attached to client_service.
+ This is because we need to be able to BF abort storage access
+ operations. */
+ wsrep_assign_from_threadvars(thd);
+ return new Wsrep_storage_service(thd);
+}
+
+wsrep::storage_service* Wsrep_server_service::storage_service(
+ wsrep::client_service& client_service)
+{
+ Wsrep_client_service& cs=
+ static_cast<Wsrep_client_service&>(client_service);
+ return wsrep_create_storage_service(cs.m_thd, "local");
+}
+
+wsrep::storage_service* Wsrep_server_service::storage_service(
+ wsrep::high_priority_service& high_priority_service)
+{
+ Wsrep_high_priority_service& hps=
+ static_cast<Wsrep_high_priority_service&>(high_priority_service);
+ return wsrep_create_storage_service(hps.m_thd, "high priority");
+}
+
+void Wsrep_server_service::release_storage_service(
+ wsrep::storage_service* storage_service)
+{
+ Wsrep_storage_service* ss=
+ static_cast<Wsrep_storage_service*>(storage_service);
+ THD* thd= ss->m_thd;
+ wsrep_reset_threadvars(thd);
+ server_threads.erase(thd);
+ delete ss;
+ delete thd;
+}
+
+Wsrep_applier_service*
+wsrep_create_streaming_applier(THD *orig_thd, const char *ctx)
+{
+ /* Reset variables to allow creating new variables in thread local
+ storage for new THD if needed. Note that reset must be done for
+ current_thd, as orig_thd may not be in effect. This may be the case when
+ streaming transaction is BF aborted and streaming applier
+ is created from BF aborter context. */
+ Wsrep_threadvars saved_threadvars(wsrep_save_threadvars());
+ if (saved_threadvars.cur_thd)
+ wsrep_reset_threadvars(saved_threadvars.cur_thd);
+ THD *thd= 0;
+ Wsrep_applier_service *ret= 0;
+ if (!wsrep_create_threadvars() &&
+ (thd= new THD(next_thread_id(), true)))
+ {
+ init_service_thd(thd, orig_thd->thread_stack);
+ wsrep_assign_from_threadvars(thd);
+ WSREP_DEBUG("Created streaming applier service in %s context with "
+ "thread id %llu", ctx, thd->thread_id);
+ if (!(ret= new (std::nothrow) Wsrep_applier_service(thd)))
+ {
+ delete thd;
+ }
+ }
+ /* Restore original thread local storage state before returning. */
+ wsrep_restore_threadvars(saved_threadvars);
+ if (saved_threadvars.cur_thd)
+ wsrep_store_threadvars(saved_threadvars.cur_thd);
+ return ret;
+}
+
+wsrep::high_priority_service*
+Wsrep_server_service::streaming_applier_service(
+ wsrep::client_service& orig_client_service)
+{
+ Wsrep_client_service& orig_cs=
+ static_cast<Wsrep_client_service&>(orig_client_service);
+ return wsrep_create_streaming_applier(orig_cs.m_thd, "local");
+}
+
+wsrep::high_priority_service*
+Wsrep_server_service::streaming_applier_service(
+ wsrep::high_priority_service& orig_high_priority_service)
+{
+ Wsrep_high_priority_service&
+ orig_hps(static_cast<Wsrep_high_priority_service&>(orig_high_priority_service));
+ return wsrep_create_streaming_applier(orig_hps.m_thd, "high priority");
+}
+
+void Wsrep_server_service::release_high_priority_service(wsrep::high_priority_service* high_priority_service)
+{
+ Wsrep_high_priority_service* hps=
+ static_cast<Wsrep_high_priority_service*>(high_priority_service);
+ THD* thd= hps->m_thd;
+ delete hps;
+ wsrep_store_threadvars(thd);
+ server_threads.erase(thd);
+ delete thd;
+ wsrep_delete_threadvars();
+}
+
+void Wsrep_server_service::background_rollback(wsrep::client_state& client_state)
+{
+ Wsrep_client_state& cs= static_cast<Wsrep_client_state&>(client_state);
+ wsrep_fire_rollbacker(cs.thd());
+}
+
+void Wsrep_server_service::bootstrap()
+{
+ wsrep::log_info()
+ << "Bootstrapping a new cluster, setting initial position to "
+ << wsrep::gtid::undefined();
+ wsrep_set_SE_checkpoint(wsrep::gtid::undefined());
+}
+
+void Wsrep_server_service::log_message(enum wsrep::log::level level,
+ const char* message)
+{
+ switch (level)
+ {
+ case wsrep::log::debug:
+ WSREP_DEBUG("%s", message);
+ break;
+ case wsrep::log::info:
+ WSREP_INFO("%s", message);
+ break;
+ case wsrep::log::warning:
+ WSREP_WARN("%s", message);
+ break;
+ case wsrep::log::error:
+ WSREP_ERROR("%s", message);
+ break;
+ case wsrep::log::unknown:
+ WSREP_UNKNOWN("%s", message);
+ break;
+ }
+}
+
+void Wsrep_server_service::log_view(
+ wsrep::high_priority_service* high_priority_service,
+ const wsrep::view& view)
+{
+ Wsrep_high_priority_service* applier=
+ static_cast<Wsrep_high_priority_service*>(high_priority_service);
+ /* Update global system variables */
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ if (wsrep_auto_increment_control && view.own_index() >= 0)
+ {
+ global_system_variables.auto_increment_offset= view.own_index() + 1;
+ global_system_variables.auto_increment_increment= view.members().size();
+ wsrep_protocol_version= view.protocol_version();
+ }
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+
+ /* Update wsrep status variables */
+ mysql_mutex_lock(&LOCK_status);
+ wsrep_cluster_size= view.members().size();
+ wsrep_local_index= view.own_index();
+ std::ostringstream os;
+ os << view.state_id().id();
+ wsrep_update_cluster_state_uuid(os.str().c_str());
+ mysql_mutex_unlock(&LOCK_status);
+ wsrep_config_state->set(view);
+ wsrep_cluster_conf_id= view.view_seqno().get();
+
+ if (view.status() == wsrep::view::primary)
+ {
+ if (applier)
+ {
+ Wsrep_id id;
+ Wsrep_view prev_view= wsrep_schema->restore_view(applier->m_thd, id);
+ bool checkpoint_was_reset= false;
+ if (prev_view.state_id().id() != view.state_id().id())
+ {
+ WSREP_DEBUG("New cluster UUID was generated, resetting position info");
+ wsrep_set_SE_checkpoint(wsrep::gtid::undefined());
+ checkpoint_was_reset= true;
+ }
+
+ if (wsrep_debug)
+ {
+ std::ostringstream os;
+ os << "Storing cluster view:\n" << view;
+ WSREP_INFO("%s", os.str().c_str());
+ DBUG_ASSERT(prev_view.state_id().id() != view.state_id().id() ||
+ view.state_id().seqno().get() >= prev_view.state_id().seqno().get());
+ }
+
+ if (trans_begin(applier->m_thd, MYSQL_START_TRANS_OPT_READ_WRITE))
+ {
+ WSREP_WARN("Failed to start transaction for store view");
+ }
+ else
+ {
+ if (wsrep_schema->store_view(applier->m_thd, view))
+ {
+ WSREP_WARN("Failed to store view");
+ trans_rollback_stmt(applier->m_thd);
+ if (!trans_rollback(applier->m_thd))
+ {
+ close_thread_tables(applier->m_thd);
+ }
+ }
+ else
+ {
+ if (trans_commit(applier->m_thd))
+ {
+ WSREP_WARN("Failed to commit transaction for store view");
+ }
+ }
+ applier->m_thd->release_transactional_locks();
+ }
+
+ /*
+ Backwards compatibility: When running in mixed cluster with
+ Galera 3.x, the provider does not generate unique sequence numbers
+ for views. This condition can be checked by inspecting last
+ committed as returned by the provider. If the last_committed
+ matches to view state_id seqno, the cluster runs in backwards
+ compatibility mode and we skip setting the checkpoint for
+ view.
+ */
+ wsrep::seqno last_committed=
+ Wsrep_server_state::instance().provider().last_committed_gtid().seqno();
+ if (checkpoint_was_reset || last_committed != view.state_id().seqno())
+ {
+ wsrep_set_SE_checkpoint(view.state_id());
+ }
+ DBUG_ASSERT(wsrep_get_SE_checkpoint().id() == view.state_id().id());
+ }
+ else
+ {
+ WSREP_DEBUG("No applier in Wsrep_server_service::log_view(), "
+ "skipping write to wsrep_schema");
+ }
+ }
+}
+
+void Wsrep_server_service::recover_streaming_appliers(wsrep::client_service& cs)
+{
+ Wsrep_client_service& client_service= static_cast<Wsrep_client_service&>(cs);
+ wsrep_recover_sr_from_storage(client_service.m_thd);
+}
+
+void Wsrep_server_service::recover_streaming_appliers(
+ wsrep::high_priority_service& hs)
+{
+ Wsrep_high_priority_service& high_priority_service=
+ static_cast<Wsrep_high_priority_service&>(hs);
+ wsrep_recover_sr_from_storage(high_priority_service.m_thd);
+}
+
+wsrep::view Wsrep_server_service::get_view(wsrep::client_service& c,
+ const wsrep::id& own_id)
+{
+ Wsrep_client_service& cs(static_cast<Wsrep_client_service&>(c));
+ wsrep::view v(wsrep_schema->restore_view(cs.m_thd, own_id));
+ return v;
+}
+
+wsrep::gtid Wsrep_server_service::get_position(wsrep::client_service&)
+{
+ return wsrep_get_SE_checkpoint();
+}
+
+void Wsrep_server_service::set_position(wsrep::client_service& c WSREP_UNUSED,
+ const wsrep::gtid& gtid)
+{
+ Wsrep_client_service& cs WSREP_UNUSED (static_cast<Wsrep_client_service&>(c));
+ DBUG_ASSERT(cs.m_client_state.transaction().state()
+ == wsrep::transaction::s_aborted);
+ // Wait until all prior committers have finished.
+ wsrep::gtid wait_for(gtid.id(),
+ wsrep::seqno(gtid.seqno().get() - 1));
+ if (auto err = Wsrep_server_state::instance().provider()
+ .wait_for_gtid(wait_for, std::numeric_limits<int>::max()))
+ {
+ WSREP_WARN("Wait for gtid returned error %d while waiting for "
+ "prior transactions to commit before setting position", err);
+ }
+ wsrep_set_SE_checkpoint(gtid);
+}
+
+void Wsrep_server_service::log_state_change(
+ enum Wsrep_server_state::state prev_state,
+ enum Wsrep_server_state::state current_state)
+{
+ WSREP_INFO("Server status change %s -> %s",
+ wsrep::to_c_string(prev_state),
+ wsrep::to_c_string(current_state));
+ mysql_mutex_lock(&LOCK_status);
+ switch (current_state)
+ {
+ case Wsrep_server_state::s_synced:
+ wsrep_ready= TRUE;
+ WSREP_INFO("Synchronized with group, ready for connections");
+ /* fall through */
+ case Wsrep_server_state::s_joined:
+ case Wsrep_server_state::s_donor:
+ wsrep_cluster_status= "Primary";
+ break;
+ case Wsrep_server_state::s_connected:
+ wsrep_cluster_status= "non-Primary";
+ wsrep_ready= FALSE;
+ wsrep_connected= TRUE;
+ break;
+ case Wsrep_server_state::s_disconnected:
+ wsrep_ready= FALSE;
+ wsrep_connected= FALSE;
+ wsrep_cluster_status= "Disconnected";
+ break;
+ default:
+ wsrep_ready= FALSE;
+ wsrep_cluster_status= "non-Primary";
+ break;
+ }
+ mysql_mutex_unlock(&LOCK_status);
+ wsrep_config_state->set(current_state);
+}
+
+bool Wsrep_server_service::sst_before_init() const
+{
+ return wsrep_before_SE();
+}
+
+std::string Wsrep_server_service::sst_request()
+{
+ return wsrep_sst_prepare();
+}
+
+int Wsrep_server_service::start_sst(const std::string& sst_request,
+ const wsrep::gtid& gtid,
+ bool bypass)
+{
+ return wsrep_sst_donate(sst_request, gtid, bypass);
+}
+
+int Wsrep_server_service::wait_committing_transactions(int timeout)
+{
+ return wsrep_wait_committing_connections_close(timeout);
+}
+
+void Wsrep_server_service::debug_sync(const char*)
+{
+}
diff --git a/sql/wsrep_server_service.h b/sql/wsrep_server_service.h
new file mode 100644
index 00000000000..168e98206e3
--- /dev/null
+++ b/sql/wsrep_server_service.h
@@ -0,0 +1,101 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef WSREP_SERVER_SERVICE_H
+#define WSREP_SERVER_SERVICE_H
+
+/* wsrep-lib */
+#include "wsrep/server_service.hpp"
+#include "wsrep/exception.hpp" // not_impemented_error(), remove when finished
+#include "wsrep/storage_service.hpp"
+
+class Wsrep_server_state;
+
+
+/* wsrep::server_service interface implementation */
+class Wsrep_server_service : public wsrep::server_service
+{
+public:
+ Wsrep_server_service(Wsrep_server_state& server_state)
+ : m_server_state(server_state)
+ { }
+
+ wsrep::storage_service* storage_service(wsrep::client_service&);
+
+ wsrep::storage_service* storage_service(wsrep::high_priority_service&);
+
+ void release_storage_service(wsrep::storage_service*);
+
+ wsrep::high_priority_service*
+ streaming_applier_service(wsrep::client_service&);
+
+ wsrep::high_priority_service*
+ streaming_applier_service(wsrep::high_priority_service&);
+
+ void release_high_priority_service(wsrep::high_priority_service*);
+
+ void background_rollback(wsrep::client_state&);
+
+ void bootstrap();
+ void log_message(enum wsrep::log::level, const char*);
+
+ void log_dummy_write_set(wsrep::client_state&, const wsrep::ws_meta&)
+ { throw wsrep::not_implemented_error(); }
+
+ void log_view(wsrep::high_priority_service*, const wsrep::view&);
+
+ void recover_streaming_appliers(wsrep::client_service&);
+ void recover_streaming_appliers(wsrep::high_priority_service&);
+ wsrep::view get_view(wsrep::client_service&, const wsrep::id& own_id);
+
+ wsrep::gtid get_position(wsrep::client_service&);
+ void set_position(wsrep::client_service&, const wsrep::gtid&);
+
+ void log_state_change(enum wsrep::server_state::state,
+ enum wsrep::server_state::state);
+
+ bool sst_before_init() const;
+
+ std::string sst_request();
+ int start_sst(const std::string&, const wsrep::gtid&, bool);
+
+ int wait_committing_transactions(int);
+
+ void debug_sync(const char*);
+private:
+ Wsrep_server_state& m_server_state;
+};
+
+/**
+ Helper method to create new streaming applier.
+
+ @param orig_thd Original thd context to copy operation context from.
+ @param ctx Context string for debug logging.
+ */
+class Wsrep_applier_service;
+Wsrep_applier_service*
+wsrep_create_streaming_applier(THD *orig_thd, const char *ctx);
+
+/**
+ Helper method to create new storage service.
+
+ @param orig_thd Original thd context to copy operation context from.
+ @param ctx Context string for debug logging.
+*/
+class Wsrep_storage_service;
+Wsrep_storage_service*
+wsrep_create_storage_service(THD *orig_thd, const char *ctx);
+
+#endif /* WSREP_SERVER_SERVICE */
diff --git a/sql/wsrep_server_state.cc b/sql/wsrep_server_state.cc
new file mode 100644
index 00000000000..ebc4efaabe5
--- /dev/null
+++ b/sql/wsrep_server_state.cc
@@ -0,0 +1,85 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "my_global.h"
+#include "wsrep_api.h"
+#include "wsrep_server_state.h"
+#include "wsrep_binlog.h" /* init/deinit group commit */
+
+mysql_mutex_t LOCK_wsrep_server_state;
+mysql_cond_t COND_wsrep_server_state;
+
+#ifdef HAVE_PSI_INTERFACE
+PSI_mutex_key key_LOCK_wsrep_server_state;
+PSI_cond_key key_COND_wsrep_server_state;
+#endif
+
+Wsrep_server_state::Wsrep_server_state(const std::string& name,
+ const std::string& incoming_address,
+ const std::string& address,
+ const std::string& working_dir,
+ const wsrep::gtid& initial_position,
+ int max_protocol_version)
+ : wsrep::server_state(m_mutex,
+ m_cond,
+ m_service,
+ NULL,
+ name,
+ incoming_address,
+ address,
+ working_dir,
+ initial_position,
+ max_protocol_version,
+ wsrep::server_state::rm_sync)
+ , m_mutex(LOCK_wsrep_server_state)
+ , m_cond(COND_wsrep_server_state)
+ , m_service(*this)
+{ }
+
+Wsrep_server_state::~Wsrep_server_state()
+{ }
+
+void Wsrep_server_state::init_once(const std::string& name,
+ const std::string& incoming_address,
+ const std::string& address,
+ const std::string& working_dir,
+ const wsrep::gtid& initial_position,
+ int max_protocol_version)
+{
+ if (m_instance == 0)
+ {
+ mysql_mutex_init(key_LOCK_wsrep_server_state, &LOCK_wsrep_server_state,
+ MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_wsrep_server_state, &COND_wsrep_server_state, 0);
+ m_instance = new Wsrep_server_state(name,
+ incoming_address,
+ address,
+ working_dir,
+ initial_position,
+ max_protocol_version);
+ }
+}
+
+void Wsrep_server_state::destroy()
+{
+
+ if (m_instance)
+ {
+ delete m_instance;
+ m_instance= 0;
+ mysql_mutex_destroy(&LOCK_wsrep_server_state);
+ mysql_cond_destroy(&COND_wsrep_server_state);
+ }
+}
diff --git a/sql/wsrep_server_state.h b/sql/wsrep_server_state.h
new file mode 100644
index 00000000000..1ef937300f6
--- /dev/null
+++ b/sql/wsrep_server_state.h
@@ -0,0 +1,74 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef WSREP_SERVER_STATE_H
+#define WSREP_SERVER_STATE_H
+
+/* wsrep-lib */
+#include "wsrep/server_state.hpp"
+#include "wsrep/provider.hpp"
+
+/* implementation */
+#include "wsrep_server_service.h"
+#include "wsrep_mutex.h"
+#include "wsrep_condition_variable.h"
+
+class Wsrep_server_state : public wsrep::server_state
+{
+public:
+ static void init_once(const std::string& name,
+ const std::string& incoming_address,
+ const std::string& address,
+ const std::string& working_dir,
+ const wsrep::gtid& initial_position,
+ int max_protocol_version);
+ static void destroy();
+
+ static Wsrep_server_state& instance()
+ {
+ return *m_instance;
+ }
+
+ static bool is_inited()
+ {
+ return (m_instance != NULL);
+ }
+
+ static wsrep::provider& get_provider()
+ {
+ return instance().provider();
+ }
+
+ static bool has_capability(int capability)
+ {
+ return (get_provider().capabilities() & capability);
+ }
+
+private:
+ Wsrep_server_state(const std::string& name,
+ const std::string& incoming_address,
+ const std::string& address,
+ const std::string& working_dir,
+ const wsrep::gtid& initial_position,
+ int max_protocol_version);
+ ~Wsrep_server_state();
+ Wsrep_mutex m_mutex;
+ Wsrep_condition_variable m_cond;
+ Wsrep_server_service m_service;
+ static Wsrep_server_state* m_instance;
+
+};
+
+#endif // WSREP_SERVER_STATE_H
diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc
index f16050866a6..09b388f0868 100644
--- a/sql/wsrep_sst.cc
+++ b/sql/wsrep_sst.cc
@@ -28,6 +28,8 @@
#include "wsrep_priv.h"
#include "wsrep_utils.h"
#include "wsrep_xid.h"
+#include "wsrep_thd.h"
+
#include <cstdio>
#include <cstdlib>
@@ -36,16 +38,136 @@
static char wsrep_defaults_file[FN_REFLEN * 2 + 10 + 30 +
sizeof(WSREP_SST_OPT_CONF) +
sizeof(WSREP_SST_OPT_CONF_SUFFIX) +
- sizeof(WSREP_SST_OPT_CONF_EXTRA)] = {0};
+ sizeof(WSREP_SST_OPT_CONF_EXTRA)]= {0};
-const char* wsrep_sst_method = WSREP_SST_DEFAULT;
-const char* wsrep_sst_receive_address = WSREP_SST_ADDRESS_AUTO;
-const char* wsrep_sst_donor = "";
-const char* wsrep_sst_auth = NULL;
+const char* wsrep_sst_method = WSREP_SST_DEFAULT;
+const char* wsrep_sst_receive_address= WSREP_SST_ADDRESS_AUTO;
+const char* wsrep_sst_donor = "";
+const char* wsrep_sst_auth = NULL;
// container for real auth string
-static const char* sst_auth_real = NULL;
-my_bool wsrep_sst_donor_rejects_queries = FALSE;
+static const char* sst_auth_real = NULL;
+my_bool wsrep_sst_donor_rejects_queries= FALSE;
+
+#define WSREP_EXTEND_TIMEOUT_INTERVAL 60
+#define WSREP_TIMEDWAIT_SECONDS 30
+
+bool sst_joiner_completed = false;
+bool sst_donor_completed = false;
+
+struct sst_thread_arg
+{
+ const char* cmd;
+ char** env;
+ char* ret_str;
+ int err;
+ mysql_mutex_t lock;
+ mysql_cond_t cond;
+
+ sst_thread_arg (const char* c, char** e)
+ : cmd(c), env(e), ret_str(0), err(-1)
+ {
+ mysql_mutex_init(key_LOCK_wsrep_sst_thread, &lock, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_wsrep_sst_thread, &cond, NULL);
+ }
+
+ ~sst_thread_arg()
+ {
+ mysql_cond_destroy (&cond);
+ mysql_mutex_unlock (&lock);
+ mysql_mutex_destroy (&lock);
+ }
+};
+
+static void wsrep_donor_monitor_end(void)
+{
+ mysql_mutex_lock(&LOCK_wsrep_donor_monitor);
+ sst_donor_completed= true;
+ mysql_cond_signal(&COND_wsrep_donor_monitor);
+ mysql_mutex_unlock(&LOCK_wsrep_donor_monitor);
+}
+
+static void wsrep_joiner_monitor_end(void)
+{
+ mysql_mutex_lock(&LOCK_wsrep_joiner_monitor);
+ sst_joiner_completed= true;
+ mysql_cond_signal(&COND_wsrep_joiner_monitor);
+ mysql_mutex_unlock(&LOCK_wsrep_joiner_monitor);
+}
+
+static void* wsrep_sst_donor_monitor_thread(void *arg __attribute__((unused)))
+{
+ int ret= 0;
+ unsigned long time_waited= 0;
+
+ mysql_mutex_lock(&LOCK_wsrep_donor_monitor);
+
+ WSREP_INFO("Donor monitor thread started to monitor");
+
+ wsp::thd thd(FALSE); // we turn off wsrep_on for this THD so that it can
+ // operate with wsrep_ready == OFF
+
+ while (!sst_donor_completed)
+ {
+ timespec ts;
+ set_timespec(ts, WSREP_TIMEDWAIT_SECONDS);
+ time_t start_time= time(NULL);
+ ret= mysql_cond_timedwait(&COND_wsrep_donor_monitor, &LOCK_wsrep_donor_monitor, &ts);
+ time_t end_time= time(NULL);
+ time_waited+= difftime(end_time, start_time);
+
+ if (ret == ETIMEDOUT && !sst_donor_completed)
+ {
+ WSREP_DEBUG("Donor waited %lu sec, extending systemd startup timeout as SST"
+ "is not completed",
+ time_waited);
+ service_manager_extend_timeout(WSREP_EXTEND_TIMEOUT_INTERVAL,
+ "WSREP state transfer ongoing...");
+ }
+ }
+
+ WSREP_INFO("Donor monitor thread ended with total time %lu sec", time_waited);
+ mysql_mutex_unlock(&LOCK_wsrep_donor_monitor);
+
+ return NULL;
+}
+
+static void* wsrep_sst_joiner_monitor_thread(void *arg __attribute__((unused)))
+{
+ int ret= 0;
+ unsigned long time_waited= 0;
+
+ mysql_mutex_lock(&LOCK_wsrep_joiner_monitor);
+
+ WSREP_INFO("Joiner monitor thread started to monitor");
+
+ wsp::thd thd(FALSE); // we turn off wsrep_on for this THD so that it can
+ // operate with wsrep_ready == OFF
+
+ while (!sst_joiner_completed)
+ {
+ timespec ts;
+ set_timespec(ts, WSREP_TIMEDWAIT_SECONDS);
+ time_t start_time= time(NULL);
+ ret= mysql_cond_timedwait(&COND_wsrep_joiner_monitor, &LOCK_wsrep_joiner_monitor, &ts);
+ time_t end_time= time(NULL);
+ time_waited+= difftime(end_time, start_time);
+
+ if (ret == ETIMEDOUT && !sst_joiner_completed)
+ {
+ WSREP_DEBUG("Joiner waited %lu sec, extending systemd startup timeout as SST"
+ "is not completed",
+ time_waited);
+ service_manager_extend_timeout(WSREP_EXTEND_TIMEOUT_INTERVAL,
+ "WSREP state transfer ongoing...");
+ }
+ }
+
+ WSREP_INFO("Joiner monitor thread ended with total time %lu sec", time_waited);
+ mysql_mutex_unlock(&LOCK_wsrep_joiner_monitor);
+
+ return NULL;
+}
bool wsrep_sst_method_check (sys_var *self, THD* thd, set_var* var)
{
@@ -61,7 +183,7 @@ bool wsrep_sst_method_check (sys_var *self, THD* thd, set_var* var)
return 0;
}
-static const char* data_home_dir = NULL;
+static const char* data_home_dir;
void wsrep_set_data_home_dir(const char *data_dir)
{
@@ -135,7 +257,7 @@ static bool sst_auth_real_set (const char* value)
{
// set sst_auth_real
if (sst_auth_real) { my_free((void *) sst_auth_real); }
- sst_auth_real = v;
+ sst_auth_real= v;
// mask wsrep_sst_auth
if (strlen(sst_auth_real))
@@ -182,6 +304,7 @@ bool wsrep_sst_donor_update (sys_var *self, THD* thd, enum_var_type type)
return 0;
}
+
bool wsrep_before_SE()
{
return (wsrep_provider != NULL
@@ -190,227 +313,114 @@ bool wsrep_before_SE()
&& strcmp (wsrep_sst_method, WSREP_SST_MYSQLDUMP));
}
-static bool sst_complete = false;
-static bool sst_needed = false;
-
-#define WSREP_EXTEND_TIMEOUT_INTERVAL 30
-#define WSREP_TIMEDWAIT_SECONDS 10
-
-void wsrep_sst_grab ()
-{
- WSREP_INFO("wsrep_sst_grab()");
- if (mysql_mutex_lock (&LOCK_wsrep_sst)) abort();
- sst_complete = false;
- mysql_mutex_unlock (&LOCK_wsrep_sst);
-}
-
-// Wait for end of SST
-bool wsrep_sst_wait ()
-{
- double total_wtime = 0;
-
- if (mysql_mutex_lock (&LOCK_wsrep_sst))
- abort();
-
- WSREP_INFO("Waiting for SST to complete.");
-
- while (!sst_complete)
- {
- struct timespec wtime;
- set_timespec(wtime, WSREP_TIMEDWAIT_SECONDS);
- time_t start_time = time(NULL);
- mysql_cond_timedwait (&COND_wsrep_sst, &LOCK_wsrep_sst, &wtime);
- time_t end_time = time(NULL);
-
- if (!sst_complete)
- {
- total_wtime += difftime(end_time, start_time);
- WSREP_DEBUG("Waiting for SST to complete. current seqno: %" PRId64 " waited %f secs.", local_seqno, total_wtime);
- service_manager_extend_timeout(WSREP_EXTEND_TIMEOUT_INTERVAL,
- "WSREP state transfer ongoing, current seqno: %" PRId64 " waited %f secs", local_seqno, total_wtime);
- }
- }
-
- if (local_seqno >= 0)
- {
- WSREP_INFO("SST complete, seqno: %lld", (long long) local_seqno);
- }
- else
- {
- WSREP_ERROR("SST failed: %d (%s)",
- int(-local_seqno), strerror(-local_seqno));
- }
-
- mysql_mutex_unlock (&LOCK_wsrep_sst);
-
- return (local_seqno >= 0);
-}
-
// Signal end of SST
-void wsrep_sst_complete (const wsrep_uuid_t* sst_uuid,
- wsrep_seqno_t sst_seqno,
- bool needed)
+static bool wsrep_sst_complete (THD* thd,
+ int const rcode,
+ wsrep::gtid const sst_gtid)
{
- if (mysql_mutex_lock (&LOCK_wsrep_sst)) abort();
- if (!sst_complete)
- {
- sst_complete = true;
- sst_needed = needed;
- local_uuid = *sst_uuid;
- local_seqno = sst_seqno;
- mysql_cond_signal (&COND_wsrep_sst);
+ Wsrep_client_service client_service(thd, thd->wsrep_cs());
+ Wsrep_server_state& server_state= Wsrep_server_state::instance();
+ enum wsrep::server_state::state state= server_state.state();
+ bool failed= false;
+ char start_pos_buf[FN_REFLEN];
+ ssize_t len= wsrep::print_to_c_str(sst_gtid, start_pos_buf, FN_REFLEN-1);
+ start_pos_buf[len]='\0';
+
+ // Do not call sst_received if we are not in joiner or
+ // initialized state on server. This is because it
+ // assumes we are on those states. Give error if we are
+ // in incorrect state.
+ if ((state == Wsrep_server_state::s_joiner ||
+ state == Wsrep_server_state::s_initialized))
+ {
+ Wsrep_server_state::instance().sst_received(client_service,
+ rcode);
+ WSREP_INFO("SST succeeded for position %s", start_pos_buf);
}
else
{
- /* This can happen when called from wsrep_synced_cb().
- At the moment there is no way to check there
- if main thread is still waiting for signal,
- so wsrep_sst_complete() is called from there
- each time wsrep_ready changes from FALSE -> TRUE.
- */
- WSREP_DEBUG("Nobody is waiting for SST.");
+ WSREP_ERROR("SST failed for position %s initialized %d server_state %s",
+ start_pos_buf,
+ server_state.is_initialized(),
+ wsrep::to_c_string(state));
+ failed= true;
}
- mysql_mutex_unlock (&LOCK_wsrep_sst);
+
+ wsrep_joiner_monitor_end();
+ return failed;
}
-/*
+ /*
If wsrep provider is loaded, inform that the new state snapshot
has been received. Also update the local checkpoint.
- @param wsrep [IN] wsrep handle
+ @param thd [IN]
@param uuid [IN] Initial state UUID
@param seqno [IN] Initial state sequence number
@param state [IN] Always NULL, also ignored by wsrep provider (?)
@param state_len [IN] Always 0, also ignored by wsrep provider (?)
- @param implicit [IN] Whether invoked implicitly due to SST
- (true) or explicitly because if change
- in wsrep_start_position by user (false).
- @return false Success
- true Error
-
+ @return true when successful, false if error
*/
-bool wsrep_sst_received (wsrep_t* const wsrep,
- const wsrep_uuid_t& uuid,
- const wsrep_seqno_t seqno,
- const void* const state,
- const size_t state_len,
- const bool implicit)
+bool wsrep_sst_received (THD* thd,
+ const wsrep_uuid_t& uuid,
+ wsrep_seqno_t const seqno,
+ const void* const state,
+ size_t const state_len)
{
+ bool error= false;
/*
To keep track of whether the local uuid:seqno should be updated. Also, note
that local state (uuid:seqno) is updated/checkpointed only after we get an
OK from wsrep provider. By doing so, the values remain consistent across
the server & wsrep provider.
*/
- bool do_update= false;
-
- // Get the locally stored uuid:seqno.
- if (wsrep_get_SE_checkpoint(local_uuid, local_seqno))
- {
- return true;
- }
-
- if (memcmp(&local_uuid, &uuid, sizeof(wsrep_uuid_t)) ||
- local_seqno < seqno || seqno < 0)
- {
- do_update= true;
- }
- else if (local_seqno > seqno)
- {
- WSREP_WARN("SST position can't be set in past. Requested: %lld, Current: "
- " %lld.", (long long)seqno, (long long)local_seqno);
/*
- If we are here because of SET command, simply return true (error) instead of
- aborting.
+ TODO: Handle backwards compatibility. WSREP API v25 does not have
+ wsrep schema.
*/
- if (implicit)
- {
- WSREP_WARN("Can't continue.");
- unireg_abort(1);
- }
- else
- {
- return true;
+ /*
+ Logical SST methods (mysqldump etc) don't update InnoDB sys header.
+ Reset the SE checkpoint before recovering view in order to avoid
+ sanity check failure.
+ */
+ wsrep::gtid const sst_gtid(wsrep::id(uuid.data, sizeof(uuid.data)),
+ wsrep::seqno(seqno));
+
+ if (!wsrep_before_SE()) {
+ wsrep_set_SE_checkpoint(wsrep::gtid::undefined());
+ wsrep_set_SE_checkpoint(sst_gtid);
}
- }
+ wsrep_verify_SE_checkpoint(uuid, seqno);
-#ifdef GTID_SUPPORT
- wsrep_init_sidno(uuid);
-#endif /* GTID_SUPPORT */
-
- if (wsrep)
- {
- int const rcode(seqno < 0 ? seqno : 0);
- wsrep_gtid_t const state_id= {uuid,
- (rcode ? WSREP_SEQNO_UNDEFINED : seqno)};
-
- wsrep_status_t ret= wsrep->sst_received(wsrep, &state_id, state,
- state_len, rcode);
-
- if (ret != WSREP_OK)
- {
- return true;
+ /*
+ Both wsrep_init_SR() and wsrep_recover_view() may use
+ wsrep thread pool. Restore original thd context before returning.
+ */
+ if (thd) {
+ wsrep_store_threadvars(thd);
+ }
+ else {
+ my_pthread_setspecific_ptr(THR_THD, NULL);
}
- }
- // Now is the good time to update the local state and checkpoint.
- if (do_update)
- {
- if (wsrep_set_SE_checkpoint(uuid, seqno))
+ /* During sst WSREP(thd) is not yet set for joiner. */
+ if (WSREP_ON)
{
- return true;
+ int const rcode(seqno < 0 ? seqno : 0);
+ error= wsrep_sst_complete(thd,rcode, sst_gtid);
}
- local_uuid= uuid;
- local_seqno= seqno;
- }
-
- return false;
+ return error;
}
-// Let applier threads to continue
-bool wsrep_sst_continue ()
-{
- if (sst_needed)
- {
- WSREP_INFO("Signalling provider to continue.");
- return wsrep_sst_received (wsrep, local_uuid, local_seqno, NULL, 0, true);
- }
- return false;
-}
-
-struct sst_thread_arg
-{
- const char* cmd;
- char** env;
- char* ret_str;
- int err;
- mysql_mutex_t lock;
- mysql_cond_t cond;
-
- sst_thread_arg (const char* c, char** e)
- : cmd(c), env(e), ret_str(0), err(-1)
- {
- mysql_mutex_init(key_LOCK_wsrep_sst_thread, &lock, MY_MUTEX_INIT_FAST);
- mysql_cond_init(key_COND_wsrep_sst_thread, &cond, NULL);
- }
-
- ~sst_thread_arg()
- {
- mysql_cond_destroy (&cond);
- mysql_mutex_unlock (&lock);
- mysql_mutex_destroy (&lock);
- }
-};
-
static int sst_scan_uuid_seqno (const char* str,
wsrep_uuid_t* uuid, wsrep_seqno_t* seqno)
{
- int offt = wsrep_uuid_scan (str, strlen(str), uuid);
+ int offt= wsrep_uuid_scan (str, strlen(str), uuid);
errno= 0; /* Reset the errno */
if (offt > 0 && strlen(str) > (unsigned int)offt && ':' == str[offt])
{
- *seqno = strtoll (str + offt + 1, NULL, 10);
+ *seqno= strtoll (str + offt + 1, NULL, 10);
if (*seqno != LLONG_MAX || errno != ERANGE)
{
return 0;
@@ -418,7 +428,7 @@ static int sst_scan_uuid_seqno (const char* str,
}
WSREP_ERROR("Failed to parse uuid:seqno pair: '%s'", str);
- return EINVAL;
+ return -EINVAL;
}
// get rid of trailing \n
@@ -428,8 +438,8 @@ static char* my_fgets (char* buf, size_t buf_len, FILE* stream)
if (ret)
{
- size_t len = strlen(ret);
- if (len > 0 && ret[len - 1] == '\n') ret[len - 1] = '\0';
+ size_t len= strlen(ret);
+ if (len > 0 && ret[len - 1] == '\n') ret[len - 1]= '\0';
}
return ret;
@@ -492,7 +502,8 @@ static int generate_binlog_index_opt_val(char** ret)
{
DBUG_ASSERT(ret);
*ret= NULL;
- if (opt_binlog_index_name) {
+ if (opt_binlog_index_name)
+ {
*ret= strcmp(opt_binlog_index_name, "0") ?
generate_name_value(WSREP_SST_OPT_BINLOG_INDEX,
opt_binlog_index_name) :
@@ -512,9 +523,10 @@ static void* sst_joiner_thread (void* a)
int err= 1;
{
- const char magic[] = "ready";
- const size_t magic_len = sizeof(magic) - 1;
- const size_t out_len = 512;
+ THD* thd;
+ const char magic[]= "ready";
+ const size_t magic_len= sizeof(magic) - 1;
+ const size_t out_len= 512;
char out[out_len];
WSREP_INFO("Running: '%s'", arg->cmd);
@@ -531,29 +543,31 @@ static void* sst_joiner_thread (void* a)
WSREP_ERROR("Failed to read '%s <addr>' from: %s\n\tRead: '%s'",
magic, arg->cmd, tmp);
proc.wait();
- if (proc.error()) err = proc.error();
+ if (proc.error()) err= proc.error();
}
else
{
- err = 0;
+ err= 0;
}
}
else
{
- err = proc.error();
+ err= proc.error();
WSREP_ERROR("Failed to execute: %s : %d (%s)",
arg->cmd, err, strerror(err));
}
- // signal sst_prepare thread with ret code,
- // it will go on sending SST request
+ /*
+ signal sst_prepare thread with ret code,
+ it will go on sending SST request
+ */
mysql_mutex_lock (&arg->lock);
if (!err)
{
- arg->ret_str = strdup (out + magic_len + 1);
- if (!arg->ret_str) err = ENOMEM;
+ arg->ret_str= strdup (out + magic_len + 1);
+ if (!arg->ret_str) err= ENOMEM;
}
- arg->err = -err;
+ arg->err= -err;
mysql_cond_signal (&arg->cond);
mysql_mutex_unlock (&arg->lock); //! @note arg is unusable after that.
@@ -561,20 +575,22 @@ static void* sst_joiner_thread (void* a)
* initializer thread to ensure single thread of
* shutdown. */
- wsrep_uuid_t ret_uuid = WSREP_UUID_UNDEFINED;
- wsrep_seqno_t ret_seqno = WSREP_SEQNO_UNDEFINED;
+ wsrep_uuid_t ret_uuid = WSREP_UUID_UNDEFINED;
+ wsrep_seqno_t ret_seqno= WSREP_SEQNO_UNDEFINED;
- // in case of successfull receiver start, wait for SST completion/end
- char* tmp = my_fgets (out, out_len, proc.pipe());
+ // in case of successfull receiver start, wait for SST
+ // completion/end
+ char* tmp= my_fgets (out, out_len, proc.pipe());
proc.wait();
+
err= EINVAL;
if (!tmp)
{
WSREP_ERROR("Failed to read uuid:seqno and wsrep_gtid_domain_id from "
"joiner script.");
- if (proc.error()) err = proc.error();
+ if (proc.error()) err= proc.error();
}
else
{
@@ -582,7 +598,14 @@ static void* sst_joiner_thread (void* a)
const char *pos= strchr(out, ' ');
if (!pos) {
- // There is no wsrep_gtid_domain_id (some older version SST script?).
+
+ if (wsrep_gtid_mode)
+ {
+ // There is no wsrep_gtid_domain_id (some older version SST script?).
+ WSREP_WARN("Did not find domain ID from SST script output '%s'. "
+ "Domain ID must be set manually to keep binlog consistent",
+ out);
+ }
err= sst_scan_uuid_seqno (out, &ret_uuid, &ret_seqno);
} else {
@@ -618,14 +641,60 @@ static void* sst_joiner_thread (void* a)
err:
+ wsrep::gtid ret_gtid;
+
if (err)
{
- ret_uuid= WSREP_UUID_UNDEFINED;
- ret_seqno= -err;
+ ret_gtid= wsrep::gtid::undefined();
+ }
+ else
+ {
+ ret_gtid= wsrep::gtid(wsrep::id(ret_uuid.data, sizeof(ret_uuid.data)),
+ wsrep::seqno(ret_seqno));
+ }
+
+ /*
+ Tell initializer thread that SST is complete
+ For that initialize a THD
+ */
+ if (my_thread_init())
+ {
+ WSREP_ERROR("my_thread_init() failed, can't signal end of SST. "
+ "Aborting.");
+ unireg_abort(1);
+ }
+
+ thd= new THD(next_thread_id());
+
+ if (!thd)
+ {
+ WSREP_ERROR("Failed to allocate THD to restore view from local state, "
+ "can't signal end of SST. Aborting.");
+ unireg_abort(1);
}
- // Tell initializer thread that SST is complete
- wsrep_sst_complete (&ret_uuid, ret_seqno, true);
+ thd->thread_stack= (char*) &thd;
+ thd->security_ctx->skip_grants();
+ thd->system_thread= SYSTEM_THREAD_GENERIC;
+ thd->real_id= pthread_self();
+
+ wsrep_assign_from_threadvars(thd);
+ wsrep_store_threadvars(thd);
+
+ /* */
+ thd->variables.wsrep_on = 0;
+ /* No binlogging */
+ thd->variables.sql_log_bin = 0;
+ thd->variables.option_bits &= ~OPTION_BIN_LOG;
+ /* No general log */
+ thd->variables.option_bits |= OPTION_LOG_OFF;
+ /* Read committed isolation to avoid gap locking */
+ thd->variables.tx_isolation= ISO_READ_COMMITTED;
+
+ wsrep_sst_complete (thd, -err, ret_gtid);
+
+ delete thd;
+ my_thread_end();
}
return NULL;
@@ -1100,29 +1169,47 @@ static ssize_t sst_prepare_other (const char* method,
}
}
- pthread_t tmp;
+ pthread_t tmp, monitor;
sst_thread_arg arg(cmd_str(), env());
+
mysql_mutex_lock (&arg.lock);
- ret = mysql_thread_create (key_wsrep_sst_joiner, &tmp, NULL, sst_joiner_thread, &arg);
+
+ ret = mysql_thread_create (key_wsrep_sst_joiner_monitor, &monitor, NULL, wsrep_sst_joiner_monitor_thread, NULL);
+
if (ret)
{
WSREP_ERROR("sst_prepare_other(): mysql_thread_create() failed: %d (%s)",
ret, strerror(ret));
return -ret;
}
+
+ sst_joiner_completed= false;
+
+ ret= mysql_thread_create (key_wsrep_sst_joiner, &tmp, NULL, sst_joiner_thread, &arg);
+
+ if (ret)
+ {
+ WSREP_ERROR("sst_prepare_other(): mysql_thread_create() failed: %d (%s)",
+ ret, strerror(ret));
+
+ pthread_detach(monitor);
+ return -ret;
+ }
+
mysql_cond_wait (&arg.cond, &arg.lock);
*addr_out= arg.ret_str;
if (!arg.err)
- ret = strlen(*addr_out);
+ ret= strlen(*addr_out);
else
{
assert (arg.err < 0);
- ret = arg.err;
+ ret= arg.err;
}
pthread_detach (tmp);
+ pthread_detach (monitor);
return ret;
}
@@ -1133,12 +1220,12 @@ extern uint mysqld_port;
static ssize_t sst_prepare_mysqldump (const char* addr_in,
const char** addr_out)
{
- ssize_t ret = strlen (addr_in);
+ ssize_t ret= strlen (addr_in);
if (!strrchr(addr_in, ':'))
{
- ssize_t s = ret + 7;
- char* tmp = (char*) malloc (s);
+ ssize_t s= ret + 7;
+ char* tmp= (char*) malloc (s);
if (tmp)
{
@@ -1149,7 +1236,7 @@ static ssize_t sst_prepare_mysqldump (const char* addr_in,
*addr_out= tmp;
return ret;
}
- if (ret > 0) /* buffer too short */ ret = -EMSGSIZE;
+ if (ret > 0) /* buffer too short */ ret= -EMSGSIZE;
free (tmp);
}
else {
@@ -1163,35 +1250,38 @@ static ssize_t sst_prepare_mysqldump (const char* addr_in,
*addr_out= addr_in;
}
+ pthread_t monitor;
+ ret = mysql_thread_create (key_wsrep_sst_joiner_monitor, &monitor, NULL, wsrep_sst_joiner_monitor_thread, NULL);
+
+ if (ret)
+ {
+ WSREP_ERROR("sst_prepare_other(): mysql_thread_create() failed: %d (%s)",
+ ret, strerror(ret));
+ return -ret;
+ }
+
+ sst_joiner_completed= false;
+ pthread_detach (monitor);
+
return ret;
}
-static bool SE_initialized = false;
-
-ssize_t wsrep_sst_prepare (void** msg)
+std::string wsrep_sst_prepare()
{
+ const ssize_t ip_max= 256;
+ char ip_buf[ip_max];
const char* addr_in= NULL;
const char* addr_out= NULL;
const char* method;
if (!strcmp(wsrep_sst_method, WSREP_SST_SKIP))
{
- ssize_t ret = strlen(WSREP_STATE_TRANSFER_TRIVIAL) + 1;
- *msg = strdup(WSREP_STATE_TRANSFER_TRIVIAL);
- if (!msg)
- {
- WSREP_ERROR("Could not allocate %zd bytes for state request", ret);
- unireg_abort(1);
- }
- return ret;
+ return WSREP_STATE_TRANSFER_TRIVIAL;
}
/*
Figure out SST receive address. Common for all SST methods.
*/
- char ip_buf[256];
- const ssize_t ip_max= sizeof(ip_buf);
-
// Attempt 1: wsrep_sst_receive_address
if (wsrep_sst_receive_address &&
strcmp (wsrep_sst_receive_address, WSREP_SST_ADDRESS_AUTO))
@@ -1208,7 +1298,7 @@ ssize_t wsrep_sst_prepare (void** msg)
{
WSREP_ERROR("Could not parse wsrep_node_address : %s",
wsrep_node_address);
- unireg_abort(1);
+ throw wsrep::runtime_error("Failed to prepare for SST. Unrecoverable");
}
memcpy(ip_buf, addr.get_address(), addr.get_address_len());
addr_in= ip_buf;
@@ -1226,7 +1316,7 @@ ssize_t wsrep_sst_prepare (void** msg)
{
WSREP_ERROR("Failed to guess address to accept state transfer. "
"wsrep_sst_receive_address must be set manually.");
- unireg_abort(1);
+ throw wsrep::runtime_error("Could not prepare state transfer request");
}
}
@@ -1235,12 +1325,16 @@ ssize_t wsrep_sst_prepare (void** msg)
if (!strcmp(method, WSREP_SST_MYSQLDUMP))
{
addr_len= sst_prepare_mysqldump (addr_in, &addr_out);
- if (addr_len < 0) unireg_abort(1);
+ if (addr_len < 0)
+ {
+ throw wsrep::runtime_error("Could not prepare mysqldimp address");
+ }
}
else
{
/*! A heuristic workaround until we learn how to stop and start engines */
- if (SE_initialized)
+ if (Wsrep_server_state::instance().is_initialized() &&
+ Wsrep_server_state::instance().state() == Wsrep_server_state::s_joiner)
{
if (!strcmp(method, WSREP_SST_XTRABACKUP) ||
!strcmp(method, WSREP_SST_XTRABACKUPV2))
@@ -1259,8 +1353,7 @@ ssize_t wsrep_sst_prepare (void** msg)
"if other means of state transfer are unavailable. "
"In that case you will need to restart the server.",
method);
- *msg = 0;
- return 0;
+ return "";
}
addr_len = sst_prepare_other (method, sst_auth_real,
@@ -1269,37 +1362,28 @@ ssize_t wsrep_sst_prepare (void** msg)
{
WSREP_ERROR("Failed to prepare for '%s' SST. Unrecoverable.",
method);
- unireg_abort(1);
+ throw wsrep::runtime_error("Failed to prepare for SST. Unrecoverable");
}
}
- size_t const method_len(strlen(method));
- size_t const msg_len (method_len + addr_len + 2 /* + auth_len + 1*/);
+ std::string ret;
+ ret += method;
+ ret.push_back('\0');
+ ret += addr_out;
- *msg = malloc (msg_len);
- if (NULL != *msg) {
- char* const method_ptr(reinterpret_cast<char*>(*msg));
- strcpy (method_ptr, method);
- char* const addr_ptr(method_ptr + method_len + 1);
- strcpy (addr_ptr, addr_out);
-
- WSREP_INFO ("Prepared SST request: %s|%s", method_ptr, addr_ptr);
- }
- else {
- WSREP_ERROR("Failed to allocate SST request of size %zu. Can't continue.",
- msg_len);
- unireg_abort(1);
- }
+ const char* method_ptr(ret.data());
+ const char* addr_ptr(ret.data() + strlen(method_ptr) + 1);
+ WSREP_INFO ("Prepared SST request: %s|%s", method_ptr, addr_ptr);
if (addr_out != addr_in) /* malloc'ed */ free ((char*)addr_out);
- return msg_len;
+ return ret;
}
// helper method for donors
static int sst_run_shell (const char* cmd_str, char** env, int max_tries)
{
- int ret = 0;
+ int ret= 0;
for (int tries=1; tries <= max_tries; tries++)
{
@@ -1310,7 +1394,7 @@ static int sst_run_shell (const char* cmd_str, char** env, int max_tries)
proc.wait();
}
- if ((ret = proc.error()))
+ if ((ret= proc.error()))
{
WSREP_ERROR("Try %d/%d: '%s' failed: %d (%s)",
tries, max_tries, proc.cmd(), ret, strerror(ret));
@@ -1328,15 +1412,12 @@ static int sst_run_shell (const char* cmd_str, char** env, int max_tries)
static void sst_reject_queries(my_bool close_conn)
{
- wsrep_ready_set (FALSE); // this will be resotred when donor becomes synced
- WSREP_INFO("Rejecting client queries for the duration of SST.");
- if (TRUE == close_conn) wsrep_close_client_connections(FALSE);
+ WSREP_INFO("Rejecting client queries for the duration of SST.");
+ if (TRUE == close_conn) wsrep_close_client_connections(FALSE);
}
static int sst_donate_mysqldump (const char* addr,
- const wsrep_uuid_t* uuid,
- const char* uuid_str,
- wsrep_seqno_t seqno,
+ const wsrep::gtid& gtid,
bool bypass,
char** env) // carries auth info
{
@@ -1360,23 +1441,31 @@ static int sst_donate_mysqldump (const char* addr,
return -ENOMEM;
}
+ /*
+ we enable new client connections so that mysqldump donation can connect in,
+ but we reject local connections from modifyingcdata during SST, to keep
+ data intact
+ */
if (!bypass && wsrep_sst_donor_rejects_queries) sst_reject_queries(TRUE);
make_wsrep_defaults_file();
+ std::ostringstream uuid_oss;
+ uuid_oss << gtid.id();
int ret= snprintf (cmd_str(), cmd_len,
"wsrep_sst_mysqldump "
WSREP_SST_OPT_ADDR " '%s' "
- WSREP_SST_OPT_PORT " '%d' "
+ WSREP_SST_OPT_PORT " '%u' "
WSREP_SST_OPT_LPORT " '%u' "
WSREP_SST_OPT_SOCKET " '%s' "
"%s"
WSREP_SST_OPT_GTID " '%s:%lld' "
WSREP_SST_OPT_GTID_DOMAIN_ID " '%d'"
"%s",
- addr, port, mysqld_port, mysqld_unix_port,
- wsrep_defaults_file, uuid_str,
- (long long)seqno, wsrep_gtid_domain_id,
+ addr, port, mysqld_port, mysqld_unix_port,
+ wsrep_defaults_file,
+ uuid_oss.str().c_str(), gtid.seqno().get(),
+ wsrep_gtid_domain_id,
bypass ? " " WSREP_SST_OPT_BYPASS : "");
if (ret < 0 || size_t(ret) >= cmd_len)
@@ -1392,16 +1481,17 @@ static int sst_donate_mysqldump (const char* addr,
ret= sst_run_shell (cmd_str(), env, 3);
- wsrep_gtid_t const state_id = { *uuid, (ret ? WSREP_SEQNO_UNDEFINED : seqno)};
-
- wsrep->sst_sent (wsrep, &state_id, ret);
+ wsrep::gtid sst_sent_gtid(ret == 0 ?
+ gtid :
+ wsrep::gtid(gtid.id(),
+ wsrep::seqno::undefined()));
+ Wsrep_server_state::instance().sst_sent(sst_sent_gtid, ret);
return ret;
}
wsrep_seqno_t wsrep_locked_seqno= WSREP_SEQNO_UNDEFINED;
-
/*
Create a file under data directory.
*/
@@ -1450,7 +1540,6 @@ static int sst_create_file(const char *name, const char *content)
return err;
}
-
static int run_sql_command(THD *thd, const char *query)
{
thd->set_query((char *)query, strlen(query));
@@ -1496,9 +1585,9 @@ static int sst_flush_tables(THD* thd)
{
/* Do not use non-supported parser character sets */
WSREP_WARN("Current client character set is non-supported parser character set: %s", current_charset->csname);
- thd->variables.character_set_client = &my_charset_latin1;
+ thd->variables.character_set_client= &my_charset_latin1;
WSREP_WARN("For SST temporally setting character set to : %s",
- my_charset_latin1.csname);
+ my_charset_latin1.csname);
}
if (run_sql_command(thd, "FLUSH TABLES WITH READ LOCK"))
@@ -1519,7 +1608,7 @@ static int sst_flush_tables(THD* thd)
}
}
- thd->variables.character_set_client = current_charset;
+ thd->variables.character_set_client= current_charset;
if (err)
{
@@ -1537,7 +1626,6 @@ static int sst_flush_tables(THD* thd)
else
{
WSREP_INFO("Tables flushed.");
-
/*
Tables have been flushed. Create a file with cluster state ID and
wsrep_gtid_domain_id.
@@ -1546,6 +1634,41 @@ static int sst_flush_tables(THD* thd)
snprintf(content, sizeof(content), "%s:%lld %d\n", wsrep_cluster_state_uuid,
(long long)wsrep_locked_seqno, wsrep_gtid_domain_id);
err= sst_create_file(flush_success, content);
+
+ const char base_name[]= "tables_flushed";
+ ssize_t const full_len= strlen(mysql_real_data_home) + strlen(base_name)+2;
+ char *real_name= (char*) malloc(full_len);
+ sprintf(real_name, "%s/%s", mysql_real_data_home, base_name);
+ char *tmp_name= (char*) malloc(full_len + 4);
+ sprintf(tmp_name, "%s.tmp", real_name);
+
+ FILE* file= fopen(tmp_name, "w+");
+ if (0 == file)
+ {
+ err= errno;
+ WSREP_ERROR("Failed to open '%s': %d (%s)", tmp_name, err,strerror(err));
+ }
+ else
+ {
+ Wsrep_server_state& server_state= Wsrep_server_state::instance();
+ std::ostringstream uuid_oss;
+
+ uuid_oss << server_state.current_view().state_id().id();
+
+ fprintf(file, "%s:%lld %u\n",
+ uuid_oss.str().c_str(), server_state.pause_seqno().get(),
+ wsrep_gtid_domain_id);
+ fsync(fileno(file));
+ fclose(file);
+ if (rename(tmp_name, real_name) == -1)
+ {
+ err= errno;
+ WSREP_ERROR("Failed to rename '%s' to '%s': %d (%s)",
+ tmp_name, real_name, err,strerror(err));
+ }
+ }
+ free(real_name);
+ free(tmp_name);
}
return err;
@@ -1554,19 +1677,19 @@ static int sst_flush_tables(THD* thd)
static void sst_disallow_writes (THD* thd, bool yes)
{
- char query_str[64] = { 0, };
- ssize_t const query_max = sizeof(query_str) - 1;
+ char query_str[64]= { 0, };
+ ssize_t const query_max= sizeof(query_str) - 1;
CHARSET_INFO *current_charset;
- current_charset = thd->variables.character_set_client;
+ current_charset= thd->variables.character_set_client;
if (!is_supported_parser_charset(current_charset))
{
/* Do not use non-supported parser character sets */
WSREP_WARN("Current client character set is non-supported parser character set: %s", current_charset->csname);
- thd->variables.character_set_client = &my_charset_latin1;
+ thd->variables.character_set_client= &my_charset_latin1;
WSREP_WARN("For SST temporally setting character set to : %s",
- my_charset_latin1.csname);
+ my_charset_latin1.csname);
}
snprintf (query_str, query_max, "SET GLOBAL innodb_disallow_writes=%d",
@@ -1576,7 +1699,7 @@ static void sst_disallow_writes (THD* thd, bool yes)
{
WSREP_ERROR("Failed to disallow InnoDB writes");
}
- thd->variables.character_set_client = current_charset;
+ thd->variables.character_set_client= current_charset;
}
static void* sst_donor_thread (void* a)
@@ -1602,11 +1725,11 @@ static void* sst_donor_thread (void* a)
wsp::thd thd(FALSE, true);
wsp::process proc(arg->cmd, "r", arg->env);
- err= proc.error();
+ err= -proc.error();
/* Inform server about SST script startup and release TO isolation */
mysql_mutex_lock (&arg->lock);
- arg->err = -err;
+ arg->err= -err;
mysql_cond_signal (&arg->cond);
mysql_mutex_unlock (&arg->lock); //! @note arg is unusable after that.
@@ -1651,7 +1774,7 @@ wait_signal:
mysql_mutex_unlock(mysql_bin_log.get_log_lock());
}
sst_disallow_writes (thd.ptr, false);
- thd.ptr->global_read_lock.unlock_global_read_lock (thd.ptr);
+ thd.ptr->global_read_lock.unlock_global_read_lock(thd.ptr);
locked= false;
}
err= 0;
@@ -1665,6 +1788,7 @@ wait_signal:
else
{
WSREP_WARN("Received unknown signal: '%s'", out);
+ proc.wait();
}
}
else
@@ -1672,7 +1796,7 @@ wait_signal:
WSREP_ERROR("Failed to read from: %s", proc.cmd());
proc.wait();
}
- if (!err && proc.error()) err= proc.error();
+ if (!err && proc.error()) err= -proc.error();
}
else
{
@@ -1688,27 +1812,26 @@ wait_signal:
mysql_mutex_unlock(mysql_bin_log.get_log_lock());
}
sst_disallow_writes (thd.ptr, false);
- thd.ptr->global_read_lock.unlock_global_read_lock (thd.ptr);
+ thd.ptr->global_read_lock.unlock_global_read_lock(thd.ptr);
}
- // signal to donor that SST is over
- struct wsrep_gtid const state_id = {
- ret_uuid, err ? WSREP_SEQNO_UNDEFINED : ret_seqno
- };
- wsrep->sst_sent (wsrep, &state_id, -err);
- proc.wait();
+ wsrep::gtid gtid(wsrep::id(ret_uuid.data, sizeof(ret_uuid.data)),
+ wsrep::seqno(err ? wsrep::seqno::undefined() :
+ wsrep::seqno(ret_seqno)));
- return NULL;
-}
+ Wsrep_server_state::instance().sst_sent(gtid, err);
+ proc.wait();
+ wsrep_donor_monitor_end();
+ return nullptr;
+}
-static int sst_donate_other (const char* method,
- const char* addr,
- const char* uuid,
- wsrep_seqno_t seqno,
- bool bypass,
- char** env) // carries auth info
+static int sst_donate_other (const char* method,
+ const char* addr,
+ const wsrep::gtid& gtid,
+ bool bypass,
+ char** env) // carries auth info
{
bool extra_args;
size_t const cmd_len= estimate_cmd_len(&extra_args);
@@ -1722,6 +1845,7 @@ static int sst_donate_other (const char* method,
}
char* binlog_opt_val= NULL;
+ char* binlog_index_opt_val= NULL;
int ret;
if ((ret= generate_binlog_opt_val(&binlog_opt_val)))
@@ -1730,8 +1854,16 @@ static int sst_donate_other (const char* method,
return ret;
}
+ if ((ret= generate_binlog_index_opt_val(&binlog_index_opt_val)))
+ {
+ WSREP_ERROR("sst_prepare_other(): generate_binlog_index_opt_val() failed %d",
+ ret);
+ }
+
make_wsrep_defaults_file();
+ std::ostringstream uuid_oss;
+ uuid_oss << gtid.id();
ret= snprintf (cmd_str(), cmd_len,
"wsrep_sst_%s "
WSREP_SST_OPT_ROLE " 'donor' "
@@ -1742,13 +1874,16 @@ static int sst_donate_other (const char* method,
WSREP_SST_OPT_GTID " '%s:%lld' "
WSREP_SST_OPT_GTID_DOMAIN_ID " '%d'"
"%s"
+ "%s"
"%s",
method, addr, mysqld_unix_port, mysql_real_data_home,
wsrep_defaults_file,
- uuid, (long long) seqno, wsrep_gtid_domain_id,
- binlog_opt_val,
+ uuid_oss.str().c_str(), gtid.seqno().get(), wsrep_gtid_domain_id,
+ binlog_opt_val, binlog_index_opt_val,
bypass ? " " WSREP_SST_OPT_BYPASS : "");
+
my_free(binlog_opt_val);
+ my_free(binlog_index_opt_val);
if (ret < 0 || size_t(ret) >= cmd_len)
{
@@ -1763,14 +1898,18 @@ static int sst_donate_other (const char* method,
pthread_t tmp;
sst_thread_arg arg(cmd_str(), env);
+
mysql_mutex_lock (&arg.lock);
- ret = mysql_thread_create (key_wsrep_sst_donor, &tmp, NULL, sst_donor_thread, &arg);
+
+ ret= mysql_thread_create (key_wsrep_sst_donor, &tmp, NULL, sst_donor_thread, &arg);
+
if (ret)
{
WSREP_ERROR("sst_donate_other(): mysql_thread_create() failed: %d (%s)",
ret, strerror(ret));
return ret;
}
+
mysql_cond_wait (&arg.cond, &arg.lock);
WSREP_INFO("sst_donor_thread signaled with %d", arg.err);
@@ -1806,14 +1945,17 @@ static bool check_request_str(const char* const str,
return false;
}
-wsrep_cb_status_t wsrep_sst_donate_cb (void* app_ctx, void* recv_ctx,
- const void* msg, size_t msg_len,
- const wsrep_gtid_t* current_gtid,
- const char* state, size_t state_len,
- bool bypass)
+int wsrep_sst_donate(const std::string& msg,
+ const wsrep::gtid& current_gtid,
+ const bool bypass)
{
- const char* method = (char*)msg;
- size_t method_len = strlen (method);
+ /* This will be reset when sync callback is called.
+ * Should we set wsrep_ready to FALSE here too? */
+
+ wsrep_config_state->set(wsrep::server_state::s_donor);
+
+ const char* method= msg.data();
+ size_t method_len= strlen (method);
if (check_request_str(method, filename_char))
{
@@ -1821,7 +1963,7 @@ wsrep_cb_status_t wsrep_sst_donate_cb (void* app_ctx, void* recv_ctx,
return WSREP_CB_FAILURE;
}
- const char* data = method + method_len + 1;
+ const char* data= method + method_len + 1;
if (check_request_str(data, address_char))
{
@@ -1829,13 +1971,6 @@ wsrep_cb_status_t wsrep_sst_donate_cb (void* app_ctx, void* recv_ctx,
return WSREP_CB_FAILURE;
}
- char uuid_str[37];
- wsrep_uuid_print (&current_gtid->uuid, uuid_str, sizeof(uuid_str));
-
- /* This will be reset when sync callback is called.
- * Should we set wsrep_ready to FALSE here too? */
- wsrep_config_state->set(WSREP_MEMBER_DONOR);
-
wsp::env env(NULL);
if (env.error())
{
@@ -1860,56 +1995,26 @@ wsrep_cb_status_t wsrep_sst_donate_cb (void* app_ctx, void* recv_ctx,
}
}
- if (!strcmp (WSREP_SST_MYSQLDUMP, method))
+ sst_donor_completed= false;
+ pthread_t monitor;
+
+ ret= mysql_thread_create (key_wsrep_sst_donor_monitor, &monitor, NULL, wsrep_sst_donor_monitor_thread, NULL);
+
+ if (ret)
{
- ret = sst_donate_mysqldump(data, &current_gtid->uuid, uuid_str,
- current_gtid->seqno, bypass, env());
+ WSREP_ERROR("sst_donate: mysql_thread_create() failed: %d (%s)",
+ ret, strerror(ret));
+ return WSREP_CB_FAILURE;
}
- else
+
+ if (!strcmp (WSREP_SST_MYSQLDUMP, method))
{
- ret = sst_donate_other(method, data, uuid_str,
- current_gtid->seqno, bypass, env());
+ ret= sst_donate_mysqldump(data, current_gtid, bypass, env());
}
-
- return (ret >= 0 ? WSREP_CB_SUCCESS : WSREP_CB_FAILURE);
-}
-
-void wsrep_SE_init_grab()
-{
- if (mysql_mutex_lock (&LOCK_wsrep_sst_init)) abort();
-}
-
-void wsrep_SE_init_wait()
-{
- double total_wtime=0;
-
- while (SE_initialized == false)
+ else
{
- struct timespec wtime;
- set_timespec(wtime, WSREP_TIMEDWAIT_SECONDS);
- time_t start_time = time(NULL);
- mysql_cond_timedwait (&COND_wsrep_sst_init, &LOCK_wsrep_sst_init, &wtime);
- time_t end_time = time(NULL);
-
- if (!SE_initialized)
- {
- total_wtime += difftime(end_time, start_time);
- WSREP_DEBUG("Waiting for SST to complete. current seqno: %" PRId64 " waited %f secs.", local_seqno, total_wtime);
- service_manager_extend_timeout(WSREP_EXTEND_TIMEOUT_INTERVAL,
- "WSREP state transfer ongoing, current seqno: %" PRId64 " waited %f secs", local_seqno, total_wtime);
- }
+ ret= sst_donate_other(method, data, current_gtid, bypass, env());
}
- mysql_mutex_unlock (&LOCK_wsrep_sst_init);
-}
-
-void wsrep_SE_init_done()
-{
- mysql_cond_signal (&COND_wsrep_sst_init);
- mysql_mutex_unlock (&LOCK_wsrep_sst_init);
-}
-
-void wsrep_SE_initialized()
-{
- SE_initialized = true;
+ return (ret >= 0 ? 0 : 1);
}
diff --git a/sql/wsrep_sst.h b/sql/wsrep_sst.h
index 38706fa4671..2389db4abe7 100644
--- a/sql/wsrep_sst.h
+++ b/sql/wsrep_sst.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2013 Codership Oy <info@codership.com>
+/* Copyright (C) 2013-2018 Codership Oy <info@codership.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,14 +13,14 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. */
-#include <my_config.h>
-
#ifndef WSREP_SST_H
#define WSREP_SST_H
-#ifdef WITH_WSREP
+#include <my_config.h>
-#include <mysql.h> // my_bool
+#include "wsrep/gtid.hpp"
+#include <my_global.h>
+#include <string>
#define WSREP_SST_OPT_ROLE "--role"
#define WSREP_SST_OPT_ADDR "--address"
@@ -78,11 +78,29 @@ extern void wsrep_SE_init_wait(); /*! wait for SE init to complete */
extern void wsrep_SE_init_done(); /*! signal that SE init is complte */
extern void wsrep_SE_initialized(); /*! mark SE initialization complete */
+/**
+ Return a string containing the state transfer request string.
+ Note that the string may contain a '\0' in the middle.
+*/
+std::string wsrep_sst_prepare();
+
+/**
+ Donate a SST.
+
+ @param request SST request string received from the joiner. Note that
+ the string may contain a '\0' in the middle.
+ @param gtid Current position of the donor
+ @param bypass If true, full SST is not needed. Joiner needs to be
+ notified that it can continue starting from gtid.
+ */
+int wsrep_sst_donate(const std::string& request,
+ const wsrep::gtid& gtid,
+ bool bypass);
+
#else
#define wsrep_SE_initialized() do { } while(0)
#define wsrep_SE_init_grab() do { } while(0)
#define wsrep_SE_init_done() do { } while(0)
#define wsrep_sst_continue() (0)
-#endif /* WITH_WSREP */
#endif /* WSREP_SST_H */
diff --git a/sql/wsrep_storage_service.cc b/sql/wsrep_storage_service.cc
new file mode 100644
index 00000000000..4885fd9f7e6
--- /dev/null
+++ b/sql/wsrep_storage_service.cc
@@ -0,0 +1,206 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "my_global.h"
+#include "wsrep_storage_service.h"
+#include "wsrep_trans_observer.h" /* wsrep_open() */
+#include "wsrep_schema.h"
+#include "wsrep_binlog.h"
+
+#include "sql_class.h"
+#include "mysqld.h" /* next_query_id() */
+#include "slave.h" /* opt_log_slave_updates() */
+#include "transaction.h" /* trans_commit(), trans_rollback() */
+
+/*
+ Temporarily enable wsrep on thd
+ */
+class Wsrep_on
+{
+public:
+ Wsrep_on(THD* thd)
+ : m_thd(thd)
+ , m_wsrep_on(thd->variables.wsrep_on)
+ {
+ thd->variables.wsrep_on= TRUE;
+ }
+ ~Wsrep_on()
+ {
+ m_thd->variables.wsrep_on= m_wsrep_on;
+ }
+private:
+ THD* m_thd;
+ my_bool m_wsrep_on;
+};
+
+Wsrep_storage_service::Wsrep_storage_service(THD* thd)
+ : wsrep::storage_service()
+ , wsrep::high_priority_context(thd->wsrep_cs())
+ , m_thd(thd)
+{
+ thd->security_ctx->skip_grants();
+ thd->system_thread= SYSTEM_THREAD_SLAVE_SQL;
+
+ /* No binlogging */
+
+ /* No general log */
+ thd->variables.option_bits |= OPTION_LOG_OFF;
+
+ /* Read committed isolation to avoid gap locking */
+ thd->variables.tx_isolation = ISO_READ_COMMITTED;
+
+ /* Keep wsrep on to enter commit ordering hooks */
+ thd->variables.wsrep_on= 1;
+ thd->wsrep_skip_locking= true;
+
+ wsrep_open(thd);
+ wsrep_before_command(thd);
+}
+
+Wsrep_storage_service::~Wsrep_storage_service()
+{
+ wsrep_after_command_ignore_result(m_thd);
+ wsrep_close(m_thd);
+ m_thd->wsrep_skip_locking= false;
+}
+
+int Wsrep_storage_service::start_transaction(const wsrep::ws_handle& ws_handle)
+{
+ DBUG_ENTER("Wsrep_storage_service::start_transaction");
+ DBUG_ASSERT(m_thd == current_thd);
+ DBUG_PRINT("info", ("Wsrep_storage_service::start_transcation(%llu, %p)",
+ m_thd->thread_id, m_thd));
+ m_thd->set_wsrep_next_trx_id(ws_handle.transaction_id().get());
+ DBUG_RETURN(m_thd->wsrep_cs().start_transaction(
+ wsrep::transaction_id(m_thd->wsrep_next_trx_id())) ||
+ trans_begin(m_thd, MYSQL_START_TRANS_OPT_READ_WRITE));
+}
+
+void Wsrep_storage_service::adopt_transaction(const wsrep::transaction& transaction)
+{
+ DBUG_ENTER("Wsrep_Storage_server::adopt_transaction");
+ DBUG_ASSERT(m_thd == current_thd);
+ m_thd->wsrep_cs().adopt_transaction(transaction);
+ trans_begin(m_thd, MYSQL_START_TRANS_OPT_READ_WRITE);
+ DBUG_VOID_RETURN;
+}
+
+int Wsrep_storage_service::append_fragment(const wsrep::id& server_id,
+ wsrep::transaction_id transaction_id,
+ int flags,
+ const wsrep::const_buffer& data,
+ const wsrep::xid& xid WSREP_UNUSED)
+{
+ DBUG_ENTER("Wsrep_storage_service::append_fragment");
+ DBUG_ASSERT(m_thd == current_thd);
+ DBUG_PRINT("info", ("Wsrep_storage_service::append_fragment(%llu, %p)",
+ m_thd->thread_id, m_thd));
+ int ret= wsrep_schema->append_fragment(m_thd,
+ server_id,
+ transaction_id,
+ wsrep::seqno(-1),
+ flags,
+ data);
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_storage_service::update_fragment_meta(const wsrep::ws_meta& ws_meta)
+{
+ DBUG_ENTER("Wsrep_storage_service::update_fragment_meta");
+ DBUG_ASSERT(m_thd == current_thd);
+ DBUG_PRINT("info", ("Wsrep_storage_service::update_fragment_meta(%llu, %p)",
+ m_thd->thread_id, m_thd));
+ int ret= wsrep_schema->update_fragment_meta(m_thd, ws_meta);
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_storage_service::remove_fragments()
+{
+ DBUG_ENTER("Wsrep_storage_service::remove_fragments");
+ DBUG_ASSERT(m_thd == current_thd);
+
+ int ret= wsrep_schema->remove_fragments(m_thd,
+ m_thd->wsrep_trx().server_id(),
+ m_thd->wsrep_trx().id(),
+ m_thd->wsrep_sr().fragments());
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_storage_service::commit(const wsrep::ws_handle& ws_handle,
+ const wsrep::ws_meta& ws_meta)
+{
+ DBUG_ENTER("Wsrep_storage_service::commit");
+ DBUG_ASSERT(m_thd == current_thd);
+ DBUG_PRINT("info", ("Wsrep_storage_service::commit(%llu, %p)",
+ m_thd->thread_id, m_thd));
+ WSREP_DEBUG("Storage service commit: %llu, %lld",
+ ws_meta.transaction_id().get(), ws_meta.seqno().get());
+ int ret= 0;
+ const bool is_ordered= !ws_meta.seqno().is_undefined();
+
+ if (is_ordered)
+ {
+ ret= m_thd->wsrep_cs().prepare_for_ordering(ws_handle, ws_meta, true);
+ }
+
+ ret= ret || trans_commit(m_thd);
+
+ if (!is_ordered)
+ {
+ /* Wsrep commit was not ordered so it does not go through commit time
+ hooks and remains active. Roll it back to make cleanup happen
+ in after_applying() call. */
+ m_thd->wsrep_cs().before_rollback();
+ m_thd->wsrep_cs().after_rollback();
+ }
+ else if (ret)
+ {
+ /* Commit failed, this probably means that the parent SR transaction
+ was BF aborted. Roll back out of order, the parent
+ transaction will release commit order after it has rolled back. */
+ m_thd->wsrep_cs().prepare_for_ordering(wsrep::ws_handle(),
+ wsrep::ws_meta(),
+ false);
+ trans_rollback(m_thd);
+ }
+ m_thd->wsrep_cs().after_applying();
+ m_thd->release_transactional_locks();
+ DBUG_RETURN(ret);
+}
+
+int Wsrep_storage_service::rollback(const wsrep::ws_handle& ws_handle,
+ const wsrep::ws_meta& ws_meta)
+{
+ DBUG_ENTER("Wsrep_storage_service::rollback");
+ DBUG_ASSERT(m_thd == current_thd);
+ DBUG_PRINT("info", ("Wsrep_storage_service::rollback(%llu, %p)",
+ m_thd->thread_id, m_thd));
+ int ret= (m_thd->wsrep_cs().prepare_for_ordering(
+ ws_handle, ws_meta, false) ||
+ trans_rollback(m_thd));
+ m_thd->wsrep_cs().after_applying();
+ m_thd->release_transactional_locks();
+ DBUG_RETURN(ret);
+}
+
+void Wsrep_storage_service::store_globals()
+{
+ wsrep_store_threadvars(m_thd);
+}
+
+void Wsrep_storage_service::reset_globals()
+{
+ wsrep_reset_threadvars(m_thd);
+}
diff --git a/sql/wsrep_storage_service.h b/sql/wsrep_storage_service.h
new file mode 100644
index 00000000000..f39543a89bc
--- /dev/null
+++ b/sql/wsrep_storage_service.h
@@ -0,0 +1,49 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef WSREP_STORAGE_SERVICE_H
+#define WSREP_STORAGE_SERVICE_H
+
+#include "wsrep/storage_service.hpp"
+#include "wsrep/client_state.hpp"
+
+class THD;
+class Wsrep_server_service;
+class Wsrep_storage_service :
+ public wsrep::storage_service,
+ public wsrep::high_priority_context
+{
+public:
+ Wsrep_storage_service(THD*);
+ ~Wsrep_storage_service();
+ int start_transaction(const wsrep::ws_handle&);
+ void adopt_transaction(const wsrep::transaction&);
+ int append_fragment(const wsrep::id&,
+ wsrep::transaction_id,
+ int flags,
+ const wsrep::const_buffer&,
+ const wsrep::xid&);
+ int update_fragment_meta(const wsrep::ws_meta&);
+ int remove_fragments();
+ int commit(const wsrep::ws_handle&, const wsrep::ws_meta&);
+ int rollback(const wsrep::ws_handle&, const wsrep::ws_meta&);
+ void store_globals();
+ void reset_globals();
+private:
+ friend class Wsrep_server_service;
+ THD* m_thd;
+};
+
+#endif /* WSREP_STORAGE_SERVICE_H */
diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc
index 6fd57edc692..023da27c3c1 100644
--- a/sql/wsrep_thd.cc
+++ b/sql/wsrep_thd.cc
@@ -15,547 +15,86 @@
#include "mariadb.h"
#include "wsrep_thd.h"
+#include "wsrep_trans_observer.h"
+#include "wsrep_high_priority_service.h"
+#include "wsrep_storage_service.h"
#include "transaction.h"
#include "rpl_rli.h"
#include "log_event.h"
#include "sql_parse.h"
-//#include "global_threads.h" // LOCK_thread_count, etc.
-#include "sql_base.h" // close_thread_tables()
#include "mysqld.h" // start_wsrep_THD();
+#include "wsrep_applier.h" // start_wsrep_THD();
+#include "mysql/service_wsrep.h"
#include "debug_sync.h"
-
-#include "slave.h" // opt_log_slave_updates
-#include "rpl_filter.h"
+#include "slave.h"
#include "rpl_rli.h"
#include "rpl_mi.h"
-#if (__LP64__)
-static volatile int64 wsrep_bf_aborts_counter(0);
-#define WSREP_ATOMIC_LOAD_LONG my_atomic_load64
-#define WSREP_ATOMIC_ADD_LONG my_atomic_add64
-#else
-static volatile int32 wsrep_bf_aborts_counter(0);
-#define WSREP_ATOMIC_LOAD_LONG my_atomic_load32
-#define WSREP_ATOMIC_ADD_LONG my_atomic_add32
-#endif
+extern "C" pthread_key(struct st_my_thread_var*, THR_KEY_mysys);
+
+static Wsrep_thd_queue* wsrep_rollback_queue= 0;
+static Atomic_counter<uint64_t> wsrep_bf_aborts_counter;
+
int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff,
enum enum_var_type scope)
{
- wsrep_local_bf_aborts = WSREP_ATOMIC_LOAD_LONG(&wsrep_bf_aborts_counter);
- var->type = SHOW_LONGLONG;
- var->value = (char*)&wsrep_local_bf_aborts;
+ wsrep_local_bf_aborts= wsrep_bf_aborts_counter;
+ var->type= SHOW_LONGLONG;
+ var->value= (char*)&wsrep_local_bf_aborts;
return 0;
}
-/* must have (&thd->LOCK_thd_data) */
-void wsrep_client_rollback(THD *thd)
-{
- WSREP_DEBUG("client rollback due to BF abort for (%lld), query: %s",
- (longlong) thd->thread_id, thd->query());
-
- WSREP_ATOMIC_ADD_LONG(&wsrep_bf_aborts_counter, 1);
-
- thd->wsrep_conflict_state= ABORTING;
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- trans_rollback(thd);
-
- if (thd->locked_tables_mode && thd->lock)
- {
- WSREP_DEBUG("unlocking tables for BF abort (%lld)",
- (longlong) thd->thread_id);
- thd->locked_tables_list.unlock_locked_tables(thd);
- thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
- }
-
- if (thd->global_read_lock.is_acquired())
- {
- WSREP_DEBUG("unlocking GRL for BF abort (%lld)",
- (longlong) thd->thread_id);
- thd->global_read_lock.unlock_global_read_lock(thd);
- }
-
- /* Release transactional metadata locks. */
- thd->release_transactional_locks();
-
- /* release explicit MDL locks */
- thd->mdl_context.release_explicit_locks();
-
- if (thd->get_binlog_table_maps())
- {
- WSREP_DEBUG("clearing binlog table map for BF abort (%lld)",
- (longlong) thd->thread_id);
- thd->clear_binlog_table_maps();
- }
- mysql_mutex_lock(&thd->LOCK_thd_data);
- thd->wsrep_conflict_state= ABORTED;
-}
-
-#define NUMBER_OF_FIELDS_TO_IDENTIFY_COORDINATOR 1
-#define NUMBER_OF_FIELDS_TO_IDENTIFY_WORKER 2
-
-static rpl_group_info* wsrep_relay_group_init(THD *thd, const char* log_fname)
+static void wsrep_replication_process(THD *thd,
+ void* arg __attribute__((unused)))
{
- Relay_log_info* rli= new Relay_log_info(false);
-
- WSREP_DEBUG("wsrep_relay_group_init %s", log_fname);
-
- if (!rli->relay_log.description_event_for_exec)
- {
- rli->relay_log.description_event_for_exec=
- new Format_description_log_event(4);
- }
-
- static LEX_CSTRING connection_name= { STRING_WITH_LEN("wsrep") };
+ DBUG_ENTER("wsrep_replication_process");
- /*
- Master_info's constructor initializes rpl_filter by either an already
- constructed Rpl_filter object from global 'rpl_filters' list if the
- specified connection name is same, or it constructs a new Rpl_filter
- object and adds it to rpl_filters. This object is later destructed by
- Mater_info's destructor by looking it up based on connection name in
- rpl_filters list.
-
- However, since all Master_info objects created here would share same
- connection name ("wsrep"), destruction of any of the existing Master_info
- objects (in wsrep_return_from_bf_mode()) would free rpl_filter referenced
- by any/all existing Master_info objects.
-
- In order to avoid that, we have added a check in Master_info's destructor
- to not free the "wsrep" rpl_filter. It will eventually be freed by
- free_all_rpl_filters() when server terminates.
- */
- rli->mi = new Master_info(&connection_name, false);
+ Wsrep_applier_service applier_service(thd);
- struct rpl_group_info *rgi= new rpl_group_info(rli);
- rgi->thd= rli->sql_driver_thd= thd;
+ WSREP_INFO("Starting applier thread %llu", thd->thread_id);
+ enum wsrep::provider::status
+ ret= Wsrep_server_state::get_provider().run_applier(&applier_service);
- if ((rgi->deferred_events_collecting= rli->mi->rpl_filter->is_on()))
- {
- rgi->deferred_events= new Deferred_log_events(rli);
- }
-
- return rgi;
-}
-
-static void wsrep_prepare_bf_thd(THD *thd, struct wsrep_thd_shadow* shadow)
-{
- shadow->options = thd->variables.option_bits;
- shadow->server_status = thd->server_status;
- shadow->wsrep_exec_mode = thd->wsrep_exec_mode;
- shadow->vio = thd->net.vio;
-
- // Disable general logging on applier threads
- thd->variables.option_bits |= OPTION_LOG_OFF;
-
- /* enable binlogging regardless of log_slave_updates setting
- this is for ensuring that both local and applier transaction go through
- same commit ordering algorithm in group commit control
- */
- thd->variables.option_bits|= OPTION_BIN_LOG;
-
- if (!thd->wsrep_rgi) thd->wsrep_rgi= wsrep_relay_group_init(thd, "wsrep_relay");
-
- /* thd->system_thread_info.rpl_sql_info isn't initialized. */
- if (!thd->slave_thread)
- thd->system_thread_info.rpl_sql_info=
- new rpl_sql_thread_info(thd->wsrep_rgi->rli->mi->rpl_filter);
-
- thd->wsrep_exec_mode= REPL_RECV;
- thd->net.vio= 0;
- thd->clear_error();
-
- shadow->tx_isolation = thd->variables.tx_isolation;
- thd->variables.tx_isolation = ISO_READ_COMMITTED;
- thd->tx_isolation = ISO_READ_COMMITTED;
-
- shadow->db = thd->db.str;
- shadow->db_length = thd->db.length;
- shadow->user_time = thd->user_time;
- shadow->row_count_func= thd->get_row_count_func();
- thd->reset_db(&null_clex_str);
-}
+ WSREP_INFO("Applier thread exiting ret: %d thd: %llu", ret, thd->thread_id);
+ mysql_mutex_lock(&LOCK_wsrep_slave_threads);
+ wsrep_close_applier(thd);
+ mysql_cond_broadcast(&COND_wsrep_slave_threads);
+ mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
-static void wsrep_return_from_bf_mode(THD *thd, struct wsrep_thd_shadow* shadow)
-{
- LEX_CSTRING db= {shadow->db, shadow->db_length };
- thd->variables.option_bits = shadow->options;
- thd->server_status = shadow->server_status;
- thd->wsrep_exec_mode = shadow->wsrep_exec_mode;
- thd->net.vio = shadow->vio;
- thd->variables.tx_isolation = shadow->tx_isolation;
- thd->user_time = shadow->user_time;
- thd->reset_db(&db);
-
- if (!thd->slave_thread)
- delete thd->system_thread_info.rpl_sql_info;
delete thd->wsrep_rgi->rli->mi;
delete thd->wsrep_rgi->rli;
-
+
thd->wsrep_rgi->cleanup_after_session();
delete thd->wsrep_rgi;
- thd->wsrep_rgi = NULL;
- thd->set_row_count_func(shadow->row_count_func);
-}
-
-void wsrep_replay_sp_transaction(THD* thd)
-{
- DBUG_ENTER("wsrep_replay_sp_transaction");
- mysql_mutex_assert_owner(&thd->LOCK_thd_data);
- DBUG_ASSERT(thd->wsrep_conflict_state == MUST_REPLAY);
- DBUG_ASSERT(wsrep_thd_trx_seqno(thd) > 0);
-
- WSREP_DEBUG("replaying SP transaction %llu", thd->thread_id);
- close_thread_tables(thd);
- if (thd->locked_tables_mode && thd->lock)
- {
- WSREP_DEBUG("releasing table lock for replaying (%u)",
- thd->thread_id);
- thd->locked_tables_list.unlock_locked_tables(thd);
- thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
- }
- thd->release_transactional_locks();
-
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- THD *replay_thd= new THD(true);
- replay_thd->thread_stack= thd->thread_stack;
-
- struct wsrep_thd_shadow shadow;
- wsrep_prepare_bf_thd(replay_thd, &shadow);
- WSREP_DEBUG("replaying set for %p rgi %p", replay_thd, replay_thd->wsrep_rgi); replay_thd->wsrep_trx_meta= thd->wsrep_trx_meta;
- replay_thd->wsrep_ws_handle= thd->wsrep_ws_handle;
- replay_thd->wsrep_ws_handle.trx_id= WSREP_UNDEFINED_TRX_ID;
- replay_thd->wsrep_conflict_state= REPLAYING;
-
- replay_thd->variables.option_bits|= OPTION_BEGIN;
- replay_thd->server_status|= SERVER_STATUS_IN_TRANS;
-
- thd->reset_globals();
- replay_thd->store_globals();
- wsrep_status_t rcode= wsrep->replay_trx(wsrep,
- &replay_thd->wsrep_ws_handle,
- (void*) replay_thd);
-
- wsrep_return_from_bf_mode(replay_thd, &shadow);
- replay_thd->reset_globals();
- delete replay_thd;
-
- mysql_mutex_lock(&thd->LOCK_thd_data);
-
- thd->store_globals();
-
- switch (rcode)
- {
- case WSREP_OK:
- {
- thd->wsrep_conflict_state= NO_CONFLICT;
- thd->killed= NOT_KILLED;
- wsrep_status_t rcode= wsrep->post_commit(wsrep, &thd->wsrep_ws_handle);
- if (rcode != WSREP_OK)
- {
- WSREP_WARN("Post commit failed for SP replay: thd: %u error: %d",
- thd->thread_id, rcode);
- }
- /* As replaying the transaction was successful, an error must not
- be returned to client, so we need to reset the error state of
- the diagnostics area */
- thd->get_stmt_da()->reset_diagnostics_area();
- break;
- }
- case WSREP_TRX_FAIL:
- {
- thd->wsrep_conflict_state= ABORTED;
- wsrep_status_t rcode= wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle);
- if (rcode != WSREP_OK)
- {
- WSREP_WARN("Post rollback failed for SP replay: thd: %u error: %d",
- thd->thread_id, rcode);
- }
- if (thd->get_stmt_da()->is_set())
- {
- thd->get_stmt_da()->reset_diagnostics_area();
- }
- my_error(ER_LOCK_DEADLOCK, MYF(0));
- break;
- }
- default:
- WSREP_ERROR("trx_replay failed for: %d, schema: %s, query: %s",
- rcode,
- (thd->db.str ? thd->db.str : "(null)"),
- WSREP_QUERY(thd));
- /* we're now in inconsistent state, must abort */
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- unireg_abort(1);
- break;
- }
-
- wsrep_cleanup_transaction(thd);
+ thd->wsrep_rgi= NULL;
- mysql_mutex_lock(&LOCK_wsrep_replaying);
- wsrep_replaying--;
- WSREP_DEBUG("replaying decreased: %d, thd: %u",
- wsrep_replaying, thd->thread_id);
- mysql_cond_broadcast(&COND_wsrep_replaying);
- mysql_mutex_unlock(&LOCK_wsrep_replaying);
-
- DBUG_VOID_RETURN;
-}
-
-void wsrep_replay_transaction(THD *thd)
-{
- DBUG_ENTER("wsrep_replay_transaction");
- /* checking if BF trx must be replayed */
- if (thd->wsrep_conflict_state== MUST_REPLAY) {
- DBUG_ASSERT(wsrep_thd_trx_seqno(thd));
- if (thd->wsrep_exec_mode!= REPL_RECV) {
- if (thd->get_stmt_da()->is_sent())
- {
- WSREP_ERROR("replay issue, thd has reported status already");
- }
-
-
- /*
- PS reprepare observer should have been removed already.
- open_table() will fail if we have dangling observer here.
- */
- DBUG_ASSERT(thd->m_reprepare_observer == NULL);
-
- struct da_shadow
- {
- enum Diagnostics_area::enum_diagnostics_status status;
- ulonglong affected_rows;
- ulonglong last_insert_id;
- char message[MYSQL_ERRMSG_SIZE];
- };
- struct da_shadow da_status;
- da_status.status= thd->get_stmt_da()->status();
- if (da_status.status == Diagnostics_area::DA_OK)
- {
- da_status.affected_rows= thd->get_stmt_da()->affected_rows();
- da_status.last_insert_id= thd->get_stmt_da()->last_insert_id();
- strmake(da_status.message,
- thd->get_stmt_da()->message(),
- sizeof(da_status.message)-1);
- }
-
- thd->get_stmt_da()->reset_diagnostics_area();
-
- thd->wsrep_conflict_state= REPLAYING;
- mysql_mutex_unlock(&thd->LOCK_thd_data);
-
- thd->reset_for_next_command();
- thd->reset_killed();
- close_thread_tables(thd);
- if (thd->locked_tables_mode && thd->lock)
- {
- WSREP_DEBUG("releasing table lock for replaying (%lld)",
- (longlong) thd->thread_id);
- thd->locked_tables_list.unlock_locked_tables(thd);
- thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
- }
- thd->release_transactional_locks();
- /*
- Replaying will call MYSQL_START_STATEMENT when handling
- BEGIN Query_log_event so end statement must be called before
- replaying.
- */
- MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
- thd->m_statement_psi= NULL;
- thd->m_digest= NULL;
- thd_proc_info(thd, "WSREP replaying trx");
- WSREP_DEBUG("replay trx: %s %lld",
- thd->query() ? thd->query() : "void",
- (long long)wsrep_thd_trx_seqno(thd));
- struct wsrep_thd_shadow shadow;
- wsrep_prepare_bf_thd(thd, &shadow);
-
- /* From trans_begin() */
- thd->variables.option_bits|= OPTION_BEGIN;
- thd->server_status|= SERVER_STATUS_IN_TRANS;
-
- /* Allow tests to block the replayer thread using the DBUG facilities */
-#ifdef ENABLED_DEBUG_SYNC
- DBUG_EXECUTE_IF("sync.wsrep_replay_cb",
- {
- const char act[]=
- "now "
- "SIGNAL sync.wsrep_replay_cb_reached "
- "WAIT_FOR signal.wsrep_replay_cb";
- DBUG_ASSERT(!debug_sync_set_action(thd,
- STRING_WITH_LEN(act)));
- };);
-#endif /* ENABLED_DEBUG_SYNC */
-
- int rcode = wsrep->replay_trx(wsrep,
- &thd->wsrep_ws_handle,
- (void *)thd);
-
- wsrep_return_from_bf_mode(thd, &shadow);
- if (thd->wsrep_conflict_state!= REPLAYING)
- WSREP_WARN("lost replaying mode: %d", thd->wsrep_conflict_state );
-
- mysql_mutex_lock(&thd->LOCK_thd_data);
-
- switch (rcode)
- {
- case WSREP_OK:
- thd->wsrep_conflict_state= NO_CONFLICT;
- wsrep->post_commit(wsrep, &thd->wsrep_ws_handle);
- WSREP_DEBUG("trx_replay successful for: %lld %lld",
- (longlong) thd->thread_id, (longlong) thd->real_id);
- if (thd->get_stmt_da()->is_sent())
- {
- WSREP_WARN("replay ok, thd has reported status");
- }
- else if (thd->get_stmt_da()->is_set())
- {
- if (thd->get_stmt_da()->status() != Diagnostics_area::DA_OK &&
- thd->get_stmt_da()->status() != Diagnostics_area::DA_OK_BULK)
- {
- WSREP_WARN("replay ok, thd has error status %d",
- thd->get_stmt_da()->status());
- }
- }
- else
- {
- if (da_status.status == Diagnostics_area::DA_OK)
- {
- my_ok(thd,
- da_status.affected_rows,
- da_status.last_insert_id,
- da_status.message);
- }
- else
- {
- my_ok(thd);
- }
- }
- break;
- case WSREP_TRX_FAIL:
- if (thd->get_stmt_da()->is_sent())
- {
- WSREP_ERROR("replay failed, thd has reported status");
- }
- else
- {
- WSREP_DEBUG("replay failed, rolling back");
- }
- thd->wsrep_conflict_state= ABORTED;
- wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle);
- break;
- default:
- WSREP_ERROR("trx_replay failed for: %d, schema: %s, query: %s",
- rcode, thd->get_db(),
- thd->query() ? thd->query() : "void");
- /* we're now in inconsistent state, must abort */
-
- /* http://bazaar.launchpad.net/~codership/codership-mysql/5.6/revision/3962#sql/wsrep_thd.cc */
- mysql_mutex_unlock(&thd->LOCK_thd_data);
-
- unireg_abort(1);
- break;
- }
-
- wsrep_cleanup_transaction(thd);
-
- mysql_mutex_lock(&LOCK_wsrep_replaying);
- wsrep_replaying--;
- WSREP_DEBUG("replaying decreased: %d, thd: %lld",
- wsrep_replaying, (longlong) thd->thread_id);
- mysql_cond_broadcast(&COND_wsrep_replaying);
- mysql_mutex_unlock(&LOCK_wsrep_replaying);
- }
- }
- DBUG_VOID_RETURN;
-}
-
-static void wsrep_replication_process(THD *thd)
-{
- int rcode;
- DBUG_ENTER("wsrep_replication_process");
-
- struct wsrep_thd_shadow shadow;
- wsrep_prepare_bf_thd(thd, &shadow);
-
- /* From trans_begin() */
- thd->variables.option_bits|= OPTION_BEGIN;
- thd->server_status|= SERVER_STATUS_IN_TRANS;
-
- thd_proc_info(thd, "wsrep applier idle");
- rcode = wsrep->recv(wsrep, (void *)thd);
- DBUG_PRINT("wsrep",("wsrep_repl returned: %d", rcode));
-
- WSREP_INFO("applier thread exiting (code:%d)", rcode);
-
- switch (rcode) {
- case WSREP_OK:
- case WSREP_NOT_IMPLEMENTED:
- case WSREP_CONN_FAIL:
- /* provider does not support slave operations / disconnected from group,
- * just close applier thread */
- break;
- case WSREP_NODE_FAIL:
- /* data inconsistency => SST is needed */
- /* Note: we cannot just blindly restart replication here,
- * SST might require server restart if storage engines must be
- * initialized after SST */
- WSREP_ERROR("node consistency compromised, aborting");
- wsrep_kill_mysql(thd);
- break;
- case WSREP_WARNING:
- case WSREP_TRX_FAIL:
- case WSREP_TRX_MISSING:
- /* these suggests a bug in provider code */
- WSREP_WARN("bad return from recv() call: %d", rcode);
- /* Shut down this node. */
- /* fall through */
- case WSREP_FATAL:
- /* Cluster connectivity is lost.
- *
- * If applier was killed on purpose (KILL_CONNECTION), we
- * avoid mysql shutdown. This is because the killer will then handle
- * shutdown processing (or replication restarting)
- */
- if (thd->killed != KILL_CONNECTION)
- {
- wsrep_kill_mysql(thd);
- }
- break;
- }
-
- mysql_mutex_lock(&LOCK_thread_count);
- wsrep_close_applier(thd);
- mysql_cond_broadcast(&COND_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
if(thd->has_thd_temporary_tables())
{
WSREP_WARN("Applier %lld has temporary tables at exit.",
thd->thread_id);
}
- wsrep_return_from_bf_mode(thd, &shadow);
DBUG_VOID_RETURN;
}
-static bool create_wsrep_THD(wsrep_thread_args* args, bool thread_count_lock)
+static bool create_wsrep_THD(Wsrep_thd_args* args, bool mutex_protected)
{
- if (!thread_count_lock)
- mysql_mutex_lock(&LOCK_thread_count);
+ if (!mutex_protected)
+ mysql_mutex_lock(&LOCK_wsrep_slave_threads);
ulong old_wsrep_running_threads= wsrep_running_threads;
- DBUG_ASSERT(args->thread_type == WSREP_APPLIER_THREAD ||
- args->thread_type == WSREP_ROLLBACKER_THREAD);
+ DBUG_ASSERT(args->thread_type() == WSREP_APPLIER_THREAD ||
+ args->thread_type() == WSREP_ROLLBACKER_THREAD);
- bool res= mysql_thread_create(args->thread_type == WSREP_APPLIER_THREAD
+ bool res= mysql_thread_create(args->thread_type() == WSREP_APPLIER_THREAD
? key_wsrep_applier : key_wsrep_rollbacker,
- &args->thread_id, &connection_attrib,
+ args->thread_id(), &connection_attrib,
start_wsrep_THD, (void*)args);
if (res)
- {
WSREP_ERROR("Can't create wsrep thread");
- }
/*
if starting a thread on server startup, wait until the this thread's THD
@@ -566,49 +105,38 @@ static bool create_wsrep_THD(wsrep_thread_args* args, bool thread_count_lock)
{
while (old_wsrep_running_threads == wsrep_running_threads)
{
- mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
+ mysql_cond_wait(&COND_wsrep_slave_threads, &LOCK_wsrep_slave_threads);
}
}
- if (!thread_count_lock)
- mysql_mutex_unlock(&LOCK_thread_count);
+ if (!mutex_protected)
+ mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
return res;
}
-bool wsrep_create_appliers(long threads, bool thread_count_lock)
+bool wsrep_create_appliers(long threads, bool mutex_protected)
{
- if (!wsrep_connected)
+ /* Dont' start slave threads if wsrep-provider or wsrep-cluster-address
+ is not set.
+ */
+ if (!WSREP_PROVIDER_EXISTS)
{
- /* see wsrep_replication_start() for the logic */
- if (wsrep_cluster_address && strlen(wsrep_cluster_address) &&
- wsrep_provider && strcasecmp(wsrep_provider, "none"))
- {
- WSREP_ERROR("Trying to launch slave threads before creating "
- "connection at '%s'", wsrep_cluster_address);
- assert(0);
- }
return false;
}
- long wsrep_threads= 0;
+ DBUG_ASSERT(wsrep_cluster_address[0]);
- while (wsrep_threads++ < threads) {
- wsrep_thread_args* arg;
+ long wsrep_threads=0;
- if((arg= (wsrep_thread_args*)my_malloc(sizeof(wsrep_thread_args), MYF(0))) == NULL)
- {
- WSREP_ERROR("Can't allocate memory for wsrep replication thread %ld\n", wsrep_threads);
- assert(0);
- }
-
- arg->thread_type= WSREP_APPLIER_THREAD;
- arg->processor= wsrep_replication_process;
-
- if (create_wsrep_THD(arg, thread_count_lock))
+ while (wsrep_threads++ < threads)
+ {
+ Wsrep_thd_args* args(new Wsrep_thd_args(wsrep_replication_process,
+ WSREP_APPLIER_THREAD,
+ pthread_self()));
+ if (create_wsrep_THD(args, mutex_protected))
{
- WSREP_ERROR("Can't create thread to manage wsrep replication");
- my_free(arg);
+ WSREP_WARN("Can't create thread to manage wsrep replication");
return true;
}
}
@@ -616,276 +144,307 @@ bool wsrep_create_appliers(long threads, bool thread_count_lock)
return false;
}
-static void wsrep_rollback_process(THD *thd)
+static void wsrep_remove_streaming_fragments(THD* thd, const char* ctx)
{
- DBUG_ENTER("wsrep_rollback_process");
+ wsrep::transaction_id transaction_id(thd->wsrep_trx().id());
+ Wsrep_storage_service* storage_service= wsrep_create_storage_service(thd, ctx);
+ storage_service->store_globals();
+ storage_service->adopt_transaction(thd->wsrep_trx());
+ storage_service->remove_fragments();
+ storage_service->commit(wsrep::ws_handle(transaction_id, 0),
+ wsrep::ws_meta());
+ Wsrep_server_state::instance().server_service()
+ .release_storage_service(storage_service);
+ wsrep_store_threadvars(thd);
+}
+
+static void wsrep_rollback_high_priority(THD *thd, THD *rollbacker)
+{
+ WSREP_DEBUG("Rollbacker aborting SR applier thd (%llu %lu)",
+ thd->thread_id, thd->real_id);
+ char* orig_thread_stack= thd->thread_stack;
+ thd->thread_stack= rollbacker->thread_stack;
+ DBUG_ASSERT(thd->wsrep_cs().mode() == Wsrep_client_state::m_high_priority);
+ /* Must be streaming and must have been removed from the
+ server state streaming appliers map. */
+ DBUG_ASSERT(thd->wsrep_trx().is_streaming());
+ DBUG_ASSERT(!Wsrep_server_state::instance().find_streaming_applier(
+ thd->wsrep_trx().server_id(),
+ thd->wsrep_trx().id()));
+ DBUG_ASSERT(thd->wsrep_applier_service);
+
+ /* Fragment removal should happen before rollback to make
+ the transaction non-observable in SR table after the rollback
+ completes. For correctness the order does not matter here,
+ but currently it is mandated by checks in some MTR tests. */
+ wsrep_remove_streaming_fragments(thd, "high priority");
+ thd->wsrep_applier_service->rollback(wsrep::ws_handle(),
+ wsrep::ws_meta());
+ thd->wsrep_applier_service->after_apply();
+ thd->thread_stack= orig_thread_stack;
+ WSREP_DEBUG("rollbacker aborted thd: (%llu %lu)",
+ thd->thread_id, thd->real_id);
+ /* Will free THD */
+ Wsrep_server_state::instance().server_service()
+ .release_high_priority_service(thd->wsrep_applier_service);
+}
+
+static void wsrep_rollback_local(THD *thd, THD *rollbacker)
+{
+ WSREP_DEBUG("Rollbacker aborting local thd (%llu %lu)",
+ thd->thread_id, thd->real_id);
+ char* orig_thread_stack= thd->thread_stack;
+ thd->thread_stack= rollbacker->thread_stack;
+ if (thd->wsrep_trx().is_streaming())
+ {
+ wsrep_remove_streaming_fragments(thd, "local");
+ }
+ /* Set thd->event_scheduler.data temporarily to NULL to avoid
+ callbacks to threadpool wait_begin() during rollback. */
+ auto saved_esd= thd->event_scheduler.data;
+ thd->event_scheduler.data= 0;
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ /* prepare THD for rollback processing */
+ thd->reset_for_next_command();
+ thd->lex->sql_command= SQLCOM_ROLLBACK;
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ /* Perform a client rollback, restore globals and signal
+ the victim only when all the resources have been
+ released */
+ thd->wsrep_cs().client_service().bf_rollback();
+ wsrep_reset_threadvars(thd);
+ /* Assign saved event_scheduler.data back before letting
+ client to continue. */
+ thd->event_scheduler.data= saved_esd;
+ thd->thread_stack= orig_thread_stack;
+ thd->wsrep_cs().sync_rollback_complete();
+ WSREP_DEBUG("rollbacker aborted thd: (%llu %lu)",
+ thd->thread_id, thd->real_id);
+}
- mysql_mutex_lock(&LOCK_wsrep_rollback);
- wsrep_aborting_thd= NULL;
+static void wsrep_rollback_process(THD *rollbacker,
+ void *arg __attribute__((unused)))
+{
+ DBUG_ENTER("wsrep_rollback_process");
- while (thd->killed == NOT_KILLED) {
- thd_proc_info(thd, "WSREP aborter idle");
- thd->mysys_var->current_mutex= &LOCK_wsrep_rollback;
- thd->mysys_var->current_cond= &COND_wsrep_rollback;
+ THD* thd= NULL;
+ DBUG_ASSERT(!wsrep_rollback_queue);
+ wsrep_rollback_queue= new Wsrep_thd_queue(rollbacker);
+ WSREP_INFO("Starting rollbacker thread %llu", rollbacker->thread_id);
- mysql_cond_wait(&COND_wsrep_rollback,&LOCK_wsrep_rollback);
+ thd_proc_info(rollbacker, "wsrep aborter idle");
+ while ((thd= wsrep_rollback_queue->pop_front()) != NULL)
+ {
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ wsrep::client_state& cs(thd->wsrep_cs());
+ const wsrep::transaction& tx(cs.transaction());
+ if (tx.state() == wsrep::transaction::s_aborted)
+ {
+ WSREP_DEBUG("rollbacker thd already aborted: %llu state: %d",
+ (long long)thd->real_id,
+ tx.state());
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ continue;
+ }
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
- WSREP_DEBUG("WSREP rollback thread wakes for signal");
+ wsrep_reset_threadvars(rollbacker);
+ wsrep_store_threadvars(thd);
+ thd->wsrep_cs().acquire_ownership();
- mysql_mutex_lock(&thd->mysys_var->mutex);
- thd_proc_info(thd, "WSREP aborter active");
- thd->mysys_var->current_mutex= 0;
- thd->mysys_var->current_cond= 0;
- mysql_mutex_unlock(&thd->mysys_var->mutex);
+ thd_proc_info(rollbacker, "wsrep aborter active");
- /* check for false alarms */
- if (!wsrep_aborting_thd)
+ /* Rollback methods below may free thd pointer. Do not try
+ to access it after method returns. */
+ if (wsrep_thd_is_applying(thd))
{
- WSREP_DEBUG("WSREP rollback thread has empty abort queue");
+ wsrep_rollback_high_priority(thd, rollbacker);
}
- /* process all entries in the queue */
- while (wsrep_aborting_thd) {
- THD *aborting;
- wsrep_aborting_thd_t next = wsrep_aborting_thd->next;
- aborting = wsrep_aborting_thd->aborting_thd;
- my_free(wsrep_aborting_thd);
- wsrep_aborting_thd= next;
- /*
- * must release mutex, appliers my want to add more
- * aborting thds in our work queue, while we rollback
- */
- mysql_mutex_unlock(&LOCK_wsrep_rollback);
-
- mysql_mutex_lock(&aborting->LOCK_thd_data);
- if (aborting->wsrep_conflict_state== ABORTED)
- {
- WSREP_DEBUG("WSREP, thd already aborted: %llu state: %d",
- (long long)aborting->real_id,
- aborting->wsrep_conflict_state);
-
- mysql_mutex_unlock(&aborting->LOCK_thd_data);
- mysql_mutex_lock(&LOCK_wsrep_rollback);
- continue;
- }
- aborting->wsrep_conflict_state= ABORTING;
-
- mysql_mutex_unlock(&aborting->LOCK_thd_data);
-
- set_current_thd(aborting);
- aborting->store_globals();
-
- mysql_mutex_lock(&aborting->LOCK_thd_data);
- wsrep_client_rollback(aborting);
- WSREP_DEBUG("WSREP rollbacker aborted thd: (%lld %lld)",
- (longlong) aborting->thread_id,
- (longlong) aborting->real_id);
- mysql_mutex_unlock(&aborting->LOCK_thd_data);
-
- set_current_thd(thd);
- thd->store_globals();
-
- mysql_mutex_lock(&LOCK_wsrep_rollback);
+ else
+ {
+ wsrep_rollback_local(thd, rollbacker);
}
+ wsrep_store_threadvars(rollbacker);
+ thd_proc_info(rollbacker, "wsrep aborter idle");
}
- mysql_mutex_unlock(&LOCK_wsrep_rollback);
- sql_print_information("WSREP: rollbacker thread exiting");
+ delete wsrep_rollback_queue;
+ wsrep_rollback_queue= NULL;
+
+ WSREP_INFO("rollbacker thread exiting %llu", rollbacker->thread_id);
+ DBUG_ASSERT(rollbacker->killed != NOT_KILLED);
DBUG_PRINT("wsrep",("wsrep rollbacker thread exiting"));
DBUG_VOID_RETURN;
}
void wsrep_create_rollbacker()
{
- if (wsrep_provider && strcasecmp(wsrep_provider, "none"))
- {
- wsrep_thread_args* arg;
- if((arg = (wsrep_thread_args*)my_malloc(sizeof(wsrep_thread_args), MYF(0))) == NULL) {
- WSREP_ERROR("Can't allocate memory for wsrep rollbacker thread\n");
- assert(0);
- }
+ DBUG_ASSERT(wsrep_cluster_address[0]);
+ Wsrep_thd_args* args(new Wsrep_thd_args(wsrep_rollback_process,
+ WSREP_ROLLBACKER_THREAD,
+ pthread_self()));
+
+ /* create rollbacker */
+ if (create_wsrep_THD(args, false))
+ WSREP_WARN("Can't create thread to manage wsrep rollback");
+}
- arg->thread_type = WSREP_ROLLBACKER_THREAD;
- arg->processor = wsrep_rollback_process;
+/*
+ Start async rollback process
- /* create rollbacker */
- if (create_wsrep_THD(arg, false)) {
- WSREP_WARN("Can't create thread to manage wsrep rollback");
- my_free(arg);
- return;
- }
+ Asserts thd->LOCK_thd_data ownership
+ */
+void wsrep_fire_rollbacker(THD *thd)
+{
+ DBUG_ASSERT(thd->wsrep_trx().state() == wsrep::transaction::s_aborting);
+ DBUG_PRINT("wsrep",("enqueuing trx abort for %llu", thd->thread_id));
+ WSREP_DEBUG("enqueuing trx abort for (%llu)", thd->thread_id);
+ if (wsrep_rollback_queue->push_back(thd))
+ {
+ WSREP_WARN("duplicate thd %llu for rollbacker",
+ thd->thread_id);
}
}
-void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe)
-{
- if (thd_ptr)
+
+int wsrep_abort_thd(THD *bf_thd_ptr, THD *victim_thd_ptr, my_bool signal)
+{
+ DBUG_ENTER("wsrep_abort_thd");
+ THD *victim_thd= (THD *) victim_thd_ptr;
+ THD *bf_thd= (THD *) bf_thd_ptr;
+
+ mysql_mutex_lock(&victim_thd->LOCK_thd_data);
+
+ /* Note that when you use RSU node is desynced from cluster, thus WSREP(thd)
+ might not be true.
+ */
+ if ((WSREP(bf_thd) ||
+ ((WSREP_ON || bf_thd->variables.wsrep_OSU_method == WSREP_OSU_RSU) &&
+ wsrep_thd_is_toi(bf_thd))) &&
+ victim_thd &&
+ !wsrep_thd_is_aborting(victim_thd))
{
- THD* thd = (THD*)thd_ptr;
- thd->wsrep_PA_safe = safe;
+ WSREP_DEBUG("wsrep_abort_thd, by: %llu, victim: %llu", (bf_thd) ?
+ (long long)bf_thd->real_id : 0, (long long)victim_thd->real_id);
+ mysql_mutex_unlock(&victim_thd->LOCK_thd_data);
+ ha_abort_transaction(bf_thd, victim_thd, signal);
+ mysql_mutex_lock(&victim_thd->LOCK_thd_data);
}
-}
-
-enum wsrep_conflict_state wsrep_thd_conflict_state(THD *thd, my_bool sync)
-{
- enum wsrep_conflict_state state = NO_CONFLICT;
- if (thd)
+ else
{
- if (sync) mysql_mutex_lock(&thd->LOCK_thd_data);
-
- state = thd->wsrep_conflict_state;
- if (sync) mysql_mutex_unlock(&thd->LOCK_thd_data);
+ WSREP_DEBUG("wsrep_abort_thd not effective: %p %p", bf_thd, victim_thd);
}
- return state;
+
+ mysql_mutex_unlock(&victim_thd->LOCK_thd_data);
+ DBUG_RETURN(1);
}
-my_bool wsrep_thd_is_wsrep(THD *thd)
+bool wsrep_bf_abort(const THD* bf_thd, THD* victim_thd)
{
- my_bool status = FALSE;
- if (thd)
+ WSREP_LOG_THD(bf_thd, "BF aborter before");
+ WSREP_LOG_THD(victim_thd, "victim before");
+ wsrep::seqno bf_seqno(bf_thd->wsrep_trx().ws_meta().seqno());
+
+ if (WSREP(victim_thd) && !victim_thd->wsrep_trx().active())
{
- status = (WSREP(thd) && WSREP_PROVIDER_EXISTS);
+ WSREP_DEBUG("wsrep_bf_abort, BF abort for non active transaction");
+ wsrep_start_transaction(victim_thd, victim_thd->wsrep_next_trx_id());
}
- return status;
-}
-my_bool wsrep_thd_is_BF(THD *thd, my_bool sync)
-{
- my_bool status = FALSE;
- if (thd)
+ bool ret;
+ if (wsrep_thd_is_toi(bf_thd))
{
- // THD can be BF only if provider exists
- if (wsrep_thd_is_wsrep(thd))
- {
- if (sync)
- mysql_mutex_lock(&thd->LOCK_thd_data);
-
- status = ((thd->wsrep_exec_mode == REPL_RECV) ||
- (thd->wsrep_exec_mode == TOTAL_ORDER));
- if (sync)
- mysql_mutex_unlock(&thd->LOCK_thd_data);
- }
+ ret= victim_thd->wsrep_cs().total_order_bf_abort(bf_seqno);
}
- return status;
-}
-
-extern "C"
-my_bool wsrep_thd_is_BF_or_commit(void *thd_ptr, my_bool sync)
-{
- bool status = FALSE;
- if (thd_ptr)
+ else
+ {
+ ret= victim_thd->wsrep_cs().bf_abort(bf_seqno);
+ }
+ if (ret)
{
- THD* thd = (THD*)thd_ptr;
- if (sync) mysql_mutex_lock(&thd->LOCK_thd_data);
-
- status = ((thd->wsrep_exec_mode == REPL_RECV) ||
- (thd->wsrep_exec_mode == TOTAL_ORDER) ||
- (thd->wsrep_exec_mode == LOCAL_COMMIT));
- if (sync) mysql_mutex_unlock(&thd->LOCK_thd_data);
+ wsrep_bf_aborts_counter++;
}
- return status;
+ return ret;
}
-extern "C"
-my_bool wsrep_thd_is_local(void *thd_ptr, my_bool sync)
+int wsrep_create_threadvars()
{
- bool status = FALSE;
- if (thd_ptr)
+ int ret= 0;
+ if (thread_handling == SCHEDULER_TYPES_COUNT)
{
- THD* thd = (THD*)thd_ptr;
- if (sync) mysql_mutex_lock(&thd->LOCK_thd_data);
-
- status = (thd->wsrep_exec_mode == LOCAL_STATE);
- if (sync) mysql_mutex_unlock(&thd->LOCK_thd_data);
+ /* Caller should have called wsrep_reset_threadvars() before this
+ method. */
+ DBUG_ASSERT(!pthread_getspecific(THR_KEY_mysys));
+ pthread_setspecific(THR_KEY_mysys, 0);
+ ret= my_thread_init();
}
- return status;
+ return ret;
}
-int wsrep_abort_thd(void *bf_thd_ptr, void *victim_thd_ptr, my_bool signal)
+void wsrep_delete_threadvars()
{
- THD *victim_thd = (THD *) victim_thd_ptr;
- THD *bf_thd = (THD *) bf_thd_ptr;
- DBUG_ENTER("wsrep_abort_thd");
-
- if ( (WSREP(bf_thd) ||
- ( (WSREP_ON || bf_thd->variables.wsrep_OSU_method == WSREP_OSU_RSU) &&
- bf_thd->wsrep_exec_mode == TOTAL_ORDER) ) &&
- victim_thd)
+ if (thread_handling == SCHEDULER_TYPES_COUNT)
{
- if ((victim_thd->wsrep_conflict_state == MUST_ABORT) ||
- (victim_thd->wsrep_conflict_state == ABORTED) ||
- (victim_thd->wsrep_conflict_state == ABORTING))
+ /* The caller should have called wsrep_store_threadvars() before
+ this method. */
+ DBUG_ASSERT(pthread_getspecific(THR_KEY_mysys));
+ /* Reset psi state to avoid deallocating applier thread
+ psi_thread. */
+#ifdef HAVE_PSI_INTERFACE
+ PSI_thread *psi_thread= PSI_CALL_get_thread();
+ if (PSI_server)
{
- WSREP_DEBUG("wsrep_abort_thd called by %llu with victim %llu already "
- "aborted. Ignoring.",
- (bf_thd) ? (long long)bf_thd->real_id : 0,
- (long long)victim_thd->real_id);
- DBUG_RETURN(1);
+ PSI_server->set_thread(0);
}
-
- WSREP_DEBUG("wsrep_abort_thd, by: %llu, victim: %llu", (bf_thd) ?
- (long long)bf_thd->real_id : 0, (long long)victim_thd->real_id);
- ha_abort_transaction(bf_thd, victim_thd, signal);
- }
- else
- {
- WSREP_DEBUG("wsrep_abort_thd not effective: %p %p", bf_thd, victim_thd);
+#endif /* HAVE_PSI_INTERFACE */
+ my_thread_end();
+ PSI_CALL_set_thread(psi_thread);
+ pthread_setspecific(THR_KEY_mysys, 0);
}
-
- DBUG_RETURN(1);
}
-extern "C"
-int wsrep_thd_in_locking_session(void *thd_ptr)
+void wsrep_assign_from_threadvars(THD *thd)
{
- if (thd_ptr && ((THD *)thd_ptr)->in_lock_tables) {
- return 1;
+ if (thread_handling == SCHEDULER_TYPES_COUNT)
+ {
+ st_my_thread_var *mysys_var= (st_my_thread_var *)pthread_getspecific(THR_KEY_mysys);
+ DBUG_ASSERT(mysys_var);
+ thd->set_mysys_var(mysys_var);
}
- return 0;
}
-bool wsrep_thd_has_explicit_locks(THD *thd)
+Wsrep_threadvars wsrep_save_threadvars()
{
- assert(thd);
- return thd->mdl_context.has_explicit_locks();
+ return Wsrep_threadvars{
+ current_thd,
+ (st_my_thread_var*) pthread_getspecific(THR_KEY_mysys)
+ };
}
-my_bool wsrep_thd_is_applier(MYSQL_THD thd)
+void wsrep_restore_threadvars(const Wsrep_threadvars& globals)
{
- my_bool is_applier= false;
-
- if (thd && thd->wsrep_applier)
- is_applier= true;
-
- return (is_applier);
-}
-
-void wsrep_set_load_multi_commit(THD *thd, bool split)
-{
- thd->wsrep_split_flag= split;
+ set_current_thd(globals.cur_thd);
+ pthread_setspecific(THR_KEY_mysys, globals.mysys_var);
}
-bool wsrep_is_load_multi_commit(THD *thd)
+int wsrep_store_threadvars(THD *thd)
{
- return thd->wsrep_split_flag;
+ if (thread_handling == SCHEDULER_TYPES_COUNT)
+ {
+ pthread_setspecific(THR_KEY_mysys, thd->mysys_var);
+ }
+ return thd->store_globals();
}
-void wsrep_report_bf_lock_wait(THD *thd,
- unsigned long long trx_id)
+void wsrep_reset_threadvars(THD *thd)
{
- if (thd)
+ if (thread_handling == SCHEDULER_TYPES_COUNT)
+ {
+ pthread_setspecific(THR_KEY_mysys, 0);
+ }
+ else
{
- WSREP_ERROR("Thread %s trx_id: %llu thread: %ld "
- "seqno: %lld query_state: %s conf_state: %s exec_mode: %s "
- "applier: %d query: %s",
- wsrep_thd_is_BF(thd, false) ? "BF" : "normal",
- trx_id,
- thd_get_thread_id(thd),
- wsrep_thd_trx_seqno(thd),
- wsrep_thd_query_state_str(thd),
- wsrep_thd_conflict_state_str(thd),
- wsrep_thd_exec_mode_str(thd),
- thd->wsrep_applier,
- wsrep_thd_query(thd));
+ thd->reset_globals();
}
}
diff --git a/sql/wsrep_thd.h b/sql/wsrep_thd.h
index 46bc08a466a..d24d8e6358f 100644
--- a/sql/wsrep_thd.h
+++ b/sql/wsrep_thd.h
@@ -13,46 +13,290 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. */
-#include <my_config.h>
-
#ifndef WSREP_THD_H
#define WSREP_THD_H
-#ifdef WITH_WSREP
+#include <my_config.h>
+#include "mysql/service_wsrep.h"
+#include "wsrep/client_state.hpp"
#include "sql_class.h"
+#include "wsrep_utils.h"
+#include <deque>
+class Wsrep_thd_queue
+{
+public:
+ Wsrep_thd_queue(THD* t) : thd(t)
+ {
+ mysql_mutex_init(key_LOCK_wsrep_thd_queue,
+ &LOCK_wsrep_thd_queue,
+ MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_wsrep_thd_queue, &COND_wsrep_thd_queue, NULL);
+ }
+ ~Wsrep_thd_queue()
+ {
+ mysql_mutex_destroy(&LOCK_wsrep_thd_queue);
+ mysql_cond_destroy(&COND_wsrep_thd_queue);
+ }
+ bool push_back(THD* thd)
+ {
+ DBUG_ASSERT(thd);
+ wsp::auto_lock lock(&LOCK_wsrep_thd_queue);
+ std::deque<THD*>::iterator it = queue.begin();
+ while (it != queue.end())
+ {
+ if (*it == thd)
+ {
+ return true;
+ }
+ it++;
+ }
+ queue.push_back(thd);
+ mysql_cond_signal(&COND_wsrep_thd_queue);
+ return false;
+ }
+ THD* pop_front()
+ {
+ wsp::auto_lock lock(&LOCK_wsrep_thd_queue);
+ while (queue.empty())
+ {
+ if (thd->killed != NOT_KILLED)
+ return NULL;
+
+ thd->mysys_var->current_mutex= &LOCK_wsrep_thd_queue;
+ thd->mysys_var->current_cond= &COND_wsrep_thd_queue;
+
+ mysql_cond_wait(&COND_wsrep_thd_queue, &LOCK_wsrep_thd_queue);
+
+ thd->mysys_var->current_mutex= 0;
+ thd->mysys_var->current_cond= 0;
+ }
+ THD* ret= queue.front();
+ queue.pop_front();
+ return ret;
+ }
+private:
+ THD* thd;
+ std::deque<THD*> queue;
+ mysql_mutex_t LOCK_wsrep_thd_queue;
+ mysql_cond_t COND_wsrep_thd_queue;
+};
int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff,
enum enum_var_type scope);
-void wsrep_client_rollback(THD *thd);
-void wsrep_replay_sp_transaction(THD* thd);
-void wsrep_replay_transaction(THD *thd);
-bool wsrep_create_appliers(long threads, bool thread_count_lock=false);
+bool wsrep_create_appliers(long threads, bool mutex_protected=false);
void wsrep_create_rollbacker();
-int wsrep_abort_thd(void *bf_thd_ptr, void *victim_thd_ptr,
- my_bool signal);
+bool wsrep_bf_abort(const THD*, THD*);
+int wsrep_abort_thd(THD *bf_thd_ptr, THD *victim_thd_ptr, my_bool signal);
+
+extern void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe);
/*
- PA = Parallel Applying (on the slave side)
+ Helper methods to deal with thread local storage.
+ The purpose of these methods is to hide the details of thread
+ local storage handling when operating with wsrep storage access
+ and streaming applier THDs
+
+ With one-thread-per-connection thread handling thread specific
+ variables are allocated when the thread is started and deallocated
+ before thread exits (my_thread_init(), my_thread_end()). However,
+ with pool-of-threads thread handling new thread specific variables
+ are allocated for each THD separately (see threadpool_add_connection()),
+ and the variables in thread local storage are assigned from
+ currently active thread (see thread_attach()). This must be taken into
+ account when storing/resetting thread local storage and when creating
+ streaming applier THDs.
*/
-extern void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe);
-extern my_bool wsrep_thd_is_BF(THD *thd, my_bool sync);
-extern my_bool wsrep_thd_is_wsrep(void *thd_ptr);
-enum wsrep_conflict_state wsrep_thd_conflict_state(void *thd_ptr, my_bool sync);
-extern "C" my_bool wsrep_thd_is_BF_or_commit(void *thd_ptr, my_bool sync);
-extern "C" my_bool wsrep_thd_is_local(void *thd_ptr, my_bool sync);
-extern "C" int wsrep_thd_in_locking_session(void *thd_ptr);
+/**
+ Create new variables for thread local storage. With
+ one-thread-per-connection thread handling this is a no op,
+ with pool-of-threads new variables are created via my_thread_init().
+ It is assumed that the caller has called wsrep_reset_threadvars() to clear
+ the thread local storage before this call.
+
+ @return Zero in case of success, non-zero otherwise.
+*/
+int wsrep_create_threadvars();
+
+/**
+ Delete variables which were created by wsrep_create_threadvars().
+ The caller must store variables into thread local storage before
+ this call via wsrep_store_threadvars().
+*/
+void wsrep_delete_threadvars();
+
+/**
+ Assign variables from current thread local storage into THD.
+ This should be called for THDs whose lifetime is limited to single
+ thread execution or which may share the operation context with some
+ parent THD (e.g. storage access) and thus don't require separately
+ allocated globals.
+
+ With one-thread-per-connection thread handling this is a no-op,
+ with pool-of-threads the variables which are currently stored into
+ thread local storage are assigned to THD.
+*/
+void wsrep_assign_from_threadvars(THD *);
+
+/**
+ Helper struct to save variables from thread local storage.
+ */
+struct Wsrep_threadvars
+{
+ THD* cur_thd;
+ st_my_thread_var* mysys_var;
+};
+
+/**
+ Save variables from thread local storage into Wsrep_threadvars struct.
+ */
+Wsrep_threadvars wsrep_save_threadvars();
+
+/**
+ Restore variables into thread local storage from Wsrep_threadvars struct.
+*/
+void wsrep_restore_threadvars(const Wsrep_threadvars&);
+
+/**
+ Store variables into thread local storage.
+*/
+int wsrep_store_threadvars(THD *);
+
+/**
+ Reset thread local storage.
+*/
+void wsrep_reset_threadvars(THD *);
+
+/**
+ Helper functions to override error status
+
+ In many contexts it is desirable to mask the original error status
+ set for THD or it is necessary to change OK status to error.
+ This function implements the common logic for the most
+ of the cases.
+
+ Rules:
+ * If the diagnostics are has OK or EOF status, override it unconditionally
+ * If the error is either ER_ERROR_DURING_COMMIT or ER_LOCK_DEADLOCK
+ it is usually the correct error status to be returned to client,
+ so don't override those by default
+ */
+
+static inline void wsrep_override_error(THD *thd, uint error)
+{
+ DBUG_ASSERT(error != ER_ERROR_DURING_COMMIT);
+ Diagnostics_area *da= thd->get_stmt_da();
+ if (da->is_ok() ||
+ da->is_eof() ||
+ !da->is_set() ||
+ (da->is_error() &&
+ da->sql_errno() != error &&
+ da->sql_errno() != ER_ERROR_DURING_COMMIT &&
+ da->sql_errno() != ER_LOCK_DEADLOCK))
+ {
+ da->reset_diagnostics_area();
+ my_error(error, MYF(0));
+ }
+}
+
+/**
+ Override error with additional wsrep status.
+ */
+static inline void wsrep_override_error(THD *thd, uint error,
+ enum wsrep::provider::status status)
+{
+ Diagnostics_area *da= thd->get_stmt_da();
+ if (da->is_ok() ||
+ !da->is_set() ||
+ (da->is_error() &&
+ da->sql_errno() != error &&
+ da->sql_errno() != ER_ERROR_DURING_COMMIT &&
+ da->sql_errno() != ER_LOCK_DEADLOCK))
+ {
+ da->reset_diagnostics_area();
+ my_error(error, MYF(0), status);
+ }
+}
+
+static inline void wsrep_override_error(THD* thd,
+ wsrep::client_error ce,
+ enum wsrep::provider::status status)
+{
+ DBUG_ASSERT(ce != wsrep::e_success);
+ switch (ce)
+ {
+ case wsrep::e_error_during_commit:
+ wsrep_override_error(thd, ER_ERROR_DURING_COMMIT, status);
+ break;
+ case wsrep::e_deadlock_error:
+ wsrep_override_error(thd, ER_LOCK_DEADLOCK);
+ break;
+ case wsrep::e_interrupted_error:
+ wsrep_override_error(thd, ER_QUERY_INTERRUPTED);
+ break;
+ case wsrep::e_size_exceeded_error:
+ wsrep_override_error(thd, ER_ERROR_DURING_COMMIT, status);
+ break;
+ case wsrep::e_append_fragment_error:
+ /* TODO: Figure out better error number */
+ wsrep_override_error(thd, ER_ERROR_DURING_COMMIT, status);
+ break;
+ case wsrep::e_not_supported_error:
+ wsrep_override_error(thd, ER_NOT_SUPPORTED_YET);
+ break;
+ case wsrep::e_timeout_error:
+ wsrep_override_error(thd, ER_LOCK_WAIT_TIMEOUT);
+ break;
+ default:
+ wsrep_override_error(thd, ER_UNKNOWN_ERROR);
+ break;
+ }
+}
-extern void wsrep_report_bf_lock_wait(THD *thd,
- unsigned long long trx_id);
+/**
+ Helper function to log THD wsrep context.
-#else /* WITH_WSREP */
+ @param thd Pointer to THD
+ @param message Optional message
+ @param function Function where the call was made from
+ */
+static inline void wsrep_log_thd(const THD *thd,
+ const char *message,
+ const char *function)
+{
+ WSREP_DEBUG("%s %s\n"
+ " thd: %llu thd_ptr: %p client_mode: %s client_state: %s trx_state: %s\n"
+ " next_trx_id: %lld trx_id: %lld seqno: %lld\n"
+ " is_streaming: %d fragments: %zu\n"
+ " sql_errno: %u message: %s\n"
+#define WSREP_THD_LOG_QUERIES
+#ifdef WSREP_THD_LOG_QUERIES
+ " command: %d query: %.72s"
+#endif /* WSREP_OBSERVER_LOG_QUERIES */
+ ,
+ function,
+ message ? message : "",
+ thd->thread_id,
+ thd,
+ wsrep_thd_client_mode_str(thd),
+ wsrep_thd_client_state_str(thd),
+ wsrep_thd_transaction_state_str(thd),
+ (long long)thd->wsrep_next_trx_id(),
+ (long long)thd->wsrep_trx_id(),
+ (long long)wsrep_thd_trx_seqno(thd),
+ thd->wsrep_trx().is_streaming(),
+ thd->wsrep_sr().fragments().size(),
+ (thd->get_stmt_da()->is_error() ? thd->get_stmt_da()->sql_errno() : 0),
+ (thd->get_stmt_da()->is_error() ? thd->get_stmt_da()->message() : "")
+#ifdef WSREP_THD_LOG_QUERIES
+ , thd->lex->sql_command,
+ WSREP_QUERY(thd)
+#endif /* WSREP_OBSERVER_LOG_QUERIES */
+ );
+}
-#define wsrep_thd_is_BF(T, S) (0)
-#define wsrep_abort_thd(X,Y,Z) do { } while(0)
-#define wsrep_create_appliers(T) do { } while(0)
+#define WSREP_LOG_THD(thd_, message_) wsrep_log_thd(thd_, message_, __FUNCTION__)
-#endif
#endif /* WSREP_THD_H */
diff --git a/sql/wsrep_trans_observer.h b/sql/wsrep_trans_observer.h
new file mode 100644
index 00000000000..8157c14fcdf
--- /dev/null
+++ b/sql/wsrep_trans_observer.h
@@ -0,0 +1,556 @@
+/* Copyright 2016-2019 Codership Oy <http://www.codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef WSREP_TRANS_OBSERVER_H
+#define WSREP_TRANS_OBSERVER_H
+
+#include "my_global.h"
+#include "mysql/service_wsrep.h"
+#include "wsrep_applier.h" /* wsrep_apply_error */
+#include "wsrep_xid.h"
+#include "wsrep_thd.h"
+#include "wsrep_binlog.h" /* register/deregister group commit */
+#include "my_dbug.h"
+
+class THD;
+
+/*
+ Return true if THD has active wsrep transaction.
+ */
+static inline bool wsrep_is_active(THD* thd)
+{
+ return (thd->wsrep_cs().state() != wsrep::client_state::s_none &&
+ thd->wsrep_cs().transaction().active());
+}
+
+/*
+ Return true if transaction is ordered.
+ */
+static inline bool wsrep_is_ordered(THD* thd)
+{
+ return thd->wsrep_trx().ordered();
+}
+
+/*
+ Return true if transaction has been BF aborted but has not been
+ rolled back yet.
+
+ It is required that the caller holds thd->LOCK_thd_data.
+*/
+static inline bool wsrep_must_abort(THD* thd)
+{
+ mysql_mutex_assert_owner(&thd->LOCK_thd_data);
+ return (thd->wsrep_trx().state() == wsrep::transaction::s_must_abort);
+}
+
+/*
+ Return true if the transaction must be replayed.
+ */
+static inline bool wsrep_must_replay(THD* thd)
+{
+ return (thd->wsrep_trx().state() == wsrep::transaction::s_must_replay);
+}
+/*
+ Return true if transaction has not been committed.
+
+ Note that we don't require thd->LOCK_thd_data here. Calling this method
+ makes sense only from codepaths which are past ordered_commit state
+ and the wsrep transaction is immune to BF aborts at that point.
+*/
+static inline bool wsrep_not_committed(THD* thd)
+{
+ return (thd->wsrep_trx().state() != wsrep::transaction::s_committed);
+}
+
+/*
+ Return true if THD is either committing a transaction or statement
+ is autocommit.
+ */
+static inline bool wsrep_is_real(THD* thd, bool all)
+{
+ return (all || thd->transaction.all.ha_list == 0);
+}
+
+/*
+ Check if a transaction has generated changes.
+ */
+static inline bool wsrep_has_changes(THD* thd)
+{
+ return (thd->wsrep_trx().is_empty() == false);
+}
+
+/*
+ Check if an active transaction has been BF aborted.
+ */
+static inline bool wsrep_is_bf_aborted(THD* thd)
+{
+ return (thd->wsrep_trx().active() && thd->wsrep_trx().bf_aborted());
+}
+
+static inline int wsrep_check_pk(THD* thd)
+{
+ if (!wsrep_certify_nonPK)
+ {
+ for (TABLE* table= thd->open_tables; table != NULL; table= table->next)
+ {
+ if (table->key_info == NULL || table->s->primary_key == MAX_KEY)
+ {
+ WSREP_DEBUG("No primary key found for table %s.%s",
+ table->s->db.str, table->s->table_name.str);
+ wsrep_override_error(thd, ER_LOCK_DEADLOCK);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static inline bool wsrep_streaming_enabled(THD* thd)
+{
+ return (thd->wsrep_sr().fragment_size() > 0);
+}
+
+/*
+ Return number of fragments succesfully certified for the
+ current statement.
+ */
+static inline size_t wsrep_fragments_certified_for_stmt(THD* thd)
+{
+ return thd->wsrep_trx().fragments_certified_for_statement();
+}
+
+static inline int wsrep_start_transaction(THD* thd, wsrep_trx_id_t trx_id)
+{
+ if (thd->wsrep_cs().state() != wsrep::client_state::s_none) {
+ if (wsrep_is_active(thd) == false)
+ return thd->wsrep_cs().start_transaction(wsrep::transaction_id(trx_id));
+ }
+ return 0;
+}
+
+/**/
+static inline int wsrep_start_trx_if_not_started(THD* thd)
+{
+ int ret= 0;
+ DBUG_ASSERT(thd->wsrep_next_trx_id() != WSREP_UNDEFINED_TRX_ID);
+ DBUG_ASSERT(thd->wsrep_cs().mode() == Wsrep_client_state::m_local);
+ if (thd->wsrep_trx().active() == false)
+ {
+ ret= wsrep_start_transaction(thd, thd->wsrep_next_trx_id());
+ }
+ return ret;
+}
+
+/*
+ Called after each row operation.
+
+ Return zero on succes, non-zero on failure.
+ */
+static inline int wsrep_after_row(THD* thd, bool)
+{
+ if (thd->wsrep_cs().state() != wsrep::client_state::s_none &&
+ wsrep_thd_is_local(thd))
+ {
+ if (wsrep_check_pk(thd))
+ {
+ return 1;
+ }
+ else if (wsrep_streaming_enabled(thd))
+ {
+ return thd->wsrep_cs().after_row();
+ }
+ }
+ return 0;
+}
+
+/*
+ Helper method to determine whether commit time hooks
+ should be run for the transaction.
+
+ Commit hooks must be run in the following cases:
+ - The transaction is local and has generated write set and is committing.
+ - The transaction has been BF aborted
+ - Is running in high priority mode and is ordered. This can be replayer,
+ applier or storage access.
+ */
+static inline bool wsrep_run_commit_hook(THD* thd, bool all)
+{
+ DBUG_ENTER("wsrep_run_commit_hook");
+ DBUG_PRINT("wsrep", ("Is_active: %d is_real %d has_changes %d is_applying %d "
+ "is_ordered: %d",
+ wsrep_is_active(thd), wsrep_is_real(thd, all),
+ wsrep_has_changes(thd), wsrep_thd_is_applying(thd),
+ wsrep_is_ordered(thd)));
+ /* Is MST commit or autocommit? */
+ bool ret= wsrep_is_active(thd) && wsrep_is_real(thd, all);
+ /* Do not commit if we are aborting */
+ ret= ret && (thd->wsrep_trx().state() != wsrep::transaction::s_aborting);
+ if (ret && !(wsrep_has_changes(thd) || /* Has generated write set */
+ /* Is high priority (replay, applier, storage) and the
+ transaction is scheduled for commit ordering */
+ (wsrep_thd_is_applying(thd) && wsrep_is_ordered(thd))))
+ {
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ DBUG_PRINT("wsrep", ("state: %s",
+ wsrep::to_c_string(thd->wsrep_trx().state())));
+ /* Transaction is local but has no changes, the commit hooks will
+ be skipped and the wsrep transaction is terminated in
+ wsrep_commit_empty() */
+ if (thd->wsrep_trx().state() == wsrep::transaction::s_executing)
+ {
+ ret= false;
+ }
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ }
+ DBUG_PRINT("wsrep", ("return: %d", ret));
+ DBUG_RETURN(ret);
+}
+
+/*
+ Called before the transaction is prepared.
+
+ Return zero on succes, non-zero on failure.
+ */
+static inline int wsrep_before_prepare(THD* thd, bool all)
+{
+ DBUG_ENTER("wsrep_before_prepare");
+ WSREP_DEBUG("wsrep_before_prepare: %d", wsrep_is_real(thd, all));
+ int ret= 0;
+ DBUG_ASSERT(wsrep_run_commit_hook(thd, all));
+ if ((ret= thd->wsrep_cs().before_prepare()) == 0)
+ {
+ DBUG_ASSERT(!thd->wsrep_trx().ws_meta().gtid().is_undefined());
+ wsrep_xid_init(&thd->wsrep_xid,
+ thd->wsrep_trx().ws_meta().gtid());
+ }
+ DBUG_RETURN(ret);
+}
+
+/*
+ Called after the transaction has been prepared.
+
+ Return zero on succes, non-zero on failure.
+ */
+static inline int wsrep_after_prepare(THD* thd, bool all)
+{
+ DBUG_ENTER("wsrep_after_prepare");
+ WSREP_DEBUG("wsrep_after_prepare: %d", wsrep_is_real(thd, all));
+ DBUG_ASSERT(wsrep_run_commit_hook(thd, all));
+ int ret= thd->wsrep_cs().after_prepare();
+ DBUG_ASSERT(ret == 0 || thd->wsrep_cs().current_error() ||
+ thd->wsrep_cs().transaction().state() == wsrep::transaction::s_must_replay);
+ DBUG_RETURN(ret);
+}
+
+/*
+ Called before the transaction is committed.
+
+ This function must be called from both client and
+ applier contexts before commit.
+
+ Return zero on succes, non-zero on failure.
+ */
+static inline int wsrep_before_commit(THD* thd, bool all)
+{
+ DBUG_ENTER("wsrep_before_commit");
+ WSREP_DEBUG("wsrep_before_commit: %d, %lld",
+ wsrep_is_real(thd, all),
+ (long long)wsrep_thd_trx_seqno(thd));
+ int ret= 0;
+ DBUG_ASSERT(wsrep_run_commit_hook(thd, all));
+ if ((ret= thd->wsrep_cs().before_commit()) == 0)
+ {
+ DBUG_ASSERT(!thd->wsrep_trx().ws_meta().gtid().is_undefined());
+ wsrep_xid_init(&thd->wsrep_xid,
+ thd->wsrep_trx().ws_meta().gtid());
+ wsrep_register_for_group_commit(thd);
+ }
+ DBUG_RETURN(ret);
+}
+
+/*
+ Called after the transaction has been ordered for commit.
+
+ This function must be called from both client and
+ applier contexts after the commit has been ordered.
+
+ @param thd Pointer to THD
+ @param all
+ @param err Error buffer in case of applying error
+
+ Return zero on succes, non-zero on failure.
+ */
+static inline int wsrep_ordered_commit(THD* thd,
+ bool all,
+ const wsrep_apply_error&)
+{
+ DBUG_ENTER("wsrep_ordered_commit");
+ WSREP_DEBUG("wsrep_ordered_commit: %d", wsrep_is_real(thd, all));
+ DBUG_ASSERT(wsrep_run_commit_hook(thd, all));
+ DBUG_RETURN(thd->wsrep_cs().ordered_commit());
+}
+
+/*
+ Called after the transaction has been committed.
+
+ Return zero on succes, non-zero on failure.
+ */
+static inline int wsrep_after_commit(THD* thd, bool all)
+{
+ DBUG_ENTER("wsrep_after_commit");
+ WSREP_DEBUG("wsrep_after_commit: %d, %d, %lld, %d",
+ wsrep_is_real(thd, all),
+ wsrep_is_active(thd),
+ (long long)wsrep_thd_trx_seqno(thd),
+ wsrep_has_changes(thd));
+ DBUG_ASSERT(wsrep_run_commit_hook(thd, all));
+ int ret= 0;
+ if (thd->wsrep_trx().state() == wsrep::transaction::s_committing)
+ {
+ ret= thd->wsrep_cs().ordered_commit();
+ }
+ wsrep_unregister_from_group_commit(thd);
+ thd->wsrep_xid.null();
+ DBUG_RETURN(ret || thd->wsrep_cs().after_commit());
+}
+
+/*
+ Called before the transaction is rolled back.
+
+ Return zero on succes, non-zero on failure.
+ */
+static inline int wsrep_before_rollback(THD* thd, bool all)
+{
+ DBUG_ENTER("wsrep_before_rollback");
+ int ret= 0;
+ if (wsrep_is_active(thd))
+ {
+ if (!all && thd->in_active_multi_stmt_transaction())
+ {
+ if (wsrep_emulate_bin_log)
+ {
+ wsrep_thd_binlog_stmt_rollback(thd);
+ }
+
+ if (thd->wsrep_trx().is_streaming() &&
+ (wsrep_fragments_certified_for_stmt(thd) > 0))
+ {
+ /* Non-safe statement rollback during SR multi statement
+ transaction. A statement rollback is considered unsafe, if
+ the same statement has already replicated one or more fragments.
+ Self abort the transaction, the actual rollback and error
+ handling will be done in after statement phase. */
+ WSREP_DEBUG("statement rollback is not safe for streaming replication");
+ wsrep_thd_self_abort(thd);
+ ret= 0;
+ }
+ }
+ else if (wsrep_is_real(thd, all) &&
+ thd->wsrep_trx().state() != wsrep::transaction::s_aborted)
+ {
+ /* Real transaction rolling back and wsrep abort not completed
+ yet */
+ /* Reset XID so that it does not trigger writing serialization
+ history in InnoDB. This needs to be avoided because rollback
+ may happen out of order and replay may follow. */
+ thd->wsrep_xid.null();
+ ret= thd->wsrep_cs().before_rollback();
+ }
+ }
+ DBUG_RETURN(ret);
+}
+
+/*
+ Called after the transaction has been rolled back.
+
+ Return zero on succes, non-zero on failure.
+ */
+static inline int wsrep_after_rollback(THD* thd, bool all)
+{
+ DBUG_ENTER("wsrep_after_rollback");
+ DBUG_RETURN((wsrep_is_real(thd, all) && wsrep_is_active(thd) &&
+ thd->wsrep_cs().transaction().state() !=
+ wsrep::transaction::s_aborted) ?
+ thd->wsrep_cs().after_rollback() : 0);
+}
+
+static inline int wsrep_before_statement(THD* thd)
+{
+ return (thd->wsrep_cs().state() != wsrep::client_state::s_none ?
+ thd->wsrep_cs().before_statement() : 0);
+}
+
+static inline
+int wsrep_after_statement(THD* thd)
+{
+ DBUG_ENTER("wsrep_after_statement");
+ DBUG_RETURN(thd->wsrep_cs().state() != wsrep::client_state::s_none ?
+ thd->wsrep_cs().after_statement() : 0);
+}
+
+static inline void wsrep_after_apply(THD* thd)
+{
+ DBUG_ASSERT(wsrep_thd_is_applying(thd));
+ WSREP_DEBUG("wsrep_after_apply %lld", thd->thread_id);
+ thd->wsrep_cs().after_applying();
+}
+
+static inline void wsrep_open(THD* thd)
+{
+ DBUG_ENTER("wsrep_open");
+ if (WSREP_ON_)
+ {
+ /* WSREP_PROVIDER_EXISTS_ cannot be set if WSREP_ON_ is not set */
+ DBUG_ASSERT(WSREP_PROVIDER_EXISTS_);
+ thd->wsrep_cs().open(wsrep::client_id(thd->thread_id));
+ thd->wsrep_cs().debug_log_level(wsrep_debug);
+ if (!thd->wsrep_applier && thd->variables.wsrep_trx_fragment_size)
+ {
+ thd->wsrep_cs().enable_streaming(
+ wsrep_fragment_unit(thd->variables.wsrep_trx_fragment_unit),
+ size_t(thd->variables.wsrep_trx_fragment_size));
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+static inline void wsrep_close(THD* thd)
+{
+ DBUG_ENTER("wsrep_close");
+ if (thd->wsrep_cs().state() != wsrep::client_state::s_none)
+ {
+ thd->wsrep_cs().close();
+ }
+ DBUG_VOID_RETURN;
+}
+
+static inline void wsrep_cleanup(THD* thd)
+{
+ DBUG_ENTER("wsrep_cleanup");
+ if (thd->wsrep_cs().state() != wsrep::client_state::s_none)
+ {
+ thd->wsrep_cs().cleanup();
+ }
+ DBUG_VOID_RETURN;
+}
+
+static inline void
+wsrep_wait_rollback_complete_and_acquire_ownership(THD *thd)
+{
+ DBUG_ENTER("wsrep_wait_rollback_complete_and_acquire_ownership");
+ if (thd->wsrep_cs().state() != wsrep::client_state::s_none)
+ {
+ thd->wsrep_cs().wait_rollback_complete_and_acquire_ownership();
+ }
+ DBUG_VOID_RETURN;
+}
+
+static inline int wsrep_before_command(THD* thd, bool keep_command_error)
+{
+ return (thd->wsrep_cs().state() != wsrep::client_state::s_none ?
+ thd->wsrep_cs().before_command(keep_command_error) : 0);
+}
+
+static inline int wsrep_before_command(THD* thd)
+{
+ return wsrep_before_command(thd, false);
+}
+
+/*
+ Called after each command.
+
+ Return zero on success, non-zero on failure.
+*/
+static inline void wsrep_after_command_before_result(THD* thd)
+{
+ if (thd->wsrep_cs().state() != wsrep::client_state::s_none)
+ {
+ thd->wsrep_cs().after_command_before_result();
+ }
+}
+
+static inline void wsrep_after_command_after_result(THD* thd)
+{
+ if (thd->wsrep_cs().state() != wsrep::client_state::s_none)
+ {
+ thd->wsrep_cs().after_command_after_result();
+ }
+}
+
+static inline void wsrep_after_command_ignore_result(THD* thd)
+{
+ wsrep_after_command_before_result(thd);
+ DBUG_ASSERT(!thd->wsrep_cs().current_error());
+ wsrep_after_command_after_result(thd);
+}
+
+static inline enum wsrep::client_error wsrep_current_error(THD* thd)
+{
+ return thd->wsrep_cs().current_error();
+}
+
+static inline enum wsrep::provider::status
+wsrep_current_error_status(THD* thd)
+{
+ return thd->wsrep_cs().current_error_status();
+}
+
+
+/*
+ Commit an empty transaction.
+
+ If the transaction is real and the wsrep transaction is still active,
+ the transaction did not generate any rows or keys and is committed
+ as empty. Here the wsrep transaction is rolled back and after statement
+ step is performed to leave the wsrep transaction in the state as it
+ never existed.
+*/
+static inline void wsrep_commit_empty(THD* thd, bool all)
+{
+ DBUG_ENTER("wsrep_commit_empty");
+ WSREP_DEBUG("wsrep_commit_empty(%llu)", thd->thread_id);
+ if (wsrep_is_real(thd, all) &&
+ wsrep_thd_is_local(thd) &&
+ thd->wsrep_trx().active() &&
+ thd->wsrep_trx().state() != wsrep::transaction::s_committed)
+ {
+ /* @todo CTAS with STATEMENT binlog format and empty result set
+ seems to be committing empty. Figure out why and try to fix
+ elsewhere. */
+ DBUG_ASSERT(!wsrep_has_changes(thd) ||
+ (thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
+ !thd->is_current_stmt_binlog_format_row()));
+ bool have_error= wsrep_current_error(thd);
+ int ret= wsrep_before_rollback(thd, all) ||
+ wsrep_after_rollback(thd, all) ||
+ wsrep_after_statement(thd);
+ /* The committing transaction was empty but it held some locks and
+ got BF aborted. As there were no certified changes in the
+ data, we ignore the deadlock error and rely on error reporting
+ by storage engine/server. */
+ if (!ret && !have_error && wsrep_current_error(thd))
+ {
+ DBUG_ASSERT(wsrep_current_error(thd) == wsrep::e_deadlock_error);
+ thd->wsrep_cs().reset_error();
+ }
+ if (ret)
+ {
+ WSREP_DEBUG("wsrep_commit_empty failed: %d", wsrep_current_error(thd));
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+#endif /* WSREP_TRANS_OBSERVER */
diff --git a/sql/wsrep_types.h b/sql/wsrep_types.h
new file mode 100644
index 00000000000..9da00e305a7
--- /dev/null
+++ b/sql/wsrep_types.h
@@ -0,0 +1,29 @@
+/* Copyright 2018 Codership Oy <info@codership.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 */
+
+/*
+ Wsrep typedefs to better conform to coding style.
+ */
+#ifndef WSREP_TYPES_H
+#define WSREP_TYPES_H
+
+#include "wsrep/seqno.hpp"
+#include "wsrep/view.hpp"
+
+typedef wsrep::id Wsrep_id;
+typedef wsrep::seqno Wsrep_seqno;
+typedef wsrep::view Wsrep_view;
+
+#endif /* WSREP_TYPES_H */
diff --git a/sql/wsrep_utils.cc b/sql/wsrep_utils.cc
index ae8fb63ef34..4cf131fd974 100644
--- a/sql/wsrep_utils.cc
+++ b/sql/wsrep_utils.cc
@@ -21,8 +21,11 @@
#endif
#include "mariadb.h"
+#include "my_global.h"
+#include "wsrep_api.h"
#include "wsrep_utils.h"
#include "wsrep_mysqld.h"
+#include "wsrep_thd.h"
#include <sql_class.h>
@@ -47,7 +50,7 @@ static wsp::string wsrep_PATH;
void
wsrep_prepend_PATH (const char* path)
{
- int count = 0;
+ int count= 0;
while (environ[count])
{
@@ -72,7 +75,7 @@ wsrep_prepend_PATH (const char* path)
old_path + strlen("PATH="));
wsrep_PATH.set (new_path);
- environ[count] = new_path;
+ environ[count]= new_path;
}
else
{
@@ -93,28 +96,28 @@ namespace wsp
bool
env::ctor_common(char** e)
{
- env_ = static_cast<char**>(malloc((len_ + 1) * sizeof(char*)));
+ env_= static_cast<char**>(malloc((len_ + 1) * sizeof(char*)));
if (env_)
{
for (size_t i(0); i < len_; ++i)
{
assert(e[i]); // caller should make sure about len_
- env_[i] = strdup(e[i]);
+ env_[i]= strdup(e[i]);
if (!env_[i])
{
- errno_ = errno;
+ errno_= errno;
WSREP_ERROR("Failed to allocate env. var: %s", e[i]);
return true;
}
}
- env_[len_] = NULL;
+ env_[len_]= NULL;
return false;
}
else
{
- errno_ = errno;
+ errno_= errno;
WSREP_ERROR("Failed to allocate env. var vector of length: %zu", len_);
return true;
}
@@ -128,15 +131,15 @@ env::dtor()
/* don't need to go beyond the first NULL */
for (size_t i(0); env_[i] != NULL; ++i) { free(env_[i]); }
free(env_);
- env_ = NULL;
+ env_= NULL;
}
- len_ = 0;
+ len_= 0;
}
env::env(char** e)
: len_(0), env_(NULL), errno_(0)
{
- if (!e) { e = environ; }
+ if (!e) { e= environ; }
/* count the size of the vector */
while (e[len_]) { ++len_; }
@@ -154,21 +157,21 @@ env::~env() { dtor(); }
int
env::append(const char* val)
{
- char** tmp = static_cast<char**>(realloc(env_, (len_ + 2)*sizeof(char*)));
+ char** tmp= static_cast<char**>(realloc(env_, (len_ + 2)*sizeof(char*)));
if (tmp)
{
- env_ = tmp;
- env_[len_] = strdup(val);
+ env_= tmp;
+ env_[len_]= strdup(val);
if (env_[len_])
{
++len_;
- env_[len_] = NULL;
+ env_[len_]= NULL;
}
- else errno_ = errno;
+ else errno_= errno;
}
- else errno_ = errno;
+ else errno_= errno;
return errno_;
}
@@ -189,7 +192,7 @@ process::process (const char* cmd, const char* type, char** env)
if (0 == str_)
{
WSREP_ERROR ("Can't allocate command line of size: %zu", strlen(cmd));
- err_ = ENOMEM;
+ err_= ENOMEM;
return;
}
@@ -205,12 +208,12 @@ process::process (const char* cmd, const char* type, char** env)
return;
}
- if (NULL == env) { env = environ; } // default to global environment
+ if (NULL == env) { env= environ; } // default to global environment
- int pipe_fds[2] = { -1, };
+ int pipe_fds[2]= { -1, };
if (::pipe(pipe_fds))
{
- err_ = errno;
+ err_= errno;
WSREP_ERROR ("pipe() failed: %d (%s)", err_, strerror(err_));
return;
}
@@ -220,16 +223,16 @@ process::process (const char* cmd, const char* type, char** env)
int const child_end (parent_end == PIPE_READ ? PIPE_WRITE : PIPE_READ);
int const close_fd (parent_end == PIPE_READ ? STDOUT_FD : STDIN_FD);
- char* const pargv[4] = { strdup("sh"), strdup("-c"), strdup(str_), NULL };
+ char* const pargv[4]= { strdup("sh"), strdup("-c"), strdup(str_), NULL };
if (!(pargv[0] && pargv[1] && pargv[2]))
{
- err_ = ENOMEM;
+ err_= ENOMEM;
WSREP_ERROR ("Failed to allocate pargv[] array.");
goto cleanup_pipe;
}
posix_spawnattr_t attr;
- err_ = posix_spawnattr_init (&attr);
+ err_= posix_spawnattr_init (&attr);
if (err_)
{
WSREP_ERROR ("posix_spawnattr_init() failed: %d (%s)",
@@ -239,7 +242,7 @@ process::process (const char* cmd, const char* type, char** env)
/* make sure that no signlas are masked in child process */
sigset_t sigmask_empty; sigemptyset(&sigmask_empty);
- err_ = posix_spawnattr_setsigmask(&attr, &sigmask_empty);
+ err_= posix_spawnattr_setsigmask(&attr, &sigmask_empty);
if (err_)
{
WSREP_ERROR ("posix_spawnattr_setsigmask() failed: %d (%s)",
@@ -255,7 +258,7 @@ process::process (const char* cmd, const char* type, char** env)
sigaddset(&default_signals, SIGPIPE);
sigaddset(&default_signals, SIGTERM);
sigaddset(&default_signals, SIGCHLD);
- err_ = posix_spawnattr_setsigdefault(&attr, &default_signals);
+ err_= posix_spawnattr_setsigdefault(&attr, &default_signals);
if (err_)
{
WSREP_ERROR ("posix_spawnattr_setsigdefault() failed: %d (%s)",
@@ -263,7 +266,7 @@ process::process (const char* cmd, const char* type, char** env)
goto cleanup_attr;
}
- err_ = posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETSIGDEF |
+ err_= posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETSIGDEF |
POSIX_SPAWN_SETSIGMASK |
POSIX_SPAWN_USEVFORK);
if (err_)
@@ -274,7 +277,7 @@ process::process (const char* cmd, const char* type, char** env)
}
posix_spawn_file_actions_t fact;
- err_ = posix_spawn_file_actions_init (&fact);
+ err_= posix_spawn_file_actions_init (&fact);
if (err_)
{
WSREP_ERROR ("posix_spawn_file_actions_init() failed: %d (%s)",
@@ -283,7 +286,7 @@ process::process (const char* cmd, const char* type, char** env)
}
// close child's stdout|stdin depending on what we returning
- err_ = posix_spawn_file_actions_addclose (&fact, close_fd);
+ err_= posix_spawn_file_actions_addclose (&fact, close_fd);
if (err_)
{
WSREP_ERROR ("posix_spawn_file_actions_addclose() failed: %d (%s)",
@@ -292,7 +295,7 @@ process::process (const char* cmd, const char* type, char** env)
}
// substitute our pipe descriptor in place of the closed one
- err_ = posix_spawn_file_actions_adddup2 (&fact,
+ err_= posix_spawn_file_actions_adddup2 (&fact,
pipe_fds[child_end], close_fd);
if (err_)
{
@@ -301,30 +304,30 @@ process::process (const char* cmd, const char* type, char** env)
goto cleanup_fact;
}
- err_ = posix_spawnp (&pid_, pargv[0], &fact, &attr, pargv, env);
+ err_= posix_spawnp (&pid_, pargv[0], &fact, &attr, pargv, env);
if (err_)
{
WSREP_ERROR ("posix_spawnp(%s) failed: %d (%s)",
pargv[2], err_, strerror(err_));
- pid_ = 0; // just to make sure it was not messed up in the call
+ pid_= 0; // just to make sure it was not messed up in the call
goto cleanup_fact;
}
- io_ = fdopen (pipe_fds[parent_end], type);
+ io_= fdopen (pipe_fds[parent_end], type);
if (io_)
{
- pipe_fds[parent_end] = -1; // skip close on cleanup
+ pipe_fds[parent_end]= -1; // skip close on cleanup
}
else
{
- err_ = errno;
+ err_= errno;
WSREP_ERROR ("fdopen() failed: %d (%s)", err_, strerror(err_));
}
cleanup_fact:
int err; // to preserve err_ code
- err = posix_spawn_file_actions_destroy (&fact);
+ err= posix_spawn_file_actions_destroy (&fact);
if (err)
{
WSREP_ERROR ("posix_spawn_file_actions_destroy() failed: %d (%s)\n",
@@ -332,7 +335,7 @@ cleanup_fact:
}
cleanup_attr:
- err = posix_spawnattr_destroy (&attr);
+ err= posix_spawnattr_destroy (&attr);
if (err)
{
WSREP_ERROR ("posix_spawnattr_destroy() failed: %d (%s)",
@@ -360,7 +363,7 @@ process::~process ()
if (fclose (io_) == -1)
{
- err_ = errno;
+ err_= errno;
WSREP_ERROR("fclose() failed: %d (%s)", err_, strerror(err_));
}
}
@@ -376,34 +379,34 @@ process::wait ()
int status;
if (-1 == waitpid(pid_, &status, 0))
{
- err_ = errno; assert (err_);
+ err_= errno; assert (err_);
WSREP_ERROR("Waiting for process failed: %s, PID(%ld): %d (%s)",
str_, (long)pid_, err_, strerror (err_));
}
else
{ // command completed, check exit status
if (WIFEXITED (status)) {
- err_ = WEXITSTATUS (status);
+ err_= WEXITSTATUS (status);
}
else { // command didn't complete with exit()
WSREP_ERROR("Process was aborted.");
- err_ = errno ? errno : ECHILD;
+ err_= errno ? errno : ECHILD;
}
if (err_) {
switch (err_) /* Translate error codes to more meaningful */
{
- case 126: err_ = EACCES; break; /* Permission denied */
- case 127: err_ = ENOENT; break; /* No such file or directory */
- case 143: err_ = EINTR; break; /* Subprocess killed */
+ case 126: err_= EACCES; break; /* Permission denied */
+ case 127: err_= ENOENT; break; /* No such file or directory */
+ case 143: err_= EINTR; break; /* Subprocess killed */
}
WSREP_ERROR("Process completed with error: %s: %d (%s)",
str_, err_, strerror(err_));
}
- pid_ = 0;
+ pid_= 0;
if (io_) fclose (io_);
- io_ = NULL;
+ io_= NULL;
}
}
else {
@@ -419,9 +422,10 @@ thd::thd (my_bool won, bool system_thread) : init(), ptr(new THD(0))
if (ptr)
{
ptr->thread_stack= (char*) &ptr;
- ptr->store_globals();
+ wsrep_assign_from_threadvars(ptr);
+ wsrep_store_threadvars(ptr);
ptr->variables.option_bits&= ~OPTION_BIN_LOG; // disable binlog
- ptr->variables.wsrep_on = won;
+ ptr->variables.wsrep_on= won;
if (system_thread)
ptr->system_thread= SYSTEM_THREAD_GENERIC;
ptr->security_ctx->master_access= ~(ulong)0;
@@ -443,7 +447,7 @@ thd::~thd ()
/* Returns INADDR_NONE, INADDR_ANY, INADDR_LOOPBACK or something else */
unsigned int wsrep_check_ip (const char* const addr, bool *is_ipv6)
{
- unsigned int ret = INADDR_NONE;
+ unsigned int ret= INADDR_NONE;
struct addrinfo *res, hints;
memset (&hints, 0, sizeof(hints));
@@ -453,7 +457,7 @@ unsigned int wsrep_check_ip (const char* const addr, bool *is_ipv6)
*is_ipv6= false;
- int gai_ret = getaddrinfo(addr, NULL, &hints, &res);
+ int gai_ret= getaddrinfo(addr, NULL, &hints, &res);
if (0 == gai_ret)
{
if (AF_INET == res->ai_family) /* IPv4 */
@@ -490,7 +494,9 @@ size_t wsrep_guess_ip (char* buf, size_t buf_len)
size_t ret= 0;
// Attempt 1: Try to get the IP from bind-address.
- if (my_bind_addr_str && my_bind_addr_str[0] != '\0')
+ // Skip if empty or bind-address=*
+ if (my_bind_addr_str && my_bind_addr_str[0] != '\0' &&
+ strcmp(my_bind_addr_str, "*") != 0)
{
bool unused;
unsigned int const ip_type= wsrep_check_ip(my_bind_addr_str, &unused);
@@ -541,7 +547,7 @@ size_t wsrep_guess_ip (char* buf, size_t buf_len)
if (getifaddrs(&ifaddr) == 0)
{
- for (ifa= ifaddr; ifa != NULL; ifa = ifa->ifa_next)
+ for (ifa= ifaddr; ifa != NULL; ifa= ifa->ifa_next)
{
if (!ifa->ifa_addr)
continue;
diff --git a/sql/wsrep_utils.h b/sql/wsrep_utils.h
index d4d9e62a620..974c623521e 100644
--- a/sql/wsrep_utils.h
+++ b/sql/wsrep_utils.h
@@ -21,6 +21,27 @@
unsigned int wsrep_check_ip (const char* const addr, bool *is_ipv6);
size_t wsrep_guess_ip (char* buf, size_t buf_len);
+namespace wsp {
+class node_status
+{
+public:
+ node_status() : status(wsrep::server_state::s_disconnected) {}
+ void set(enum wsrep::server_state::state new_status,
+ const wsrep::view* view= 0)
+ {
+ if (status != new_status || 0 != view)
+ {
+ wsrep_notify_status(new_status, view);
+ status= new_status;
+ }
+ }
+ enum wsrep::server_state::state get() const { return status; }
+private:
+ enum wsrep::server_state::state status;
+};
+} /* namespace wsp */
+
+extern wsp::node_status local_status;
/* returns the length of the host part of the address string */
size_t wsrep_host_len(const char* addr, size_t addr_len);
@@ -174,52 +195,37 @@ private:
class Config_state
{
public:
- Config_state() : view_(), status_(WSREP_MEMBER_UNDEFINED)
+ Config_state() : view_(), status_(wsrep::server_state::s_disconnected)
{}
- void set(wsrep_member_status_t status, const wsrep_view_info_t* view)
+ void set(const wsrep::view& view)
{
- wsrep_notify_status(status, view);
+ wsrep_notify_status(status_, &view);
lock();
-
- status_= status;
- view_= *view;
- member_info_.clear();
-
- wsrep_member_info_t memb;
- for(int i= 0; i < view->memb_num; i ++)
- {
- memb= view->members[i];
- member_info_.append_val(memb);
- }
-
+ view_= view;
unlock();
}
- void set(wsrep_member_status_t status)
+ void set(enum wsrep::server_state::state status)
{
- wsrep_notify_status(status, 0);
+ wsrep_notify_status(status);
+
lock();
status_= status;
unlock();
}
- wsrep_view_info_t get_view_info() const
+ const wsrep::view& get_view_info() const
{
return view_;
}
- wsrep_member_status_t get_status() const
+ enum wsrep::server_state::state get_status() const
{
return status_;
}
- Dynamic_array<wsrep_member_info_t> * get_member_info()
- {
- return &member_info_;
- }
-
int lock()
{
return mysql_mutex_lock(&LOCK_wsrep_config_state);
@@ -231,9 +237,8 @@ public:
}
private:
- wsrep_view_info_t view_;
- wsrep_member_status_t status_;
- Dynamic_array<wsrep_member_info_t> member_info_;
+ wsrep::view view_;
+ enum wsrep::server_state::state status_;
};
} /* namespace wsp */
@@ -309,12 +314,23 @@ public:
string() : string_(0) {}
explicit string(size_t s) : string_(static_cast<char*>(malloc(s))) {}
char* operator()() { return string_; }
- void set(char* str) { if (string_) free (string_); string_ = str; }
+ void set(char* str) { if (string_) free (string_); string_= str; }
~string() { set (0); }
private:
char* string_;
};
+/* scope level lock */
+class auto_lock
+{
+public:
+ auto_lock(mysql_mutex_t* m) : m_(m) { mysql_mutex_lock(m_); }
+ ~auto_lock() { mysql_mutex_unlock(m_); }
+private:
+ mysql_mutex_t& operator =(mysql_mutex_t&);
+ mysql_mutex_t* const m_;
+};
+
#ifdef REMOVED
class lock
{
@@ -324,7 +340,7 @@ public:
lock (pthread_mutex_t* mtx) : mtx_(mtx)
{
- int err = pthread_mutex_lock (mtx_);
+ int err= pthread_mutex_lock (mtx_);
if (err)
{
@@ -335,7 +351,7 @@ public:
virtual ~lock ()
{
- int err = pthread_mutex_unlock (mtx_);
+ int err= pthread_mutex_unlock (mtx_);
if (err)
{
diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc
index 413550935cb..e4cfd0d89c9 100644
--- a/sql/wsrep_var.cc
+++ b/sql/wsrep_var.cc
@@ -25,6 +25,7 @@
#include <my_dir.h>
#include <cstdio>
#include <cstdlib>
+#include "wsrep_trans_observer.h"
ulong wsrep_reject_queries;
@@ -41,6 +42,60 @@ int wsrep_init_vars()
return 0;
}
+static int get_provider_option_value(const char* opts,
+ const char* opt_name,
+ ulong* opt_value)
+{
+ int ret= 1;
+ ulong opt_value_tmp;
+ char *opt_value_str, *s, *opts_copy= my_strdup(opts, MYF(MY_WME));
+
+ if ((opt_value_str= strstr(opts_copy, opt_name)) == NULL)
+ goto end;
+ opt_value_str= strtok_r(opt_value_str, "=", &s);
+ if (opt_value_str == NULL) goto end;
+ opt_value_str= strtok_r(NULL, ";", &s);
+ if (opt_value_str == NULL) goto end;
+
+ opt_value_tmp= strtoul(opt_value_str, NULL, 10);
+ if (errno == ERANGE) goto end;
+
+ *opt_value= opt_value_tmp;
+ ret= 0;
+
+end:
+ my_free(opts_copy);
+ return ret;
+}
+
+static bool refresh_provider_options()
+{
+ WSREP_DEBUG("refresh_provider_options: %s",
+ (wsrep_provider_options) ? wsrep_provider_options : "null");
+
+ try
+ {
+ std::string opts= Wsrep_server_state::instance().provider().options();
+ wsrep_provider_options_init(opts.c_str());
+ get_provider_option_value(wsrep_provider_options,
+ (char*)"repl.max_ws_size",
+ &wsrep_max_ws_size);
+ return false;
+ }
+ catch (...)
+ {
+ WSREP_ERROR("Failed to get provider options");
+ return true;
+ }
+}
+
+void wsrep_set_wsrep_on()
+{
+ WSREP_PROVIDER_EXISTS_= wsrep_provider &&
+ strncasecmp(wsrep_provider, WSREP_NONE, FN_REFLEN);
+ WSREP_ON_= global_system_variables.wsrep_on && WSREP_PROVIDER_EXISTS_;
+}
+
/* This is intentionally declared as a weak global symbol, so that
linking will succeed even if the server is built with a dynamically
linked InnoDB. */
@@ -49,9 +104,43 @@ struct handlerton* innodb_hton_ptr __attribute__((weak));
bool wsrep_on_update (sys_var *self, THD* thd, enum_var_type var_type)
{
- if (var_type == OPT_GLOBAL) {
- // FIXME: this variable probably should be changed only per session
- thd->variables.wsrep_on = global_system_variables.wsrep_on;
+ if (var_type == OPT_GLOBAL)
+ {
+ my_bool saved_wsrep_on= global_system_variables.wsrep_on;
+
+ thd->variables.wsrep_on= global_system_variables.wsrep_on;
+
+ // If wsrep has not been inited we need to do it now
+ if (global_system_variables.wsrep_on && wsrep_provider && !wsrep_inited)
+ {
+ // wsrep_init() rewrites provide if it fails
+ char* tmp= strdup(wsrep_provider);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+
+ if (wsrep_init())
+ {
+ my_error(ER_CANT_OPEN_LIBRARY, MYF(0), tmp, my_error, "wsrep_init failed");
+ //rcode= true;
+ saved_wsrep_on= false;
+ }
+
+ free(tmp);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ }
+
+ thd->variables.wsrep_on= global_system_variables.wsrep_on= saved_wsrep_on;
+ }
+
+ wsrep_set_wsrep_on();
+
+ if (var_type == OPT_GLOBAL)
+ {
+ if (thd->variables.wsrep_on &&
+ thd->wsrep_cs().state() == wsrep::client_state::s_none)
+ {
+ wsrep_open(thd);
+ wsrep_before_command(thd);
+ }
}
return false;
@@ -64,21 +153,62 @@ bool wsrep_on_check(sys_var *self, THD* thd, set_var* var)
if (check_has_super(self, thd, var))
return true;
- if (new_wsrep_on && innodb_hton_ptr && innodb_lock_schedule_algorithm != 0) {
- my_message(ER_WRONG_ARGUMENTS, " WSREP (galera) can't be enabled "
- "if innodb_lock_schedule_algorithm=VATS. Please configure"
- " innodb_lock_schedule_algorithm=FCFS and restart.", MYF(0));
+ if (new_wsrep_on)
+ {
+ if (innodb_hton_ptr && innodb_lock_schedule_algorithm != 0)
+ {
+ my_message(ER_WRONG_ARGUMENTS, " WSREP (galera) can't be enabled "
+ "if innodb_lock_schedule_algorithm=VATS. Please configure"
+ " innodb_lock_schedule_algorithm=FCFS and restart.", MYF(0));
+ return true;
+ }
+
+ if (!WSREP_PROVIDER_EXISTS)
+ {
+ my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) can't be enabled "
+ "if the wsrep_provider is unset or set to 'none'", MYF(0));
+ return true;
+ }
+
+ if (var->type == OPT_SESSION &&
+ !global_system_variables.wsrep_on)
+ {
+ my_message(ER_WRONG_ARGUMENTS,
+ "Can't enable @@session.wsrep_on, "
+ "while @@global.wsrep_on is disabled", MYF(0));
+ return true;
+ }
+ }
+
+ if (thd->in_active_multi_stmt_transaction())
+ {
+ my_error(ER_CANT_DO_THIS_DURING_AN_TRANSACTION, MYF(0));
return true;
}
+
+ if (var->type == OPT_GLOBAL)
+ {
+ /*
+ The global value is about to change. Cleanup
+ the transaction state and close the client
+ state. wsrep_on_update() will take care of
+ reopening it should wsrep_on be re-enabled.
+ */
+ if (global_system_variables.wsrep_on && !new_wsrep_on)
+ {
+ wsrep_commit_empty(thd, true);
+ wsrep_after_statement(thd);
+ wsrep_after_command_ignore_result(thd);
+ wsrep_close(thd);
+ wsrep_cleanup(thd);
+ }
+ }
+
return false;
}
bool wsrep_causal_reads_update (sys_var *self, THD* thd, enum_var_type var_type)
{
- // global setting should not affect session setting.
- // if (var_type == OPT_GLOBAL) {
- // thd->variables.wsrep_causal_reads = global_system_variables.wsrep_causal_reads;
- // }
if (thd->variables.wsrep_causal_reads) {
thd->variables.wsrep_sync_wait |= WSREP_SYNC_WAIT_BEFORE_READ;
} else {
@@ -97,15 +227,11 @@ bool wsrep_causal_reads_update (sys_var *self, THD* thd, enum_var_type var_type)
bool wsrep_sync_wait_update (sys_var* self, THD* thd, enum_var_type var_type)
{
- // global setting should not affect session setting.
- // if (var_type == OPT_GLOBAL) {
- // thd->variables.wsrep_sync_wait = global_system_variables.wsrep_sync_wait;
- // }
- thd->variables.wsrep_causal_reads = thd->variables.wsrep_sync_wait &
+ thd->variables.wsrep_causal_reads= thd->variables.wsrep_sync_wait &
WSREP_SYNC_WAIT_BEFORE_READ;
// update global settings too
- global_system_variables.wsrep_causal_reads = global_system_variables.wsrep_sync_wait &
+ global_system_variables.wsrep_causal_reads= global_system_variables.wsrep_sync_wait &
WSREP_SYNC_WAIT_BEFORE_READ;
return false;
@@ -127,7 +253,7 @@ bool wsrep_start_position_verify (const char* start_str)
ssize_t uuid_len;
// Check whether it has minimum acceptable length.
- start_len = strlen (start_str);
+ start_len= strlen (start_str);
if (start_len < 34)
return true;
@@ -135,7 +261,7 @@ bool wsrep_start_position_verify (const char* start_str)
Parse the input to check whether UUID length is acceptable
and seqno has been provided.
*/
- uuid_len = wsrep_uuid_scan (start_str, start_len, &uuid);
+ uuid_len= wsrep_uuid_scan (start_str, start_len, &uuid);
if (uuid_len < 0 || (start_len - uuid_len) < 2)
return true;
@@ -144,8 +270,11 @@ bool wsrep_start_position_verify (const char* start_str)
return true;
char* endptr;
- wsrep_seqno_t const seqno __attribute__((unused)) // to avoid GCC warnings
- (strtoll(&start_str[uuid_len + 1], &endptr, 10));
+ wsrep_seqno_t const seqno(strtoll(&start_str[uuid_len + 1], &endptr, 10));
+
+ // Do not allow seqno < -1
+ if (*endptr == '\0' && seqno < -1)
+ return true;
// Remaining string was seqno.
if (*endptr == '\0') return false;
@@ -155,20 +284,31 @@ bool wsrep_start_position_verify (const char* start_str)
static
-bool wsrep_set_local_position(const char* const value, size_t length,
- bool const sst)
+bool wsrep_set_local_position(THD* thd, const char* const value,
+ size_t length, bool const sst)
{
wsrep_uuid_t uuid;
- size_t const uuid_len = wsrep_uuid_scan(value, length, &uuid);
- wsrep_seqno_t const seqno = strtoll(value + uuid_len + 1, NULL, 10);
+ size_t const uuid_len= wsrep_uuid_scan(value, length, &uuid);
+ wsrep_seqno_t const seqno= strtoll(value + uuid_len + 1, NULL, 10);
+
+ char start_pos_buf[FN_REFLEN];
+ memcpy(start_pos_buf, value, length);
+ start_pos_buf[length]='\0';
+
+ // If both are same as WSREP_START_POSITION_ZERO just set local
+ if (!strcmp(start_pos_buf, WSREP_START_POSITION_ZERO) &&
+ !strcmp(wsrep_start_position, WSREP_START_POSITION_ZERO))
+ goto set;
+ else
+ WSREP_INFO("SST setting local position to %s current %s", start_pos_buf, wsrep_start_position);
+
+ if (sst)
+ return (wsrep_sst_received (thd, uuid, seqno, NULL, 0));
+
+set:
+ local_uuid= uuid;
+ local_seqno= seqno;
- if (sst) {
- return wsrep_sst_received (wsrep, uuid, seqno, NULL, 0, false);
- } else {
- // initialization
- local_uuid = uuid;
- local_seqno = seqno;
- }
return false;
}
@@ -185,19 +325,34 @@ bool wsrep_start_position_check (sys_var *self, THD* thd, set_var* var)
var->save_result.string_value.length);
start_pos_buf[var->save_result.string_value.length]= 0;
+
+ WSREP_DEBUG("SST wsrep_start_position check for new position %s old %s",
+ start_pos_buf, wsrep_start_position);
+
// Verify the format.
if (wsrep_start_position_verify(start_pos_buf)) return true;
+
+ // Give error if position is updated when wsrep is not enabled or
+ // provider is not loaded.
+ if ((!WSREP_ON || !Wsrep_server_state::instance().is_provider_loaded())
+ && strcmp(start_pos_buf, WSREP_START_POSITION_ZERO))
+ {
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "Cannot set 'wsrep_start_position' because "
+ "wsrep is switched off or provider is not loaded");
+ goto err;
+ }
+
/*
As part of further verification, we try to update the value and catch
- errors (if any).
+ errors (if any) only when value actually has been changed.
*/
- if (wsrep_set_local_position(var->save_result.string_value.str,
+ if (wsrep_set_local_position(thd, var->save_result.string_value.str,
var->save_result.string_value.length,
true))
- {
goto err;
- }
return false;
@@ -219,12 +374,12 @@ bool wsrep_start_position_init (const char* val)
{
if (NULL == val || wsrep_start_position_verify (val))
{
- WSREP_ERROR("Bad initial value for wsrep_start_position: %s",
+ WSREP_ERROR("Bad initial value for wsrep_start_position: %s",
(val ? val : ""));
return true;
}
- if (wsrep_set_local_position (val, strlen(val), false))
+ if (wsrep_set_local_position (NULL, val, strlen(val), false))
{
WSREP_ERROR("Failed to set initial wsep_start_position: %s", val);
return true;
@@ -233,55 +388,6 @@ bool wsrep_start_position_init (const char* val)
return false;
}
-static int get_provider_option_value(const char* opts,
- const char* opt_name,
- ulong* opt_value)
-{
- int ret= 1;
- ulong opt_value_tmp;
- char *opt_value_str, *s, *opts_copy= my_strdup(opts, MYF(MY_WME));
-
- if ((opt_value_str= strstr(opts_copy, opt_name)) == NULL)
- goto end;
- opt_value_str= strtok_r(opt_value_str, "=", &s);
- if (opt_value_str == NULL) goto end;
- opt_value_str= strtok_r(NULL, ";", &s);
- if (opt_value_str == NULL) goto end;
-
- opt_value_tmp= strtoul(opt_value_str, NULL, 10);
- if (errno == ERANGE) goto end;
-
- *opt_value= opt_value_tmp;
- ret= 0;
-
-end:
- my_free(opts_copy);
- return ret;
-}
-
-static bool refresh_provider_options()
-{
- DBUG_ASSERT(wsrep);
-
- WSREP_DEBUG("refresh_provider_options: %s",
- (wsrep_provider_options) ? wsrep_provider_options : "null");
- char* opts= wsrep->options_get(wsrep);
- if (opts)
- {
- wsrep_provider_options_init(opts);
- get_provider_option_value(wsrep_provider_options,
- (char*)"repl.max_ws_size",
- &wsrep_max_ws_size);
- free(opts);
- }
- else
- {
- WSREP_ERROR("Failed to get provider options");
- return true;
- }
- return false;
-}
-
static int wsrep_provider_verify (const char* provider_str)
{
MY_STAT f_stat;
@@ -302,6 +408,12 @@ static int wsrep_provider_verify (const char* provider_str)
{
return 1;
}
+
+ if (MY_S_ISDIR(f_stat.st_mode))
+ {
+ return 1;
+ }
+
return 0;
}
@@ -330,21 +442,24 @@ bool wsrep_provider_update (sys_var *self, THD* thd, enum_var_type type)
{
bool rcode= false;
- bool wsrep_on_saved= thd->variables.wsrep_on;
- thd->variables.wsrep_on= false;
WSREP_DEBUG("wsrep_provider_update: %s", wsrep_provider);
- /* stop replication is heavy operation, and includes closing all client
+ /* stop replication is heavy operation, and includes closing all client
connections. Closing clients may need to get LOCK_global_system_variables
at least in MariaDB.
- Note: releasing LOCK_global_system_variables may cause race condition, if
+ Note: releasing LOCK_global_system_variables may cause race condition, if
there can be several concurrent clients changing wsrep_provider
*/
mysql_mutex_unlock(&LOCK_global_system_variables);
wsrep_stop_replication(thd);
- mysql_mutex_lock(&LOCK_global_system_variables);
+
+ /* provider status variables are allocated in provider library
+ and need to freed here, otherwise a dangling reference to
+ wsrep_status_vars would remain in THD
+ */
+ wsrep_free_status(thd);
if (wsrep_inited == 1)
wsrep_deinit(false);
@@ -355,25 +470,26 @@ bool wsrep_provider_update (sys_var *self, THD* thd, enum_var_type type)
if (wsrep_init())
{
my_error(ER_CANT_OPEN_LIBRARY, MYF(0), tmp, my_error, "wsrep_init failed");
- rcode = true;
+ rcode= true;
}
free(tmp);
// we sure don't want to use old address with new provider
wsrep_cluster_address_init(NULL);
wsrep_provider_options_init(NULL);
+ if (!rcode)
+ refresh_provider_options();
- thd->variables.wsrep_on= wsrep_on_saved;
-
- refresh_provider_options();
+ wsrep_set_wsrep_on();
+ mysql_mutex_lock(&LOCK_global_system_variables);
return rcode;
}
void wsrep_provider_init (const char* value)
{
- WSREP_DEBUG("wsrep_provider_init: %s -> %s",
- (wsrep_provider) ? wsrep_provider : "null",
+ WSREP_DEBUG("wsrep_provider_init: %s -> %s",
+ (wsrep_provider) ? wsrep_provider : "null",
(value) ? value : "null");
if (NULL == value || wsrep_provider_verify (value))
{
@@ -383,12 +499,13 @@ void wsrep_provider_init (const char* value)
}
if (wsrep_provider) my_free((void *)wsrep_provider);
- wsrep_provider = my_strdup(value, MYF(0));
+ wsrep_provider= my_strdup(value, MYF(0));
+ wsrep_set_wsrep_on();
}
bool wsrep_provider_options_check(sys_var *self, THD* thd, set_var* var)
{
- if (wsrep == NULL)
+ if (!WSREP_ON)
{
my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) not started", MYF(0));
return true;
@@ -398,22 +515,28 @@ bool wsrep_provider_options_check(sys_var *self, THD* thd, set_var* var)
bool wsrep_provider_options_update(sys_var *self, THD* thd, enum_var_type type)
{
- DBUG_ASSERT(wsrep);
- wsrep_status_t ret= wsrep->options_set(wsrep, wsrep_provider_options);
- if (ret != WSREP_OK)
+ if (wsrep_provider_options)
{
- WSREP_ERROR("Set options returned %d", ret);
- refresh_provider_options();
- return true;
+ enum wsrep::provider::status ret=
+ Wsrep_server_state::instance().provider().options(wsrep_provider_options);
+ if (ret)
+ {
+ WSREP_ERROR("Set options returned %d", ret);
+ goto err;
+ }
+
+ return refresh_provider_options();
}
- return refresh_provider_options();
+err:
+ refresh_provider_options();
+ return true;
}
void wsrep_provider_options_init(const char* value)
{
- if (wsrep_provider_options && wsrep_provider_options != value)
+ if (wsrep_provider_options && wsrep_provider_options != value)
my_free((void *)wsrep_provider_options);
- wsrep_provider_options = (value) ? my_strdup(value, MYF(0)) : NULL;
+ wsrep_provider_options= (value) ? my_strdup(value, MYF(0)) : NULL;
}
bool wsrep_reject_queries_update(sys_var *self, THD* thd, enum_var_type type)
@@ -438,6 +561,25 @@ bool wsrep_reject_queries_update(sys_var *self, THD* thd, enum_var_type type)
return false;
}
+bool wsrep_debug_update(sys_var *self, THD* thd, enum_var_type type)
+{
+ // Give warnings if wsrep_debug is set and wsrep is disabled or
+ // provider is not loaded, it will not have any effect
+ if ((!WSREP_ON || !Wsrep_server_state::instance().is_provider_loaded())
+ && wsrep_debug)
+ {
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "Setting 'wsrep_debug' has no effect because "
+ "wsrep is switched off");
+ wsrep_debug= 0;
+ }
+ else
+ Wsrep_server_state::instance().debug_log_level(wsrep_debug);
+
+ return false;
+}
+
static int wsrep_cluster_address_verify (const char* cluster_address_str)
{
/* There is no predefined address format, it depends on provider. */
@@ -467,53 +609,48 @@ bool wsrep_cluster_address_check (sys_var *self, THD* thd, set_var* var)
bool wsrep_cluster_address_update (sys_var *self, THD* thd, enum_var_type type)
{
- bool wsrep_on_saved;
-
- /* Do not proceed if wsrep provider is not loaded. */
- if (!wsrep)
+ if (!Wsrep_server_state::instance().is_provider_loaded())
{
- WSREP_INFO("wsrep provider is not loaded, can't re(start) replication.");
+ WSREP_INFO("WSREP (galera) provider is not loaded, can't re(start) replication.");
return false;
}
- wsrep_on_saved= thd->variables.wsrep_on;
- thd->variables.wsrep_on= false;
-
- /* stop replication is heavy operation, and includes closing all client
+ /* stop replication is heavy operation, and includes closing all client
connections. Closing clients may need to get LOCK_global_system_variables
at least in MariaDB.
-
- Note: releasing LOCK_global_system_variables may cause race condition, if
- there can be several concurrent clients changing wsrep_provider
*/
+ char *tmp= my_strdup(wsrep_cluster_address, MYF(MY_WME));
+ WSREP_DEBUG("wsrep_cluster_address_update: %s", wsrep_cluster_address);
mysql_mutex_unlock(&LOCK_global_system_variables);
- wsrep_stop_replication(thd);
- /*
- Unlock and lock LOCK_wsrep_slave_threads to maintain lock order & avoid
- any potential deadlock.
- */
- mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
- mysql_mutex_lock(&LOCK_global_system_variables);
- mysql_mutex_lock(&LOCK_wsrep_slave_threads);
+ mysql_mutex_lock(&LOCK_wsrep_cluster_config);
+ wsrep_stop_replication(thd);
- if (wsrep_start_replication())
+ if (*tmp && wsrep_start_replication(tmp))
{
wsrep_create_rollbacker();
WSREP_DEBUG("Cluster address update creating %ld applier threads running %lu",
wsrep_slave_threads, wsrep_running_applier_threads);
wsrep_create_appliers(wsrep_slave_threads);
}
+ mysql_mutex_unlock(&LOCK_wsrep_cluster_config);
- thd->variables.wsrep_on= wsrep_on_saved;
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ if (strcmp(tmp, wsrep_cluster_address))
+ {
+ my_free((void*)wsrep_cluster_address);
+ wsrep_cluster_address= tmp;
+ }
+ else
+ my_free(tmp);
return false;
}
void wsrep_cluster_address_init (const char* value)
{
- WSREP_DEBUG("wsrep_cluster_address_init: %s -> %s",
- (wsrep_cluster_address) ? wsrep_cluster_address : "null",
+ WSREP_DEBUG("wsrep_cluster_address_init: %s -> %s",
+ (wsrep_cluster_address) ? wsrep_cluster_address : "null",
(value) ? value : "null");
my_free((void*) wsrep_cluster_address);
@@ -590,7 +727,7 @@ void wsrep_node_address_init (const char* value)
if (wsrep_node_address && strcmp(wsrep_node_address, value))
my_free ((void*)wsrep_node_address);
- wsrep_node_address = (value) ? my_strdup(value, MYF(0)) : NULL;
+ wsrep_node_address= (value) ? my_strdup(value, MYF(0)) : NULL;
}
static void wsrep_slave_count_change_update ()
@@ -602,7 +739,12 @@ static void wsrep_slave_count_change_update ()
bool wsrep_slave_threads_update (sys_var *self, THD* thd, enum_var_type type)
{
- mysql_mutex_lock(&LOCK_thread_count);
+ if (!wsrep_cluster_address_exists())
+ return false;
+
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_wsrep_slave_threads);
+ mysql_mutex_lock(&LOCK_global_system_variables);
bool res= false;
wsrep_slave_count_change_update();
@@ -615,14 +757,14 @@ bool wsrep_slave_threads_update (sys_var *self, THD* thd, enum_var_type type)
wsrep_slave_count_change = 0;
}
- mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_wsrep_slave_threads);
return res;
}
bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var)
{
- if (wsrep == NULL)
+ if (!WSREP_ON)
{
my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) not started", MYF(0));
return true;
@@ -647,17 +789,17 @@ bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var)
}
return false;
}
- wsrep_status_t ret(WSREP_WARNING);
+ int ret= 1;
if (new_wsrep_desync) {
- ret = wsrep->desync (wsrep);
- if (ret != WSREP_OK) {
- WSREP_WARN ("SET desync failed %d for schema: %s, query: %s",
- ret, thd->get_db(), thd->query());
+ ret= Wsrep_server_state::instance().provider().desync();
+ if (ret) {
+ WSREP_WARN ("SET desync failed %d for schema: %s, query: %s", ret,
+ thd->db.str, WSREP_QUERY(thd));
my_error (ER_CANNOT_USER, MYF(0), "'desync'", thd->query());
return true;
}
} else {
- ret = wsrep->resync (wsrep);
+ ret= Wsrep_server_state::instance().provider().resync();
if (ret != WSREP_OK) {
WSREP_WARN ("SET resync failed %d for schema: %s, query: %s", ret,
thd->get_db(), thd->query());
@@ -670,13 +812,102 @@ bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var)
bool wsrep_desync_update (sys_var *self, THD* thd, enum_var_type type)
{
- DBUG_ASSERT(wsrep);
+ return false;
+}
+
+bool wsrep_trx_fragment_size_check (sys_var *self, THD* thd, set_var* var)
+{
+ if (var->value == NULL) {
+ return false;
+ }
+
+ const ulong new_trx_fragment_size= var->value->val_uint();
+
+ if (!WSREP(thd) && new_trx_fragment_size > 0) {
+ push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "Cannot set 'wsrep_trx_fragment_size' to a value other than "
+ "0 because wsrep is switched off.");
+ return true;
+ }
+
+ if (new_trx_fragment_size > 0 && !wsrep_provider_is_SR_capable()) {
+ push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "Cannot set 'wsrep_trx_fragment_size' to a value other than "
+ "0 because the wsrep_provider does not support streaming "
+ "replication.");
+ return true;
+ }
+
+ if (wsrep_protocol_version < 4 && new_trx_fragment_size > 0) {
+ push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "Cannot set 'wsrep_trx_fragment_size' to a value other than "
+ "0 because cluster is not yet operating in Galera 4 mode.");
+ return true;
+ }
+
+ return false;
+}
+
+bool wsrep_trx_fragment_size_update(sys_var* self, THD *thd, enum_var_type)
+{
+ WSREP_DEBUG("wsrep_trx_fragment_size_update: %llu",
+ thd->variables.wsrep_trx_fragment_size);
+
+ // Give error if wsrep_trx_fragment_size is set and wsrep is disabled or
+ // provider is not loaded
+ if (!WSREP_ON || !Wsrep_server_state::instance().is_provider_loaded())
+ {
+ push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "Cannot set 'wsrep_trx_fragment_size' because "
+ "wsrep is switched off");
+ return true;
+ }
+
+ if (thd->variables.wsrep_trx_fragment_size)
+ {
+ return thd->wsrep_cs().enable_streaming(
+ wsrep_fragment_unit(thd->variables.wsrep_trx_fragment_unit),
+ size_t(thd->variables.wsrep_trx_fragment_size));
+ }
+ else
+ {
+ thd->wsrep_cs().disable_streaming();
+ return false;
+ }
+}
+
+bool wsrep_trx_fragment_unit_update(sys_var* self, THD *thd, enum_var_type)
+{
+ WSREP_DEBUG("wsrep_trx_fragment_unit_update: %lu",
+ thd->variables.wsrep_trx_fragment_unit);
+
+ // Give error if wsrep_trx_fragment_unit is set and wsrep is disabled or
+ // provider is not loaded
+ if (!WSREP_ON || !Wsrep_server_state::instance().is_provider_loaded())
+ {
+ push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "Cannot set 'wsrep_trx_fragment_unit' because "
+ "wsrep is switched off");
+ return true;
+ }
+
+ if (thd->variables.wsrep_trx_fragment_size)
+ {
+ return thd->wsrep_cs().enable_streaming(
+ wsrep_fragment_unit(thd->variables.wsrep_trx_fragment_unit),
+ size_t(thd->variables.wsrep_trx_fragment_size));
+ }
return false;
}
bool wsrep_max_ws_size_check(sys_var *self, THD* thd, set_var* var)
{
- if (wsrep == NULL)
+ if (!WSREP_ON)
{
my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) not started", MYF(0));
return true;
@@ -684,36 +915,35 @@ bool wsrep_max_ws_size_check(sys_var *self, THD* thd, set_var* var)
return false;
}
-bool wsrep_max_ws_size_update (sys_var *self, THD *thd, enum_var_type)
+bool wsrep_max_ws_size_update(sys_var *self, THD *thd, enum_var_type)
{
- DBUG_ASSERT(wsrep);
-
char max_ws_size_opt[128];
my_snprintf(max_ws_size_opt, sizeof(max_ws_size_opt),
- "repl.max_ws_size=%lu", wsrep_max_ws_size);
- wsrep_status_t ret= wsrep->options_set(wsrep, max_ws_size_opt);
- if (ret != WSREP_OK)
+ "repl.max_ws_size=%d", wsrep_max_ws_size);
+ enum wsrep::provider::status ret= Wsrep_server_state::instance().provider().options(max_ws_size_opt);
+ if (ret)
{
WSREP_ERROR("Set options returned %d", ret);
- refresh_provider_options();
return true;
}
return refresh_provider_options();
}
+#if UNUSED /* eaec266eb16c (Sergei Golubchik 2014-09-28) */
static SHOW_VAR wsrep_status_vars[]=
{
{"connected", (char*) &wsrep_connected, SHOW_BOOL},
- {"ready", (char*) &wsrep_ready, SHOW_BOOL},
+ {"ready", (char*) &wsrep_show_ready, SHOW_FUNC},
{"cluster_state_uuid",(char*) &wsrep_cluster_state_uuid,SHOW_CHAR_PTR},
{"cluster_conf_id", (char*) &wsrep_cluster_conf_id, SHOW_LONGLONG},
{"cluster_status", (char*) &wsrep_cluster_status, SHOW_CHAR_PTR},
{"cluster_size", (char*) &wsrep_cluster_size, SHOW_LONG_NOFLUSH},
{"local_index", (char*) &wsrep_local_index, SHOW_LONG_NOFLUSH},
- {"local_bf_aborts", (char*) &wsrep_show_bf_aborts, SHOW_SIMPLE_FUNC},
+ {"local_bf_aborts", (char*) &wsrep_show_bf_aborts, SHOW_FUNC},
{"provider_name", (char*) &wsrep_provider_name, SHOW_CHAR_PTR},
{"provider_version", (char*) &wsrep_provider_version, SHOW_CHAR_PTR},
{"provider_vendor", (char*) &wsrep_provider_vendor, SHOW_CHAR_PTR},
+ {"provider_capabilities", (char*) &wsrep_provider_capabilities, SHOW_CHAR_PTR},
{"thread_count", (char*) &wsrep_running_threads, SHOW_LONG_NOFLUSH},
{"applier_thread_count", (char*)&wsrep_running_applier_threads, SHOW_LONG_NOFLUSH},
{"rollbacker_thread_count", (char *)&wsrep_running_rollbacker_threads, SHOW_LONG_NOFLUSH},
@@ -724,48 +954,93 @@ static int show_var_cmp(const void *var1, const void *var2)
return strcasecmp(((SHOW_VAR*)var1)->name, ((SHOW_VAR*)var2)->name);
}
-int wsrep_show_status (THD *thd, SHOW_VAR *var, void *buff,
- system_status_var *, enum_var_type scope)
+/*
+ * Status variables stuff below
+ */
+static inline void
+wsrep_assign_to_mysql (SHOW_VAR* mysql, wsrep_stats_var* wsrep_var)
+{
+ mysql->name= wsrep_var->name;
+ switch (wsrep_var->type) {
+ case WSREP_VAR_INT64:
+ mysql->value= (char*) &wsrep_var->value._int64;
+ mysql->type= SHOW_LONGLONG;
+ break;
+ case WSREP_VAR_STRING:
+ mysql->value= (char*) &wsrep_var->value._string;
+ mysql->type= SHOW_CHAR_PTR;
+ break;
+ case WSREP_VAR_DOUBLE:
+ mysql->value= (char*) &wsrep_var->value._double;
+ mysql->type= SHOW_DOUBLE;
+ break;
+ }
+}
+#endif /* UNUSED */
+
+#if DYNAMIC
+// somehow this mysql status thing works only with statically allocated arrays.
+static SHOW_VAR* mysql_status_vars= NULL;
+static int mysql_status_len= -1;
+#else
+static SHOW_VAR mysql_status_vars[512 + 1];
+static const int mysql_status_len= 512;
+#endif
+
+static void export_wsrep_status_to_mysql(THD* thd)
{
- uint i, maxi= SHOW_VAR_FUNC_BUFF_SIZE / sizeof(*var) - 1;
- SHOW_VAR *v= (SHOW_VAR *)buff;
+ int wsrep_status_len, i;
- var->type= SHOW_ARRAY;
- var->value= buff;
+ thd->wsrep_status_vars= Wsrep_server_state::instance().status();
- for (i=0; i < array_elements(wsrep_status_vars); i++)
- *v++= wsrep_status_vars[i];
+ wsrep_status_len= thd->wsrep_status_vars.size();
- DBUG_ASSERT(i < maxi);
+#if DYNAMIC
+ if (wsrep_status_len != mysql_status_len) {
+ void* tmp= realloc (mysql_status_vars,
+ (wsrep_status_len + 1) * sizeof(SHOW_VAR));
+ if (!tmp) {
- if (wsrep != NULL)
- {
- wsrep_stats_var* stats= wsrep->stats_get(wsrep);
- for (wsrep_stats_var *sv= stats;
- i < maxi && sv && sv->name; i++,
- sv++, v++)
- {
- v->name = thd->strdup(sv->name);
- switch (sv->type) {
- case WSREP_VAR_INT64:
- v->value = (char*)thd->memdup(&sv->value._integer64, sizeof(longlong));
- v->type = SHOW_LONGLONG;
- break;
- case WSREP_VAR_STRING:
- v->value = thd->strdup(sv->value._string);
- v->type = SHOW_CHAR;
- break;
- case WSREP_VAR_DOUBLE:
- v->value = (char*)thd->memdup(&sv->value._double, sizeof(double));
- v->type = SHOW_DOUBLE;
- break;
- }
+ sql_print_error ("Out of memory for wsrep status variables."
+ "Number of variables: %d", wsrep_status_len);
+ return;
}
- wsrep->stats_free(wsrep, stats);
+
+ mysql_status_len= wsrep_status_len;
+ mysql_status_vars= (SHOW_VAR*)tmp;
+ }
+ /* @TODO: fix this: */
+#else
+ if (mysql_status_len < wsrep_status_len) wsrep_status_len= mysql_status_len;
+#endif
+
+ for (i= 0; i < wsrep_status_len; i++)
+ {
+ mysql_status_vars[i].name= (char*)thd->wsrep_status_vars[i].name().c_str();
+ mysql_status_vars[i].value= (char*)thd->wsrep_status_vars[i].value().c_str();
+ mysql_status_vars[i].type= SHOW_CHAR;
}
- my_qsort(buff, i, sizeof(*v), show_var_cmp);
+ mysql_status_vars[wsrep_status_len].name = NullS;
+ mysql_status_vars[wsrep_status_len].value = NullS;
+ mysql_status_vars[wsrep_status_len].type = SHOW_LONG;
+}
- v->name= 0; // terminator
+int wsrep_show_status (THD *thd, SHOW_VAR *var, void *,
+ system_status_var *, enum_var_type)
+{
+ /* Note that we should allow show status like 'wsrep%' even
+ when WSREP(thd) is false. */
+ if (WSREP_ON)
+ {
+ export_wsrep_status_to_mysql(thd);
+ var->type= SHOW_ARRAY;
+ var->value= (char *) &mysql_status_vars;
+ }
return 0;
}
+
+void wsrep_free_status (THD* thd)
+{
+ thd->wsrep_status_vars.clear();
+}
diff --git a/sql/wsrep_var.h b/sql/wsrep_var.h
index 6258b5ab66f..b1b2932cdfe 100644
--- a/sql/wsrep_var.h
+++ b/sql/wsrep_var.h
@@ -13,11 +13,11 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. */
-#include <my_config.h>
-
#ifndef WSREP_VAR_H
#define WSREP_VAR_H
+#include <my_config.h>
+
#ifdef WITH_WSREP
#define WSREP_CLUSTER_NAME "my_wsrep_cluster"
@@ -35,6 +35,7 @@ class set_var;
class THD;
int wsrep_init_vars();
+void wsrep_set_wsrep_on();
#define CHECK_ARGS (sys_var *self, THD* thd, set_var *var)
#define UPDATE_ARGS (sys_var *self, THD* thd, enum_var_type type)
@@ -90,13 +91,20 @@ extern bool wsrep_slave_threads_update UPDATE_ARGS;
extern bool wsrep_desync_check CHECK_ARGS;
extern bool wsrep_desync_update UPDATE_ARGS;
+extern bool wsrep_trx_fragment_size_check CHECK_ARGS;
+extern bool wsrep_trx_fragment_size_update UPDATE_ARGS;
+
+extern bool wsrep_trx_fragment_unit_update UPDATE_ARGS;
+
extern bool wsrep_max_ws_size_check CHECK_ARGS;
extern bool wsrep_max_ws_size_update UPDATE_ARGS;
+
extern bool wsrep_reject_queries_update UPDATE_ARGS;
+extern bool wsrep_debug_update UPDATE_ARGS;
+
#else /* WITH_WSREP */
-#define WSREP_NONE
#define wsrep_provider_init(X)
#define wsrep_init_vars() (0)
#define wsrep_start_position_init(X)
diff --git a/sql/wsrep_xid.cc b/sql/wsrep_xid.cc
index 923abe53c6c..d8f6e013820 100644
--- a/sql/wsrep_xid.cc
+++ b/sql/wsrep_xid.cc
@@ -21,8 +21,9 @@
#include "sql_class.h"
#include "wsrep_mysqld.h" // for logging macros
-#include <algorithm> /* std::sort() */
+#include <mysql/service_wsrep.h>
+#include <algorithm> /* std::sort() */
/*
* WSREPXid
*/
@@ -36,20 +37,22 @@
#define WSREP_XID_SEQNO_OFFSET (WSREP_XID_UUID_OFFSET + sizeof(wsrep_uuid_t))
#define WSREP_XID_GTRID_LEN (WSREP_XID_SEQNO_OFFSET + sizeof(wsrep_seqno_t))
-void wsrep_xid_init(XID* xid, const wsrep_uuid_t& uuid, wsrep_seqno_t seqno)
+void wsrep_xid_init(XID* xid, const wsrep::gtid& wsgtid)
{
xid->formatID= 1;
xid->gtrid_length= WSREP_XID_GTRID_LEN;
xid->bqual_length= 0;
memset(xid->data, 0, sizeof(xid->data));
memcpy(xid->data, WSREP_XID_PREFIX, WSREP_XID_PREFIX_LEN);
- xid->data[WSREP_XID_VERSION_OFFSET] = WSREP_XID_VERSION_2;
- memcpy(xid->data + WSREP_XID_UUID_OFFSET, &uuid, sizeof(wsrep_uuid_t));
- int8store(xid->data + WSREP_XID_SEQNO_OFFSET,seqno);
+ xid->data[WSREP_XID_VERSION_OFFSET]= WSREP_XID_VERSION_2;
+ memcpy(xid->data + WSREP_XID_UUID_OFFSET, wsgtid.id().data(),sizeof(wsrep::id));
+ int8store(xid->data + WSREP_XID_SEQNO_OFFSET, wsgtid.seqno().get());
}
-int wsrep_is_wsrep_xid(const XID* xid)
+extern "C"
+int wsrep_is_wsrep_xid(const void* xid_ptr)
{
+ const XID* xid= static_cast<const XID*>(xid_ptr);
return (xid->formatID == 1 &&
xid->gtrid_length == WSREP_XID_GTRID_LEN &&
xid->bqual_length == 0 &&
@@ -58,33 +61,36 @@ int wsrep_is_wsrep_xid(const XID* xid)
xid->data[WSREP_XID_VERSION_OFFSET] == WSREP_XID_VERSION_2));
}
-const wsrep_uuid_t* wsrep_xid_uuid(const XID& xid)
+const unsigned char* wsrep_xid_uuid(const xid_t* xid)
{
- if (wsrep_is_wsrep_xid(&xid))
- return reinterpret_cast<const wsrep_uuid_t*>(xid.data
- + WSREP_XID_UUID_OFFSET);
+ DBUG_ASSERT(xid);
+ static wsrep::id const undefined;
+ if (wsrep_is_wsrep_xid(xid))
+ return reinterpret_cast<const unsigned char*>
+ (xid->data + WSREP_XID_UUID_OFFSET);
else
- return &WSREP_UUID_UNDEFINED;
+ return static_cast<const unsigned char*>(wsrep::id::undefined().data());
}
-const unsigned char* wsrep_xid_uuid(const xid_t* xid)
+const wsrep::id& wsrep_xid_uuid(const XID& xid)
{
- DBUG_ASSERT(xid);
- return wsrep_xid_uuid(*xid)->data;
+ compile_time_assert(sizeof(wsrep::id) == sizeof(wsrep_uuid_t));
+ return *reinterpret_cast<const wsrep::id*>(wsrep_xid_uuid(&xid));
}
-wsrep_seqno_t wsrep_xid_seqno(const XID& xid)
+long long wsrep_xid_seqno(const xid_t* xid)
{
- wsrep_seqno_t ret= WSREP_SEQNO_UNDEFINED;
- if (wsrep_is_wsrep_xid(&xid))
+ DBUG_ASSERT(xid);
+ long long ret= wsrep::seqno::undefined().get();
+ if (wsrep_is_wsrep_xid(xid))
{
- switch (xid.data[WSREP_XID_VERSION_OFFSET])
+ switch (xid->data[WSREP_XID_VERSION_OFFSET])
{
case WSREP_XID_VERSION_1:
- memcpy(&ret, xid.data + WSREP_XID_SEQNO_OFFSET, sizeof ret);
+ memcpy(&ret, xid->data + WSREP_XID_SEQNO_OFFSET, sizeof ret);
break;
case WSREP_XID_VERSION_2:
- ret= sint8korr(xid.data + WSREP_XID_SEQNO_OFFSET);
+ ret= sint8korr(xid->data + WSREP_XID_SEQNO_OFFSET);
break;
default:
break;
@@ -93,10 +99,9 @@ wsrep_seqno_t wsrep_xid_seqno(const XID& xid)
return ret;
}
-long long wsrep_xid_seqno(const xid_t* xid)
+wsrep::seqno wsrep_xid_seqno(const XID& xid)
{
- DBUG_ASSERT(xid);
- return wsrep_xid_seqno(*xid);
+ return wsrep::seqno(wsrep_xid_seqno(&xid));
}
static my_bool set_SE_checkpoint(THD* unused, plugin_ref plugin, void* arg)
@@ -106,11 +111,11 @@ static my_bool set_SE_checkpoint(THD* unused, plugin_ref plugin, void* arg)
if (hton->set_checkpoint)
{
- const wsrep_uuid_t* uuid(wsrep_xid_uuid(*xid));
- char uuid_str[40] = {0, };
- wsrep_uuid_print(uuid, uuid_str, sizeof(uuid_str));
+ const unsigned char* uuid= wsrep_xid_uuid(xid);
+ char uuid_str[40]= {0, };
+ wsrep_uuid_print((const wsrep_uuid_t*)uuid, uuid_str, sizeof(uuid_str));
WSREP_DEBUG("Set WSREPXid for InnoDB: %s:%lld",
- uuid_str, (long long)wsrep_xid_seqno(*xid));
+ uuid_str, (long long)wsrep_xid_seqno(xid));
hton->set_checkpoint(hton, xid);
}
return FALSE;
@@ -122,10 +127,10 @@ bool wsrep_set_SE_checkpoint(XID& xid)
&xid);
}
-bool wsrep_set_SE_checkpoint(const wsrep_uuid_t& uuid, wsrep_seqno_t seqno)
+bool wsrep_set_SE_checkpoint(const wsrep::gtid& wsgtid)
{
XID xid;
- wsrep_xid_init(&xid, uuid, seqno);
+ wsrep_xid_init(&xid, wsgtid);
return wsrep_set_SE_checkpoint(xid);
}
@@ -137,11 +142,12 @@ static my_bool get_SE_checkpoint(THD* unused, plugin_ref plugin, void* arg)
if (hton->get_checkpoint)
{
hton->get_checkpoint(hton, xid);
- const wsrep_uuid_t* uuid(wsrep_xid_uuid(*xid));
- char uuid_str[40] = {0, };
- wsrep_uuid_print(uuid, uuid_str, sizeof(uuid_str));
+ wsrep_uuid_t uuid;
+ memcpy(&uuid, wsrep_xid_uuid(xid), sizeof(uuid));
+ char uuid_str[40]= {0, };
+ wsrep_uuid_print(&uuid, uuid_str, sizeof(uuid_str));
WSREP_DEBUG("Read WSREPXid from InnoDB: %s:%lld",
- uuid_str, (long long)wsrep_xid_seqno(*xid));
+ uuid_str, (long long)wsrep_xid_seqno(xid));
}
return FALSE;
}
@@ -152,34 +158,28 @@ bool wsrep_get_SE_checkpoint(XID& xid)
&xid);
}
-bool wsrep_get_SE_checkpoint(wsrep_uuid_t& uuid, wsrep_seqno_t& seqno)
+wsrep::gtid wsrep_get_SE_checkpoint()
{
- uuid= WSREP_UUID_UNDEFINED;
- seqno= WSREP_SEQNO_UNDEFINED;
-
XID xid;
xid.null();
if (wsrep_get_SE_checkpoint(xid))
{
- return true;
+ return wsrep::gtid();
}
if (xid.is_null())
{
- return false;
+ return wsrep::gtid();
}
if (!wsrep_is_wsrep_xid(&xid))
{
WSREP_WARN("Read non-wsrep XID from storage engines.");
- return false;
+ return wsrep::gtid();
}
- uuid= *wsrep_xid_uuid(xid);
- seqno= wsrep_xid_seqno(xid);
-
- return false;
+ return wsrep::gtid(wsrep_xid_uuid(xid),wsrep_xid_seqno(xid));
}
/*
@@ -196,7 +196,7 @@ struct Wsrep_xid_cmp
const bool right_is_wsrep= wsrep_is_wsrep_xid(&right);
if (left_is_wsrep && right_is_wsrep)
{
- return (wsrep_xid_seqno(left) < wsrep_xid_seqno(right));
+ return (wsrep_xid_seqno(&left) < wsrep_xid_seqno(&right));
}
else if (left_is_wsrep)
{
diff --git a/sql/wsrep_xid.h b/sql/wsrep_xid.h
index 30a28b44933..a1b9afc1817 100644
--- a/sql/wsrep_xid.h
+++ b/sql/wsrep_xid.h
@@ -20,17 +20,17 @@
#ifdef WITH_WSREP
-#include "../wsrep/wsrep_api.h"
+#include "wsrep/gtid.hpp"
#include "handler.h" // XID typedef
-void wsrep_xid_init(xid_t*, const wsrep_uuid_t&, wsrep_seqno_t);
-const wsrep_uuid_t* wsrep_xid_uuid(const XID&);
-wsrep_seqno_t wsrep_xid_seqno(const XID&);
+void wsrep_xid_init(xid_t*, const wsrep::gtid&);
+const wsrep::id& wsrep_xid_uuid(const XID&);
+wsrep::seqno wsrep_xid_seqno(const XID&);
+wsrep::gtid wsrep_get_SE_checkpoint();
+bool wsrep_set_SE_checkpoint(const wsrep::gtid& gtid);
//void wsrep_get_SE_checkpoint(XID&); /* uncomment if needed */
-bool wsrep_get_SE_checkpoint(wsrep_uuid_t&, wsrep_seqno_t&);
//void wsrep_set_SE_checkpoint(XID&); /* uncomment if needed */
-bool wsrep_set_SE_checkpoint(const wsrep_uuid_t&, wsrep_seqno_t);
void wsrep_sort_xid_array(XID *array, int len);
diff --git a/sql/xa.cc b/sql/xa.cc
new file mode 100644
index 00000000000..85fbe9358a3
--- /dev/null
+++ b/sql/xa.cc
@@ -0,0 +1,877 @@
+/*
+ Copyright (c) 2000, 2016, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2019, MariaDB Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+*/
+
+#include "mariadb.h"
+#include "sql_class.h"
+#include "transaction.h"
+#include "my_cpu.h"
+
+
+/***************************************************************************
+ Handling of XA id cacheing
+***************************************************************************/
+enum xa_states { XA_ACTIVE= 0, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY };
+
+
+struct XID_cache_insert_element
+{
+ enum xa_states xa_state;
+ XID *xid;
+ XID_cache_element *xid_cache_element;
+
+ XID_cache_insert_element(enum xa_states xa_state_arg, XID *xid_arg):
+ xa_state(xa_state_arg), xid(xid_arg) {}
+};
+
+
+class XID_cache_element
+{
+ /*
+ m_state is used to prevent elements from being deleted while XA RECOVER
+ iterates xid cache and to prevent recovered elments from being acquired by
+ multiple threads.
+
+ bits 1..29 are reference counter
+ bit 30 is RECOVERED flag
+ bit 31 is ACQUIRED flag (thread owns this xid)
+ bit 32 is unused
+
+ Newly allocated and deleted elements have m_state set to 0.
+
+ On lock() m_state is atomically incremented. It also creates load-ACQUIRE
+ memory barrier to make sure m_state is actually updated before furhter
+ memory accesses. Attempting to lock an element that has neither ACQUIRED
+ nor RECOVERED flag set returns failure and further accesses to element
+ memory are forbidden.
+
+ On unlock() m_state is decremented. It also creates store-RELEASE memory
+ barrier to make sure m_state is actually updated after preceding memory
+ accesses.
+
+ ACQUIRED flag is set when thread registers it's xid or when thread acquires
+ recovered xid.
+
+ RECOVERED flag is set for elements found during crash recovery.
+
+ ACQUIRED and RECOVERED flags are cleared before element is deleted from
+ hash in a spin loop, after last reference is released.
+ */
+ std::atomic<int32_t> m_state;
+public:
+ static const int32 ACQUIRED= 1 << 30;
+ static const int32 RECOVERED= 1 << 29;
+ /* Error reported by the Resource Manager (RM) to the Transaction Manager. */
+ uint rm_error;
+ enum xa_states xa_state;
+ XID xid;
+ bool is_set(int32_t flag)
+ { return m_state.load(std::memory_order_relaxed) & flag; }
+ void set(int32_t flag)
+ {
+ DBUG_ASSERT(!is_set(ACQUIRED | RECOVERED));
+ m_state.fetch_add(flag, std::memory_order_relaxed);
+ }
+ bool lock()
+ {
+ int32_t old= m_state.fetch_add(1, std::memory_order_acquire);
+ if (old & (ACQUIRED | RECOVERED))
+ return true;
+ unlock();
+ return false;
+ }
+ void unlock()
+ { m_state.fetch_sub(1, std::memory_order_release); }
+ void mark_uninitialized()
+ {
+ int32_t old= ACQUIRED;
+ while (!m_state.compare_exchange_weak(old, 0,
+ std::memory_order_relaxed,
+ std::memory_order_relaxed))
+ {
+ old&= ACQUIRED | RECOVERED;
+ (void) LF_BACKOFF();
+ }
+ }
+ void acquired_to_recovered()
+ {
+ m_state.fetch_or(RECOVERED, std::memory_order_relaxed);
+ m_state.fetch_and(~ACQUIRED, std::memory_order_release);
+ }
+ bool acquire_recovered()
+ {
+ int32_t old= RECOVERED;
+ while (!m_state.compare_exchange_weak(old, ACQUIRED | RECOVERED,
+ std::memory_order_acquire,
+ std::memory_order_relaxed))
+ {
+ if (!(old & RECOVERED) || (old & ACQUIRED))
+ return false;
+ old= RECOVERED;
+ (void) LF_BACKOFF();
+ }
+ return true;
+ }
+ static void lf_hash_initializer(LF_HASH *hash __attribute__((unused)),
+ XID_cache_element *element,
+ XID_cache_insert_element *new_element)
+ {
+ DBUG_ASSERT(!element->is_set(ACQUIRED | RECOVERED));
+ element->rm_error= 0;
+ element->xa_state= new_element->xa_state;
+ element->xid.set(new_element->xid);
+ new_element->xid_cache_element= element;
+ }
+ static void lf_alloc_constructor(uchar *ptr)
+ {
+ XID_cache_element *element= (XID_cache_element*) (ptr + LF_HASH_OVERHEAD);
+ element->m_state= 0;
+ }
+ static void lf_alloc_destructor(uchar *ptr)
+ {
+ DBUG_ASSERT(!reinterpret_cast<XID_cache_element*>(ptr + LF_HASH_OVERHEAD)
+ ->is_set(ACQUIRED));
+ }
+ static uchar *key(const XID_cache_element *element, size_t *length,
+ my_bool not_used __attribute__((unused)))
+ {
+ *length= element->xid.key_length();
+ return element->xid.key();
+ }
+};
+
+
+static LF_HASH xid_cache;
+static bool xid_cache_inited;
+
+
+bool THD::fix_xid_hash_pins()
+{
+ if (!xid_hash_pins)
+ xid_hash_pins= lf_hash_get_pins(&xid_cache);
+ return !xid_hash_pins;
+}
+
+
+void XID_STATE::set_error(uint error)
+{
+ if (is_explicit_XA())
+ xid_cache_element->rm_error= error;
+}
+
+
+void XID_STATE::er_xaer_rmfail() const
+{
+ static const char *xa_state_names[]=
+ { "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY" };
+ my_error(ER_XAER_RMFAIL, MYF(0), is_explicit_XA() ?
+ xa_state_names[xid_cache_element->xa_state] : "NON-EXISTING");
+}
+
+
+/**
+ Check that XA transaction has an uncommitted work. Report an error
+ to the user in case when there is an uncommitted work for XA transaction.
+
+ @return result of check
+ @retval false XA transaction is NOT in state IDLE, PREPARED
+ or ROLLBACK_ONLY.
+ @retval true XA transaction is in state IDLE or PREPARED
+ or ROLLBACK_ONLY.
+*/
+
+bool XID_STATE::check_has_uncommitted_xa() const
+{
+ if (is_explicit_XA() && xid_cache_element->xa_state != XA_ACTIVE)
+ {
+ er_xaer_rmfail();
+ return true;
+ }
+ return false;
+}
+
+
+XID *XID_STATE::get_xid() const
+{
+ DBUG_ASSERT(is_explicit_XA());
+ return &xid_cache_element->xid;
+}
+
+
+void xid_cache_init()
+{
+ xid_cache_inited= true;
+ lf_hash_init(&xid_cache, sizeof(XID_cache_element), LF_HASH_UNIQUE, 0, 0,
+ (my_hash_get_key) XID_cache_element::key, &my_charset_bin);
+ xid_cache.alloc.constructor= XID_cache_element::lf_alloc_constructor;
+ xid_cache.alloc.destructor= XID_cache_element::lf_alloc_destructor;
+ xid_cache.initializer=
+ (lf_hash_initializer) XID_cache_element::lf_hash_initializer;
+}
+
+
+void xid_cache_free()
+{
+ if (xid_cache_inited)
+ {
+ lf_hash_destroy(&xid_cache);
+ xid_cache_inited= false;
+ }
+}
+
+
+/**
+ Find recovered XA transaction by XID.
+*/
+
+static XID_cache_element *xid_cache_search(THD *thd, XID *xid)
+{
+ DBUG_ASSERT(thd->xid_hash_pins);
+ XID_cache_element *element=
+ (XID_cache_element*) lf_hash_search(&xid_cache, thd->xid_hash_pins,
+ xid->key(), xid->key_length());
+ if (element)
+ {
+ if (!element->acquire_recovered())
+ element= 0;
+ lf_hash_search_unpin(thd->xid_hash_pins);
+ DEBUG_SYNC(thd, "xa_after_search");
+ }
+ return element;
+}
+
+
+bool xid_cache_insert(XID *xid)
+{
+ XID_cache_insert_element new_element(XA_PREPARED, xid);
+ LF_PINS *pins;
+
+ if (!(pins= lf_hash_get_pins(&xid_cache)))
+ return true;
+
+ int res= lf_hash_insert(&xid_cache, pins, &new_element);
+ switch (res)
+ {
+ case 0:
+ new_element.xid_cache_element->set(XID_cache_element::RECOVERED);
+ break;
+ case 1:
+ res= 0;
+ }
+ lf_hash_put_pins(pins);
+ return res;
+}
+
+
+bool xid_cache_insert(THD *thd, XID_STATE *xid_state, XID *xid)
+{
+ XID_cache_insert_element new_element(XA_ACTIVE, xid);
+
+ if (thd->fix_xid_hash_pins())
+ return true;
+
+ int res= lf_hash_insert(&xid_cache, thd->xid_hash_pins, &new_element);
+ switch (res)
+ {
+ case 0:
+ xid_state->xid_cache_element= new_element.xid_cache_element;
+ xid_state->xid_cache_element->set(XID_cache_element::ACQUIRED);
+ break;
+ case 1:
+ my_error(ER_XAER_DUPID, MYF(0));
+ }
+ return res;
+}
+
+
+static void xid_cache_delete(THD *thd, XID_cache_element *&element)
+{
+ DBUG_ASSERT(thd->xid_hash_pins);
+ element->mark_uninitialized();
+ lf_hash_delete(&xid_cache, thd->xid_hash_pins,
+ element->xid.key(), element->xid.key_length());
+}
+
+
+void xid_cache_delete(THD *thd, XID_STATE *xid_state)
+{
+ DBUG_ASSERT(xid_state->is_explicit_XA());
+ xid_cache_delete(thd, xid_state->xid_cache_element);
+ xid_state->xid_cache_element= 0;
+}
+
+
+struct xid_cache_iterate_arg
+{
+ my_hash_walk_action action;
+ void *argument;
+};
+
+static my_bool xid_cache_iterate_callback(XID_cache_element *element,
+ xid_cache_iterate_arg *arg)
+{
+ my_bool res= FALSE;
+ if (element->lock())
+ {
+ res= arg->action(element, arg->argument);
+ element->unlock();
+ }
+ return res;
+}
+
+static int xid_cache_iterate(THD *thd, my_hash_walk_action action, void *arg)
+{
+ xid_cache_iterate_arg argument= { action, arg };
+ return thd->fix_xid_hash_pins() ? -1 :
+ lf_hash_iterate(&xid_cache, thd->xid_hash_pins,
+ (my_hash_walk_action) xid_cache_iterate_callback,
+ &argument);
+}
+
+
+/**
+ Mark a XA transaction as rollback-only if the RM unilaterally
+ rolled back the transaction branch.
+
+ @note If a rollback was requested by the RM, this function sets
+ the appropriate rollback error code and transits the state
+ to XA_ROLLBACK_ONLY.
+
+ @return TRUE if transaction was rolled back or if the transaction
+ state is XA_ROLLBACK_ONLY. FALSE otherwise.
+*/
+static bool xa_trans_rolled_back(XID_cache_element *element)
+{
+ if (element->rm_error)
+ {
+ switch (element->rm_error) {
+ case ER_LOCK_WAIT_TIMEOUT:
+ my_error(ER_XA_RBTIMEOUT, MYF(0));
+ break;
+ case ER_LOCK_DEADLOCK:
+ my_error(ER_XA_RBDEADLOCK, MYF(0));
+ break;
+ default:
+ my_error(ER_XA_RBROLLBACK, MYF(0));
+ }
+ element->xa_state= XA_ROLLBACK_ONLY;
+ }
+
+ return element->xa_state == XA_ROLLBACK_ONLY;
+}
+
+
+/**
+ Rollback the active XA transaction.
+
+ @return TRUE if the rollback failed, FALSE otherwise.
+*/
+
+static bool xa_trans_force_rollback(THD *thd)
+{
+ bool rc= false;
+
+ if (ha_rollback_trans(thd, true))
+ {
+ my_error(ER_XAER_RMERR, MYF(0));
+ rc= true;
+ }
+
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->transaction.all.reset();
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+ xid_cache_delete(thd, &thd->transaction.xid_state);
+
+ trans_track_end_trx(thd);
+ thd->mdl_context.release_transactional_locks(thd);
+
+ return rc;
+}
+
+
+/**
+ Starts an XA transaction with the given xid value.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_xa_start(THD *thd)
+{
+ DBUG_ENTER("trans_xa_start");
+
+ if (thd->transaction.xid_state.is_explicit_XA() &&
+ thd->transaction.xid_state.xid_cache_element->xa_state == XA_IDLE &&
+ thd->lex->xa_opt == XA_RESUME)
+ {
+ bool not_equal=
+ !thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid);
+ if (not_equal)
+ my_error(ER_XAER_NOTA, MYF(0));
+ else
+ thd->transaction.xid_state.xid_cache_element->xa_state= XA_ACTIVE;
+ DBUG_RETURN(not_equal);
+ }
+
+ /* TODO: JOIN is not supported yet. */
+ if (thd->lex->xa_opt != XA_NONE)
+ my_error(ER_XAER_INVAL, MYF(0));
+ else if (thd->transaction.xid_state.is_explicit_XA())
+ thd->transaction.xid_state.er_xaer_rmfail();
+ else if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
+ my_error(ER_XAER_OUTSIDE, MYF(0));
+ else if (!trans_begin(thd))
+ {
+ if (xid_cache_insert(thd, &thd->transaction.xid_state, thd->lex->xid))
+ {
+ trans_rollback(thd);
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(FALSE);
+ }
+
+ DBUG_RETURN(TRUE);
+}
+
+
+/**
+ Put a XA transaction in the IDLE state.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_xa_end(THD *thd)
+{
+ DBUG_ENTER("trans_xa_end");
+
+ /* TODO: SUSPEND and FOR MIGRATE are not supported yet. */
+ if (thd->lex->xa_opt != XA_NONE)
+ my_error(ER_XAER_INVAL, MYF(0));
+ else if (!thd->transaction.xid_state.is_explicit_XA() ||
+ thd->transaction.xid_state.xid_cache_element->xa_state != XA_ACTIVE)
+ thd->transaction.xid_state.er_xaer_rmfail();
+ else if (!thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid))
+ my_error(ER_XAER_NOTA, MYF(0));
+ else if (!xa_trans_rolled_back(thd->transaction.xid_state.xid_cache_element))
+ thd->transaction.xid_state.xid_cache_element->xa_state= XA_IDLE;
+
+ DBUG_RETURN(thd->is_error() ||
+ thd->transaction.xid_state.xid_cache_element->xa_state != XA_IDLE);
+}
+
+
+/**
+ Put a XA transaction in the PREPARED state.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_xa_prepare(THD *thd)
+{
+ DBUG_ENTER("trans_xa_prepare");
+
+ if (!thd->transaction.xid_state.is_explicit_XA() ||
+ thd->transaction.xid_state.xid_cache_element->xa_state != XA_IDLE)
+ thd->transaction.xid_state.er_xaer_rmfail();
+ else if (!thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid))
+ my_error(ER_XAER_NOTA, MYF(0));
+ else if (ha_prepare(thd))
+ {
+ xid_cache_delete(thd, &thd->transaction.xid_state);
+ my_error(ER_XA_RBROLLBACK, MYF(0));
+ }
+ else
+ thd->transaction.xid_state.xid_cache_element->xa_state= XA_PREPARED;
+
+ DBUG_RETURN(thd->is_error() ||
+ thd->transaction.xid_state.xid_cache_element->xa_state != XA_PREPARED);
+}
+
+
+/**
+ Commit and terminate the a XA transaction.
+ Transactional locks are released if transaction ended
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+
+*/
+
+bool trans_xa_commit(THD *thd)
+{
+ bool res= TRUE;
+ DBUG_ENTER("trans_xa_commit");
+
+ if (!thd->transaction.xid_state.is_explicit_XA() ||
+ !thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid))
+ {
+ if (thd->fix_xid_hash_pins())
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ if (auto xs= xid_cache_search(thd, thd->lex->xid))
+ {
+ res= xa_trans_rolled_back(xs);
+ ha_commit_or_rollback_by_xid(thd->lex->xid, !res);
+ xid_cache_delete(thd, xs);
+ }
+ else
+ my_error(ER_XAER_NOTA, MYF(0));
+ DBUG_RETURN(res);
+ }
+
+ if (xa_trans_rolled_back(thd->transaction.xid_state.xid_cache_element))
+ {
+ xa_trans_force_rollback(thd);
+ DBUG_RETURN(thd->is_error());
+ }
+ else if (thd->transaction.xid_state.xid_cache_element->xa_state == XA_IDLE &&
+ thd->lex->xa_opt == XA_ONE_PHASE)
+ {
+ int r= ha_commit_trans(thd, TRUE);
+ if ((res= MY_TEST(r)))
+ my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
+ }
+ else if (thd->transaction.xid_state.xid_cache_element->xa_state == XA_PREPARED &&
+ thd->lex->xa_opt == XA_NONE)
+ {
+ MDL_request mdl_request;
+
+ /*
+ Acquire metadata lock which will ensure that COMMIT is blocked
+ by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
+ progress blocks FTWRL).
+
+ We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
+ */
+ mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_TRANSACTION);
+
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ {
+ ha_rollback_trans(thd, TRUE);
+ my_error(ER_XAER_RMERR, MYF(0));
+ }
+ else
+ {
+ DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
+
+ res= MY_TEST(ha_commit_one_phase(thd, 1));
+ if (res)
+ my_error(ER_XAER_RMERR, MYF(0));
+ }
+ }
+ else
+ {
+ thd->transaction.xid_state.er_xaer_rmfail();
+ DBUG_RETURN(TRUE);
+ }
+
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->transaction.all.reset();
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+ xid_cache_delete(thd, &thd->transaction.xid_state);
+
+ trans_track_end_trx(thd);
+ thd->mdl_context.release_transactional_locks(thd);
+
+ DBUG_RETURN(res);
+}
+
+
+/**
+ Roll back and terminate a XA transaction.
+ Transactional locks are released if transaction ended
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_xa_rollback(THD *thd)
+{
+ DBUG_ENTER("trans_xa_rollback");
+
+ if (!thd->transaction.xid_state.is_explicit_XA() ||
+ !thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid))
+ {
+ if (thd->fix_xid_hash_pins())
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ if (auto xs= xid_cache_search(thd, thd->lex->xid))
+ {
+ xa_trans_rolled_back(xs);
+ ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
+ xid_cache_delete(thd, xs);
+ }
+ else
+ my_error(ER_XAER_NOTA, MYF(0));
+ DBUG_RETURN(thd->get_stmt_da()->is_error());
+ }
+
+ if (thd->transaction.xid_state.xid_cache_element->xa_state == XA_ACTIVE)
+ {
+ thd->transaction.xid_state.er_xaer_rmfail();
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(xa_trans_force_rollback(thd));
+}
+
+
+bool trans_xa_detach(THD *thd)
+{
+ DBUG_ASSERT(thd->transaction.xid_state.is_explicit_XA());
+#if 1
+ return xa_trans_force_rollback(thd);
+#else
+ if (thd->transaction.xid_state.xid_cache_element->xa_state != XA_PREPARED)
+ return xa_trans_force_rollback(thd);
+ thd->transaction.xid_state.xid_cache_element->acquired_to_recovered();
+ thd->transaction.xid_state.xid_cache_element= 0;
+ thd->transaction.cleanup();
+
+ Ha_trx_info *ha_info, *ha_info_next;
+ for (ha_info= thd->transaction.all.ha_list;
+ ha_info;
+ ha_info= ha_info_next)
+ {
+ ha_info_next= ha_info->next();
+ ha_info->reset(); /* keep it conveniently zero-filled */
+ }
+
+ thd->transaction.all.ha_list= 0;
+ thd->transaction.all.no_2pc= 0;
+ thd->server_status&= ~(SERVER_STATUS_IN_TRANS |
+ SERVER_STATUS_IN_TRANS_READONLY);
+ thd->mdl_context.release_transactional_locks(thd);
+
+ return false;
+#endif
+}
+
+
+/**
+ return the XID as it appears in the SQL function's arguments.
+ So this string can be passed to XA START, XA PREPARE etc...
+
+ @note
+ the 'buf' has to have space for at least SQL_XIDSIZE bytes.
+*/
+
+
+/*
+ 'a'..'z' 'A'..'Z', '0'..'9'
+ and '-' '_' ' ' symbols don't have to be
+ converted.
+*/
+
+static const char xid_needs_conv[128]=
+{
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
+ 0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1
+};
+
+/*
+ The size of XID string representation in the form
+ 'gtrid', 'bqual', formatID
+ see xid_t::get_sql_string() for details.
+*/
+#define SQL_XIDSIZE (XIDDATASIZE * 2 + 8 + MY_INT64_NUM_DECIMAL_DIGITS)
+
+/* The 'buf' has to have space for at least SQL_XIDSIZE bytes. */
+static uint get_sql_xid(XID *xid, char *buf)
+{
+ int tot_len= xid->gtrid_length + xid->bqual_length;
+ int i;
+ const char *orig_buf= buf;
+
+ for (i=0; i<tot_len; i++)
+ {
+ uchar c= ((uchar *) xid->data)[i];
+ if (c >= 128 || xid_needs_conv[c])
+ break;
+ }
+
+ if (i >= tot_len)
+ {
+ /* No need to convert characters to hexadecimals. */
+ *buf++= '\'';
+ memcpy(buf, xid->data, xid->gtrid_length);
+ buf+= xid->gtrid_length;
+ *buf++= '\'';
+ if (xid->bqual_length > 0 || xid->formatID != 1)
+ {
+ *buf++= ',';
+ *buf++= '\'';
+ memcpy(buf, xid->data+xid->gtrid_length, xid->bqual_length);
+ buf+= xid->bqual_length;
+ *buf++= '\'';
+ }
+ }
+ else
+ {
+ *buf++= 'X';
+ *buf++= '\'';
+ for (i= 0; i < xid->gtrid_length; i++)
+ {
+ *buf++=_dig_vec_lower[((uchar*) xid->data)[i] >> 4];
+ *buf++=_dig_vec_lower[((uchar*) xid->data)[i] & 0x0f];
+ }
+ *buf++= '\'';
+ if (xid->bqual_length > 0 || xid->formatID != 1)
+ {
+ *buf++= ',';
+ *buf++= 'X';
+ *buf++= '\'';
+ for (; i < tot_len; i++)
+ {
+ *buf++=_dig_vec_lower[((uchar*) xid->data)[i] >> 4];
+ *buf++=_dig_vec_lower[((uchar*) xid->data)[i] & 0x0f];
+ }
+ *buf++= '\'';
+ }
+ }
+
+ if (xid->formatID != 1)
+ {
+ *buf++= ',';
+ buf+= my_longlong10_to_str_8bit(&my_charset_bin, buf,
+ MY_INT64_NUM_DECIMAL_DIGITS, -10, xid->formatID);
+ }
+
+ return (uint)(buf - orig_buf);
+}
+
+
+/**
+ return the list of XID's to a client, the same way SHOW commands do.
+
+ @note
+ I didn't find in XA specs that an RM cannot return the same XID twice,
+ so mysql_xa_recover does not filter XID's to ensure uniqueness.
+ It can be easily fixed later, if necessary.
+*/
+
+static my_bool xa_recover_callback(XID_cache_element *xs, Protocol *protocol,
+ char *data, uint data_len, CHARSET_INFO *data_cs)
+{
+ if (xs->xa_state == XA_PREPARED)
+ {
+ protocol->prepare_for_resend();
+ protocol->store_longlong((longlong) xs->xid.formatID, FALSE);
+ protocol->store_longlong((longlong) xs->xid.gtrid_length, FALSE);
+ protocol->store_longlong((longlong) xs->xid.bqual_length, FALSE);
+ protocol->store(data, data_len, data_cs);
+ if (protocol->write())
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static my_bool xa_recover_callback_short(XID_cache_element *xs,
+ Protocol *protocol)
+{
+ return xa_recover_callback(xs, protocol, xs->xid.data,
+ xs->xid.gtrid_length + xs->xid.bqual_length, &my_charset_bin);
+}
+
+
+static my_bool xa_recover_callback_verbose(XID_cache_element *xs,
+ Protocol *protocol)
+{
+ char buf[SQL_XIDSIZE];
+ uint len= get_sql_xid(&xs->xid, buf);
+ return xa_recover_callback(xs, protocol, buf, len,
+ &my_charset_utf8_general_ci);
+}
+
+
+bool mysql_xa_recover(THD *thd)
+{
+ List<Item> field_list;
+ Protocol *protocol= thd->protocol;
+ MEM_ROOT *mem_root= thd->mem_root;
+ my_hash_walk_action action;
+ DBUG_ENTER("mysql_xa_recover");
+
+ field_list.push_back(new (mem_root)
+ Item_int(thd, "formatID", 0,
+ MY_INT32_NUM_DECIMAL_DIGITS), mem_root);
+ field_list.push_back(new (mem_root)
+ Item_int(thd, "gtrid_length", 0,
+ MY_INT32_NUM_DECIMAL_DIGITS), mem_root);
+ field_list.push_back(new (mem_root)
+ Item_int(thd, "bqual_length", 0,
+ MY_INT32_NUM_DECIMAL_DIGITS), mem_root);
+ {
+ uint len;
+ CHARSET_INFO *cs;
+
+ if (thd->lex->verbose)
+ {
+ len= SQL_XIDSIZE;
+ cs= &my_charset_utf8_general_ci;
+ action= (my_hash_walk_action) xa_recover_callback_verbose;
+ }
+ else
+ {
+ len= XIDDATASIZE;
+ cs= &my_charset_bin;
+ action= (my_hash_walk_action) xa_recover_callback_short;
+ }
+
+ field_list.push_back(new (mem_root)
+ Item_empty_string(thd, "data", len, cs), mem_root);
+ }
+
+ if (protocol->send_result_set_metadata(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ DBUG_RETURN(1);
+
+ if (xid_cache_iterate(thd, action, protocol))
+ DBUG_RETURN(1);
+ my_eof(thd);
+ DBUG_RETURN(0);
+}
diff --git a/sql/xa.h b/sql/xa.h
new file mode 100644
index 00000000000..7cf74efad35
--- /dev/null
+++ b/sql/xa.h
@@ -0,0 +1,44 @@
+/*
+ Copyright (c) 2000, 2016, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2019, MariaDB Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+*/
+
+
+class XID_cache_element;
+
+struct XID_STATE {
+ XID_cache_element *xid_cache_element;
+
+ bool check_has_uncommitted_xa() const;
+ bool is_explicit_XA() const { return xid_cache_element != 0; }
+ void set_error(uint error);
+ void er_xaer_rmfail() const;
+ XID *get_xid() const;
+};
+
+void xid_cache_init(void);
+void xid_cache_free(void);
+bool xid_cache_insert(XID *xid);
+bool xid_cache_insert(THD *thd, XID_STATE *xid_state, XID *xid);
+void xid_cache_delete(THD *thd, XID_STATE *xid_state);
+
+bool trans_xa_start(THD *thd);
+bool trans_xa_end(THD *thd);
+bool trans_xa_prepare(THD *thd);
+bool trans_xa_commit(THD *thd);
+bool trans_xa_rollback(THD *thd);
+bool trans_xa_detach(THD *thd);
+bool mysql_xa_recover(THD *thd);