summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <mkindahl@dl145h.mysql.com>2007-07-25 15:40:43 +0200
committerunknown <mkindahl@dl145h.mysql.com>2007-07-25 15:40:43 +0200
commitef9738fe92e16b74c84b7f071e2cefb7b13b7fdd (patch)
tree86abdb47243643d4d06ad915695763fe597197c7 /sql
parentb9c18aead4c151a555325462c20121d8c8a654a3 (diff)
parent6d0e34413d0aee7553b826d4cb5c9c64939ee990 (diff)
downloadmariadb-git-ef9738fe92e16b74c84b7f071e2cefb7b13b7fdd.tar.gz
Merge dl145h.mysql.com:/data0/mkindahl/mysql-5.1-main
into dl145h.mysql.com:/data0/mkindahl/mysql-5.1-2team include/my_bitmap.h: Auto merged mysql-test/suite/binlog/r/binlog_multi_engine.result: Auto merged mysql-test/suite/ndb/r/ndb_binlog_multi.result: Auto merged mysql-test/suite/ndb/t/ndb_autodiscover3.test: Auto merged mysql-test/suite/ndb/t/ndb_binlog_basic.test: Auto merged mysql-test/suite/ndb/t/ndb_binlog_ddl_multi.test: Auto merged mysql-test/suite/ndb/t/ndb_binlog_discover.test: Auto merged mysql-test/suite/ndb/t/ndb_binlog_ignore_db.test: Auto merged mysql-test/suite/ndb/t/ndb_binlog_log_bin.test: Auto merged mysql-test/suite/ndb/t/ndb_binlog_multi.test: Auto merged mysql-test/suite/ndb/t/ndb_multi_row.test: Auto merged mysql-test/suite/rpl/r/rpl_row_log.result: Auto merged mysql-test/suite/rpl/r/rpl_row_log_innodb.result: Auto merged mysql-test/suite/rpl/r/rpl_stm_log.result: Auto merged mysql-test/suite/rpl/r/rpl_truncate_7ndb_2.result: Auto merged mysql-test/suite/rpl_ndb/r/rpl_ndb_dd_partitions.result: Auto merged mysql-test/suite/rpl_ndb/r/rpl_ndb_log.result: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_UUID.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_bank.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_basic.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_blob.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_blob2.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_circular.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_circular_simplex.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_dd_advance.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_dd_basic.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_dd_partitions.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_ddl.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_delete_nowhere.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_do_db.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_do_table.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_extraCol.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_func003.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_idempotent.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_insert_ignore.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_multi.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_multi_update2.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_multi_update3.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_rep_ignore.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_row_001.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_sp003.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_sp006.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_sync.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndb_trig004.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_ndbapi_multi.test: Auto merged mysql-test/suite/rpl_ndb/t/rpl_row_basic_7ndb.test: Auto merged sql/log_event.cc: Auto merged mysql-test/suite/rpl_ndb/r/rpl_truncate_7ndb.result: Manual merge mysql-test/t/disabled.def: Manual merge of main tree into replication tree
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt20
-rw-r--r--sql/Makefile.am3
-rw-r--r--sql/events.cc3
-rw-r--r--sql/field.cc18
-rw-r--r--sql/field.h4
-rw-r--r--sql/field_conv.cc17
-rw-r--r--sql/filesort.cc21
-rw-r--r--sql/ha_ndbcluster.cc21
-rw-r--r--sql/ha_partition.cc81
-rw-r--r--sql/ha_partition.h1
-rw-r--r--sql/handler.h57
-rw-r--r--sql/item.cc2
-rw-r--r--sql/item.h26
-rw-r--r--sql/item_cmpfunc.cc110
-rw-r--r--sql/item_cmpfunc.h16
-rw-r--r--sql/item_create.cc14
-rw-r--r--sql/item_func.h32
-rw-r--r--sql/item_strfunc.cc2
-rw-r--r--sql/item_strfunc.h14
-rw-r--r--sql/item_sum.cc1
-rw-r--r--sql/item_timefunc.cc5
-rw-r--r--sql/item_timefunc.h17
-rw-r--r--sql/item_xmlfunc.h1
-rw-r--r--sql/lock.cc96
-rw-r--r--sql/log.cc145
-rw-r--r--sql/log_event.cc17
-rw-r--r--sql/mysql_priv.h118
-rw-r--r--sql/mysqld.cc3
-rw-r--r--sql/opt_range.cc40
-rw-r--r--sql/partition_info.cc44
-rw-r--r--sql/partition_info.h7
-rw-r--r--sql/rpl_utility.h2
-rw-r--r--sql/set_var.cc8
-rw-r--r--sql/share/errmsg.txt72
-rw-r--r--sql/slave.cc320
-rw-r--r--sql/sp.cc63
-rw-r--r--sql/sp_head.cc56
-rw-r--r--sql/sp_head.h6
-rw-r--r--sql/spatial.cc23
-rw-r--r--sql/sql_base.cc8
-rw-r--r--sql/sql_cache.cc741
-rw-r--r--sql/sql_cache.h73
-rw-r--r--sql/sql_class.cc150
-rw-r--r--sql/sql_class.h81
-rw-r--r--sql/sql_db.cc10
-rw-r--r--sql/sql_insert.cc151
-rw-r--r--sql/sql_lex.cc78
-rw-r--r--sql/sql_lex.h50
-rw-r--r--sql/sql_load.cc1
-rw-r--r--sql/sql_parse.cc19
-rw-r--r--sql/sql_partition.cc45
-rw-r--r--sql/sql_prepare.cc59
-rw-r--r--sql/sql_rename.cc26
-rw-r--r--sql/sql_repl.cc6
-rw-r--r--sql/sql_select.cc14
-rw-r--r--sql/sql_show.cc63
-rw-r--r--sql/sql_show.h3
-rw-r--r--sql/sql_sort.h9
-rw-r--r--sql/sql_table.cc85
-rw-r--r--sql/sql_trigger.cc80
-rw-r--r--sql/sql_trigger.h8
-rw-r--r--sql/sql_udf.h1
-rw-r--r--sql/sql_update.cc105
-rw-r--r--sql/sql_view.cc13
-rw-r--r--sql/sql_yacc.yy27
-rw-r--r--sql/stacktrace.c9
-rw-r--r--sql/table.cc229
-rw-r--r--sql/table.h79
-rw-r--r--sql/uniques.cc13
-rw-r--r--sql/unireg.cc2
-rw-r--r--sql/unireg.h2
71 files changed, 2609 insertions, 1137 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 85c2013d8e7..a8aa7d70586 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -15,9 +15,10 @@
INCLUDE("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake")
SET(CMAKE_CXX_FLAGS_DEBUG
- "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR")
+ "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR /Zi")
SET(CMAKE_C_FLAGS_DEBUG
- "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR")
+ "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR /Zi")
+SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /MAP /MAPINFO:EXPORTS")
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/extra/yassl/include
@@ -40,7 +41,8 @@ SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/sql/message.rc
ADD_DEFINITIONS(-DMYSQL_SERVER -D_CONSOLE -DHAVE_DLOPEN)
-ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc
+ADD_EXECUTABLE(mysqld${MYSQLD_EXE_SUFFIX}
+ ../sql-common/client.c derror.cc des_key_file.cc
discover.cc ../libmysql/errmsg.c field.cc field_conv.cc
filesort.cc gstream.cc
ha_partition.cc
@@ -81,7 +83,8 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc
${PROJECT_SOURCE_DIR}/include/mysql_version.h
${PROJECT_SOURCE_DIR}/sql/sql_builtin.cc
${PROJECT_SOURCE_DIR}/sql/lex_hash.h)
-TARGET_LINK_LIBRARIES(mysqld heap myisam myisammrg mysys yassl zlib dbug yassl
+TARGET_LINK_LIBRARIES(mysqld${MYSQLD_EXE_SUFFIX}
+ heap myisam myisammrg mysys yassl zlib dbug yassl
taocrypt strings vio regex wsock32 ws2_32)
IF(EMBED_MANIFESTS)
@@ -97,16 +100,16 @@ IF(WITH_CSV_STORAGE_ENGINE)
TARGET_LINK_LIBRARIES(mysqld csv)
ENDIF(WITH_CSV_STORAGE_ENGINE)
IF(WITH_EXAMPLE_STORAGE_ENGINE)
- TARGET_LINK_LIBRARIES(mysqld example)
+ TARGET_LINK_LIBRARIES(mysqld${MYSQLD_EXE_SUFFIX} example)
ENDIF(WITH_EXAMPLE_STORAGE_ENGINE)
IF(WITH_FEDERATED_STORAGE_ENGINE)
TARGET_LINK_LIBRARIES(mysqld federated)
ENDIF(WITH_FEDERATED_STORAGE_ENGINE)
IF(WITH_INNOBASE_STORAGE_ENGINE)
- TARGET_LINK_LIBRARIES(mysqld innobase)
+ TARGET_LINK_LIBRARIES(mysqld${MYSQLD_EXE_SUFFIX} innobase)
ENDIF(WITH_INNOBASE_STORAGE_ENGINE)
-ADD_DEPENDENCIES(mysqld GenError)
+ADD_DEPENDENCIES(mysqld${MYSQLD_EXE_SUFFIX} GenError)
# Sql Parser custom command
ADD_CUSTOM_COMMAND(
@@ -138,8 +141,7 @@ ADD_CUSTOM_COMMAND(
COMMAND ${GEN_LEX_HASH_EXE} ARGS > lex_hash.h
DEPENDS ${GEN_LEX_HASH_EXE}
)
-
-ADD_DEPENDENCIES(mysqld gen_lex_hash)
+ADD_DEPENDENCIES(mysqld${MYSQLD_EXE_SUFFIX} gen_lex_hash)
ADD_LIBRARY(udf_example MODULE udf_example.c udf_example.def)
ADD_DEPENDENCIES(udf_example strings)
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 36d066758bc..ff33c1cae0c 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -43,8 +43,7 @@ mysqld_LDADD = libndb.la \
@pstack_libs@ \
@mysql_plugin_libs@ \
$(LDADD) $(CXXLDFLAGS) $(WRAPLIBS) @LIBDL@ \
- $(yassl_libs) $(openssl_libs) \
- @MYSQLD_EXTRA_LIBS@
+ $(yassl_libs) $(openssl_libs) @MYSQLD_EXTRA_LIBS@
noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
item_strfunc.h item_timefunc.h \
diff --git a/sql/events.cc b/sql/events.cc
index e48daeca63d..8d32580816f 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -717,7 +717,8 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
protocol->store(et->name.str, et->name.length, system_charset_info);
protocol->store(sql_mode.str, sql_mode.length, system_charset_info);
protocol->store(tz_name->ptr(), tz_name->length(), system_charset_info);
- protocol->store(show_str.c_ptr(), show_str.length(), &my_charset_bin);
+ protocol->store(show_str.c_ptr(), show_str.length(),
+ et->creation_ctx->get_client_cs());
protocol->store(et->creation_ctx->get_client_cs()->csname,
strlen(et->creation_ctx->get_client_cs()->csname),
system_charset_info);
diff --git a/sql/field.cc b/sql/field.cc
index 55a93ed46d6..a970a6e4318 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1360,6 +1360,18 @@ bool Field::send_binary(Protocol *protocol)
}
+int Field::store(const char *to, uint length, CHARSET_INFO *cs,
+ enum_check_fields check_level)
+{
+ int res;
+ enum_check_fields old_check_level= table->in_use->count_cuted_fields;
+ table->in_use->count_cuted_fields= check_level;
+ res= store(to, length, cs);
+ table->in_use->count_cuted_fields= old_check_level;
+ return res;
+}
+
+
my_decimal *Field::val_decimal(my_decimal *decimal)
{
/* This never have to be called */
@@ -2316,6 +2328,7 @@ Field_new_decimal::Field_new_decimal(uchar *ptr_arg,
unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg)
{
precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg);
+ set_if_smaller(precision, DECIMAL_MAX_PRECISION);
DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) &&
(dec <= DECIMAL_MAX_SCALE));
bin_size= my_decimal_get_binary_size(precision, dec);
@@ -2332,6 +2345,7 @@ Field_new_decimal::Field_new_decimal(uint32 len_arg,
NONE, name, dec_arg, 0, unsigned_arg)
{
precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg);
+ set_if_smaller(precision, DECIMAL_MAX_PRECISION);
DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) &&
(dec <= DECIMAL_MAX_SCALE));
bin_size= my_decimal_get_binary_size(precision, dec);
@@ -5392,7 +5406,8 @@ int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs)
else
{
tmp= l_time.day + l_time.month*32 + l_time.year*16*32;
- if (!error && (ret != MYSQL_TIMESTAMP_DATE))
+ if (!error && (ret != MYSQL_TIMESTAMP_DATE) &&
+ thd->count_cuted_fields != CHECK_FIELD_IGNORE)
error= 3; // Datetime was cut (note)
}
@@ -6457,6 +6472,7 @@ Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table,
is 2.
****************************************************************************/
+const uint Field_varstring::MAX_SIZE= UINT_MAX16;
int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
{
diff --git a/sql/field.h b/sql/field.h
index a0fe0f2e57e..8bf087c7ebd 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -100,6 +100,8 @@ public:
virtual int store(longlong nr, bool unsigned_val)=0;
virtual int store_decimal(const my_decimal *d)=0;
virtual int store_time(MYSQL_TIME *ltime, timestamp_type t_type);
+ int store(const char *to, uint length, CHARSET_INFO *cs,
+ enum_check_fields check_level);
virtual double val_real(void)=0;
virtual longlong val_int(void)=0;
virtual my_decimal *val_decimal(my_decimal *);
@@ -1183,7 +1185,7 @@ public:
The maximum space available in a Field_varstring, in bytes. See
length_bytes.
*/
- static const uint MAX_SIZE= UINT_MAX16;
+ static const uint MAX_SIZE;
/* Store number of bytes used to store length (1 or 2) */
uint32 length_bytes;
Field_varstring(uchar *ptr_arg,
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index 44aea9acee0..33c7897c3db 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -795,11 +795,18 @@ int field_conv(Field *to,Field *from)
blob->value.copy();
return blob->store(blob->value.ptr(),blob->value.length(),from->charset());
}
- if ((from->result_type() == STRING_RESULT &&
- (to->result_type() == STRING_RESULT ||
- (from->real_type() != MYSQL_TYPE_ENUM &&
- from->real_type() != MYSQL_TYPE_SET))) ||
- to->type() == MYSQL_TYPE_DECIMAL)
+ if (from->real_type() == MYSQL_TYPE_ENUM &&
+ to->real_type() == MYSQL_TYPE_ENUM &&
+ from->val_int() == 0)
+ {
+ ((Field_enum *)(to))->store_type(0);
+ return 0;
+ }
+ else if ((from->result_type() == STRING_RESULT &&
+ (to->result_type() == STRING_RESULT ||
+ (from->real_type() != MYSQL_TYPE_ENUM &&
+ from->real_type() != MYSQL_TYPE_SET))) ||
+ to->type() == MYSQL_TYPE_DECIMAL)
{
char buff[MAX_FIELD_WIDTH];
String result(buff,sizeof(buff),from->charset());
diff --git a/sql/filesort.cc b/sql/filesort.cc
index b1dfb4d5e71..b6a5d844eac 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -1120,7 +1120,8 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
int flag)
{
int error;
- uint rec_length,sort_length,res_length,offset;
+ uint rec_length,res_length,offset;
+ size_t sort_length;
ulong maxcount;
ha_rows max_rows,org_max_rows;
my_off_t to_start_filepos;
@@ -1128,6 +1129,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
BUFFPEK *buffpek;
QUEUE queue;
qsort2_cmp cmp;
+ void *first_cmp_arg;
volatile THD::killed_state *killed= &current_thd->killed;
THD::killed_state not_killable;
DBUG_ENTER("merge_buffers");
@@ -1152,9 +1154,18 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
/* The following will fire if there is not enough space in sort_buffer */
DBUG_ASSERT(maxcount!=0);
+ if (param->unique_buff)
+ {
+ cmp= param->compare;
+ first_cmp_arg= (void *) &param->cmp_context;
+ }
+ else
+ {
+ cmp= get_ptr_compare(sort_length);
+ first_cmp_arg= (void*) &sort_length;
+ }
if (init_queue(&queue, (uint) (Tb-Fb)+1, offsetof(BUFFPEK,key), 0,
- (queue_compare) (cmp= get_ptr_compare(sort_length)),
- (void*) &sort_length))
+ (queue_compare) cmp, first_cmp_arg))
DBUG_RETURN(1); /* purecov: inspected */
for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
{
@@ -1207,7 +1218,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
buffpek= (BUFFPEK*) queue_top(&queue);
if (cmp) // Remove duplicates
{
- if (!(*cmp)(&sort_length, &(param->unique_buff),
+ if (!(*cmp)(first_cmp_arg, &(param->unique_buff),
(uchar**) &buffpek->key))
goto skip_duplicate;
memcpy(param->unique_buff, (uchar*) buffpek->key, rec_length);
@@ -1259,7 +1270,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
*/
if (cmp)
{
- if (!(*cmp)(&sort_length, &(param->unique_buff), (uchar**) &buffpek->key))
+ if (!(*cmp)(first_cmp_arg, &(param->unique_buff), (uchar**) &buffpek->key))
{
buffpek->key+= rec_length; // Remove duplicate
--buffpek->mem_count;
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index c78cb4e65b4..0b8ffd4a2fb 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -7033,9 +7033,6 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
}
}
- // Lock mutex before deleting and creating frm files
- pthread_mutex_lock(&LOCK_open);
-
if (!global_read_lock)
{
// Delete old files
@@ -7049,15 +7046,17 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
table_list.db= (char*) db;
table_list.alias= table_list.table_name= (char*)file_name;
(void)mysql_rm_table_part2(thd, &table_list,
- /* if_exists */ FALSE,
- /* drop_temporary */ FALSE,
- /* drop_view */ FALSE,
- /* dont_log_query*/ TRUE);
+ FALSE, /* if_exists */
+ FALSE, /* drop_temporary */
+ FALSE, /* drop_view */
+ TRUE /* dont_log_query*/);
+
/* Clear error message that is returned when table is deleted */
thd->clear_error();
}
}
+ pthread_mutex_lock(&LOCK_open);
// Create new files
List_iterator_fast<char> it2(create_list);
while ((file_name=it2++))
@@ -7068,7 +7067,7 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
}
pthread_mutex_unlock(&LOCK_open);
-
+
hash_free(&ok_tables);
hash_free(&ndb_tables);
@@ -10141,6 +10140,10 @@ static int ndbcluster_fill_files_table(handlerton *hton,
{
if (ndberr.classification == NdbError::SchemaError)
continue;
+
+ if (ndberr.classification == NdbError::UnknownResultError)
+ continue;
+
ERR_RETURN(ndberr);
}
NdbDictionary::Tablespace ts= dict->getTablespace(df.getTablespace());
@@ -10220,6 +10223,8 @@ static int ndbcluster_fill_files_table(handlerton *hton,
{
if (ndberr.classification == NdbError::SchemaError)
continue;
+ if (ndberr.classification == NdbError::UnknownResultError)
+ continue;
ERR_RETURN(ndberr);
}
NdbDictionary::LogfileGroup lfg=
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index d874525c4ad..e4924e8e8f2 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -2961,8 +2961,34 @@ int ha_partition::rnd_init(bool scan)
uint32 part_id;
DBUG_ENTER("ha_partition::rnd_init");
- include_partition_fields_in_used_fields();
-
+ /*
+ For operations that may need to change data, we may need to extend
+ read_set.
+ */
+ if (m_lock_type == F_WRLCK)
+ {
+ /*
+ If write_set contains any of the fields used in partition and
+ subpartition expression, we need to set all bits in read_set because
+ the row may need to be inserted in a different [sub]partition. In
+ other words update_row() can be converted into write_row(), which
+ requires a complete record.
+ */
+ if (bitmap_is_overlapping(&m_part_info->full_part_field_set,
+ table->write_set))
+ bitmap_set_all(table->read_set);
+ else
+ {
+ /*
+ Some handlers only read fields as specified by the bitmap for the
+ read set. For partitioned handlers we always require that the
+ fields of the partition functions are read such that we can
+ calculate the partition id to place updated and deleted records.
+ */
+ bitmap_union(table->read_set, &m_part_info->full_part_field_set);
+ }
+ }
+
/* Now we see what the index of our first important partition is */
DBUG_PRINT("info", ("m_part_info->used_partitions: 0x%lx",
(long) m_part_info->used_partitions.bitmap));
@@ -3118,7 +3144,7 @@ int ha_partition::rnd_next(uchar *buf)
continue; // Probably MyISAM
if (result != HA_ERR_END_OF_FILE)
- break; // Return error
+ goto end_dont_reset_start_part; // Return error
/* End current partition */
late_extra_no_cache(part_id);
@@ -3144,6 +3170,7 @@ int ha_partition::rnd_next(uchar *buf)
end:
m_part_spec.start_part= NO_CURRENT_PART_ID;
+end_dont_reset_start_part:
table->status= STATUS_NOT_FOUND;
DBUG_RETURN(result);
}
@@ -3275,7 +3302,15 @@ int ha_partition::index_init(uint inx, bool sorted)
m_start_key.length= 0;
m_ordered= sorted;
m_curr_key_info= table->key_info+inx;
- include_partition_fields_in_used_fields();
+ /*
+ Some handlers only read fields as specified by the bitmap for the
+ read set. For partitioned handlers we always require that the
+ fields of the partition functions are read such that we can
+ calculate the partition id to place updated and deleted records.
+ But this is required for operations that may need to change data only.
+ */
+ if (m_lock_type == F_WRLCK)
+ bitmap_union(table->read_set, &m_part_info->full_part_field_set);
file= m_file;
do
{
@@ -4144,35 +4179,6 @@ int ha_partition::handle_ordered_prev(uchar *buf)
}
-/*
- Set fields in partition functions in read set for underlying handlers
-
- SYNOPSIS
- include_partition_fields_in_used_fields()
-
- RETURN VALUE
- NONE
-
- DESCRIPTION
- Some handlers only read fields as specified by the bitmap for the
- read set. For partitioned handlers we always require that the
- fields of the partition functions are read such that we can
- calculate the partition id to place updated and deleted records.
-*/
-
-void ha_partition::include_partition_fields_in_used_fields()
-{
- Field **ptr= m_part_field_array;
- DBUG_ENTER("ha_partition::include_partition_fields_in_used_fields");
-
- do
- {
- bitmap_set_bit(table->read_set, (*ptr)->field_index);
- } while (*(++ptr));
- DBUG_VOID_RETURN;
-}
-
-
/****************************************************************************
MODULE information calls
****************************************************************************/
@@ -4714,6 +4720,12 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info,
HA_EXTRA_KEY_CACHE:
HA_EXTRA_NO_KEY_CACHE:
This parameters are no longer used and could be removed.
+
+ 7) Parameters only used by federated tables for query processing
+ ----------------------------------------------------------------
+ HA_EXTRA_INSERT_WITH_UPDATE:
+ Inform handler that an "INSERT...ON DUPLICATE KEY UPDATE" will be
+ executed. This condition is unset by HA_EXTRA_NO_IGNORE_DUP_KEY.
*/
int ha_partition::extra(enum ha_extra_function operation)
@@ -4795,6 +4807,9 @@ int ha_partition::extra(enum ha_extra_function operation)
*/
break;
}
+ /* Category 7), used by federated handlers */
+ case HA_EXTRA_INSERT_WITH_UPDATE:
+ DBUG_RETURN(loop_extra(operation));
default:
{
/* Temporary crash to discover what is wrong */
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index a168007ea04..895f001fa6a 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -449,7 +449,6 @@ private:
int handle_ordered_next(uchar * buf, bool next_same);
int handle_ordered_prev(uchar * buf);
void return_top_record(uchar * buf);
- void include_partition_fields_in_used_fields();
public:
/*
-------------------------------------------------------------------------
diff --git a/sql/handler.h b/sql/handler.h
index 4095a2f4cb1..f45b28c55f5 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -328,13 +328,21 @@ typedef ulonglong my_xid; // this line is the same as in log_event.h
#define MYSQL_XID_OFFSET (MYSQL_XID_PREFIX_LEN+sizeof(server_id))
#define MYSQL_XID_GTRID_LEN (MYSQL_XID_OFFSET+sizeof(my_xid))
-#define XIDDATASIZE 128
+#define XIDDATASIZE MYSQL_XIDDATASIZE
#define MAXGTRIDSIZE 64
#define MAXBQUALSIZE 64
#define COMPATIBLE_DATA_YES 0
#define COMPATIBLE_DATA_NO 1
+/**
+ struct xid_t is binary compatible with the XID structure as
+ in the X/Open CAE Specification, Distributed Transaction Processing:
+ The XA Specification, X/Open Company Ltd., 1991.
+ http://www.opengroup.org/bookstore/catalog/c193.htm
+
+ @see MYSQL_XID in mysql/plugin.h
+*/
struct xid_t {
long formatID;
long gtrid_length;
@@ -655,7 +663,7 @@ struct handlerton
uint (*alter_table_flags)(uint flags);
int (*alter_tablespace)(handlerton *hton, THD *thd, st_alter_tablespace *ts_info);
int (*fill_files_table)(handlerton *hton, THD *thd,
- struct st_table_list *tables,
+ TABLE_LIST *tables,
class Item *cond);
uint32 flags; /* global handler flags */
/*
@@ -1631,16 +1639,49 @@ public:
/* Type of table for caching query */
virtual uint8 table_cache_type() { return HA_CACHE_TBL_NONTRANSACT; }
- /* ask handler about permission to cache table when query is to be cached */
+
+
+ /**
+ @brief Register a named table with a call back function to the query cache.
+
+ @param thd The thread handle
+ @param table_key A pointer to the table name in the table cache
+ @param key_length The length of the table name
+ @param[out] engine_callback The pointer to the storage engine call back
+ function
+ @param[out] engine_data Storage engine specific data which could be
+ anything
+
+ This method offers the storage engine, the possibility to store a reference
+ to a table name which is going to be used with query cache.
+ The method is called each time a statement is written to the cache and can
+ be used to verify if a specific statement is cachable. It also offers
+ the possibility to register a generic (but static) call back function which
+ is called each time a statement is matched against the query cache.
+
+ @note If engine_data supplied with this function is different from
+ engine_data supplied with the callback function, and the callback returns
+ FALSE, a table invalidation on the current table will occur.
+
+ @return Upon success the engine_callback will point to the storage engine
+ call back function, if any, and engine_data will point to any storage
+ engine data used in the specific implementation.
+ @retval TRUE Success
+ @retval FALSE The specified table or current statement should not be
+ cached
+ */
+
virtual my_bool register_query_cache_table(THD *thd, char *table_key,
- uint key_length,
- qc_engine_callback
- *engine_callback,
- ulonglong *engine_data)
+ uint key_length,
+ qc_engine_callback
+ *engine_callback,
+ ulonglong *engine_data)
{
*engine_callback= 0;
- return 1;
+ return TRUE;
}
+
+
/*
RETURN
true Primary key (if there is one) is clustered key covering all fields
diff --git a/sql/item.cc b/sql/item.cc
index d7743c491eb..711a21ecbec 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -5899,7 +5899,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items)
if (!arg->fixed)
{
bool res;
- st_table_list *orig_next_table= context->last_name_resolution_table;
+ TABLE_LIST *orig_next_table= context->last_name_resolution_table;
context->last_name_resolution_table= context->first_name_resolution_table;
res= arg->fix_fields(thd, &arg);
context->last_name_resolution_table= orig_next_table;
diff --git a/sql/item.h b/sql/item.h
index 6df85476f03..432da6c3a1c 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -19,7 +19,7 @@
#endif
class Protocol;
-struct st_table_list;
+struct TABLE_LIST;
void item_init(void); /* Init item functions */
class Item_field;
@@ -843,8 +843,7 @@ public:
german character for double s is equal to 2 s.
The default is that an item is not allowed
- in a partition function. However all mathematical functions, string
- manipulation functions, date functions are allowed. Allowed functions
+ in a partition function. Allowed functions
can never depend on server version, they cannot depend on anything
related to the environment. They can also only depend on a set of
fields in the table itself. They cannot depend on other tables and
@@ -1633,6 +1632,7 @@ public:
uint decimal_precision() const
{ return (uint)(max_length - test(value < 0)); }
bool eq(const Item *, bool binary_cmp) const;
+ bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
};
@@ -1650,6 +1650,7 @@ public:
void print(String *str);
Item_num *neg ();
uint decimal_precision() const { return max_length; }
+ bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
};
@@ -1692,6 +1693,7 @@ public:
uint decimal_precision() const { return decimal_value.precision(); }
bool eq(const Item *, bool binary_cmp) const;
void set_decimal_value(my_decimal *value_par);
+ bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
};
@@ -1752,7 +1754,6 @@ public:
{}
void print(String *str) { str->append(func_name); }
Item *safe_charset_converter(CHARSET_INFO *tocs);
- bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
};
@@ -1861,7 +1862,6 @@ public:
CHARSET_INFO *cs= NULL):
Item_string(name, length, cs)
{}
- bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
};
@@ -1915,7 +1915,6 @@ public:
unsigned_flag=1;
}
enum_field_types field_type() const { return int_field_type; }
- bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
};
@@ -2116,6 +2115,12 @@ public:
bool fix_fields(THD *, Item **);
bool eq(const Item *item, bool binary_cmp) const;
+ Item *get_tmp_table_item(THD *thd)
+ {
+ Item *item= Item_ref::get_tmp_table_item(thd);
+ item->name= name;
+ return item;
+ }
virtual Ref_Type ref_type() { return VIEW_REF; }
};
@@ -2237,7 +2242,6 @@ public:
}
Item *clone_item();
virtual Item *real_item() { return ref; }
- bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
};
#ifdef MYSQL_SERVER
@@ -2438,14 +2442,6 @@ enum trg_action_time_type
TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1, TRG_ACTION_MAX
};
-/*
- Event on which trigger is invoked.
-*/
-enum trg_event_type
-{
- TRG_EVENT_INSERT= 0 , TRG_EVENT_UPDATE= 1, TRG_EVENT_DELETE= 2, TRG_EVENT_MAX
-};
-
class Table_triggers_list;
/*
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 3477ce84b21..755e711e383 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -719,6 +719,67 @@ Arg_comparator::can_compare_as_dates(Item *a, Item *b, ulonglong *const_value)
}
+/*
+ Retrieves correct TIME value from the given item.
+
+ SYNOPSIS
+ get_time_value()
+ thd thread handle
+ item_arg [in/out] item to retrieve TIME value from
+ cache_arg [in/out] pointer to place to store the cache item to
+ warn_item [in] unused
+ is_null [out] TRUE <=> the item_arg is null
+
+ DESCRIPTION
+ Retrieves the correct TIME value from given item for comparison by the
+ compare_datetime() function.
+ If item's result can be compared as longlong then its int value is used
+ and a value returned by get_time function is used otherwise.
+ If an item is a constant one then its value is cached and it isn't
+ get parsed again. An Item_cache_int object is used for for cached values.
+ It seamlessly substitutes the original item. The cache item is marked as
+ non-constant to prevent re-caching it again.
+
+ RETURN
+ obtained value
+*/
+
+ulonglong
+get_time_value(THD *thd, Item ***item_arg, Item **cache_arg,
+ Item *warn_item, bool *is_null)
+{
+ ulonglong value;
+ Item *item= **item_arg;
+ MYSQL_TIME ltime;
+
+ if (item->result_as_longlong())
+ {
+ value= item->val_int();
+ *is_null= item->null_value;
+ }
+ else
+ {
+ *is_null= item->get_time(&ltime);
+ value= !*is_null ? TIME_to_ulonglong_datetime(&ltime) : 0;
+ }
+ /*
+ Do not cache GET_USER_VAR() function as its const_item() may return TRUE
+ for the current thread but it still may change during the execution.
+ */
+ if (item->const_item() && cache_arg && (item->type() != Item::FUNC_ITEM ||
+ ((Item_func*)item)->functype() != Item_func::GUSERVAR_FUNC))
+ {
+ Item_cache_int *cache= new Item_cache_int();
+ /* Mark the cache as non-const to prevent re-caching. */
+ cache->set_used_tables(1);
+ cache->store(item, value);
+ *cache_arg= cache;
+ *item_arg= cache_arg;
+ }
+ return value;
+}
+
+
int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg,
Item **a1, Item **a2,
Item_result type)
@@ -757,8 +818,23 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg,
}
is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC);
func= &Arg_comparator::compare_datetime;
+ get_value_func= &get_datetime_value;
return 0;
}
+ else if (type == STRING_RESULT && (*a)->field_type() == MYSQL_TYPE_TIME &&
+ (*b)->field_type() == MYSQL_TYPE_TIME)
+ {
+ /* Compare TIME values as integers. */
+ thd= current_thd;
+ owner= owner_arg;
+ a_cache= 0;
+ b_cache= 0;
+ is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC);
+ func= &Arg_comparator::compare_datetime;
+ get_value_func= &get_time_value;
+ return 0;
+ }
+
return set_compare_func(owner_arg, type);
}
@@ -776,8 +852,10 @@ void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1)
b_cache= 0;
is_nulls_eq= FALSE;
func= &Arg_comparator::compare_datetime;
+ get_value_func= &get_datetime_value;
}
+
/*
Retrieves correct DATETIME value from given item.
@@ -891,8 +969,8 @@ int Arg_comparator::compare_datetime()
bool is_null= FALSE;
ulonglong a_value, b_value;
- /* Get DATE/DATETIME value of the 'a' item. */
- a_value= get_datetime_value(thd, &a, &a_cache, *b, &is_null);
+ /* Get DATE/DATETIME/TIME value of the 'a' item. */
+ a_value= (*get_value_func)(thd, &a, &a_cache, *b, &is_null);
if (!is_nulls_eq && is_null)
{
if (owner)
@@ -900,8 +978,8 @@ int Arg_comparator::compare_datetime()
return -1;
}
- /* Get DATE/DATETIME value of the 'b' item. */
- b_value= get_datetime_value(thd, &b, &b_cache, *a, &is_null);
+ /* Get DATE/DATETIME/TIME value of the 'b' item. */
+ b_value= (*get_value_func)(thd, &b, &b_cache, *a, &is_null);
if (is_null)
{
if (owner)
@@ -1771,6 +1849,7 @@ void Item_func_between::fix_length_and_dec()
max_length= 1;
int i;
bool datetime_found= FALSE;
+ int time_items_found= 0;
compare_as_dates= TRUE;
THD *thd= current_thd;
@@ -1791,17 +1870,19 @@ void Item_func_between::fix_length_and_dec()
At least one of items should be a DATE/DATETIME item and other items
should return the STRING result.
*/
- for (i= 0; i < 3; i++)
+ if (cmp_type == STRING_RESULT)
{
- if (args[i]->is_datetime())
+ for (i= 0; i < 3; i++)
{
- datetime_found= TRUE;
- continue;
+ if (args[i]->is_datetime())
+ {
+ datetime_found= TRUE;
+ continue;
+ }
+ if (args[i]->field_type() == MYSQL_TYPE_TIME &&
+ args[i]->result_as_longlong())
+ time_items_found++;
}
- if (args[i]->result_type() == STRING_RESULT)
- continue;
- compare_as_dates= FALSE;
- break;
}
if (!datetime_found)
compare_as_dates= FALSE;
@@ -1811,6 +1892,11 @@ void Item_func_between::fix_length_and_dec()
ge_cmp.set_datetime_cmp_func(args, args + 1);
le_cmp.set_datetime_cmp_func(args, args + 2);
}
+ else if (time_items_found == 3)
+ {
+ /* Compare TIME items as integers. */
+ cmp_type= INT_RESULT;
+ }
else if (args[0]->real_item()->type() == FIELD_ITEM &&
thd->lex->sql_command != SQLCOM_CREATE_VIEW &&
thd->lex->sql_command != SQLCOM_SHOW_CREATE)
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 1bc52ea093c..fcbacc32d88 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -42,6 +42,8 @@ class Arg_comparator: public Sql_alloc
bool is_nulls_eq; // TRUE <=> compare for the EQUAL_FUNC
enum enum_date_cmp_type { CMP_DATE_DFLT= 0, CMP_DATE_WITH_DATE,
CMP_DATE_WITH_STR, CMP_STR_WITH_DATE };
+ ulonglong (*get_value_func)(THD *thd, Item ***item_arg, Item **cache_arg,
+ Item *warn_item, bool *is_null);
public:
DTCollation cmp_collation;
@@ -355,7 +357,6 @@ public:
}
Item *neg_transformer(THD *thd);
virtual Item *negated_item();
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool subst_argument_checker(uchar **arg) { return TRUE; }
};
@@ -367,7 +368,6 @@ public:
enum Functype functype() const { return NOT_FUNC; }
const char *func_name() const { return "not"; }
Item *neg_transformer(THD *thd);
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
void print(String *str);
};
@@ -598,7 +598,6 @@ public:
bool is_bool_func() { return 1; }
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
uint decimal_precision() const { return 1; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -610,7 +609,6 @@ public:
optimize_type select_optimize() const { return OPTIMIZE_NONE; }
const char *func_name() const { return "strcmp"; }
void print(String *str) { Item_func::print(str); }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -673,7 +671,6 @@ public:
const char *func_name() const { return "ifnull"; }
Field *tmp_table_field(TABLE *table);
uint decimal_precision() const;
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -714,7 +711,6 @@ public:
void print(String *str) { Item_func::print(str); }
table_map not_null_tables() const { return 0; }
bool is_null();
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
/* Functions to handle the optimized IN */
@@ -1141,7 +1137,6 @@ public:
void print(String *str);
Item *find_item(String *str);
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
- bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
void cleanup();
};
@@ -1211,7 +1206,6 @@ public:
bool nulls_in_row();
bool is_bool_func() { return 1; }
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class cmp_item_row :public cmp_item
@@ -1283,7 +1277,6 @@ public:
optimize_type select_optimize() const { return OPTIMIZE_NULL; }
Item *neg_transformer(THD *thd);
CHARSET_INFO *compare_collation() { return args[0]->collation.collation; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
/* Functions used by HAVING for rewriting IN subquery */
@@ -1310,7 +1303,6 @@ public:
*/
table_map used_tables() const
{ return used_tables_cache | RAND_TABLE_BIT; }
- bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
};
@@ -1333,7 +1325,6 @@ public:
void print(String *str);
CHARSET_INFO *compare_collation() { return args[0]->collation.collation; }
void top_level_item() { abort_on_null=1; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -1372,7 +1363,6 @@ public:
const char *func_name() const { return "like"; }
bool fix_fields(THD *thd, Item **ref);
void cleanup();
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
#ifdef USE_REGEX
@@ -1395,7 +1385,6 @@ public:
const char *func_name() const { return "regexp"; }
void print(String *str) { print_op(str); }
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
#else
@@ -1452,7 +1441,6 @@ public:
Item *transform(Item_transformer transformer, uchar *arg);
void traverse_cond(Cond_traverser, void *arg, traverse_order order);
void neg_arguments(THD *thd);
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool subst_argument_checker(uchar **arg) { return TRUE; }
Item *compile(Item_analyzer analyzer, uchar **arg_p,
Item_transformer transformer, uchar *arg_t);
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 20041b1176a..fa15b992e5c 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -2326,7 +2326,7 @@ Item*
Create_qfunc::create(THD *thd, LEX_STRING name, List<Item> *item_list)
{
LEX_STRING db;
- if (thd->copy_db_to(&db.str, &db.length))
+ if (thd->lex->copy_db_to(&db.str, &db.length))
return NULL;
return create(thd, db, name, false, item_list);
@@ -5039,6 +5039,18 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type,
my_error(ER_M_BIGGER_THAN_D, MYF(0), "");
return 0;
}
+ if (len > DECIMAL_MAX_PRECISION)
+ {
+ my_error(ER_TOO_BIG_PRECISION, MYF(0), len, a->name,
+ DECIMAL_MAX_PRECISION);
+ return 0;
+ }
+ if (dec > DECIMAL_MAX_SCALE)
+ {
+ my_error(ER_TOO_BIG_SCALE, MYF(0), dec, a->name,
+ DECIMAL_MAX_SCALE);
+ return 0;
+ }
res= new (thd->mem_root) Item_decimal_typecast(a, len, dec);
break;
}
diff --git a/sql/item_func.h b/sql/item_func.h
index 8fc68f93e12..568effb2f63 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -253,7 +253,6 @@ public:
void fix_num_length_and_dec();
void find_num_type();
String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -311,7 +310,6 @@ public:
{ max_length=args[0]->max_length; unsigned_flag=0; }
void print(String *str);
uint decimal_precision() const { return args[0]->decimal_precision(); }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -345,7 +343,6 @@ public:
void fix_length_and_dec() {};
const char *func_name() const { return "decimal_typecast"; }
void print(String *);
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -442,6 +439,7 @@ public:
void fix_length_and_dec();
void fix_num_length_and_dec();
uint decimal_precision() const { return args[0]->decimal_precision(); }
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -454,6 +452,7 @@ public:
my_decimal *decimal_op(my_decimal *);
const char *func_name() const { return "abs"; }
void fix_length_and_dec();
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
// A class to handle logarithmic and trigonometric functions
@@ -488,7 +487,6 @@ public:
Item_func_exp(Item *a) :Item_dec_func(a) {}
double val_real();
const char *func_name() const { return "exp"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -498,7 +496,6 @@ public:
Item_func_ln(Item *a) :Item_dec_func(a) {}
double val_real();
const char *func_name() const { return "ln"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -509,7 +506,6 @@ public:
Item_func_log(Item *a,Item *b) :Item_dec_func(a,b) {}
double val_real();
const char *func_name() const { return "log"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -519,7 +515,6 @@ public:
Item_func_log2(Item *a) :Item_dec_func(a) {}
double val_real();
const char *func_name() const { return "log2"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -529,7 +524,6 @@ public:
Item_func_log10(Item *a) :Item_dec_func(a) {}
double val_real();
const char *func_name() const { return "log10"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -539,7 +533,6 @@ public:
Item_func_sqrt(Item *a) :Item_dec_func(a) {}
double val_real();
const char *func_name() const { return "sqrt"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -549,7 +542,6 @@ public:
Item_func_pow(Item *a,Item *b) :Item_dec_func(a,b) {}
double val_real();
const char *func_name() const { return "pow"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -559,7 +551,6 @@ public:
Item_func_acos(Item *a) :Item_dec_func(a) {}
double val_real();
const char *func_name() const { return "acos"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_asin :public Item_dec_func
@@ -568,7 +559,6 @@ public:
Item_func_asin(Item *a) :Item_dec_func(a) {}
double val_real();
const char *func_name() const { return "asin"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_atan :public Item_dec_func
@@ -578,7 +568,6 @@ public:
Item_func_atan(Item *a,Item *b) :Item_dec_func(a,b) {}
double val_real();
const char *func_name() const { return "atan"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_cos :public Item_dec_func
@@ -587,7 +576,6 @@ public:
Item_func_cos(Item *a) :Item_dec_func(a) {}
double val_real();
const char *func_name() const { return "cos"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_sin :public Item_dec_func
@@ -596,7 +584,6 @@ public:
Item_func_sin(Item *a) :Item_dec_func(a) {}
double val_real();
const char *func_name() const { return "sin"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_tan :public Item_dec_func
@@ -605,7 +592,6 @@ public:
Item_func_tan(Item *a) :Item_dec_func(a) {}
double val_real();
const char *func_name() const { return "tan"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_integer :public Item_int_func
@@ -633,6 +619,7 @@ public:
longlong int_op();
double real_op();
my_decimal *decimal_op(my_decimal *);
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -644,6 +631,7 @@ public:
longlong int_op();
double real_op();
my_decimal *decimal_op(my_decimal *);
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
/* This handles round and truncate */
@@ -684,7 +672,6 @@ public:
Item_func_sign(Item *a) :Item_int_func(a) {}
const char *func_name() const { return "sign"; }
longlong val_int();
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -699,7 +686,6 @@ public:
const char *func_name() const { return name; }
void fix_length_and_dec()
{ decimals= NOT_FIXED_DEC; max_length= float_length(decimals); }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -725,7 +711,6 @@ public:
void fix_length_and_dec();
enum Item_result result_type () const { return cmp_type; }
bool result_as_longlong() { return compare_as_dates; };
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
uint cmp_datetimes(ulonglong *value);
};
@@ -781,7 +766,6 @@ public:
longlong val_int();
const char *func_name() const { return "length"; }
void fix_length_and_dec() { max_length=10; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_bit_length :public Item_func_length
@@ -801,7 +785,6 @@ public:
longlong val_int();
const char *func_name() const { return "char_length"; }
void fix_length_and_dec() { max_length=10; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_coercibility :public Item_int_func
@@ -825,7 +808,6 @@ public:
longlong val_int();
void fix_length_and_dec();
void print(String *str);
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -850,7 +832,6 @@ public:
longlong val_int();
const char *func_name() const { return "ascii"; }
void fix_length_and_dec() { max_length=3; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_ord :public Item_int_func
@@ -860,7 +841,6 @@ public:
Item_func_ord(Item *a) :Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "ord"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_find_in_set :public Item_int_func
@@ -874,7 +854,6 @@ public:
longlong val_int();
const char *func_name() const { return "find_in_set"; }
void fix_length_and_dec();
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
/* Base class for all bit functions: '~', '|', '^', '&', '>>', '<<' */
@@ -886,7 +865,6 @@ public:
Item_func_bit(Item *a) :Item_int_func(a) {}
void fix_length_and_dec() { unsigned_flag= 1; }
void print(String *str) { print_op(str); }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_bit_or :public Item_func_bit
@@ -912,7 +890,6 @@ public:
longlong val_int();
const char *func_name() const { return "bit_count"; }
void fix_length_and_dec() { max_length=2; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_shift_left :public Item_func_bit
@@ -1363,7 +1340,6 @@ public:
longlong val_int();
const char *func_name() const { return "inet_aton"; }
void fix_length_and_dec() { decimals= 0; max_length= 21; maybe_null= 1; unsigned_flag= 1;}
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index a376504512f..d54b9961562 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -25,7 +25,7 @@
#include "mysql_priv.h"
#include <m_ctype.h>
-#include "md5.h"
+#include "my_md5.h"
#include "sha1.h"
#include "my_aes.h"
#include <zlib.h>
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index c036ec490db..6d2d9c199c9 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -50,7 +50,6 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "md5"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -94,7 +93,6 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "concat"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_concat_ws :public Item_str_func
@@ -116,7 +114,6 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "reverse"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -154,7 +151,6 @@ protected:
public:
Item_str_conv(Item *item) :Item_str_func(item) {}
String *val_str(String *);
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -455,7 +451,6 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "soundex"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -549,7 +544,6 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "rpad"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -562,7 +556,6 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "lpad"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -577,7 +570,6 @@ public:
collation.set(default_charset());
max_length= 64;
}
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -594,7 +586,6 @@ public:
decimals=0;
max_length=args[0]->max_length*2*collation.collation->mbmaxlen;
}
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_unhex :public Item_str_func
@@ -614,7 +605,6 @@ public:
decimals=0;
max_length=(1+args[0]->max_length)/2;
}
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -638,7 +628,6 @@ public:
}
void print(String *str);
const char *func_name() const { return "cast_as_binary"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -678,7 +667,6 @@ public:
String* val_str(String* str);
const char *func_name() const { return "inet_ntoa"; }
void fix_length_and_dec() { decimals = 0; max_length=3*8+7; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_quote :public Item_str_func
@@ -693,7 +681,6 @@ public:
collation.set(args[0]->collation);
max_length= args[0]->max_length * 2 + 2;
}
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_conv_charset :public Item_str_func
@@ -794,7 +781,6 @@ public:
const char *func_name() const { return "crc32"; }
void fix_length_and_dec() { max_length=10; }
longlong val_int();
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_uncompressed_length : public Item_int_func
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 0fa46d231a9..8fe843a2a2d 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -3089,6 +3089,7 @@ Item_func_group_concat::Item_func_group_concat(THD *thd,
original(item)
{
quick_group= item->quick_group;
+ result.set_charset(collation.collation);
}
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 95ff7dfd336..a87efa9e68c 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -2534,11 +2534,8 @@ longlong Item_date_typecast::val_int()
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
- if (args[0]->get_date(&ltime, TIME_FUZZY_DATE))
- {
- null_value= 1;
+ if ((null_value= args[0]->get_date(&ltime, TIME_FUZZY_DATE)))
return 0;
- }
return (longlong) (ltime.year * 10000L + ltime.month * 100 + ltime.day);
}
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index bd0954e6bdb..32f5bcf8e52 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -38,7 +38,6 @@ public:
{
max_length=6*MY_CHARSET_BIN_MB_MAXLEN;
}
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -53,7 +52,6 @@ public:
decimals=0;
max_length=6*MY_CHARSET_BIN_MB_MAXLEN;
}
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -129,6 +127,7 @@ public:
max_length=10*my_charset_bin.mbmaxlen;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
};
@@ -224,7 +223,6 @@ public:
max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_yearweek :public Item_int_func
@@ -303,6 +301,7 @@ class Item_func_dayname :public Item_func_weekday
max_length=9*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
};
@@ -319,7 +318,6 @@ public:
decimals=0;
max_length=10*MY_CHARSET_BIN_MB_MAXLEN;
}
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -603,7 +601,6 @@ public:
void fix_length_and_dec();
uint format_length(const String *format);
bool eq(const Item *item, bool binary_cmp) const;
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -617,7 +614,6 @@ class Item_func_from_unixtime :public Item_date_func
const char *func_name() const { return "from_unixtime"; }
void fix_length_and_dec();
bool get_date(MYSQL_TIME *res, uint fuzzy_date);
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -676,7 +672,6 @@ public:
}
const char *func_name() const { return "sec_to_time"; }
bool result_as_longlong() { return TRUE; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -698,7 +693,6 @@ public:
bool get_date(MYSQL_TIME *res, uint fuzzy_date);
bool eq(const Item *item, bool binary_cmp) const;
void print(String *str);
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -753,7 +747,6 @@ public:
max_length=args[0]->max_length;
maybe_null= 1;
}
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -773,7 +766,6 @@ public:
String *val_str(String *a);
void fix_length_and_dec();
void print(String *str);
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -884,7 +876,6 @@ public:
max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
}
longlong val_int();
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -907,7 +898,6 @@ public:
}
void print(String *str);
const char *func_name() const { return "add_time"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
double val_real() { return val_real_from_decimal(); }
my_decimal *val_decimal(my_decimal *decimal_value)
{
@@ -949,7 +939,6 @@ public:
:Item_str_timefunc(a, b ,c) {}
String *val_str(String *str);
const char *func_name() const { return "maketime"; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_microsecond :public Item_int_func
@@ -981,7 +970,6 @@ public:
maybe_null=1;
}
void print(String *str);
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -1028,7 +1016,6 @@ public:
{
return tmp_table_field_from_field_type(table, 1);
}
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h
index 650893fa7bd..278c98baf7c 100644
--- a/sql/item_xmlfunc.h
+++ b/sql/item_xmlfunc.h
@@ -41,7 +41,6 @@ public:
Item_func_xml_extractvalue(Item *a,Item *b) :Item_xml_str_func(a,b) {}
const char *func_name() const { return "extractvalue"; }
String *val_str(String *);
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
diff --git a/sql/lock.cc b/sql/lock.cc
index 50922a682a2..4260a031def 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -1033,6 +1033,102 @@ end:
}
+/**
+ @brief Lock all tables in list with an exclusive table name lock.
+
+ @param thd Thread handle.
+ @param table_list Names of tables to lock.
+
+ @note This function needs to be protected by LOCK_open. If we're
+ under LOCK TABLES, this function does not work as advertised. Namely,
+ it does not exclude other threads from using this table and does not
+ put an exclusive name lock on this table into the table cache.
+
+ @see lock_table_names
+ @see unlock_table_names
+
+ @retval TRUE An error occured.
+ @retval FALSE Name lock successfully acquired.
+*/
+
+bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list)
+{
+ if (lock_table_names(thd, table_list))
+ return TRUE;
+
+ /*
+ Upgrade the table name locks from semi-exclusive to exclusive locks.
+ */
+ for (TABLE_LIST *table= table_list; table; table= table->next_global)
+ {
+ if (table->table)
+ table->table->open_placeholder= 1;
+ }
+ return FALSE;
+}
+
+
+/**
+ @brief Test is 'table' is protected by an exclusive name lock.
+
+ @param[in] thd The current thread handler
+ @param[in] table Table container containing the single table to be tested
+
+ @note Needs to be protected by LOCK_open mutex.
+
+ @return Error status code
+ @retval TRUE Table is protected
+ @retval FALSE Table is not protected
+*/
+
+bool
+is_table_name_exclusively_locked_by_this_thread(THD *thd,
+ TABLE_LIST *table_list)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+
+ key_length= create_table_def_key(thd, key, table_list, 0);
+
+ return is_table_name_exclusively_locked_by_this_thread(thd, (uchar *)key,
+ key_length);
+}
+
+
+/**
+ @brief Test is 'table key' is protected by an exclusive name lock.
+
+ @param[in] thd The current thread handler.
+ @param[in] table Table container containing the single table to be tested.
+
+ @note Needs to be protected by LOCK_open mutex
+
+ @retval TRUE Table is protected
+ @retval FALSE Table is not protected
+ */
+
+bool
+is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key,
+ int key_length)
+{
+ HASH_SEARCH_STATE state;
+ TABLE *table;
+
+ for (table= (TABLE*) hash_first(&open_cache, key,
+ key_length, &state);
+ table ;
+ table= (TABLE*) hash_next(&open_cache, key,
+ key_length, &state))
+ {
+ if (table->in_use == thd &&
+ table->open_placeholder == 1 &&
+ table->s->version == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/*
Unlock all tables in list with a name lock
diff --git a/sql/log.cc b/sql/log.cc
index 6dc204265b0..0bf77d68410 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1445,7 +1445,7 @@ static int binlog_close_connection(handlerton *hton, THD *thd)
{
binlog_trx_data *const trx_data=
(binlog_trx_data*) thd->ha_data[binlog_hton->slot];
- DBUG_ASSERT(mysql_bin_log.is_open() && trx_data->empty());
+ DBUG_ASSERT(trx_data->empty());
thd->ha_data[binlog_hton->slot]= 0;
trx_data->~binlog_trx_data();
my_free((uchar*)trx_data, MYF(0));
@@ -1570,7 +1570,6 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
DBUG_ENTER("binlog_commit");
binlog_trx_data *const trx_data=
(binlog_trx_data*) thd->ha_data[binlog_hton->slot];
- DBUG_ASSERT(mysql_bin_log.is_open());
if (trx_data->empty())
{
@@ -1598,7 +1597,6 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
int error=0;
binlog_trx_data *const trx_data=
(binlog_trx_data*) thd->ha_data[binlog_hton->slot];
- DBUG_ASSERT(mysql_bin_log.is_open());
if (trx_data->empty()) {
trx_data->reset();
@@ -1659,7 +1657,6 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
{
DBUG_ENTER("binlog_savepoint_rollback");
- DBUG_ASSERT(mysql_bin_log.is_open());
/*
Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some
@@ -3945,13 +3942,120 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
return ER_ERROR_ON_WRITE;
- uint bytes= my_b_bytes_in_cache(cache);
+ uint length= my_b_bytes_in_cache(cache), group, carry, hdr_offs;
+ long val;
+ uchar header[LOG_EVENT_HEADER_LEN];
+
+ /*
+ The events in the buffer have incorrect end_log_pos data
+ (relative to beginning of group rather than absolute),
+ so we'll recalculate them in situ so the binlog is always
+ correct, even in the middle of a group. This is possible
+ because we now know the start position of the group (the
+ offset of this cache in the log, if you will); all we need
+ to do is to find all event-headers, and add the position of
+ the group to the end_log_pos of each event. This is pretty
+ straight forward, except that we read the cache in segments,
+ so an event-header might end up on the cache-border and get
+ split.
+ */
+
+ group= (uint)my_b_tell(&log_file);
+ hdr_offs= carry= 0;
+
do
{
- if (my_b_write(&log_file, cache->read_pos, bytes))
+
+ /*
+ if we only got a partial header in the last iteration,
+ get the other half now and process a full header.
+ */
+ if (unlikely(carry > 0))
+ {
+ DBUG_ASSERT(carry < LOG_EVENT_HEADER_LEN);
+
+ /* assemble both halves */
+ memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN - carry);
+
+ /* fix end_log_pos */
+ val= uint4korr(&header[LOG_POS_OFFSET]) + group;
+ int4store(&header[LOG_POS_OFFSET], val);
+
+ /* write the first half of the split header */
+ if (my_b_write(&log_file, header, carry))
+ return ER_ERROR_ON_WRITE;
+
+ /*
+ copy fixed second half of header to cache so the correct
+ version will be written later.
+ */
+ memcpy((char *)cache->read_pos, &header[carry], LOG_EVENT_HEADER_LEN - carry);
+
+ /* next event header at ... */
+ hdr_offs = uint4korr(&header[EVENT_LEN_OFFSET]) - carry;
+
+ carry= 0;
+ }
+
+ /* if there is anything to write, process it. */
+
+ if (likely(length > 0))
+ {
+ /*
+ process all event-headers in this (partial) cache.
+ if next header is beyond current read-buffer,
+ we'll get it later (though not necessarily in the
+ very next iteration, just "eventually").
+ */
+
+ while (hdr_offs < length)
+ {
+ /*
+ partial header only? save what we can get, process once
+ we get the rest.
+ */
+
+ if (hdr_offs + LOG_EVENT_HEADER_LEN > length)
+ {
+ carry= length - hdr_offs;
+ memcpy(header, (char *)cache->read_pos + hdr_offs, carry);
+ length= hdr_offs;
+ }
+ else
+ {
+ /* we've got a full event-header, and it came in one piece */
+
+ uchar *log_pos= (uchar *)cache->read_pos + hdr_offs + LOG_POS_OFFSET;
+
+ /* fix end_log_pos */
+ val= uint4korr(log_pos) + group;
+ int4store(log_pos, val);
+
+ /* next event header at ... */
+ log_pos= (uchar *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET;
+ hdr_offs += uint4korr(log_pos);
+
+ }
+ }
+
+ /*
+ Adjust hdr_offs. Note that it may still point beyond the segment
+ read in the next iteration; if the current event is very long,
+ it may take a couple of read-iterations (and subsequent adjustments
+ of hdr_offs) for it to point into the then-current segment.
+ If we have a split header (!carry), hdr_offs will be set at the
+ beginning of the next iteration, overwriting the value we set here:
+ */
+ hdr_offs -= length;
+ }
+
+ /* Write data to the binary log file */
+ if (my_b_write(&log_file, cache->read_pos, length))
return ER_ERROR_ON_WRITE;
- cache->read_pos= cache->read_end;
- } while ((bytes= my_b_fill(cache)));
+ cache->read_pos=cache->read_end; // Mark buffer used up
+ } while ((length= my_b_fill(cache)));
+
+ DBUG_ASSERT(carry == 0);
if (sync_log)
flush_and_sync();
@@ -4028,7 +4132,7 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
if ((write_error= write_cache(cache, false, false)))
goto err;
-
+
if (commit_event && commit_event->write(&log_file))
goto err;
if (flush_and_sync())
@@ -5140,6 +5244,29 @@ err1:
return 1;
}
+
+#ifdef INNODB_COMPATIBILITY_HOOKS
+/**
+ Get the file name of the MySQL binlog.
+ @return the name of the binlog file
+*/
+extern "C"
+const char* mysql_bin_log_file_name(void)
+{
+ return mysql_bin_log.get_log_fname();
+}
+/**
+ Get the current position of the MySQL binlog.
+ @return byte offset from the beginning of the binlog
+*/
+extern "C"
+ulonglong mysql_bin_log_file_pos(void)
+{
+ return (ulonglong) mysql_bin_log.get_log_file()->pos_in_file;
+}
+#endif /* INNODB_COMPATIBILITY_HOOKS */
+
+
struct st_mysql_storage_engine binlog_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 89a126a74c5..dc492d08c10 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -6109,8 +6109,9 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
default:
rli->report(ERROR_LEVEL, thd->net.last_errno,
- "Error in %s event: row application failed",
- get_type_str());
+ "Error in %s event: row application failed. %s",
+ get_type_str(),
+ thd->net.last_error ? thd->net.last_error : "");
thd->query_error= 1;
break;
}
@@ -6131,9 +6132,10 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
{ /* error has occured during the transaction */
rli->report(ERROR_LEVEL, thd->net.last_errno,
"Error in %s event: error during transaction execution "
- "on table %s.%s",
+ "on table %s.%s. %s",
get_type_str(), table->s->db.str,
- table->s->table_name.str);
+ table->s->table_name.str,
+ thd->net.last_error ? thd->net.last_error : "");
/*
If one day we honour --skip-slave-errors in row-based replication, and
@@ -7097,7 +7099,12 @@ replace_record(THD *thd, TABLE *table,
}
if ((keynum= table->file->get_dup_key(error)) < 0)
{
- /* We failed to retrieve the duplicate key */
+ table->file->print_error(error, MYF(0));
+ /*
+ We failed to retrieve the duplicate key
+ - either because the error was not "duplicate key" error
+ - or because the information which key is not available
+ */
DBUG_RETURN(error);
}
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index ae7c4ca5f3f..7b7bc81957e 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -98,7 +98,7 @@ char* query_table_status(THD *thd,const char *db,const char *table_name);
DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) > 0); \
if (((uchar*)Thd) != NULL) \
push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \
- ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \
+ ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX_WITH_VER), \
(Old), (Ver), (New)); \
else \
sql_print_warning("The syntax '%s' is deprecated and will be removed " \
@@ -531,9 +531,9 @@ void debug_sync_point(const char* lock_name, uint lock_timeout);
#define SHOW_LOG_STATUS_FREE "FREE"
#define SHOW_LOG_STATUS_INUSE "IN USE"
-struct st_table_list;
+struct TABLE_LIST;
class String;
-void view_store_options(THD *thd, st_table_list *table, String *buff);
+void view_store_options(THD *thd, TABLE_LIST *table, String *buff);
/* Options to add_table_to_list() */
#define TL_OPTION_UPDATING 1
@@ -574,6 +574,13 @@ enum enum_parsing_place
struct st_table;
class THD;
+enum enum_check_fields
+{
+ CHECK_FIELD_IGNORE,
+ CHECK_FIELD_WARN,
+ CHECK_FIELD_ERROR_FOR_NULL
+};
+
/* Struct to handle simple linked lists */
typedef struct st_sql_list {
@@ -712,6 +719,26 @@ bool end_active_trans(THD *thd);
int end_trans(THD *thd, enum enum_mysql_completiontype completion);
Item *negate_expression(THD *thd, Item *expr);
+
+/* log.cc */
+int vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
+void sql_print_error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+void sql_print_warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+void sql_print_information(const char *format, ...)
+ ATTRIBUTE_FORMAT(printf, 1, 2);
+typedef void (*sql_print_message_func)(const char *format, ...)
+ ATTRIBUTE_FORMAT(printf, 1, 2);
+extern sql_print_message_func sql_print_message_handlers[];
+
+int error_log_print(enum loglevel level, const char *format,
+ va_list args);
+
+bool slow_log_print(THD *thd, const char *query, uint query_length,
+ time_t query_start_arg);
+
+bool general_log_print(THD *thd, enum enum_server_command command,
+ const char *format,...);
+
#include "sql_class.h"
#include "sql_acl.h"
#include "tztime.h"
@@ -901,10 +928,7 @@ void mysql_client_binlog_statement(THD *thd);
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
my_bool drop_temporary);
int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
- bool drop_temporary, bool drop_view, bool log_query);
-int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables,
- bool if_exists, bool drop_temporary,
- bool log_query);
+ bool drop_temporary, bool drop_view, bool log_query);
bool quick_rm_table(handlerton *base,const char *db,
const char *table_name, uint flags);
void close_cached_table(THD *thd, TABLE *table);
@@ -1175,7 +1199,11 @@ bool mysqld_show_open_tables(THD *thd,const char *wild);
bool mysqld_show_logs(THD *thd);
void append_identifier(THD *thd, String *packet, const char *name,
uint length);
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
+#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
+#ifdef MYSQL_SERVER
void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild);
int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd);
bool mysqld_show_create(THD *thd, TABLE_LIST *table_list);
@@ -1204,9 +1232,6 @@ void reset_status_vars();
/* information schema */
extern LEX_STRING INFORMATION_SCHEMA_NAME;
extern const LEX_STRING partition_keywords[];
-LEX_STRING *make_lex_string(THD *thd, LEX_STRING *lex_str,
- const char* str, uint length,
- bool allocate_lex_string);
ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name);
ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx);
int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
@@ -1367,7 +1392,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr);
void close_temporary_tables(THD *thd);
void close_tables_for_reopen(THD *thd, TABLE_LIST **tables);
TABLE_LIST *find_table_in_list(TABLE_LIST *table,
- st_table_list *TABLE_LIST::*link,
+ TABLE_LIST *TABLE_LIST::*link,
const char *db_name,
const char *table_name);
TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
@@ -1612,25 +1637,6 @@ bool init_errmessage(void);
#endif /* MYSQL_SERVER */
void sql_perror(const char *message);
-
-int vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
-void sql_print_error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
-void sql_print_warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
-void sql_print_information(const char *format, ...)
- ATTRIBUTE_FORMAT(printf, 1, 2);
-typedef void (*sql_print_message_func)(const char *format, ...)
- ATTRIBUTE_FORMAT(printf, 1, 2);
-extern sql_print_message_func sql_print_message_handlers[];
-
-int error_log_print(enum loglevel level, const char *format,
- va_list args);
-
-bool slow_log_print(THD *thd, const char *query, uint query_length,
- time_t query_start_arg);
-
-bool general_log_print(THD *thd, enum enum_server_command command,
- const char *format,...);
-
bool fn_format_relative_to_data_home(char * to, const char *name,
const char *dir, const char *extension);
#ifdef MYSQL_SERVER
@@ -1684,9 +1690,14 @@ extern int creating_table; // How many mysql_create_table() are running
*/
extern time_t server_start_time;
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
extern uint mysql_data_home_len;
extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH],
- mysql_real_data_home[], *opt_mysql_tmpdir, mysql_charsets_dir[],
+ mysql_real_data_home[];
+#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
+#ifdef MYSQL_SERVER
+extern char *opt_mysql_tmpdir, mysql_charsets_dir[],
def_ft_boolean_syntax[sizeof(ft_boolean_syntax)];
#define mysql_tmpdir (my_tmpdir(&mysql_tmpdir_list))
extern MY_TMPDIR mysql_tmpdir_list;
@@ -1703,8 +1714,13 @@ extern Gt_creator gt_creator;
extern Lt_creator lt_creator;
extern Ge_creator ge_creator;
extern Le_creator le_creator;
-extern char language[FN_REFLEN], reg_ext[FN_EXTLEN];
+extern char language[FN_REFLEN];
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
+extern char reg_ext[FN_EXTLEN];
extern uint reg_ext_length;
+#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
+#ifdef MYSQL_SERVER
extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN];
extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file;
extern char log_error_file[FN_REFLEN], *opt_tc_log_file;
@@ -1733,17 +1749,32 @@ extern ulong max_binlog_size, max_relay_log_size;
extern ulong opt_binlog_rows_event_max_size;
extern ulong rpl_recovery_rank, thread_cache_size, thread_pool_size;
extern ulong back_log;
-extern ulong specialflag, current_pid;
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
+extern ulong specialflag;
+#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
+#ifdef MYSQL_SERVER
+extern ulong current_pid;
extern ulong expire_logs_days, sync_binlog_period, sync_binlog_counter;
extern ulong opt_tc_log_size, tc_log_max_pages_used, tc_log_page_size;
extern ulong tc_log_page_waits;
extern my_bool relay_log_purge, opt_innodb_safe_binlog, opt_innodb;
extern uint test_flags,select_errors,ha_open_options;
extern uint protocol_version, mysqld_port, dropping_tables;
-extern uint delay_key_write_options, lower_case_table_names;
+extern uint delay_key_write_options;
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
+extern uint lower_case_table_names;
+#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
+#ifdef MYSQL_SERVER
extern bool opt_endinfo, using_udf_functions;
extern my_bool locked_in_memory;
-extern bool opt_using_transactions, mysqld_embedded;
+extern bool opt_using_transactions;
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
+extern bool mysqld_embedded;
+#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
+#ifdef MYSQL_SERVER
extern bool using_update_log, opt_large_files, server_id_supplied;
extern bool opt_update_log, opt_bin_log, opt_error_log;
extern my_bool opt_log, opt_slow_log;
@@ -1767,8 +1798,12 @@ extern uint opt_crash_binlog_innodb;
extern char *shared_memory_base_name, *mysqld_unix_port;
extern my_bool opt_enable_shared_memory;
extern char *default_tz_name;
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
extern my_bool opt_large_pages;
extern uint opt_large_page_size;
+#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
+#ifdef MYSQL_SERVER
extern char *opt_logname, *opt_slow_logname;
extern const char *log_output_str;
@@ -1804,7 +1839,11 @@ extern MY_BITMAP temp_pool;
extern String my_empty_string;
extern const String my_null_string;
extern SHOW_VAR status_vars[];
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
extern struct system_variables global_system_variables;
+#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
+#ifdef MYSQL_SERVER
extern struct system_variables max_system_variables;
extern struct system_status_var global_status_var;
extern struct rand_struct sql_rand;
@@ -1882,6 +1921,11 @@ bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list);
bool lock_table_names(THD *thd, TABLE_LIST *table_list);
void unlock_table_names(THD *thd, TABLE_LIST *table_list,
TABLE_LIST *last_table);
+bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list);
+bool is_table_name_exclusively_locked_by_this_thread(THD *thd,
+ TABLE_LIST *table_list);
+bool is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key,
+ int key_length);
/* old unireg functions */
@@ -2002,10 +2046,14 @@ int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr);
char *fn_rext(char *name);
/* Conversion functions */
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
uint strconvert(CHARSET_INFO *from_cs, const char *from,
CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors);
uint filename_to_tablename(const char *from, char *to, uint to_length);
uint tablename_to_filename(const char *from, char *to, uint to_length);
+#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
+#ifdef MYSQL_SERVER
uint build_table_filename(char *buff, size_t bufflen, const char *db,
const char *table, const char *ext, uint flags);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 39a7c4e9095..6d22047b9db 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -762,6 +762,7 @@ static void close_connections(void)
DBUG_PRINT("info",("Waiting for select thread"));
#ifndef DONT_USE_THR_ALARM
+ if (pthread_kill(select_thread, thr_client_alarm))
break; // allready dead
#endif
set_timespec(abstime, 2);
@@ -7819,8 +7820,6 @@ static void get_options(int *argc,char **argv)
if (opt_short_log_format)
opt_specialflag|= SPECIAL_SHORT_LOG_FORMAT;
- if (opt_log_queries_not_using_indexes)
- opt_specialflag|= SPECIAL_LOG_QUERIES_NOT_USING_INDEXES;
if (init_global_datetime_format(MYSQL_TIMESTAMP_DATE,
&global_system_variables.date_format) ||
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 4944c994d3d..948b268d9a3 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -1264,8 +1264,16 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
thd= head->in_use;
if (!(file= head->file->clone(thd->mem_root)))
{
+ /*
+ Manually set the error flag. Note: there seems to be quite a few
+ places where a failure could cause the server to "hang" the client by
+ sending no response to a query. ATM those are not real errors because
+ the storage engine calls in question happen to never fail with the
+ existing storage engines.
+ */
+ thd->net.report_error= 1; /* purecov: inspected */
/* Caller will free the memory */
- goto failure;
+ goto failure; /* purecov: inspected */
}
head->column_bitmaps_set(&column_bitmap, &column_bitmap);
@@ -7244,6 +7252,11 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree, bool update_tbl_stats)
param->is_ror_scan is cleared if the function detects that the key scan is
not a Rowid-Ordered Retrieval scan ( see comments for is_key_scan_ror
function for description of which key scans are ROR scans)
+
+ RETURN
+ #records E(#records) for given subtree
+ HA_POS_ERROR if subtree cannot be used for record retrieval
+
*/
static ha_rows
@@ -7445,27 +7458,24 @@ check_quick_keys(PARAM *param, uint idx, SEL_ARG *key_tree,
ROR (Rowid Ordered Retrieval) key scan is a key scan that produces
ordered sequence of rowids (ha_xxx::cmp_ref is the comparison function)
- An index scan is a ROR scan if it is done using a condition in form
+ This function is needed to handle a practically-important special case:
+ an index scan is a ROR scan if it is done using a condition in form
- "key1_1=c_1 AND ... AND key1_n=c_n" (1)
+ "key1_1=c_1 AND ... AND key1_n=c_n"
where the index is defined on (key1_1, ..., key1_N [,a_1, ..., a_n])
- and the table has a clustered Primary Key
-
- PRIMARY KEY(a_1, ..., a_n, b1, ..., b_k) with first key parts being
- identical to uncovered parts ot the key being scanned (2)
-
- Scans on HASH indexes are not ROR scans,
- any range scan on clustered primary key is ROR scan (3)
+ and the table has a clustered Primary Key defined as
- Check (1) is made in check_quick_keys()
- Check (3) is made check_quick_select()
- Check (2) is made by this function.
+ PRIMARY KEY(a_1, ..., a_n, b1, ..., b_k)
+
+ i.e. the first key parts of it are identical to uncovered parts ot the
+ key being scanned. This function assumes that the index flags do not
+ include HA_KEY_SCAN_NOT_ROR flag (that is checked elsewhere).
RETURN
- TRUE If the scan is ROR-scan
- FALSE otherwise
+ TRUE The scan is ROR-scan
+ FALSE Otherwise
*/
static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts)
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index 23bc3c96e8f..308b96d84f8 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -905,7 +905,6 @@ bool partition_info::set_up_charset_field_preps()
Field *field, **ptr;
uchar **char_ptrs;
unsigned i;
- bool found;
size_t size;
uint tot_fields= 0;
uint tot_part_fields= 0;
@@ -918,7 +917,6 @@ bool partition_info::set_up_charset_field_preps()
{
ptr= part_field_array;
/* Set up arrays and buffers for those fields */
- i= 0;
while ((field= *(ptr++)))
{
if (field_is_partition_charset(field))
@@ -954,7 +952,7 @@ bool partition_info::set_up_charset_field_preps()
}
part_charset_field_array[i]= NULL;
}
- if (is_sub_partitioned() && list_of_subpart_fields &&
+ if (is_sub_partitioned() && !list_of_subpart_fields &&
check_part_func_fields(subpart_field_array, FALSE))
{
/* Set up arrays and buffers for those fields */
@@ -962,7 +960,10 @@ bool partition_info::set_up_charset_field_preps()
while ((field= *(ptr++)))
{
if (field_is_partition_charset(field))
+ {
tot_subpart_fields++;
+ tot_fields++;
+ }
}
size= tot_subpart_fields * sizeof(char*);
if (!(char_ptrs= (uchar**) sql_calloc(size)))
@@ -975,10 +976,10 @@ bool partition_info::set_up_charset_field_preps()
if (!(char_ptrs= (uchar**) sql_alloc(size)))
goto error;
subpart_charset_field_array= (Field**)char_ptrs;
+ ptr= subpart_field_array;
i= 0;
while ((field= *(ptr++)))
{
- unsigned j= 0;
CHARSET_INFO *cs;
uchar *field_buf;
LINT_INIT(field_buf);
@@ -987,28 +988,16 @@ bool partition_info::set_up_charset_field_preps()
continue;
cs= ((Field_str*)field)->charset();
size= field->pack_length();
- found= FALSE;
- for (j= 0; j < tot_part_fields; j++)
- {
- if (field == part_charset_field_array[i])
- found= TRUE;
- }
- if (!found)
- {
- tot_fields++;
- if (!(field_buf= (uchar*) sql_calloc(size)))
- goto error;
- }
+ if (!(field_buf= (uchar*) sql_calloc(size)))
+ goto error;
+ subpart_charset_field_array[i]= field;
subpart_field_buffers[i++]= field_buf;
}
- if (!(char_ptrs= (uchar**) sql_calloc(size)))
- goto error;
- restore_subpart_field_ptrs= char_ptrs;
+ subpart_charset_field_array[i]= NULL;
}
if (tot_fields)
{
- uint j,k,l;
-
+ uint k;
size= tot_fields*sizeof(char**);
if (!(char_ptrs= (uchar**)sql_calloc(size)))
goto error;
@@ -1026,11 +1015,12 @@ bool partition_info::set_up_charset_field_preps()
full_part_field_buffers[i]= part_field_buffers[i];
}
k= tot_part_fields;
- l= 0;
for (i= 0; i < tot_subpart_fields; i++)
{
+ uint j;
+ bool found= FALSE;
field= subpart_charset_field_array[i];
- found= FALSE;
+
for (j= 0; j < tot_part_fields; j++)
{
if (field == part_charset_field_array[i])
@@ -1038,12 +1028,12 @@ bool partition_info::set_up_charset_field_preps()
}
if (!found)
{
- full_part_charset_field_array[l]= subpart_charset_field_array[k];
- full_part_field_buffers[l]= subpart_field_buffers[k];
- k++; l++;
+ full_part_charset_field_array[k]= subpart_charset_field_array[i];
+ full_part_field_buffers[k]= subpart_field_buffers[i];
+ k++;
}
}
- full_part_charset_field_array[tot_fields]= NULL;
+ full_part_charset_field_array[k]= NULL;
}
DBUG_RETURN(FALSE);
error:
diff --git a/sql/partition_info.h b/sql/partition_info.h
index ce2f2a7b358..10edea074c0 100644
--- a/sql/partition_info.h
+++ b/sql/partition_info.h
@@ -81,6 +81,13 @@ public:
*/
Field **full_part_field_array;
Field **full_part_charset_field_array;
+ /*
+ Set of all fields used in partition and subpartition expression.
+ Required for testing of partition fields in write_set when
+ updating. We need to set all bits in read_set because the row may
+ need to be inserted in a different [sub]partition.
+ */
+ MY_BITMAP full_part_field_set;
/*
When we have a field that requires transformation before calling the
diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h
index 2ce8def4577..79e69aecaeb 100644
--- a/sql/rpl_utility.h
+++ b/sql/rpl_utility.h
@@ -128,7 +128,7 @@ private:
slave thread, but nowhere else.
*/
struct RPL_TABLE_LIST
- : public st_table_list
+ : public TABLE_LIST
{
bool m_tabledef_valid;
table_def m_tabledef;
diff --git a/sql/set_var.cc b/sql/set_var.cc
index d9869ce6809..7f3e808090d 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -616,6 +616,8 @@ sys_var_thd_time_zone sys_time_zone(&vars, "time_zone");
/* Global read-only variable containing hostname */
static sys_var_const_str sys_hostname(&vars, "hostname", glob_hostname);
+sys_var_thd_bool sys_keep_files_on_create(&vars, "keep_files_on_create",
+ &SV::keep_files_on_create);
/* Read only variables */
static sys_var_have_variable sys_have_compress(&vars, "have_compress", &have_compress);
@@ -2218,9 +2220,9 @@ void sys_var_log_output::set_default(THD *thd, enum_var_type type)
{
pthread_mutex_lock(&LOCK_global_system_variables);
logger.lock();
- logger.init_slow_log(LOG_TABLE);
- logger.init_general_log(LOG_TABLE);
- *value= LOG_TABLE;
+ logger.init_slow_log(LOG_FILE);
+ logger.init_general_log(LOG_FILE);
+ *value= LOG_FILE;
logger.unlock();
pthread_mutex_unlock(&LOCK_global_system_variables);
}
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 0e3544415d1..682eee06e02 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -1478,6 +1478,8 @@ ER_DUP_KEYNAME 42000 S1009
spa "Nombre de clave duplicado '%-.192s'"
swe "Nyckelnamn '%-.192s' finns flera gånger"
ukr "äÕÂÌÀÀÞÅ ¦Í'Ñ ËÌÀÞÁ '%-.192s'"
+# When using this error code, please use ER(ER_DUP_ENTRY_WITH_KEY_NAME)
+# for the message string. See, for example, code in handler.cc.
ER_DUP_ENTRY 23000 S1009
cze "Zdvojen-Bý klíè '%-.192s' (èíslo klíèe %d)"
dan "Ens værdier '%-.192s' for indeks %d"
@@ -5022,7 +5024,9 @@ ER_UNKNOWN_STORAGE_ENGINE 42000
ger "Unbekannte Speicher-Engine '%s'"
por "Motor de tabela desconhecido '%s'"
spa "Desconocido motor de tabla '%s'"
-ER_UNUSED_1
+# When using this error code, use ER(ER_WARN_DEPRECATED_SYNTAX_WITH_VER)
+# for the message string. See, for example, code in mysql_priv.h.
+ER_WARN_DEPRECATED_SYNTAX
eng "'%s' is deprecated; use '%s' instead"
ger "'%s' ist veraltet. Bitte benutzen Sie '%s'"
por "'%s' é desatualizado. Use '%s' em seu lugar"
@@ -5606,8 +5610,6 @@ ER_SP_RECURSION_LIMIT
ER_SP_PROC_TABLE_CORRUPT
eng "Failed to load routine %-.192s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)"
ger "Routine %-.192s konnte nicht geladen werden. Die Tabelle mysql.proc fehlt, ist beschädigt, oder enthält fehlerhaften Daten (interner Code: %d)"
-ER_FOREIGN_SERVER_EXISTS
- eng "The foreign server, %s, you are trying to create already exists."
ER_SP_WRONG_NAME 42000
eng "Incorrect routine name '%-.192s'"
ger "Ungültiger Routinenname '%-.192s'"
@@ -5629,6 +5631,34 @@ ER_NON_GROUPING_FIELD_USED 42000
ER_TABLE_CANT_HANDLE_SPKEYS
eng "The used table type doesn't support SPATIAL indexes"
ger "Der verwendete Tabellentyp unterstützt keine SPATIAL-Indizes"
+ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA
+ eng "Triggers can not be created on system tables"
+ ger "Trigger können nicht auf Systemtabellen erzeugt werden"
+ER_REMOVED_SPACES
+ eng "Leading spaces are removed from name '%s'"
+ ger "Führende Leerzeichen werden aus dem Namen '%s' entfernt"
+ER_AUTOINC_READ_FAILED
+ eng "Failed to read auto-increment value from storage engine"
+ ger "Lesen des Autoincrement-Werts von der Speicher-Engine fehlgeschlagen"
+ER_USERNAME
+ eng "user name"
+ ger "Benutzername"
+ER_HOSTNAME
+ eng "host name"
+ ger "Hostname"
+ER_WRONG_STRING_LENGTH
+ eng "String '%-.70s' is too long for %s (should be no longer than %d)"
+ ger "String '%-.70s' ist zu lang für %s (sollte nicht länger sein als %d)"
+ER_NON_INSERTABLE_TABLE
+ eng "The target table %-.100s of the %s is not insertable-into"
+ ger "Die Zieltabelle %-.100s von %s ist nicht einfügbar"
+ER_ADMIN_WRONG_MRG_TABLE
+ eng "Table '%-.64s' is differently defined or of non-MyISAM type or doesn't exist"
+ER_FOREIGN_SERVER_EXISTS
+ eng "The foreign server, %s, you are trying to create already exists."
+ER_FOREIGN_SERVER_DOESNT_EXIST
+ eng "The foreign server name you are trying to reference does not exist. Data source error: %-.64s"
+ ger "Die externe Verbindung, auf die Sie zugreifen wollen, existiert nicht. Datenquellenfehlermeldung: %-.64s"
ER_ILLEGAL_HA_CREATE_OPTION
eng "Table storage engine '%-.64s' does not support the create option '%.64s'"
ger "Speicher-Engine '%-.64s' der Tabelle unterstützt die Option '%.64s' nicht"
@@ -5848,9 +5878,6 @@ ER_BINLOG_ROW_WRONG_TABLE_DEF
ER_BINLOG_ROW_RBR_TO_SBR
eng "Slave running with --log-slave-updates must use row-based binary logging to be able to replicate row-based binary log events"
ger "Slave, die mit --log-slave-updates laufen, müssen zeilenbasiertes Loggen verwenden, um zeilenbasierte Binärlog-Ereignisse loggen zu können"
-ER_FOREIGN_SERVER_DOESNT_EXIST
- eng "The foreign server name you are trying to reference does not exist. Data source error: %-.64s"
- ger "Die externe Verbindung, auf die Sie zugreifen wollen, existiert nicht. Datenquellenfehlermeldung: %-.64s"
ER_EVENT_ALREADY_EXISTS
eng "Event '%-.192s' already exists"
ger "Event '%-.192s' existiert bereits"
@@ -5901,7 +5928,9 @@ ER_EVENT_DATA_TOO_LONG
ER_DROP_INDEX_FK
eng "Cannot drop index '%-.192s': needed in a foreign key constraint"
ger "Kann Index '%-.192s' nicht löschen: wird für einen Fremdschlüssel benötigt"
-ER_WARN_DEPRECATED_SYNTAX
+# When using this error message, use the ER_WARN_DEPRECATED_SYNTAX error
+# code. See, for example, code in mysql_priv.h.
+ER_WARN_DEPRECATED_SYNTAX_WITH_VER
eng "The syntax '%s' is deprecated and will be removed in MySQL %s. Please use %s instead"
ger "Die Syntax '%s' ist veraltet und wird in MySQL %s entfernt. Bitte benutzen Sie statt dessen %s"
ER_CANT_WRITE_LOCK_LOG_TABLE
@@ -5916,9 +5945,6 @@ ER_FOREIGN_DUPLICATE_KEY 23000 S1009
ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE
eng "Column count of mysql.%s is wrong. Expected %d, found %d. Created with MySQL %d, now running %d. Please use mysql_upgrade to fix this error."
ger "Spaltenanzahl von mysql.%s falsch. %d erwartet, aber %d erhalten. Erzeugt mit MySQL %d, jetzt unter %d. Bitte benutzen Sie mysql_upgrade, um den Fehler zu beheben"
-ER_REMOVED_SPACES
- eng "Leading spaces are removed from name '%s'"
- ger "Führende Leerzeichen werden aus dem Namen '%s' entfernt"
ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR
eng "Cannot switch out of the row-based binary log format when the session has open temporary tables"
ger "Kann nicht aus dem zeilenbasierten Binärlog-Format herauswechseln, wenn die Sitzung offene temporäre Tabellen hat"
@@ -5975,9 +6001,6 @@ ER_BASE64_DECODE_ERROR
eng "Decoding of base64 string failed"
swe "Avkodning av base64 sträng misslyckades"
ger "Der Server hat keine zeilenbasierte Replikation"
-ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA
- eng "Triggers can not be created on system tables"
- ger "Trigger können nicht auf Systemtabellen erzeugt werden"
ER_EVENT_RECURSION_FORBIDDEN
eng "Recursion of EVENT DDL statements is forbidden when body is present"
ger "Rekursivität von EVENT-DDL-Anweisungen ist unzulässig wenn ein Hauptteil (Body) existiert"
@@ -5987,27 +6010,12 @@ ER_EVENTS_DB_ERROR
ER_ONLY_INTEGERS_ALLOWED
eng "Only integers allowed as number here"
ger "An dieser Stelle sind nur Ganzzahlen zulässig"
-ER_AUTOINC_READ_FAILED
- eng "Failed to read auto-increment value from storage engine"
- ger "Lesen des Autoincrement-Werts von der Speicher-Engine fehlgeschlagen"
-ER_USERNAME
- eng "user name"
- ger "Benutzername"
-ER_HOSTNAME
- eng "host name"
- ger "Hostname"
-ER_WRONG_STRING_LENGTH
- eng "String '%-.70s' is too long for %s (should be no longer than %d)"
- ger "String '%-.70s' ist zu lang für %s (sollte nicht länger sein als %d)"
ER_UNSUPORTED_LOG_ENGINE
eng "This storage engine cannot be used for log tables""
ger "Diese Speicher-Engine kann für Logtabellen nicht verwendet werden"
ER_BAD_LOG_STATEMENT
eng "You cannot '%s' a log table if logging is enabled"
ger "Sie können eine Logtabelle nicht '%s', wenn Loggen angeschaltet ist"
-ER_NON_INSERTABLE_TABLE
- eng "The target table %-.100s of the %s is not insertable-into"
- ger "Die Zieltabelle %-.100s von %s ist nicht einfügbar"
ER_CANT_RENAME_LOG_TABLE
eng "Cannot rename '%s'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to '%s'"
ger "Kann '%s' nicht umbenennen. Wenn Loggen angeschaltet ist, müssen beim Umbenennen zu/von einer Logtabelle zwei Tabellen angegeben werden: die Logtabelle zu einer Archivtabelle und eine weitere Tabelle zurück zu '%s'"
@@ -6023,6 +6031,8 @@ ER_WRONG_PARAMETERS_TO_STORED_FCT 42000
ER_NATIVE_FCT_NAME_COLLISION
eng "This function '%-.192s' has the same name as a native function"
ger "Die Funktion '%-.192s' hat denselben Namen wie eine native Funktion"
+# When using this error message, use the ER_DUP_ENTRY error code. See, for
+# example, code in handler.cc.
ER_DUP_ENTRY_WITH_KEY_NAME 23000 S1009
cze "Zvojen-Bý klíè '%-.64s' (èíslo klíèe '%-.192s')"
dan "Ens værdier '%-.64s' for indeks '%-.192s'"
@@ -6052,15 +6062,13 @@ ER_BINLOG_PURGE_EMFILE
eng "Too many files opened, please execute the command again"
ger "Zu viele offene Dateien, bitte führen Sie den Befehl noch einmal aus"
ER_EVENT_CANNOT_CREATE_IN_THE_PAST
- eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been created"
+ eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation."
ER_EVENT_CANNOT_ALTER_IN_THE_PAST
- eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been altered"
+ eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation."
ER_SLAVE_INCIDENT
eng "The incident %s occured on the master. Message: %-.64s"
ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT
eng "Table has no partition for some existing values"
-ER_ADMIN_WRONG_MRG_TABLE
- eng "Table '%-.64s' is differently defined or of non-MyISAM type or doesn't exist"
ER_BINLOG_UNSAFE_STATEMENT
eng "Statement is not safe to log in statement format."
swe "Detta är inte säkert att logga i statement-format."
diff --git a/sql/slave.cc b/sql/slave.cc
index 6c7968c2b3f..2e8e3f582de 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -27,6 +27,7 @@
#include <my_dir.h>
#include <sql_common.h>
#include <errmsg.h>
+#include <mysys_err.h>
#ifdef HAVE_REPLICATION
@@ -59,6 +60,58 @@ ulonglong relay_log_space_limit = 0;
int disconnect_slave_event_count = 0, abort_slave_event_count = 0;
int events_till_abort = -1;
+enum enum_slave_reconnect_actions
+{
+ SLAVE_RECON_ACT_REG= 0,
+ SLAVE_RECON_ACT_DUMP= 1,
+ SLAVE_RECON_ACT_EVENT= 2,
+ SLAVE_RECON_ACT_MAX
+};
+
+enum enum_slave_reconnect_messages
+{
+ SLAVE_RECON_MSG_WAIT= 0,
+ SLAVE_RECON_MSG_KILLED_WAITING= 1,
+ SLAVE_RECON_MSG_AFTER= 2,
+ SLAVE_RECON_MSG_FAILED= 3,
+ SLAVE_RECON_MSG_COMMAND= 4,
+ SLAVE_RECON_MSG_KILLED_AFTER= 5,
+ SLAVE_RECON_MSG_MAX
+};
+
+static const char *reconnect_messages[SLAVE_RECON_ACT_MAX][SLAVE_RECON_MSG_MAX]=
+{
+ {
+ "Waiting to reconnect after a failed registration on master",
+ "Slave I/O thread killed while waitnig to reconnect after a failed \
+registration on master",
+ "Reconnecting after a failed registration on master",
+ "failed registering on master, reconnecting to try again, \
+log '%s' at postion %s",
+ "COM_REGISTER_SLAVE",
+ "Slave I/O thread killed during or after reconnect"
+ },
+ {
+ "Waiting to reconnect after a failed binlog dump request",
+ "Slave I/O thread killed while retrying master dump",
+ "Reconnecting after a failed binlog dump request",
+ "failed dump request, reconnecting to try again, log '%s' at postion %s",
+ "COM_BINLOG_DUMP",
+ "Slave I/O thread killed during or after reconnect"
+ },
+ {
+ "Waiting to reconnect after a failed master event read",
+ "Slave I/O thread killed while waiting to reconnect after a failed read",
+ "Reconnecting after a failed master event read",
+ "Slave I/O thread: Failed reading log event, reconnecting to retry, \
+log '%s' at postion %s",
+ "",
+ "Slave I/O thread killed during or after a reconnect done to recover from \
+failed read"
+ }
+};
+
+
typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE;
static int process_io_rotate(MASTER_INFO* mi, Rotate_log_event* rev);
@@ -1098,12 +1151,14 @@ static void write_ignored_events_info_to_relay_log(THD *thd, MASTER_INFO *mi)
}
-int register_slave_on_master(MYSQL* mysql, MASTER_INFO *mi)
+int register_slave_on_master(MYSQL* mysql, MASTER_INFO *mi,
+ bool *suppress_warnings)
{
uchar buf[1024], *pos= buf;
uint report_host_len, report_user_len=0, report_password_len=0;
DBUG_ENTER("register_slave_on_master");
+ *suppress_warnings= FALSE;
if (!report_host)
DBUG_RETURN(0);
report_host_len= strlen(report_host);
@@ -1127,11 +1182,18 @@ int register_slave_on_master(MYSQL* mysql, MASTER_INFO *mi)
if (simple_command(mysql, COM_REGISTER_SLAVE, buf, (size_t) (pos- buf), 0))
{
- char buf[256];
- my_snprintf(buf, sizeof(buf),
- "%s (Errno: %d)", mysql_error(mysql), mysql_errno(mysql));
- mi->report(ERROR_LEVEL, ER_SLAVE_MASTER_COM_FAILURE,
- ER(ER_SLAVE_MASTER_COM_FAILURE), "COM_REGISTER_SLAVE", buf);
+ if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED)
+ {
+ *suppress_warnings= TRUE; // Suppress reconnect warning
+ }
+ else
+ {
+ char buf[256];
+ my_snprintf(buf, sizeof(buf), "%s (Errno: %d)", mysql_error(mysql),
+ mysql_errno(mysql));
+ mi->report(ERROR_LEVEL, ER_SLAVE_MASTER_COM_FAILURE,
+ ER(ER_SLAVE_MASTER_COM_FAILURE), "COM_REGISTER_SLAVE", buf);
+ }
DBUG_RETURN(1);
}
DBUG_RETURN(0);
@@ -1460,6 +1522,8 @@ static int request_dump(MYSQL* mysql, MASTER_INFO* mi,
int binlog_flags = 0; // for now
char* logname = mi->master_log_name;
DBUG_ENTER("request_dump");
+
+ *suppress_warnings= FALSE;
// TODO if big log files: Change next to int8store()
int4store(buf, (ulong) mi->master_log_pos);
@@ -1475,7 +1539,7 @@ static int request_dump(MYSQL* mysql, MASTER_INFO* mi,
now we just fill up the error log :-)
*/
if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED)
- *suppress_warnings= 1; // Suppress reconnect warning
+ *suppress_warnings= TRUE; // Suppress reconnect warning
else
sql_print_error("Error on COM_BINLOG_DUMP: %d %s, will retry in %d secs",
mysql_errno(mysql), mysql_error(mysql),
@@ -1539,7 +1603,7 @@ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings)
ulong len;
DBUG_ENTER("read_event");
- *suppress_warnings= 0;
+ *suppress_warnings= FALSE;
/*
my_real_read() will time us out
We check if we were told to die, and if not, try reading again
@@ -1879,6 +1943,94 @@ on this slave.\
}
+static bool check_io_slave_killed(THD *thd, MASTER_INFO *mi, const char *info)
+{
+ if (io_slave_killed(thd, mi))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_information(info);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/**
+ @brief Try to reconnect slave IO thread.
+
+ @details Terminates current connection to master, sleeps for
+ @c mi->connect_retry msecs and initiates new connection with
+ @c safe_reconnect(). Variable pointed by @c retry_count is increased -
+ if it exceeds @c master_retry_count then connection is not re-established
+ and function signals error.
+ Unless @c suppres_warnings is TRUE, a warning is put in the server error log
+ when reconnecting. The warning message and messages used to report errors
+ are taken from @c messages array. In case @c master_retry_count is exceeded,
+ no messages are added to the log.
+
+ @param[in] thd Thread context.
+ @param[in] mysql MySQL connection.
+ @param[in] mi Master connection information.
+ @param[in,out] retry_count Number of attempts to reconnect.
+ @param[in] suppress_warnings TRUE when a normal net read timeout
+ has caused to reconnecting.
+ @param[in] messages Messages to print/log, see
+ reconnect_messages[] array.
+
+ @retval 0 OK.
+ @retval 1 There was an error.
+*/
+
+static int try_to_reconnect(THD *thd, MYSQL *mysql, MASTER_INFO *mi,
+ uint *retry_count, bool suppress_warnings,
+ const char *messages[SLAVE_RECON_MSG_MAX])
+{
+ mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT;
+ thd->proc_info= messages[SLAVE_RECON_MSG_WAIT];
+#ifdef SIGNAL_WITH_VIO_CLOSE
+ thd->clear_active_vio();
+#endif
+ end_server(mysql);
+ if ((*retry_count)++)
+ {
+ if (*retry_count > master_retry_count)
+ return 1; // Don't retry forever
+ safe_sleep(thd, mi->connect_retry, (CHECK_KILLED_FUNC) io_slave_killed,
+ (void *) mi);
+ }
+ if (check_io_slave_killed(thd, mi, messages[SLAVE_RECON_MSG_KILLED_WAITING]))
+ return 1;
+ thd->proc_info = messages[SLAVE_RECON_MSG_AFTER];
+ if (!suppress_warnings)
+ {
+ char buf[256], llbuff[22];
+ my_snprintf(buf, sizeof(buf), messages[SLAVE_RECON_MSG_FAILED],
+ IO_RPL_LOG_NAME, llstr(mi->master_log_pos, llbuff));
+ /*
+ Raise a warining during registering on master/requesting dump.
+ Log a message reading event.
+ */
+ if (messages[SLAVE_RECON_MSG_COMMAND][0])
+ {
+ mi->report(WARNING_LEVEL, ER_SLAVE_MASTER_COM_FAILURE,
+ ER(ER_SLAVE_MASTER_COM_FAILURE),
+ messages[SLAVE_RECON_MSG_COMMAND], buf);
+ }
+ else
+ {
+ sql_print_information(buf);
+ }
+ }
+ if (safe_reconnect(thd, mysql, mi, 1) || io_slave_killed(thd, mi))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_information(messages[SLAVE_RECON_MSG_KILLED_AFTER]);
+ return 1;
+ }
+ return 0;
+}
+
+
/* Slave I/O Thread entry point */
pthread_handler_t handle_slave_io(void *arg)
@@ -1889,7 +2041,10 @@ pthread_handler_t handle_slave_io(void *arg)
RELAY_LOG_INFO *rli= &mi->rli;
char llbuff[22];
uint retry_count;
-
+ bool suppress_warnings;
+#ifndef DBUG_OFF
+ uint retry_count_reg= 0, retry_count_dump= 0, retry_count_event= 0;
+#endif
// needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff
my_thread_init();
DBUG_ENTER("handle_slave_io");
@@ -1975,79 +2130,56 @@ connected:
Register ourselves with the master.
*/
thd->proc_info = "Registering slave on master";
- if (register_slave_on_master(mysql, mi))
+ if (register_slave_on_master(mysql, mi, &suppress_warnings))
{
sql_print_error("Slave I/O thread couldn't register on master");
- goto err;
+ if (check_io_slave_killed(thd, mi, "Slave I/O thread killed while \
+registering slave on master") ||
+ try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_REG]))
+ goto err;
+ goto connected;
}
+ DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_REG",
+ if (!retry_count_reg)
+ {
+ retry_count_reg++;
+ sql_print_information("Forcing to reconnect slave I/O thread");
+ if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_REG]))
+ goto err;
+ goto connected;
+ });
}
DBUG_PRINT("info",("Starting reading binary log from master"));
while (!io_slave_killed(thd,mi))
{
- bool suppress_warnings= 0;
thd->proc_info = "Requesting binlog dump";
if (request_dump(mysql, mi, &suppress_warnings))
{
sql_print_error("Failed on request_dump()");
- if (io_slave_killed(thd,mi))
- {
- sql_print_information("Slave I/O thread killed while requesting master \
-dump");
- goto err;
- }
-
- mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT;
- thd->proc_info= "Waiting to reconnect after a failed binlog dump request";
-#ifdef SIGNAL_WITH_VIO_CLOSE
- thd->clear_active_vio();
-#endif
- end_server(mysql);
- /*
- First time retry immediately, assuming that we can recover
- right away - if first time fails, sleep between re-tries
- hopefuly the admin can fix the problem sometime
- */
- if (retry_count++)
- {
- if (retry_count > master_retry_count)
- goto err; // Don't retry forever
- safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed,
- (void*)mi);
- }
- if (io_slave_killed(thd,mi))
- {
- sql_print_information("Slave I/O thread killed while retrying master \
-dump");
+ if (check_io_slave_killed(thd, mi, "Slave I/O thread killed while \
+requesting master dump") ||
+ try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_DUMP]))
goto err;
- }
-
- thd->proc_info = "Reconnecting after a failed binlog dump request";
- if (!suppress_warnings) {
- char buf[256];
- my_snprintf(buf, sizeof(buf),
- "failed dump request, reconnecting to try again,"
- " log '%s' at postion %s",
- IO_RPL_LOG_NAME,
- llstr(mi->master_log_pos,llbuff));
- mi->report(WARNING_LEVEL, ER_SLAVE_MASTER_COM_FAILURE,
- ER(ER_SLAVE_MASTER_COM_FAILURE), "COM_BINLOG_DUMP", buf);
- }
- if (safe_reconnect(thd, mysql, mi, suppress_warnings) ||
- io_slave_killed(thd,mi))
- {
- sql_print_information("Slave I/O thread killed during or \
-after reconnect");
- goto err;
- }
-
goto connected;
}
+ DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_DUMP",
+ if (!retry_count_dump)
+ {
+ retry_count_dump++;
+ sql_print_information("Forcing to reconnect slave I/O thread");
+ if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_DUMP]))
+ goto err;
+ goto connected;
+ });
while (!io_slave_killed(thd,mi))
{
ulong event_len;
- suppress_warnings= 0;
/*
We say "waiting" because read_event() will wait if there's nothing to
read. But if there's something to read, it will not wait. The
@@ -2056,64 +2188,44 @@ after reconnect");
*/
thd->proc_info= "Waiting for master to send event";
event_len= read_event(mysql, mi, &suppress_warnings);
- if (io_slave_killed(thd,mi))
- {
- if (global_system_variables.log_warnings)
- sql_print_information("Slave I/O thread killed while reading event");
+ if (check_io_slave_killed(thd, mi, "Slave I/O thread killed while \
+reading event"))
goto err;
- }
+ DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_EVENT",
+ if (!retry_count_event)
+ {
+ retry_count_event++;
+ sql_print_information("Forcing to reconnect slave I/O thread");
+ if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_EVENT]))
+ goto err;
+ goto connected;
+ });
if (event_len == packet_error)
{
uint mysql_error_number= mysql_errno(mysql);
- if (mysql_error_number == CR_NET_PACKET_TOO_LARGE)
- {
+ switch (mysql_error_number) {
+ case CR_NET_PACKET_TOO_LARGE:
sql_print_error("\
Log entry on master is longer than max_allowed_packet (%ld) on \
slave. If the entry is correct, restart the server with a higher value of \
max_allowed_packet",
thd->variables.max_allowed_packet);
goto err;
- }
- if (mysql_error_number == ER_MASTER_FATAL_ERROR_READING_BINLOG)
- {
+ case ER_MASTER_FATAL_ERROR_READING_BINLOG:
sql_print_error(ER(mysql_error_number), mysql_error_number,
mysql_error(mysql));
goto err;
- }
- mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT;
- thd->proc_info = "Waiting to reconnect after a failed master event read";
-#ifdef SIGNAL_WITH_VIO_CLOSE
- thd->clear_active_vio();
-#endif
- end_server(mysql);
- if (retry_count++)
- {
- if (retry_count > master_retry_count)
- goto err; // Don't retry forever
- safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed,
- (void*) mi);
- }
- if (io_slave_killed(thd,mi))
- {
- if (global_system_variables.log_warnings)
- sql_print_information("Slave I/O thread killed while waiting to \
-reconnect after a failed read");
+ case EE_OUTOFMEMORY:
+ case ER_OUTOFMEMORY:
+ sql_print_error("\
+Stopping slave I/O thread due to out-of-memory error from master");
goto err;
}
- thd->proc_info = "Reconnecting after a failed master event read";
- if (!suppress_warnings)
- sql_print_information("Slave I/O thread: Failed reading log event, \
-reconnecting to retry, log '%s' position %s", IO_RPL_LOG_NAME,
- llstr(mi->master_log_pos, llbuff));
- if (safe_reconnect(thd, mysql, mi, suppress_warnings) ||
- io_slave_killed(thd,mi))
- {
- if (global_system_variables.log_warnings)
- sql_print_information("Slave I/O thread killed during or after a \
-reconnect done to recover from failed read");
+ if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_EVENT]))
goto err;
- }
goto connected;
} // if (event_len == packet_error)
diff --git a/sql/sp.cc b/sql/sp.cc
index 66c6c05c930..aed4976f839 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -602,6 +602,15 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
(*sphp)->set_info(created, modified, &chistics, sql_mode);
(*sphp)->set_creation_ctx(creation_ctx);
(*sphp)->optimize();
+ /*
+ Not strictly necessary to invoke this method here, since we know
+ that we've parsed CREATE PROCEDURE/FUNCTION and not an
+ UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
+ maintain the invariant that this method is called for each
+ distinct statement, in case its logic is extended with other
+ types of analyses in future.
+ */
+ newlex.set_trg_event_type_for_tables();
}
}
@@ -1912,32 +1921,40 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
TABLE_LIST *table)
{
int ret= 0;
- Table_triggers_list *triggers= table->table->triggers;
- if (add_used_routine(lex, thd->stmt_arena, &triggers->sroutines_key,
- table->belong_to_view))
+
+ Sroutine_hash_entry **last_cached_routine_ptr=
+ (Sroutine_hash_entry **)lex->sroutines_list.next;
+
+ if (static_cast<int>(table->lock_type) >=
+ static_cast<int>(TL_WRITE_ALLOW_WRITE))
{
- Sroutine_hash_entry **last_cached_routine_ptr=
- (Sroutine_hash_entry **)lex->sroutines_list.next;
for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
{
- for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
+ if (table->trg_event_map &
+ static_cast<uint8>(1 << static_cast<int>(i)))
{
- sp_head *trigger_body= triggers->bodies[i][j];
- if (trigger_body)
+ for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
{
- (void)trigger_body->
- add_used_tables_to_table_list(thd, &lex->query_tables_last,
- table->belong_to_view);
- sp_update_stmt_used_routines(thd, lex,
- &trigger_body->m_sroutines,
- table->belong_to_view);
- trigger_body->propagate_attributes(lex);
+ /* We can have only one trigger per action type currently */
+ sp_head *trigger= table->table->triggers->bodies[i][j];
+ if (trigger &&
+ add_used_routine(lex, thd->stmt_arena, &trigger->m_sroutines_key,
+ table->belong_to_view))
+ {
+ trigger->add_used_tables_to_table_list(thd, &lex->query_tables_last,
+ table->belong_to_view);
+ trigger->propagate_attributes(lex);
+ sp_update_stmt_used_routines(thd, lex,
+ &trigger->m_sroutines,
+ table->belong_to_view);
+ }
}
}
}
- ret= sp_cache_routines_and_add_tables_aux(thd, lex,
- *last_cached_routine_ptr, FALSE);
}
+ ret= sp_cache_routines_and_add_tables_aux(thd, lex,
+ *last_cached_routine_ptr,
+ FALSE);
return ret;
}
@@ -2044,15 +2061,11 @@ sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db,
DBUG_PRINT("enter", ("newdb: %s", new_db.str));
/*
- Set new_db to an empty string if it's NULL, because mysql_change_db
- requires a non-NULL argument.
- new_db.str can be NULL only if we're restoring the old database after
- execution of a stored procedure and there were no current database
- selected. The stored procedure itself must always have its database
- initialized.
+ A stored routine always belongs to some database. The
+ old database (old_db) might be NULL, but to restore the
+ old database we will use mysql_change_db.
*/
- if (new_db.str == NULL)
- new_db.str= empty_c_string;
+ DBUG_ASSERT(new_db.str && new_db.length);
if (thd->db)
{
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index f0cc5204749..9b67a89bed2 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -512,12 +512,35 @@ sp_head::init(LEX *lex)
*/
lex->trg_table_fields.empty();
my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
- m_param_begin= m_param_end= m_body_begin= 0;
- m_qname.str= m_db.str= m_name.str= m_params.str=
- m_body.str= m_defstr.str= 0;
- m_qname.length= m_db.length= m_name.length= m_params.length=
- m_body.length= m_defstr.length= 0;
+
+ m_param_begin= NULL;
+ m_param_end= NULL;
+
+ m_body_begin= NULL ;
+
+ m_qname.str= NULL;
+ m_qname.length= 0;
+
+ m_db.str= NULL;
+ m_db.length= 0;
+
+ m_name.str= NULL;
+ m_name.length= 0;
+
+ m_params.str= NULL;
+ m_params.length= 0;
+
+ m_body.str= NULL;
+ m_body.length= 0;
+
+ m_defstr.str= NULL;
+ m_defstr.length= 0;
+
+ m_sroutines_key.str= NULL;
+ m_sroutines_key.length= 0;
+
m_return_field_def.charset= NULL;
+
DBUG_VOID_RETURN;
}
@@ -543,9 +566,14 @@ sp_head::init_sp_name(THD *thd, sp_name *spname)
if (spname->m_qname.length == 0)
spname->init_qname(thd);
- m_qname.length= spname->m_qname.length;
- m_qname.str= strmake_root(thd->mem_root, spname->m_qname.str,
- m_qname.length);
+ m_sroutines_key.length= spname->m_sroutines_key.length;
+ m_sroutines_key.str= (char*) memdup_root(thd->mem_root,
+ spname->m_sroutines_key.str,
+ spname->m_sroutines_key.length + 1);
+ m_sroutines_key.str[0]= static_cast<char>(m_type);
+
+ m_qname.length= m_sroutines_key.length - 1;
+ m_qname.str= m_sroutines_key.str + 1;
DBUG_VOID_RETURN;
}
@@ -1924,8 +1952,11 @@ sp_head::restore_lex(THD *thd)
{
DBUG_ENTER("sp_head::restore_lex");
LEX *sublex= thd->lex;
- LEX *oldlex= (LEX *)m_lex.pop();
+ LEX *oldlex;
+
+ sublex->set_trg_event_type_for_tables();
+ oldlex= (LEX *)m_lex.pop();
if (! oldlex)
return; // Nothing to restore
@@ -2260,7 +2291,8 @@ sp_head::show_create_routine(THD *thd, int type)
protocol->store(sql_mode.str, sql_mode.length, system_charset_info);
if (full_access)
- protocol->store(m_defstr.str, m_defstr.length, &my_charset_bin);
+ protocol->store(m_defstr.str, m_defstr.length,
+ m_creation_ctx->get_client_cs());
else
protocol->store_null();
@@ -3544,6 +3576,7 @@ typedef struct st_sp_table
thr_lock_type lock_type; /* lock type used for prelocking */
uint lock_count;
uint query_lock_count;
+ uint8 trg_event_map;
} SP_TABLE;
uchar *
@@ -3630,6 +3663,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
tab->query_lock_count++;
if (tab->query_lock_count > tab->lock_count)
tab->lock_count++;
+ tab->trg_event_map|= table->trg_event_map;
}
else
{
@@ -3651,6 +3685,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
tab->db_length= table->db_length;
tab->lock_type= table->lock_type;
tab->lock_count= tab->query_lock_count= 1;
+ tab->trg_event_map= table->trg_event_map;
my_hash_insert(&m_sptabs, (uchar *)tab);
}
}
@@ -3728,6 +3763,7 @@ sp_head::add_used_tables_to_table_list(THD *thd,
table->cacheable_table= 1;
table->prelocking_placeholder= 1;
table->belong_to_view= belong_to_view;
+ table->trg_event_map= stab->trg_event_map;
/* Everyting else should be zeroed */
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 490fda67bfe..f6764fbc90e 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -184,6 +184,12 @@ public:
st_sp_chistics *m_chistics;
ulong m_sql_mode; // For SHOW CREATE and execution
LEX_STRING m_qname; // db.name
+ /**
+ Key representing routine in the set of stored routines used by statement.
+ [routine_type]db.name\0
+ @sa sp_name::m_sroutines_key
+ */
+ LEX_STRING m_sroutines_key;
LEX_STRING m_db;
LEX_STRING m_name;
LEX_STRING m_params;
diff --git a/sql/spatial.cc b/sql/spatial.cc
index e0680ed182c..97e5fcfa27a 100644
--- a/sql/spatial.cc
+++ b/sql/spatial.cc
@@ -17,7 +17,28 @@
#ifdef HAVE_SPATIAL
-#define MAX_DIGITS_IN_DOUBLE 16
+/*
+ exponential notation :
+ 1 sign
+ 1 number before the decimal point
+ 1 decimal point
+ 14 number of significant digits (see String::qs_append(double))
+ 1 'e' sign
+ 1 exponent sign
+ 3 exponent digits
+ ==
+ 22
+
+ "f" notation :
+ 1 optional 0
+ 1 sign
+ 14 number significant digits (see String::qs_append(double) )
+ 1 decimal point
+ ==
+ 17
+*/
+
+#define MAX_DIGITS_IN_DOUBLE 22
/***************************** Gis_class_info *******************************/
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index fb5fd2ec627..0cd46a7c6fb 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -64,11 +64,11 @@ Prelock_error_handler::handle_error(uint sql_errno,
if (sql_errno == ER_NO_SUCH_TABLE)
{
m_handled_errors++;
- return TRUE; // 'TRUE', as per coding style
+ return TRUE;
}
m_unhandled_errors++;
- return FALSE; // 'FALSE', as per coding style
+ return FALSE;
}
@@ -1476,7 +1476,7 @@ void close_temporary_tables(THD *thd)
*/
TABLE_LIST *find_table_in_list(TABLE_LIST *table,
- st_table_list *TABLE_LIST::*link,
+ TABLE_LIST *TABLE_LIST::*link,
const char *db_name,
const char *table_name)
{
@@ -3533,7 +3533,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
*/
for (tables= *start; tables ;tables= tables->next_global)
{
- safe_to_ignore_table= FALSE; // 'FALSE', as per coding style
+ safe_to_ignore_table= FALSE;
if (tables->lock_type == TL_WRITE_DEFAULT)
{
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 719c67aff6a..cab9ef94d6d 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -268,6 +268,39 @@ are stored in one block.
If join_results allocated new block(s) then we need call pack_cache again.
+7. Interface
+The query cache interfaces with the rest of the server code through 7
+functions:
+ 1. Query_cache::send_result_to_client
+ - Called before parsing and used to match a statement with the stored
+ queries hash.
+ If a match is found the cached result set is sent through repeated
+ calls to net_real_write. (note: calling thread doesn't have a regis-
+ tered result set writer: thd->net.query_cache_query=0)
+ 2. Query_cache::store_query
+ - Called just before handle_select() and is used to register a result
+ set writer to the statement currently being processed
+ (thd->net.query_cache_query).
+ 3. query_cache_insert
+ - Called from net_real_write to append a result set to a cached query
+ if (and only if) this query has a registered result set writer
+ (thd->net.query_cache_query).
+ 4. Query_cache::invalidate
+ - Called from various places to invalidate query cache based on data-
+ base, table and myisam file name. During an on going invalidation
+ the query cache is temporarily disabled.
+ 5. Query_cache::flush
+ - Used when a RESET QUERY CACHE is issued. This clears the entire
+ cache block by block.
+ 6. Query_cache::resize
+ - Used to change the available memory used by the query cache. This
+ will also invalidate the entrie query cache in one free operation.
+ 7. Query_cache::pack
+ - Used when a FLUSH QUERY CACHE is issued. This changes the order of
+ the used memory blocks in physical memory order and move all avail-
+ able memory to the 'bottom' of the memory.
+
+
TODO list:
- Delayed till after-parsing qache answer (for column rights processing)
@@ -615,49 +648,55 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
DBUG_VOID_RETURN;
STRUCT_LOCK(&query_cache.structure_guard_mutex);
+ bool interrupt;
+ query_cache.wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ return;
+ }
- if (unlikely(query_cache.query_cache_size == 0 ||
- query_cache.flush_in_progress))
+ Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query;
+ if (!query_block)
{
+ /*
+ We lost the writer and the currently processed query has been
+ invalidated; there is nothing left to do.
+ */
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
DBUG_VOID_RETURN;
}
- Query_cache_block *query_block = ((Query_cache_block*)
- net->query_cache_query);
- if (query_block)
- {
- Query_cache_query *header = query_block->query();
- Query_cache_block *result = header->result();
+ Query_cache_query *header= query_block->query();
+ Query_cache_block *result= header->result();
- DUMP(&query_cache);
- BLOCK_LOCK_WR(query_block);
- DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
+ DUMP(&query_cache);
+ BLOCK_LOCK_WR(query_block);
+ DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
- /*
- On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be
- done by query_cache.append_result_data if success (if not we need
- query_cache.structure_guard_mutex locked to free query)
- */
- if (!query_cache.append_result_data(&result, length, (uchar*) packet,
- query_block))
- {
- DBUG_PRINT("warning", ("Can't append data"));
- header->result(result);
- DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block));
- // The following call will remove the lock on query_block
- query_cache.free_query(query_block);
- // append_result_data no success => we need unlock
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
- DBUG_VOID_RETURN;
- }
+ /*
+ On success, STRUCT_UNLOCK is done by append_result_data. Otherwise, we
+ still need structure_guard_mutex to free the query, and therefore unlock
+ it later in this function.
+ */
+ if (!query_cache.append_result_data(&result, length, (uchar*) packet,
+ query_block))
+ {
+ DBUG_PRINT("warning", ("Can't append data"));
header->result(result);
- header->last_pkt_nr= net->pkt_nr;
- BLOCK_UNLOCK_WR(query_block);
- DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
- }
- else
+ DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block));
+ // The following call will remove the lock on query_block
+ query_cache.free_query(query_block);
+ // append_result_data no success => we need unlock
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
+
+ header->result(result);
+ header->last_pkt_nr= net->pkt_nr;
+ BLOCK_UNLOCK_WR(query_block);
+ DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
+
DBUG_VOID_RETURN;
}
@@ -671,17 +710,21 @@ void query_cache_abort(NET *net)
DBUG_VOID_RETURN;
STRUCT_LOCK(&query_cache.structure_guard_mutex);
-
- if (unlikely(query_cache.query_cache_size == 0 ||
- query_cache.flush_in_progress))
+ bool interrupt;
+ query_cache.wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
{
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
DBUG_VOID_RETURN;
}
+ /*
+ While we were waiting another thread might have changed the status
+ of the writer. Make sure the writer still exists before continue.
+ */
Query_cache_block *query_block= ((Query_cache_block*)
net->query_cache_query);
- if (query_block) // Test if changed by other thread
+ if (query_block)
{
DUMP(&query_cache);
BLOCK_LOCK_WR(query_block);
@@ -713,13 +756,22 @@ void query_cache_end_of_result(THD *thd)
STRUCT_LOCK(&query_cache.structure_guard_mutex);
- if (unlikely(query_cache.query_cache_size == 0 ||
- query_cache.flush_in_progress))
- goto end;
+ bool interrupt;
+ query_cache.wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
query_block= ((Query_cache_block*) thd->net.query_cache_query);
if (query_block)
{
+ /*
+ The writer is still present; finish last result block by chopping it to
+ suitable size if needed and setting block type. Since this is the last
+ block, the writer should be dropped.
+ */
DUMP(&query_cache);
BLOCK_LOCK_WR(query_block);
Query_cache_query *header= query_block->query();
@@ -741,19 +793,22 @@ void query_cache_end_of_result(THD *thd)
query_cache.wreck() switched query cache off but left content
untouched for investigation (it is debugging method).
*/
- goto end;
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
}
#endif
header->found_rows(current_thd->limit_found_rows);
header->result()->type= Query_cache_block::RESULT;
+
+ /* Drop the writer. */
header->writer(0);
thd->net.query_cache_query= 0;
+
BLOCK_UNLOCK_WR(query_block);
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
}
-end:
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -765,6 +820,18 @@ void query_cache_invalidate_by_MyISAM_filename(const char *filename)
}
+/*
+ The following function forms part of the C plugin API
+*/
+extern "C"
+void mysql_query_cache_invalidate4(THD *thd,
+ const char *key, unsigned key_length,
+ int using_trx)
+{
+ query_cache.invalidate(thd, key, (uint32) key_length, (my_bool) using_trx);
+}
+
+
/*****************************************************************************
Query_cache methods
*****************************************************************************/
@@ -801,9 +868,9 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
DBUG_ASSERT(initialized);
STRUCT_LOCK(&structure_guard_mutex);
- while (flush_in_progress)
- pthread_cond_wait(&COND_flush_finished, &structure_guard_mutex);
- flush_in_progress= TRUE;
+ while (is_flushing())
+ pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
+ m_cache_status= Query_cache::FLUSH_IN_PROGRESS;
STRUCT_UNLOCK(&structure_guard_mutex);
free_cache();
@@ -814,8 +881,8 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
DBUG_EXECUTE("check_querycache",check_integrity(0););
STRUCT_LOCK(&structure_guard_mutex);
- flush_in_progress= FALSE;
- pthread_cond_signal(&COND_flush_finished);
+ m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
+ pthread_cond_signal(&COND_cache_status_changed);
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(new_query_cache_size);
@@ -910,8 +977,13 @@ def_week_frmt: %lu",
ha_release_temporary_latches(thd);
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size == 0 || flush_in_progress)
+ if (query_cache_size == 0 || is_flushing())
{
+ /*
+ A table- or a full flush operation can potentially take a long time to
+ finish. We choose not to wait for them and skip caching statements
+ instead.
+ */
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -954,7 +1026,7 @@ def_week_frmt: %lu",
Query_cache_block *query_block;
query_block= write_block_data(tot_length, (uchar*) thd->query,
ALIGN_SIZE(sizeof(Query_cache_query)),
- Query_cache_block::QUERY, local_tables, 1);
+ Query_cache_block::QUERY, local_tables);
if (query_block != 0)
{
DBUG_PRINT("qcache", ("query block 0x%lx allocated, %lu",
@@ -1088,13 +1160,21 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
}
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size == 0 || flush_in_progress)
+
+ if (query_cache_size == 0)
+ goto err_unlock;
+
+ if (is_flushing())
{
- DBUG_PRINT("qcache", ("query cache disabled"));
+ /* Return; Query cache is temporarily disabled while we flush. */
+ DBUG_PRINT("qcache",("query cache disabled"));
goto err_unlock;
}
- /* Check that we haven't forgot to reset the query cache variables */
+ /*
+ Check that we haven't forgot to reset the query cache variables;
+ make sure there are no attached query cache writer to this thread.
+ */
DBUG_ASSERT(thd->net.query_cache_query == 0);
Query_cache_block *query_block;
@@ -1267,7 +1347,9 @@ def_week_frmt: %lu",
("Handler require invalidation queries of %s.%s %lu-%lu",
table_list.db, table_list.alias,
(ulong) engine_data, (ulong) table->engine_data()));
- invalidate_table((uchar *) table->db(), table->key_length());
+ invalidate_table_internal(thd,
+ (uchar *) table->db(),
+ table->key_length());
}
else
thd->lex->safe_to_cache_query= 0; // Don't try to cache this
@@ -1330,32 +1412,26 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
my_bool using_transactions)
{
DBUG_ENTER("Query_cache::invalidate (table list)");
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- DUMP(this);
- using_transactions= using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
- for (; tables_used; tables_used= tables_used->next_local)
- {
- DBUG_ASSERT(!using_transactions || tables_used->table!=0);
- if (tables_used->derived)
- continue;
- if (using_transactions &&
- (tables_used->table->file->table_cache_type() ==
- HA_CACHE_TBL_TRANSACT))
- /*
- Tables_used->table can't be 0 in transaction.
- Only 'drop' invalidate not opened table, but 'drop'
- force transaction finish.
- */
- thd->add_changed_table(tables_used->table);
- else
- invalidate_table(tables_used);
- }
+ using_transactions= using_transactions &&
+ (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ for (; tables_used; tables_used= tables_used->next_local)
+ {
+ DBUG_ASSERT(!using_transactions || tables_used->table!=0);
+ if (tables_used->derived)
+ continue;
+ if (using_transactions &&
+ (tables_used->table->file->table_cache_type() ==
+ HA_CACHE_TBL_TRANSACT))
+ /*
+ tables_used->table can't be 0 in transaction.
+ Only 'drop' invalidate not opened table, but 'drop'
+ force transaction finish.
+ */
+ thd->add_changed_table(tables_used->table);
+ else
+ invalidate_table(thd, tables_used);
}
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -1363,21 +1439,13 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
{
DBUG_ENTER("Query_cache::invalidate (changed table list)");
- if (tables_used)
+ THD *thd= current_thd;
+ for (; tables_used; tables_used= tables_used->next)
{
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- DUMP(this);
- for (; tables_used; tables_used= tables_used->next)
- {
- invalidate_table((uchar*) tables_used->key, tables_used->key_length);
- DBUG_PRINT("qcache", ("db: %s table: %s", tables_used->key,
- tables_used->key+
- strlen(tables_used->key)+1));
- }
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
+ invalidate_table(thd, (uchar*) tables_used->key, tables_used->key_length);
+ DBUG_PRINT("qcache", ("db: %s table: %s", tables_used->key,
+ tables_used->key+
+ strlen(tables_used->key)+1));
}
DBUG_VOID_RETURN;
}
@@ -1396,20 +1464,14 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used)
{
DBUG_ENTER("Query_cache::invalidate_locked_for_write");
- if (tables_used)
+ for (; tables_used; tables_used= tables_used->next_local)
{
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
+ if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE) &&
+ tables_used->table)
{
- DUMP(this);
- for (; tables_used; tables_used= tables_used->next_local)
- {
- if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE) &&
- tables_used->table)
- invalidate_table(tables_used->table);
- }
+ THD *thd= current_thd;
+ invalidate_table(thd, tables_used->table);
}
- STRUCT_UNLOCK(&structure_guard_mutex);
}
DBUG_VOID_RETURN;
}
@@ -1423,18 +1485,14 @@ void Query_cache::invalidate(THD *thd, TABLE *table,
{
DBUG_ENTER("Query_cache::invalidate (table)");
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- using_transactions= using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
- if (using_transactions &&
- (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
- thd->add_changed_table(table);
- else
- invalidate_table(table);
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
+ using_transactions= using_transactions &&
+ (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ if (using_transactions &&
+ (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
+ thd->add_changed_table(table);
+ else
+ invalidate_table(thd, table);
+
DBUG_VOID_RETURN;
}
@@ -1443,31 +1501,80 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
my_bool using_transactions)
{
DBUG_ENTER("Query_cache::invalidate (key)");
-
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- using_transactions= using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
- if (using_transactions) // used for innodb => has_transactions() is TRUE
- thd->add_changed_table(key, key_length);
- else
- invalidate_table((uchar*)key, key_length);
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
+
+ using_transactions= using_transactions &&
+ (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ if (using_transactions) // used for innodb => has_transactions() is TRUE
+ thd->add_changed_table(key, key_length);
+ else
+ invalidate_table(thd, (uchar*)key, key_length);
DBUG_VOID_RETURN;
}
+
+/**
+ @brief Synchronize the thread with any flushing operations.
+
+ This helper function is called whenever a thread needs to operate on the
+ query cache structure (example: during invalidation). If a table flush is in
+ progress this function will wait for it to stop. If a full flush is in
+ progress, the function will set the interrupt parameter to indicate that the
+ current operation is redundant and should be interrupted.
+
+ @param[out] interrupt This out-parameter will be set to TRUE if the calling
+ function is redundant and should be interrupted.
+
+ @return If the interrupt-parameter is TRUE then m_cache_status is set to
+ NO_FLUSH_IN_PROGRESS. If the interrupt-parameter is FALSE then
+ m_cache_status is set to FLUSH_IN_PROGRESS.
+ The structure_guard_mutex will in any case be locked.
+*/
+
+void Query_cache::wait_while_table_flush_is_in_progress(bool *interrupt)
+{
+ while (is_flushing())
+ {
+ /*
+ If there already is a full flush in progress query cache isn't enabled
+ and additional flushes are redundant; just return instead.
+ */
+ if (m_cache_status == Query_cache::FLUSH_IN_PROGRESS)
+ {
+ *interrupt= TRUE;
+ return;
+ }
+ /*
+ If a table flush is in progress; wait on cache status to change.
+ */
+ if (m_cache_status == Query_cache::TABLE_FLUSH_IN_PROGRESS)
+ pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
+ }
+ *interrupt= FALSE;
+}
+
+
/**
@brief Remove all cached queries that uses the given database
*/
+
void Query_cache::invalidate(char *db)
{
bool restart= FALSE;
DBUG_ENTER("Query_cache::invalidate (db)");
+
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
+ bool interrupt;
+ wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ return;
+ }
+
+ THD *thd= current_thd;
+
+ if (query_cache_size > 0)
{
if (tables_blocks)
{
@@ -1479,7 +1586,10 @@ void Query_cache::invalidate(char *db)
Query_cache_block *next= table_block->next;
Query_cache_table *table = table_block->table();
if (strcmp(table->db(),db) == 0)
- invalidate_table(table_block);
+ {
+ Query_cache_block_table *list_root= table_block->table(0);
+ invalidate_query_block_list(thd,list_root);
+ }
table_block= next;
@@ -1526,21 +1636,12 @@ void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
{
DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename");
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- /* Calculate the key outside the lock to make the lock shorter */
- char key[MAX_DBKEY_LENGTH];
- uint32 db_length;
- uint key_length= filename_2_table_key(key, filename, &db_length);
- Query_cache_block *table_block;
- if ((table_block = (Query_cache_block*) hash_search(&tables,
- (uchar*) key,
- key_length)))
- invalidate_table(table_block);
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
-
+ /* Calculate the key outside the lock to make the lock shorter */
+ char key[MAX_DBKEY_LENGTH];
+ uint32 db_length;
+ uint key_length= filename_2_table_key(key, filename, &db_length);
+ THD *thd= current_thd;
+ invalidate_table(thd,(uchar *)key, key_length);
DBUG_VOID_RETURN;
}
@@ -1562,16 +1663,43 @@ void Query_cache::flush()
DBUG_VOID_RETURN;
}
- /* Join result in cache in 1 block (if result length > join_limit) */
+
+/**
+ @brief Rearrange the memory blocks and join result in cache in 1 block (if
+ result length > join_limit)
+
+ @param[in] join_limit If the minimum length of a result block to be joined.
+ @param[in] iteration_limit The maximum number of packing and joining
+ sequences.
+
+*/
void Query_cache::pack(ulong join_limit, uint iteration_limit)
{
DBUG_ENTER("Query_cache::pack");
+
+ bool interrupt;
+ STRUCT_LOCK(&structure_guard_mutex);
+ wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
+
+ if (query_cache_size == 0)
+ {
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
+
uint i = 0;
do
{
pack_cache();
} while ((++i < iteration_limit) && join_results(join_limit));
+
+ STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -1590,7 +1718,7 @@ void Query_cache::destroy()
free_cache();
STRUCT_UNLOCK(&structure_guard_mutex);
- pthread_cond_destroy(&COND_flush_finished);
+ pthread_cond_destroy(&COND_cache_status_changed);
pthread_mutex_destroy(&structure_guard_mutex);
initialized = 0;
}
@@ -1606,8 +1734,8 @@ void Query_cache::init()
{
DBUG_ENTER("Query_cache::init");
pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST);
- pthread_cond_init(&COND_flush_finished, NULL);
- flush_in_progress= FALSE;
+ pthread_cond_init(&COND_cache_status_changed, NULL);
+ m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
initialized = 1;
DBUG_VOID_RETURN;
}
@@ -1809,9 +1937,10 @@ void Query_cache::make_disabled()
/**
@class Query_cache
@brief Free all resources allocated by the cache.
- @details This function frees all resources allocated by the cache. You
- have to call init_cache() before using the cache again. This function requires
- the structure_guard_mutex to be locked.
+
+ This function frees all resources allocated by the cache. You
+ have to call init_cache() before using the cache again. This function
+ requires the structure_guard_mutex to be locked.
*/
void Query_cache::free_cache()
@@ -1830,24 +1959,17 @@ void Query_cache::free_cache()
*****************************************************************************/
-/*
- flush_cache() - flush the cache.
-
- SYNOPSIS
- flush_cache()
-
- DESCRIPTION
- This function will flush cache contents. It assumes we have
- 'structure_guard_mutex' locked. The function sets the
- flush_in_progress flag and releases the lock, so other threads may
- proceed skipping the cache as if it is disabled. Concurrent
- flushes are performed in turn.
-
- After flush_cache() call, the cache is flushed, all the freed
- memory is accumulated in bin[0], and the 'structure_guard_mutex'
- is locked. However, since we could release the mutex during
- execution, the rest of the cache state could have been changed,
- and should not be relied on.
+/**
+ @brief Flush the cache.
+
+ This function will flush cache contents. It assumes we have
+ 'structure_guard_mutex' locked. The function sets the m_cache_status flag and
+ releases the lock, so other threads may proceed skipping the cache as if it
+ is disabled. Concurrent flushes are performed in turn.
+ After flush_cache() call, the cache is flushed, all the freed memory is
+ accumulated in bin[0], and the 'structure_guard_mutex' is locked. However,
+ since we could release the mutex during execution, the rest of the cache
+ state could have been changed, and should not be relied on.
*/
void Query_cache::flush_cache()
@@ -1859,15 +1981,15 @@ void Query_cache::flush_cache()
Query_cache::free_cache()) depends on the fact that after the
flush the cache is empty.
*/
- while (flush_in_progress)
- pthread_cond_wait(&COND_flush_finished, &structure_guard_mutex);
+ while (is_flushing())
+ pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
/*
- Setting 'flush_in_progress' will prevent other threads from using
+ Setting 'FLUSH_IN_PROGRESS' will prevent other threads from using
the cache while we are in the middle of the flush, and we release
the lock so that other threads won't block.
*/
- flush_in_progress= TRUE;
+ m_cache_status= Query_cache::FLUSH_IN_PROGRESS;
STRUCT_UNLOCK(&structure_guard_mutex);
my_hash_reset(&queries);
@@ -1878,8 +2000,8 @@ void Query_cache::flush_cache()
}
STRUCT_LOCK(&structure_guard_mutex);
- flush_in_progress= FALSE;
- pthread_cond_signal(&COND_flush_finished);
+ m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
+ pthread_cond_signal(&COND_cache_status_changed);
}
/*
@@ -1897,7 +2019,7 @@ my_bool Query_cache::free_old_query()
sequence is breached.
Also we don't need remove locked queries at this point.
*/
- Query_cache_block *query_block = 0;
+ Query_cache_block *query_block= 0;
if (queries_blocks != 0)
{
Query_cache_block *block = queries_blocks;
@@ -2035,8 +2157,7 @@ Query_cache_block *
Query_cache::write_block_data(ulong data_len, uchar* data,
ulong header_len,
Query_cache_block::block_type type,
- TABLE_COUNTER_TYPE ntab,
- my_bool under_guard)
+ TABLE_COUNTER_TYPE ntab)
{
ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
ALIGN_SIZE(ntab*sizeof(Query_cache_block_table)) +
@@ -2046,9 +2167,8 @@ Query_cache::write_block_data(ulong data_len, uchar* data,
DBUG_ENTER("Query_cache::write_block_data");
DBUG_PRINT("qcache", ("data: %ld, header: %ld, all header: %ld",
data_len, header_len, all_headers_len));
- Query_cache_block *block = allocate_block(max(align_len,
- min_allocation_unit),
- 1, 0, under_guard);
+ Query_cache_block *block= allocate_block(max(align_len,
+ min_allocation_unit),1, 0);
if (block != 0)
{
block->type = type;
@@ -2265,8 +2385,7 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
if (!(new_block= allocate_block(max(min_size, align_len),
min_result_data_size == 0,
- all_headers_len + min_result_data_size,
- 1)))
+ all_headers_len + min_result_data_size)))
{
DBUG_PRINT("warning", ("Can't allocate block for results"));
DBUG_RETURN(FALSE);
@@ -2308,51 +2427,109 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
Invalidate the first table in the table_list
*/
-void Query_cache::invalidate_table(TABLE_LIST *table_list)
+void Query_cache::invalidate_table(THD *thd, TABLE_LIST *table_list)
{
if (table_list->table != 0)
- invalidate_table(table_list->table); // Table is open
+ invalidate_table(thd, table_list->table); // Table is open
else
{
char key[MAX_DBKEY_LENGTH];
uint key_length;
- Query_cache_block *table_block;
+
key_length=(uint) (strmov(strmov(key,table_list->db)+1,
table_list->table_name) -key)+ 1;
// We don't store temporary tables => no key_length+=4 ...
- if ((table_block = (Query_cache_block*)
- hash_search(&tables,(uchar*) key,key_length)))
- invalidate_table(table_block);
+ invalidate_table(thd, (uchar *)key, key_length);
}
}
-void Query_cache::invalidate_table(TABLE *table)
+void Query_cache::invalidate_table(THD *thd, TABLE *table)
{
- invalidate_table((uchar*) table->s->table_cache_key.str,
+ invalidate_table(thd, (uchar*) table->s->table_cache_key.str,
table->s->table_cache_key.length);
}
-void Query_cache::invalidate_table(uchar * key, uint32 key_length)
+void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
+{
+ bool interrupt;
+ STRUCT_LOCK(&structure_guard_mutex);
+ wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ return;
+ }
+
+ /*
+ Setting 'TABLE_FLUSH_IN_PROGRESS' will temporarily disable the cache
+ so that structural changes to cache won't block the entire server.
+ However, threads requesting to change the query cache will still have
+ to wait for the flush to finish.
+ */
+ m_cache_status= Query_cache::TABLE_FLUSH_IN_PROGRESS;
+ STRUCT_UNLOCK(&structure_guard_mutex);
+
+ if (query_cache_size > 0)
+ invalidate_table_internal(thd, key, key_length);
+
+ STRUCT_LOCK(&structure_guard_mutex);
+ m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
+
+ /*
+ net_real_write might be waiting on a change on the m_cache_status
+ variable.
+ */
+ pthread_cond_signal(&COND_cache_status_changed);
+ STRUCT_UNLOCK(&structure_guard_mutex);
+}
+
+
+/**
+ Try to locate and invalidate a table by name.
+ The caller must ensure that no other thread is trying to work with
+ the query cache when this function is executed.
+
+ @pre structure_guard_mutex is acquired or TABLE_FLUSH_IN_PROGRESS is set.
+*/
+
+void
+Query_cache::invalidate_table_internal(THD *thd, uchar *key, uint32 key_length)
{
- Query_cache_block *table_block;
- if ((table_block = ((Query_cache_block*)
- hash_search(&tables, key, key_length))))
- invalidate_table(table_block);
+ Query_cache_block *table_block=
+ (Query_cache_block*)hash_search(&tables, key, key_length);
+ if (table_block)
+ {
+ Query_cache_block_table *list_root= table_block->table(0);
+ invalidate_query_block_list(thd, list_root);
+ }
}
-void Query_cache::invalidate_table(Query_cache_block *table_block)
+/**
+ @brief Invalidate a linked list of query cache blocks.
+
+ Each block tries to aquire a block level lock before
+ free_query is a called. This function will in turn affect
+ related table- and result-blocks.
+
+ @param[in,out] thd Thread context.
+ @param[in,out] list_root A pointer to a circular list of query blocks.
+
+*/
+
+void
+Query_cache::invalidate_query_block_list(THD *thd,
+ Query_cache_block_table *list_root)
{
- Query_cache_block_table *list_root = table_block->table(0);
while (list_root->next != list_root)
{
- Query_cache_block *query_block = list_root->next->block();
+ Query_cache_block *query_block= list_root->next->block();
BLOCK_LOCK_WR(query_block);
free_query(query_block);
+ DBUG_EXECUTE_IF("debug_cache_locks", sleep(10););
}
}
-
/*
Register given table list begining with given position in tables table of
block
@@ -2495,9 +2672,13 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block,
return (n);
}
-/*
- Insert used tablename in cache
- Returns 0 on error
+
+/**
+ @brief Insert used table name into the cache.
+
+ @return Error status
+ @retval FALSE On error
+ @retval TRUE On success
*/
my_bool
@@ -2511,9 +2692,10 @@ Query_cache::insert_table(uint key_len, char *key,
DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d",
(ulong)node, key_len));
- Query_cache_block *table_block = ((Query_cache_block *)
- hash_search(&tables, (uchar*) key,
- key_len));
+ THD *thd= current_thd;
+
+ Query_cache_block *table_block=
+ (Query_cache_block *)hash_search(&tables, (uchar*) key, key_len);
if (table_block &&
table_block->table()->engine_data() != engine_data)
@@ -2528,7 +2710,11 @@ Query_cache::insert_table(uint key_len, char *key,
as far as we delete all queries with this table, table block will be
deleted, too
*/
- invalidate_table(table_block);
+ {
+ Query_cache_block_table *list_root= table_block->table(0);
+ invalidate_query_block_list(thd, list_root);
+ }
+
table_block= 0;
}
@@ -2536,21 +2722,29 @@ Query_cache::insert_table(uint key_len, char *key,
{
DBUG_PRINT("qcache", ("new table block from 0x%lx (%u)",
(ulong) key, (int) key_len));
- table_block = write_block_data(key_len, (uchar*) key,
- ALIGN_SIZE(sizeof(Query_cache_table)),
- Query_cache_block::TABLE,
- 1, 1);
+ table_block= write_block_data(key_len, (uchar*) key,
+ ALIGN_SIZE(sizeof(Query_cache_table)),
+ Query_cache_block::TABLE, 1);
if (table_block == 0)
{
DBUG_PRINT("qcache", ("Can't write table name to cache"));
DBUG_RETURN(0);
}
- Query_cache_table *header = table_block->table();
+ Query_cache_table *header= table_block->table();
double_linked_list_simple_include(table_block,
- &tables_blocks);
- Query_cache_block_table *list_root = table_block->table(0);
- list_root->n = 0;
- list_root->next = list_root->prev = list_root;
+ &tables_blocks);
+ /*
+ First node in the Query_cache_block_table-chain is the table-type
+ block. This block will only have one Query_cache_block_table (n=0).
+ */
+ Query_cache_block_table *list_root= table_block->table(0);
+ list_root->n= 0;
+
+ /*
+ The node list is circular in nature.
+ */
+ list_root->next= list_root->prev= list_root;
+
if (my_hash_insert(&tables, (const uchar *) table_block))
{
DBUG_PRINT("qcache", ("Can't insert table to hash"));
@@ -2558,20 +2752,37 @@ Query_cache::insert_table(uint key_len, char *key,
free_memory_block(table_block);
DBUG_RETURN(0);
}
- char *db = header->db();
+ char *db= header->db();
header->table(db + db_length + 1);
header->key_length(key_len);
header->type(cache_type);
header->callback(callback);
header->engine_data(engine_data);
+
+ /*
+ We insert this table without the assumption that it isn't refrenenced by
+ any queries.
+ */
+ header->m_cached_query_count= 0;
}
- Query_cache_block_table *list_root = table_block->table(0);
- node->next = list_root->next;
- list_root->next = node;
- node->next->prev = node;
- node->prev = list_root;
- node->parent = table_block->table();
+ /*
+ Table is now in the cache; link the table_block-node associated
+ with the currently processed query into the chain of queries depending
+ on the cached table.
+ */
+ Query_cache_block_table *list_root= table_block->table(0);
+ node->next= list_root->next;
+ list_root->next= node;
+ node->next->prev= node;
+ node->prev= list_root;
+ node->parent= table_block->table();
+ /*
+ Increase the counter to keep track on how long this chain
+ of queries is.
+ */
+ Query_cache_table *table_block_data= table_block->table();
+ table_block_data->m_cached_query_count++;
DBUG_RETURN(1);
}
@@ -2579,15 +2790,27 @@ Query_cache::insert_table(uint key_len, char *key,
void Query_cache::unlink_table(Query_cache_block_table *node)
{
DBUG_ENTER("Query_cache::unlink_table");
- node->prev->next = node->next;
- node->next->prev = node->prev;
- Query_cache_block_table *neighbour = node->next;
+ node->prev->next= node->next;
+ node->next->prev= node->prev;
+ Query_cache_block_table *neighbour= node->next;
+ Query_cache_table *table_block_data= node->parent;
+ table_block_data->m_cached_query_count--;
+
+ DBUG_ASSERT(table_block_data->m_cached_query_count >= 0);
+
if (neighbour->next == neighbour)
{
- // list is empty (neighbor is root of list)
- Query_cache_block *table_block = neighbour->block();
+ DBUG_ASSERT(table_block_data->m_cached_query_count == 0);
+ /*
+ If neighbor is root of list, the list is empty.
+ The root of the list is always a table-type block
+ which contain exactly one Query_cache_block_table
+ node object, thus we can use the block() method
+ to calculate the Query_cache_block address.
+ */
+ Query_cache_block *table_block= neighbour->block();
double_linked_list_exclude(table_block,
- &tables_blocks);
+ &tables_blocks);
hash_delete(&tables,(uchar *) table_block);
free_memory_block(table_block);
}
@@ -2599,12 +2822,11 @@ void Query_cache::unlink_table(Query_cache_block_table *node)
*****************************************************************************/
Query_cache_block *
-Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
- my_bool under_guard)
+Query_cache::allocate_block(ulong len, my_bool not_less, ulong min)
{
DBUG_ENTER("Query_cache::allocate_block");
- DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu, uder_guard %d",
- len, not_less,min,under_guard));
+ DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu",
+ len, not_less,min));
if (len >= min(query_cache_size, query_cache_limit))
{
@@ -2613,17 +2835,6 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
DBUG_RETURN(0); // in any case we don't have such piece of memory
}
- if (!under_guard)
- {
- STRUCT_LOCK(&structure_guard_mutex);
-
- if (unlikely(query_cache.query_cache_size == 0 || flush_in_progress))
- {
- STRUCT_UNLOCK(&structure_guard_mutex);
- DBUG_RETURN(0);
- }
- }
-
/* Free old queries until we have enough memory to store this block */
Query_cache_block *block;
do
@@ -2638,8 +2849,6 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
split_block(block,ALIGN_SIZE(len));
}
- if (!under_guard)
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(block);
}
@@ -2874,9 +3083,7 @@ uint Query_cache::find_bin(ulong size)
}
uint bin = steps[left].idx -
(uint)((size - steps[left].size)/steps[left].increment);
-#ifndef DBUG_OFF
- bins_dump();
-#endif
+
DBUG_PRINT("qcache", ("bin %u step %u, size %lu step size %lu",
bin, left, size, steps[left].size));
DBUG_RETURN(bin);
@@ -3165,18 +3372,17 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
Packing
*****************************************************************************/
+
+/**
+ @brief Rearrange all memory blocks so that free memory joins at the
+ 'bottom' of the allocated memory block containing all cache data.
+ @see Query_cache::pack(ulong join_limit, uint iteration_limit)
+*/
+
void Query_cache::pack_cache()
{
DBUG_ENTER("Query_cache::pack_cache");
- STRUCT_LOCK(&structure_guard_mutex);
-
- if (unlikely(query_cache_size == 0 || flush_in_progress))
- {
- STRUCT_UNLOCK(&structure_guard_mutex);
- DBUG_VOID_RETURN;
- }
-
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
uchar *border = 0;
@@ -3210,7 +3416,6 @@ void Query_cache::pack_cache()
}
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -3485,8 +3690,7 @@ my_bool Query_cache::join_results(ulong join_limit)
my_bool has_moving = 0;
DBUG_ENTER("Query_cache::join_results");
- STRUCT_LOCK(&structure_guard_mutex);
- if (queries_blocks != 0 && !flush_in_progress)
+ if (queries_blocks != 0)
{
DBUG_ASSERT(query_cache_size > 0);
Query_cache_block *block = queries_blocks;
@@ -3549,7 +3753,6 @@ my_bool Query_cache::join_results(ulong join_limit)
block = block->next;
} while ( block != queries_blocks );
}
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(has_moving);
}
@@ -3785,6 +3988,14 @@ void Query_cache::tables_dump()
}
+/**
+ @brief Checks integrity of the various linked lists
+
+ @return Error status code
+ @retval FALSE Query cache is operational.
+ @retval TRUE Query cache is broken.
+*/
+
my_bool Query_cache::check_integrity(bool locked)
{
my_bool result = 0;
@@ -3794,14 +4005,8 @@ my_bool Query_cache::check_integrity(bool locked)
if (!locked)
STRUCT_LOCK(&structure_guard_mutex);
- if (unlikely(query_cache_size == 0 || flush_in_progress))
- {
- if (!locked)
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
-
- DBUG_PRINT("qcache", ("Query Cache not initialized"));
- DBUG_RETURN(0);
- }
+ while (is_flushing())
+ pthread_cond_wait(&COND_cache_status_changed,&structure_guard_mutex);
if (hash_check(&queries))
{
diff --git a/sql/sql_cache.h b/sql/sql_cache.h
index 3c5d784ce94..c4c7e1dbc5e 100644
--- a/sql/sql_cache.h
+++ b/sql/sql_cache.h
@@ -65,17 +65,44 @@ struct Query_cache_query;
struct Query_cache_result;
class Query_cache;
+/**
+ @brief This class represents a node in the linked chain of queries
+ belonging to one table.
+ @note The root of this linked list is not a query-type block, but the table-
+ type block which all queries has in common.
+*/
struct Query_cache_block_table
{
Query_cache_block_table() {} /* Remove gcc warning */
- TABLE_COUNTER_TYPE n; // numbr in table (from 0)
+
+ /**
+ This node holds a position in a static table list belonging
+ to the associated query (base 0).
+ */
+ TABLE_COUNTER_TYPE n;
+
+ /**
+ Pointers to the next and previous node, linking all queries with
+ a common table.
+ */
Query_cache_block_table *next, *prev;
+
+ /**
+ A pointer to the table-type block which all
+ linked queries has in common.
+ */
Query_cache_table *parent;
+
+ /**
+ A method to calculate the address of the query cache block
+ owning this node. The purpose of this calculation is to
+ make it easier to move the query cache block without having
+ to modify all the pointer addresses.
+ */
inline Query_cache_block *block();
};
-
struct Query_cache_block
{
Query_cache_block() {} /* Remove gcc warning */
@@ -151,6 +178,11 @@ struct Query_cache_table
/* data need by some engines */
ulonglong engine_data_buff;
+ /**
+ The number of queries depending of this table.
+ */
+ int32 m_cached_query_count;
+
inline char *db() { return (char *) data(); }
inline char *table() { return tbl; }
inline void table(char *table_arg) { tbl= table_arg; }
@@ -237,11 +269,17 @@ public:
ulong free_memory, queries_in_cache, hits, inserts, refused,
free_memory_blocks, total_blocks, lowmem_prunes;
+
private:
- pthread_cond_t COND_flush_finished;
- bool flush_in_progress;
+ pthread_cond_t COND_cache_status_changed;
+
+ enum Cache_status { NO_FLUSH_IN_PROGRESS, FLUSH_IN_PROGRESS,
+ TABLE_FLUSH_IN_PROGRESS };
+
+ Cache_status m_cache_status;
void free_query_internal(Query_cache_block *point);
+ void invalidate_table_internal(THD *thd, uchar *key, uint32 key_length);
protected:
/*
@@ -253,7 +291,7 @@ protected:
2. query block (for operation inside query (query block/results))
Thread doing cache flush releases the mutex once it sets
- flush_in_progress flag, so other threads may bypass the cache as
+ m_cache_status flag, so other threads may bypass the cache as
if it is disabled, not waiting for reset to finish. The exception
is other threads that were going to do cache flush---they'll wait
till the end of a flush operation.
@@ -270,6 +308,7 @@ protected:
/* options */
ulong min_allocation_unit, min_result_data_size;
uint def_query_hash_size, def_table_hash_size;
+
uint mem_bin_num, mem_bin_steps; // See at init_cache & find_bin
my_bool initialized;
@@ -295,10 +334,13 @@ protected:
ulong data_len,
Query_cache_block *query_block,
my_bool first_block);
- void invalidate_table(TABLE_LIST *table);
- void invalidate_table(TABLE *table);
- void invalidate_table(uchar *key, uint32 key_length);
- void invalidate_table(Query_cache_block *table_block);
+ void invalidate_table(THD *thd, TABLE_LIST *table);
+ void invalidate_table(THD *thd, TABLE *table);
+ void invalidate_table(THD *thd, uchar *key, uint32 key_length);
+ void invalidate_table(THD *thd, Query_cache_block *table_block);
+ void invalidate_query_block_list(THD *thd,
+ Query_cache_block_table *list_root);
+
TABLE_COUNTER_TYPE
register_tables_from_list(TABLE_LIST *tables_used,
TABLE_COUNTER_TYPE counter,
@@ -337,6 +379,8 @@ protected:
Query_cache_block *pprev);
my_bool join_results(ulong join_limit);
+ void wait_while_table_flush_is_in_progress(bool *interrupt);
+
/*
Following function control structure_guard_mutex
by themself or don't need structure_guard_mutex
@@ -347,8 +391,7 @@ protected:
Query_cache_block *write_block_data(ulong data_len, uchar* data,
ulong header_len,
Query_cache_block::block_type type,
- TABLE_COUNTER_TYPE ntab = 0,
- my_bool under_guard=0);
+ TABLE_COUNTER_TYPE ntab = 0);
my_bool append_result_data(Query_cache_block **result,
ulong data_len, uchar* data,
Query_cache_block *parent);
@@ -360,8 +403,7 @@ protected:
inline ulong get_min_first_result_data_size();
inline ulong get_min_append_result_data_size();
Query_cache_block *allocate_block(ulong len, my_bool not_less,
- ulong min,
- my_bool under_guard=0);
+ ulong min);
/*
If query is cacheable return number tables in query
(query without tables not cached)
@@ -424,6 +466,11 @@ protected:
friend void query_cache_end_of_result(THD *thd);
friend void query_cache_abort(NET *net);
+ bool is_flushing(void)
+ {
+ return (m_cache_status != Query_cache::NO_FLUSH_IN_PROGRESS);
+ }
+
/*
The following functions are only used when debugging
We don't protect these with ifndef DBUG_OFF to not have to recompile
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 40b37ed7405..da975ee3103 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -207,6 +207,31 @@ Open_tables_state::Open_tables_state(ulong version_arg)
The following functions form part of the C plugin API
*/
+extern "C" int mysql_tmpfile(const char *prefix)
+{
+ char filename[FN_REFLEN];
+ File fd = create_temp_file(filename, mysql_tmpdir, prefix,
+#ifdef __WIN__
+ O_BINARY | O_TRUNC | O_SEQUENTIAL |
+ O_SHORT_LIVED |
+#endif /* __WIN__ */
+ O_CREAT | O_EXCL | O_RDWR | O_TEMPORARY,
+ MYF(MY_WME));
+ if (fd >= 0) {
+#ifndef __WIN__
+ /*
+ This can be removed once the following bug is fixed:
+ Bug #28903 create_temp_file() doesn't honor O_TEMPORARY option
+ (file not removed) (Unix)
+ */
+ unlink(filename);
+#endif /* !__WIN__ */
+ }
+
+ return fd;
+}
+
+
extern "C"
int thd_in_lock_tables(const THD *thd)
{
@@ -253,6 +278,11 @@ int thd_tx_isolation(const THD *thd)
return (int) thd->variables.tx_isolation;
}
+extern "C"
+void thd_inc_row_count(THD *thd)
+{
+ thd->row_count++;
+}
/*
Dumps a text description of a thread, its security context
@@ -381,7 +411,7 @@ THD::THD()
current_linfo = 0;
slave_thread = 0;
bzero(&variables, sizeof(variables));
- thread_id= variables.pseudo_thread_id= 0;
+ thread_id= 0;
one_shot_set= 0;
file_id = 0;
query_id= 0;
@@ -483,6 +513,49 @@ void THD::pop_internal_handler()
m_internal_handler= NULL;
}
+extern "C"
+void *thd_alloc(MYSQL_THD thd, unsigned int size)
+{
+ return thd->alloc(size);
+}
+
+extern "C"
+void *thd_calloc(MYSQL_THD thd, unsigned int size)
+{
+ return thd->calloc(size);
+}
+
+extern "C"
+char *thd_strdup(MYSQL_THD thd, const char *str)
+{
+ return thd->strdup(str);
+}
+
+extern "C"
+char *thd_strmake(MYSQL_THD thd, const char *str, unsigned int size)
+{
+ return thd->strmake(str, size);
+}
+
+extern "C"
+LEX_STRING *thd_make_lex_string(THD *thd, LEX_STRING *lex_str,
+ const char *str, unsigned int size,
+ int allocate_lex_string)
+{
+ return thd->make_lex_string(lex_str, str, size,
+ (bool) allocate_lex_string);
+}
+
+extern "C"
+void *thd_memdup(MYSQL_THD thd, const void* str, unsigned int size)
+{
+ return thd->memdup(str, size);
+}
+
+void thd_get_xid(const MYSQL_THD thd, MYSQL_XID *xid)
+{
+ *xid = *(MYSQL_XID *) &thd->transaction.xid_state.xid;
+}
/*
Init common variables that has to be reset on start and on change_user
@@ -498,6 +571,12 @@ void THD::init(void)
variables.date_format);
variables.datetime_format= date_time_format_copy((THD*) 0,
variables.datetime_format);
+ /*
+ variables= global_system_variables above has reset
+ variables.pseudo_thread_id to 0. We need to correct it here to
+ avoid temporary tables replication failure.
+ */
+ variables.pseudo_thread_id= thread_id;
pthread_mutex_unlock(&LOCK_global_system_variables);
server_status= SERVER_STATUS_AUTOCOMMIT;
if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)
@@ -851,6 +930,30 @@ void THD::cleanup_after_query()
}
+/**
+ Create a LEX_STRING in this connection
+
+ @param lex_str pointer to LEX_STRING object to be initialized
+ @param str initializer to be copied into lex_str
+ @param length length of str, in bytes
+ @param allocate_lex_string if TRUE, allocate new LEX_STRING object,
+ instead of using lex_str value
+ @return NULL on failure, or pointer to the LEX_STRING object
+*/
+LEX_STRING *THD::make_lex_string(LEX_STRING *lex_str,
+ const char* str, uint length,
+ bool allocate_lex_string)
+{
+ if (allocate_lex_string)
+ if (!(lex_str= (LEX_STRING *)alloc(sizeof(LEX_STRING))))
+ return 0;
+ if (!(lex_str->str= strmake_root(mem_root, str, length)))
+ return 0;
+ lex_str->length= length;
+ return lex_str;
+}
+
+
/*
Convert a string to another character set
@@ -1445,6 +1548,8 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
field_sep_char= (exchange->enclosed->length() ? (*exchange->enclosed)[0] :
field_term_length ? (*exchange->field_term)[0] : INT_MAX);
escape_char= (exchange->escaped->length() ? (*exchange->escaped)[0] : -1);
+ is_ambiguous_field_sep= test(strchr(ESCAPE_CHARS, field_sep_char));
+ is_unsafe_field_sep= test(strchr(NUMERIC_CHARS, field_sep_char));
line_sep_char= (exchange->line_term->length() ?
(*exchange->line_term)[0] : INT_MAX);
if (!field_term_length)
@@ -1519,7 +1624,8 @@ bool select_export::send_data(List<Item> &items)
used_length=min(res->length(),item->max_length);
else
used_length=res->length();
- if (result_type == STRING_RESULT && escape_char != -1)
+ if ((result_type == STRING_RESULT || is_unsafe_field_sep) &&
+ escape_char != -1)
{
char *pos, *start, *end;
CHARSET_INFO *res_charset= res->charset();
@@ -1585,7 +1691,9 @@ bool select_export::send_data(List<Item> &items)
NEED_ESCAPING(pos[1])))
{
char tmp_buff[2];
- tmp_buff[0]= escape_char;
+ tmp_buff[0]= ((int) *pos == field_sep_char &&
+ is_ambiguous_field_sep) ?
+ field_sep_char : escape_char;
tmp_buff[1]= *pos ? *pos : '0';
if (my_b_write(&cache,(uchar*) start,(uint) (pos-start)) ||
my_b_write(&cache,(uchar*) tmp_buff,2))
@@ -2436,7 +2544,43 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup)
DBUG_VOID_RETURN;
}
+/**
+ Check the killed state of a user thread
+ @param thd user thread
+ @retval 0 the user thread is active
+ @retval 1 the user thread has been killed
+*/
+extern "C" int thd_killed(const MYSQL_THD thd)
+{
+ return(thd->killed);
+}
+
+#ifdef INNODB_COMPATIBILITY_HOOKS
+extern "C" struct charset_info_st *thd_charset(MYSQL_THD thd)
+{
+ return(thd->charset());
+}
+extern "C" char **thd_query(MYSQL_THD thd)
+{
+ return(&thd->query);
+}
+
+extern "C" int thd_slave_thread(const MYSQL_THD thd)
+{
+ return(thd->slave_thread);
+}
+
+extern "C" int thd_non_transactional_update(const MYSQL_THD thd)
+{
+ return(thd->no_trans_update.all);
+}
+
+extern "C" int thd_binlog_format(const MYSQL_THD thd)
+{
+ return (int) thd->variables.binlog_format;
+}
+#endif // INNODB_COMPATIBILITY_HOOKS */
/****************************************************************************
Handling of statement states in functions and triggers.
diff --git a/sql/sql_class.h b/sql/sql_class.h
index a8d62d93b21..71c13e001ee 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -39,8 +39,6 @@ enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE };
enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON,
DELAY_KEY_WRITE_ALL };
-enum enum_check_fields
-{ CHECK_FIELD_IGNORE, CHECK_FIELD_WARN, CHECK_FIELD_ERROR_FOR_NULL };
enum enum_mark_columns
{ MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE};
@@ -67,11 +65,23 @@ typedef struct st_user_var_events
#define RP_LOCK_LOG_IS_ALREADY_LOCKED 1
#define RP_FORCE_ROTATE 2
+/*
+ The COPY_INFO structure is used by INSERT/REPLACE code.
+ The schema of the row counting by the INSERT/INSERT ... ON DUPLICATE KEY
+ UPDATE code:
+ If a row is inserted then the copied variable is incremented.
+ If a row is updated by the INSERT ... ON DUPLICATE KEY UPDATE and the
+ new data differs from the old one then the copied and the updated
+ variables are incremented.
+ The touched variable is incremented if a row was touched by the update part
+ of the INSERT ... ON DUPLICATE KEY UPDATE no matter whether the row
+ was actually changed or not.
+*/
typedef struct st_copy_info {
- ha_rows records;
- ha_rows deleted;
- ha_rows updated;
- ha_rows copied;
+ ha_rows records; /* Number of processed records */
+ ha_rows deleted; /* Number of deleted records */
+ ha_rows updated; /* Number of updated records */
+ ha_rows copied; /* Number of copied records */
ha_rows error_count;
ha_rows touched; /* Number of touched records */
enum enum_duplicates handle_duplicates;
@@ -179,7 +189,7 @@ public:
Table_ident *table, List<Key_part_spec> &ref_cols,
uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg)
:Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols),
- ref_table(table), ref_columns(cols),
+ ref_table(table), ref_columns(ref_cols),
delete_opt(delete_opt_arg), update_opt(update_opt_arg),
match_opt(match_opt_arg)
{}
@@ -305,6 +315,7 @@ struct system_variables
my_bool old_mode;
my_bool query_cache_wlock_invalidate;
my_bool engine_condition_pushdown;
+ my_bool keep_files_on_create;
my_bool ndb_force_send;
my_bool ndb_use_copying_alter_table;
my_bool ndb_use_exact_count;
@@ -492,13 +503,6 @@ public:
{ return strdup_root(mem_root,str); }
inline char *strmake(const char *str, size_t size)
{ return strmake_root(mem_root,str,size); }
- inline bool LEX_STRING_make(LEX_STRING *lex_str, const char *str,
- size_t size)
- {
- return ((lex_str->str=
- strmake_root(mem_root, str, (lex_str->length= size)))) == 0;
- }
-
inline void *memdup(const void *str, size_t size)
{ return memdup_root(mem_root,str,size); }
inline void *memdup_w_gap(const void *str, size_t size, uint gap)
@@ -1566,11 +1570,27 @@ public:
proc_info = old_msg;
pthread_mutex_unlock(&mysys_var->mutex);
}
+
+ static inline void safe_time(time_t *t)
+ {
+ /**
+ Wrapper around time() which retries on error (-1)
+
+ @details
+ This is needed because, despite the documentation, time() may fail
+ in some circumstances. Here we retry time() until it succeeds, and
+ log the failure so that performance problems related to this can be
+ identified.
+ */
+ while(unlikely(time(t) == ((time_t) -1)))
+ sql_print_information("time() failed with %d", errno);
+ }
+
inline time_t query_start() { query_start_used=1; return start_time; }
- inline void set_time() { if (user_time) start_time=time_after_lock=user_time; else time_after_lock=time(&start_time); }
- inline void end_time() { time(&start_time); }
+ inline void set_time() { if (user_time) start_time=time_after_lock=user_time; else { safe_time(&start_time); time_after_lock= start_time; }}
+ inline void end_time() { safe_time(&start_time); }
inline void set_time(time_t t) { time_after_lock=start_time=user_time=t; }
- inline void lock_time() { time(&time_after_lock); }
+ inline void lock_time() { safe_time(&time_after_lock); }
inline ulonglong found_rows(void)
{
return limit_found_rows;
@@ -1596,6 +1616,10 @@ public:
return alloc_root(&transaction.mem_root,size);
}
+ LEX_STRING *make_lex_string(LEX_STRING *lex_str,
+ const char* str, uint length,
+ bool allocate_lex_string);
+
bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
const char *from, uint from_length,
CHARSET_INFO *from_cs);
@@ -1949,9 +1973,30 @@ public:
};
+#define ESCAPE_CHARS "ntrb0ZN" // keep synchronous with READ_INFO::unescape
+
+
+/*
+ List of all possible characters of a numeric value text representation.
+*/
+#define NUMERIC_CHARS ".0123456789e+-"
+
+
class select_export :public select_to_file {
uint field_term_length;
int field_sep_char,escape_char,line_sep_char;
+ /*
+ The is_ambiguous_field_sep field is true if a value of the field_sep_char
+ field is one of the 'n', 't', 'r' etc characters
+ (see the READ_INFO::unescape method and the ESCAPE_CHARS constant value).
+ */
+ bool is_ambiguous_field_sep;
+ /*
+ The is_unsafe_field_sep field is true if a value of the field_sep_char
+ field is one of the '0'..'9', '+', '-', '.' and 'e' characters
+ (see the NUMERIC_CHARS constant value).
+ */
+ bool is_unsafe_field_sep;
bool fixed_row_size;
public:
select_export(sql_exchange *ex) :select_to_file(ex) {}
@@ -1999,8 +2044,8 @@ class select_insert :public select_result_interceptor {
class select_create: public select_insert {
ORDER *group;
TABLE_LIST *create_table;
- TABLE_LIST *select_tables;
HA_CREATE_INFO *create_info;
+ TABLE_LIST *select_tables;
Alter_info *alter_info;
Field **field;
public:
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 43d84740f0b..575db5b80f7 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -1113,7 +1113,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
}
}
if (thd->killed ||
- (tot_list && mysql_rm_table_part2_with_lock(thd, tot_list, 1, 0, 1)))
+ (tot_list && mysql_rm_table_part2(thd, tot_list, 1, 0, 1, 1)))
goto err;
/* Remove RAID directories */
@@ -1397,10 +1397,10 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
{
if (force_switch)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR));
-
- /* Change db to NULL. */
+ /*
+ This can only happen when we restore the old db in THD after
+ execution of a routine is complete. Change db to NULL.
+ */
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index eeac5e7c4fe..b747c706f75 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -661,7 +661,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
/*
Fill in the given fields and dump it to the table file
*/
- info.records= info.deleted= info.copied= info.updated= 0;
+ bzero((char*) &info,sizeof(info));
info.ignore= ignore;
info.handle_duplicates=duplic;
info.update_fields= &update_fields;
@@ -695,6 +695,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
if (duplic == DUP_REPLACE &&
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
+ if (duplic == DUP_UPDATE)
+ table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE);
/*
let's *try* to start bulk inserts. It won't necessary
start them as values_list.elements should be greater than
@@ -1419,7 +1421,12 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
goto before_trg_err;
table->file->restore_auto_increment(prev_insert_id);
- if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) ||
+ if (table->next_number_field)
+ table->file->adjust_next_insert_id_after_explicit_value(
+ table->next_number_field->val_int());
+ info->touched++;
+ if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ &&
+ !bitmap_is_subset(table->write_set, table->read_set)) ||
compare_record(table))
{
if ((error=table->file->ha_update_row(table->record[1],
@@ -1446,9 +1453,6 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
handled separately by THD::arg_of_last_insert_id_function.
*/
insert_id_for_cur_row= table->file->insert_id_for_cur_row= 0;
- if (table->next_number_field)
- table->file->adjust_next_insert_id_after_explicit_value(
- table->next_number_field->val_int());
trg_error= (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
TRG_ACTION_AFTER, TRUE));
@@ -1759,18 +1763,18 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
thd->proc_info="waiting for delay_list";
pthread_mutex_lock(&LOCK_delayed_insert); // Protect master list
I_List_iterator<Delayed_insert> it(delayed_threads);
- Delayed_insert *tmp;
- while ((tmp=it++))
+ Delayed_insert *di;
+ while ((di= it++))
{
- if (!strcmp(tmp->thd.db, table_list->db) &&
- !strcmp(table_list->table_name, tmp->table->s->table_name.str))
+ if (!strcmp(table_list->db, di->table_list.db) &&
+ !strcmp(table_list->table_name, di->table_list.table_name))
{
- tmp->lock();
+ di->lock();
break;
}
}
pthread_mutex_unlock(&LOCK_delayed_insert); // For unlink from list
- return tmp;
+ return di;
}
@@ -1796,21 +1800,41 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
Two latter cases indicate a request for lock upgrade.
XXX: why do we regard INSERT DELAYED into a view as an error and
- do not simply a lock upgrade?
+ do not simply perform a lock upgrade?
+
+ TODO: The approach with using two mutexes to work with the
+ delayed thread list -- LOCK_delayed_insert and
+ LOCK_delayed_create -- is redundant, and we only need one of
+ them to protect the list. The reason we have two locks is that
+ we do not want to block look-ups in the list while we're waiting
+ for the newly created thread to open the delayed table. However,
+ this wait itself is redundant -- we always call get_local_table
+ later on, and there wait again until the created thread acquires
+ a table lock.
+
+ As is redundant the concept of locks_in_memory, since we already
+ have another counter with similar semantics - tables_in_use,
+ both of them are devoted to counting the number of producers for
+ a given consumer (delayed insert thread), only at different
+ stages of producer-consumer relationship.
+
+ 'dead' and 'status' variables in Delayed_insert are redundant
+ too, since there is already 'di->thd.killed' and
+ di->stacked_inserts.
*/
static
bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
{
int error;
- Delayed_insert *tmp;
+ Delayed_insert *di;
DBUG_ENTER("delayed_get_table");
/* Must be set in the parser */
DBUG_ASSERT(table_list->db);
/* Find the thread which handles this table. */
- if (!(tmp=find_handler(thd,table_list)))
+ if (!(di= find_handler(thd, table_list)))
{
/*
No match. Create a new thread to handle the table, but
@@ -1824,9 +1848,9 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
The first search above was done without LOCK_delayed_create.
Another thread might have created the handler in between. Search again.
*/
- if (! (tmp= find_handler(thd, table_list)))
+ if (! (di= find_handler(thd, table_list)))
{
- if (!(tmp=new Delayed_insert()))
+ if (!(di= new Delayed_insert()))
{
my_error(ER_OUTOFMEMORY,MYF(0),sizeof(Delayed_insert));
thd->fatal_error();
@@ -1835,28 +1859,30 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
pthread_mutex_lock(&LOCK_thread_count);
thread_count++;
pthread_mutex_unlock(&LOCK_thread_count);
- tmp->thd.set_db(table_list->db, strlen(table_list->db));
- tmp->thd.query= my_strdup(table_list->table_name,MYF(MY_WME));
- if (tmp->thd.db == NULL || tmp->thd.query == NULL)
+ di->thd.set_db(table_list->db, strlen(table_list->db));
+ di->thd.query= my_strdup(table_list->table_name, MYF(MY_WME));
+ if (di->thd.db == NULL || di->thd.query == NULL)
{
/* The error is reported */
- delete tmp;
+ delete di;
thd->fatal_error();
goto end_create;
}
- tmp->table_list= *table_list; // Needed to open table
- tmp->table_list.alias= tmp->table_list.table_name= tmp->thd.query;
- tmp->lock();
- pthread_mutex_lock(&tmp->mutex);
- if ((error=pthread_create(&tmp->thd.real_id,&connection_attrib,
- handle_delayed_insert,(void*) tmp)))
+ di->table_list= *table_list; // Needed to open table
+ /* Replace volatile strings with local copies */
+ di->table_list.alias= di->table_list.table_name= di->thd.query;
+ di->table_list.db= di->thd.db;
+ di->lock();
+ pthread_mutex_lock(&di->mutex);
+ if ((error= pthread_create(&di->thd.real_id, &connection_attrib,
+ handle_delayed_insert, (void*) di)))
{
DBUG_PRINT("error",
("Can't create thread to handle delayed insert (error %d)",
error));
- pthread_mutex_unlock(&tmp->mutex);
- tmp->unlock();
- delete tmp;
+ pthread_mutex_unlock(&di->mutex);
+ di->unlock();
+ delete di;
my_error(ER_CANT_CREATE_THREAD, MYF(0), error);
thd->fatal_error();
goto end_create;
@@ -1864,15 +1890,15 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
/* Wait until table is open */
thd->proc_info="waiting for handler open";
- while (!tmp->thd.killed && !tmp->table && !thd->killed)
+ while (!di->thd.killed && !di->table && !thd->killed)
{
- pthread_cond_wait(&tmp->cond_client,&tmp->mutex);
+ pthread_cond_wait(&di->cond_client, &di->mutex);
}
- pthread_mutex_unlock(&tmp->mutex);
+ pthread_mutex_unlock(&di->mutex);
thd->proc_info="got old table";
- if (tmp->thd.killed)
+ if (di->thd.killed)
{
- if (tmp->thd.net.report_error)
+ if (di->thd.net.report_error)
{
/*
Copy the error message. Note that we don't treat fatal
@@ -1880,31 +1906,34 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
main thread. Use of my_message will enable stored
procedures continue handlers.
*/
- my_message(tmp->thd.net.last_errno, tmp->thd.net.last_error,
+ my_message(di->thd.net.last_errno, di->thd.net.last_error,
MYF(0));
}
- tmp->unlock();
+ di->unlock();
goto end_create;
}
if (thd->killed)
{
- tmp->unlock();
+ di->unlock();
goto end_create;
}
+ pthread_mutex_lock(&LOCK_delayed_insert);
+ delayed_threads.append(di);
+ pthread_mutex_unlock(&LOCK_delayed_insert);
}
pthread_mutex_unlock(&LOCK_delayed_create);
}
- pthread_mutex_lock(&tmp->mutex);
- table_list->table= tmp->get_local_table(thd);
- pthread_mutex_unlock(&tmp->mutex);
+ pthread_mutex_lock(&di->mutex);
+ table_list->table= di->get_local_table(thd);
+ pthread_mutex_unlock(&di->mutex);
if (table_list->table)
{
DBUG_ASSERT(thd->net.report_error == 0);
- thd->di=tmp;
+ thd->di= di;
}
/* Unlock the delayed insert object after its last access. */
- tmp->unlock();
+ di->unlock();
DBUG_RETURN((table_list->table == NULL));
end_create:
@@ -1934,7 +1963,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
my_ptrdiff_t adjust_ptrs;
Field **field,**org_field, *found_next_number_field;
TABLE *copy;
- TABLE_SHARE *share= table->s;
+ TABLE_SHARE *share;
uchar *bitmap;
DBUG_ENTER("Delayed_insert::get_local_table");
@@ -1958,6 +1987,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
goto error;
}
}
+ share= table->s;
/*
Allocate memory for the TABLE object, the field pointers array, and
@@ -2153,26 +2183,26 @@ void kill_delayed_threads(void)
VOID(pthread_mutex_lock(&LOCK_delayed_insert)); // For unlink from list
I_List_iterator<Delayed_insert> it(delayed_threads);
- Delayed_insert *tmp;
- while ((tmp=it++))
+ Delayed_insert *di;
+ while ((di= it++))
{
- tmp->thd.killed= THD::KILL_CONNECTION;
- if (tmp->thd.mysys_var)
+ di->thd.killed= THD::KILL_CONNECTION;
+ if (di->thd.mysys_var)
{
- pthread_mutex_lock(&tmp->thd.mysys_var->mutex);
- if (tmp->thd.mysys_var->current_cond)
+ pthread_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 (&tmp->mutex != tmp->thd.mysys_var->current_mutex)
- pthread_mutex_lock(tmp->thd.mysys_var->current_mutex);
- pthread_cond_broadcast(tmp->thd.mysys_var->current_cond);
- if (&tmp->mutex != tmp->thd.mysys_var->current_mutex)
- pthread_mutex_unlock(tmp->thd.mysys_var->current_mutex);
+ if (&di->mutex != di->thd.mysys_var->current_mutex)
+ pthread_mutex_lock(di->thd.mysys_var->current_mutex);
+ pthread_cond_broadcast(di->thd.mysys_var->current_cond);
+ if (&di->mutex != di->thd.mysys_var->current_mutex)
+ pthread_mutex_unlock(di->thd.mysys_var->current_mutex);
}
- pthread_mutex_unlock(&tmp->thd.mysys_var->mutex);
+ pthread_mutex_unlock(&di->thd.mysys_var->mutex);
}
}
VOID(pthread_mutex_unlock(&LOCK_delayed_insert)); // For unlink from list
@@ -2246,11 +2276,6 @@ pthread_handler_t handle_delayed_insert(void *arg)
}
di->table->copy_blobs=1;
- /* One can now use this */
- pthread_mutex_lock(&LOCK_delayed_insert);
- delayed_threads.append(di);
- pthread_mutex_unlock(&LOCK_delayed_insert);
-
/* Tell client that the thread is initialized */
pthread_cond_signal(&di->cond_client);
@@ -2546,6 +2571,8 @@ bool Delayed_insert::handle_inserts(void)
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
using_opt_replace= 1;
}
+ if (info.handle_duplicates == DUP_UPDATE)
+ table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE);
thd.clear_error(); // reset error for binlog
if (write_record(&thd, table, &info))
{
@@ -2890,6 +2917,8 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
if (info.handle_duplicates == DUP_REPLACE &&
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
+ if (info.handle_duplicates == DUP_UPDATE)
+ table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE);
thd->no_trans_update.stmt= FALSE;
thd->abort_on_warning= (!info.ignore &&
(thd->variables.sql_mode &
@@ -3481,6 +3510,8 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
if (info.handle_duplicates == DUP_REPLACE &&
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
+ if (info.handle_duplicates == DUP_UPDATE)
+ table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE);
if (!thd->prelocked_mode)
table->file->ha_start_bulk_insert((ha_rows) 0);
thd->no_trans_update.stmt= FALSE;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 639f0d2325d..e493dc05047 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -124,7 +124,7 @@ Lex_input_stream::Lex_input_stream(THD *thd,
m_tok_start_prev(NULL),
m_buf(buffer),
m_buf_length(length),
- m_echo(true),
+ m_echo(TRUE),
m_cpp_tok_start(NULL),
m_cpp_tok_start_prev(NULL),
m_cpp_tok_end(NULL),
@@ -1200,7 +1200,7 @@ int MYSQLlex(void *arg, void *yythd)
{
lip->in_comment= DISCARD_COMMENT;
/* Accept '/' '*' '!', but do not keep this marker. */
- lip->set_echo(false);
+ lip->set_echo(FALSE);
lip->yySkip();
lip->yySkip();
lip->yySkip();
@@ -1233,7 +1233,7 @@ int MYSQLlex(void *arg, void *yythd)
if (version <= MYSQL_VERSION_ID)
{
/* Expand the content of the special comment as real code */
- lip->set_echo(true);
+ lip->set_echo(TRUE);
state=MY_LEX_START;
break;
}
@@ -1241,7 +1241,7 @@ int MYSQLlex(void *arg, void *yythd)
else
{
state=MY_LEX_START;
- lip->set_echo(true);
+ lip->set_echo(TRUE);
break;
}
}
@@ -1261,7 +1261,7 @@ int MYSQLlex(void *arg, void *yythd)
if (! lip->eof())
lip->yySkip(); // remove last '/'
state = MY_LEX_START; // Try again
- lip->set_echo(true);
+ lip->set_echo(TRUE);
break;
case MY_LEX_END_LONG_COMMENT:
if ((lip->in_comment != NO_COMMENT) && lip->yyPeek() == '/')
@@ -1272,7 +1272,7 @@ int MYSQLlex(void *arg, void *yythd)
lip->set_echo(lip->in_comment == PRESERVE_COMMENT);
lip->yySkipn(2);
/* And start recording the tokens again */
- lip->set_echo(true);
+ lip->set_echo(TRUE);
lip->in_comment=NO_COMMENT;
state=MY_LEX_START;
}
@@ -1297,7 +1297,7 @@ int MYSQLlex(void *arg, void *yythd)
lip->found_semicolon= lip->get_ptr();
thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
lip->next_state= MY_LEX_END;
- lip->set_echo(true);
+ lip->set_echo(TRUE);
return (END_OF_INPUT);
}
state= MY_LEX_CHAR; // Return ';'
@@ -1309,9 +1309,9 @@ int MYSQLlex(void *arg, void *yythd)
if (lip->eof())
{
lip->yyUnget(); // Reject the last '\0'
- lip->set_echo(false);
+ lip->set_echo(FALSE);
lip->yySkip();
- lip->set_echo(true);
+ lip->set_echo(TRUE);
lip->next_state=MY_LEX_END; // Mark for next loop
return(END_OF_INPUT);
}
@@ -2297,7 +2297,7 @@ bool st_lex::need_correct_ident()
VIEW_CHECK_CASCADED CHECK OPTION CASCADED
*/
-uint8 st_lex::get_effective_with_check(st_table_list *view)
+uint8 st_lex::get_effective_with_check(TABLE_LIST *view)
{
if (view->select_lex->master_unit() == &unit &&
which_check_option_applicable())
@@ -2306,6 +2306,43 @@ uint8 st_lex::get_effective_with_check(st_table_list *view)
}
+/**
+ This method should be called only during parsing.
+ It is aware of compound statements (stored routine bodies)
+ and will initialize the destination with the default
+ database of the stored routine, rather than the default
+ database of the connection it is parsed in.
+ E.g. if one has no current database selected, or current database
+ set to 'bar' and then issues:
+
+ CREATE PROCEDURE foo.p1() BEGIN SELECT * FROM t1 END//
+
+ t1 is meant to refer to foo.t1, not to bar.t1.
+
+ This method is needed to support this rule.
+
+ @return TRUE in case of error (parsing should be aborted, FALSE in
+ case of success
+*/
+
+bool
+st_lex::copy_db_to(char **p_db, size_t *p_db_length) const
+{
+ if (sphead)
+ {
+ DBUG_ASSERT(sphead->m_db.str && sphead->m_db.length);
+ /*
+ It is safe to assign the string by-pointer, both sphead and
+ its statements reside in the same memory root.
+ */
+ *p_db= sphead->m_db.str;
+ if (p_db_length)
+ *p_db_length= sphead->m_db.length;
+ return FALSE;
+ }
+ return thd->copy_db_to(p_db, p_db_length);
+}
+
/*
initialize limit counters
@@ -2329,6 +2366,27 @@ void st_select_lex_unit::set_limit(SELECT_LEX *sl)
}
+/**
+ Update the parsed tree with information about triggers that
+ may be fired when executing this statement.
+*/
+
+void st_lex::set_trg_event_type_for_tables()
+{
+ /*
+ 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();
+
+ while (tables)
+ {
+ tables->set_trg_event_type(this);
+ tables= tables->next_local;
+ }
+}
+
+
/*
Unlink the first table from the global table list and the first table from
outer select (lex->select_lex) local list
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 254403fe736..9ac7f2835f0 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1239,19 +1239,19 @@ public:
}
/** Get the raw query buffer. */
- const char* get_buf()
+ const char *get_buf()
{
return m_buf;
}
/** Get the pre-processed query buffer. */
- const char* get_cpp_buf()
+ const char *get_cpp_buf()
{
return m_cpp_buf;
}
/** Get the end of the raw query buffer. */
- const char* get_end_of_query()
+ const char *get_end_of_query()
{
return m_end_of_query;
}
@@ -1279,43 +1279,43 @@ public:
}
/** Get the token start position, in the raw buffer. */
- const char* get_tok_start()
+ const char *get_tok_start()
{
return m_tok_start;
}
/** Get the token start position, in the pre-processed buffer. */
- const char* get_cpp_tok_start()
+ const char *get_cpp_tok_start()
{
return m_cpp_tok_start;
}
/** Get the token end position, in the raw buffer. */
- const char* get_tok_end()
+ const char *get_tok_end()
{
return m_tok_end;
}
/** Get the token end position, in the pre-processed buffer. */
- const char* get_cpp_tok_end()
+ const char *get_cpp_tok_end()
{
return m_cpp_tok_end;
}
/** Get the previous token start position, in the raw buffer. */
- const char* get_tok_start_prev()
+ const char *get_tok_start_prev()
{
return m_tok_start_prev;
}
/** Get the current stream pointer, in the raw buffer. */
- const char* get_ptr()
+ const char *get_ptr()
{
return m_ptr;
}
/** Get the current stream pointer, in the pre-processed buffer. */
- const char* get_cpp_ptr()
+ const char *get_cpp_ptr()
{
return m_cpp_ptr;
}
@@ -1365,22 +1365,22 @@ public:
private:
/** Pointer to the current position in the raw input stream. */
- const char* m_ptr;
+ const char *m_ptr;
/** Starting position of the last token parsed, in the raw buffer. */
- const char* m_tok_start;
+ const char *m_tok_start;
/** Ending position of the previous token parsed, in the raw buffer. */
- const char* m_tok_end;
+ const char *m_tok_end;
/** End of the query text in the input stream, in the raw buffer. */
- const char* m_end_of_query;
+ const char *m_end_of_query;
/** Starting position of the previous token parsed, in the raw buffer. */
- const char* m_tok_start_prev;
+ const char *m_tok_start_prev;
/** Begining of the query text in the input stream, in the raw buffer. */
- const char* m_buf;
+ const char *m_buf;
/** Length of the raw buffer. */
uint m_buf_length;
@@ -1389,28 +1389,28 @@ private:
bool m_echo;
/** Pre-processed buffer. */
- char* m_cpp_buf;
+ char *m_cpp_buf;
/** Pointer to the current position in the pre-processed input stream. */
- char* m_cpp_ptr;
+ char *m_cpp_ptr;
/**
Starting position of the last token parsed,
in the pre-processed buffer.
*/
- const char* m_cpp_tok_start;
+ const char *m_cpp_tok_start;
/**
Starting position of the previous token parsed,
in the pre-procedded buffer.
*/
- const char* m_cpp_tok_start_prev;
+ const char *m_cpp_tok_start_prev;
/**
Ending position of the previous token parsed,
in the pre-processed buffer.
*/
- const char* m_cpp_tok_end;
+ const char *m_cpp_tok_end;
/** UTF8-body buffer created during parsing. */
char *m_body_utf8;
@@ -1433,7 +1433,7 @@ public:
Position of ';' in the stream, to delimit multiple queries.
This delimiter is in the raw buffer.
*/
- const char* found_semicolon;
+ const char *found_semicolon;
/** SQL_MODE = IGNORE_SPACE. */
bool ignore_space;
@@ -1732,6 +1732,8 @@ typedef struct st_lex : public Query_tables_list
un->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();
@@ -1741,7 +1743,7 @@ typedef struct st_lex : public Query_tables_list
bool can_not_use_merged();
bool only_view_structure();
bool need_correct_ident();
- uint8 get_effective_with_check(st_table_list *view);
+ uint8 get_effective_with_check(TABLE_LIST *view);
/*
Is this update command where 'WHITH CHECK OPTION' clause is important
@@ -1780,6 +1782,8 @@ typedef struct st_lex : public Query_tables_list
context_stack.pop();
}
+ bool copy_db_to(char **p_db, size_t *p_db_length) const;
+
Name_resolution_context *current_context()
{
return context_stack.head();
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 0138030487b..3ffbdf83815 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -851,6 +851,7 @@ continue_loop:;
char
READ_INFO::unescape(char chr)
{
+ /* keep this switch synchornous with the ESCAPE_CHARS macro */
switch(chr) {
case 'n': return '\n';
case 't': return '\t';
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index bd151dc2028..f2a61b7f7c5 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1025,8 +1025,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
HA_CREATE_INFO create_info;
status_var_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB]);
- if (thd->LEX_STRING_make(&db, packet, packet_length -1) ||
- thd->LEX_STRING_make(&alias, db.str, db.length) ||
+ if (thd->make_lex_string(&db, packet, packet_length - 1, FALSE) ||
+ thd->make_lex_string(&alias, db.str, db.length, FALSE) ||
check_db_name(&db))
{
my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL");
@@ -1046,7 +1046,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
status_var_increment(thd->status_var.com_stat[SQLCOM_DROP_DB]);
LEX_STRING db;
- if (thd->LEX_STRING_make(&db, packet, packet_length - 1) ||
+ if (thd->make_lex_string(&db, packet, packet_length - 1, FALSE) ||
check_db_name(&db))
{
my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL");
@@ -1323,7 +1323,7 @@ void log_slow_statement(THD *thd)
thd->variables.long_query_time ||
((thd->server_status &
(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
- (specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES)))
+ opt_log_queries_not_using_indexes))
{
thd->status_var.long_query_count++;
slow_log_print(thd, thd->query, thd->query_length, start_of_query);
@@ -1391,7 +1391,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
LEX_STRING db;
size_t dummy;
if (lex->select_lex.db == NULL &&
- thd->copy_db_to(&lex->select_lex.db, &dummy))
+ lex->copy_db_to(&lex->select_lex.db, &dummy))
{
DBUG_RETURN(1);
}
@@ -2449,7 +2449,7 @@ end_with_restore_list:
check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0)))
goto error;
}
- query_cache_invalidate3(thd, first_table, 0);
+
if (end_active_trans(thd) || mysql_rename_tables(thd, first_table, 0))
goto error;
break;
@@ -5392,8 +5392,9 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
(thd->query_length= (ulong)(*found_semicolon - thd->query)))
thd->query_length--;
/* Actually execute the query */
- mysql_execute_command(thd);
- query_cache_end_of_result(thd);
+ lex->set_trg_event_type_for_tables();
+ mysql_execute_command(thd);
+ query_cache_end_of_result(thd);
}
}
}
@@ -5680,7 +5681,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->db= table->db.str;
ptr->db_length= table->db.length;
}
- else if (thd->copy_db_to(&ptr->db, &ptr->db_length))
+ else if (lex->copy_db_to(&ptr->db, &ptr->db_length))
DBUG_RETURN(0);
ptr->alias= alias_str;
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index f3253e5b086..5cfb46a99ab 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -36,7 +36,7 @@
#include "mysql_priv.h"
#include <errno.h>
#include <m_ctype.h>
-#include "md5.h"
+#include "my_md5.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -523,6 +523,7 @@ static bool set_up_field_array(TABLE *table,
SYNOPSIS
create_full_part_field_array()
+ thd Thread handle
table TABLE object for which partition fields are set-up
part_info Reference to partitioning data structure
@@ -537,11 +538,12 @@ static bool set_up_field_array(TABLE *table,
This function is called from fix_partition_func
*/
-static bool create_full_part_field_array(TABLE *table,
+static bool create_full_part_field_array(THD *thd, TABLE *table,
partition_info *part_info)
{
bool result= FALSE;
Field **ptr;
+ my_bitmap_map *bitmap_buf;
DBUG_ENTER("create_full_part_field_array");
if (!part_info->is_sub_partitioned())
@@ -578,6 +580,35 @@ static bool create_full_part_field_array(TABLE *table,
part_info->full_part_field_array= field_array;
part_info->no_full_part_fields= no_part_fields;
}
+
+ /*
+ Initialize the set of all fields used in partition and subpartition
+ expression. Required for testing of partition fields in write_set
+ when updating. We need to set all bits in read_set because the row
+ may need to be inserted in a different [sub]partition.
+ */
+ if (!(bitmap_buf= (my_bitmap_map*)
+ thd->alloc(bitmap_buffer_size(table->s->fields))))
+ {
+ mem_alloc_error(bitmap_buffer_size(table->s->fields));
+ result= TRUE;
+ goto end;
+ }
+ if (bitmap_init(&part_info->full_part_field_set, bitmap_buf,
+ table->s->fields, FALSE))
+ {
+ mem_alloc_error(table->s->fields);
+ result= TRUE;
+ goto end;
+ }
+ /*
+ full_part_field_array may be NULL if storage engine supports native
+ partitioning.
+ */
+ if ((ptr= part_info->full_part_field_array))
+ for (; *ptr; ptr++)
+ bitmap_set_bit(&part_info->full_part_field_set, (*ptr)->field_index);
+
end:
DBUG_RETURN(result);
}
@@ -1636,7 +1667,7 @@ bool fix_partition_func(THD *thd, TABLE *table,
my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
goto end;
}
- if (unlikely(create_full_part_field_array(table, part_info)))
+ if (unlikely(create_full_part_field_array(thd, table, part_info)))
goto end;
if (unlikely(check_primary_key(table)))
goto end;
@@ -2869,6 +2900,8 @@ uint32 get_partition_id_range_for_endpoint(partition_info *part_info,
}
if (unsigned_flag)
part_func_value-= 0x8000000000000000ULL;
+ if (left_endpoint && !include_endpoint)
+ part_func_value++;
while (max_part_id > min_part_id)
{
loc_part_id= (max_part_id + min_part_id + 1) >> 1;
@@ -3294,7 +3327,9 @@ static uint32 get_sub_part_id_from_key(const TABLE *table,uchar *buf,
key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length);
if (likely(rec0 == buf))
+ {
part_id= part_info->get_subpartition_id(part_info);
+ }
else
{
Field **part_field_array= part_info->subpart_field_array;
@@ -3337,8 +3372,10 @@ bool get_part_id_from_key(const TABLE *table, uchar *buf, KEY *key_info,
key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length);
if (likely(rec0 == buf))
+ {
result= part_info->get_part_partition_id(part_info, part_id,
&func_value);
+ }
else
{
Field **part_field_array= part_info->part_field_array;
@@ -3384,8 +3421,10 @@ void get_full_part_id_from_key(const TABLE *table, uchar *buf,
key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length);
if (likely(rec0 == buf))
+ {
result= part_info->get_partition_id(part_info, &part_spec->start_part,
&func_value);
+ }
else
{
Field **part_field_array= part_info->full_part_field_array;
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 406e242cada..a97bd908468 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1941,13 +1941,6 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length)
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
}
- else
- {
- const char *format= "[%lu] %.*b";
- general_log_print(thd, COM_STMT_PREPARE, format, stmt->id,
- stmt->query_length, stmt->query);
-
- }
/* check_prepared_statemnt sends the metadata packet in case of success */
DBUG_VOID_RETURN;
}
@@ -2330,12 +2323,6 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
test(flags & (ulong) CURSOR_TYPE_READ_ONLY));
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
- if (error == 0)
- {
- const char *format= "[%lu] %.*b";
- general_log_print(thd, COM_STMT_EXECUTE, format, stmt->id,
- thd->query_length, thd->query);
- }
DBUG_VOID_RETURN;
set_params_data_err:
@@ -2880,6 +2867,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
error= parse_sql(thd, &lip, NULL) ||
thd->net.report_error ||
init_param_array(this);
+ lex->set_trg_event_type_for_tables();
/*
While doing context analysis of the query (in check_prepared_statement)
@@ -2929,6 +2917,29 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
init_stmt_after_parse(lex);
state= Query_arena::PREPARED;
flags&= ~ (uint) IS_IN_USE;
+
+ /*
+ Log COM_EXECUTE to the general log. Note, that in case of SQL
+ prepared statements this causes two records to be output:
+
+ Query PREPARE stmt from @user_variable
+ Prepare <statement SQL text>
+
+ This is considered user-friendly, since in the
+ second log entry we output the actual statement text.
+
+ Do not print anything if this is an SQL prepared statement and
+ we're inside a stored procedure (also called Dynamic SQL) --
+ sub-statements inside stored procedures are not logged into
+ the general log.
+ */
+ if (thd->spcont == NULL)
+ {
+ const char *format= "[%lu] %.*b";
+ general_log_print(thd, COM_STMT_PREPARE, format, id,
+ query_length, query);
+
+ }
}
DBUG_RETURN(error);
}
@@ -3075,6 +3086,28 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (state == Query_arena::PREPARED)
state= Query_arena::EXECUTED;
+ /*
+ Log COM_EXECUTE to the general log. Note, that in case of SQL
+ prepared statements this causes two records to be output:
+
+ Query EXECUTE <statement name>
+ Execute <statement SQL text>
+
+ This is considered user-friendly, since in the
+ second log entry we output values of parameter markers.
+
+ Do not print anything if this is an SQL prepared statement and
+ we're inside a stored procedure (also called Dynamic SQL) --
+ sub-statements inside stored procedures are not logged into
+ the general log.
+ */
+ if (error == 0 && thd->spcont == NULL)
+ {
+ const char *format= "[%lu] %.*b";
+ general_log_print(thd, COM_STMT_EXECUTE, format, id,
+ thd->query_length, thd->query);
+ }
+
error:
flags&= ~ (uint) IS_IN_USE;
return error;
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index 866d82377c0..f5e1b8988f3 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -144,10 +144,13 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
}
}
- VOID(pthread_mutex_lock(&LOCK_open));
- if (lock_table_names(thd, table_list))
+ pthread_mutex_lock(&LOCK_open);
+ if (lock_table_names_exclusively(thd, table_list))
+ {
+ pthread_mutex_unlock(&LOCK_open);
goto err;
-
+ }
+
error=0;
if ((ren_table=rename_tables(thd,table_list,0)))
{
@@ -170,6 +173,17 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
error= 1;
}
+ /*
+ An exclusive lock on table names is satisfactory to ensure
+ no other thread accesses this table.
+ However, NDB assumes that handler::rename_tables is called under
+ LOCK_open. And it indeed is, from ALTER TABLE.
+ TODO: remove this limitation.
+ We still should unlock LOCK_open as early as possible, to provide
+ higher concurrency - query_cache_invalidate can take minutes to
+ complete.
+ */
+ pthread_mutex_unlock(&LOCK_open);
/* Lets hope this doesn't fail as the result will be messy */
if (!silent && !error)
@@ -178,10 +192,14 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
send_ok(thd);
}
+ if (!error)
+ query_cache_invalidate3(thd, table_list, 0);
+
+ pthread_mutex_lock(&LOCK_open);
unlock_table_names(thd, table_list, (TABLE_LIST*) 0);
+ pthread_mutex_unlock(&LOCK_open);
err:
- pthread_mutex_unlock(&LOCK_open);
/* enable logging back if needed */
if (disable_logs)
{
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 1616e895107..5d9d30b6020 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -1442,13 +1442,11 @@ err:
}
if (errmsg)
- {
my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
"SHOW BINLOG EVENTS", errmsg);
- DBUG_RETURN(TRUE);
- }
+ else
+ send_eof(thd);
- send_eof(thd);
pthread_mutex_lock(&LOCK_thread_count);
thd->current_linfo = 0;
pthread_mutex_unlock(&LOCK_thread_count);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 3cda4029161..be6d1f74852 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -8703,9 +8703,15 @@ static void restore_prev_nj_state(JOIN_TAB *last)
{
TABLE_LIST *last_emb= last->table->pos_in_table_list->embedding;
JOIN *join= last->join;
- while (last_emb && !(--last_emb->nested_join->counter))
+ while (last_emb)
{
- join->cur_embedding_map &= last_emb->nested_join->nj_map;
+ if (!(--last_emb->nested_join->counter))
+ join->cur_embedding_map&= ~last_emb->nested_join->nj_map;
+ else if (last_emb->nested_join->join_list.elements-1 ==
+ last_emb->nested_join->counter)
+ join->cur_embedding_map|= last_emb->nested_join->nj_map;
+ else
+ break;
last_emb= last_emb->embedding;
}
}
@@ -15752,11 +15758,11 @@ static void print_join(THD *thd, String *str, List<TABLE_LIST> *tables)
Print table as it should be in join list
SYNOPSIS
- st_table_list::print();
+ TABLE_LIST::print();
str string where table should bbe printed
*/
-void st_table_list::print(THD *thd, String *str)
+void TABLE_LIST::print(THD *thd, String *str)
{
if (nested_join)
{
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index f66897df671..5b8cb93baab 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -639,7 +639,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
if (table_list->view)
{
- protocol->store(buffer.ptr(), buffer.length(), &my_charset_bin);
+ protocol->store(buffer.ptr(), buffer.length(),
+ table_list->view_creation_ctx->get_client_cs());
protocol->store(table_list->view_creation_ctx->get_client_cs()->csname,
system_charset_info);
@@ -2175,20 +2176,6 @@ void calc_sum_of_all_status(STATUS_VAR *to)
}
-LEX_STRING *make_lex_string(THD *thd, LEX_STRING *lex_str,
- const char* str, uint length,
- bool allocate_lex_string)
-{
- MEM_ROOT *mem= thd->mem_root;
- if (allocate_lex_string)
- if (!(lex_str= (LEX_STRING *)thd->alloc(sizeof(LEX_STRING))))
- return 0;
- lex_str->str= strmake_root(mem, str, length);
- lex_str->length= length;
- return lex_str;
-}
-
-
/* INFORMATION_SCHEMA name */
LEX_STRING INFORMATION_SCHEMA_NAME= { C_STRING_WITH_LEN("information_schema")};
@@ -2273,11 +2260,9 @@ bool uses_only_table_name_fields(Item *item, TABLE_LIST *table)
if (item->type() == Item::FUNC_ITEM)
{
Item_func *item_func= (Item_func*)item;
- Item **child;
- Item **item_end= (item_func->arguments()) + item_func->argument_count();
- for (child= item_func->arguments(); child != item_end; child++)
+ for (uint i=0; i<item_func->argument_count(); i++)
{
- if (!uses_only_table_name_fields(*child, table))
+ if (!uses_only_table_name_fields(item_func->arguments()[i], table))
return 0;
}
}
@@ -2839,7 +2824,7 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond)
}
-static int get_schema_tables_record(THD *thd, struct st_table_list *tables,
+static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
const char *base_name,
const char *file_name)
@@ -3031,7 +3016,7 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables,
}
-static int get_schema_column_record(THD *thd, struct st_table_list *tables,
+static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
const char *base_name,
const char *file_name)
@@ -3538,7 +3523,7 @@ err:
}
-static int get_schema_stat_record(THD *thd, struct st_table_list *tables,
+static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
const char *base_name,
const char *file_name)
@@ -3628,7 +3613,7 @@ static int get_schema_stat_record(THD *thd, struct st_table_list *tables,
}
-static int get_schema_views_record(THD *thd, struct st_table_list *tables,
+static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
const char *base_name,
const char *file_name)
@@ -3746,7 +3731,7 @@ bool store_constraints(THD *thd, TABLE *table, const char *db,
}
-static int get_schema_constraints_record(THD *thd, struct st_table_list *tables,
+static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
const char *base_name,
const char *file_name)
@@ -3847,7 +3832,7 @@ static bool store_trigger(THD *thd, TABLE *table, const char *db,
}
-static int get_schema_triggers_record(THD *thd, struct st_table_list *tables,
+static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
const char *base_name,
const char *file_name)
@@ -3924,7 +3909,7 @@ void store_key_column_usage(TABLE *table, const char*db, const char *tname,
static int get_schema_key_column_usage_record(THD *thd,
- struct st_table_list *tables,
+ TABLE_LIST *tables,
TABLE *table, bool res,
const char *base_name,
const char *file_name)
@@ -4109,7 +4094,7 @@ static void store_schema_partitions_record(THD *thd, TABLE *schema_table,
}
-static int get_schema_partitions_record(THD *thd, struct st_table_list *tables,
+static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
const char *base_name,
const char *file_name)
@@ -4655,7 +4640,7 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond)
*/
static int
-get_referential_constraints_record(THD *thd, struct st_table_list *tables,
+get_referential_constraints_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
const char *base_name, const char *file_name)
{
@@ -5194,10 +5179,10 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
We have to make non const db_name & table_name
because of lower_case_table_names
*/
- make_lex_string(thd, &db, INFORMATION_SCHEMA_NAME.str,
- INFORMATION_SCHEMA_NAME.length, 0);
- make_lex_string(thd, &table, schema_table->table_name,
- strlen(schema_table->table_name), 0);
+ thd->make_lex_string(&db, INFORMATION_SCHEMA_NAME.str,
+ INFORMATION_SCHEMA_NAME.length, 0);
+ thd->make_lex_string(&table, schema_table->table_name,
+ strlen(schema_table->table_name), 0);
if (schema_table->old_format(thd, schema_table) || /* Handle old syntax */
!sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0),
0, 0, TL_READ))
@@ -5920,12 +5905,17 @@ int initialize_schema_table(st_plugin_int *plugin)
schema_table->idx_field1= -1,
schema_table->idx_field2= -1;
+ /* Make the name available to the init() function. */
+ schema_table->table_name= plugin->name.str;
+
if (plugin->plugin->init(schema_table))
{
sql_print_error("Plugin '%s' init function returned error.",
plugin->name.str);
goto err;
}
+
+ /* Make sure the plugin name is not set inside the init() function. */
schema_table->table_name= plugin->name.str;
}
@@ -5983,6 +5973,8 @@ static bool show_create_trigger_impl(THD *thd,
LEX_STRING trg_connection_cl_name;
LEX_STRING trg_db_cl_name;
+ CHARSET_INFO *trg_client_cs;
+
/*
TODO: Check privileges here. This functionality will be added by
implementation of the following WL items:
@@ -6008,6 +6000,11 @@ static bool show_create_trigger_impl(THD *thd,
trg_sql_mode,
&trg_sql_mode_str);
+ /* Resolve trigger client character set. */
+
+ if (resolve_charset(trg_client_cs_name.str, NULL, &trg_client_cs))
+ return TRUE;
+
/* Send header. */
fields.push_back(new Item_empty_string("Trigger", NAME_LEN));
@@ -6054,7 +6051,7 @@ static bool show_create_trigger_impl(THD *thd,
p->store(trg_sql_original_stmt.str,
trg_sql_original_stmt.length,
- &my_charset_bin);
+ trg_client_cs);
p->store(trg_client_cs_name.str,
trg_client_cs_name.length,
diff --git a/sql/sql_show.h b/sql/sql_show.h
index d5c3f3bf675..57004323ca9 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -20,9 +20,8 @@
class String;
class THD;
struct st_ha_create_information;
-struct st_table_list;
typedef st_ha_create_information HA_CREATE_INFO;
-typedef st_table_list TABLE_LIST;
+struct TABLE_LIST;
enum find_files_result {
FIND_FILES_OK,
diff --git a/sql/sql_sort.h b/sql/sql_sort.h
index da28ca07e2c..1e9322f7f5b 100644
--- a/sql/sql_sort.h
+++ b/sql/sql_sort.h
@@ -50,6 +50,12 @@ typedef struct st_buffpek { /* Struktur om sorteringsbuffrarna */
ulong max_keys; /* Max keys in buffert */
} BUFFPEK;
+struct BUFFPEK_COMPARE_CONTEXT
+{
+ qsort_cmp2 key_compare;
+ void *key_compare_arg;
+};
+
typedef struct st_sort_param {
uint rec_length; /* Length of sorted records */
uint sort_length; /* Length of sorted columns */
@@ -65,6 +71,9 @@ typedef struct st_sort_param {
uchar *unique_buff;
bool not_killable;
char* tmp_buffer;
+ /* The fields below are used only by Unique class */
+ qsort2_cmp compare;
+ BUFFPEK_COMPARE_CONTEXT cmp_context;
} SORTPARAM;
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 047c210d6a5..dc3e72554fa 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1430,19 +1430,8 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
LOCK_open during wait_if_global_read_lock(), other threads could not
close their tables. This would make a pretty deadlock.
*/
- thd->mysys_var->current_mutex= &LOCK_open;
- thd->mysys_var->current_cond= &COND_refresh;
- VOID(pthread_mutex_lock(&LOCK_open));
-
error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
- pthread_mutex_unlock(&LOCK_open);
-
- pthread_mutex_lock(&thd->mysys_var->mutex);
- thd->mysys_var->current_mutex= 0;
- thd->mysys_var->current_cond= 0;
- pthread_mutex_unlock(&thd->mysys_var->mutex);
-
if (need_start_waiters)
start_waiting_global_read_lock(thd);
@@ -1452,49 +1441,6 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
DBUG_RETURN(FALSE);
}
-
-/*
- delete (drop) tables.
-
- SYNOPSIS
- mysql_rm_table_part2_with_lock()
- thd Thread handle
- tables List of tables to delete
- if_exists If 1, don't give error if one table doesn't exists
- dont_log_query Don't write query to log files. This will also not
- generate warnings if the handler files doesn't exists
-
- NOTES
- Works like documented in mysql_rm_table(), but don't check
- global_read_lock and don't send_ok packet to server.
-
- RETURN
- 0 ok
- 1 error
-*/
-
-int mysql_rm_table_part2_with_lock(THD *thd,
- TABLE_LIST *tables, bool if_exists,
- bool drop_temporary, bool dont_log_query)
-{
- int error;
- thd->mysys_var->current_mutex= &LOCK_open;
- thd->mysys_var->current_cond= &COND_refresh;
- VOID(pthread_mutex_lock(&LOCK_open));
-
- error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 1,
- dont_log_query);
-
- pthread_mutex_unlock(&LOCK_open);
-
- pthread_mutex_lock(&thd->mysys_var->mutex);
- thd->mysys_var->current_mutex= 0;
- thd->mysys_var->current_cond= 0;
- pthread_mutex_unlock(&thd->mysys_var->mutex);
- return error;
-}
-
-
/*
Execute the drop of a normal or temporary table
@@ -1541,7 +1487,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
LINT_INIT(alias);
LINT_INIT(path_length);
- safe_mutex_assert_owner(&LOCK_open);
if (thd->current_stmt_binlog_row_based && !dont_log_query)
{
@@ -1551,6 +1496,9 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
else
built_query.append("DROP TABLE ");
}
+
+ pthread_mutex_lock(&LOCK_open);
+
/*
If we have the table in the definition cache, we don't have to check the
.frm file to find if the table is a normal table (not view) and what
@@ -1570,12 +1518,16 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
table->table_name_length, table->table_name, 1))
{
my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
+ pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(1);
}
}
- if (!drop_temporary && lock_table_names(thd, tables))
+ if (!drop_temporary && lock_table_names_exclusively(thd, tables))
+ {
+ pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(1);
+ }
/* Don't give warnings for not found errors, as we already generate notes */
thd->no_warnings_for_error= 1;
@@ -1586,7 +1538,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
handlerton *table_type;
enum legacy_db_type frm_db_type;
- mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, TRUE);
+ mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, 1);
if (!close_temporary_table(thd, table))
{
tmp_table_deleted=1;
@@ -1635,8 +1587,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
if (thd->killed)
{
- thd->no_warnings_for_error= 0;
- DBUG_RETURN(-1);
+ error= -1;
+ goto err_with_placeholders;
}
alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
/* remove .frm file and engine files */
@@ -1699,6 +1651,11 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
wrong_tables.append(String(table->table_name,system_charset_info));
}
}
+ /*
+ It's safe to unlock LOCK_open: we have an exclusive lock
+ on the table name.
+ */
+ pthread_mutex_unlock(&LOCK_open);
thd->tmp_table_used= tmp_table_deleted;
error= 0;
if (wrong_tables.length())
@@ -1758,9 +1715,10 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
*/
}
}
-
- if (!drop_temporary)
- unlock_table_names(thd, tables, (TABLE_LIST*) 0);
+ pthread_mutex_lock(&LOCK_open);
+err_with_placeholders:
+ unlock_table_names(thd, tables, (TABLE_LIST*) 0);
+ pthread_mutex_unlock(&LOCK_open);
thd->no_warnings_for_error= 0;
DBUG_RETURN(error);
}
@@ -4697,8 +4655,11 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
DBUG_EXECUTE_IF("sleep_create_like_before_ha_create", my_sleep(6000000););
dst_path[dst_path_length - reg_ext_length]= '\0'; // Remove .frm
+ if (thd->variables.keep_files_on_create)
+ create_info->options|= HA_CREATE_KEEP_FILES;
err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
VOID(pthread_mutex_unlock(&LOCK_open));
+
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
{
if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 06dd0dded43..fa0154dc39e 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -1212,17 +1212,6 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
table->triggers= triggers;
/*
- Construct key that will represent triggers for this table in the set
- of routines used by statement.
- */
- triggers->sroutines_key.length= 1+strlen(db)+1+strlen(table_name)+1;
- if (!(triggers->sroutines_key.str= (char*)
- alloc_root(&table->mem_root, triggers->sroutines_key.length)))
- DBUG_RETURN(1);
- triggers->sroutines_key.str[0]= TYPE_ENUM_TRIGGER;
- strxmov(triggers->sroutines_key.str+1, db, ".", table_name, NullS);
-
- /*
TODO: This could be avoided if there is no triggers
for UPDATE and DELETE.
*/
@@ -1270,6 +1259,15 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
DBUG_ASSERT(lex.sphead == 0);
goto err_with_lex_cleanup;
}
+ /*
+ Not strictly necessary to invoke this method here, since we know
+ that we've parsed CREATE TRIGGER and not an
+ UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
+ maintain the invariant that this method is called for each
+ distinct statement, in case its logic is extended with other
+ types of analyses in future.
+ */
+ lex.set_trg_event_type_for_tables();
lex.sphead->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode);
@@ -1606,8 +1604,6 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name)
bzero(&table, sizeof(table));
init_alloc_root(&table.mem_root, 8192, 0);
- safe_mutex_assert_owner(&LOCK_open);
-
if (Table_triggers_list::check_n_load(thd, db, name, &table, 1))
{
result= 1;
@@ -1774,26 +1770,24 @@ Table_triggers_list::change_table_name_in_trignames(const char *db_name,
}
-/*
- Update .TRG and .TRN files after renaming triggers' subject table.
+/**
+ @brief Update .TRG and .TRN files after renaming triggers' subject table.
- SYNOPSIS
- change_table_name()
- thd Thread context
- db Old database of subject table
- old_table Old name of subject table
- new_db New database for subject table
- new_table New name of subject table
+ @param[in,out] thd Thread context
+ @param[in] db Old database of subject table
+ @param[in] old_table Old name of subject table
+ @param[in] new_db New database for subject table
+ @param[in] new_table New name of subject table
- NOTE
+ @note
This method tries to leave trigger related files in consistent state,
i.e. it either will complete successfully, or will fail leaving files
in their initial state.
Also this method assumes that subject table is not renamed to itself.
+ This method needs to be called under an exclusive table name lock.
- RETURN VALUE
- FALSE Success
- TRUE Error
+ @retval FALSE Success
+ @retval TRUE Error
*/
bool Table_triggers_list::change_table_name(THD *thd, const char *db,
@@ -1809,7 +1803,19 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
bzero(&table, sizeof(table));
init_alloc_root(&table.mem_root, 8192, 0);
- safe_mutex_assert_owner(&LOCK_open);
+ /*
+ This method interfaces the mysql server code protected by
+ either LOCK_open mutex or with an exclusive table name lock.
+ In the future, only an exclusive table name lock will be enough.
+ */
+#ifndef DBUG_OFF
+ uchar key[MAX_DBKEY_LENGTH];
+ uint key_length= (uint) (strmov(strmov((char*)&key[0], db)+1,
+ old_table)-(char*)&key[0])+1;
+
+ if (!is_table_name_exclusively_locked_by_this_thread(thd, key, key_length))
+ safe_mutex_assert_owner(&LOCK_open);
+#endif
DBUG_ASSERT(my_strcasecmp(table_alias_charset, db, new_db) ||
my_strcasecmp(table_alias_charset, old_table, new_table));
@@ -1891,8 +1897,9 @@ bool Table_triggers_list::process_triggers(THD *thd,
{
bool err_status;
Sub_statement_state statement_state;
+ sp_head *sp_trigger= bodies[event][time_type];
- if (!bodies[event][time_type])
+ if (sp_trigger == NULL)
return FALSE;
if (old_row_is_record1)
@@ -1905,15 +1912,20 @@ bool Table_triggers_list::process_triggers(THD *thd,
new_field= record1_field;
old_field= trigger_table->field;
}
+ /*
+ This trigger must have been processed by the pre-locking
+ algorithm.
+ */
+ DBUG_ASSERT(trigger_table->pos_in_table_list->trg_event_map &
+ static_cast<uint>(1 << static_cast<int>(event)));
thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
err_status=
- bodies[event][time_type]->execute_trigger(
- thd,
- &trigger_table->s->db,
- &trigger_table->s->table_name,
- &subject_table_grants[event][time_type]);
+ sp_trigger->execute_trigger(thd,
+ &trigger_table->s->db,
+ &trigger_table->s->table_name,
+ &subject_table_grants[event][time_type]);
thd->restore_sub_statement_state(&statement_state);
@@ -1927,7 +1939,7 @@ bool Table_triggers_list::process_triggers(THD *thd,
SYNOPSIS
mark_fields_used()
thd Current thread context
- event Type of event triggers for which we are going to inspect
+ event Type of event triggers for which we are going to ins
DESCRIPTION
This method marks fields of subject table which are read/set in its
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index bfdbae12bdc..8f6b08c927f 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -56,14 +56,6 @@ class Table_triggers_list: public Sql_alloc
updating trigger definitions during RENAME TABLE.
*/
List<LEX_STRING> on_table_names_list;
- /*
- Key representing triggers for this table in set of all stored
- routines used by statement.
- TODO: We won't need this member once triggers namespace will be
- database-wide instead of table-wide because then we will be able
- to use key based on sp_name as for other stored routines.
- */
- LEX_STRING sroutines_key;
/*
Grant information for each trigger (pair: subject table, trigger definer).
diff --git a/sql/sql_udf.h b/sql/sql_udf.h
index 3cd9343610c..4b8b492698e 100644
--- a/sql/sql_udf.h
+++ b/sql/sql_udf.h
@@ -47,7 +47,6 @@ typedef struct st_udf_func
} udf_func;
class Item_result_field;
-struct st_table_list;
class udf_handler :public Sql_alloc
{
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index ef1f46bfdd2..1016a23b5ae 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -83,6 +83,75 @@ static bool check_fields(THD *thd, List<Item> &items)
}
+/**
+ @brief Re-read record if more columns are needed for error message.
+
+ @detail If we got a duplicate key error, we want to write an error
+ message containing the value of the duplicate key. If we do not have
+ all fields of the key value in record[0], we need to re-read the
+ record with a proper read_set.
+
+ @param[in] error error number
+ @param[in] table table
+*/
+
+static void prepare_record_for_error_message(int error, TABLE *table)
+{
+ Field **field_p;
+ Field *field;
+ uint keynr;
+ MY_BITMAP unique_map; /* Fields in offended unique. */
+ my_bitmap_map unique_map_buf[bitmap_buffer_size(MAX_FIELDS)];
+ DBUG_ENTER("prepare_record_for_error_message");
+
+ /*
+ Only duplicate key errors print the key value.
+ If storage engine does always read all columns, we have the value alraedy.
+ */
+ if ((error != HA_ERR_FOUND_DUPP_KEY) ||
+ !(table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ))
+ DBUG_VOID_RETURN;
+
+ /*
+ Get the number of the offended index.
+ We will see MAX_KEY if the engine cannot determine the affected index.
+ */
+ if ((keynr= table->file->get_dup_key(error)) >= MAX_KEY)
+ DBUG_VOID_RETURN;
+
+ /* Create unique_map with all fields used by that index. */
+ bitmap_init(&unique_map, unique_map_buf, table->s->fields, FALSE);
+ table->mark_columns_used_by_index_no_reset(keynr, &unique_map);
+
+ /* Subtract read_set and write_set. */
+ bitmap_subtract(&unique_map, table->read_set);
+ bitmap_subtract(&unique_map, table->write_set);
+
+ /*
+ If the unique index uses columns that are neither in read_set
+ nor in write_set, we must re-read the record.
+ Otherwise no need to do anything.
+ */
+ if (bitmap_is_clear_all(&unique_map))
+ DBUG_VOID_RETURN;
+
+ /* Get identifier of last read record into table->file->ref. */
+ table->file->position(table->record[0]);
+ /* Add all fields used by unique index to read_set. */
+ bitmap_union(table->read_set, &unique_map);
+ /* Tell the engine about the new set. */
+ table->file->column_bitmaps_signal();
+ /* Read record that is identified by table->file->ref. */
+ (void) table->file->rnd_pos(table->record[1], table->file->ref);
+ /* Copy the newly read columns into the new record. */
+ for (field_p= table->field; (field= *field_p); field_p++)
+ if (bitmap_is_set(&unique_map, field->field_index))
+ field->copy_from_tmp(table->s->rec_buff_length);
+
+ DBUG_VOID_RETURN;
+}
+
+
/*
Process usual UPDATE
@@ -231,6 +300,17 @@ int mysql_update(THD *thd,
if (cond_value == Item::COND_FALSE)
limit= 0; // Impossible WHERE
}
+
+ /*
+ If a timestamp field settable on UPDATE is present then to avoid wrong
+ update force the table handler to retrieve write-only fields to be able
+ to compare records and detect data change.
+ */
+ if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ &&
+ table->timestamp_field &&
+ (table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_UPDATE ||
+ table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH))
+ bitmap_union(table->read_set, table->write_set);
// Don't count on usage of 'only index' when calculating which key to use
table->covering_keys.clear_all();
@@ -471,6 +551,13 @@ int mysql_update(THD *thd,
will_batch= !table->file->start_bulk_update();
/*
+ Assure that we can use position()
+ if we need to create an error message.
+ */
+ if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ)
+ table->prepare_for_position();
+
+ /*
We can use compare_record() to optimize away updates if
the table handler is returning all columns OR if
if all updated columns are read
@@ -573,6 +660,8 @@ int mysql_update(THD *thd,
*/
if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
thd->fatal_error(); /* Other handler errors are fatal */
+
+ prepare_record_for_error_message(error, table);
table->file->print_error(error,MYF(0));
error= 1;
break;
@@ -596,13 +685,16 @@ int mysql_update(THD *thd,
{
if (error)
{
+ /* purecov: begin inspected */
/*
The handler should not report error of duplicate keys if they
are ignored. This is a requirement on batching handlers.
*/
+ prepare_record_for_error_message(error, table);
table->file->print_error(error,MYF(0));
error= 1;
break;
+ /* purecov: end */
}
/*
Either an error was found and we are ignoring errors or there
@@ -668,9 +760,12 @@ int mysql_update(THD *thd,
in the batched update.
*/
{
+ /* purecov: begin inspected */
thd->fatal_error();
+ prepare_record_for_error_message(loc_error, table);
table->file->print_error(loc_error,MYF(0));
error= 1;
+ /* purecov: end */
}
else
updated-= dup_key_found;
@@ -1540,6 +1635,8 @@ bool multi_update::send_data(List<Item> &not_used_values)
*/
if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
thd->fatal_error(); /* Other handler errors are fatal */
+
+ prepare_record_for_error_message(error, table);
table->file->print_error(error,MYF(0));
DBUG_RETURN(1);
}
@@ -1676,7 +1773,7 @@ int multi_update::do_updates(bool from_send_error)
ha_rows org_updated;
TABLE *table, *tmp_table;
List_iterator_fast<TABLE> check_opt_it(unupdated_check_opt_tables);
- DBUG_ENTER("do_updates");
+ DBUG_ENTER("multi_update::do_updates");
do_update= 0; // Don't retry this function
if (!found)
@@ -1819,6 +1916,7 @@ err:
if (!from_send_error)
{
thd->fatal_error();
+ prepare_record_for_error_message(local_error, table);
table->file->print_error(local_error,MYF(0));
}
@@ -1849,6 +1947,7 @@ bool multi_update::send_eof()
{
char buff[STRING_BUFFER_USUAL_SIZE];
ulonglong id;
+ DBUG_ENTER("multi_update::send_eof");
thd->proc_info="updating reference tables";
/* Does updates for the last n - 1 tables, returns 0 if ok */
@@ -1904,7 +2003,7 @@ bool multi_update::send_eof()
/* Safety: If we haven't got an error before (can happen in do_updates) */
my_message(ER_UNKNOWN_ERROR, "An error occured in multi-table update",
MYF(0));
- return TRUE;
+ DBUG_RETURN(TRUE);
}
id= thd->arg_of_last_insert_id_function ?
@@ -1914,5 +2013,5 @@ bool multi_update::send_eof()
thd->row_count_func=
(thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
::send_ok(thd, (ulong) thd->row_count_func, id, buff);
- return FALSE;
+ DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index ce311f5d4a2..9a46bbc39e4 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -1199,7 +1199,20 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
*/
for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
tbl->lock_type= table->lock_type;
+ /*
+ If the view is mergeable, we might want to
+ INSERT/UPDATE/DELETE into tables of this view. Preserve the
+ original sql command and 'duplicates' of the outer lex.
+ This is used later in set_trg_event_type_for_command.
+ */
+ lex->sql_command= old_lex->sql_command;
+ lex->duplicates= old_lex->duplicates;
}
+ /*
+ This method has a dependency on the proper lock type being set,
+ so in case of views should be called here.
+ */
+ lex->set_trg_event_type_for_tables();
/*
If we are opening this view as part of implicit LOCK TABLES, then
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 3bd930f6041..d9a808bf8f7 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1941,12 +1941,13 @@ sp_name:
| ident
{
THD *thd= YYTHD;
+ LEX *lex= thd->lex;
LEX_STRING db;
if (check_routine_name(&$1))
{
MYSQL_YYABORT;
}
- if (thd->copy_db_to(&db.str, &db.length))
+ if (lex->copy_db_to(&db.str, &db.length))
MYSQL_YYABORT;
$$= new sp_name(db, $1, false);
if ($$)
@@ -4107,8 +4108,7 @@ part_bit_expr:
}
Lex->part_info->curr_part_elem->has_null_value= TRUE;
}
- else if (part_expr->result_type() != INT_RESULT &&
- !part_expr->null_value)
+ else if (part_expr->result_type() != INT_RESULT)
{
my_parse_error(ER(ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR));
MYSQL_YYABORT;
@@ -5131,14 +5131,13 @@ alter:
Lex->create_info.default_table_charset= NULL;
Lex->create_info.used_fields= 0;
}
- opt_create_database_options
+ create_database_options
{
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
+ LEX *lex=Lex;
lex->sql_command=SQLCOM_ALTER_DB;
lex->name= $3;
if (lex->name.str == NULL &&
- thd->copy_db_to(&lex->name.str, &lex->name.length))
+ lex->copy_db_to(&lex->name.str, &lex->name.length))
MYSQL_YYABORT;
}
| ALTER PROCEDURE sp_name
@@ -5592,12 +5591,11 @@ alter_list_item:
}
| RENAME opt_to table_ident
{
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
+ LEX *lex=Lex;
size_t dummy;
lex->select_lex.db=$3->db.str;
if (lex->select_lex.db == NULL &&
- thd->copy_db_to(&lex->select_lex.db, &dummy))
+ lex->copy_db_to(&lex->select_lex.db, &dummy))
{
MYSQL_YYABORT;
}
@@ -10882,10 +10880,9 @@ require_list_element:
grant_ident:
'*'
{
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
+ LEX *lex= Lex;
size_t dummy;
- if (thd->copy_db_to(&lex->current_select->db, &dummy))
+ if (lex->copy_db_to(&lex->current_select->db, &dummy))
MYSQL_YYABORT;
if (lex->grant == GLOBAL_ACLS)
lex->grant = DB_ACLS & ~GRANT_ACL;
@@ -11531,12 +11528,12 @@ trigger_tail:
MYSQL_YYABORT;
sp->reset_thd_mem_root(thd);
sp->init(lex);
+ sp->m_type= TYPE_ENUM_TRIGGER;
sp->init_sp_name(thd, $3);
lex->stmt_definition_begin= $2;
lex->ident.str= $7;
lex->ident.length= $11 - $7;
- sp->m_type= TYPE_ENUM_TRIGGER;
lex->sphead= sp;
lex->spname= $3;
/*
@@ -11612,9 +11609,9 @@ sp_tail:
sp= new sp_head();
sp->reset_thd_mem_root(YYTHD);
sp->init(lex);
+ sp->m_type= TYPE_ENUM_PROCEDURE;
sp->init_sp_name(YYTHD, $3);
- sp->m_type= TYPE_ENUM_PROCEDURE;
lex->sphead= sp;
/*
* We have to turn of CLIENT_MULTI_QUERIES while parsing a
diff --git a/sql/stacktrace.c b/sql/stacktrace.c
index 40507907120..b1267e20774 100644
--- a/sql/stacktrace.c
+++ b/sql/stacktrace.c
@@ -228,6 +228,15 @@ void write_core(int sig)
void write_core(int sig)
{
signal(sig, SIG_DFL);
+#ifdef HAVE_gcov
+ /*
+ For GCOV build, crashing will prevent the writing of code coverage
+ information from this process, causing gcov output to be incomplete.
+ So we force the writing of coverage information here before terminating.
+ */
+ extern void __gcov_flush(void);
+ __gcov_flush();
+#endif
pthread_kill(pthread_self(), sig);
#if defined(P_MYID) && !defined(SCO)
/* On Solaris, the above kill is not enough */
diff --git a/sql/table.cc b/sql/table.cc
index 27a93b85fb5..a58f59d3a75 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -19,7 +19,7 @@
#include "mysql_priv.h"
#include "sql_trigger.h"
#include <m_ctype.h>
-#include "md5.h"
+#include "my_md5.h"
/* Functions defined in this file */
@@ -98,7 +98,7 @@ View_creation_ctx *View_creation_ctx::create(THD *thd)
/*************************************************************************/
View_creation_ctx * View_creation_ctx::create(THD *thd,
- st_table_list *view)
+ TABLE_LIST *view)
{
View_creation_ctx *ctx= new (thd->mem_root) View_creation_ctx(thd);
@@ -2849,15 +2849,144 @@ void st_table::reset_item_list(List<Item> *item_list) const
}
}
+
+/**
+ Set the initial purpose of this TABLE_LIST object in the list of
+ used tables. We need to track this information on table-by-
+ table basis, since when this table becomes an element of the
+ pre-locked list, it's impossible to identify which SQL
+ sub-statement it has been originally used in.
+
+ E.g.:
+
+ User request: SELECT * FROM t1 WHERE f1();
+ FUNCTION f1(): DELETE FROM t2; RETURN 1;
+ BEFORE DELETE trigger on t2: INSERT INTO t3 VALUES (old.a);
+
+ For this user request, the pre-locked list will contain t1, t2, t3
+ table elements, each needed for different DML.
+
+ This method is called immediately after parsing for tables
+ of the table list of the top-level select lex.
+
+ The trigger event map is updated to reflect INSERT, UPDATE, DELETE,
+ REPLACE, LOAD DATA, CREATE TABLE .. SELECT, CREATE TABLE ..
+ REPLACE SELECT statements, and additionally ON DUPLICATE KEY UPDATE
+ clause.
+*/
+
+void
+TABLE_LIST::set_trg_event_type(const st_lex *lex)
+{
+ enum trg_event_type trg_event;
+
+ /*
+ Some auxiliary operations
+ (e.g. GRANT processing) create TABLE_LIST instances outside
+ the parser. Additionally, some commands (e.g. OPTIMIZE) change
+ the lock type for a table only after parsing is done. Luckily,
+ these do not fire triggers and do not need to pre-load them.
+ For these TABLE_LISTs set_trg_event_type is never called, and
+ trg_event_map is always empty. That means that the pre-locking
+ algorithm will ignore triggers defined on these tables, if
+ any, and the execution will either fail with an assert in
+ sql_trigger.cc or with an error that a used table was not
+ pre-locked, in case of a production build.
+
+ TODO: this usage pattern creates unnecessary module dependencies
+ and should be rewritten to go through the parser.
+ Table list instances created outside the parser in most cases
+ refer to mysql.* system tables. It is not allowed to have
+ a trigger on a system table, but keeping track of
+ initialization provides extra safety in case this limitation
+ is circumvented.
+ */
+
+ /*
+ This is a fast check to filter out statements that do
+ not change data, or tables on the right side, in case of
+ INSERT .. SELECT, CREATE TABLE .. SELECT and so on.
+ Here we also filter out OPTIMIZE statement and non-updateable
+ views, for which lock_type is TL_UNLOCK or TL_READ after
+ parsing.
+ */
+ if (static_cast<int>(lock_type) < static_cast<int>(TL_WRITE_ALLOW_WRITE))
+ return;
+
+ switch (lex->sql_command) {
+ /*
+ Basic INSERT. If there is an additional ON DUPLIATE KEY UPDATE
+ clause, it will be handled later in this method.
+ */
+ case SQLCOM_INSERT: /* fall through */
+ case SQLCOM_INSERT_SELECT:
+ /*
+ LOAD DATA ... INFILE is expected to fire BEFORE/AFTER INSERT
+ triggers.
+ If the statement also has REPLACE clause, it will be
+ handled later in this method.
+ */
+ case SQLCOM_LOAD: /* fall through */
+ /*
+ REPLACE is semantically equivalent to INSERT. In case
+ of a primary or unique key conflict, it deletes the old
+ record and inserts a new one. So we also may need to
+ fire ON DELETE triggers. This functionality is handled
+ later in this method.
+ */
+ case SQLCOM_REPLACE: /* fall through */
+ case SQLCOM_REPLACE_SELECT:
+ /*
+ CREATE TABLE ... SELECT defaults to INSERT if the table or
+ view already exists. REPLACE option of CREATE TABLE ...
+ REPLACE SELECT is handled later in this method.
+ */
+ case SQLCOM_CREATE_TABLE:
+ trg_event= TRG_EVENT_INSERT;
+ break;
+ /* Basic update and multi-update */
+ case SQLCOM_UPDATE: /* fall through */
+ case SQLCOM_UPDATE_MULTI:
+ trg_event= TRG_EVENT_UPDATE;
+ break;
+ /* Basic delete and multi-delete */
+ case SQLCOM_DELETE: /* fall through */
+ case SQLCOM_DELETE_MULTI:
+ trg_event= TRG_EVENT_DELETE;
+ break;
+ default:
+ /*
+ OK to return, since value of 'duplicates' is irrelevant
+ for non-updating commands.
+ */
+ return;
+ }
+ trg_event_map|= static_cast<uint8>(1 << static_cast<int>(trg_event));
+
+ switch (lex->duplicates) {
+ case DUP_UPDATE:
+ trg_event= TRG_EVENT_UPDATE;
+ break;
+ case DUP_REPLACE:
+ trg_event= TRG_EVENT_DELETE;
+ break;
+ case DUP_ERROR:
+ default:
+ return;
+ }
+ trg_event_map|= static_cast<uint8>(1 << static_cast<int>(trg_event));
+}
+
+
/*
calculate md5 of query
SYNOPSIS
- st_table_list::calc_md5()
+ TABLE_LIST::calc_md5()
buffer buffer for md5 writing
*/
-void st_table_list::calc_md5(char *buffer)
+void TABLE_LIST::calc_md5(char *buffer)
{
my_MD5_CTX context;
uchar digest[16];
@@ -2882,10 +3011,10 @@ void st_table_list::calc_md5(char *buffer)
it (it is a kind of optimisation)
SYNOPSIS
- st_table_list::set_underlying_merge()
+ TABLE_LIST::set_underlying_merge()
*/
-void st_table_list::set_underlying_merge()
+void TABLE_LIST::set_underlying_merge()
{
TABLE_LIST *tbl;
@@ -2920,7 +3049,7 @@ void st_table_list::set_underlying_merge()
setup fields of placeholder of merged VIEW
SYNOPSIS
- st_table_list::setup_underlying()
+ TABLE_LIST::setup_underlying()
thd - thread handler
DESCRIPTION
@@ -2933,9 +3062,9 @@ void st_table_list::set_underlying_merge()
TRUE - error
*/
-bool st_table_list::setup_underlying(THD *thd)
+bool TABLE_LIST::setup_underlying(THD *thd)
{
- DBUG_ENTER("st_table_list::setup_underlying");
+ DBUG_ENTER("TABLE_LIST::setup_underlying");
if (!field_translation && merge_underlying_list)
{
@@ -2998,7 +3127,7 @@ bool st_table_list::setup_underlying(THD *thd)
Prepare where expression of view
SYNOPSIS
- st_table_list::prep_where()
+ TABLE_LIST::prep_where()
thd - thread handler
conds - condition of this JOIN
no_where_clause - do not build WHERE or ON outer qwery do not need it
@@ -3012,10 +3141,10 @@ bool st_table_list::setup_underlying(THD *thd)
TRUE - error
*/
-bool st_table_list::prep_where(THD *thd, Item **conds,
+bool TABLE_LIST::prep_where(THD *thd, Item **conds,
bool no_where_clause)
{
- DBUG_ENTER("st_table_list::prep_where");
+ DBUG_ENTER("TABLE_LIST::prep_where");
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
@@ -3115,7 +3244,7 @@ merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded)
Prepare check option expression of table
SYNOPSIS
- st_table_list::prep_check_option()
+ TABLE_LIST::prep_check_option()
thd - thread handler
check_opt_type - WITH CHECK OPTION type (VIEW_CHECK_NONE,
VIEW_CHECK_LOCAL, VIEW_CHECK_CASCADED)
@@ -3130,16 +3259,16 @@ merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded)
This method builds check option condition to use it later on
every call (usual execution or every SP/PS call).
This method have to be called after WHERE preparation
- (st_table_list::prep_where)
+ (TABLE_LIST::prep_where)
RETURN
FALSE - OK
TRUE - error
*/
-bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type)
+bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
{
- DBUG_ENTER("st_table_list::prep_check_option");
+ DBUG_ENTER("TABLE_LIST::prep_check_option");
bool is_cascaded= check_opt_type == VIEW_CHECK_CASCADED;
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
@@ -3198,12 +3327,12 @@ bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type)
Hide errors which show view underlying table information
SYNOPSIS
- st_table_list::hide_view_error()
+ TABLE_LIST::hide_view_error()
thd thread handler
*/
-void st_table_list::hide_view_error(THD *thd)
+void TABLE_LIST::hide_view_error(THD *thd)
{
/* Hide "Unknown column" or "Unknown function" error */
if (thd->net.last_errno == ER_BAD_FIELD_ERROR ||
@@ -3234,7 +3363,7 @@ void st_table_list::hide_view_error(THD *thd)
table_to_find (TABLE)
SYNOPSIS
- st_table_list::find_underlying_table()
+ TABLE_LIST::find_underlying_table()
table_to_find table to find
RETURN
@@ -3242,7 +3371,7 @@ void st_table_list::hide_view_error(THD *thd)
found table reference
*/
-st_table_list *st_table_list::find_underlying_table(TABLE *table_to_find)
+TABLE_LIST *TABLE_LIST::find_underlying_table(TABLE *table_to_find)
{
/* is this real table and table which we are looking for? */
if (table == table_to_find && merge_underlying_list == 0)
@@ -3261,10 +3390,10 @@ st_table_list *st_table_list::find_underlying_table(TABLE *table_to_find)
cleunup items belonged to view fields translation table
SYNOPSIS
- st_table_list::cleanup_items()
+ TABLE_LIST::cleanup_items()
*/
-void st_table_list::cleanup_items()
+void TABLE_LIST::cleanup_items()
{
if (!field_translation)
return;
@@ -3280,7 +3409,7 @@ void st_table_list::cleanup_items()
check CHECK OPTION condition
SYNOPSIS
- st_table_list::view_check_option()
+ TABLE_LIST::view_check_option()
ignore_failure ignore check option fail
RETURN
@@ -3289,7 +3418,7 @@ void st_table_list::cleanup_items()
VIEW_CHECK_SKIP FAILED, but continue
*/
-int st_table_list::view_check_option(THD *thd, bool ignore_failure)
+int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure)
{
if (check_option && check_option->val_int() == 0)
{
@@ -3314,7 +3443,7 @@ int st_table_list::view_check_option(THD *thd, bool ignore_failure)
table belong to given mask
SYNOPSIS
- st_table_list::check_single_table()
+ TABLE_LIST::check_single_table()
table_arg reference on variable where to store found table
(should be 0 on call, to find table, or point to table for
unique test)
@@ -3326,9 +3455,9 @@ int st_table_list::view_check_option(THD *thd, bool ignore_failure)
TRUE found several tables
*/
-bool st_table_list::check_single_table(st_table_list **table_arg,
+bool TABLE_LIST::check_single_table(TABLE_LIST **table_arg,
table_map map,
- st_table_list *view_arg)
+ TABLE_LIST *view_arg)
{
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
@@ -3361,7 +3490,7 @@ bool st_table_list::check_single_table(st_table_list **table_arg,
TRUE - out of memory
*/
-bool st_table_list::set_insert_values(MEM_ROOT *mem_root)
+bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root)
{
if (table)
{
@@ -3385,7 +3514,7 @@ bool st_table_list::set_insert_values(MEM_ROOT *mem_root)
Test if this is a leaf with respect to name resolution.
SYNOPSIS
- st_table_list::is_leaf_for_name_resolution()
+ TABLE_LIST::is_leaf_for_name_resolution()
DESCRIPTION
A table reference is a leaf with respect to name resolution if
@@ -3397,7 +3526,7 @@ bool st_table_list::set_insert_values(MEM_ROOT *mem_root)
RETURN
TRUE if a leaf, FALSE otherwise.
*/
-bool st_table_list::is_leaf_for_name_resolution()
+bool TABLE_LIST::is_leaf_for_name_resolution()
{
return (view || is_natural_join || is_join_columns_complete ||
!nested_join);
@@ -3409,7 +3538,7 @@ bool st_table_list::is_leaf_for_name_resolution()
respect to name resolution.
SYNOPSIS
- st_table_list::first_leaf_for_name_resolution()
+ TABLE_LIST::first_leaf_for_name_resolution()
DESCRIPTION
Given that 'this' is a nested table reference, recursively walk
@@ -3427,7 +3556,7 @@ bool st_table_list::is_leaf_for_name_resolution()
else return 'this'
*/
-TABLE_LIST *st_table_list::first_leaf_for_name_resolution()
+TABLE_LIST *TABLE_LIST::first_leaf_for_name_resolution()
{
TABLE_LIST *cur_table_ref;
NESTED_JOIN *cur_nested_join;
@@ -3467,7 +3596,7 @@ TABLE_LIST *st_table_list::first_leaf_for_name_resolution()
respect to name resolution.
SYNOPSIS
- st_table_list::last_leaf_for_name_resolution()
+ TABLE_LIST::last_leaf_for_name_resolution()
DESCRIPTION
Given that 'this' is a nested table reference, recursively walk
@@ -3485,7 +3614,7 @@ TABLE_LIST *st_table_list::first_leaf_for_name_resolution()
- else - 'this'
*/
-TABLE_LIST *st_table_list::last_leaf_for_name_resolution()
+TABLE_LIST *TABLE_LIST::last_leaf_for_name_resolution()
{
TABLE_LIST *cur_table_ref= this;
NESTED_JOIN *cur_nested_join;
@@ -3527,7 +3656,7 @@ TABLE_LIST *st_table_list::last_leaf_for_name_resolution()
want_access Acess which we require
*/
-void st_table_list::register_want_access(ulong want_access)
+void TABLE_LIST::register_want_access(ulong want_access)
{
/* Remove SHOW_VIEW_ACL, because it will be checked during making view */
want_access&= ~SHOW_VIEW_ACL;
@@ -3546,7 +3675,7 @@ void st_table_list::register_want_access(ulong want_access)
Load security context information for this view
SYNOPSIS
- st_table_list::prepare_view_securety_context()
+ TABLE_LIST::prepare_view_securety_context()
thd [in] thread handler
RETURN
@@ -3555,9 +3684,9 @@ void st_table_list::register_want_access(ulong want_access)
*/
#ifndef NO_EMBEDDED_ACCESS_CHECKS
-bool st_table_list::prepare_view_securety_context(THD *thd)
+bool TABLE_LIST::prepare_view_securety_context(THD *thd)
{
- DBUG_ENTER("st_table_list::prepare_view_securety_context");
+ DBUG_ENTER("TABLE_LIST::prepare_view_securety_context");
DBUG_PRINT("enter", ("table: %s", alias));
DBUG_ASSERT(!prelocking_placeholder && view);
@@ -3606,17 +3735,17 @@ bool st_table_list::prepare_view_securety_context(THD *thd)
Find security context of current view
SYNOPSIS
- st_table_list::find_view_security_context()
+ TABLE_LIST::find_view_security_context()
thd [in] thread handler
*/
#ifndef NO_EMBEDDED_ACCESS_CHECKS
-Security_context *st_table_list::find_view_security_context(THD *thd)
+Security_context *TABLE_LIST::find_view_security_context(THD *thd)
{
Security_context *sctx;
TABLE_LIST *upper_view= this;
- DBUG_ENTER("st_table_list::find_view_security_context");
+ DBUG_ENTER("TABLE_LIST::find_view_security_context");
DBUG_ASSERT(view);
while (upper_view && !upper_view->view_suid)
@@ -3645,7 +3774,7 @@ Security_context *st_table_list::find_view_security_context(THD *thd)
Prepare security context and load underlying tables priveleges for view
SYNOPSIS
- st_table_list::prepare_security()
+ TABLE_LIST::prepare_security()
thd [in] thread handler
RETURN
@@ -3653,11 +3782,11 @@ Security_context *st_table_list::find_view_security_context(THD *thd)
TRUE Error
*/
-bool st_table_list::prepare_security(THD *thd)
+bool TABLE_LIST::prepare_security(THD *thd)
{
List_iterator_fast<TABLE_LIST> tb(*view_tables);
TABLE_LIST *tbl;
- DBUG_ENTER("st_table_list::prepare_security");
+ DBUG_ENTER("TABLE_LIST::prepare_security");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *save_security_ctx= thd->security_ctx;
@@ -4406,10 +4535,10 @@ void st_table::mark_columns_needed_for_insert()
Cleanup this table for re-execution.
SYNOPSIS
- st_table_list::reinit_before_use()
+ TABLE_LIST::reinit_before_use()
*/
-void st_table_list::reinit_before_use(THD *thd)
+void TABLE_LIST::reinit_before_use(THD *thd)
{
/*
Reset old pointers to TABLEs: they are not valid since the tables
@@ -4436,7 +4565,7 @@ void st_table_list::reinit_before_use(THD *thd)
Return subselect that contains the FROM list this table is taken from
SYNOPSIS
- st_table_list::containing_subselect()
+ TABLE_LIST::containing_subselect()
RETURN
Subselect item for the subquery that contains the FROM list
@@ -4445,7 +4574,7 @@ void st_table_list::reinit_before_use(THD *thd)
*/
-Item_subselect *st_table_list::containing_subselect()
+Item_subselect *TABLE_LIST::containing_subselect()
{
return (select_lex ? select_lex->master_unit()->item : 0);
}
@@ -4459,7 +4588,7 @@ Item_subselect *st_table_list::containing_subselect()
DESCRIPTION
The parser collects the index hints for each table in a "tagged list"
- (st_table_list::index_hints). Using the information in this tagged list
+ (TABLE_LIST::index_hints). Using the information in this tagged list
this function sets the members st_table::keys_in_use_for_query,
st_table::keys_in_use_for_group_by, st_table::keys_in_use_for_order_by,
st_table::force_index and st_table::covering_keys.
@@ -4501,7 +4630,7 @@ Item_subselect *st_table_list::containing_subselect()
FALSE no errors found
TRUE found and reported an error.
*/
-bool st_table_list::process_index_hints(TABLE *table)
+bool TABLE_LIST::process_index_hints(TABLE *table)
{
/* initialize the result variables */
table->keys_in_use_for_query= table->keys_in_use_for_group_by=
diff --git a/sql/table.h b/sql/table.h
index 4c98f5146ab..494b74d564c 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -38,7 +38,7 @@ public:
static View_creation_ctx *create(THD *thd);
static View_creation_ctx *create(THD *thd,
- struct st_table_list *view);
+ TABLE_LIST *view);
private:
View_creation_ctx(THD *thd)
@@ -85,6 +85,15 @@ enum tmp_table_type
INTERNAL_TMP_TABLE, SYSTEM_TMP_TABLE
};
+/** Event on which trigger is invoked. */
+enum trg_event_type
+{
+ TRG_EVENT_INSERT= 0,
+ TRG_EVENT_UPDATE= 1,
+ TRG_EVENT_DELETE= 2,
+ TRG_EVENT_MAX
+};
+
enum frm_type_enum
{
FRMTYPE_ERROR= 0,
@@ -386,7 +395,7 @@ struct st_table {
/* Table's triggers, 0 if there are no of them */
Table_triggers_list *triggers;
- struct st_table_list *pos_in_table_list;/* Element referring to this table */
+ TABLE_LIST *pos_in_table_list;/* Element referring to this table */
ORDER *group;
const char *alias; /* alias or table name */
uchar *null_flags;
@@ -625,7 +634,7 @@ typedef struct st_field_info
} ST_FIELD_INFO;
-struct st_table_list;
+struct TABLE_LIST;
typedef class Item COND;
typedef struct st_schema_table
@@ -633,12 +642,12 @@ typedef struct st_schema_table
const char* table_name;
ST_FIELD_INFO *fields_info;
/* Create information_schema table */
- TABLE *(*create_table) (THD *thd, struct st_table_list *table_list);
+ TABLE *(*create_table) (THD *thd, TABLE_LIST *table_list);
/* Fill table with data */
- int (*fill_table) (THD *thd, struct st_table_list *tables, COND *cond);
+ int (*fill_table) (THD *thd, TABLE_LIST *tables, COND *cond);
/* Handle fileds for old SHOW */
int (*old_format) (THD *thd, struct st_schema_table *schema_table);
- int (*process_table) (THD *thd, struct st_table_list *tables,
+ int (*process_table) (THD *thd, TABLE_LIST *tables,
TABLE *table, bool res, const char *base_name,
const char *file_name);
int idx_field1, idx_field2;
@@ -671,7 +680,7 @@ struct st_lex;
class select_union;
class TMP_TABLE_PARAM;
-Item *create_view_field(THD *thd, st_table_list *view, Item **field_ref,
+Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
const char *name);
struct Field_translator
@@ -692,7 +701,7 @@ class Natural_join_column: public Sql_alloc
public:
Field_translator *view_field; /* Column reference of merge view. */
Field *table_field; /* Column reference of table or temp view. */
- st_table_list *table_ref; /* Original base table/view reference. */
+ TABLE_LIST *table_ref; /* Original base table/view reference. */
/*
True if a common join column of two NATURAL/USING join operands. Notice
that when we have a hierarchy of nested NATURAL/USING joins, a column can
@@ -702,8 +711,8 @@ public:
*/
bool is_common;
public:
- Natural_join_column(Field_translator *field_param, st_table_list *tab);
- Natural_join_column(Field *field_param, st_table_list *tab);
+ Natural_join_column(Field_translator *field_param, TABLE_LIST *tab);
+ Natural_join_column(Field *field_param, TABLE_LIST *tab);
const char *name();
Item *create_item(THD *thd);
Field *field();
@@ -746,9 +755,9 @@ public:
*/
class index_hint;
-typedef struct st_table_list
+struct TABLE_LIST
{
- st_table_list() {} /* Remove gcc warning */
+ TABLE_LIST() {} /* Remove gcc warning */
/**
Prepare TABLE_LIST that consists of one table instance to use in
@@ -769,9 +778,9 @@ typedef struct st_table_list
views as leaves (unlike 'next_leaf' below). Created at parse time
in st_select_lex::add_table_to_list() -> table_list.link_in_list().
*/
- struct st_table_list *next_local;
+ TABLE_LIST *next_local;
/* link in a global list of all queries tables */
- struct st_table_list *next_global, **prev_global;
+ TABLE_LIST *next_global, **prev_global;
char *db, *alias, *table_name, *schema_table_name;
char *option; /* Used by cache index */
Item *on_expr; /* Used with outer join */
@@ -791,7 +800,7 @@ typedef struct st_table_list
'this' represents a NATURAL or USING join operation. Thus after
parsing 'this' is a NATURAL/USING join iff (natural_join != NULL).
*/
- struct st_table_list *natural_join;
+ TABLE_LIST *natural_join;
/*
True if 'this' represents a nested join that is a NATURAL JOIN.
For one of the operands of 'this', the member 'natural_join' points
@@ -815,7 +824,7 @@ typedef struct st_table_list
base tables. All of these TABLE_LIST instances contain a
materialized list of columns. The list is local to a subquery.
*/
- struct st_table_list *next_name_resolution_table;
+ TABLE_LIST *next_name_resolution_table;
/* Index names in a "... JOIN ... USE/IGNORE INDEX ..." clause. */
List<index_hint> *index_hints;
TABLE *table; /* opened table */
@@ -832,7 +841,7 @@ typedef struct st_table_list
here it will be reference of first occurrence of t1 to second (as you
can see this lists can't be merged)
*/
- st_table_list *correspondent_table;
+ TABLE_LIST *correspondent_table;
st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
st_select_lex *schema_select_lex;
@@ -853,20 +862,20 @@ typedef struct st_table_list
does not include the tables of subqueries used in the view. Is set only
for merged views.
*/
- st_table_list *merge_underlying_list;
+ TABLE_LIST *merge_underlying_list;
/*
- 0 for base tables
- in case of the view it is the list of all (not only underlying
tables but also used in subquery ones) tables of the view.
*/
- List<st_table_list> *view_tables;
+ List<TABLE_LIST> *view_tables;
/* most upper view this table belongs to */
- st_table_list *belong_to_view;
+ TABLE_LIST *belong_to_view;
/*
The view directly referencing this table
(non-zero only for merged underlying tables of a view).
*/
- st_table_list *referencing_view;
+ TABLE_LIST *referencing_view;
/*
Security context (non-zero only for tables which belong
to view with SQL SECURITY DEFINER)
@@ -883,7 +892,7 @@ typedef struct st_table_list
leaves. Created in setup_tables() -> make_leaves_list().
*/
bool allowed_show;
- st_table_list *next_leaf;
+ TABLE_LIST *next_leaf;
Item *where; /* VIEW WHERE clause condition */
Item *check_option; /* WITH CHECK OPTION condition */
LEX_STRING select_stmt; /* text of (CREATE/SELECT) statement */
@@ -923,8 +932,8 @@ typedef struct st_table_list
table_map dep_tables; /* tables the table depends on */
table_map on_expr_dep_tables; /* tables on expression depends on */
struct st_nested_join *nested_join; /* if the element is a nested join */
- st_table_list *embedding; /* nested join containing the table */
- List<struct st_table_list> *join_list;/* join list the table belongs to */
+ TABLE_LIST *embedding; /* nested join containing the table */
+ List<TABLE_LIST> *join_list;/* join list the table belongs to */
bool cacheable_table; /* stop PS caching */
/* used in multi-upd/views privilege check */
bool table_in_first_from_clause;
@@ -979,6 +988,13 @@ typedef struct st_table_list
/* End of view definition context. */
+ /**
+ Indicates what triggers we need to pre-load for this TABLE_LIST
+ when opening an associated TABLE. This is filled after
+ the parsed tree is created.
+ */
+ uint8 trg_event_map;
+
enum enum_schema_table_state schema_table_state;
void calc_md5(char *buffer);
void set_underlying_merge();
@@ -991,15 +1007,15 @@ typedef struct st_table_list
!table;
}
void print(THD *thd, String *str);
- bool check_single_table(st_table_list **table, table_map map,
- st_table_list *view);
+ bool check_single_table(TABLE_LIST **table, table_map map,
+ TABLE_LIST *view);
bool set_insert_values(MEM_ROOT *mem_root);
void hide_view_error(THD *thd);
- st_table_list *find_underlying_table(TABLE *table);
- st_table_list *first_leaf_for_name_resolution();
- st_table_list *last_leaf_for_name_resolution();
+ TABLE_LIST *find_underlying_table(TABLE *table);
+ TABLE_LIST *first_leaf_for_name_resolution();
+ TABLE_LIST *last_leaf_for_name_resolution();
bool is_leaf_for_name_resolution();
- inline st_table_list *top_table()
+ inline TABLE_LIST *top_table()
{ return belong_to_view ? belong_to_view : this; }
inline bool prepare_check_option(THD *thd)
{
@@ -1036,6 +1052,7 @@ typedef struct st_table_list
*/
bool process_index_hints(TABLE *table);
+ void set_trg_event_type(const st_lex *lex);
private:
bool prep_check_option(THD *thd, uint8 check_opt_type);
bool prep_where(THD *thd, Item **conds, bool no_where_clause);
@@ -1043,7 +1060,7 @@ private:
Cleanup for re-execution in a prepared statement or a stored
procedure.
*/
-} TABLE_LIST;
+};
class Item;
diff --git a/sql/uniques.cc b/sql/uniques.cc
index 7a05ceaddfc..0394eee9c6d 100644
--- a/sql/uniques.cc
+++ b/sql/uniques.cc
@@ -360,17 +360,12 @@ Unique::reset()
}
/*
- The comparison function, passed to queue_init() in merge_walk() must
+ The comparison function, passed to queue_init() in merge_walk() and in
+ merge_buffers() when the latter is called from Uniques::get() must
use comparison function of Uniques::tree, but compare members of struct
BUFFPEK.
*/
-struct BUFFPEK_COMPARE_CONTEXT
-{
- qsort_cmp2 key_compare;
- void *key_compare_arg;
-};
-
C_MODE_START
static int buffpek_compare(void *arg, uchar *key_ptr1, uchar *key_ptr2)
@@ -629,6 +624,10 @@ bool Unique::get(TABLE *table)
sort_param.unique_buff= sort_buffer+(sort_param.keys*
sort_param.sort_length);
+ sort_param.compare= (qsort2_cmp) buffpek_compare;
+ sort_param.cmp_context.key_compare= tree.compare;
+ sort_param.cmp_context.key_compare_arg= tree.custom_arg;
+
/* Merge the buffers to one file, removing duplicates */
if (merge_many_buff(&sort_param,sort_buffer,file_ptr,&maxbuffer,&file))
goto err;
diff --git a/sql/unireg.cc b/sql/unireg.cc
index 57847bc70c6..f9e8e54439a 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -355,6 +355,8 @@ int rea_create_table(THD *thd, const char *path,
// Make sure mysql_create_frm din't remove extension
DBUG_ASSERT(*fn_rext(frm_name));
+ if (thd->variables.keep_files_on_create)
+ create_info->options|= HA_CREATE_KEEP_FILES;
if (file->create_handler_files(path, NULL, CHF_CREATE_FLAG, create_info))
goto err_handler;
if (!create_info->frm_only && ha_create_table(thd, path, db, table_name,
diff --git a/sql/unireg.h b/sql/unireg.h
index 5b73c6e9caa..6c26f30f116 100644
--- a/sql/unireg.h
+++ b/sql/unireg.h
@@ -124,7 +124,7 @@
#define SPECIAL_NO_HOST_CACHE 512 /* Don't cache hosts */
#define SPECIAL_SHORT_LOG_FORMAT 1024
#define SPECIAL_SAFE_MODE 2048
-#define SPECIAL_LOG_QUERIES_NOT_USING_INDEXES 4096 /* Log q not using indexes */
+#define SPECIAL_LOG_QUERIES_NOT_USING_INDEXES 4096 /* Obsolete */
/* Extern defines */
#define store_record(A,B) bmove_align((A)->B,(A)->record[0],(size_t) (A)->s->reclength)