summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/Makefile.am8
-rw-r--r--sql/field.cc213
-rw-r--r--sql/field_conv.cc18
-rw-r--r--sql/filesort.cc1
-rw-r--r--sql/ha_archive.cc34
-rw-r--r--sql/ha_archive.h1
-rw-r--r--sql/ha_innodb.cc13
-rw-r--r--sql/ha_myisammrg.cc15
-rw-r--r--sql/item.cc1
-rw-r--r--sql/item_func.cc36
-rw-r--r--sql/item_func.h1
-rw-r--r--sql/item_strfunc.cc3
-rw-r--r--sql/item_timefunc.cc4
-rw-r--r--sql/key.cc24
-rw-r--r--sql/log.cc15
-rw-r--r--sql/log_event.cc1
-rw-r--r--sql/mysql_priv.h24
-rw-r--r--sql/mysqld.cc19
-rw-r--r--sql/opt_range.cc23
-rw-r--r--sql/opt_range.h9
-rw-r--r--sql/set_var.cc30
-rw-r--r--sql/share/errmsg.txt2
-rw-r--r--sql/slave.cc2
-rw-r--r--sql/slave.h3
-rw-r--r--sql/sp.cc39
-rw-r--r--sql/sql_acl.cc1
-rw-r--r--sql/sql_analyse.cc8
-rw-r--r--sql/sql_class.cc24
-rw-r--r--sql/sql_class.h59
-rw-r--r--sql/sql_insert.cc54
-rw-r--r--sql/sql_load.cc12
-rw-r--r--sql/sql_parse.cc17
-rw-r--r--sql/sql_select.cc54
-rw-r--r--sql/sql_show.cc2
-rw-r--r--sql/sql_table.cc5
-rw-r--r--sql/sql_trigger.cc32
-rw-r--r--sql/sql_trigger.h7
-rw-r--r--sql/sql_update.cc26
-rw-r--r--sql/stacktrace.c2
-rw-r--r--sql/table.cc16
-rw-r--r--sql/unireg.cc15
41 files changed, 566 insertions, 307 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am
index e837e10472c..d9cea960915 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -27,7 +27,7 @@ INCLUDES = @ZLIB_INCLUDES@ \
WRAPLIBS= @WRAPLIBS@
SUBDIRS = share
libexec_PROGRAMS = mysqld
-noinst_PROGRAMS = gen_lex_hash
+EXTRA_PROGRAMS = gen_lex_hash
bin_PROGRAMS = mysql_tzinfo_to_sql
gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@
LDADD = $(top_builddir)/myisam/libmyisam.a \
@@ -153,7 +153,11 @@ sql_yacc.o: sql_yacc.cc sql_yacc.h $(HEADERS)
@echo "If it fails, re-run configure with --with-low-memory"
$(CXXCOMPILE) $(LM_CFLAGS) -c $<
-lex_hash.h: gen_lex_hash$(EXEEXT)
+# This generates lex_hash.h
+# NOTE Built sources should depend on their sources not the tool
+# this avoid the rebuild of the built files in a source dist
+lex_hash.h: gen_lex_hash.cc lex.h
+ $(MAKE) $(AM_MAKEFLAGS) gen_lex_hash$(EXEEXT)
./gen_lex_hash$(EXEEXT) > $@
# For testing of udf_example.so
diff --git a/sql/field.cc b/sql/field.cc
index 4860f6ea3da..9b512fc6d4b 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -2316,11 +2316,16 @@ int Field_new_decimal::store(const char *from, uint length,
from, length, charset, &decimal_value)) &&
table->in_use->abort_on_warning)
{
+ /* Because "from" is not NUL-terminated and we use %s in the ER() */
+ String from_as_str;
+ from_as_str.copy(from, length, &my_charset_bin);
+
push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_ERROR,
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "decimal", from, field_name,
+ "decimal", from_as_str.c_ptr(), field_name,
(ulong) table->in_use->row_count);
+
DBUG_RETURN(err);
}
@@ -2333,13 +2338,20 @@ int Field_new_decimal::store(const char *from, uint length,
set_value_on_overflow(&decimal_value, decimal_value.sign());
break;
case E_DEC_BAD_NUM:
+ {
+ /* Because "from" is not NUL-terminated and we use %s in the ER() */
+ String from_as_str;
+ from_as_str.copy(from, length, &my_charset_bin);
+
push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "decimal", from, field_name,
+ "decimal", from_as_str.c_ptr(), field_name,
(ulong) table->in_use->row_count);
my_decimal_set_zero(&decimal_value);
+
break;
+ }
}
#ifndef DBUG_OFF
@@ -2482,30 +2494,27 @@ void Field_new_decimal::sql_type(String &str) const
int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs)
{
- int not_used; // We can ignore result from str2int
char *end;
- long tmp= my_strntol(cs, from, len, 10, &end, &not_used);
- int error= 0;
+ int error;
if (unsigned_flag)
{
- if (tmp < 0)
- {
- tmp=0; /* purecov: inspected */
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
- error= 1;
- }
- else if (tmp > 255)
+ ulonglong tmp= cs->cset->strntoull10rnd(cs, from, len, 1, &end, &error);
+ if (error == MY_ERRNO_ERANGE || tmp > 255)
{
- tmp= 255;
+ set_if_smaller(tmp, 255);
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (table->in_use->count_cuted_fields && check_int(from,len,end,cs))
error= 1;
+ else
+ error= 0;
+ ptr[0]= (char) tmp;
}
else
{
+ longlong tmp= cs->cset->strntoull10rnd(cs, from, len, 0, &end, &error);
if (tmp < -128)
{
tmp= -128;
@@ -2520,8 +2529,10 @@ int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs)
}
else if (table->in_use->count_cuted_fields && check_int(from,len,end,cs))
error= 1;
+ else
+ error= 0;
+ ptr[0]= (char) tmp;
}
- ptr[0]= (char) tmp;
return error;
}
@@ -2686,30 +2697,34 @@ void Field_tiny::sql_type(String &res) const
int Field_short::store(const char *from,uint len,CHARSET_INFO *cs)
{
- int not_used; // We can ignore result from str2int
char *end;
- long tmp= my_strntol(cs, from, len, 10, &end, &not_used);
- int error= 0;
+ int error;
if (unsigned_flag)
{
- if (tmp < 0)
+ ulonglong tmp= cs->cset->strntoull10rnd(cs, from, len, 1, &end, &error);
+ if (error == MY_ERRNO_ERANGE || tmp > UINT_MAX16)
{
- tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
- error= 1;
- }
- else if (tmp > UINT_MAX16)
- {
- tmp=UINT_MAX16;
+ set_if_smaller(tmp, UINT_MAX16);
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (table->in_use->count_cuted_fields && check_int(from,len,end,cs))
error= 1;
+ else
+ error= 0;
+#ifdef WORDS_BIGENDIAN
+ if (table->s->db_low_byte_first)
+ {
+ int2store(ptr,tmp);
+ }
+ else
+#endif
+ shortstore(ptr,(short) tmp);
}
else
{
+ longlong tmp= cs->cset->strntoull10rnd(cs, from, len, 0, &end, &error);
if (tmp < INT_MIN16)
{
tmp= INT_MIN16;
@@ -2724,15 +2739,17 @@ int Field_short::store(const char *from,uint len,CHARSET_INFO *cs)
}
else if (table->in_use->count_cuted_fields && check_int(from,len,end,cs))
error= 1;
- }
+ else
+ error= 0;
#ifdef WORDS_BIGENDIAN
- if (table->s->db_low_byte_first)
- {
- int2store(ptr,tmp);
- }
- else
+ if (table->s->db_low_byte_first)
+ {
+ int2store(ptr,tmp);
+ }
+ else
#endif
- shortstore(ptr,(short) tmp);
+ shortstore(ptr,(short) tmp);
+ }
return error;
}
@@ -2960,30 +2977,27 @@ void Field_short::sql_type(String &res) const
int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs)
{
- int not_used; // We can ignore result from str2int
char *end;
- long tmp= my_strntol(cs, from, len, 10, &end, &not_used);
- int error= 0;
+ int error;
if (unsigned_flag)
{
- if (tmp < 0)
- {
- tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
- error= 1;
- }
- else if (tmp >= (long) (1L << 24))
+ ulonglong tmp= cs->cset->strntoull10rnd(cs, from, len, 1, &end, &error);
+ if (error == MY_ERRNO_ERANGE || tmp > UINT_MAX24)
{
- tmp=(long) (1L << 24)-1L;
+ set_if_smaller(tmp, UINT_MAX24);
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (table->in_use->count_cuted_fields && check_int(from,len,end,cs))
error= 1;
+ else
+ error= 0;
+ int3store(ptr,tmp);
}
else
{
+ longlong tmp= cs->cset->strntoull10rnd(cs, from, len, 0, &end, &error);
if (tmp < INT_MIN24)
{
tmp= INT_MIN24;
@@ -2998,9 +3012,10 @@ int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs)
}
else if (table->in_use->count_cuted_fields && check_int(from,len,end,cs))
error= 1;
+ else
+ error= 0;
+ int3store(ptr,tmp);
}
-
- int3store(ptr,tmp);
return error;
}
@@ -3196,64 +3211,47 @@ static bool test_if_minus(CHARSET_INFO *cs,
int Field_long::store(const char *from,uint len,CHARSET_INFO *cs)
{
- ulong tmp_scan;
- longlong tmp;
long store_tmp;
int error;
char *end;
- tmp_scan= cs->cset->scan(cs, from, from+len, MY_SEQ_SPACES);
- len-= tmp_scan;
- from+= tmp_scan;
-
- end= (char*) from+len;
- tmp= cs->cset->strtoll10(cs, from, &end, &error);
-
- if (error != MY_ERRNO_EDOM)
+ if (unsigned_flag)
{
- if (unsigned_flag)
+ ulonglong tmp= cs->cset->strntoull10rnd(cs, from, len, 1, &end, &error);
+ if (error == MY_ERRNO_ERANGE || tmp > (ulonglong) UINT_MAX32)
{
- if (error < 0)
- {
- error= 1;
- tmp= 0;
- }
- else if ((ulonglong) tmp > (ulonglong) UINT_MAX32)
- {
- tmp= UINT_MAX32;
- error= 1;
- }
- else
- error= 0;
+ set_if_smaller(tmp, (ulonglong) UINT_MAX32);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
}
+ else if (table->in_use->count_cuted_fields && check_int(from,len,end,cs))
+ error= 1;
else
- {
- if (error < 0)
- {
- error= 0;
- if (tmp < INT_MIN32)
- {
- tmp= INT_MIN32;
- error= 1;
- }
- }
- else if (tmp > INT_MAX32)
- {
- tmp= INT_MAX32;
- error= 1;
- }
- }
+ error= 0;
+ store_tmp= (long) tmp;
}
- if (error)
+ else
{
- error= error != MY_ERRNO_EDOM ? 1 : 2;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ longlong tmp= cs->cset->strntoull10rnd(cs, from, len, 0, &end, &error);
+ if (tmp < INT_MIN32)
+ {
+ tmp= INT_MIN32;
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if (tmp > INT_MAX32)
+ {
+ tmp=INT_MAX32;
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ error= 1;
+ }
+ else if (table->in_use->count_cuted_fields && check_int(from,len,end,cs))
+ error= 1;
+ else
+ error= 0;
+ store_tmp= (long) tmp;
}
- else if (from+len != end && table->in_use->count_cuted_fields &&
- check_int(from,len,end,cs))
- error= 2;
- store_tmp= (long) tmp;
#ifdef WORDS_BIGENDIAN
if (table->s->db_low_byte_first)
{
@@ -3489,33 +3487,20 @@ void Field_long::sql_type(String &res) const
int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs)
{
- longlong tmp;
- int error= 0;
+ int error;
char *end;
+ ulonglong tmp;
- tmp= cs->cset->scan(cs, from, from+len, MY_SEQ_SPACES);
- len-= (uint)tmp;
- from+= tmp;
- if (unsigned_flag)
- {
- if (!len || test_if_minus(cs, from, from + len))
- {
- tmp=0; // Set negative to 0
- error= 1;
- }
- else
- tmp=(longlong) my_strntoull(cs,from,len,10,&end,&error);
- }
- else
- tmp=my_strntoll(cs,from,len,10,&end,&error);
- if (error)
+ tmp= cs->cset->strntoull10rnd(cs,from,len,unsigned_flag,&end,&error);
+ if (error == MY_ERRNO_ERANGE)
{
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
- else if (from+len != end && table->in_use->count_cuted_fields &&
- check_int(from,len,end,cs))
- error= 2;
+ else if (table->in_use->count_cuted_fields && check_int(from,len,end,cs))
+ error= 1;
+ else
+ error= 0;
#ifdef WORDS_BIGENDIAN
if (table->s->db_low_byte_first)
{
@@ -7921,7 +7906,7 @@ int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs)
(delta == -1 && (uchar) *from > ((1 << bit_len) - 1)) ||
(!bit_len && delta < 0))
{
- set_rec_bits(0xff, bit_ptr, bit_ofs, bit_len);
+ set_rec_bits((1 << bit_len) - 1, bit_ptr, bit_ofs, bit_len);
memset(ptr, 0xff, bytes_in_rec);
if (table->in_use->really_abort_on_warning())
set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1);
@@ -8877,7 +8862,7 @@ create_field::create_field(Field *old_field,Field *orig_field)
case 3: sql_type= FIELD_TYPE_MEDIUM_BLOB; break;
default: sql_type= FIELD_TYPE_LONG_BLOB; break;
}
- length=(length+charset->mbmaxlen-1) / charset->mbmaxlen;
+ length/= charset->mbmaxlen;
key_length/= charset->mbmaxlen;
break;
case MYSQL_TYPE_STRING:
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index 3200f2ca9b2..95ff985376d 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -428,6 +428,21 @@ static void do_varstring2(Copy_field *copy)
length);
}
+
+static void do_varstring2_mb(Copy_field *copy)
+{
+ int well_formed_error;
+ CHARSET_INFO *cs= copy->from_field->charset();
+ uint char_length= (copy->to_length - HA_KEY_BLOB_LENGTH) / cs->mbmaxlen;
+ uint from_length= uint2korr(copy->from_ptr);
+ const char *from_beg= copy->from_ptr + HA_KEY_BLOB_LENGTH;
+ uint length= cs->cset->well_formed_len(cs, from_beg, from_beg + from_length,
+ char_length, &well_formed_error);
+ int2store(copy->to_ptr, length);
+ memcpy(copy->to_ptr+HA_KEY_BLOB_LENGTH, from_beg, length);
+}
+
+
/***************************************************************************
** The different functions that fills in a Copy_field class
***************************************************************************/
@@ -587,7 +602,8 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*)
return do_field_string;
if (to_length != from_length)
return (((Field_varstring*) to)->length_bytes == 1 ?
- do_varstring1 : do_varstring2);
+ do_varstring1 : (from->charset()->mbmaxlen == 1 ?
+ do_varstring2 : do_varstring2_mb));
}
else if (to_length < from_length)
return (from->charset()->mbmaxlen == 1 ?
diff --git a/sql/filesort.cc b/sql/filesort.cc
index f13354d5c72..b063b416191 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -601,6 +601,7 @@ static inline void store_length(uchar *to, uint length, uint pack_length)
break;
case 3:
mi_int3store(to, length);
+ break;
default:
mi_int4store(to, length);
break;
diff --git a/sql/ha_archive.cc b/sql/ha_archive.cc
index bb94a99e700..bc3c819c4ed 100644
--- a/sql/ha_archive.cc
+++ b/sql/ha_archive.cc
@@ -120,6 +120,8 @@ static bool archive_inited= FALSE;
/* Variables for archive share methods */
pthread_mutex_t archive_mutex;
static HASH archive_open_tables;
+static z_off_t max_zfile_size;
+static int zoffset_size;
/* The file extension */
#define ARZ ".ARZ" // The data file
@@ -203,6 +205,18 @@ bool archive_db_init()
}
else
{
+ zoffset_size= 2 << ((zlibCompileFlags() >> 6) & 3);
+ switch (sizeof(z_off_t)) {
+ case 2:
+ max_zfile_size= INT_MAX16;
+ break;
+ case 8:
+ max_zfile_size= LONGLONG_MAX;
+ break;
+ case 4:
+ default:
+ max_zfile_size= INT_MAX32;
+ }
archive_inited= TRUE;
DBUG_RETURN(FALSE);
}
@@ -240,7 +254,7 @@ ha_archive::ha_archive(TABLE *table_arg)
buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info);
/* The size of the offset value we will use for position() */
- ref_length = 2 << ((zlibCompileFlags() >> 6) & 3);
+ ref_length = zoffset_size;
DBUG_ASSERT(ref_length <= sizeof(z_off_t));
}
@@ -480,7 +494,8 @@ int ha_archive::init_archive_writer()
DBUG_RETURN(1);
}
share->archive_write_open= TRUE;
-
+ info(HA_STATUS_TIME);
+ share->approx_file_size= data_file_length;
DBUG_RETURN(0);
}
@@ -651,10 +666,21 @@ error:
*/
int ha_archive::real_write_row(byte *buf, gzFile writer)
{
- z_off_t written;
+ z_off_t written, total_row_length;
uint *ptr, *end;
DBUG_ENTER("ha_archive::real_write_row");
-
+ total_row_length= table->s->reclength;
+ for (ptr= table->s->blob_field, end= ptr + table->s->blob_fields;
+ ptr != end; ptr++)
+ total_row_length+= ((Field_blob*) table->field[*ptr])->get_length();
+ if (share->approx_file_size > max_zfile_size - total_row_length)
+ {
+ info(HA_STATUS_TIME);
+ share->approx_file_size= data_file_length;
+ if (share->approx_file_size > max_zfile_size - total_row_length)
+ DBUG_RETURN(HA_ERR_RECORD_FILE_FULL);
+ }
+ share->approx_file_size+= total_row_length;
written= gzwrite(writer, buf, table->s->reclength);
DBUG_PRINT("ha_archive::real_write_row", ("Wrote %d bytes expected %d", written, table->s->reclength));
if (!delayed_insert || !bulk_insert)
diff --git a/sql/ha_archive.h b/sql/ha_archive.h
index 2bac9fa605e..564b9f03bf5 100644
--- a/sql/ha_archive.h
+++ b/sql/ha_archive.h
@@ -38,6 +38,7 @@ typedef struct st_archive_share {
bool dirty; /* Flag for if a flush should occur */
bool crashed; /* Meta file is crashed */
ha_rows rows_recorded; /* Number of rows in tables */
+ z_off_t approx_file_size; /* Approximate archive data file size */
} ARCHIVE_SHARE;
/*
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 4e73d4fb285..e99d93784c9 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -5970,19 +5970,6 @@ ha_innobase::start_stmt(
prebuilt->select_lock_type =
prebuilt->stored_select_lock_type;
}
-
- if (prebuilt->stored_select_lock_type != LOCK_S
- && prebuilt->stored_select_lock_type != LOCK_X) {
- sql_print_error("stored_select_lock_type is %lu inside "
- "::start_stmt()!",
- prebuilt->stored_select_lock_type);
-
- /* Set the value to LOCK_X: this is just fault
- tolerance, we do not know what the correct value
- should be! */
-
- prebuilt->select_lock_type = LOCK_X;
- }
}
trx->detailed_error[0] = '\0';
diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc
index cb11d9b0452..a4f23cd3cf0 100644
--- a/sql/ha_myisammrg.cc
+++ b/sql/ha_myisammrg.cc
@@ -326,9 +326,22 @@ void ha_myisammrg::info(uint flag)
if (flag & HA_STATUS_CONST)
{
if (table->s->key_parts && info.rec_per_key)
+ {
+#ifdef HAVE_purify
+ /*
+ valgrind may be unhappy about it, because optimizer may access values
+ between file->keys and table->key_parts, that will be uninitialized.
+ It's safe though, because even if opimizer will decide to use a key
+ with such a number, it'll be an error later anyway.
+ */
+ bzero((char*) table->key_info[0].rec_per_key,
+ sizeof(table->key_info[0].rec_per_key) * table->s->key_parts);
+#endif
memcpy((char*) table->key_info[0].rec_per_key,
(char*) info.rec_per_key,
- sizeof(table->key_info[0].rec_per_key)*table->s->key_parts);
+ sizeof(table->key_info[0].rec_per_key) *
+ min(file->keys, table->s->key_parts));
+ }
}
}
diff --git a/sql/item.cc b/sql/item.cc
index a0eef7a19e9..d56ca95093b 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3745,6 +3745,7 @@ void Item_field::cleanup()
I.e. we can drop 'field'.
*/
field= result_field= 0;
+ null_value= FALSE;
DBUG_VOID_RETURN;
}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 2e594c74031..a294bbd7a71 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -3345,6 +3345,35 @@ longlong Item_func_release_lock::val_int()
}
+bool Item_func_last_insert_id::fix_fields(THD *thd, Item **ref)
+{
+ DBUG_ASSERT(fixed == 0);
+
+ if (Item_int_func::fix_fields(thd, ref))
+ return TRUE;
+
+ if (arg_count == 0)
+ {
+ if (!thd->last_insert_id_used)
+ {
+ /*
+ As this statement calls LAST_INSERT_ID(), set
+ THD::last_insert_id_used and remember first generated insert
+ id of the previous statement in THD::current_insert_id.
+ */
+ thd->last_insert_id_used= TRUE;
+ thd->last_insert_id_used_bin_log= TRUE;
+ thd->current_insert_id= thd->last_insert_id;
+ }
+ null_value= FALSE;
+ }
+
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+
+ return FALSE;
+}
+
+
longlong Item_func_last_insert_id::val_int()
{
THD *thd= current_thd;
@@ -3354,12 +3383,13 @@ longlong Item_func_last_insert_id::val_int()
longlong value= args[0]->val_int();
thd->insert_id(value);
null_value= args[0]->null_value;
- return value; // Avoid side effect of insert_id()
+ return value;
}
- thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- return thd->last_insert_id_used ? thd->current_insert_id : thd->insert_id();
+
+ return thd->current_insert_id;
}
+
/* This function is just used to test speed of different functions */
longlong Item_func_benchmark::val_int()
diff --git a/sql/item_func.h b/sql/item_func.h
index 177daf0311f..31adc033034 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -891,6 +891,7 @@ public:
if (arg_count)
max_length= args[0]->max_length;
}
+ bool fix_fields(THD *thd, Item **ref);
};
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 46a96c28d88..31c2f44fc3e 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1149,12 +1149,13 @@ void Item_func_substr::fix_length_and_dec()
}
if (arg_count == 3 && args[2]->const_item())
{
- int32 length= (int32) args[2]->val_int() * collation.collation->mbmaxlen;
+ int32 length= (int32) args[2]->val_int();
if (length <= 0)
max_length=0; /* purecov: inspected */
else
set_if_smaller(max_length,(uint) length);
}
+ max_length*= collation.collation->mbmaxlen;
}
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 30230005f6e..48d6458bd88 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -65,7 +65,7 @@ static bool make_datetime(date_time_format_types format, TIME *ltime,
ltime->hour, ltime->minute, ltime->second);
break;
case TIME_MICROSECOND:
- length= cs->cset->snprintf(cs, buff, length, "%s%02d:%02d:%02d.%06d",
+ length= cs->cset->snprintf(cs, buff, length, "%s%02d:%02d:%02d.%06ld",
ltime->neg ? "-" : "",
ltime->hour, ltime->minute, ltime->second,
ltime->second_part);
@@ -82,7 +82,7 @@ static bool make_datetime(date_time_format_types format, TIME *ltime,
break;
case DATE_TIME_MICROSECOND:
length= cs->cset->snprintf(cs, buff, length,
- "%04d-%02d-%02d %02d:%02d:%02d.%06d",
+ "%04d-%02d-%02d %02d:%02d:%02d.%06ld",
ltime->year, ltime->month, ltime->day,
ltime->hour, ltime->minute, ltime->second,
ltime->second_part);
diff --git a/sql/key.cc b/sql/key.cc
index 75161e4f616..5e658312352 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -18,6 +18,7 @@
/* Functions to handle keys and fields in forms */
#include "mysql_priv.h"
+#include "sql_trigger.h"
/*
** Search after with key field is. If no key starts with field test
@@ -342,12 +343,24 @@ void key_unpack(String *to,TABLE *table,uint idx)
/*
- Return 1 if any field in a list is part of key or the key uses a field
- that is automaticly updated (like a timestamp)
+ Check if key uses field that is listed in passed field list or is
+ automatically updated (like a timestamp) or can be updated by before
+ update trigger defined on the table.
+
+ SYNOPSIS
+ is_key_used()
+ table TABLE object with which keys and fields are associated.
+ idx Key to be checked.
+ fields List of fields to be checked.
+
+ RETURN VALUE
+ TRUE Key uses field which meets one the above conditions
+ FALSE Otherwise
*/
-bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields)
+bool is_key_used(TABLE *table, uint idx, List<Item> &fields)
{
+ Table_triggers_list *triggers= table->triggers;
List_iterator_fast<Item> f(fields);
KEY_PART_INFO *key_part,*key_part_end;
for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
@@ -366,6 +379,9 @@ bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields)
if (key_part->field->eq(field->field))
return 1;
}
+ if (triggers &&
+ triggers->is_updated_in_before_update_triggers(key_part->field))
+ return 1;
}
/*
@@ -374,7 +390,7 @@ bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields)
*/
if (idx != table->s->primary_key && table->s->primary_key < MAX_KEY &&
(table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
- return check_if_key_used(table, table->s->primary_key, fields);
+ return is_key_used(table, table->s->primary_key, fields);
return 0;
}
diff --git a/sql/log.cc b/sql/log.cc
index 1cd01865f9f..960fc4f60c2 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -566,15 +566,18 @@ bool MYSQL_LOG::open(const char *log_name,
case LOG_NORMAL:
{
char *end;
- int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s. "
+ int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s (%s). "
#ifdef EMBEDDED_LIBRARY
- "embedded library\n", my_progname, server_version
+ "embedded library\n",
+ my_progname, server_version, MYSQL_COMPILATION_COMMENT
#elif __NT__
"started with:\nTCP Port: %d, Named Pipe: %s\n",
- my_progname, server_version, mysqld_port, mysqld_unix_port
+ my_progname, server_version, MYSQL_COMPILATION_COMMENT,
+ mysqld_port, mysqld_unix_port
#else
"started with:\nTcp port: %d Unix socket: %s\n",
- my_progname,server_version,mysqld_port,mysqld_unix_port
+ my_progname, server_version, MYSQL_COMPILATION_COMMENT,
+ mysqld_port, mysqld_unix_port
#endif
);
end=strnmov(buff+len,"Time Id Command Argument\n",
@@ -1702,7 +1705,7 @@ bool MYSQL_LOG::write(Log_event *event_info)
if (thd)
{
- if (thd->last_insert_id_used)
+ if (thd->last_insert_id_used_bin_log)
{
Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
thd->current_insert_id);
@@ -1994,7 +1997,7 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
tmp_errno=errno;
strmov(db,thd->db);
}
- if (thd->last_insert_id_used)
+ if (thd->last_insert_id_used_bin_log)
{
end=strmov(end,",last_insert_id=");
end=longlong10_to_str((longlong) thd->current_insert_id,end,-10);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 219434ab218..271658d8054 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -3365,7 +3365,6 @@ int Intvar_log_event::exec_event(struct st_relay_log_info* rli)
{
switch (type) {
case LAST_INSERT_ID_EVENT:
- thd->last_insert_id_used = 1;
thd->last_insert_id = val;
break;
case INSERT_ID_EVENT:
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index ea3b3a9bd83..75525b5ac33 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -131,7 +131,18 @@ MY_LOCALE *my_locale_by_name(const char *name);
#define MAX_ACCEPT_RETRY 10 // Test accept this many times
#define MAX_FIELDS_BEFORE_HASH 32
#define USER_VARS_HASH_SIZE 16
-#define STACK_MIN_SIZE 8192 // Abort if less stack during eval.
+
+/*
+ Value of 9236 discovered through binary search 2006-09-26 on Ubuntu Dapper
+ Drake, libc6 2.3.6-0ubuntu2, Linux kernel 2.6.15-27-686, on x86. (Added
+ 100 bytes as reasonable buffer against growth and other environments'
+ requirements.)
+
+ Feel free to raise this by the smallest amount you can to get the
+ "execution_constants" test to pass.
+ */
+#define STACK_MIN_SIZE 9336 // Abort if less stack during eval.
+
#define STACK_MIN_SIZE_FOR_OPEN 1024*80
#define STACK_BUFF_ALLOC 256 // For stack overrun checks
#ifndef MYSQLD_NET_RETRY_COUNT
@@ -1118,17 +1129,17 @@ void key_restore(byte *to_record, byte *from_key, KEY *key_info,
uint key_length);
bool key_cmp_if_same(TABLE *form,const byte *key,uint index,uint key_length);
void key_unpack(String *to,TABLE *form,uint index);
-bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields);
+bool is_key_used(TABLE *table, uint idx, List<Item> &fields);
int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length);
bool init_errmessage(void);
void sql_perror(const char *message);
void vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
-void sql_print_error(const char *format, ...);
-void sql_print_warning(const char *format, ...);
-void sql_print_information(const char *format, ...);
-
+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);
bool fn_format_relative_to_data_home(my_string to, const char *name,
@@ -1233,7 +1244,6 @@ extern my_bool opt_log_queries_not_using_indexes;
extern bool opt_disable_networking, opt_skip_show_db;
extern my_bool opt_character_set_client_handshake;
extern bool volatile abort_loop, shutdown_in_progress, grant_option;
-extern bool mysql_proc_table_exists;
extern uint volatile thread_count, thread_running, global_read_lock;
extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types;
extern my_bool opt_safe_show_db, opt_local_infile;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 8a4f212340d..1dd15398cd1 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -1025,8 +1025,8 @@ extern "C" sig_handler print_signal_warning(int sig)
if (!DBUG_IN_USE)
{
if (global_system_variables.log_warnings)
- sql_print_warning("Got signal %d from thread %d",
- sig,my_thread_id());
+ sql_print_warning("Got signal %d from thread %ld",
+ sig, my_thread_id());
}
#ifdef DONT_REMEMBER_SIGNAL
my_sigset(sig,print_signal_warning); /* int. thread system calls */
@@ -1531,8 +1531,8 @@ static void network_init(void)
if (strlen(mysqld_unix_port) > (sizeof(UNIXaddr.sun_path) - 1))
{
- sql_print_error("The socket file path is too long (> %d): %s",
- sizeof(UNIXaddr.sun_path) - 1, mysqld_unix_port);
+ sql_print_error("The socket file path is too long (> %lu): %s",
+ sizeof(UNIXaddr.sun_path) - 1, mysqld_unix_port);
unireg_abort(1);
}
if ((unix_sock= socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
@@ -2912,7 +2912,7 @@ static void openssl_lock(int mode, openssl_lock_t *lock, const char *file,
}
if (err)
{
- sql_print_error("Fatal: can't %s OpenSSL %s lock", what);
+ sql_print_error("Fatal: can't %s OpenSSL lock", what);
abort();
}
}
@@ -7042,14 +7042,15 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
exit(1);
}
switch (method-1) {
- case 0:
- method_conv= MI_STATS_METHOD_NULLS_NOT_EQUAL;
+ case 2:
+ method_conv= MI_STATS_METHOD_IGNORE_NULLS;
break;
case 1:
method_conv= MI_STATS_METHOD_NULLS_EQUAL;
break;
- case 2:
- method_conv= MI_STATS_METHOD_IGNORE_NULLS;
+ case 0:
+ default:
+ method_conv= MI_STATS_METHOD_NULLS_NOT_EQUAL;
break;
}
global_system_variables.myisam_stats_method= method_conv;
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index eb7d627de96..d68ea0abfe7 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -5559,8 +5559,9 @@ void SEL_ARG::test_use_count(SEL_ARG *root)
ulong count=count_key_part_usage(root,pos->next_key_part);
if (count > pos->next_key_part->use_count)
{
- sql_print_information("Use_count: Wrong count for key at 0x%lx, %lu should be %lu",
- pos,pos->next_key_part->use_count,count);
+ sql_print_information("Use_count: Wrong count for key at 0x%lx, %lu "
+ "should be %lu", (long unsigned int)pos,
+ pos->next_key_part->use_count, count);
return;
}
pos->next_key_part->test_use_count(root);
@@ -5568,7 +5569,7 @@ void SEL_ARG::test_use_count(SEL_ARG *root)
}
if (e_count != elements)
sql_print_warning("Wrong use count: %u (should be %u) for tree at 0x%lx",
- e_count, elements, (gptr) this);
+ e_count, elements, (long unsigned int) this);
}
#endif
@@ -6127,42 +6128,42 @@ static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length)
}
-bool QUICK_SELECT_I::check_if_keys_used(List<Item> *fields)
+bool QUICK_SELECT_I::is_keys_used(List<Item> *fields)
{
- return check_if_key_used(head, index, *fields);
+ return is_key_used(head, index, *fields);
}
-bool QUICK_INDEX_MERGE_SELECT::check_if_keys_used(List<Item> *fields)
+bool QUICK_INDEX_MERGE_SELECT::is_keys_used(List<Item> *fields)
{
QUICK_RANGE_SELECT *quick;
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
while ((quick= it++))
{
- if (check_if_key_used(head, quick->index, *fields))
+ if (is_key_used(head, quick->index, *fields))
return 1;
}
return 0;
}
-bool QUICK_ROR_INTERSECT_SELECT::check_if_keys_used(List<Item> *fields)
+bool QUICK_ROR_INTERSECT_SELECT::is_keys_used(List<Item> *fields)
{
QUICK_RANGE_SELECT *quick;
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
while ((quick= it++))
{
- if (check_if_key_used(head, quick->index, *fields))
+ if (is_key_used(head, quick->index, *fields))
return 1;
}
return 0;
}
-bool QUICK_ROR_UNION_SELECT::check_if_keys_used(List<Item> *fields)
+bool QUICK_ROR_UNION_SELECT::is_keys_used(List<Item> *fields)
{
QUICK_SELECT_I *quick;
List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
while ((quick= it++))
{
- if (quick->check_if_keys_used(fields))
+ if (quick->is_keys_used(fields))
return 1;
}
return 0;
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 784dd648ad2..ddc959f1cdf 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -225,8 +225,9 @@ public:
Return 1 if any index used by this quick select
a) uses field that is listed in passed field list or
b) is automatically updated (like a timestamp)
+ c) can be updated by one of before update triggers defined on table
*/
- virtual bool check_if_keys_used(List<Item> *fields);
+ virtual bool is_keys_used(List<Item> *fields);
/*
rowid of last row retrieved by this quick select. This is used only when
@@ -424,7 +425,7 @@ public:
int get_type() { return QS_TYPE_INDEX_MERGE; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
void add_info_string(String *str);
- bool check_if_keys_used(List<Item> *fields);
+ bool is_keys_used(List<Item> *fields);
#ifndef DBUG_OFF
void dbug_dump(int indent, bool verbose);
#endif
@@ -483,7 +484,7 @@ public:
int get_type() { return QS_TYPE_ROR_INTERSECT; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
void add_info_string(String *str);
- bool check_if_keys_used(List<Item> *fields);
+ bool is_keys_used(List<Item> *fields);
#ifndef DBUG_OFF
void dbug_dump(int indent, bool verbose);
#endif
@@ -537,7 +538,7 @@ public:
int get_type() { return QS_TYPE_ROR_UNION; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
void add_info_string(String *str);
- bool check_if_keys_used(List<Item> *fields);
+ bool is_keys_used(List<Item> *fields);
#ifndef DBUG_OFF
void dbug_dump(int indent, bool verbose);
#endif
diff --git a/sql/set_var.cc b/sql/set_var.cc
index c667e2f2bcc..d0268d7017b 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -1255,8 +1255,8 @@ static void fix_tx_isolation(THD *thd, enum_var_type type)
thd->variables.tx_isolation);
}
-static void fix_completion_type(THD *thd __attribute__(unused),
- enum_var_type type __attribute__(unused)) {}
+static void fix_completion_type(THD *thd __attribute__((unused)),
+ enum_var_type type __attribute__((unused))) {}
static int check_completion_type(THD *thd, set_var *var)
{
@@ -1295,14 +1295,14 @@ static void fix_net_retry_count(THD *thd, enum_var_type type)
thd->net.retry_count=thd->variables.net_retry_count;
}
#else /* HAVE_REPLICATION */
-static void fix_net_read_timeout(THD *thd __attribute__(unused),
- enum_var_type type __attribute__(unused))
+static void fix_net_read_timeout(THD *thd __attribute__((unused)),
+ enum_var_type type __attribute__((unused)))
{}
-static void fix_net_write_timeout(THD *thd __attribute__(unused),
- enum_var_type type __attribute__(unused))
+static void fix_net_write_timeout(THD *thd __attribute__((unused)),
+ enum_var_type type __attribute__((unused)))
{}
-static void fix_net_retry_count(THD *thd __attribute__(unused),
- enum_var_type type __attribute__(unused))
+static void fix_net_retry_count(THD *thd __attribute__((unused)),
+ enum_var_type type __attribute__((unused)))
{}
#endif /* HAVE_REPLICATION */
@@ -2571,8 +2571,18 @@ bool sys_var_last_insert_id::update(THD *thd, set_var *var)
byte *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
- thd->sys_var_tmp.long_value= (long) thd->insert_id();
- return (byte*) &thd->last_insert_id;
+ if (!thd->last_insert_id_used)
+ {
+ /*
+ As this statement reads @@LAST_INSERT_ID, set
+ THD::last_insert_id_used and remember first generated insert id
+ of the previous statement in THD::current_insert_id.
+ */
+ thd->last_insert_id_used= TRUE;
+ thd->last_insert_id_used_bin_log= TRUE;
+ thd->current_insert_id= thd->last_insert_id;
+ }
+ return (byte*) &thd->current_insert_id;
}
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index c3657e8246d..2b6d2b18f88 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -3813,7 +3813,7 @@ ER_WRONG_MRG_TABLE
cze "V-B¹echny tabulky v MERGE tabulce nejsou definovány stejnì"
dan "Tabellerne i MERGE er ikke defineret ens"
nla "Niet alle tabellen in de MERGE tabel hebben identieke gedefinities"
- eng "All tables in the MERGE table are not identically defined"
+ eng "Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist"
est "Kõik tabelid MERGE tabeli määratluses ei ole identsed"
fre "Toutes les tables de la table de type MERGE n'ont pas la même définition"
ger "Nicht alle Tabellen in der MERGE-Tabelle sind gleich definiert"
diff --git a/sql/slave.cc b/sql/slave.cc
index 55cff94a179..28bb7f12008 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -4613,7 +4613,7 @@ static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi,
suppress_warnings= 0;
sql_print_error("Slave I/O thread: error %s to master \
'%s@%s:%d': \
-Error: '%s' errno: %d retry-time: %d retries: %d",
+Error: '%s' errno: %d retry-time: %d retries: %lu",
(reconnect ? "reconnecting" : "connecting"),
mi->user,mi->host,mi->port,
mysql_error(mysql), last_errno,
diff --git a/sql/slave.h b/sql/slave.h
index c355f7172a9..65adb4564cc 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -553,7 +553,8 @@ void init_table_rule_array(DYNAMIC_ARRAY* a, bool* a_inited);
const char *print_slave_db_safe(const char *db);
int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int error_code);
void skip_load_data_infile(NET* net);
-void slave_print_error(RELAY_LOG_INFO* rli, int err_code, const char* msg, ...);
+void slave_print_error(RELAY_LOG_INFO *rli, int err_code, const char *msg, ...)
+ ATTRIBUTE_FORMAT(printf, 3, 4);
void end_slave(); /* clean up */
void init_master_info_with_options(MASTER_INFO* mi);
diff --git a/sql/sp.cc b/sql/sp.cc
index fc72822c15e..49dbed79fe5 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -66,8 +66,6 @@ enum
MYSQL_PROC_FIELD_COUNT
};
-bool mysql_proc_table_exists= 1;
-
/* Tells what SP_DEFAULT_ACCESS should be mapped to */
#define SP_DEFAULT_ACCESS_MAPPING SP_CONTAINS_SQL
@@ -119,13 +117,6 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
bool not_used;
DBUG_ENTER("open_proc_table");
- /*
- Speed up things if mysql.proc doesn't exists. mysql_proc_table_exists
- is set when we create or read stored procedure or on flush privileges.
- */
- if (!mysql_proc_table_exists)
- DBUG_RETURN(0);
-
thd->reset_n_backup_open_tables_state(backup);
bzero((char*) &tables, sizeof(tables));
@@ -135,7 +126,6 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
MYSQL_LOCK_IGNORE_FLUSH)))
{
thd->restore_backup_open_tables_state(backup);
- mysql_proc_table_exists= 0;
DBUG_RETURN(0);
}
@@ -184,15 +174,6 @@ static TABLE *open_proc_table_for_update(THD *thd)
table= open_ltable(thd, &tables, TL_WRITE);
- /*
- Under explicit LOCK TABLES or in prelocked mode we should not
- say that mysql.proc table does not exist if we are unable to
- open and lock it for writing since this condition may be
- transient.
- */
- if (!(thd->locked_tables || thd->prelocked_mode) || table)
- mysql_proc_table_exists= test(table);
-
DBUG_RETURN(table);
}
@@ -1610,14 +1591,6 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
case SP_KEY_NOT_FOUND:
ret= SP_OK;
break;
- case SP_OPEN_TABLE_FAILED:
- /*
- Force it to attempt opening it again on subsequent calls;
- otherwise we will get one error message the first time, and
- then ER_SP_PROC_TABLE_CORRUPT (below) on subsequent tries.
- */
- mysql_proc_table_exists= 1;
- /* Fall through */
default:
/*
Any error when loading an existing routine is either some problem
@@ -1633,7 +1606,17 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
*/
if (!thd->net.report_error)
{
- char n[NAME_LEN*2+2];
+ /*
+ SP allows full NAME_LEN chars thus he have to allocate enough
+ size in bytes. Otherwise there is stack overrun could happen
+ if multibyte sequence is `name`. `db` is still safe because the
+ rest of the server checks agains NAME_LEN bytes and not chars.
+ Hence, the overrun happens only if the name is in length > 32 and
+ uses multibyte (cyrillic, greek, etc.)
+
+ !! Change 3 with SYSTEM_CHARSET_MBMAXLEN when it's defined.
+ */
+ char n[NAME_LEN*3*2+2];
/* m_qname.str is not always \0 terminated */
memcpy(n, name.m_qname.str, name.m_qname.length);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index e6170cae994..50cee6eeca4 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -202,7 +202,6 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
DBUG_ENTER("acl_load");
grant_version++; /* Privileges updated */
- mysql_proc_table_exists= 1; // Assume mysql.proc exists
acl_cache->clear(1); // Clear locked hostname cache
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc
index af9246c673a..62b6d4f7920 100644
--- a/sql/sql_analyse.cc
+++ b/sql/sql_analyse.cc
@@ -807,9 +807,9 @@ void field_str::get_opt_type(String *answer, ha_rows total_rows)
else if (num_info.decimals) // DOUBLE(%d,%d) sometime
{
if (num_info.dval > -FLT_MAX && num_info.dval < FLT_MAX)
- sprintf(buff, "FLOAT(%d,%d)", num_info.integers, num_info.decimals);
+ sprintf(buff, "FLOAT(%d,%d)", (num_info.integers + num_info.decimals), num_info.decimals);
else
- sprintf(buff, "DOUBLE(%d,%d)", num_info.integers, num_info.decimals);
+ sprintf(buff, "DOUBLE(%d,%d)", (num_info.integers + num_info.decimals), num_info.decimals);
}
else if (ev_num_info.llval >= -128 &&
ev_num_info.ullval <=
@@ -916,10 +916,10 @@ void field_real::get_opt_type(String *answer,
else
{
if (min_arg >= -FLT_MAX && max_arg <= FLT_MAX)
- sprintf(buff, "FLOAT(%d,%d)", (int) max_length - (item->decimals + 1),
+ sprintf(buff, "FLOAT(%d,%d)", (int) max_length - (item->decimals + 1) + max_notzero_dec_len,
max_notzero_dec_len);
else
- sprintf(buff, "DOUBLE(%d,%d)", (int) max_length - (item->decimals + 1),
+ sprintf(buff, "DOUBLE(%d,%d)", (int) max_length - (item->decimals + 1) + max_notzero_dec_len,
max_notzero_dec_len);
answer->append(buff, (uint) strlen(buff));
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 093173ab949..645ac6e28f3 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -179,9 +179,9 @@ THD::THD()
lock_id(&main_lock_id),
user_time(0), in_sub_stmt(0), global_read_lock(0), is_fatal_error(0),
rand_used(0), time_zone_used(0),
- last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0),
- in_lock_tables(0), bootstrap(0), derived_tables_processing(FALSE),
- spcont(NULL)
+ last_insert_id_used(0), last_insert_id_used_bin_log(0), insert_id_used(0),
+ clear_next_insert_id(0), in_lock_tables(0), bootstrap(0),
+ derived_tables_processing(FALSE), spcont(NULL)
{
stmt_arena= this;
thread_stack= 0;
@@ -553,10 +553,25 @@ bool THD::store_globals()
}
-/* Cleanup after a query */
+/*
+ Cleanup after query.
+
+ SYNOPSIS
+ THD::cleanup_after_query()
+
+ DESCRIPTION
+ This function is used to reset thread data to its default state.
+
+ NOTE
+ This function is not suitable for setting thread data to some
+ non-default values, as there is only one replication thread, so
+ different master threads may overwrite data of each other on
+ slave.
+*/
void THD::cleanup_after_query()
{
+ last_insert_id_used= FALSE;
if (clear_next_insert_id)
{
clear_next_insert_id= 0;
@@ -568,6 +583,7 @@ void THD::cleanup_after_query()
where= THD::DEFAULT_WHERE;
}
+
/*
Convert a string to another character set
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 039c133e885..62cfb0119aa 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -306,7 +306,7 @@ public:
const char *log_name);
void new_file(bool need_lock);
bool write(THD *thd, enum enum_server_command command,
- const char *format,...);
+ const char *format, ...) ATTRIBUTE_FORMAT(printf, 4, 5);
bool write(THD *thd, const char *query, uint query_length,
time_t query_start=0);
bool write(Log_event* event_info); // binary log write
@@ -1252,17 +1252,29 @@ public:
ulonglong next_insert_id;
/* Remember last next_insert_id to reset it if something went wrong */
ulonglong prev_insert_id;
+
/*
- The insert_id used for the last statement or set by SET LAST_INSERT_ID=#
- or SELECT LAST_INSERT_ID(#). Used for binary log and returned by
- LAST_INSERT_ID()
+ At the beginning of the statement last_insert_id holds the first
+ generated value of the previous statement. During statement
+ execution it is updated to the value just generated, but then
+ restored to the value that was generated first, so for the next
+ statement it will again be "the first generated value of the
+ previous statement".
+
+ It may also be set with "LAST_INSERT_ID(expr)" or
+ "@@LAST_INSERT_ID= expr", but the effect of such setting will be
+ seen only in the next statement.
*/
ulonglong last_insert_id;
+
/*
- Set to the first value that LAST_INSERT_ID() returned for the last
- statement. When this is set, last_insert_id_used is set to true.
+ current_insert_id remembers the first generated value of the
+ previous statement, and does not change during statement
+ execution. Its value returned from LAST_INSERT_ID() and
+ @@LAST_INSERT_ID.
*/
ulonglong current_insert_id;
+
ulonglong limit_found_rows;
ulonglong options; /* Bitmap of states */
longlong row_count_func; /* For the ROW_COUNT() function */
@@ -1325,7 +1337,31 @@ public:
bool last_cuted_field;
bool no_errors, password, is_fatal_error;
bool query_start_used, rand_used, time_zone_used;
- bool last_insert_id_used,insert_id_used, clear_next_insert_id;
+
+ /*
+ last_insert_id_used is set when current statement calls
+ LAST_INSERT_ID() or reads @@LAST_INSERT_ID.
+ */
+ bool last_insert_id_used;
+
+ /*
+ last_insert_id_used is set when current statement or any stored
+ function called from this statement calls LAST_INSERT_ID() or
+ reads @@LAST_INSERT_ID, so that binary log LAST_INSERT_ID_EVENT be
+ generated. Required for statement-based binary log for issuing
+ "SET LAST_INSERT_ID= #" before "SELECT func()", if func() reads
+ LAST_INSERT_ID.
+ */
+ bool last_insert_id_used_bin_log;
+
+ /*
+ insert_id_used is set when current statement updates
+ THD::last_insert_id, so that binary log INSERT_ID_EVENT be
+ generated.
+ */
+ bool insert_id_used;
+
+ bool clear_next_insert_id;
/* for IS NULL => = last_insert_id() fix in remove_eq_conds() */
bool substitute_null_with_insert_id;
bool in_lock_tables;
@@ -1461,15 +1497,6 @@ public:
insert_id_used=1;
substitute_null_with_insert_id= TRUE;
}
- inline ulonglong insert_id(void)
- {
- if (!last_insert_id_used)
- {
- last_insert_id_used=1;
- current_insert_id=last_insert_id;
- }
- return last_insert_id;
- }
inline ulonglong found_rows(void)
{
return limit_found_rows;
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 3db6eae8695..2ce83caa369 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -590,10 +590,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
#endif
error=write_record(thd, table ,&info);
/*
- If auto_increment values are used, save the first one
- for LAST_INSERT_ID() and for the update log.
- We can't use insert_id() as we don't want to touch the
- last_insert_id_used flag.
+ If auto_increment values are used, save the first one for
+ LAST_INSERT_ID() and for the update log.
*/
if (! id && thd->insert_id_used)
{ // Get auto increment value
@@ -1303,6 +1301,9 @@ public:
time_t start_time;
bool query_start_used,last_insert_id_used,insert_id_used, ignore, log_query;
ulonglong last_insert_id;
+ ulonglong next_insert_id;
+ ulong auto_increment_increment;
+ ulong auto_increment_offset;
timestamp_auto_set_type timestamp_field_type;
uint query_length;
@@ -1684,6 +1685,22 @@ static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic, bool igno
row->last_insert_id= thd->last_insert_id;
row->timestamp_field_type= table->timestamp_field_type;
+ /* The session variable settings can always be copied. */
+ row->auto_increment_increment= thd->variables.auto_increment_increment;
+ row->auto_increment_offset= thd->variables.auto_increment_offset;
+ /*
+ Next insert id must be set for the first value in a multi-row insert
+ only. So clear it after the first use. Assume a multi-row insert.
+ Since the user thread doesn't really execute the insert,
+ thd->next_insert_id is left untouched between the rows. If we copy
+ the same insert id to every row of the multi-row insert, the delayed
+ insert thread would copy this before inserting every row. Thus it
+ tries to insert all rows with the same insert id. This fails on the
+ unique constraint. So just the first row would be really inserted.
+ */
+ row->next_insert_id= thd->next_insert_id;
+ thd->next_insert_id= 0;
+
di->rows.push_back(row);
di->stacked_inserts++;
di->status=1;
@@ -2055,6 +2072,14 @@ bool delayed_insert::handle_inserts(void)
thd.insert_id_used=row->insert_id_used;
table->timestamp_field_type= row->timestamp_field_type;
+ /* The session variable settings can always be copied. */
+ thd.variables.auto_increment_increment= row->auto_increment_increment;
+ thd.variables.auto_increment_offset= row->auto_increment_offset;
+ /* Next insert id must be used only if non-zero. */
+ if (row->next_insert_id)
+ thd.next_insert_id= row->next_insert_id;
+ DBUG_PRINT("loop", ("next_insert_id: %lu", (ulong) thd.next_insert_id));
+
info.ignore= row->ignore;
info.handle_duplicates= row->dup;
if (info.ignore ||
@@ -2076,6 +2101,20 @@ bool delayed_insert::handle_inserts(void)
info.error_count++; // Ignore errors
thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status);
row->log_query = 0;
+ /*
+ We must reset next_insert_id. Otherwise all following rows may
+ become duplicates. If write_record() failed on a duplicate and
+ next_insert_id would be left unchanged, the next rows would also
+ be tried with the same insert id and would fail. Since the end
+ of a multi-row statement is unknown here, all following rows in
+ the queue would be dropped, regardless which thread added them.
+ After the queue is used up, next_insert_id is cleared and the
+ next run will succeed. This could even happen if these come from
+ the same multi-row statement as the current queue contents. That
+ way it would look somewhat random which rows are rejected after
+ a duplicate.
+ */
+ thd.next_insert_id= 0;
}
if (using_ignore)
{
@@ -2121,6 +2160,7 @@ bool delayed_insert::handle_inserts(void)
/* This should never happen */
table->file->print_error(error,MYF(0));
sql_print_error("%s",thd.net.last_error);
+ DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed in loop"));
goto err;
}
query_cache_invalidate3(&thd, table, 1);
@@ -2146,6 +2186,7 @@ bool delayed_insert::handle_inserts(void)
{ // This shouldn't happen
table->file->print_error(error,MYF(0));
sql_print_error("%s",thd.net.last_error);
+ DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed after loop"));
goto err;
}
query_cache_invalidate3(&thd, table, 1);
@@ -2153,13 +2194,16 @@ bool delayed_insert::handle_inserts(void)
DBUG_RETURN(0);
err:
+ DBUG_EXECUTE("error", max_rows= 0;);
/* Remove all not used rows */
while ((row=rows.get()))
{
delete row;
thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status);
stacked_inserts--;
+ DBUG_EXECUTE("error", max_rows++;);
}
+ DBUG_PRINT("error", ("dropped %lu rows after an error", max_rows));
thread_safe_increment(delayed_insert_errors, &LOCK_delayed_status);
pthread_mutex_lock(&mutex);
DBUG_RETURN(1);
@@ -2447,7 +2491,7 @@ bool select_insert::send_data(List<Item> &values)
*/
table->next_number_field->reset();
if (!last_insert_id && thd->insert_id_used)
- last_insert_id= thd->insert_id();
+ last_insert_id= thd->last_insert_id;
}
}
DBUG_RETURN(error);
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index d5faf6ee7e9..bdc08b7bd2d 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -616,10 +616,8 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
thd->no_trans_update= no_trans_update;
/*
- If auto_increment values are used, save the first one
- for LAST_INSERT_ID() and for the binary/update log.
- We can't use insert_id() as we don't want to touch the
- last_insert_id_used flag.
+ If auto_increment values are used, save the first one for
+ LAST_INSERT_ID() and for the binary/update log.
*/
if (!id && thd->insert_id_used)
id= thd->last_insert_id;
@@ -784,10 +782,8 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (write_record(thd, table, &info))
DBUG_RETURN(1);
/*
- If auto_increment values are used, save the first one
- for LAST_INSERT_ID() and for the binary/update log.
- We can't use insert_id() as we don't want to touch the
- last_insert_id_used flag.
+ If auto_increment values are used, save the first one for
+ LAST_INSERT_ID() and for the binary/update log.
*/
if (!id && thd->insert_id_used)
id= thd->last_insert_id;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 18d048df393..c62c286cfdb 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2422,6 +2422,20 @@ mysql_execute_command(THD *thd)
thd->net.no_send_error= 0;
/*
+ Remember first generated insert id value of the previous
+ statement. We remember it here at the beginning of the statement,
+ and also in Item_func_last_insert_id::fix_fields() and
+ sys_var_last_insert_id::value_ptr(). Last two places are required
+ because LAST_INSERT_ID() and @@LAST_INSERT_ID may also be used in
+ expression that is not executed with mysql_execute_command().
+
+ And we remember it here because some statements read
+ @@LAST_INSERT_ID indirectly, like "SELECT * FROM t1 WHERE id IS
+ NULL", that may replace "id IS NULL" with "id = <LAST_INSERT_ID>".
+ */
+ thd->current_insert_id= thd->last_insert_id;
+
+ /*
In many cases first table of main SELECT_LEX have special meaning =>
check that it is first table in global list and relink it first in
queries_tables list if it is necessary (we need such relinking only
@@ -5636,7 +5650,8 @@ void mysql_reset_thd_for_next_command(THD *thd)
DBUG_ENTER("mysql_reset_thd_for_next_command");
thd->free_list= 0;
thd->select_number= 1;
- thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
+ thd->query_start_used= thd->insert_id_used=0;
+ thd->last_insert_id_used_bin_log= FALSE;
thd->is_fatal_error= thd->time_zone_used= 0;
thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS |
SERVER_QUERY_NO_INDEX_USED |
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 1dce7390ef1..cb1c393457e 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1137,17 +1137,28 @@ JOIN::optimize()
tmp_table_param.hidden_field_count= (all_fields.elements -
fields_list.elements);
+ ORDER *tmp_group= ((!simple_group && !procedure &&
+ !(test_flags & TEST_NO_KEY_GROUP)) ? group_list :
+ (ORDER*) 0);
+ /*
+ Pushing LIMIT to the temporary table creation is not applicable
+ when there is ORDER BY or GROUP BY or there is no GROUP BY, but
+ there are aggregate functions, because in all these cases we need
+ all result rows.
+ */
+ ha_rows tmp_rows_limit= ((order == 0 || skip_sort_order ||
+ test(select_options & OPTION_BUFFER_RESULT)) &&
+ !tmp_group &&
+ !thd->lex->current_select->with_sum_func) ?
+ select_limit : HA_POS_ERROR;
+
if (!(exec_tmp_table1 =
create_tmp_table(thd, &tmp_table_param, all_fields,
- ((!simple_group && !procedure &&
- !(test_flags & TEST_NO_KEY_GROUP)) ?
- group_list : (ORDER*) 0),
+ tmp_group,
group_list ? 0 : select_distinct,
group_list && simple_group,
select_options,
- (order == 0 || skip_sort_order ||
- test(select_options & OPTION_BUFFER_RESULT)) ?
- select_limit : HA_POS_ERROR,
+ tmp_rows_limit,
(char *) "")))
DBUG_RETURN(1);
@@ -8269,7 +8280,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
Field *field=((Item_field*) args[0])->field;
if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null &&
(thd->options & OPTION_AUTO_IS_NULL) &&
- thd->insert_id() && thd->substitute_null_with_insert_id)
+ thd->current_insert_id && thd->substitute_null_with_insert_id)
{
#ifdef HAVE_QUERY_CACHE
query_cache_abort(&thd->net);
@@ -8277,9 +8288,16 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
COND *new_cond;
if ((new_cond= new Item_func_eq(args[0],
new Item_int("last_insert_id()",
- thd->insert_id(),
+ thd->current_insert_id,
21))))
{
+ /*
+ Set THD::last_insert_id_used_bin_log manually, as this
+ statement uses LAST_INSERT_ID() in a sense, and should
+ issue LAST_INSERT_ID_EVENT.
+ */
+ thd->last_insert_id_used_bin_log= TRUE;
+
cond=new_cond;
/*
Item_func_eq can't be fixed after creation so we do not check
@@ -9224,6 +9242,13 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
thd->variables.max_heap_table_size) :
thd->variables.tmp_table_size)/ table->s->reclength);
set_if_bigger(table->s->max_rows,1); // For dummy start options
+ /*
+ Push the LIMIT clause to the temporary table creation, so that we
+ materialize only up to 'rows_limit' records instead of all result records.
+ */
+ set_if_smaller(table->s->max_rows, rows_limit);
+ param->end_write_records= rows_limit;
+
keyinfo= param->keyinfo;
if (group)
@@ -9353,19 +9378,6 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
}
}
- /*
- Push the LIMIT clause to the temporary table creation, so that we
- materialize only up to 'rows_limit' records instead of all result records.
- This optimization is not applicable when there is GROUP BY or there is
- no GROUP BY, but there are aggregate functions, because both must be
- computed for all result rows.
- */
- if (!group && !thd->lex->current_select->with_sum_func)
- {
- set_if_smaller(table->s->max_rows, rows_limit);
- param->end_write_records= rows_limit;
- }
-
if (thd->is_fatal_error) // If end of memory
goto err; /* purecov: inspected */
table->s->db_record_offset= 1;
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index eb78f4fbdae..4469bbacf90 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -4277,7 +4277,7 @@ ST_FIELD_INFO triggers_fields_info[]=
ST_FIELD_INFO variables_fields_info[]=
{
{"Variable_name", 80, MYSQL_TYPE_STRING, 0, 0, "Variable_name"},
- {"Value", 255, MYSQL_TYPE_STRING, 0, 0, "Value"},
+ {"Value", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, "Value"},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
};
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 8864cf3c8bc..f6dbba4aa79 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -224,7 +224,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
DBUG_ENTER("mysql_rm_table_part2");
- if (lock_table_names(thd, tables))
+ if (!drop_temporary && lock_table_names(thd, tables))
DBUG_RETURN(1);
/* Don't give warnings for not found errors, as we already generate notes */
@@ -334,7 +334,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
}
}
- unlock_table_names(thd, tables, (TABLE_LIST*) 0);
+ if (!drop_temporary)
+ unlock_table_names(thd, tables, (TABLE_LIST*) 0);
thd->no_warnings_for_error= 0;
DBUG_RETURN(error);
}
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 6bb50d602c3..c0c9c34cbeb 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -1548,6 +1548,38 @@ void Table_triggers_list::mark_fields_used(THD *thd, trg_event_type event)
/*
+ Check if field of subject table can be changed in before update trigger.
+
+ SYNOPSIS
+ is_updated_in_before_update_triggers()
+ field Field object for field to be checked
+
+ NOTE
+ Field passed to this function should be bound to the same
+ TABLE object as Table_triggers_list.
+
+ RETURN VALUE
+ TRUE Field is changed
+ FALSE Otherwise
+*/
+
+bool Table_triggers_list::is_updated_in_before_update_triggers(Field *fld)
+{
+ Item_trigger_field *trg_fld;
+ for (trg_fld= trigger_fields[TRG_EVENT_UPDATE][TRG_ACTION_BEFORE];
+ trg_fld != 0;
+ trg_fld= trg_fld->next_trg_field)
+ {
+ if (trg_fld->get_settable_routine_parameter() &&
+ trg_fld->field_idx != (uint)-1 &&
+ table->field[trg_fld->field_idx]->eq(fld))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
Trigger BUG#14090 compatibility hook
SYNOPSIS
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index 521905a2c56..7e0fadfa677 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -116,15 +116,12 @@ public:
bodies[TRG_EVENT_DELETE][TRG_ACTION_AFTER]);
}
- bool has_before_update_triggers()
- {
- return test(bodies[TRG_EVENT_UPDATE][TRG_ACTION_BEFORE]);
- }
-
void set_table(TABLE *new_table);
void mark_fields_used(THD *thd, trg_event_type event);
+ bool is_updated_in_before_update_triggers(Field *fld);
+
friend class Item_trigger_field;
friend int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
TABLE_LIST *table);
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index f81ee3abdca..d431b671f18 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -274,7 +274,7 @@ int mysql_update(THD *thd,
{
used_index= select->quick->index;
used_key_is_modified= (!select->quick->unique_key_range() &&
- select->quick->check_if_keys_used(&fields));
+ select->quick->is_keys_used(&fields));
}
else
{
@@ -282,7 +282,7 @@ int mysql_update(THD *thd,
if (used_index == MAX_KEY) // no index for sort order
used_index= table->file->key_used_on_scan;
if (used_index != MAX_KEY)
- used_key_is_modified= check_if_key_used(table, used_index, fields);
+ used_key_is_modified= is_key_used(table, used_index, fields);
}
if (used_key_is_modified || order)
@@ -568,7 +568,7 @@ int mysql_update(THD *thd,
thd->row_count_func=
(thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
send_ok(thd, (ulong) thd->row_count_func,
- thd->insert_id_used ? thd->insert_id() : 0L,buff);
+ thd->insert_id_used ? thd->last_insert_id : 0L,buff);
DBUG_PRINT("info",("%d records updated",updated));
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */
@@ -1095,6 +1095,8 @@ multi_update::initialize_tables(JOIN *join)
List<Item> temp_fields= *fields_for_table[cnt];
ORDER group;
+ if (ignore)
+ table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
if (table == main_table) // First table in join
{
if (safe_update_on_fly(join->join_tab, &temp_fields))
@@ -1189,21 +1191,15 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
return TRUE; // At most one matching row
case JT_REF:
case JT_REF_OR_NULL:
- return !check_if_key_used(table, join_tab->ref.key, *fields) &&
- !(table->triggers &&
- table->triggers->has_before_update_triggers());
+ return !is_key_used(table, join_tab->ref.key, *fields);
case JT_ALL:
/* If range search on index */
if (join_tab->quick)
- return !join_tab->quick->check_if_keys_used(fields) &&
- !(table->triggers &&
- table->triggers->has_before_update_triggers());
+ return !join_tab->quick->is_keys_used(fields);
/* If scanning in clustered key */
if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
table->s->primary_key < MAX_KEY)
- return !check_if_key_used(table, table->s->primary_key, *fields) &&
- !(table->triggers &&
- table->triggers->has_before_update_triggers());
+ return !is_key_used(table, table->s->primary_key, *fields);
return TRUE;
default:
break; // Avoid compler warning
@@ -1216,7 +1212,11 @@ multi_update::~multi_update()
{
TABLE_LIST *table;
for (table= update_tables ; table; table= table->next_local)
+ {
table->table->no_keyread= table->table->no_cache= 0;
+ if (ignore)
+ table->table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+ }
if (tmp_tables)
{
@@ -1567,6 +1567,6 @@ 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,
- thd->insert_id_used ? thd->insert_id() : 0L,buff);
+ thd->insert_id_used ? thd->last_insert_id : 0L,buff);
return FALSE;
}
diff --git a/sql/stacktrace.c b/sql/stacktrace.c
index 43f35c452f7..a2fe2ab88f1 100644
--- a/sql/stacktrace.c
+++ b/sql/stacktrace.c
@@ -222,7 +222,7 @@ terribly wrong...\n");
fprintf(stderr, "Stack trace seems successful - bottom reached\n");
end:
- fprintf(stderr, "Please read http://dev.mysql.com/doc/mysql/en/Using_stack_trace.html and follow instructions on how to resolve the stack trace. Resolved\n\
+ fprintf(stderr, "Please read http://dev.mysql.com/doc/mysql/en/using-stack-trace.html and follow instructions on how to resolve the stack trace. Resolved\n\
stack trace is much more helpful in diagnosing the problem, so please do \n\
resolve it\n");
}
diff --git a/sql/table.cc b/sql/table.cc
index 4dd3494f834..3a76ba0cc1a 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -468,7 +468,21 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
count)))
goto err;
for (count= 0; count < interval->count; count++)
- interval->type_lengths[count]= strlen(interval->type_names[count]);
+ {
+ char *val= (char*) interval->type_names[count];
+ interval->type_lengths[count]= strlen(val);
+ /*
+ Replace all ',' symbols with NAMES_SEP_CHAR.
+ See the comment in unireg.cc, pack_fields() function
+ for details.
+ */
+ for (uint cnt= 0 ; cnt < interval->type_lengths[count] ; cnt++)
+ {
+ char c= val[cnt];
+ if (c == ',')
+ val[cnt]= NAMES_SEP_CHAR;
+ }
+ }
interval->type_lengths[count]= 0;
}
}
diff --git a/sql/unireg.cc b/sql/unireg.cc
index 93afd9c9e4e..768a288ca19 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -718,6 +718,21 @@ static bool pack_fields(File file, List<create_field> &create_fields,
tmp.append(NAMES_SEP_CHAR);
for (const char **pos=field->interval->type_names ; *pos ; pos++)
{
+ char *val= (char*) *pos;
+ uint str_len= strlen(val);
+ /*
+ Note, hack: in old frm NAMES_SEP_CHAR is used to separate
+ names in the interval (ENUM/SET). To allow names to contain
+ NAMES_SEP_CHAR, we replace it with a comma before writing frm.
+ Backward conversion is done during frm file opening,
+ See table.cc, openfrm() function
+ */
+ for (uint cnt= 0 ; cnt < str_len ; cnt++)
+ {
+ char c= val[cnt];
+ if (c == NAMES_SEP_CHAR)
+ val[cnt]= ',';
+ }
tmp.append(*pos);
tmp.append(NAMES_SEP_CHAR);
}