summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/examples/ha_archive.cc124
-rw-r--r--sql/examples/ha_archive.h4
-rw-r--r--sql/field.cc286
-rw-r--r--sql/field.h19
-rw-r--r--sql/field_conv.cc3
-rw-r--r--sql/filesort.cc12
-rw-r--r--sql/ha_berkeley.cc12
-rw-r--r--sql/ha_heap.cc80
-rw-r--r--sql/ha_heap.h10
-rw-r--r--sql/ha_innodb.cc192
-rw-r--r--sql/ha_innodb.h5
-rw-r--r--sql/ha_myisam.h9
-rw-r--r--sql/ha_myisammrg.cc11
-rw-r--r--sql/ha_myisammrg.h1
-rw-r--r--sql/ha_ndbcluster.cc903
-rw-r--r--sql/ha_ndbcluster.h86
-rw-r--r--sql/handler.cc118
-rw-r--r--sql/handler.h10
-rw-r--r--sql/item.cc379
-rw-r--r--sql/item.h144
-rw-r--r--sql/item_cmpfunc.cc267
-rw-r--r--sql/item_cmpfunc.h33
-rw-r--r--sql/item_create.cc11
-rw-r--r--sql/item_create.h2
-rw-r--r--sql/item_func.cc238
-rw-r--r--sql/item_func.h18
-rw-r--r--sql/item_geofunc.cc7
-rw-r--r--sql/item_row.cc15
-rw-r--r--sql/item_strfunc.cc242
-rw-r--r--sql/item_strfunc.h25
-rw-r--r--sql/item_subselect.cc214
-rw-r--r--sql/item_subselect.h24
-rw-r--r--sql/item_sum.cc61
-rw-r--r--sql/item_sum.h26
-rw-r--r--sql/item_timefunc.cc222
-rw-r--r--sql/item_timefunc.h8
-rw-r--r--sql/lex.h2
-rw-r--r--sql/lock.cc68
-rw-r--r--sql/log.cc107
-rw-r--r--sql/log_event.cc135
-rw-r--r--sql/log_event.h26
-rw-r--r--sql/mysql_priv.h51
-rw-r--r--sql/mysqld.cc397
-rw-r--r--sql/net_serv.cc18
-rw-r--r--sql/opt_range.cc30
-rw-r--r--sql/opt_range.h2
-rw-r--r--sql/password.c5
-rw-r--r--sql/procedure.h11
-rw-r--r--sql/protocol.cc30
-rw-r--r--sql/protocol.h1
-rw-r--r--sql/repl_failsafe.cc7
-rw-r--r--sql/set_var.cc132
-rw-r--r--sql/set_var.h2
-rw-r--r--sql/share/french/errmsg.txt2
-rw-r--r--sql/share/greek/errmsg.txt2
-rw-r--r--sql/share/japanese-sjis/errmsg.txt325
-rw-r--r--sql/share/portuguese/errmsg.txt2
-rw-r--r--sql/share/romanian/errmsg.txt2
-rw-r--r--sql/share/serbian/errmsg.txt2
-rw-r--r--sql/share/spanish/errmsg.txt6
-rw-r--r--sql/share/swedish/errmsg.txt4
-rw-r--r--sql/slave.cc104
-rw-r--r--sql/slave.h36
-rw-r--r--sql/spatial.cc4
-rw-r--r--sql/sql_acl.cc274
-rw-r--r--sql/sql_acl.h2
-rw-r--r--sql/sql_analyse.cc117
-rw-r--r--sql/sql_base.cc138
-rw-r--r--sql/sql_cache.cc50
-rw-r--r--sql/sql_cache.h6
-rw-r--r--sql/sql_class.cc47
-rw-r--r--sql/sql_class.h98
-rw-r--r--sql/sql_db.cc90
-rw-r--r--sql/sql_delete.cc10
-rw-r--r--sql/sql_derived.cc11
-rw-r--r--sql/sql_do.cc2
-rw-r--r--sql/sql_error.cc10
-rw-r--r--sql/sql_handler.cc6
-rw-r--r--sql/sql_help.cc17
-rw-r--r--sql/sql_insert.cc153
-rw-r--r--sql/sql_lex.cc81
-rw-r--r--sql/sql_lex.h80
-rw-r--r--sql/sql_list.h13
-rw-r--r--sql/sql_load.cc7
-rw-r--r--sql/sql_parse.cc574
-rw-r--r--sql/sql_prepare.cc96
-rw-r--r--sql/sql_rename.cc2
-rw-r--r--sql/sql_repl.cc21
-rw-r--r--sql/sql_repl.h2
-rw-r--r--sql/sql_select.cc145
-rw-r--r--sql/sql_select.h2
-rw-r--r--sql/sql_show.cc39
-rw-r--r--sql/sql_string.cc37
-rw-r--r--sql/sql_string.h16
-rw-r--r--sql/sql_table.cc236
-rw-r--r--sql/sql_udf.h3
-rw-r--r--sql/sql_union.cc45
-rw-r--r--sql/sql_update.cc126
-rw-r--r--sql/sql_yacc.yy219
-rw-r--r--sql/strfunc.cc61
-rw-r--r--sql/structs.h7
-rw-r--r--sql/table.cc115
-rw-r--r--sql/table.h10
-rw-r--r--sql/thr_malloc.cc7
-rw-r--r--sql/tztime.cc12
-rw-r--r--sql/tztime.h6
-rw-r--r--sql/udf_example.cc13
-rw-r--r--sql/unireg.cc33
-rw-r--r--sql/unireg.h2
109 files changed, 5851 insertions, 2524 deletions
diff --git a/sql/examples/ha_archive.cc b/sql/examples/ha_archive.cc
index 6fbfb3f9f9d..bc4af0c7dc7 100644
--- a/sql/examples/ha_archive.cc
+++ b/sql/examples/ha_archive.cc
@@ -42,18 +42,20 @@
handle bulk inserts as well (that is if someone was trying to read at
the same time since we would want to flush).
- A "meta" file is kept. All this file does is contain information on
- the number of rows.
+ A "meta" file is kept alongside the data file. This file serves two purpose.
+ The first purpose is to track the number of rows in the table. The second
+ purpose is to determine if the table was closed properly or not. When the
+ meta file is first opened it is marked as dirty. It is opened when the table
+ itself is opened for writing. When the table is closed the new count for rows
+ is written to the meta file and the file is marked as clean. If the meta file
+ is opened and it is marked as dirty, it is assumed that a crash occured. At
+ this point an error occurs and the user is told to rebuild the file.
+ A rebuild scans the rows and rewrites the meta file. If corruption is found
+ in the data file then the meta file is not repaired.
- No attempts at durability are made. You can corrupt your data. A repair
- method was added to repair the meta file that stores row information,
- but if your data file gets corrupted I haven't solved that. I could
- create a repair that would solve this, but do you want to take a
- chance of loosing your data?
+ At some point a recovery method for such a drastic case needs to be divised.
- Locks are row level, and you will get a consistant read. Transactions
- will be added later (they are not that hard to add at this
- stage).
+ Locks are row level, and you will get a consistant read.
For performance as far as table scans go it is quite fast. I don't have
good numbers but locally it has out performed both Innodb and MyISAM. For
@@ -88,7 +90,6 @@
compression but may speed up ordered searches).
Checkpoint the meta file to allow for faster rebuilds.
Dirty open (right now the meta file is repaired if a crash occured).
- Transactions.
Option to allow for dirty reads, this would lower the sync calls, which would make
inserts a lot faster, but would mean highly arbitrary reads.
@@ -113,10 +114,11 @@
data - The data is stored in a "row +blobs" format.
*/
+/* If the archive storage engine has been inited */
+static bool archive_inited= 0;
/* Variables for archive share methods */
pthread_mutex_t archive_mutex;
static HASH archive_open_tables;
-static int archive_init= 0;
/* The file extension */
#define ARZ ".ARZ" // The data file
@@ -142,6 +144,51 @@ static byte* archive_get_key(ARCHIVE_SHARE *share,uint *length,
return (byte*) share->table_name;
}
+
+/*
+ Initialize the archive handler.
+
+ SYNOPSIS
+ archive_db_init()
+ void
+
+ RETURN
+ FALSE OK
+ TRUE Error
+*/
+
+bool archive_db_init()
+{
+ archive_inited= 1;
+ VOID(pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST));
+ return (hash_init(&archive_open_tables, system_charset_info, 32, 0, 0,
+ (hash_get_key) archive_get_key, 0, 0));
+}
+
+
+/*
+ Release the archive handler.
+
+ SYNOPSIS
+ archive_db_end()
+ void
+
+ RETURN
+ FALSE OK
+*/
+
+bool archive_db_end()
+{
+ if (archive_inited)
+ {
+ hash_free(&archive_open_tables);
+ VOID(pthread_mutex_destroy(&archive_mutex));
+ }
+ archive_inited= 0;
+ return FALSE;
+}
+
+
/*
This method reads the header of a datafile and returns whether or not it was successful.
*/
@@ -269,23 +316,6 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
uint length;
char *tmp_name;
- if (!archive_init)
- {
- /* Hijack a mutex for init'ing the storage engine */
- pthread_mutex_lock(&LOCK_mysql_create_db);
- if (!archive_init)
- {
- VOID(pthread_mutex_init(&archive_mutex,MY_MUTEX_INIT_FAST));
- if (hash_init(&archive_open_tables,system_charset_info,32,0,0,
- (hash_get_key) archive_get_key,0,0))
- {
- pthread_mutex_unlock(&LOCK_mysql_create_db);
- return NULL;
- }
- archive_init++;
- }
- pthread_mutex_unlock(&LOCK_mysql_create_db);
- }
pthread_mutex_lock(&archive_mutex);
length=(uint) strlen(table_name);
@@ -333,6 +363,7 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
opposite.
*/
(void)write_meta_file(share->meta_file, share->rows_recorded, TRUE);
+
/*
It is expensive to open and close the data files and since you can't have
a gzip file that can be both read and written we keep a writer open
@@ -341,10 +372,8 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
if ((share->archive_write= gzopen(share->data_file_name, "ab")) == NULL)
goto error2;
if (my_hash_insert(&archive_open_tables, (byte*) share))
- goto error2;
- thr_lock_init(&share->lock);
- if (pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST))
goto error3;
+ thr_lock_init(&share->lock);
}
share->use_count++;
pthread_mutex_unlock(&archive_mutex);
@@ -352,14 +381,13 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
return share;
error3:
- VOID(pthread_mutex_destroy(&share->mutex));
- thr_lock_delete(&share->lock);
/* We close, but ignore errors since we already have errors */
(void)gzclose(share->archive_write);
error2:
my_close(share->meta_file,MYF(0));
error:
pthread_mutex_unlock(&archive_mutex);
+ VOID(pthread_mutex_destroy(&share->mutex));
my_free((gptr) share, MYF(0));
return NULL;
@@ -382,6 +410,8 @@ int ha_archive::free_share(ARCHIVE_SHARE *share)
(void)write_meta_file(share->meta_file, share->rows_recorded, FALSE);
if (gzclose(share->archive_write) == Z_ERRNO)
rc= 1;
+ if (my_close(share->meta_file, MYF(0)))
+ rc= 1;
my_free((gptr) share, MYF(0));
}
pthread_mutex_unlock(&archive_mutex);
@@ -493,23 +523,30 @@ int ha_archive::create(const char *name, TABLE *table_arg,
if ((archive= gzdopen(create_file, "ab")) == NULL)
{
error= errno;
- delete_table(name);
- goto error;
+ goto error2;
}
if (write_data_header(archive))
{
- gzclose(archive);
- goto error2;
+ error= errno;
+ goto error3;
}
if (gzclose(archive))
+ {
+ error= errno;
goto error2;
+ }
+
+ my_close(create_file, MYF(0));
DBUG_RETURN(0);
+error3:
+ /* We already have an error, so ignore results of gzclose. */
+ (void)gzclose(archive);
error2:
- error= errno;
- delete_table(name);
+ my_close(create_file, MYF(0));
+ delete_table(name);
error:
/* Return error number, if we got one */
DBUG_RETURN(error ? error : -1);
@@ -528,6 +565,7 @@ error:
int ha_archive::write_row(byte * buf)
{
z_off_t written;
+ Field_blob **field;
DBUG_ENTER("ha_archive::write_row");
statistic_increment(ha_write_count,&LOCK_status);
@@ -543,7 +581,7 @@ int ha_archive::write_row(byte * buf)
We should probably mark the table as damagaged if the record is written
but the blob fails.
*/
- for (Field_blob **field=table->blob_field ; *field ; field++)
+ for (field= table->blob_field ; *field ; field++)
{
char *ptr;
uint32 size= (*field)->get_length();
@@ -735,7 +773,7 @@ int ha_archive::rebuild_meta_file(char *table_name, File meta_file)
if ((rebuild_file= gzopen(data_file_name, "rb")) == NULL)
DBUG_RETURN(errno ? errno : -1);
- if (rc= read_data_header(rebuild_file))
+ if ((rc= read_data_header(rebuild_file)))
goto error;
/*
@@ -799,7 +837,7 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt)
DBUG_RETURN(-1);
}
- while (read= gzread(reader, block, IO_SIZE))
+ while ((read= gzread(reader, block, IO_SIZE)))
gzwrite(writer, block, read);
gzclose(reader);
diff --git a/sql/examples/ha_archive.h b/sql/examples/ha_archive.h
index b619de5f6c1..855d756368d 100644
--- a/sql/examples/ha_archive.h
+++ b/sql/examples/ha_archive.h
@@ -125,3 +125,7 @@ public:
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type);
};
+
+bool archive_db_init(void);
+bool archive_db_end(void);
+
diff --git a/sql/field.cc b/sql/field.cc
index 3e625f3a582..2baf1bf84fd 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -245,6 +245,7 @@ static Field::field_cast_enum field_cast_date[]=
Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP};
static Field::field_cast_enum field_cast_newdate[]=
{Field::FIELD_CAST_NEWDATE,
+ Field::FIELD_CAST_DATE,
Field::FIELD_CAST_DATETIME,
Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING,
Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP};
@@ -531,7 +532,8 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs)
/* Convert character set if the old one is multi byte */
if (cs->mbmaxlen > 1)
{
- tmp.copy(from, len, cs, &my_charset_bin);
+ uint dummy_errors;
+ tmp.copy(from, len, cs, &my_charset_bin, &dummy_errors);
from= tmp.ptr();
len= tmp.length();
}
@@ -966,7 +968,9 @@ int Field_decimal::store(longlong nr)
double Field_decimal::val_real(void)
{
int not_used;
- return my_strntod(&my_charset_bin, ptr, field_length, NULL, &not_used);
+ char *end_not_used;
+ return my_strntod(&my_charset_bin, ptr, field_length, &end_not_used,
+ &not_used);
}
longlong Field_decimal::val_int(void)
@@ -1774,6 +1778,24 @@ void Field_medium::sql_type(String &res) const
** long int
****************************************************************************/
+/*
+ A helper function to check whether the next character
+ in the string "s" is MINUS SIGN.
+*/
+#ifdef HAVE_CHARSET_ucs2
+static bool test_if_minus(CHARSET_INFO *cs,
+ const char *s, const char *e)
+{
+ my_wc_t wc;
+ return cs->cset->mb_wc(cs, &wc, (uchar*) s, (uchar*) e) > 0 && wc == '-';
+}
+#else
+/*
+ If not UCS2 support is compiled then it is easier
+*/
+#define test_if_minus(cs, s, e) (*s == '-')
+#endif
+
int Field_long::store(const char *from,uint len,CHARSET_INFO *cs)
{
@@ -1788,7 +1810,7 @@ int Field_long::store(const char *from,uint len,CHARSET_INFO *cs)
if (unsigned_flag)
{
- if (!len || *from == '-')
+ if (!len || test_if_minus(cs, from, from + len))
{
tmp=0; // Set negative to 0
my_errno=ERANGE;
@@ -1859,9 +1881,9 @@ int Field_long::store(double nr)
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
- else if (nr > (double) (ulong) ~0L)
+ else if (nr > (double) UINT_MAX32)
{
- res=(int32) (uint32) ~0L;
+ res= UINT_MAX32;
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
@@ -2084,7 +2106,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs)
my_errno=0;
if (unsigned_flag)
{
- if (!len || *from == '-')
+ if (!len || test_if_minus(cs, from, from + len))
{
tmp=0; // Set negative to 0
my_errno= ERANGE;
@@ -2144,7 +2166,7 @@ int Field_longlong::store(double nr)
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
- else if (nr >= (double) LONGLONG_MAX)
+ else if (nr >= (double) (ulonglong) LONGLONG_MAX)
{
res=(longlong) LONGLONG_MAX;
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
@@ -2394,23 +2416,7 @@ int Field_float::store(double nr)
int Field_float::store(longlong nr)
{
- int error= 0;
- float j= (float) nr;
- if (unsigned_flag && j < 0)
- {
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
- j=0;
- error= 1;
- }
-#ifdef WORDS_BIGENDIAN
- if (table->db_low_byte_first)
- {
- float4store(ptr,j);
- }
- else
-#endif
- memcpy_fixed(ptr,(byte*) &j,sizeof(j));
- return error;
+ return store((double)nr);
}
@@ -2689,23 +2695,7 @@ int Field_double::store(double nr)
int Field_double::store(longlong nr)
{
- double j= (double) nr;
- int error= 0;
- if (unsigned_flag && j < 0)
- {
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
- error= 1;
- j=0;
- }
-#ifdef WORDS_BIGENDIAN
- if (table->db_low_byte_first)
- {
- float8store(ptr,j);
- }
- else
-#endif
- doublestore(ptr,j);
- return error;
+ return store((double)nr);
}
@@ -3542,9 +3532,17 @@ void Field_time::sql_type(String &res) const
int Field_year::store(const char *from, uint len,CHARSET_INFO *cs)
{
- int not_used; // We can ignore result from str2int
+ int err;
char *end;
- long nr= my_strntol(cs, from, len, 10, &end, &not_used);
+ long nr= my_strntol(cs, from, len, 10, &end, &err);
+
+ if (err)
+ {
+ if (table->in_use->count_cuted_fields)
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
+ *ptr= 0;
+ return 0;
+ }
if (nr < 0 || nr >= 100 && nr <= 1900 || nr > 2155)
{
@@ -4085,6 +4083,10 @@ int Field_datetime::store(longlong nr)
void Field_datetime::store_time(TIME *ltime,timestamp_type type)
{
longlong tmp;
+ /*
+ We don't perform range checking here since values stored in TIME
+ structure always fit into DATETIME range.
+ */
if (type == MYSQL_TIMESTAMP_DATE || type == MYSQL_TIMESTAMP_DATETIME)
tmp=((ltime->year*10000L+ltime->month*100+ltime->day)*LL(1000000)+
(ltime->hour*10000L+ltime->minute*100+ltime->second));
@@ -4275,9 +4277,12 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
/* Convert character set if nesessary */
if (String::needs_conversion(length, cs, field_charset, &not_used))
{
- tmpstr.copy(from, length, cs, field_charset);
+ uint conv_errors;
+ tmpstr.copy(from, length, cs, field_charset, &conv_errors);
from= tmpstr.ptr();
length= tmpstr.length();
+ if (conv_errors)
+ error= 1;
}
/*
@@ -4300,11 +4305,11 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
from+= field_charset->cset->scan(field_charset, from, end,
MY_SEQ_SPACES);
if (from != end)
- {
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
- error=1;
- }
+ error= 1;
}
+ if (error)
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
+
return error;
}
@@ -4325,13 +4330,20 @@ int Field_str::store(double nr)
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
uint length;
bool use_scientific_notation= TRUE;
- use_scientific_notation= TRUE;
- if (field_length < 32 && fabs(nr) < log_10[field_length]-1)
+ /*
+ Check fabs(nr) against longest value that can be stored in field,
+ which depends on whether the value is < 1 or not, and negative or not
+ */
+ double anr= fabs(nr);
+ int neg= (nr < 0.0) ? 1 : 0;
+ if (field_length > 4 && field_length < 32 &&
+ (anr < 1.0 ? anr > 1/(log_10[max(0,field_length-neg-2)]) /* -2 for "0." */
+ : anr < log_10[field_length-neg]-1))
use_scientific_notation= FALSE;
length= (uint) my_sprintf(buff, (buff, "%-.*g",
(use_scientific_notation ?
- max(0, (int)field_length-5) :
+ max(0, (int)field_length-neg-5) :
field_length),
nr));
/*
@@ -4360,8 +4372,9 @@ int Field_string::store(longlong nr)
double Field_string::val_real(void)
{
int not_used;
+ char *end_not_used;
CHARSET_INFO *cs=charset();
- return my_strntod(cs,ptr,field_length,(char**)0,&not_used);
+ return my_strntod(cs, ptr, field_length, &end_not_used, &not_used);
}
@@ -4386,6 +4399,8 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)),
int Field_string::cmp(const char *a_ptr, const char *b_ptr)
{
+ uint a_len, b_len;
+
if (field_charset->strxfrm_multiply > 1)
{
/*
@@ -4397,22 +4412,31 @@ int Field_string::cmp(const char *a_ptr, const char *b_ptr)
(const uchar*) b_ptr,
field_length);
}
- return my_strnncoll(field_charset,(const uchar*) a_ptr, field_length,
- (const uchar*) b_ptr, field_length);
+ if (field_charset->mbmaxlen != 1)
+ {
+ uint char_len= field_length/field_charset->mbmaxlen;
+ a_len= my_charpos(field_charset, a_ptr, a_ptr + field_length, char_len);
+ b_len= my_charpos(field_charset, b_ptr, b_ptr + field_length, char_len);
+ }
+ else
+ a_len= b_len= field_length;
+ return my_strnncoll(field_charset,(const uchar*) a_ptr, a_len,
+ (const uchar*) b_ptr, b_len);
}
+
void Field_string::sort_string(char *to,uint length)
{
uint tmp=my_strnxfrm(field_charset,
(unsigned char *) to, length,
(unsigned char *) ptr, field_length);
- if (tmp < length)
- field_charset->cset->fill(field_charset, to + tmp, length - tmp, ' ');
+ DBUG_ASSERT(tmp == length);
}
void Field_string::sql_type(String &res) const
{
+ THD *thd= table->in_use;
CHARSET_INFO *cs=res.charset();
ulong length= cs->cset->snprintf(cs,(char*) res.ptr(),
res.alloced_length(), "%s(%d)",
@@ -4423,6 +4447,9 @@ void Field_string::sql_type(String &res) const
(has_charset() ? "char" : "binary")),
(int) field_length / charset()->mbmaxlen);
res.length(length);
+ if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
+ has_charset() && (charset()->state & MY_CS_BINSORT))
+ res.append(" binary");
}
char *Field_string::pack(char *to, const char *from, uint max_length)
@@ -4528,16 +4555,20 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
/* Convert character set if nesessary */
if (String::needs_conversion(length, cs, field_charset, &not_used))
{
- tmpstr.copy(from, length, cs, field_charset);
+ uint conv_errors;
+ tmpstr.copy(from, length, cs, field_charset, &conv_errors);
from= tmpstr.ptr();
length= tmpstr.length();
+ if (conv_errors)
+ error= 1;
}
if (length > field_length)
{
length=field_length;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
error= 1;
}
+ if (error)
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
memcpy(ptr+HA_KEY_BLOB_LENGTH,from,length);
int2store(ptr, length);
return error;
@@ -4559,7 +4590,9 @@ double Field_varstring::val_real(void)
int not_used;
uint length=uint2korr(ptr)+HA_KEY_BLOB_LENGTH;
CHARSET_INFO *cs=charset();
- return my_strntod(cs, ptr+HA_KEY_BLOB_LENGTH, length, (char**)0, &not_used);
+ char *end_not_used;
+ return my_strntod(cs, ptr+HA_KEY_BLOB_LENGTH, length, &end_not_used,
+ &not_used);
}
@@ -4601,9 +4634,7 @@ void Field_varstring::sort_string(char *to,uint length)
(uchar*) to, length,
(uchar*) ptr+HA_KEY_BLOB_LENGTH,
tot_length);
- if (tot_length < length)
- field_charset->cset->fill(field_charset, to+tot_length,length-tot_length,
- binary() ? (char) 0 : ' ');
+ DBUG_ASSERT(tot_length == length);
}
@@ -4865,6 +4896,7 @@ void Field_blob::put_length(char *pos, uint32 length)
int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
{
+ int error= 0;
if (!length)
{
bzero(ptr,Field_blob::pack_length());
@@ -4881,9 +4913,12 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
if ((was_conversion= String::needs_conversion(length, cs, field_charset,
&not_used)))
{
- tmpstr.copy(from, length, cs, field_charset);
+ uint conv_errors;
+ tmpstr.copy(from, length, cs, field_charset, &conv_errors);
from= tmpstr.ptr();
length= tmpstr.length();
+ if (conv_errors)
+ error= 1;
}
copy_length= max_data_length();
@@ -4897,8 +4932,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
min(length, copy_length),
copy_length);
if (copy_length < length)
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
-
+ error= 1;
Field_blob::store_length(copy_length);
if (was_conversion || table->copy_blobs || copy_length <= MAX_FIELD_WIDTH)
{ // Must make a copy
@@ -4910,6 +4944,8 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
}
bmove(ptr+packlength,(char*) &from,sizeof(char*));
}
+ if (error)
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
return 0;
}
@@ -4934,12 +4970,13 @@ double Field_blob::val_real(void)
{
int not_used;
char *blob;
+ char *end_not_used;
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
if (!blob)
return 0.0;
uint32 length=get_length(ptr);
CHARSET_INFO *cs=charset();
- return my_strntod(cs,blob,length,(char**)0, &not_used);
+ return my_strntod(cs,blob,length, &end_not_used, &not_used);
}
@@ -5116,10 +5153,7 @@ void Field_blob::sort_string(char *to,uint length)
blob_length=my_strnxfrm(field_charset,
(uchar*) to, length,
(uchar*) blob, blob_length);
- if (blob_length < length)
- field_charset->cset->fill(field_charset, to+blob_length,
- length-blob_length,
- binary() ? (char) 0 : ' ');
+ DBUG_ASSERT(blob_length == length);
}
}
@@ -5490,14 +5524,14 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs)
/* Convert character set if nesessary */
if (String::needs_conversion(length, cs, field_charset, &not_used))
{
- tmpstr.copy(from, length, cs, field_charset);
+ uint dummy_errors;
+ tmpstr.copy(from, length, cs, field_charset, &dummy_errors);
from= tmpstr.ptr();
length= tmpstr.length();
}
/* Remove end space */
- while (length > 0 && my_isspace(system_charset_info,from[length-1]))
- length--;
+ length= field_charset->cset->lengthsp(field_charset, from, length);
uint tmp=find_type2(typelib, from, length, field_charset);
if (!tmp)
{
@@ -5599,7 +5633,7 @@ String *Field_enum::val_str(String *val_buffer __attribute__((unused)),
val_ptr->set("", 0, field_charset);
else
val_ptr->set((const char*) typelib->type_names[tmp-1],
- (uint) strlen(typelib->type_names[tmp-1]),
+ typelib->type_lengths[tmp-1],
field_charset);
return val_ptr;
}
@@ -5636,12 +5670,14 @@ void Field_enum::sql_type(String &res) const
res.append("enum(");
bool flag=0;
- for (const char **pos= typelib->type_names; *pos; pos++)
+ uint *len= typelib->type_lengths;
+ for (const char **pos= typelib->type_names; *pos; pos++, len++)
{
+ uint dummy_errors;
if (flag)
res.append(',');
/* convert to res.charset() == utf8, then quote */
- enum_item.copy(*pos, strlen(*pos), charset(), res.charset());
+ enum_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors);
append_unescaped(&res, enum_item.ptr(), enum_item.length());
flag= 1;
}
@@ -5672,7 +5708,8 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs)
/* Convert character set if nesessary */
if (String::needs_conversion(length, cs, field_charset, &not_used_offset))
{
- tmpstr.copy(from, length, cs, field_charset);
+ uint dummy_errors;
+ tmpstr.copy(from, length, cs, field_charset, &dummy_errors);
from= tmpstr.ptr();
length= tmpstr.length();
}
@@ -5719,14 +5756,15 @@ String *Field_set::val_str(String *val_buffer,
uint bitnr=0;
val_buffer->length(0);
+ val_buffer->set_charset(field_charset);
while (tmp && bitnr < (uint) typelib->count)
{
if (tmp & 1)
{
if (val_buffer->length())
- val_buffer->append(field_separator);
+ val_buffer->append(&field_separator, 1, &my_charset_latin1);
String str(typelib->type_names[bitnr],
- (uint) strlen(typelib->type_names[bitnr]),
+ typelib->type_lengths[bitnr],
field_charset);
val_buffer->append(str);
}
@@ -5746,12 +5784,14 @@ void Field_set::sql_type(String &res) const
res.append("set(");
bool flag=0;
- for (const char **pos= typelib->type_names; *pos; pos++)
+ uint *len= typelib->type_lengths;
+ for (const char **pos= typelib->type_names; *pos; pos++, len++)
{
+ uint dummy_errors;
if (flag)
res.append(',');
/* convert to res.charset() == utf8, then quote */
- set_item.copy(*pos, strlen(*pos), charset(), res.charset());
+ set_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors);
append_unescaped(&res, set_item.ptr(), set_item.length());
flag= 1;
}
@@ -5806,25 +5846,24 @@ bool Field_num::eq_def(Field *field)
void create_field::create_length_to_internal_length(void)
{
- switch (sql_type)
- {
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_STRING:
- length*= charset->mbmaxlen;
- pack_length= calc_pack_length(sql_type == FIELD_TYPE_VAR_STRING ?
- FIELD_TYPE_STRING : sql_type, length);
- break;
- case MYSQL_TYPE_ENUM:
- case MYSQL_TYPE_SET:
- length*= charset->mbmaxlen;
- break;
- default:
- /* do nothing */
- break;
+ switch (sql_type) {
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ length*= charset->mbmaxlen;
+ pack_length= calc_pack_length(sql_type == FIELD_TYPE_VAR_STRING ?
+ FIELD_TYPE_STRING : sql_type, length);
+ break;
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ length*= charset->mbmaxlen;
+ break;
+ default:
+ /* do nothing */
+ break;
}
}
@@ -5909,8 +5948,15 @@ Field *make_field(char *ptr, uint32 field_length,
if (f_is_alpha(pack_flag))
{
if (!f_is_packed(pack_flag))
- return new Field_string(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table, field_charset);
+ {
+ if (field_type == FIELD_TYPE_STRING ||
+ field_type == FIELD_TYPE_DECIMAL || // 3.23 or 4.0 string
+ field_type == FIELD_TYPE_VAR_STRING)
+ return new Field_string(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table,
+ field_charset);
+ return 0; // Error
+ }
uint pack_length=calc_pack_length((enum_field_types)
f_packtype(pack_flag),
@@ -6011,6 +6057,40 @@ Field *make_field(char *ptr, uint32 field_length,
}
+/*
+ Check if field_type is appropriate field type
+ to create field for tmp table using
+ item->tmp_table_field() method
+
+ SYNOPSIS
+ field_types_to_be_kept()
+ field_type - field type
+
+ NOTE
+ it is used in function get_holder_example_field()
+ from item.cc
+
+ RETURN
+ 1 - can use item->tmp_table_field() method
+ 0 - can not use item->tmp_table_field() method
+
+*/
+
+bool field_types_to_be_kept(enum_field_types field_type)
+{
+ switch (field_type)
+ {
+ case FIELD_TYPE_DATE:
+ case FIELD_TYPE_NEWDATE:
+ case FIELD_TYPE_TIME:
+ case FIELD_TYPE_DATETIME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
/* Create a field suitable for create of table */
create_field::create_field(Field *old_field,Field *orig_field)
@@ -6042,6 +6122,8 @@ create_field::create_field(Field *old_field,Field *orig_field)
}
length=(length+charset->mbmaxlen-1)/charset->mbmaxlen; // QQ: Probably not needed
break;
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
case FIELD_TYPE_STRING:
case FIELD_TYPE_VAR_STRING:
length=(length+charset->mbmaxlen-1)/charset->mbmaxlen;
diff --git a/sql/field.h b/sql/field.h
index 8887da1dc0f..b5d88504939 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -277,6 +277,7 @@ public:
virtual bool get_date(TIME *ltime,uint fuzzydate);
virtual bool get_time(TIME *ltime);
virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; }
+ virtual CHARSET_INFO *sort_charset(void) const { return charset(); }
virtual bool has_charset(void) const { return FALSE; }
virtual void set_charset(CHARSET_INFO *charset) { }
bool set_warning(const unsigned int level, const unsigned int code,
@@ -1152,6 +1153,8 @@ public:
bool optimize_range(uint idx, uint part) { return 0; }
bool eq_def(Field *field);
bool has_charset(void) const { return TRUE; }
+ /* enum and set are sorted as integers */
+ CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
field_cast_enum field_cast_type() { return FIELD_CAST_ENUM; }
};
@@ -1198,6 +1201,7 @@ public:
uint decimals,flags,pack_length;
Field::utype unireg_check;
TYPELIB *interval; // Which interval to use
+ List<String> interval_list;
CHARSET_INFO *charset;
Field::geometry_type geom_type;
Field *field; // For alter table
@@ -1264,6 +1268,7 @@ int set_field_to_null(Field *field);
int set_field_to_null_with_conversions(Field *field, bool no_conversions);
bool test_if_int(const char *str, int length, const char *int_end,
CHARSET_INFO *cs);
+bool field_types_to_be_kept(enum_field_types field_type);
/*
The following are for the interface with the .frm file
@@ -1274,10 +1279,10 @@ bool test_if_int(const char *str, int length, const char *int_end,
#define FIELDFLAG_NUMBER 2
#define FIELDFLAG_ZEROFILL 4
#define FIELDFLAG_PACK 120 // Bits used for packing
-#define FIELDFLAG_INTERVAL 256
-#define FIELDFLAG_BITFIELD 512 // mangled with dec!
-#define FIELDFLAG_BLOB 1024 // mangled with dec!
-#define FIELDFLAG_GEOM 2048
+#define FIELDFLAG_INTERVAL 256 // mangled with decimals!
+#define FIELDFLAG_BITFIELD 512 // mangled with decimals!
+#define FIELDFLAG_BLOB 1024 // mangled with decimals!
+#define FIELDFLAG_GEOM 2048 // mangled with decimals!
#define FIELDFLAG_LEFT_FULLSCREEN 8192
#define FIELDFLAG_RIGHT_FULLSCREEN 16384
@@ -1302,10 +1307,10 @@ bool test_if_int(const char *str, int length, const char *int_end,
#define f_decimals(x) ((uint8) (((x) >> FIELDFLAG_DEC_SHIFT) & FIELDFLAG_MAX_DEC))
#define f_is_alpha(x) (!f_is_num(x))
#define f_is_binary(x) ((x) & FIELDFLAG_BINARY) // 4.0- compatibility
-#define f_is_enum(x) ((x) & FIELDFLAG_INTERVAL)
-#define f_is_bitfield(x) ((x) & FIELDFLAG_BITFIELD)
+#define f_is_enum(x) (((x) & (FIELDFLAG_INTERVAL | FIELDFLAG_NUMBER)) == FIELDFLAG_INTERVAL)
+#define f_is_bitfield(x) (((x) & (FIELDFLAG_BITFIELD | FIELDFLAG_NUMBER)) == FIELDFLAG_BITFIELD)
#define f_is_blob(x) (((x) & (FIELDFLAG_BLOB | FIELDFLAG_NUMBER)) == FIELDFLAG_BLOB)
-#define f_is_geom(x) ((x) & FIELDFLAG_GEOM)
+#define f_is_geom(x) (((x) & (FIELDFLAG_GEOM | FIELDFLAG_NUMBER)) == FIELDFLAG_GEOM)
#define f_is_equ(x) ((x) & (1+2+FIELDFLAG_PACK+31*256))
#define f_settype(x) (((int) x) << FIELDFLAG_PACK_SHIFT)
#define f_maybe_null(x) (x & FIELDFLAG_MAYBE_NULL)
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index 890687fc925..8b362bbf807 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -473,7 +473,7 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*)
{
if (to->flags & BLOB_FLAG)
{
- if (!(from->flags & BLOB_FLAG))
+ if (!(from->flags & BLOB_FLAG) || from->charset() != to->charset())
return do_conv_blob;
if (from_length != to_length ||
to->table->db_low_byte_first != from->table->db_low_byte_first)
@@ -559,6 +559,7 @@ void field_conv(Field *to,Field *from)
if (to->real_type() == from->real_type())
{
if (to->pack_length() == from->pack_length() &&
+ !(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) &&
to->real_type() != FIELD_TYPE_ENUM &&
to->real_type() != FIELD_TYPE_SET &&
from->charset() == to->charset() &&
diff --git a/sql/filesort.cc b/sql/filesort.cc
index bd0de022fd4..76ce9ac4ce2 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -622,10 +622,7 @@ static void make_sortkey(register SORTPARAM *param,
}
uint tmp_length=my_strnxfrm(cs,to,sort_field->length,
(unsigned char *) from, length);
- if (tmp_length < sort_field->length)
- cs->cset->fill(cs, (char*) to+tmp_length,
- sort_field->length-tmp_length,
- fill_char);
+ DBUG_ASSERT(tmp_length == sort_field->length);
}
else
{
@@ -806,11 +803,16 @@ int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
if (flush_io_cache(to_file))
break; /* purecov: inspected */
temp=from_file; from_file=to_file; to_file=temp;
+ setup_io_cache(from_file);
+ setup_io_cache(to_file);
*maxbuffer= (uint) (lastbuff-buffpek)-1;
}
close_cached_file(to_file); // This holds old result
if (to_file == t_file)
+ {
*t_file=t_file2; // Copy result file
+ setup_io_cache(t_file);
+ }
DBUG_RETURN(*maxbuffer >= MERGEBUFF2); /* Return 1 if interrupted */
} /* merge_many_buff */
@@ -1125,7 +1127,7 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset)
else
{
sortorder->length=sortorder->field->pack_length();
- if (use_strnxfrm((cs=sortorder->field->charset())))
+ if (use_strnxfrm((cs=sortorder->field->sort_charset())))
{
sortorder->need_strxnfrm= 1;
*multi_byte_charset= 1;
diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc
index 09b3e340d1f..e33cd3fca1b 100644
--- a/sql/ha_berkeley.cc
+++ b/sql/ha_berkeley.cc
@@ -165,11 +165,13 @@ bool berkeley_init(void)
{
db_env->close(db_env,0); /* purecov: inspected */
db_env=0; /* purecov: inspected */
+ goto err;
}
(void) hash_init(&bdb_open_tables,system_charset_info,32,0,0,
(hash_get_key) bdb_get_key,0,0);
pthread_mutex_init(&bdb_mutex,MY_MUTEX_INIT_FAST);
+err:
DBUG_RETURN(db_env == 0);
}
@@ -234,13 +236,13 @@ int berkeley_show_logs(Protocol *protocol)
{
char **all_logs, **free_logs, **a, **f;
int error=1;
- MEM_ROOT show_logs_root;
- MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
+ MEM_ROOT **root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,THR_MALLOC);
+ MEM_ROOT show_logs_root, *old_mem_root= *root_ptr;
DBUG_ENTER("berkeley_show_logs");
init_sql_alloc(&show_logs_root, BDB_LOG_ALLOC_BLOCK_SIZE,
BDB_LOG_ALLOC_BLOCK_SIZE);
- my_pthread_setspecific_ptr(THR_MALLOC,&show_logs_root);
+ *root_ptr= &show_logs_root;
if ((error= db_env->log_archive(db_env, &all_logs,
DB_ARCH_ABS | DB_ARCH_LOG)) ||
@@ -277,15 +279,17 @@ int berkeley_show_logs(Protocol *protocol)
}
err:
free_root(&show_logs_root,MYF(0));
- my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ *root_ptr= old_mem_root;
DBUG_RETURN(error);
}
+
static void berkeley_print_error(const char *db_errpfx, char *buffer)
{
sql_print_error("%s: %s",db_errpfx,buffer); /* purecov: tested */
}
+
static void berkeley_noticecall(DB_ENV *db_env, db_notices notice)
{
switch (notice)
diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc
index 19b15c6fbcc..3c2249ce281 100644
--- a/sql/ha_heap.cc
+++ b/sql/ha_heap.cc
@@ -30,6 +30,18 @@
const char **ha_heap::bas_ext() const
{ static const char *ext[1]= { NullS }; return ext; }
+/*
+ Hash index statistics is updated (copied from HP_KEYDEF::hash_buckets to
+ rec_per_key) after 1/HEAP_STATS_UPDATE_THRESHOLD fraction of table records
+ have been inserted/updated/deleted. delete_all_rows() and table flush cause
+ immediate update.
+
+ NOTE
+ hash index statistics must be updated when number of table records changes
+ from 0 to non-zero value and vice versa. Otherwise records_in_range may
+ erroneously return 0 and 'range' may miss records.
+*/
+#define HEAP_STATS_UPDATE_THRESHOLD 10
int ha_heap::open(const char *name, int mode, uint test_if_locked)
{
@@ -48,6 +60,7 @@ int ha_heap::open(const char *name, int mode, uint test_if_locked)
{
/* Initialize variables for the opened table */
set_keys_for_scanning();
+ update_key_stats();
}
return (file ? 0 : 1);
}
@@ -84,28 +97,60 @@ void ha_heap::set_keys_for_scanning(void)
}
}
+void ha_heap::update_key_stats()
+{
+ for (uint i= 0; i < table->keys; i++)
+ {
+ KEY *key=table->key_info+i;
+ if (!key->rec_per_key)
+ continue;
+ if (key->algorithm != HA_KEY_ALG_BTREE)
+ {
+ ha_rows hash_buckets= file->s->keydef[i].hash_buckets;
+ key->rec_per_key[key->key_parts-1]=
+ hash_buckets ? file->s->records/hash_buckets : 0;
+ }
+ }
+ records_changed= 0;
+}
+
int ha_heap::write_row(byte * buf)
{
+ int res;
statistic_increment(ha_write_count,&LOCK_status);
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
table->timestamp_field->set_time();
if (table->next_number_field && buf == table->record[0])
update_auto_increment();
- return heap_write(file,buf);
+ res= heap_write(file,buf);
+ if (!res && ++records_changed*HEAP_STATS_UPDATE_THRESHOLD >
+ file->s->records)
+ update_key_stats();
+ return res;
}
int ha_heap::update_row(const byte * old_data, byte * new_data)
{
+ int res;
statistic_increment(ha_update_count,&LOCK_status);
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
table->timestamp_field->set_time();
- return heap_update(file,old_data,new_data);
+ res= heap_update(file,old_data,new_data);
+ if (!res && ++records_changed*HEAP_STATS_UPDATE_THRESHOLD >
+ file->s->records)
+ update_key_stats();
+ return res;
}
int ha_heap::delete_row(const byte * buf)
{
+ int res;
statistic_increment(ha_delete_count,&LOCK_status);
- return heap_delete(file,buf);
+ res= heap_delete(file,buf);
+ if (!res && table->tmp_table == NO_TMP_TABLE &&
+ ++records_changed*HEAP_STATS_UPDATE_THRESHOLD > file->s->records)
+ update_key_stats();
+ return res;
}
int ha_heap::index_read(byte * buf, const byte * key, uint key_len,
@@ -227,6 +272,8 @@ int ha_heap::extra(enum ha_extra_function operation)
int ha_heap::delete_all_rows()
{
heap_clear(file);
+ if (table->tmp_table == NO_TMP_TABLE)
+ update_key_stats();
return 0;
}
@@ -384,7 +431,8 @@ ha_rows ha_heap::records_in_range(uint inx, key_range *min_key,
min_key->flag != HA_READ_KEY_EXACT ||
max_key->flag != HA_READ_AFTER_KEY)
return HA_POS_ERROR; // Can only use exact keys
- return 10; // Good guess
+ else
+ return key->rec_per_key[key->key_parts-1];
}
@@ -413,12 +461,24 @@ int ha_heap::create(const char *name, TABLE *table_arg,
KEY_PART_INFO *key_part= pos->key_part;
KEY_PART_INFO *key_part_end= key_part + pos->key_parts;
- mem_per_row+= (pos->key_length + (sizeof(char*) * 2));
-
keydef[key].keysegs= (uint) pos->key_parts;
keydef[key].flag= (pos->flags & (HA_NOSAME | HA_NULL_ARE_EQUAL));
keydef[key].seg= seg;
- keydef[key].algorithm= ((pos->algorithm == HA_KEY_ALG_UNDEF) ?
+
+ switch (pos->algorithm) {
+ case HA_KEY_ALG_UNDEF:
+ case HA_KEY_ALG_HASH:
+ keydef[key].algorithm= HA_KEY_ALG_HASH;
+ mem_per_row+= sizeof(char*) * 2; // = sizeof(HASH_INFO)
+ break;
+ case HA_KEY_ALG_BTREE:
+ keydef[key].algorithm= HA_KEY_ALG_BTREE;
+ mem_per_row+=sizeof(TREE_ELEMENT)+pos->key_length+sizeof(char*);
+ break;
+ default:
+ DBUG_ASSERT(0); // cannot happen
+ }
+ keydef[key].algorithm= ((pos->algorithm == HA_KEY_ALG_UNDEF) ?
HA_KEY_ALG_HASH : pos->algorithm);
for (; key_part != key_part_end; key_part++, seg++)
@@ -454,17 +514,17 @@ int ha_heap::create(const char *name, TABLE *table_arg,
}
}
mem_per_row+= MY_ALIGN(table_arg->reclength + 1, sizeof(char*));
- max_rows = (ha_rows) (current_thd->variables.max_heap_table_size /
- mem_per_row);
HP_CREATE_INFO hp_create_info;
hp_create_info.auto_key= auto_key;
hp_create_info.auto_key_type= auto_key_type;
hp_create_info.auto_increment= (create_info->auto_increment_value ?
create_info->auto_increment_value - 1 : 0);
+ hp_create_info.max_table_size=current_thd->variables.max_heap_table_size;
+ max_rows = (ha_rows) (hp_create_info.max_table_size / mem_per_row);
error= heap_create(fn_format(buff,name,"","",4+2),
table_arg->keys,keydef, table_arg->reclength,
(ulong) ((table_arg->max_rows < max_rows &&
- table_arg->max_rows) ?
+ table_arg->max_rows) ?
table_arg->max_rows : max_rows),
(ulong) table_arg->min_rows, &hp_create_info);
my_free((gptr) keydef, MYF(0));
diff --git a/sql/ha_heap.h b/sql/ha_heap.h
index 9ca6b9b76b6..f36e9f31c55 100644
--- a/sql/ha_heap.h
+++ b/sql/ha_heap.h
@@ -27,9 +27,10 @@ class ha_heap: public handler
{
HP_INFO *file;
key_map btree_keys;
-
- public:
- ha_heap(TABLE *table): handler(table), file(0) {}
+ /* number of records changed since last statistics update */
+ uint records_changed;
+public:
+ ha_heap(TABLE *table): handler(table), file(0), records_changed(0) {}
~ha_heap() {}
const char *table_type() const { return "HEAP"; }
const char *index_type(uint inx)
@@ -91,5 +92,6 @@ class ha_heap: public handler
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type);
-
+private:
+ void update_key_stats();
};
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 8d9ecb95fc0..4f32bb08e9a 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -278,15 +278,15 @@ convert_error_code_to_mysql(
} else if (error == (int) DB_LOCK_WAIT_TIMEOUT) {
- /* Since we rolled back the whole transaction, we must
- tell it also to MySQL so that MySQL knows to empty the
- cached binlog for this transaction */
+ /* Since we rolled back the whole transaction, we must
+ tell it also to MySQL so that MySQL knows to empty the
+ cached binlog for this transaction */
- if (thd) {
- ha_rollback(thd);
- }
+ if (thd) {
+ ha_rollback(thd);
+ }
- return(HA_ERR_LOCK_WAIT_TIMEOUT);
+ return(HA_ERR_LOCK_WAIT_TIMEOUT);
} else if (error == (int) DB_NO_REFERENCED_ROW) {
@@ -331,6 +331,9 @@ convert_error_code_to_mysql(
} else if (error == (int) DB_NO_SAVEPOINT) {
return(HA_ERR_NO_SAVEPOINT);
+ } else if (error == (int) DB_LOCK_TABLE_FULL) {
+
+ return(HA_ERR_LOCK_TABLE_FULL);
} else {
return(-1); // Unknown error
}
@@ -469,7 +472,7 @@ innobase_mysql_tmpfile(void)
{
char filename[FN_REFLEN];
int fd2 = -1;
- File fd = create_temp_file(filename, NullS, "ib",
+ File fd = create_temp_file(filename, mysql_tmpdir, "ib",
#ifdef __WIN__
O_BINARY | O_TRUNC | O_SEQUENTIAL |
O_TEMPORARY | O_SHORT_LIVED |
@@ -1505,17 +1508,14 @@ innobase_close_connection(
*****************************************************************************/
/********************************************************************
-This function is not relevant since we store the tables and indexes
-into our own tablespace, not as files, whose extension this function would
-give. */
+Gives the file extension of an InnoDB single-table tablespace. */
const char**
ha_innobase::bas_ext() const
/*========================*/
- /* out: file extension strings, currently not
- used */
+ /* out: file extension string */
{
- static const char* ext[] = {".InnoDB", NullS};
+ static const char* ext[] = {".ibd", NullS};
return(ext);
}
@@ -2248,6 +2248,8 @@ build_template(
templ->mysql_col_len = (ulint) field->pack_length();
templ->type = get_innobase_type_from_mysql_type(field);
+ templ->charset = dtype_get_charset_coll_noninline(
+ index->table->cols[i].type.prtype);
templ->is_unsigned = (ulint) (field->flags & UNSIGNED_FLAG);
if (templ->type == DATA_BLOB) {
@@ -2314,6 +2316,72 @@ ha_innobase::write_row(
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
table->timestamp_field->set_time();
+ if (user_thd->lex->sql_command == SQLCOM_ALTER_TABLE
+ && num_write_row >= 10000) {
+ /* ALTER TABLE is COMMITted at every 10000 copied rows.
+ The IX table lock for the original table has to be re-issued.
+ As this method will be called on a temporary table where the
+ contents of the original table is being copied to, it is
+ a bit tricky to determine the source table. The cursor
+ position in the source table need not be adjusted after the
+ intermediate COMMIT, since writes by other transactions are
+ being blocked by a MySQL table lock TL_WRITE_ALLOW_READ. */
+
+ dict_table_t* src_table;
+ ibool mode;
+
+ num_write_row = 0;
+
+ /* Commit the transaction. This will release the table
+ locks, so they have to be acquired again. */
+
+ /* Altering an InnoDB table */
+ /* Get the source table. */
+ src_table = lock_get_src_table(
+ prebuilt->trx, prebuilt->table, &mode);
+ if (!src_table) {
+ no_commit:
+ /* Unknown situation: do not commit */
+ /*
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+ " InnoDB error: ALTER TABLE is holding lock"
+ " on %lu tables!\n",
+ prebuilt->trx->mysql_n_tables_locked);
+ */
+ ;
+ } else if (src_table == prebuilt->table) {
+ /* Source table is not in InnoDB format:
+ no need to re-acquire locks on it. */
+
+ /* Altering to InnoDB format */
+ innobase_commit(user_thd, prebuilt->trx);
+ /* Note that this transaction is still active. */
+ user_thd->transaction.all.innodb_active_trans = 1;
+ /* We will need an IX lock on the destination table. */
+ prebuilt->sql_stat_start = TRUE;
+ } else {
+ /* Ensure that there are no other table locks than
+ LOCK_IX and LOCK_AUTO_INC on the destination table. */
+ if (!lock_is_table_exclusive(prebuilt->table,
+ prebuilt->trx)) {
+ goto no_commit;
+ }
+
+ /* Commit the transaction. This will release the table
+ locks, so they have to be acquired again. */
+ innobase_commit(user_thd, prebuilt->trx);
+ /* Note that this transaction is still active. */
+ user_thd->transaction.all.innodb_active_trans = 1;
+ /* Re-acquire the table lock on the source table. */
+ row_lock_table_for_mysql(prebuilt, src_table, mode);
+ /* We will need an IX lock on the destination table. */
+ prebuilt->sql_stat_start = TRUE;
+ }
+ }
+
+ num_write_row++;
+
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
@@ -2362,8 +2430,9 @@ ha_innobase::write_row(
same SQL statement! */
if (auto_inc == 0 && user_thd->next_insert_id != 0) {
- auto_inc = user_thd->next_insert_id;
- auto_inc_counter_for_this_stat = auto_inc;
+
+ auto_inc_counter_for_this_stat
+ = user_thd->next_insert_id;
}
if (auto_inc == 0 && auto_inc_counter_for_this_stat) {
@@ -2371,14 +2440,14 @@ ha_innobase::write_row(
this SQL statement with SET INSERT_ID. We must
assign sequential values from the counter. */
- auto_inc_counter_for_this_stat++;
- incremented_auto_inc_for_stat = TRUE;
-
auto_inc = auto_inc_counter_for_this_stat;
/* We give MySQL a new value to place in the
auto-inc column */
user_thd->next_insert_id = auto_inc;
+
+ auto_inc_counter_for_this_stat++;
+ incremented_auto_inc_for_stat = TRUE;
}
if (auto_inc != 0) {
@@ -2464,7 +2533,7 @@ ha_innobase::write_row(
NOTE that a REPLACE command and LOAD DATA INFILE REPLACE
handles a duplicate key error
itself, and we must not decrement the autoinc counter
- if we are performing a those statements.
+ if we are performing those statements.
NOTE 2: if there was an error, for example a deadlock,
which caused InnoDB to roll back the whole transaction
already in the call of row_insert_for_mysql(), we may no
@@ -3887,11 +3956,9 @@ ha_innobase::discard_or_import_tablespace(
err = row_import_tablespace_for_mysql(dict_table->name, trx);
}
- if (err == DB_SUCCESS) {
- DBUG_RETURN(0);
- }
+ err = convert_error_code_to_mysql(err, NULL);
- DBUG_RETURN(-1);
+ DBUG_RETURN(err);
}
/*********************************************************************
@@ -4605,12 +4672,12 @@ ha_innobase::update_table_comment(
dict_print_info_on_foreign_keys(FALSE, file,
prebuilt->trx, prebuilt->table);
flen = ftell(file);
- if(length + flen + 3 > 64000) {
+ if (flen < 0) {
+ flen = 0;
+ } else if (length + flen + 3 > 64000) {
flen = 64000 - 3 - length;
}
- ut_ad(flen > 0);
-
/* allocate buffer for the full string, and
read the contents of the temporary file */
@@ -4674,12 +4741,12 @@ ha_innobase::get_foreign_key_create_info(void)
prebuilt->trx->op_info = (char*)"";
flen = ftell(file);
- if(flen > 64000 - 1) {
+ if (flen < 0) {
+ flen = 0;
+ } else if(flen > 64000 - 1) {
flen = 64000 - 1;
}
- ut_ad(flen >= 0);
-
/* allocate buffer for the string, and
read the contents of the temporary file */
@@ -4922,19 +4989,6 @@ ha_innobase::external_lock(
update_thd(thd);
- if (prebuilt->table->ibd_file_missing && !current_thd->tablespace_op) {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB error:\n"
-"MySQL is trying to use a table handle but the .ibd file for\n"
-"table %s does not exist.\n"
-"Have you deleted the .ibd file from the database directory under\n"
-"the MySQL datadir, or have you used DISCARD TABLESPACE?\n"
-"Look from section 15.1 of http://www.innodb.com/ibman.html\n"
-"how you can resolve the problem.\n",
- prebuilt->table->name);
- DBUG_RETURN(HA_ERR_CRASHED);
- }
-
trx = prebuilt->trx;
prebuilt->sql_stat_start = TRUE;
@@ -4982,11 +5036,21 @@ ha_innobase::external_lock(
prebuilt->select_lock_type = LOCK_S;
}
+ /* Starting from 4.1.9, no InnoDB table lock is taken in LOCK
+ TABLES if AUTOCOMMIT=1. It does not make much sense to acquire
+ an InnoDB table lock if it is released immediately at the end
+ of LOCK TABLES, and InnoDB's table locks in that case cause
+ VERY easily deadlocks. */
+
if (prebuilt->select_lock_type != LOCK_NONE) {
+
if (thd->in_lock_tables &&
- thd->variables.innodb_table_locks) {
+ thd->variables.innodb_table_locks &&
+ (thd->options & OPTION_NOT_AUTOCOMMIT)) {
+
ulint error;
- error = row_lock_table_for_mysql(prebuilt);
+ error = row_lock_table_for_mysql(prebuilt,
+ NULL, LOCK_TABLE_EXP);
if (error != DB_SUCCESS) {
error = convert_error_code_to_mysql(
@@ -5078,12 +5142,12 @@ innodb_show_status(
srv_printf_innodb_monitor(srv_monitor_file);
flen = ftell(srv_monitor_file);
os_file_set_eof(srv_monitor_file);
- if(flen > 64000 - 1) {
+ if (flen < 0) {
+ flen = 0;
+ } else if (flen > 64000 - 1) {
flen = 64000 - 1;
}
- ut_ad(flen > 0);
-
/* allocate buffer for the string, and
read the contents of the temporary file */
@@ -5203,7 +5267,9 @@ ha_innobase::store_lock(
if ((lock_type == TL_READ && thd->in_lock_tables) ||
(lock_type == TL_READ_HIGH_PRIORITY && thd->in_lock_tables) ||
lock_type == TL_READ_WITH_SHARED_LOCKS ||
- lock_type == TL_READ_NO_INSERT) {
+ lock_type == TL_READ_NO_INSERT ||
+ thd->lex->sql_command != SQLCOM_SELECT) {
+
/* The OR cases above are in this order:
1) MySQL is doing LOCK TABLES ... READ LOCAL, or
2) (we do not know when TL_READ_HIGH_PRIORITY is used), or
@@ -5211,10 +5277,32 @@ ha_innobase::store_lock(
4) we are doing a complex SQL statement like
INSERT INTO ... SELECT ... and the logical logging (MySQL
binlog) requires the use of a locking read, or
- MySQL is doing LOCK TABLES ... READ. */
+ MySQL is doing LOCK TABLES ... READ.
+ 5) we let InnoDB do locking reads for all SQL statements that
+ are not simple SELECTs; note that select_lock_type in this
+ case may get strengthened in ::external_lock() to LOCK_X. */
+
+ if (srv_locks_unsafe_for_binlog &&
+ prebuilt->trx->isolation_level != TRX_ISO_SERIALIZABLE &&
+ (lock_type == TL_READ || lock_type == TL_READ_NO_INSERT) &&
+ thd->lex->sql_command != SQLCOM_SELECT &&
+ thd->lex->sql_command != SQLCOM_UPDATE_MULTI &&
+ thd->lex->sql_command != SQLCOM_DELETE_MULTI ) {
+
+ /* In case we have innobase_locks_unsafe_for_binlog
+ option set and isolation level of the transaction
+ is not set to serializable and MySQL is doing
+ INSERT INTO...SELECT without FOR UPDATE or IN
+ SHARE MODE we use consistent read for select.
+ Similarly, in case of DELETE...SELECT and
+ UPDATE...SELECT when these are not multi table.*/
- prebuilt->select_lock_type = LOCK_S;
- prebuilt->stored_select_lock_type = LOCK_S;
+ prebuilt->select_lock_type = LOCK_NONE;
+ prebuilt->stored_select_lock_type = LOCK_NONE;
+ } else {
+ prebuilt->select_lock_type = LOCK_S;
+ prebuilt->stored_select_lock_type = LOCK_S;
+ }
} else if (lock_type != TL_IGNORE) {
diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h
index 7f7b000a100..e76a966c6b9 100644
--- a/sql/ha_innodb.h
+++ b/sql/ha_innodb.h
@@ -64,6 +64,7 @@ class ha_innobase: public handler
uint last_match_mode;/* match mode of the latest search:
ROW_SEL_EXACT, ROW_SEL_EXACT_PREFIX,
or undefined */
+ uint num_write_row; /* number of write_row() calls */
longlong auto_inc_counter_for_this_stat;
ulong max_supported_row_length(const byte *buf);
@@ -85,7 +86,8 @@ class ha_innobase: public handler
HA_PRIMARY_KEY_IN_READ_INDEX |
HA_TABLE_SCAN_ON_INDEX),
last_dup_key((uint) -1),
- start_of_scan(0)
+ start_of_scan(0),
+ num_write_row(0)
{
}
~ha_innobase() {}
@@ -198,6 +200,7 @@ extern my_bool innobase_very_fast_shutdown; /* set this to 1 just before
is equivalent to a 'crash' */
extern "C" {
extern ulong srv_max_buf_pool_modified_pct;
+extern ulong srv_max_purge_lag;
extern ulong srv_auto_extend_increment;
extern ulong srv_max_purge_lag;
}
diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h
index 6fde84d6f6f..1e6cf2f4ada 100644
--- a/sql/ha_myisam.h
+++ b/sql/ha_myisam.h
@@ -81,7 +81,6 @@ class ha_myisam: public handler
int index_first(byte * buf);
int index_last(byte * buf);
int index_next_same(byte *buf, const byte *key, uint keylen);
- int index_end() { ft_handler=NULL; return 0; }
int ft_init()
{
if (!ft_handler)
@@ -89,8 +88,12 @@ class ha_myisam: public handler
ft_handler->please->reinit_search(ft_handler);
return 0;
}
- FT_INFO *ft_init_ext(uint flags, uint inx,const byte *key, uint keylen)
- { return ft_init_search(flags,file,inx,(byte*) key,keylen, table->record[0]); }
+ FT_INFO *ft_init_ext(uint flags, uint inx,String *key)
+ {
+ return ft_init_search(flags,file,inx,
+ (byte *)key->ptr(), key->length(), key->charset(),
+ table->record[0]);
+ }
int ft_read(byte *buf);
int rnd_init(bool scan);
int rnd_next(byte *buf);
diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc
index bf4c2a36ffd..bf47b4625e0 100644
--- a/sql/ha_myisammrg.cc
+++ b/sql/ha_myisammrg.cc
@@ -35,6 +35,17 @@
const char **ha_myisammrg::bas_ext() const
{ static const char *ext[]= { ".MRG", NullS }; return ext; }
+const char *ha_myisammrg::index_type(uint key_number)
+{
+ return ((table->key_info[key_number].flags & HA_FULLTEXT) ?
+ "FULLTEXT" :
+ (table->key_info[key_number].flags & HA_SPATIAL) ?
+ "SPATIAL" :
+ (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ?
+ "RTREE" :
+ "BTREE");
+}
+
int ha_myisammrg::open(const char *name, int mode, uint test_if_locked)
{
diff --git a/sql/ha_myisammrg.h b/sql/ha_myisammrg.h
index 264c580220c..6058c32c805 100644
--- a/sql/ha_myisammrg.h
+++ b/sql/ha_myisammrg.h
@@ -32,6 +32,7 @@ class ha_myisammrg: public handler
~ha_myisammrg() {}
const char *table_type() const { return "MRG_MyISAM"; }
const char **bas_ext() const;
+ const char *index_type(uint key_number);
ulong table_flags() const
{
return (HA_REC_NOT_IN_SEQ | HA_AUTO_PART_KEY | HA_READ_RND_SAME |
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index 044cb85b913..3e2fc5b2855 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -32,6 +32,10 @@
#include <ndbapi/NdbApi.hpp>
#include <ndbapi/NdbScanFilter.hpp>
+// options from from mysqld.cc
+extern my_bool opt_ndb_optimized_node_selection;
+extern const char *opt_ndbcluster_connectstring;
+
// Default value for parallelism
static const int parallelism= 240;
@@ -39,12 +43,6 @@ static const int parallelism= 240;
// createable against NDB from this handler
static const int max_transactions= 256;
-// Default value for prefetch of autoincrement values
-static const ha_rows autoincrement_prefetch= 32;
-
-// connectstring to cluster if given by mysqld
-const char *ndbcluster_connectstring= 0;
-
static const char *ha_ndb_ext=".ndb";
#define NDB_HIDDEN_PRIMARY_KEY_LENGTH 8
@@ -103,51 +101,52 @@ struct err_code_mapping
{
int ndb_err;
int my_err;
+ int show_warning;
};
static const err_code_mapping err_map[]=
{
- { 626, HA_ERR_KEY_NOT_FOUND },
- { 630, HA_ERR_FOUND_DUPP_KEY },
- { 893, HA_ERR_FOUND_DUPP_UNIQUE },
- { 721, HA_ERR_TABLE_EXIST },
- { 4244, HA_ERR_TABLE_EXIST },
-
- { 709, HA_ERR_NO_SUCH_TABLE },
- { 284, HA_ERR_NO_SUCH_TABLE },
-
- { 266, HA_ERR_LOCK_WAIT_TIMEOUT },
- { 274, HA_ERR_LOCK_WAIT_TIMEOUT },
- { 296, HA_ERR_LOCK_WAIT_TIMEOUT },
- { 297, HA_ERR_LOCK_WAIT_TIMEOUT },
- { 237, HA_ERR_LOCK_WAIT_TIMEOUT },
-
- { 623, HA_ERR_RECORD_FILE_FULL },
- { 624, HA_ERR_RECORD_FILE_FULL },
- { 625, HA_ERR_RECORD_FILE_FULL },
- { 826, HA_ERR_RECORD_FILE_FULL },
- { 827, HA_ERR_RECORD_FILE_FULL },
- { 832, HA_ERR_RECORD_FILE_FULL },
-
- { 0, 1 },
-
- { -1, -1 }
+ { 626, HA_ERR_KEY_NOT_FOUND, 0 },
+ { 630, HA_ERR_FOUND_DUPP_KEY, 0 },
+ { 893, HA_ERR_FOUND_DUPP_KEY, 0 },
+ { 721, HA_ERR_TABLE_EXIST, 1 },
+ { 4244, HA_ERR_TABLE_EXIST, 1 },
+
+ { 709, HA_ERR_NO_SUCH_TABLE, 1 },
+ { 284, HA_ERR_NO_SUCH_TABLE, 1 },
+
+ { 266, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
+ { 274, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
+ { 296, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
+ { 297, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
+ { 237, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
+
+ { 623, HA_ERR_RECORD_FILE_FULL, 1 },
+ { 624, HA_ERR_RECORD_FILE_FULL, 1 },
+ { 625, HA_ERR_RECORD_FILE_FULL, 1 },
+ { 826, HA_ERR_RECORD_FILE_FULL, 1 },
+ { 827, HA_ERR_RECORD_FILE_FULL, 1 },
+ { 832, HA_ERR_RECORD_FILE_FULL, 1 },
+
+ { 0, 1, 0 },
+
+ { -1, -1, 1 }
};
static int ndb_to_mysql_error(const NdbError *err)
{
uint i;
- for (i=0 ; err_map[i].ndb_err != err->code ; i++)
+ for (i=0; err_map[i].ndb_err != err->code && err_map[i].my_err != -1; i++);
+ if (err_map[i].show_warning)
{
- if (err_map[i].my_err == -1){
- // Push the NDB error message as warning
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
- ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
- err->code, err->message, "NDB");
- return err->code;
- }
+ // Push the NDB error message as warning
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ err->code, err->message, "NDB");
}
+ if (err_map[i].my_err == -1)
+ return err->code;
return err_map[i].my_err;
}
@@ -161,7 +160,7 @@ int execute_no_commit(ha_ndbcluster *h, NdbConnection *trans)
if (m_batch_execute)
return 0;
#endif
- return trans->execute(NoCommit,AbortOnError,1);
+ return trans->execute(NoCommit,AbortOnError,h->m_force_send);
}
inline
@@ -172,7 +171,18 @@ int execute_commit(ha_ndbcluster *h, NdbConnection *trans)
if (m_batch_execute)
return 0;
#endif
- return trans->execute(Commit,AbortOnError,1);
+ return trans->execute(Commit,AbortOnError,h->m_force_send);
+}
+
+inline
+int execute_commit(THD *thd, NdbConnection *trans)
+{
+ int m_batch_execute= 0;
+#ifdef NOT_USED
+ if (m_batch_execute)
+ return 0;
+#endif
+ return trans->execute(Commit,AbortOnError,thd->variables.ndb_force_send);
}
inline
@@ -183,7 +193,7 @@ int execute_no_commit_ie(ha_ndbcluster *h, NdbConnection *trans)
if (m_batch_execute)
return 0;
#endif
- return trans->execute(NoCommit,IgnoreError,1);
+ return trans->execute(NoCommit, AO_IgnoreError,h->m_force_send);
}
/*
@@ -202,6 +212,13 @@ Thd_ndb::~Thd_ndb()
{
if (ndb)
delete ndb;
+ ndb= 0;
+}
+
+inline
+Ndb *ha_ndbcluster::get_ndb()
+{
+ return ((Thd_ndb*)current_thd->transaction.thd_ndb)->ndb;
}
/*
@@ -226,6 +243,8 @@ void ha_ndbcluster::set_rec_per_key()
void ha_ndbcluster::records_update()
{
+ if (m_ha_not_exact_count)
+ return;
DBUG_ENTER("ha_ndbcluster::records_update");
struct Ndb_table_local_info *info= (struct Ndb_table_local_info *)m_table_info;
DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
@@ -233,8 +252,9 @@ void ha_ndbcluster::records_update()
info->no_uncommitted_rows_count));
// if (info->records == ~(ha_rows)0)
{
+ Ndb *ndb= get_ndb();
Uint64 rows;
- if(ndb_get_table_statistics(m_ndb, m_tabname, &rows, 0) == 0){
+ if(ndb_get_table_statistics(ndb, m_tabname, &rows, 0) == 0){
info->records= rows;
}
}
@@ -249,6 +269,8 @@ void ha_ndbcluster::records_update()
void ha_ndbcluster::no_uncommitted_rows_execute_failure()
{
+ if (m_ha_not_exact_count)
+ return;
DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_execute_failure");
THD *thd= current_thd;
((Thd_ndb*)(thd->transaction.thd_ndb))->error= 1;
@@ -257,6 +279,8 @@ void ha_ndbcluster::no_uncommitted_rows_execute_failure()
void ha_ndbcluster::no_uncommitted_rows_init(THD *thd)
{
+ if (m_ha_not_exact_count)
+ return;
DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_init");
struct Ndb_table_local_info *info= (struct Ndb_table_local_info *)m_table_info;
Thd_ndb *thd_ndb= (Thd_ndb *)thd->transaction.thd_ndb;
@@ -274,6 +298,8 @@ void ha_ndbcluster::no_uncommitted_rows_init(THD *thd)
void ha_ndbcluster::no_uncommitted_rows_update(int c)
{
+ if (m_ha_not_exact_count)
+ return;
DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_update");
struct Ndb_table_local_info *info=
(struct Ndb_table_local_info *)m_table_info;
@@ -286,6 +312,8 @@ void ha_ndbcluster::no_uncommitted_rows_update(int c)
void ha_ndbcluster::no_uncommitted_rows_reset(THD *thd)
{
+ if (m_ha_not_exact_count)
+ return;
DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_reset");
((Thd_ndb*)(thd->transaction.thd_ndb))->count++;
((Thd_ndb*)(thd->transaction.thd_ndb))->error= 0;
@@ -311,7 +339,8 @@ int ha_ndbcluster::ndb_err(NdbConnection *trans)
switch (err.classification) {
case NdbError::SchemaError:
{
- NDBDICT *dict= m_ndb->getDictionary();
+ Ndb *ndb= get_ndb();
+ NDBDICT *dict= ndb->getDictionary();
DBUG_PRINT("info", ("invalidateTable %s", m_tabname));
dict->invalidateTable(m_tabname);
table->version=0L; /* Free when thread is ready */
@@ -324,7 +353,7 @@ int ha_ndbcluster::ndb_err(NdbConnection *trans)
DBUG_PRINT("info", ("transformed ndbcluster error %d to mysql error %d",
err.code, res));
if (res == HA_ERR_FOUND_DUPP_KEY)
- dupkey= table->primary_key;
+ m_dupkey= table->primary_key;
DBUG_RETURN(res);
}
@@ -341,7 +370,7 @@ bool ha_ndbcluster::get_error_message(int error,
DBUG_ENTER("ha_ndbcluster::get_error_message");
DBUG_PRINT("enter", ("error: %d", error));
- Ndb *ndb= ((Thd_ndb*)current_thd->transaction.thd_ndb)->ndb;
+ Ndb *ndb= get_ndb();
if (!ndb)
DBUG_RETURN(FALSE);
@@ -551,10 +580,10 @@ int ha_ndbcluster::get_ndb_blobs_value(NdbBlob *last_ndb_blob)
blob_size+= 8 - blob_size % 8;
if (loop == 1)
{
- char *buf= blobs_buffer + offset;
+ char *buf= m_blobs_buffer + offset;
uint32 len= 0xffffffff; // Max uint32
DBUG_PRINT("value", ("read blob ptr=%x len=%u",
- (uint)buf, (uint)blob_len));
+ (UintPtr)buf, (uint)blob_len));
if (ndb_blob->readData(buf, len) != 0)
DBUG_RETURN(-1);
DBUG_ASSERT(len == blob_len);
@@ -563,15 +592,15 @@ int ha_ndbcluster::get_ndb_blobs_value(NdbBlob *last_ndb_blob)
offset+= blob_size;
}
}
- if (loop == 0 && offset > blobs_buffer_size)
+ if (loop == 0 && offset > m_blobs_buffer_size)
{
- my_free(blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
- blobs_buffer_size= 0;
+ my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
+ m_blobs_buffer_size= 0;
DBUG_PRINT("value", ("allocate blobs buffer size %u", offset));
- blobs_buffer= my_malloc(offset, MYF(MY_WME));
- if (blobs_buffer == NULL)
+ m_blobs_buffer= my_malloc(offset, MYF(MY_WME));
+ if (m_blobs_buffer == NULL)
DBUG_RETURN(-1);
- blobs_buffer_size= offset;
+ m_blobs_buffer_size= offset;
}
}
DBUG_RETURN(0);
@@ -670,7 +699,8 @@ bool ha_ndbcluster::uses_blob_value(bool all_fields)
int ha_ndbcluster::get_metadata(const char *path)
{
- NDBDICT *dict= m_ndb->getDictionary();
+ Ndb *ndb= get_ndb();
+ NDBDICT *dict= ndb->getDictionary();
const NDBTAB *tab;
int error;
bool invalidating_ndb_table= FALSE;
@@ -735,6 +765,42 @@ int ha_ndbcluster::get_metadata(const char *path)
DBUG_RETURN(build_index_list(table, ILBP_OPEN));
}
+static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
+ const NDBINDEX *index,
+ KEY *key_info)
+{
+ DBUG_ENTER("fix_unique_index_attr_order");
+ unsigned sz= index->getNoOfIndexColumns();
+
+ if (data.unique_index_attrid_map)
+ my_free((char*)data.unique_index_attrid_map, MYF(0));
+ data.unique_index_attrid_map= (unsigned char*)my_malloc(sz,MYF(MY_WME));
+
+ KEY_PART_INFO* key_part= key_info->key_part;
+ KEY_PART_INFO* end= key_part+key_info->key_parts;
+ DBUG_ASSERT(key_info->key_parts == sz);
+ for (unsigned i= 0; key_part != end; key_part++, i++)
+ {
+ const char *field_name= key_part->field->field_name;
+ unsigned name_sz= strlen(field_name);
+ if (name_sz >= NDB_MAX_ATTR_NAME_SIZE)
+ name_sz= NDB_MAX_ATTR_NAME_SIZE-1;
+#ifndef DBUG_OFF
+ data.unique_index_attrid_map[i]= 255;
+#endif
+ for (unsigned j= 0; j < sz; j++)
+ {
+ const NdbDictionary::Column *c= index->getColumn(j);
+ if (strncmp(field_name, c->getName(), name_sz) == 0)
+ {
+ data.unique_index_attrid_map[i]= j;
+ break;
+ }
+ }
+ DBUG_ASSERT(data.unique_index_attrid_map[i] != 255);
+ }
+ DBUG_RETURN(0);
+}
int ha_ndbcluster::build_index_list(TABLE *tab, enum ILBP phase)
{
@@ -745,7 +811,8 @@ int ha_ndbcluster::build_index_list(TABLE *tab, enum ILBP phase)
static const char* unique_suffix= "$unique";
KEY* key_info= tab->key_info;
const char **key_name= tab->keynames.type_names;
- NdbDictionary::Dictionary *dict= m_ndb->getDictionary();
+ Ndb *ndb= get_ndb();
+ NdbDictionary::Dictionary *dict= ndb->getDictionary();
DBUG_ENTER("build_index_list");
// Save information about all known indexes
@@ -777,7 +844,8 @@ int ha_ndbcluster::build_index_list(TABLE *tab, enum ILBP phase)
error= create_unique_index(unique_index_name, key_info);
break;
case UNIQUE_INDEX:
- error= create_unique_index(unique_index_name, key_info);
+ if (!(error= check_index_fields_not_null(i)))
+ error= create_unique_index(unique_index_name, key_info);
break;
case ORDERED_INDEX:
error= create_ordered_index(index_name, key_info);
@@ -807,7 +875,8 @@ int ha_ndbcluster::build_index_list(TABLE *tab, enum ILBP phase)
const NDBINDEX *index= dict->getIndex(unique_index_name, m_tabname);
if (!index) DBUG_RETURN(1);
m_index[i].unique_index= (void *) index;
- }
+ error= fix_unique_index_attr_order(m_index[i], index, key_info);
+ }
}
DBUG_RETURN(error);
@@ -829,6 +898,26 @@ NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_table(uint inx) const
ORDERED_INDEX);
}
+int ha_ndbcluster::check_index_fields_not_null(uint inx)
+{
+ KEY* key_info= table->key_info + inx;
+ KEY_PART_INFO* key_part= key_info->key_part;
+ KEY_PART_INFO* end= key_part+key_info->key_parts;
+ DBUG_ENTER("check_index_fields_not_null");
+
+ for (; key_part != end; key_part++)
+ {
+ Field* field= key_part->field;
+ if (field->maybe_null())
+ {
+ my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX),
+ MYF(0),field->field_name);
+ DBUG_RETURN(ER_NULL_COLUMN_IN_INDEX);
+ }
+ }
+
+ DBUG_RETURN(0);
+}
void ha_ndbcluster::release_metadata()
{
@@ -845,6 +934,11 @@ void ha_ndbcluster::release_metadata()
{
m_index[i].unique_index= NULL;
m_index[i].index= NULL;
+ if (m_index[i].unique_index_attrid_map)
+ {
+ my_free((char *)m_index[i].unique_index_attrid_map, MYF(0));
+ m_index[i].unique_index_attrid_map= NULL;
+ }
}
DBUG_VOID_RETURN;
@@ -854,7 +948,7 @@ int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type)
{
if (type >= TL_WRITE_ALLOW_WRITE)
return NdbOperation::LM_Exclusive;
- else if (uses_blob_value(retrieve_all_fields))
+ else if (uses_blob_value(m_retrieve_all_fields))
return NdbOperation::LM_Read;
else
return NdbOperation::LM_CommittedRead;
@@ -1018,7 +1112,8 @@ int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf)
{
Field *field= table->field[i];
if ((thd->query_id == field->query_id) ||
- retrieve_all_fields)
+ m_retrieve_all_fields ||
+ (field->flags & PRI_KEY_FLAG) && m_retrieve_primary_key)
{
if (get_ndb_value(op, field, i, buf))
ERR_RETURN(trans->getNdbError());
@@ -1055,7 +1150,7 @@ int ha_ndbcluster::complemented_pk_read(const byte *old_data, byte *new_data)
THD *thd= current_thd;
DBUG_ENTER("complemented_pk_read");
- if (retrieve_all_fields)
+ if (m_retrieve_all_fields)
// We have allready retrieved all fields, nothing to complement
DBUG_RETURN(0);
@@ -1093,6 +1188,34 @@ int ha_ndbcluster::complemented_pk_read(const byte *old_data, byte *new_data)
DBUG_RETURN(0);
}
+/*
+ Peek to check if a particular row already exists
+*/
+
+int ha_ndbcluster::peek_row()
+{
+ NdbConnection *trans= m_active_trans;
+ NdbOperation *op;
+ THD *thd= current_thd;
+ DBUG_ENTER("peek_row");
+
+ NdbOperation::LockMode lm=
+ (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
+ if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
+ op->readTuple(lm) != 0)
+ ERR_RETURN(trans->getNdbError());
+
+ int res;
+ if ((res= set_primary_key(op)))
+ ERR_RETURN(trans->getNdbError());
+
+ if (execute_no_commit_ie(this,trans) != 0)
+ {
+ table->status= STATUS_NOT_FOUND;
+ DBUG_RETURN(ndb_err(trans));
+ }
+ DBUG_RETURN(0);
+}
/*
Read one record from NDB using unique secondary index
@@ -1128,9 +1251,11 @@ int ha_ndbcluster::unique_index_read(const byte *key,
for (i= 0; key_part != end; key_part++, i++)
{
- if (set_ndb_key(op, key_part->field, i, key_ptr))
+ if (set_ndb_key(op, key_part->field,
+ m_index[active_index].unique_index_attrid_map[i],
+ key_part->null_bit ? key_ptr + 1 : key_ptr))
ERR_RETURN(trans->getNdbError());
- key_ptr+= key_part->length;
+ key_ptr+= key_part->store_length;
}
// Get non-index attribute(s)
@@ -1138,7 +1263,7 @@ int ha_ndbcluster::unique_index_read(const byte *key,
{
Field *field= table->field[i];
if ((thd->query_id == field->query_id) ||
- (field->flags & PRI_KEY_FLAG))
+ (field->flags & PRI_KEY_FLAG)) // && m_retrieve_primary_key ??
{
if (get_ndb_value(op, field, i, buf))
ERR_RETURN(op->getNdbError());
@@ -1192,14 +1317,14 @@ inline int ha_ndbcluster::next_result(byte *buf)
/*
We can only handle one tuple with blobs at a time.
*/
- if (ops_pending && blobs_pending)
+ if (m_ops_pending && m_blobs_pending)
{
if (execute_no_commit(this,trans) != 0)
DBUG_RETURN(ndb_err(trans));
- ops_pending= 0;
- blobs_pending= FALSE;
+ m_ops_pending= 0;
+ m_blobs_pending= FALSE;
}
- check= cursor->nextResult(contact_ndb);
+ check= cursor->nextResult(contact_ndb, m_force_send);
if (check == 0)
{
// One more record found
@@ -1219,10 +1344,11 @@ inline int ha_ndbcluster::next_result(byte *buf)
all pending update or delete operations should
be sent to NDB
*/
- DBUG_PRINT("info", ("ops_pending: %d", ops_pending));
- if (ops_pending)
+ DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));
+ if (m_ops_pending)
{
- if (current_thd->transaction.on)
+ // if (current_thd->transaction.on)
+ if (m_transaction_on)
{
if (execute_no_commit(this,trans) != 0)
DBUG_RETURN(ndb_err(trans));
@@ -1234,7 +1360,7 @@ inline int ha_ndbcluster::next_result(byte *buf)
int res= trans->restart();
DBUG_ASSERT(res == 0);
}
- ops_pending= 0;
+ m_ops_pending= 0;
}
contact_ndb= (check == 2);
@@ -1290,7 +1416,6 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
Field *field= key_part->field;
uint part_len= key_part->length;
uint part_store_len= key_part->store_length;
- bool part_nullable= (bool) key_part->null_bit;
// Info about each key part
struct part_st {
bool part_last;
@@ -1312,9 +1437,9 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
p.part_last= (tot_len + part_store_len >= key_tot_len[j]);
p.key= keys[j];
p.part_ptr= &p.key->key[tot_len];
- p.part_null= (field->maybe_null() && *p.part_ptr);
+ p.part_null= key_part->null_bit && *p.part_ptr;
p.bound_ptr= (const char *)
- p.part_null ? 0 : part_nullable ? p.part_ptr + 1 : p.part_ptr;
+ p.part_null ? 0 : key_part->null_bit ? p.part_ptr + 1 : p.part_ptr;
if (j == 0)
{
@@ -1393,8 +1518,10 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
// Set bound if not cancelled via type -1
if (p.bound_type != -1)
- if (op->setBound(field->field_name, p.bound_type, p.bound_ptr))
+ {
+ if (op->setBound(i, p.bound_type, p.bound_ptr))
ERR_RETURN(op->getNdbError());
+ }
}
}
@@ -1418,7 +1545,7 @@ int ha_ndbcluster::define_read_attrs(byte* buf, NdbOperation* op)
Field *field= table->field[i];
if ((thd->query_id == field->query_id) ||
(field->flags & PRI_KEY_FLAG) ||
- retrieve_all_fields)
+ m_retrieve_all_fields)
{
if (get_ndb_value(op, field, i, buf))
ERR_RETURN(op->getNdbError());
@@ -1487,7 +1614,7 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
DBUG_ASSERT(op->getSorted() == sorted);
DBUG_ASSERT(op->getLockMode() ==
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
- if(op->reset_bounds())
+ if(op->reset_bounds(m_force_send))
DBUG_RETURN(ndb_err(m_active_trans));
}
@@ -1562,7 +1689,7 @@ int ha_ndbcluster::filtered_scan(const byte *key, uint key_len,
Field* field= key_part->field;
uint ndb_fieldnr= key_part->fieldnr-1;
DBUG_PRINT("key_part", ("fieldnr: %d", ndb_fieldnr));
- // const NDBCOL *col= tab->getColumn(ndb_fieldnr);
+ //const NDBCOL *col= ((const NDBTAB *) m_table)->getColumn(ndb_fieldnr);
uint32 field_len= field->pack_length();
DBUG_DUMP("key", (char*)key, field_len);
@@ -1631,9 +1758,17 @@ int ha_ndbcluster::write_row(byte *record)
int res;
DBUG_ENTER("write_row");
- if(m_ignore_dup_key_not_supported)
+ if(m_ignore_dup_key && table->primary_key != MAX_KEY)
{
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+ int peek_res= peek_row();
+
+ if (!peek_res)
+ {
+ m_dupkey= table->primary_key;
+ DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
+ }
+ if (peek_res != HA_ERR_KEY_NOT_FOUND)
+ DBUG_RETURN(peek_res);
}
statistic_increment(ha_write_count,&LOCK_status);
@@ -1651,7 +1786,8 @@ int ha_ndbcluster::write_row(byte *record)
if (table->primary_key == MAX_KEY)
{
// Table has hidden primary key
- Uint64 auto_value= m_ndb->getAutoIncrementValue((const NDBTAB *) m_table);
+ Ndb *ndb= get_ndb();
+ Uint64 auto_value= ndb->getAutoIncrementValue((const NDBTAB *) m_table);
if (set_hidden_key(op, table->fields, (const byte*)&auto_value))
ERR_RETURN(op->getNdbError());
}
@@ -1661,9 +1797,9 @@ int ha_ndbcluster::write_row(byte *record)
if (has_auto_increment)
{
- skip_auto_increment= FALSE;
+ m_skip_auto_increment= FALSE;
update_auto_increment();
- skip_auto_increment= !auto_increment_column_changed;
+ m_skip_auto_increment= !auto_increment_column_changed;
}
if ((res= set_primary_key(op)))
@@ -1678,7 +1814,7 @@ int ha_ndbcluster::write_row(byte *record)
if (!(field->flags & PRI_KEY_FLAG) &&
set_ndb_value(op, field, i, &set_blob_value))
{
- skip_auto_increment= TRUE;
+ m_skip_auto_increment= TRUE;
ERR_RETURN(op->getNdbError());
}
}
@@ -1690,25 +1826,26 @@ int ha_ndbcluster::write_row(byte *record)
to NoCommit the transaction between each row.
Find out how this is detected!
*/
- rows_inserted++;
+ m_rows_inserted++;
no_uncommitted_rows_update(1);
- bulk_insert_not_flushed= TRUE;
- if ((rows_to_insert == 1) ||
- ((rows_inserted % bulk_insert_rows) == 0) ||
+ m_bulk_insert_not_flushed= TRUE;
+ if ((m_rows_to_insert == 1) ||
+ ((m_rows_inserted % m_bulk_insert_rows) == 0) ||
set_blob_value)
{
THD *thd= current_thd;
// Send rows to NDB
DBUG_PRINT("info", ("Sending inserts to NDB, "\
"rows_inserted:%d, bulk_insert_rows: %d",
- (int)rows_inserted, (int)bulk_insert_rows));
+ (int)m_rows_inserted, (int)m_bulk_insert_rows));
- bulk_insert_not_flushed= FALSE;
- if (thd->transaction.on)
+ m_bulk_insert_not_flushed= FALSE;
+ // if (thd->transaction.on)
+ if (m_transaction_on)
{
if (execute_no_commit(this,trans) != 0)
{
- skip_auto_increment= TRUE;
+ m_skip_auto_increment= TRUE;
no_uncommitted_rows_execute_failure();
DBUG_RETURN(ndb_err(trans));
}
@@ -1717,7 +1854,7 @@ int ha_ndbcluster::write_row(byte *record)
{
if (execute_commit(this,trans) != 0)
{
- skip_auto_increment= TRUE;
+ m_skip_auto_increment= TRUE;
no_uncommitted_rows_execute_failure();
DBUG_RETURN(ndb_err(trans));
}
@@ -1725,17 +1862,18 @@ int ha_ndbcluster::write_row(byte *record)
DBUG_ASSERT(res == 0);
}
}
- if ((has_auto_increment) && (skip_auto_increment))
+ if ((has_auto_increment) && (m_skip_auto_increment))
{
+ Ndb *ndb= get_ndb();
Uint64 next_val= (Uint64) table->next_number_field->val_int() + 1;
DBUG_PRINT("info",
("Trying to set next auto increment value to %lu",
(ulong) next_val));
- if (m_ndb->setAutoIncrementValue((const NDBTAB *) m_table, next_val, TRUE))
+ if (ndb->setAutoIncrementValue((const NDBTAB *) m_table, next_val, TRUE))
DBUG_PRINT("info",
("Setting next auto increment value to %u", next_val));
}
- skip_auto_increment= TRUE;
+ m_skip_auto_increment= TRUE;
DBUG_RETURN(0);
}
@@ -1815,7 +1953,9 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
}
// Delete old row
DBUG_PRINT("info", ("insert succeded"));
+ m_primary_key_update= TRUE;
delete_res= delete_row(old_data);
+ m_primary_key_update= FALSE;
if (delete_res)
{
DBUG_PRINT("info", ("delete failed"));
@@ -1838,9 +1978,9 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
DBUG_PRINT("info", ("Calling updateTuple on cursor"));
if (!(op= cursor->updateTuple()))
ERR_RETURN(trans->getNdbError());
- ops_pending++;
+ m_ops_pending++;
if (uses_blob_value(FALSE))
- blobs_pending= TRUE;
+ m_blobs_pending= TRUE;
}
else
{
@@ -1875,7 +2015,7 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
for (i= 0; i < table->fields; i++)
{
Field *field= table->field[i];
- if ((thd->query_id == field->query_id) &&
+ if (((thd->query_id == field->query_id) || m_retrieve_all_fields) &&
(!(field->flags & PRI_KEY_FLAG)) &&
set_ndb_value(op, field, i))
ERR_RETURN(op->getNdbError());
@@ -1916,7 +2056,7 @@ int ha_ndbcluster::delete_row(const byte *record)
DBUG_PRINT("info", ("Calling deleteTuple on cursor"));
if (cursor->deleteTuple() != 0)
ERR_RETURN(trans->getNdbError());
- ops_pending++;
+ m_ops_pending++;
no_uncommitted_rows_update(-1);
@@ -1946,8 +2086,10 @@ int ha_ndbcluster::delete_row(const byte *record)
else
{
int res;
- if ((res= set_primary_key(op)))
- return res;
+ if ((res= (m_primary_key_update ?
+ set_primary_key_from_old_data(op, record)
+ : set_primary_key(op))))
+ return res;
}
}
@@ -2134,24 +2276,26 @@ void ha_ndbcluster::print_results()
fprintf(DBUG_FILE, "Double\t%f", value);
break;
}
- case NdbDictionary::Column::Decimal: {
+ case NdbDictionary::Column::Olddecimal: {
char *value= field->ptr;
-
- fprintf(DBUG_FILE, "Decimal\t'%-*s'", field->pack_length(), value);
+ fprintf(DBUG_FILE, "Olddecimal\t'%-*s'", field->pack_length(), value);
+ break;
+ }
+ case NdbDictionary::Column::Olddecimalunsigned: {
+ char *value= field->ptr;
+ fprintf(DBUG_FILE, "Olddecimalunsigned\t'%-*s'", field->pack_length(), value);
break;
}
case NdbDictionary::Column::Char:{
- char buf[field->pack_length()+1];
- char *value= (char *) field->ptr;
- snprintf(buf, field->pack_length(), "%s", value);
- fprintf(DBUG_FILE, "Char\t'%s'", buf);
+ const char *value= (char *) field->ptr;
+ fprintf(DBUG_FILE, "Char\t'%.*s'", field->pack_length(), value);
break;
}
case NdbDictionary::Column::Varchar:
case NdbDictionary::Column::Binary:
case NdbDictionary::Column::Varbinary: {
- char *value= (char *) field->ptr;
- fprintf(DBUG_FILE, "'%s'", value);
+ const char *value= (char *) field->ptr;
+ fprintf(DBUG_FILE, "Var\t'%.*s'", field->pack_length(), value);
break;
}
case NdbDictionary::Column::Datetime: {
@@ -2159,9 +2303,14 @@ void ha_ndbcluster::print_results()
fprintf(DBUG_FILE, "Datetime\t%llu", value);
break;
}
- case NdbDictionary::Column::Timespec: {
+ case NdbDictionary::Column::Date: {
+ Uint64 value= (Uint64) *field->ptr;
+ fprintf(DBUG_FILE, "Date\t%llu", value);
+ break;
+ }
+ case NdbDictionary::Column::Time: {
Uint64 value= (Uint64) *field->ptr;
- fprintf(DBUG_FILE, "Timespec\t%llu", value);
+ fprintf(DBUG_FILE, "Time\t%llu", value);
break;
}
case NdbDictionary::Column::Blob: {
@@ -2202,6 +2351,28 @@ int ha_ndbcluster::index_end()
DBUG_RETURN(close_scan());
}
+/**
+ * Check if key contains null
+ */
+static
+int
+check_null_in_key(const KEY* key_info, const byte *key, uint key_len)
+{
+ KEY_PART_INFO *curr_part, *end_part;
+ const byte* end_ptr = key + key_len;
+ curr_part= key_info->key_part;
+ end_part= curr_part + key_info->key_parts;
+
+
+ for (; curr_part != end_part && key < end_ptr; curr_part++)
+ {
+ if(curr_part->null_bit && *key)
+ return 1;
+
+ key += curr_part->store_length;
+ }
+ return 0;
+}
int ha_ndbcluster::index_read(byte *buf,
const byte *key, uint key_len,
@@ -2219,6 +2390,8 @@ int ha_ndbcluster::index_read(byte *buf,
case PRIMARY_KEY_INDEX:
if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len)
{
+ if(m_active_cursor && (error= close_scan()))
+ DBUG_RETURN(error);
DBUG_RETURN(pk_read(key, key_len, buf));
}
else if (type == PRIMARY_KEY_INDEX)
@@ -2228,8 +2401,11 @@ int ha_ndbcluster::index_read(byte *buf,
break;
case UNIQUE_ORDERED_INDEX:
case UNIQUE_INDEX:
- if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len)
+ if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len &&
+ !check_null_in_key(key_info, key, key_len))
{
+ if(m_active_cursor && (error= close_scan()))
+ DBUG_RETURN(error);
DBUG_RETURN(unique_index_read(key, key_len, buf));
}
else if (type == UNIQUE_INDEX)
@@ -2303,14 +2479,14 @@ int ha_ndbcluster::index_last(byte *buf)
int res;
if((res= ordered_index_scan(0, 0, TRUE, buf)) == 0){
NdbResultSet *cursor= m_active_cursor;
- while((res= cursor->nextResult(TRUE)) == 0);
+ while((res= cursor->nextResult(TRUE, m_force_send)) == 0);
if(res == 1){
unpack_record(buf);
table->status= 0;
DBUG_RETURN(0);
}
}
- DBUG_RETURN(1);
+ DBUG_RETURN(res);
}
@@ -2333,6 +2509,8 @@ int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
start_key->length == key_info->key_length &&
start_key->flag == HA_READ_KEY_EXACT)
{
+ if(m_active_cursor && (error= close_scan()))
+ DBUG_RETURN(error);
error= pk_read(start_key->key, start_key->length, buf);
DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
}
@@ -2340,10 +2518,12 @@ int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
case UNIQUE_ORDERED_INDEX:
case UNIQUE_INDEX:
key_info= table->key_info + active_index;
- if (start_key &&
- start_key->length == key_info->key_length &&
- start_key->flag == HA_READ_KEY_EXACT)
+ if (start_key && start_key->length == key_info->key_length &&
+ start_key->flag == HA_READ_KEY_EXACT &&
+ !check_null_in_key(key_info, start_key->key, start_key->length))
{
+ if(m_active_cursor && (error= close_scan()))
+ DBUG_RETURN(error);
error= unique_index_read(start_key->key, start_key->length, buf);
DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
}
@@ -2389,7 +2569,7 @@ int ha_ndbcluster::rnd_init(bool scan)
{
if (!scan)
DBUG_RETURN(1);
- int res= cursor->restart();
+ int res= cursor->restart(m_force_send);
DBUG_ASSERT(res == 0);
}
index_init(table->primary_key);
@@ -2406,21 +2586,21 @@ int ha_ndbcluster::close_scan()
DBUG_RETURN(1);
- if (ops_pending)
+ if (m_ops_pending)
{
/*
Take over any pending transactions to the
deleteing/updating transaction before closing the scan
*/
- DBUG_PRINT("info", ("ops_pending: %d", ops_pending));
+ DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending));
if (execute_no_commit(this,trans) != 0) {
no_uncommitted_rows_execute_failure();
DBUG_RETURN(ndb_err(trans));
}
- ops_pending= 0;
+ m_ops_pending= 0;
}
- cursor->close();
+ cursor->close(m_force_send);
m_active_cursor= NULL;
DBUG_RETURN(0);
}
@@ -2532,14 +2712,20 @@ void ha_ndbcluster::info(uint flag)
DBUG_PRINT("info", ("HA_STATUS_VARIABLE"));
if (m_table_info)
{
- records_update();
+ if (m_ha_not_exact_count)
+ records= 100;
+ else
+ records_update();
}
else
{
- Uint64 rows;
- if(ndb_get_table_statistics(m_ndb, m_tabname, &rows, 0) == 0){
- records= rows;
- }
+ if ((my_errno= check_ndb_connection()))
+ DBUG_VOID_RETURN;
+ Ndb *ndb= get_ndb();
+ Uint64 rows= 100;
+ if (current_thd->variables.ndb_use_exact_count)
+ ndb_get_table_statistics(ndb, m_tabname, &rows, 0);
+ records= rows;
}
}
if (flag & HA_STATUS_CONST)
@@ -2550,7 +2736,7 @@ void ha_ndbcluster::info(uint flag)
if (flag & HA_STATUS_ERRKEY)
{
DBUG_PRINT("info", ("HA_STATUS_ERRKEY"));
- errkey= dupkey;
+ errkey= m_dupkey;
}
if (flag & HA_STATUS_AUTO)
DBUG_PRINT("info", ("HA_STATUS_AUTO"));
@@ -2645,20 +2831,21 @@ int ha_ndbcluster::extra(enum ha_extra_function operation)
m_use_write= TRUE;
} else
{
- m_ignore_dup_key_not_supported= TRUE;
+ DBUG_PRINT("info", ("Ignoring duplicate key"));
+ m_ignore_dup_key= TRUE;
}
break;
case HA_EXTRA_NO_IGNORE_DUP_KEY:
DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_DUP_KEY"));
DBUG_PRINT("info", ("Turning OFF use of write instead of insert"));
m_use_write= FALSE;
- m_ignore_dup_key_not_supported= FALSE;
+ m_ignore_dup_key= FALSE;
break;
case HA_EXTRA_RETRIEVE_ALL_COLS: /* Retrieve all columns, not just those
where field->query_id is the same as
the current query id */
DBUG_PRINT("info", ("HA_EXTRA_RETRIEVE_ALL_COLS"));
- retrieve_all_fields= TRUE;
+ m_retrieve_all_fields= TRUE;
break;
case HA_EXTRA_PREPARE_FOR_DELETE:
DBUG_PRINT("info", ("HA_EXTRA_PREPARE_FOR_DELETE"));
@@ -2671,6 +2858,7 @@ int ha_ndbcluster::extra(enum ha_extra_function operation)
break;
case HA_EXTRA_RETRIEVE_PRIMARY_KEY:
DBUG_PRINT("info", ("HA_EXTRA_RETRIEVE_PRIMARY_KEY"));
+ m_retrieve_primary_key= TRUE;
break;
case HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
DBUG_PRINT("info", ("HA_EXTRA_CHANGE_KEY_TO_UNIQUE"));
@@ -2702,8 +2890,8 @@ void ha_ndbcluster::start_bulk_insert(ha_rows rows)
DBUG_ENTER("start_bulk_insert");
DBUG_PRINT("enter", ("rows: %d", (int)rows));
- rows_inserted= 0;
- rows_to_insert= rows;
+ m_rows_inserted= 0;
+ m_rows_to_insert= rows;
/*
Calculate how many rows that should be inserted
@@ -2717,7 +2905,7 @@ void ha_ndbcluster::start_bulk_insert(ha_rows rows)
batch= bytesperbatch/bytes;
batch= batch == 0 ? 1 : batch;
DBUG_PRINT("info", ("batch: %d, bytes: %d", batch, bytes));
- bulk_insert_rows= batch;
+ m_bulk_insert_rows= batch;
DBUG_VOID_RETURN;
}
@@ -2731,22 +2919,22 @@ int ha_ndbcluster::end_bulk_insert()
DBUG_ENTER("end_bulk_insert");
// Check if last inserts need to be flushed
- if (bulk_insert_not_flushed)
+ if (m_bulk_insert_not_flushed)
{
NdbConnection *trans= m_active_trans;
// Send rows to NDB
DBUG_PRINT("info", ("Sending inserts to NDB, "\
"rows_inserted:%d, bulk_insert_rows: %d",
- rows_inserted, bulk_insert_rows));
- bulk_insert_not_flushed= FALSE;
+ m_rows_inserted, m_bulk_insert_rows));
+ m_bulk_insert_not_flushed= FALSE;
if (execute_no_commit(this,trans) != 0) {
no_uncommitted_rows_execute_failure();
my_errno= error= ndb_err(trans);
}
}
- rows_inserted= 0;
- rows_to_insert= 1;
+ m_rows_inserted= 0;
+ m_rows_to_insert= 1;
DBUG_RETURN(error);
}
@@ -2768,7 +2956,7 @@ int ha_ndbcluster::reset()
const char **ha_ndbcluster::bas_ext() const
-{ static const char *ext[]= { ".ndb", NullS }; return ext; }
+{ static const char *ext[]= { ha_ndb_ext, NullS }; return ext; }
/*
@@ -2863,6 +3051,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
DBUG_RETURN(1);
Thd_ndb *thd_ndb= (Thd_ndb*)thd->transaction.thd_ndb;
+ Ndb *ndb= thd_ndb->ndb;
DBUG_PRINT("enter", ("transaction.thd_ndb->lock_count: %d",
thd_ndb->lock_count));
@@ -2880,9 +3069,9 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
DBUG_ASSERT(!thd->transaction.stmt.ndb_tid);
DBUG_PRINT("trans",("Starting transaction stmt"));
- trans= m_ndb->startTransaction();
+ trans= ndb->startTransaction();
if (trans == NULL)
- ERR_RETURN(m_ndb->getNdbError());
+ ERR_RETURN(ndb->getNdbError());
no_uncommitted_rows_reset(thd);
thd->transaction.stmt.ndb_tid= trans;
}
@@ -2894,9 +3083,9 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
// A "master" transaction ha not been started yet
DBUG_PRINT("trans",("starting transaction, all"));
- trans= m_ndb->startTransaction();
+ trans= ndb->startTransaction();
if (trans == NULL)
- ERR_RETURN(m_ndb->getNdbError());
+ ERR_RETURN(ndb->getNdbError());
no_uncommitted_rows_reset(thd);
/*
@@ -2927,15 +3116,26 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
pointer to point to the NDB transaction.
*/
+ // store thread specific data first to set the right context
+ m_force_send= thd->variables.ndb_force_send;
+ m_ha_not_exact_count= !thd->variables.ndb_use_exact_count;
+ m_autoincrement_prefetch= thd->variables.ndb_autoincrement_prefetch_sz;
+ if (!thd->transaction.on)
+ m_transaction_on= FALSE;
+ else
+ m_transaction_on= thd->variables.ndb_use_transactions;
+ // m_use_local_query_cache= thd->variables.ndb_use_local_query_cache;
+
m_active_trans= thd->transaction.all.ndb_tid ?
(NdbConnection*)thd->transaction.all.ndb_tid:
(NdbConnection*)thd->transaction.stmt.ndb_tid;
DBUG_ASSERT(m_active_trans);
// Start of transaction
- retrieve_all_fields= FALSE;
- ops_pending= 0;
+ m_retrieve_all_fields= FALSE;
+ m_retrieve_primary_key= FALSE;
+ m_ops_pending= 0;
{
- NDBDICT *dict= m_ndb->getDictionary();
+ NDBDICT *dict= ndb->getDictionary();
const NDBTAB *tab;
void *tab_info;
if (!(tab= dict->getTable(m_tabname, &tab_info)))
@@ -2962,7 +3162,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
We must in this case close the transaction to release resources
*/
DBUG_PRINT("trans",("ending non-updating transaction"));
- m_ndb->closeTransaction(m_active_trans);
+ ndb->closeTransaction(m_active_trans);
thd->transaction.stmt.ndb_tid= 0;
}
}
@@ -2981,13 +3181,13 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
DBUG_PRINT("warning", ("m_active_cursor != NULL"));
m_active_cursor= NULL;
- if (blobs_pending)
+ if (m_blobs_pending)
DBUG_PRINT("warning", ("blobs_pending != 0"));
- blobs_pending= 0;
+ m_blobs_pending= 0;
- if (ops_pending)
+ if (m_ops_pending)
DBUG_PRINT("warning", ("ops_pending != 0L"));
- ops_pending= 0;
+ m_ops_pending= 0;
}
DBUG_RETURN(error);
}
@@ -3008,24 +3208,26 @@ int ha_ndbcluster::start_stmt(THD *thd)
NdbConnection *trans= (NdbConnection*)thd->transaction.stmt.ndb_tid;
if (!trans){
+ Ndb *ndb= ((Thd_ndb*)thd->transaction.thd_ndb)->ndb;
DBUG_PRINT("trans",("Starting transaction stmt"));
NdbConnection *tablock_trans=
(NdbConnection*)thd->transaction.all.ndb_tid;
- DBUG_PRINT("info", ("tablock_trans: %x", (uint)tablock_trans));
+ DBUG_PRINT("info", ("tablock_trans: %x", (UintPtr)tablock_trans));
DBUG_ASSERT(tablock_trans);
-// trans= m_ndb->hupp(tablock_trans);
- trans= m_ndb->startTransaction();
+// trans= ndb->hupp(tablock_trans);
+ trans= ndb->startTransaction();
if (trans == NULL)
- ERR_RETURN(m_ndb->getNdbError());
+ ERR_RETURN(ndb->getNdbError());
no_uncommitted_rows_reset(thd);
thd->transaction.stmt.ndb_tid= trans;
}
m_active_trans= trans;
// Start of statement
- retrieve_all_fields= FALSE;
- ops_pending= 0;
+ m_retrieve_all_fields= FALSE;
+ m_retrieve_primary_key= FALSE;
+ m_ops_pending= 0;
DBUG_RETURN(error);
}
@@ -3047,7 +3249,7 @@ int ndbcluster_commit(THD *thd, void *ndb_transaction)
"stmt" : "all"));
DBUG_ASSERT(ndb && trans);
- if (execute_commit(0,trans) != 0)
+ if (execute_commit(thd,trans) != 0)
{
const NdbError err= trans->getNdbError();
const NdbOperation *error_op= trans->getNdbErrorOperation();
@@ -3102,17 +3304,18 @@ static int create_ndb_column(NDBCOL &col,
HA_CREATE_INFO *info)
{
// Set name
- col.setName(field->field_name);
+ {
+ char truncated_field_name[NDB_MAX_ATTR_NAME_SIZE];
+ strnmov(truncated_field_name,field->field_name,sizeof(truncated_field_name));
+ truncated_field_name[sizeof(truncated_field_name)-1]= '\0';
+ col.setName(truncated_field_name);
+ }
// Get char set
CHARSET_INFO *cs= field->charset();
// Set type and sizes
const enum enum_field_types mysql_type= field->real_type();
switch (mysql_type) {
// Numeric types
- case MYSQL_TYPE_DECIMAL:
- col.setType(NDBCOL::Char);
- col.setLength(field->pack_length());
- break;
case MYSQL_TYPE_TINY:
if (field->flags & UNSIGNED_FLAG)
col.setType(NDBCOL::Tinyunsigned);
@@ -3156,22 +3359,51 @@ static int create_ndb_column(NDBCOL &col,
col.setType(NDBCOL::Double);
col.setLength(1);
break;
- // Date types
- case MYSQL_TYPE_TIMESTAMP:
- col.setType(NDBCOL::Unsigned);
- col.setLength(1);
+ case MYSQL_TYPE_DECIMAL:
+ {
+ Field_decimal *f= (Field_decimal*)field;
+ uint precision= f->pack_length();
+ uint scale= f->decimals();
+ if (field->flags & UNSIGNED_FLAG)
+ {
+ col.setType(NDBCOL::Olddecimalunsigned);
+ precision-= (scale > 0);
+ }
+ else
+ {
+ col.setType(NDBCOL::Olddecimal);
+ precision-= 1 + (scale > 0);
+ }
+ col.setPrecision(precision);
+ col.setScale(scale);
+ col.setLength(1);
+ }
break;
+ // Date types
case MYSQL_TYPE_DATETIME:
col.setType(NDBCOL::Datetime);
col.setLength(1);
break;
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_NEWDATE:
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_DATE: // ?
col.setType(NDBCOL::Char);
col.setLength(field->pack_length());
break;
+ case MYSQL_TYPE_NEWDATE:
+ col.setType(NDBCOL::Date);
+ col.setLength(1);
+ break;
+ case MYSQL_TYPE_TIME:
+ col.setType(NDBCOL::Time);
+ col.setLength(1);
+ break;
+ case MYSQL_TYPE_YEAR:
+ col.setType(NDBCOL::Year);
+ col.setLength(1);
+ break;
+ case MYSQL_TYPE_TIMESTAMP:
+ col.setType(NDBCOL::Timestamp);
+ col.setLength(1);
+ break;
// Char types
case MYSQL_TYPE_STRING:
if (field->flags & BINARY_FLAG)
@@ -3291,13 +3523,59 @@ static int create_ndb_column(NDBCOL &col,
Create a table in NDB Cluster
*/
+static void ndb_set_fragmentation(NDBTAB &tab, TABLE *form, uint pk_length)
+{
+ if (form->max_rows == 0) /* default setting, don't set fragmentation */
+ return;
+ /**
+ * get the number of fragments right
+ */
+ uint no_fragments;
+ {
+#if MYSQL_VERSION_ID >= 50000
+ uint acc_row_size= 25 + /*safety margin*/ 2;
+#else
+ uint acc_row_size= pk_length*4;
+ /* add acc overhead */
+ if (pk_length <= 8) /* main page will set the limit */
+ acc_row_size+= 25 + /*safety margin*/ 2;
+ else /* overflow page will set the limit */
+ acc_row_size+= 4 + /*safety margin*/ 4;
+#endif
+ ulonglong acc_fragment_size= 512*1024*1024;
+ ulonglong max_rows= form->max_rows;
+#if MYSQL_VERSION_ID >= 50100
+ no_fragments= (max_rows*acc_row_size)/acc_fragment_size+1;
+#else
+ no_fragments= ((max_rows*acc_row_size)/acc_fragment_size+1
+ +1/*correct rounding*/)/2;
+#endif
+ }
+ {
+ uint no_nodes= g_ndb_cluster_connection->no_db_nodes();
+ NDBTAB::FragmentType ftype;
+ if (no_fragments > 2*no_nodes)
+ {
+ ftype= NDBTAB::FragAllLarge;
+ if (no_fragments > 4*no_nodes)
+ push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ "Ndb might have problems storing the max amount of rows specified");
+ }
+ else if (no_fragments > no_nodes)
+ ftype= NDBTAB::FragAllMedium;
+ else
+ ftype= NDBTAB::FragAllSmall;
+ tab.setFragmentType(ftype);
+ }
+}
+
int ha_ndbcluster::create(const char *name,
TABLE *form,
HA_CREATE_INFO *info)
{
NDBTAB tab;
NDBCOL col;
- uint pack_length, length, i;
+ uint pack_length, length, i, pk_length= 0;
const void *data, *pack_data;
const char **key_names= form->keynames.type_names;
char name2[FN_HEADLEN];
@@ -3344,6 +3622,8 @@ int ha_ndbcluster::create(const char *name,
if ((my_errno= create_ndb_column(col, field, info)))
DBUG_RETURN(my_errno);
tab.addColumn(col);
+ if(col.getPrimaryKey())
+ pk_length += (field->pack_length() + 3) / 4;
}
// No primary key, create shadow key as 64 bit, auto increment
@@ -3357,13 +3637,49 @@ int ha_ndbcluster::create(const char *name,
col.setPrimaryKey(TRUE);
col.setAutoIncrement(TRUE);
tab.addColumn(col);
+ pk_length += 2;
}
+ // Make sure that blob tables don't have to big part size
+ for (i= 0; i < form->fields; i++)
+ {
+ /**
+ * The extra +7 concists
+ * 2 - words from pk in blob table
+ * 5 - from extra words added by tup/dict??
+ */
+ switch (form->field[i]->real_type()) {
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ {
+ NdbDictionary::Column * col = tab.getColumn(i);
+ int size = pk_length + (col->getPartSize()+3)/4 + 7;
+ if(size > NDB_MAX_TUPLE_SIZE_IN_WORDS &&
+ (pk_length+7) < NDB_MAX_TUPLE_SIZE_IN_WORDS)
+ {
+ size = NDB_MAX_TUPLE_SIZE_IN_WORDS - pk_length - 7;
+ col->setPartSize(4*size);
+ }
+ /**
+ * If size > NDB_MAX and pk_length+7 >= NDB_MAX
+ * then the table can't be created anyway, so skip
+ * changing part size, and have error later
+ */
+ }
+ default:
+ break;
+ }
+ }
+
+ ndb_set_fragmentation(tab, form, pk_length);
+
if ((my_errno= check_ndb_connection()))
DBUG_RETURN(my_errno);
// Create the table in NDB
- NDBDICT *dict= m_ndb->getDictionary();
+ Ndb *ndb= get_ndb();
+ NDBDICT *dict= ndb->getDictionary();
if (dict->createTable(tab) != 0)
{
const NdbError err= dict->getNdbError();
@@ -3408,7 +3724,8 @@ int ha_ndbcluster::create_index(const char *name,
KEY *key_info,
bool unique)
{
- NdbDictionary::Dictionary *dict= m_ndb->getDictionary();
+ Ndb *ndb= get_ndb();
+ NdbDictionary::Dictionary *dict= ndb->getDictionary();
KEY_PART_INFO *key_part= key_info->key_part;
KEY_PART_INFO *end= key_part + key_info->key_parts;
@@ -3430,7 +3747,12 @@ int ha_ndbcluster::create_index(const char *name,
{
Field *field= key_part->field;
DBUG_PRINT("info", ("attr: %s", field->field_name));
- ndb_index.addColumnName(field->field_name);
+ {
+ char truncated_field_name[NDB_MAX_ATTR_NAME_SIZE];
+ strnmov(truncated_field_name,field->field_name,sizeof(truncated_field_name));
+ truncated_field_name[sizeof(truncated_field_name)-1]= '\0';
+ ndb_index.addColumnName(truncated_field_name);
+ }
}
if (dict->createIndex(ndb_index))
@@ -3448,24 +3770,35 @@ int ha_ndbcluster::create_index(const char *name,
int ha_ndbcluster::rename_table(const char *from, const char *to)
{
+ NDBDICT *dict;
char new_tabname[FN_HEADLEN];
+ const NDBTAB *orig_tab;
+ int result;
DBUG_ENTER("ha_ndbcluster::rename_table");
+ DBUG_PRINT("info", ("Renaming %s to %s", from, to));
set_dbname(from);
set_tabname(from);
set_tabname(to, new_tabname);
if (check_ndb_connection())
DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);
+
+ Ndb *ndb= get_ndb();
+ dict= ndb->getDictionary();
+ if (!(orig_tab= dict->getTable(m_tabname)))
+ ERR_RETURN(dict->getNdbError());
-
- int result= alter_table_name(m_tabname, new_tabname);
- if (result == 0)
+ m_table= (void *)orig_tab;
+ // Change current database to that of target table
+ set_dbname(to);
+ ndb->setDatabaseName(m_dbname);
+ if (!(result= alter_table_name(new_tabname)))
{
- set_tabname(to);
- handler::rename_table(from, to);
+ // Rename .ndb file
+ result= handler::rename_table(from, to);
}
-
+
DBUG_RETURN(result);
}
@@ -3474,19 +3807,17 @@ int ha_ndbcluster::rename_table(const char *from, const char *to)
Rename a table in NDB Cluster using alter table
*/
-int ha_ndbcluster::alter_table_name(const char *from, const char *to)
+int ha_ndbcluster::alter_table_name(const char *to)
{
- NDBDICT *dict= m_ndb->getDictionary();
- const NDBTAB *orig_tab;
+ Ndb *ndb= get_ndb();
+ NDBDICT *dict= ndb->getDictionary();
+ const NDBTAB *orig_tab= (const NDBTAB *) m_table;
+ int ret;
DBUG_ENTER("alter_table_name_table");
- DBUG_PRINT("enter", ("Renaming %s to %s", from, to));
- if (!(orig_tab= dict->getTable(from)))
- ERR_RETURN(dict->getNdbError());
-
- NdbDictionary::Table copy_tab= dict->getTableForAlteration(from);
- copy_tab.setName(to);
- if (dict->alterTable(copy_tab) != 0)
+ NdbDictionary::Table new_tab= *orig_tab;
+ new_tab.setName(to);
+ if (dict->alterTable(new_tab) != 0)
ERR_RETURN(dict->getNdbError());
m_table= NULL;
@@ -3509,7 +3840,7 @@ int ha_ndbcluster::delete_table(const char *name)
if (check_ndb_connection())
DBUG_RETURN(HA_ERR_NO_CONNECTION);
-
+ // Remove .ndb file
handler::delete_table(name);
DBUG_RETURN(drop_table());
}
@@ -3521,8 +3852,9 @@ int ha_ndbcluster::delete_table(const char *name)
int ha_ndbcluster::drop_table()
{
- NdbDictionary::Dictionary *dict= m_ndb->getDictionary();
-
+ Ndb *ndb= get_ndb();
+ NdbDictionary::Dictionary *dict= ndb->getDictionary();
+
DBUG_ENTER("drop_table");
DBUG_PRINT("enter", ("Deleting %s", m_tabname));
@@ -3555,16 +3887,17 @@ longlong ha_ndbcluster::get_auto_increment()
{
DBUG_ENTER("get_auto_increment");
DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
+ Ndb *ndb= get_ndb();
int cache_size=
- (rows_to_insert - rows_inserted < autoincrement_prefetch) ?
- rows_to_insert - rows_inserted
- : (rows_to_insert > autoincrement_prefetch) ?
- rows_to_insert
- : autoincrement_prefetch;
+ (m_rows_to_insert - m_rows_inserted < m_autoincrement_prefetch) ?
+ m_rows_to_insert - m_rows_inserted
+ : (m_rows_to_insert > m_autoincrement_prefetch) ?
+ m_rows_to_insert
+ : m_autoincrement_prefetch;
Uint64 auto_value=
- (skip_auto_increment) ?
- m_ndb->readAutoIncrementValue((const NDBTAB *) m_table)
- : m_ndb->getAutoIncrementValue((const NDBTAB *) m_table, cache_size);
+ (m_skip_auto_increment) ?
+ ndb->readAutoIncrementValue((const NDBTAB *) m_table)
+ : ndb->getAutoIncrementValue((const NDBTAB *) m_table, cache_size);
DBUG_RETURN((longlong)auto_value);
}
@@ -3577,7 +3910,6 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
handler(table_arg),
m_active_trans(NULL),
m_active_cursor(NULL),
- m_ndb(NULL),
m_table(NULL),
m_table_info(NULL),
m_table_flags(HA_REC_NOT_IN_SEQ |
@@ -3586,18 +3918,25 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
HA_NO_PREFIX_CHAR_KEYS),
m_share(0),
m_use_write(FALSE),
- m_ignore_dup_key_not_supported(FALSE),
- retrieve_all_fields(FALSE),
- rows_to_insert(1),
- rows_inserted(0),
- bulk_insert_rows(1024),
- bulk_insert_not_flushed(FALSE),
- ops_pending(0),
- skip_auto_increment(TRUE),
- blobs_pending(0),
- blobs_buffer(0),
- blobs_buffer_size(0),
- dupkey((uint) -1)
+ m_ignore_dup_key(FALSE),
+ m_primary_key_update(FALSE),
+ m_retrieve_all_fields(FALSE),
+ m_retrieve_primary_key(FALSE),
+ m_rows_to_insert(1),
+ m_rows_inserted(0),
+ m_bulk_insert_rows(1024),
+ m_bulk_insert_not_flushed(FALSE),
+ m_ops_pending(0),
+ m_skip_auto_increment(TRUE),
+ m_blobs_pending(0),
+ m_blobs_buffer(0),
+ m_blobs_buffer_size(0),
+ m_dupkey((uint) -1),
+ m_ha_not_exact_count(FALSE),
+ m_force_send(TRUE),
+ m_autoincrement_prefetch(32),
+ m_transaction_on(TRUE),
+ m_use_local_query_cache(FALSE)
{
int i;
@@ -3611,9 +3950,10 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
for (i= 0; i < MAX_KEY; i++)
{
- m_index[i].type= UNDEFINED_INDEX;
- m_index[i].unique_index= NULL;
- m_index[i].index= NULL;
+ m_index[i].type= UNDEFINED_INDEX;
+ m_index[i].unique_index= NULL;
+ m_index[i].index= NULL;
+ m_index[i].unique_index_attrid_map= NULL;
}
DBUG_VOID_RETURN;
@@ -3631,8 +3971,8 @@ ha_ndbcluster::~ha_ndbcluster()
if (m_share)
free_share(m_share);
release_metadata();
- my_free(blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
- blobs_buffer= 0;
+ my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
+ m_blobs_buffer= 0;
// Check for open cursor/transaction
if (m_active_cursor) {
@@ -3700,7 +4040,6 @@ int ha_ndbcluster::close(void)
DBUG_ENTER("close");
free_share(m_share); m_share= 0;
release_metadata();
- m_ndb= NULL;
DBUG_RETURN(0);
}
@@ -3758,14 +4097,16 @@ Ndb* check_ndb_in_thd(THD* thd)
}
+
int ha_ndbcluster::check_ndb_connection()
{
THD* thd= current_thd;
+ Ndb *ndb;
DBUG_ENTER("check_ndb_connection");
- if (!(m_ndb= check_ndb_in_thd(thd)))
+ if (!(ndb= check_ndb_in_thd(thd)))
DBUG_RETURN(HA_ERR_NO_CONNECTION);
- m_ndb->setDatabaseName(m_dbname);
+ ndb->setDatabaseName(m_dbname);
DBUG_RETURN(0);
}
@@ -4039,40 +4380,52 @@ bool ndbcluster_init()
int res;
DBUG_ENTER("ndbcluster_init");
// Set connectstring if specified
- if (ndbcluster_connectstring != 0)
- DBUG_PRINT("connectstring", ("%s", ndbcluster_connectstring));
+ if (opt_ndbcluster_connectstring != 0)
+ DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring));
if ((g_ndb_cluster_connection=
- new Ndb_cluster_connection(ndbcluster_connectstring)) == 0)
+ new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0)
{
- DBUG_PRINT("error",("Ndb_cluster_connection(%s)",ndbcluster_connectstring));
- DBUG_RETURN(TRUE);
+ DBUG_PRINT("error",("Ndb_cluster_connection(%s)",
+ opt_ndbcluster_connectstring));
+ goto ndbcluster_init_error;
}
+ g_ndb_cluster_connection->set_optimized_node_selection
+ (opt_ndb_optimized_node_selection);
+
// Create a Ndb object to open the connection to NDB
g_ndb= new Ndb(g_ndb_cluster_connection, "sys");
g_ndb->getDictionary()->set_local_table_data_size(sizeof(Ndb_table_local_info));
if (g_ndb->init() != 0)
{
ERR_PRINT (g_ndb->getNdbError());
- DBUG_RETURN(TRUE);
+ goto ndbcluster_init_error;
}
- if ((res= g_ndb_cluster_connection->connect(1)) == 0)
+ if ((res= g_ndb_cluster_connection->connect(0,0,0)) == 0)
{
- g_ndb->waitUntilReady(10);
+ DBUG_PRINT("info",("NDBCLUSTER storage engine at %s on port %d",
+ g_ndb_cluster_connection->get_connected_host(),
+ g_ndb_cluster_connection->get_connected_port()));
+ g_ndb_cluster_connection->wait_until_ready(10,3);
}
else if(res == 1)
{
if (g_ndb_cluster_connection->start_connect_thread()) {
DBUG_PRINT("error", ("g_ndb_cluster_connection->start_connect_thread()"));
- DBUG_RETURN(TRUE);
+ goto ndbcluster_init_error;
+ }
+ {
+ char buf[1024];
+ DBUG_PRINT("info",("NDBCLUSTER storage engine not started, will connect using %s",
+ g_ndb_cluster_connection->get_connectstring(buf,sizeof(buf))));
}
}
else
{
DBUG_ASSERT(res == -1);
DBUG_PRINT("error", ("permanent error"));
- DBUG_RETURN(TRUE);
+ goto ndbcluster_init_error;
}
(void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0,
@@ -4082,9 +4435,12 @@ bool ndbcluster_init()
ndbcluster_inited= 1;
#ifdef USE_DISCOVER_ON_STARTUP
if (ndb_discover_tables() != 0)
- DBUG_RETURN(TRUE);
+ goto ndbcluster_init_error;
#endif
DBUG_RETURN(FALSE);
+ ndbcluster_init_error:
+ ndbcluster_end();
+ DBUG_RETURN(TRUE);
}
@@ -4247,6 +4603,65 @@ ha_ndbcluster::records_in_range(uint inx, key_range *min_key,
DBUG_RETURN(10); /* Good guess when you don't know anything */
}
+ulong ha_ndbcluster::table_flags(void) const
+{
+ if (m_ha_not_exact_count)
+ return m_table_flags | HA_NOT_EXACT_COUNT;
+ else
+ return m_table_flags;
+}
+const char * ha_ndbcluster::table_type() const
+{
+ return("ndbcluster");
+}
+uint ha_ndbcluster::max_supported_record_length() const
+{
+ return NDB_MAX_TUPLE_SIZE;
+}
+uint ha_ndbcluster::max_supported_keys() const
+{
+ return MAX_KEY;
+}
+uint ha_ndbcluster::max_supported_key_parts() const
+{
+ return NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY;
+}
+uint ha_ndbcluster::max_supported_key_length() const
+{
+ return NDB_MAX_KEY_SIZE;
+}
+bool ha_ndbcluster::low_byte_first() const
+{
+#ifdef WORDS_BIGENDIAN
+ return FALSE;
+#else
+ return TRUE;
+#endif
+}
+bool ha_ndbcluster::has_transactions()
+{
+ return m_transaction_on;
+}
+const char* ha_ndbcluster::index_type(uint key_number)
+{
+ switch (get_index_type(key_number)) {
+ case ORDERED_INDEX:
+ case UNIQUE_ORDERED_INDEX:
+ case PRIMARY_KEY_ORDERED_INDEX:
+ return "BTREE";
+ case UNIQUE_INDEX:
+ case PRIMARY_KEY_INDEX:
+ default:
+ return "HASH";
+ }
+}
+uint8 ha_ndbcluster::table_cache_type()
+{
+ if (m_use_local_query_cache)
+ return HA_CACHE_TBL_TRANSACT;
+ else
+ return HA_CACHE_TBL_NOCACHE;
+}
/*
Handling the shared NDB_SHARE structure that is needed to
@@ -4413,13 +4828,12 @@ ndb_get_table_statistics(Ndb* ndb, const char * table,
{
DBUG_ENTER("ndb_get_table_statistics");
DBUG_PRINT("enter", ("table: %s", table));
-
+ NdbConnection* pTrans= ndb->startTransaction();
do
{
- NdbConnection* pTrans= ndb->startTransaction();
if (pTrans == NULL)
break;
-
+
NdbScanOperation* pOp= pTrans->getNdbScanOperation(table);
if (pOp == NULL)
break;
@@ -4436,13 +4850,13 @@ ndb_get_table_statistics(Ndb* ndb, const char * table,
pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
- check= pTrans->execute(NoCommit);
+ check= pTrans->execute(NoCommit, AbortOnError, TRUE);
if (check == -1)
break;
Uint64 sum_rows= 0;
Uint64 sum_commits= 0;
- while((check= rs->nextResult(TRUE)) == 0)
+ while((check= rs->nextResult(TRUE, TRUE)) == 0)
{
sum_rows+= rows;
sum_commits+= commits;
@@ -4451,6 +4865,8 @@ ndb_get_table_statistics(Ndb* ndb, const char * table,
if (check == -1)
break;
+ rs->close(TRUE);
+
ndb->closeTransaction(pTrans);
if(row_count)
* row_count= sum_rows;
@@ -4460,6 +4876,7 @@ ndb_get_table_statistics(Ndb* ndb, const char * table,
DBUG_RETURN(0);
} while(0);
+ ndb->closeTransaction(pTrans);
DBUG_PRINT("exit", ("failed"));
DBUG_RETURN(-1);
}
diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h
index 8224d1c4167..07b305bad3e 100644
--- a/sql/ha_ndbcluster.h
+++ b/sql/ha_ndbcluster.h
@@ -26,7 +26,6 @@
#endif
#include <ndbapi_limits.h>
-#include <ndb_types.h>
class Ndb; // Forward declaration
class NdbOperation; // Forward declaration
@@ -53,6 +52,7 @@ typedef struct ndb_index_data {
NDB_INDEX_TYPE type;
void *index;
void *unique_index;
+ unsigned char *unique_index_attrid_map;
} NDB_INDEX_DATA;
typedef struct st_ndbcluster_share {
@@ -119,15 +119,14 @@ class ha_ndbcluster: public handler
int reset();
int external_lock(THD *thd, int lock_type);
int start_stmt(THD *thd);
- const char * table_type() const { return("ndbcluster");}
+ const char * table_type() const;
const char ** bas_ext() const;
- ulong table_flags(void) const { return m_table_flags; }
+ ulong table_flags(void) const;
ulong index_flags(uint idx, uint part, bool all_parts) const;
- uint max_supported_record_length() const { return NDB_MAX_TUPLE_SIZE; };
- uint max_supported_keys() const { return MAX_KEY; }
- uint max_supported_key_parts() const
- { return NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY; };
- uint max_supported_key_length() const { return NDB_MAX_KEY_SIZE;};
+ uint max_supported_record_length() const;
+ uint max_supported_keys() const;
+ uint max_supported_key_parts() const;
+ uint max_supported_key_length() const;
int rename_table(const char *from, const char *to);
int delete_table(const char *name);
@@ -136,28 +135,9 @@ class ha_ndbcluster: public handler
THR_LOCK_DATA **to,
enum thr_lock_type lock_type);
- bool low_byte_first() const
- {
-#ifdef WORDS_BIGENDIAN
- return FALSE;
-#else
- return TRUE;
-#endif
- }
- bool has_transactions() { return TRUE; }
-
- const char* index_type(uint key_number) {
- switch (get_index_type(key_number)) {
- case ORDERED_INDEX:
- case UNIQUE_ORDERED_INDEX:
- case PRIMARY_KEY_ORDERED_INDEX:
- return "BTREE";
- case UNIQUE_INDEX:
- case PRIMARY_KEY_INDEX:
- default:
- return "HASH";
- }
- }
+ bool low_byte_first() const;
+ bool has_transactions();
+ const char* index_type(uint key_number);
double scan_time();
ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key);
@@ -166,10 +146,10 @@ class ha_ndbcluster: public handler
static Thd_ndb* seize_thd_ndb();
static void release_thd_ndb(Thd_ndb* thd_ndb);
- uint8 table_cache_type() { return HA_CACHE_TBL_NOCACHE; }
+ uint8 table_cache_type();
private:
- int alter_table_name(const char *from, const char *to);
+ int alter_table_name(const char *to);
int drop_table();
int create_index(const char *name, KEY *key_info, bool unique);
int create_ordered_index(const char *name, KEY *key_info);
@@ -181,9 +161,11 @@ class ha_ndbcluster: public handler
void release_metadata();
NDB_INDEX_TYPE get_index_type(uint idx_no) const;
NDB_INDEX_TYPE get_index_type_from_table(uint index_no) const;
-
+ int check_index_fields_not_null(uint index_no);
+
int pk_read(const byte *key, uint key_len, byte *buf);
int complemented_pk_read(const byte *old_data, byte *new_data);
+ int peek_row();
int unique_index_read(const byte *key, uint key_len,
byte *buf);
int ordered_index_scan(const key_range *start_key,
@@ -229,7 +211,6 @@ class ha_ndbcluster: public handler
NdbConnection *m_active_trans;
NdbResultSet *m_active_cursor;
- Ndb *m_ndb;
void *m_table;
void *m_table_info;
char m_dbname[FN_HEADLEN];
@@ -243,20 +224,29 @@ class ha_ndbcluster: public handler
typedef union { NdbRecAttr *rec; NdbBlob *blob; void *ptr; } NdbValue;
NdbValue m_value[NDB_MAX_ATTRIBUTES_IN_TABLE];
bool m_use_write;
- bool m_ignore_dup_key_not_supported;
- bool retrieve_all_fields;
- ha_rows rows_to_insert;
- ha_rows rows_inserted;
- ha_rows bulk_insert_rows;
- bool bulk_insert_not_flushed;
- ha_rows ops_pending;
- bool skip_auto_increment;
- bool blobs_pending;
+ bool m_ignore_dup_key;
+ bool m_primary_key_update;
+ bool m_retrieve_all_fields;
+ bool m_retrieve_primary_key;
+ ha_rows m_rows_to_insert;
+ ha_rows m_rows_inserted;
+ ha_rows m_bulk_insert_rows;
+ bool m_bulk_insert_not_flushed;
+ ha_rows m_ops_pending;
+ bool m_skip_auto_increment;
+ bool m_blobs_pending;
// memory for blobs in one tuple
- char *blobs_buffer;
- uint32 blobs_buffer_size;
- uint dupkey;
-
+ char *m_blobs_buffer;
+ uint32 m_blobs_buffer_size;
+ uint m_dupkey;
+ // set from thread variables at external lock
+ bool m_ha_not_exact_count;
+ bool m_force_send;
+ ha_rows m_autoincrement_prefetch;
+ bool m_transaction_on;
+ bool m_use_local_query_cache;
+
+ Ndb *get_ndb();
void set_rec_per_key();
void records_update();
void no_uncommitted_rows_execute_failure();
@@ -265,6 +255,8 @@ class ha_ndbcluster: public handler
void no_uncommitted_rows_reset(THD *);
friend int execute_no_commit(ha_ndbcluster*, NdbConnection*);
+ friend int execute_commit(ha_ndbcluster*, NdbConnection*);
+ friend int execute_no_commit_ie(ha_ndbcluster*, NdbConnection*);
};
bool ndbcluster_init(void);
diff --git a/sql/handler.cc b/sql/handler.cc
index f7a1a6ef0bf..70ba236a5d5 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -109,6 +109,9 @@ const char *tx_isolation_names[] =
TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"",
tx_isolation_names, NULL};
+static TYPELIB known_extensions= {0,"known_exts", NULL, NULL};
+uint known_extensions_id= 0;
+
enum db_type ha_resolve_by_name(const char *name, uint namelen)
{
THD *thd=current_thd;
@@ -275,6 +278,16 @@ int ha_init()
opt_using_transactions=1;
}
#endif
+#ifdef HAVE_ARCHIVE_DB
+ if (have_archive_db == SHOW_OPTION_YES)
+ {
+ if (archive_db_init())
+ {
+ have_archive_db= SHOW_OPTION_DISABLED;
+ error= 1;
+ }
+ }
+#endif
return error;
}
@@ -306,6 +319,10 @@ int ha_panic(enum ha_panic_function flag)
if (have_ndbcluster == SHOW_OPTION_YES)
error|=ndbcluster_end();
#endif
+#ifdef HAVE_ARCHIVE_DB
+ if (have_archive_db == SHOW_OPTION_YES)
+ error|= archive_db_end();
+#endif
return error;
} /* ha_panic */
@@ -581,6 +598,12 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans)
if (opt_using_transactions)
{
bool operation_done=0;
+ /*
+ As rollback can be 30 times slower than insert in InnoDB, and user may
+ not know there's rollback (if it's because of a dupl row), better warn.
+ */
+ const char *save_proc_info= thd->proc_info;
+ thd->proc_info= "Rolling back";
#ifdef HAVE_NDBCLUSTER_DB
if (trans->ndb_tid)
{
@@ -652,6 +675,7 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans)
thd->variables.tx_isolation=thd->session_tx_isolation;
if (operation_done)
statistic_increment(ha_rollback_count,&LOCK_status);
+ thd->proc_info= save_proc_info;
}
#endif /* USING_TRANSACTIONS */
DBUG_RETURN(error);
@@ -713,7 +737,7 @@ int ha_rollback_to_savepoint(THD *thd, char *savepoint_name)
if (unlikely((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) &&
my_b_tell(&thd->transaction.trans_log)))
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE, FALSE);
if (mysql_bin_log.write(&qinfo))
error= 1;
}
@@ -751,7 +775,7 @@ int ha_savepoint(THD *thd, char *savepoint_name)
innobase_savepoint(thd,savepoint_name,
my_b_tell(&thd->transaction.trans_log));
#endif
- Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE, FALSE);
if (mysql_bin_log.write(&qinfo))
error= 1;
}
@@ -764,6 +788,25 @@ int ha_savepoint(THD *thd, char *savepoint_name)
DBUG_RETURN(error);
}
+
+int ha_start_consistent_snapshot(THD *thd)
+{
+#ifdef HAVE_INNOBASE_DB
+ if ((have_innodb == SHOW_OPTION_YES) &&
+ !innobase_start_trx_and_assign_read_view(thd))
+ return 0;
+#endif
+ /*
+ Same idea as when one wants to CREATE TABLE in one engine which does not
+ exist:
+ */
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ "This MySQL server does not support any "
+ "consistent-read capable storage engine");
+ return 0;
+}
+
+
bool ha_flush_logs()
{
bool result=0;
@@ -924,8 +967,10 @@ int handler::read_first_row(byte * buf, uint primary_key)
/*
If there is very few deleted rows in the table, find the first row by
scanning the table.
+ TODO remove the test for HA_READ_ORDER
*/
- if (deleted < 10 || primary_key >= MAX_KEY)
+ if (deleted < 10 || primary_key >= MAX_KEY ||
+ !(index_flags(primary_key, 0, 0) & HA_READ_ORDER))
{
(void) ha_rnd_init(1);
while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ;
@@ -1052,6 +1097,9 @@ void handler::print_error(int error, myf errflag)
textno=ER_DUP_KEY;
break;
}
+ case HA_ERR_NULL_IN_SPATIAL:
+ textno= ER_UNKNOWN_ERROR;
+ DBUG_VOID_RETURN;
case HA_ERR_FOUND_DUPP_UNIQUE:
textno=ER_DUP_UNIQUE;
break;
@@ -1165,7 +1213,8 @@ uint handler::get_dup_key(int error)
{
DBUG_ENTER("handler::get_dup_key");
table->file->errkey = (uint) -1;
- if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOUND_DUPP_UNIQUE)
+ if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOUND_DUPP_UNIQUE ||
+ error == HA_ERR_NULL_IN_SPATIAL)
info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK);
DBUG_RETURN(table->file->errkey);
}
@@ -1633,3 +1682,64 @@ int handler::index_read_idx(byte * buf, uint index, const byte * key,
return error;
}
+
+/*
+ Returns a list of all known extensions.
+
+ SYNOPSIS
+ ha_known_exts()
+
+ NOTES
+ No mutexes, worst case race is a minor surplus memory allocation
+ We have to recreate the extension map if mysqld is restarted (for example
+ within libmysqld)
+
+ RETURN VALUE
+ pointer pointer to TYPELIB structure
+*/
+
+TYPELIB *ha_known_exts(void)
+{
+ if (!known_extensions.type_names || mysys_usage_id != known_extensions_id)
+ {
+ show_table_type_st *types;
+ List<char> found_exts;
+ List_iterator_fast<char> it(found_exts);
+ const char **ext, *old_ext;
+
+ known_extensions_id= mysys_usage_id;
+ found_exts.push_back((char*) ".db");
+ for (types= sys_table_types; types->type; types++)
+ {
+ if (*types->value == SHOW_OPTION_YES)
+ {
+ handler *file= get_new_handler(0,(enum db_type) types->db_type);
+ for (ext= file->bas_ext(); *ext; ext++)
+ {
+ while ((old_ext= it++))
+ {
+ if (!strcmp(old_ext, *ext))
+ break;
+ }
+ if (!old_ext)
+ found_exts.push_back((char *) *ext);
+
+ it.rewind();
+ }
+ delete file;
+ }
+ }
+ ext= (const char **) my_once_alloc(sizeof(char *)*
+ (found_exts.elements+1),
+ MYF(MY_WME | MY_FAE));
+
+ DBUG_ASSERT(ext);
+ known_extensions.count= found_exts.elements;
+ known_extensions.type_names= ext;
+
+ while ((old_ext= it++))
+ *ext++= old_ext;
+ *ext= 0;
+ }
+ return &known_extensions;
+}
diff --git a/sql/handler.h b/sql/handler.h
index a7ce4e708fd..0426312f404 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -138,6 +138,8 @@
#define HA_CACHE_TBL_ASKTRANSACT 2
#define HA_CACHE_TBL_TRANSACT 4
+/* Options of START TRANSACTION statement (and later of SET TRANSACTION stmt) */
+#define MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT 1
enum db_type
{
@@ -370,8 +372,8 @@ public:
virtual int read_range_next();
int compare_key(key_range *range);
virtual int ft_init() { return HA_ERR_WRONG_COMMAND; }
- virtual FT_INFO *ft_init_ext(uint flags,uint inx,const byte *key,
- uint keylen)
+ void ft_end() { ft_handler=NULL; }
+ virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key)
{ return NULL; }
virtual int ft_read(byte *buf) { return HA_ERR_WRONG_COMMAND; }
virtual int rnd_next(byte *buf)=0;
@@ -567,5 +569,5 @@ int ha_discover(THD* thd, const char* dbname, const char* name,
int ha_find_files(THD *thd,const char *db,const char *path,
const char *wild, bool dir,List<char>* files);
int ha_table_exists(THD* thd, const char* db, const char* name);
-
-
+TYPELIB *ha_known_exts(void);
+int ha_start_consistent_snapshot(THD *thd);
diff --git a/sql/item.cc b/sql/item.cc
index b4c416e7741..76cbaa99029 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -45,7 +45,7 @@ Item::Item():
{
marker= 0;
maybe_null=null_value=with_sum_func=unsigned_flag=0;
- collation.set(default_charset(), DERIVATION_COERCIBLE);
+ collation.set(&my_charset_bin, DERIVATION_COERCIBLE);
name= 0;
decimals= 0; max_length= 0;
@@ -69,7 +69,7 @@ Item::Item():
}
/*
- Constructor used by Item_field, Item_ref & agregate (sum) functions.
+ Constructor used by Item_field, Item_*_ref & agregate (sum) functions.
Used for duplicating lists in processing queries with temporary
tables
*/
@@ -114,7 +114,7 @@ Item_ident::Item_ident(const char *db_name_par,const char *table_name_par,
name = (char*) field_name_par;
}
-// Constructor used by Item_field & Item_ref (see Item comment)
+// Constructor used by Item_field & Item_*_ref (see Item comment)
Item_ident::Item_ident(THD *thd, Item_ident *item)
:Item(thd, item),
orig_db_name(item->orig_db_name),
@@ -205,6 +205,41 @@ bool Item::eq(const Item *item, bool binary_cmp) const
}
+Item *Item::safe_charset_converter(CHARSET_INFO *tocs)
+{
+ /*
+ Don't allow automatic conversion to non-Unicode charsets,
+ as it potentially loses data.
+ */
+ if (!(tocs->state & MY_CS_UNICODE))
+ return NULL; // safe conversion is not possible
+ return new Item_func_conv_charset(this, tocs);
+}
+
+
+Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs)
+{
+ Item_string *conv;
+ uint conv_errors;
+ String tmp, cstr, *ostr= val_str(&tmp);
+ cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
+ if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(),
+ cstr.charset(),
+ collation.derivation)))
+ {
+ /*
+ Safe conversion is not possible (or EOM).
+ We could not convert a string into the requested character set
+ without data loss. The target charset does not cover all the
+ characters from the string. Operation cannot be done correctly.
+ */
+ return NULL;
+ }
+ conv->str_value.copy();
+ return conv;
+}
+
+
bool Item_string::eq(const Item *item, bool binary_cmp) const
{
if (type() == item->type())
@@ -259,7 +294,95 @@ CHARSET_INFO *Item::default_charset()
return current_thd->variables.collation_connection;
}
-bool DTCollation::aggregate(DTCollation &dt, bool superset_conversion)
+
+/*
+ Move SUM items out from item tree and replace with reference
+
+ SYNOPSIS
+ split_sum_func2()
+ thd Thread handler
+ ref_pointer_array Pointer to array of reference fields
+ fields All fields in select
+ ref Pointer to item
+
+ NOTES
+ This is from split_sum_func2() for items that should be split
+
+ All found SUM items are added FIRST in the fields list and
+ we replace the item with a reference.
+
+ thd->fatal_error() may be called if we are out of memory
+*/
+
+
+void Item::split_sum_func2(THD *thd, Item **ref_pointer_array,
+ List<Item> &fields, Item **ref)
+{
+ if (type() != SUM_FUNC_ITEM && with_sum_func)
+ {
+ /* Will split complicated items and ignore simple ones */
+ split_sum_func(thd, ref_pointer_array, fields);
+ }
+ else if ((type() == SUM_FUNC_ITEM ||
+ (used_tables() & ~PARAM_TABLE_BIT)) &&
+ type() != REF_ITEM)
+ {
+ /*
+ Replace item with a reference so that we can easily calculate
+ it (in case of sum functions) or copy it (in case of fields)
+
+ The test above is to ensure we don't do a reference for things
+ that are constants (PARAM_TABLE_BIT is in effect a constant)
+ or already referenced (for example an item in HAVING)
+ */
+ uint el= fields.elements;
+ Item *new_item;
+ ref_pointer_array[el]= this;
+ if (!(new_item= new Item_ref(ref_pointer_array + el, 0, name)))
+ return; // fatal_error is set
+ fields.push_front(this);
+ ref_pointer_array[el]= this;
+ thd->change_item_tree(ref, new_item);
+ }
+}
+
+
+/*
+ Aggregate two collations together taking
+ into account their coercibility (aka derivation):
+
+ 0 == DERIVATION_EXPLICIT - an explicitely written COLLATE clause
+ 1 == DERIVATION_NONE - a mix of two different collations
+ 2 == DERIVATION_IMPLICIT - a column
+ 3 == DERIVATION_COERCIBLE - a string constant
+
+ The most important rules are:
+
+ 1. If collations are the same:
+ chose this collation, and the strongest derivation.
+
+ 2. If collations are different:
+ - Character sets may differ, but only if conversion without
+ data loss is possible. The caller provides flags whether
+ character set conversion attempts should be done. If no
+ flags are substituted, then the character sets must be the same.
+ Currently processed flags are:
+ MY_COLL_ALLOW_SUPERSET_CONV - allow conversion to a superset
+ MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value
+ - two EXPLICIT collations produce an error, e.g. this is wrong:
+ CONCAT(expr1 collate latin1_swedish_ci, expr2 collate latin1_german_ci)
+ - the side with smaller derivation value wins,
+ i.e. a column is stronger than a string constant,
+ an explicit COLLATE clause is stronger than a column.
+ - if derivations are the same, we have DERIVATION_NONE,
+ we'll wait for an explicit COLLATE clause which possibly can
+ come from another argument later: for example, this is valid,
+ but we don't know yet when collecting the first two arguments:
+ CONCAT(latin1_swedish_ci_column,
+ latin1_german1_ci_column,
+ expr COLLATE latin1_german2_ci)
+*/
+bool DTCollation::aggregate(DTCollation &dt, uint flags)
{
nagg++;
if (!my_charset_same(collation, dt.collation))
@@ -290,28 +413,37 @@ bool DTCollation::aggregate(DTCollation &dt, bool superset_conversion)
else
; // Do nothing
}
- else if (superset_conversion)
+ else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) &&
+ derivation < dt.derivation &&
+ collation->state & MY_CS_UNICODE)
{
- if (derivation < dt.derivation &&
- collation->state & MY_CS_UNICODE)
- ; // Do nothing
- else if (dt.derivation < derivation &&
- dt.collation->state & MY_CS_UNICODE)
- {
- set(dt);
- strong= nagg;
- }
- else
- {
- // Cannot convert to superset
- set(0, DERIVATION_NONE);
- return 1;
- }
+ // Do nothing
+ }
+ else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) &&
+ dt.derivation < derivation &&
+ dt.collation->state & MY_CS_UNICODE)
+ {
+ set(dt);
+ strong= nagg;
+ }
+ else if ((flags & MY_COLL_ALLOW_COERCIBLE_CONV) &&
+ derivation < dt.derivation &&
+ dt.derivation >= DERIVATION_COERCIBLE)
+ {
+ // Do nothing;
+ }
+ else if ((flags & MY_COLL_ALLOW_COERCIBLE_CONV) &&
+ dt.derivation < derivation &&
+ derivation >= DERIVATION_COERCIBLE)
+ {
+ set(dt);
+ strong= nagg;
}
else
{
+ // Cannot apply conversion
set(0, DERIVATION_NONE);
- return 1;
+ return 1;
}
}
else if (derivation < dt.derivation)
@@ -678,6 +810,12 @@ String *Item_null::val_str(String *str)
}
+Item *Item_null::safe_charset_converter(CHARSET_INFO *tocs)
+{
+ collation.set(tocs);
+ return this;
+}
+
/*********************** Item_param related ******************************/
/*
@@ -751,6 +889,21 @@ void Item_param::set_double(double d)
}
+/*
+ Set parameter value from TIME value.
+
+ SYNOPSIS
+ set_time()
+ tm - datetime value to set (time_type is ignored)
+ type - type of datetime value
+ max_length_arg - max length of datetime value as string
+
+ NOTE
+ If we value to be stored is not normalized, zero value will be stored
+ instead and proper warning will be produced. This function relies on
+ the fact that even wrong value sent over binary protocol fits into
+ MAX_DATE_STRING_REP_LENGTH buffer.
+*/
void Item_param::set_time(TIME *tm, timestamp_type type, uint32 max_length_arg)
{
DBUG_ENTER("Item_param::set_time");
@@ -758,6 +911,17 @@ void Item_param::set_time(TIME *tm, timestamp_type type, uint32 max_length_arg)
value.time= *tm;
value.time.time_type= type;
+ if (value.time.year > 9999 || value.time.month > 12 ||
+ value.time.day > 31 ||
+ type != MYSQL_TIMESTAMP_TIME && value.time.hour > 23 ||
+ value.time.minute > 59 || value.time.second > 59)
+ {
+ char buff[MAX_DATE_STRING_REP_LENGTH];
+ uint length= my_TIME_to_str(&value.time, buff);
+ make_truncated_value_warning(current_thd, buff, length, type);
+ set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR);
+ }
+
state= TIME_VALUE;
maybe_null= 0;
max_length= max_length_arg;
@@ -773,7 +937,9 @@ bool Item_param::set_str(const char *str, ulong length)
Assign string with no conversion: data is converted only after it's
been written to the binary log.
*/
- if (str_value.copy(str, length, &my_charset_bin, &my_charset_bin))
+ uint dummy_errors;
+ if (str_value.copy(str, length, &my_charset_bin, &my_charset_bin,
+ &dummy_errors))
DBUG_RETURN(TRUE);
state= STRING_VALUE;
maybe_null= 0;
@@ -974,8 +1140,9 @@ double Item_param::val()
case LONG_DATA_VALUE:
{
int dummy_err;
+ char *end_not_used;
return my_strntod(str_value.charset(), (char*) str_value.ptr(),
- str_value.length(), (char**) 0, &dummy_err);
+ str_value.length(), &end_not_used, &dummy_err);
}
case TIME_VALUE:
/*
@@ -1130,6 +1297,10 @@ bool Item_param::convert_str_value(THD *thd)
value.cs_info.character_set_client,
value.cs_info.final_character_set_of_str_value);
}
+ else
+ str_value.set_charset(value.cs_info.final_character_set_of_str_value);
+ /* Here str_value is guaranteed to be in final_character_set_of_str_value */
+
max_length= str_value.length();
decimals= 0;
/*
@@ -1254,6 +1425,7 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
{
+ enum_parsing_place place= NO_MATTER;
DBUG_ASSERT(fixed == 0);
if (!field) // If field is not checked
{
@@ -1301,8 +1473,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
}
Item_subselect *prev_subselect_item= prev_unit->item;
- enum_parsing_place place=
- prev_subselect_item->parsing_place;
+ place= prev_subselect_item->parsing_place;
/*
check table fields only if subquery used somewhere out of HAVING
or outer SELECT do not use groupping (i.e. tables are
@@ -1326,7 +1497,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
&not_used)) !=
(Item **) not_found_item)
{
- if (*refer && (*refer)->fixed) // Avoid crash in case of error
+ if (refer && (*refer)->fixed) // Avoid crash in case of error
{
prev_subselect_item->used_tables_cache|= (*refer)->used_tables();
prev_subselect_item->const_item_cache&= (*refer)->const_item();
@@ -1364,18 +1535,32 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
}
else if (refer != (Item **)not_found_item)
{
- if (!(*refer)->fixed)
+ if (!last->ref_pointer_array[counter])
{
my_error(ER_ILLEGAL_REFERENCE, MYF(0), name,
"forward reference in item list");
return -1;
}
-
- Item_ref *rf= new Item_ref(last->ref_pointer_array + counter,
- (char *)table_name, (char *)field_name);
+ DBUG_ASSERT((*refer)->fixed);
+ /*
+ Here, a subset of actions performed by Item_ref::set_properties
+ is not enough. So we pass ptr to NULL into Item_[direct]_ref
+ constructor, so no initialization is performed, and call
+ fix_fields() below.
+ */
+ Item *save= last->ref_pointer_array[counter];
+ last->ref_pointer_array[counter]= NULL;
+ Item_ref *rf= (place == IN_HAVING ?
+ new Item_ref(last->ref_pointer_array + counter,
+ (char *)table_name,
+ (char *)field_name) :
+ new Item_direct_ref(last->ref_pointer_array + counter,
+ (char *)table_name,
+ (char *)field_name));
if (!rf)
return 1;
thd->change_item_tree(ref, rf);
+ last->ref_pointer_array[counter]= save;
/*
rf is Item_ref => never substitute other items (in this case)
during fix_fields() => we can use rf after fix_fields()
@@ -1921,11 +2106,11 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
{
DBUG_ASSERT(fixed == 0);
uint counter;
+ enum_parsing_place place= NO_MATTER;
bool not_used;
if (!ref)
{
TABLE_LIST *where= 0, *table_list;
- bool upward_lookup= 0;
SELECT_LEX_UNIT *prev_unit= thd->lex->current_select->master_unit();
SELECT_LEX *sl= prev_unit->outer_select();
/*
@@ -1946,7 +2131,6 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
{
Field *tmp= (Field*) not_found_field;
SELECT_LEX *last= 0;
- upward_lookup= 1;
/*
We can't find table field in select list of current select,
consequently we have to find it in outer subselect(s).
@@ -1966,7 +2150,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
&not_used)) !=
(Item **)not_found_item)
{
- if (*ref && (*ref)->fixed) // Avoid crash in case of error
+ if (ref && (*ref)->fixed) // Avoid crash in case of error
{
prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
prev_subselect_item->const_item_cache&= (*ref)->const_item();
@@ -1979,8 +2163,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
// it is primary INSERT st_select_lex => skip first table resolving
table_list= table_list->next;
}
- enum_parsing_place place=
- prev_subselect_item->parsing_place;
+ place= prev_subselect_item->parsing_place;
/*
check table fields only if subquery used somewhere out of HAVING
or SELECT list or outer SELECT do not use groupping (i.e. tables
@@ -2011,20 +2194,10 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
return -1;
if (ref == (Item **)not_found_item && tmp == not_found_field)
{
- if (upward_lookup)
- {
- // We can't say exactly what absend (table or field)
- my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0),
- full_name(), thd->where);
- }
- else
- {
- // Call to report error
- find_item_in_list(this,
- *(thd->lex->current_select->get_item_list()),
- &counter, REPORT_ALL_ERRORS, &not_used);
- }
- ref= 0; // Safety
+ // We can't say exactly what absend (table or field)
+ my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0),
+ full_name(), thd->where);
+ ref= 0; // Safety
return 1;
}
if (tmp != not_found_field)
@@ -2042,14 +2215,28 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
mark_as_dependent(thd, last, thd->lex->current_select, fld);
return 0;
}
- if (!(*ref)->fixed)
+ if (!last->ref_pointer_array[counter])
{
my_error(ER_ILLEGAL_REFERENCE, MYF(0), name,
"forward reference in item list");
return -1;
}
+ DBUG_ASSERT((*ref)->fixed);
mark_as_dependent(thd, last, thd->lex->current_select,
this);
+ if (place == IN_HAVING)
+ {
+ Item_ref *rf;
+ if (!(rf= new Item_direct_ref(last->ref_pointer_array + counter,
+ (char *)table_name,
+ (char *)field_name)))
+ return 1;
+ ref= 0; // Safety
+ if (rf->fix_fields(thd, tables, ref) || rf->check_cols(1))
+ return 1;
+ thd->change_item_tree(reference, rf);
+ return 0;
+ }
ref= last->ref_pointer_array + counter;
}
else if (!ref)
@@ -2085,19 +2272,24 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
"forward reference in item list"));
return 1;
}
+
+ set_properties();
+
+ if (ref && (*ref)->check_cols(1))
+ return 1;
+ return 0;
+}
+
+void Item_ref::set_properties()
+{
max_length= (*ref)->max_length;
maybe_null= (*ref)->maybe_null;
decimals= (*ref)->decimals;
collation.set((*ref)->collation);
with_sum_func= (*ref)->with_sum_func;
fixed= 1;
-
- if (ref && (*ref)->check_cols(1))
- return 1;
- return 0;
}
-
void Item_ref::print(String *str)
{
if (ref && *ref)
@@ -2143,7 +2335,7 @@ bool Item_default_value::fix_fields(THD *thd,
fixed= 1;
return 0;
}
- if (arg->fix_fields(thd, table_list, &arg))
+ if (!arg->fixed && arg->fix_fields(thd, table_list, &arg))
return 1;
if (arg->type() == REF_ITEM)
@@ -2190,7 +2382,7 @@ bool Item_insert_value::fix_fields(THD *thd,
Item **items)
{
DBUG_ASSERT(fixed == 0);
- if (arg->fix_fields(thd, table_list, &arg))
+ if (!arg->fixed && arg->fix_fields(thd, table_list, &arg))
return 1;
if (arg->type() == REF_ITEM)
@@ -2394,10 +2586,12 @@ double Item_cache_str::val()
DBUG_ASSERT(fixed == 1);
int err;
if (value)
+ {
+ char *end_not_used;
return my_strntod(value->charset(), (char*) value->ptr(),
- value->length(), (char**) 0, &err);
- else
- return (double)0;
+ value->length(), &end_not_used, &err);
+ }
+ return (double)0;
}
@@ -2500,7 +2694,53 @@ void Item_cache_row::bring_value()
}
-Item_type_holder::Item_type_holder(THD *thd, Item *item)
+/*
+ Returns field for temporary table dependind on item type
+
+ SYNOPSIS
+ get_holder_example_field()
+ thd - thread handler
+ item - pointer to item
+ table - empty table object
+
+ NOTE
+ It is possible to return field for Item_func
+ items only if field type of this item is
+ date or time or datetime type.
+ also see function field_types_to_be_kept() from
+ field.cc
+
+ RETURN
+ # - field
+ 0 - no field
+*/
+
+Field *get_holder_example_field(THD *thd, Item *item, TABLE *table)
+{
+ DBUG_ASSERT(table);
+
+ Item_func *tmp_item= 0;
+ if (item->type() == Item::FIELD_ITEM)
+ return (((Item_field*) item)->field);
+ if (item->type() == Item::FUNC_ITEM)
+ tmp_item= (Item_func *) item;
+ else if (item->type() == Item::SUM_FUNC_ITEM)
+ {
+ Item_sum *item_sum= (Item_sum *) item;
+ if (item_sum->keep_field_type())
+ {
+ if (item_sum->args[0]->type() == Item::FIELD_ITEM)
+ return (((Item_field*) item_sum->args[0])->field);
+ if (item_sum->args[0]->type() == Item::FUNC_ITEM)
+ tmp_item= (Item_func *) item_sum->args[0];
+ }
+ }
+ return (tmp_item && field_types_to_be_kept(tmp_item->field_type()) ?
+ tmp_item->tmp_table_field(table) : 0);
+}
+
+
+Item_type_holder::Item_type_holder(THD *thd, Item *item, TABLE *table)
:Item(thd, item), item_type(item->result_type()),
orig_type(item_type)
{
@@ -2510,10 +2750,7 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item)
It is safe assign pointer on field, because it will be used just after
all JOIN::prepare calls and before any SELECT execution
*/
- if (item->type() == Item::FIELD_ITEM)
- field_example= ((Item_field*) item)->field;
- else
- field_example= 0;
+ field_example= get_holder_example_field(thd, item, table);
max_length= real_length(item);
maybe_null= item->maybe_null;
collation.set(item->collation);
@@ -2553,25 +2790,23 @@ inline bool is_attr_compatible(Item *from, Item *to)
(to->maybe_null || !from->maybe_null) &&
(to->result_type() != STRING_RESULT ||
from->result_type() != STRING_RESULT ||
- my_charset_same(from->collation.collation,
- to->collation.collation)));
+ (from->collation.collation == to->collation.collation)));
}
-bool Item_type_holder::join_types(THD *thd, Item *item)
+bool Item_type_holder::join_types(THD *thd, Item *item, TABLE *table)
{
uint32 new_length= real_length(item);
bool use_new_field= 0, use_expression_type= 0;
Item_result new_result_type= type_convertor[item_type][item->result_type()];
- bool item_is_a_field= item->type() == Item::FIELD_ITEM;
-
+ Field *field= get_holder_example_field(thd, item, table);
+ bool item_is_a_field= (field != NULL);
/*
Check if both items point to fields: in this case we
can adjust column types of result table in the union smartly.
*/
if (field_example && item_is_a_field)
{
- Field *field= ((Item_field *)item)->field;
/* Can 'field_example' field store data of the column? */
if ((use_new_field=
(!field->field_cast_compatible(field_example->field_cast_type()) ||
@@ -2612,7 +2847,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
It is safe to assign a pointer to field here, because it will be used
before any table is closed.
*/
- field_example= ((Item_field*) item)->field;
+ field_example= field;
}
old_cs= collation.collation->name;
diff --git a/sql/item.h b/sql/item.h
index e22cef7ba7f..97e2b0c0945 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -31,12 +31,32 @@ void item_init(void); /* Init item functions */
enum Derivation
{
+ DERIVATION_IGNORABLE= 4,
DERIVATION_COERCIBLE= 3,
DERIVATION_IMPLICIT= 2,
DERIVATION_NONE= 1,
DERIVATION_EXPLICIT= 0
};
+/*
+ Flags for collation aggregation modes:
+ MY_COLL_ALLOW_SUPERSET_CONV - allow conversion to a superset
+ MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value
+ (i.e. constant).
+ MY_COLL_ALLOW_CONV - allow any kind of conversion
+ (combintion of the above two)
+ MY_COLL_DISALLOW_NONE - don't allow return DERIVATION_NONE
+ (e.g. when aggregating for comparison)
+ MY_COLL_CMP_CONV - combination of MY_COLL_ALLOW_CONV
+ and MY_COLL_DISALLOW_NONE
+*/
+
+#define MY_COLL_ALLOW_SUPERSET_CONV 1
+#define MY_COLL_ALLOW_COERCIBLE_CONV 2
+#define MY_COLL_ALLOW_CONV 3
+#define MY_COLL_DISALLOW_NONE 4
+#define MY_COLL_CMP_CONV 7
+
class DTCollation {
public:
CHARSET_INFO *collation;
@@ -72,13 +92,14 @@ public:
{ collation= collation_arg; }
void set(Derivation derivation_arg)
{ derivation= derivation_arg; }
- bool aggregate(DTCollation &dt, bool superset_conversion= FALSE);
- bool set(DTCollation &dt1, DTCollation &dt2, bool superset_conversion= FALSE)
- { set(dt1); return aggregate(dt2, superset_conversion); }
+ bool aggregate(DTCollation &dt, uint flags= 0);
+ bool set(DTCollation &dt1, DTCollation &dt2, uint flags= 0)
+ { set(dt1); return aggregate(dt2, flags); }
const char *derivation_name() const
{
switch(derivation)
{
+ case DERIVATION_IGNORABLE: return "IGNORABLE";
case DERIVATION_COERCIBLE: return "COERCIBLE";
case DERIVATION_IMPLICIT: return "IMPLICIT";
case DERIVATION_EXPLICIT: return "EXPLICIT";
@@ -98,7 +119,7 @@ public:
static void *operator new(size_t size, MEM_ROOT *mem_root)
{ return (void*) alloc_root(mem_root, (uint) size); }
static void operator delete(void *ptr,size_t size) {}
- static void operator delete(void *ptr,size_t size, MEM_ROOT *mem_root) {}
+ static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
enum Type {FIELD_ITEM, FUNC_ITEM, SUM_FUNC_ITEM, STRING_ITEM,
INT_ITEM, REAL_ITEM, NULL_ITEM, VARBIN_ITEM,
@@ -241,10 +262,21 @@ public:
virtual void update_used_tables() {}
virtual void split_sum_func(THD *thd, Item **ref_pointer_array,
List<Item> &fields) {}
+ /* Called for items that really have to be split */
+ void split_sum_func2(THD *thd, Item **ref_pointer_array, List<Item> &fields,
+ Item **ref);
virtual bool get_date(TIME *ltime,uint fuzzydate);
virtual bool get_time(TIME *ltime);
virtual bool get_date_result(TIME *ltime,uint fuzzydate)
{ return get_date(ltime,fuzzydate); }
+ /*
+ This function is used only in Item_func_isnull/Item_func_isnotnull
+ (implementations of IS NULL/IS NOT NULL clauses). Item_func_is{not}null
+ calls this method instead of one of val/result*() methods, which
+ normally will set null_value. This allows to determine nullness of
+ a complex expression without fully evaluating it.
+ Any new item which can be NULL must implement this call.
+ */
virtual bool is_null() { return 0; }
/*
it is "top level" item of WHERE clause and we do not need correct NULL
@@ -292,6 +324,7 @@ public:
Field *tmp_table_field_from_field_type(TABLE *table);
virtual Item *neg_transformer(THD *thd) { return NULL; }
+ virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
void delete_self()
{
cleanup();
@@ -420,6 +453,7 @@ public:
max_length= 0;
name= name_par ? name_par : (char*) "NULL";
fixed= 1;
+ collation.set(&my_charset_bin, DERIVATION_IGNORABLE);
}
enum Type type() const { return NULL_ITEM; }
bool eq(const Item *item, bool binary_cmp) const;
@@ -437,6 +471,7 @@ public:
Item *new_item() { return new Item_null(name); }
bool is_null() { return 1; }
void print(String *str) { str->append("NULL", 4); }
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
};
@@ -549,6 +584,8 @@ public:
void print(String *str) { str->append('?'); }
/* parameter never equal to other parameter of other item */
bool eq(const Item *item, bool binary_cmp) const { return 0; }
+ bool is_null()
+ { DBUG_ASSERT(state != NO_VALUE); return state == NULL_VALUE; }
};
class Item_int :public Item_num
@@ -682,8 +719,9 @@ public:
{
DBUG_ASSERT(fixed == 1);
int err;
+ char *end_not_used;
return my_strntod(str_value.charset(), (char*) str_value.ptr(),
- str_value.length(), (char**) 0, &err);
+ str_value.length(), &end_not_used, &err);
}
longlong val_int()
{
@@ -705,8 +743,9 @@ public:
Item *new_item()
{
return new Item_string(name, str_value.ptr(),
- str_value.length(), &my_charset_bin);
+ str_value.length(), collation.collation);
}
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
String *const_string() { return &str_value; }
inline void append(char *str, uint length) { str_value.append(str, length); }
void print(String *str);
@@ -730,7 +769,7 @@ class Item_empty_string :public Item_string
public:
Item_empty_string(const char *header,uint length, CHARSET_INFO *cs= NULL) :
Item_string("",0, cs ? cs : &my_charset_bin)
- { name=(char*) header; max_length=length;}
+ { name=(char*) header; max_length= cs ? length * cs->mbmaxlen : length; }
void make_field(Send_field *field);
};
@@ -792,14 +831,36 @@ public:
class Item_ref :public Item_ident
{
+protected:
+ void set_properties();
public:
Field *result_field; /* Save result here */
Item **ref;
Item_ref(const char *db_par, const char *table_name_par,
const char *field_name_par)
:Item_ident(db_par, table_name_par, field_name_par), ref(0) {}
+ /*
+ This constructor is used in two scenarios:
+ A) *item = NULL
+ No initialization is performed, fix_fields() call will be necessary.
+
+ B) *item points to an Item this Item_ref will refer to. This is
+ used for GROUP BY. fix_fields() will not be called in this case,
+ so we call set_properties to make this item "fixed". set_properties
+ performs a subset of action Item_ref::fix_fields does, and this subset
+ is enough for Item_ref's used in GROUP BY.
+
+ TODO we probably fix a superset of problems like in BUG#6658. Check this
+ with Bar, and if we have a more broader set of problems like this.
+ */
Item_ref(Item **item, const char *table_name_par, const char *field_name_par)
- :Item_ident(NullS, table_name_par, field_name_par), ref(item) {}
+ :Item_ident(NullS, table_name_par, field_name_par), ref(item)
+ {
+ DBUG_ASSERT(item);
+ if (*item)
+ set_properties();
+ }
+
/* Constructor need to process subselect with temporary tables (see Item) */
Item_ref(THD *thd, Item_ref *item) :Item_ident(thd, item), ref(item->ref) {}
enum Type type() const { return REF_ITEM; }
@@ -807,29 +868,34 @@ public:
{ return ref && (*ref)->eq(item, binary_cmp); }
double val()
{
+ DBUG_ASSERT(fixed);
double tmp=(*ref)->val_result();
null_value=(*ref)->null_value;
return tmp;
}
longlong val_int()
{
+ DBUG_ASSERT(fixed);
longlong tmp=(*ref)->val_int_result();
null_value=(*ref)->null_value;
return tmp;
}
String *val_str(String* tmp)
{
+ DBUG_ASSERT(fixed);
tmp=(*ref)->str_result(tmp);
null_value=(*ref)->null_value;
return tmp;
}
bool is_null()
{
+ DBUG_ASSERT(fixed);
(void) (*ref)->val_int_result();
return (*ref)->null_value;
}
bool get_date(TIME *ltime,uint fuzzydate)
{
+ DBUG_ASSERT(fixed);
return (null_value=(*ref)->get_date_result(ltime,fuzzydate));
}
bool send(Protocol *prot, String *tmp){ return (*ref)->send(prot, tmp); }
@@ -854,7 +920,52 @@ public:
void print(String *str);
};
+
+/*
+ The same as Item_ref, but get value from val_* family of method to get
+ value of item on which it referred instead of result* family.
+*/
+class Item_direct_ref :public Item_ref
+{
+public:
+ Item_direct_ref(Item **item, const char *table_name_par,
+ const char *field_name_par)
+ :Item_ref(item, table_name_par, field_name_par) {}
+ /* Constructor need to process subselect with temporary tables (see Item) */
+ Item_direct_ref(THD *thd, Item_direct_ref *item) : Item_ref(thd, item) {}
+
+ double val()
+ {
+ double tmp=(*ref)->val();
+ null_value=(*ref)->null_value;
+ return tmp;
+ }
+ longlong val_int()
+ {
+ longlong tmp=(*ref)->val_int();
+ null_value=(*ref)->null_value;
+ return tmp;
+ }
+ String *val_str(String* tmp)
+ {
+ tmp=(*ref)->val_str(tmp);
+ null_value=(*ref)->null_value;
+ return tmp;
+ }
+ bool is_null()
+ {
+ (void) (*ref)->val_int();
+ return (*ref)->null_value;
+ }
+ bool get_date(TIME *ltime,uint fuzzydate)
+ {
+ return (null_value=(*ref)->get_date(ltime,fuzzydate));
+ }
+};
+
+
class Item_in_subselect;
+
class Item_ref_null_helper: public Item_ref
{
protected:
@@ -876,9 +987,9 @@ class Item_null_helper :public Item_ref_null_helper
public:
Item_null_helper(Item_in_subselect* master, Item *item,
const char *table_name_par, const char *field_name_par)
- :Item_ref_null_helper(master, &store, table_name_par, field_name_par),
+ :Item_ref_null_helper(master, &item, table_name_par, field_name_par),
store(item)
- {}
+ { ref= &store; }
void print(String *str);
};
@@ -934,9 +1045,10 @@ public:
double val()
{
int err;
+ char *end_not_used;
return (null_value ? 0.0 :
my_strntod(str_value.charset(), (char*) str_value.ptr(),
- str_value.length(),NULL,&err));
+ str_value.length(), &end_not_used, &err));
}
longlong val_int()
{
@@ -1092,7 +1204,7 @@ class Item_cache_int: public Item_cache
{
longlong value;
public:
- Item_cache_int(): Item_cache() {}
+ Item_cache_int(): Item_cache(), value(0) {}
void store(Item *item);
double val() { DBUG_ASSERT(fixed == 1); return (double) value; }
@@ -1110,7 +1222,7 @@ class Item_cache_real: public Item_cache
{
double value;
public:
- Item_cache_real(): Item_cache() {}
+ Item_cache_real(): Item_cache(), value(0) {}
void store(Item *item);
double val() { DBUG_ASSERT(fixed == 1); return value; }
@@ -1132,7 +1244,7 @@ class Item_cache_str: public Item_cache
char buffer[80];
String *value, value_buff;
public:
- Item_cache_str(): Item_cache() { }
+ Item_cache_str(): Item_cache(), value(0) { }
void store(Item *item);
double val();
@@ -1214,14 +1326,14 @@ protected:
Item_result orig_type;
Field *field_example;
public:
- Item_type_holder(THD*, Item*);
+ Item_type_holder(THD*, Item*, TABLE *);
Item_result result_type () const { return item_type; }
enum Type type() const { return TYPE_HOLDER; }
double val();
longlong val_int();
String *val_str(String*);
- bool join_types(THD *thd, Item *);
+ bool join_types(THD *thd, Item *, TABLE *);
Field *example() { return field_example; }
static uint32 real_length(Item *item);
void cleanup()
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index c9396aaa67c..690da1be18c 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -106,7 +106,7 @@ longlong Item_func_not::val_int()
DBUG_ASSERT(fixed == 1);
double value=args[0]->val();
null_value=args[0]->null_value;
- return !null_value && value == 0 ? 1 : 0;
+ return ((!null_value && value == 0) ? 1 : 0);
}
/*
@@ -117,13 +117,23 @@ longlong Item_func_not_all::val_int()
{
DBUG_ASSERT(fixed == 1);
double value= args[0]->val();
- if (abort_on_null)
- {
- null_value= 0;
- return (args[0]->null_value || value == 0) ? 1 : 0;
- }
+
+ /*
+ return TRUE if there was records in underlaying select in max/min
+ optimisation (ALL subquery)
+ */
+ if (empty_underlying_subquery())
+ return 1;
+
null_value= args[0]->null_value;
- return (!null_value && value == 0) ? 1 : 0;
+ return ((!null_value && value == 0) ? 1 : 0);
+}
+
+
+bool Item_func_not_all::empty_underlying_subquery()
+{
+ return ((test_sum_item && !test_sum_item->any_value()) ||
+ (test_sub_item && !test_sub_item->any_value()));
}
void Item_func_not_all::print(String *str)
@@ -134,6 +144,30 @@ void Item_func_not_all::print(String *str)
args[0]->print(str);
}
+
+/*
+ Special NOP (No OPeration) for ALL subquery it is like Item_func_not_all
+ (return TRUE if underlaying sudquery do not return rows) but if subquery
+ returns some rows it return same value as argument (TRUE/FALSE).
+*/
+
+longlong Item_func_nop_all::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ double value= args[0]->val();
+
+ /*
+ return FALSE if there was records in underlaying select in max/min
+ optimisation (SAME/ANY subquery)
+ */
+ if (empty_underlying_subquery())
+ return 0;
+
+ null_value= args[0]->null_value;
+ return (null_value || value == 0) ? 0 : 1;
+}
+
+
/*
Convert a constant expression or string to an integer.
This is done when comparing DATE's of different formats and
@@ -173,64 +207,11 @@ void Item_bool_func2::fix_length_and_dec()
if (!args[0] || !args[1])
return;
- /*
- We allow to convert to Unicode character sets in some cases.
- The conditions when conversion is possible are:
- - arguments A and B have different charsets
- - A wins according to coercibility rules
- - character set of A is superset for character set of B
-
- If all of the above is true, then it's possible to convert
- B into the character set of A, and then compare according
- to the collation of A.
- */
-
- if (args[0] && args[1])
- {
- uint strong= 0;
- uint weak= 0;
- uint32 dummy_offset;
- DTCollation coll;
-
- if (args[0]->result_type() == STRING_RESULT &&
- args[1]->result_type() == STRING_RESULT &&
- String::needs_conversion(0, args[0]->collation.collation,
- args[1]->collation.collation,
- &dummy_offset) &&
- !coll.set(args[0]->collation, args[1]->collation, TRUE))
- {
- Item* conv= 0;
- Item_arena *arena= thd->current_arena, backup;
- strong= coll.strong;
- weak= strong ? 0 : 1;
- /*
- In case we're in statement prepare, create conversion item
- in its memory: it will be reused on each execute.
- */
- if (arena->is_stmt_prepare())
- thd->set_n_backup_item_arena(arena, &backup);
- if (args[weak]->type() == STRING_ITEM)
- {
- String tmp, cstr;
- String *ostr= args[weak]->val_str(&tmp);
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(),
- args[strong]->collation.collation);
- conv= new Item_string(cstr.ptr(),cstr.length(),cstr.charset(),
- args[weak]->collation.derivation);
- ((Item_string*)conv)->str_value.copy();
- }
- else
- {
- conv= new Item_func_conv_charset(args[weak],
- args[strong]->collation.collation);
- conv->collation.set(args[weak]->collation.derivation);
- conv->fix_fields(thd, 0, &conv);
- }
- if (arena->is_stmt_prepare())
- thd->restore_backup_item_arena(arena, &backup);
- args[weak]= conv ? conv : args[weak];
- }
- }
+ DTCollation coll;
+ if (args[0]->result_type() == STRING_RESULT &&
+ args[1]->result_type() == STRING_RESULT &&
+ agg_arg_charsets(coll, args, 2, MY_COLL_CMP_CONV))
+ return;
// Make a special case of compare with fields to get nicer DATE comparisons
@@ -284,7 +265,7 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type)
comparators= 0;
return 1;
}
- if (!(comparators= (Arg_comparator *) sql_alloc(sizeof(Arg_comparator)*n)))
+ if (!(comparators= new Arg_comparator[n]))
return 1;
for (uint i=0; i < n; i++)
{
@@ -788,7 +769,7 @@ void Item_func_interval::fix_length_and_dec()
maybe_null= 0;
max_length= 2;
used_tables_cache|= row->used_tables();
- not_null_tables_cache&= row->not_null_tables();
+ not_null_tables_cache= row->not_null_tables();
with_sum_func= with_sum_func || row->with_sum_func;
const_item_cache&= row->const_item();
}
@@ -846,7 +827,7 @@ void Item_func_between::fix_length_and_dec()
return;
agg_cmp_type(&cmp_type, args, 3);
if (cmp_type == STRING_RESULT &&
- agg_arg_collations_for_comparison(cmp_collation, args, 3))
+ agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV))
return;
/*
@@ -962,7 +943,7 @@ Item_func_ifnull::fix_length_and_dec()
decimals=max(args[0]->decimals,args[1]->decimals);
agg_result_type(&cached_result_type, args, 2);
if (cached_result_type == STRING_RESULT)
- agg_arg_collations(collation, args, arg_count);
+ agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV);
else if (cached_result_type != REAL_RESULT)
decimals= 0;
@@ -1058,7 +1039,7 @@ Item_func_if::fix_length_and_dec()
agg_result_type(&cached_result_type, args+1, 2);
if (cached_result_type == STRING_RESULT)
{
- if (agg_arg_collations(collation, args+1, 2))
+ if (agg_arg_charsets(collation, args+1, 2, MY_COLL_ALLOW_CONV))
return;
}
else
@@ -1112,6 +1093,9 @@ Item_func_nullif::fix_length_and_dec()
max_length=args[0]->max_length;
decimals=args[0]->decimals;
agg_result_type(&cached_result_type, args, 2);
+ if (cached_result_type == STRING_RESULT &&
+ agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV))
+ return;
}
}
@@ -1329,7 +1313,7 @@ void Item_func_case::fix_length_and_dec()
agg_result_type(&cached_result_type, agg, nagg);
if ((cached_result_type == STRING_RESULT) &&
- agg_arg_collations(collation, agg, nagg))
+ agg_arg_charsets(collation, agg, nagg, MY_COLL_ALLOW_CONV))
return;
@@ -1345,7 +1329,7 @@ void Item_func_case::fix_length_and_dec()
nagg++;
agg_cmp_type(&cmp_type, agg, nagg);
if ((cmp_type == STRING_RESULT) &&
- agg_arg_collations_for_comparison(cmp_collation, agg, nagg))
+ agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV))
return;
}
@@ -1452,7 +1436,7 @@ void Item_func_coalesce::fix_length_and_dec()
set_if_bigger(decimals,args[i]->decimals);
}
if (cached_result_type == STRING_RESULT)
- agg_arg_collations(collation, args, arg_count);
+ agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV);
else if (cached_result_type != REAL_RESULT)
decimals= 0;
}
@@ -1518,7 +1502,11 @@ void in_string::set(uint pos,Item *item)
String *str=((String*) base)+pos;
String *res=item->val_str(str);
if (res && res != str)
+ {
+ if (res->uses_buffer_owned_by(str))
+ res->copy();
*str= *res;
+ }
if (!str->charset())
{
CHARSET_INFO *cs;
@@ -1540,6 +1528,12 @@ in_row::in_row(uint elements, Item * item)
size= sizeof(cmp_item_row);
compare= (qsort2_cmp) cmp_row;
tmp.store_value(item);
+ /*
+ We need to reset these as otherwise we will call sort() with
+ uninitialized (even if not used) elements
+ */
+ used_count= elements;
+ collation= 0;
}
in_row::~in_row()
@@ -1764,63 +1758,13 @@ void Item_func_in::fix_length_and_dec()
agg_cmp_type(&cmp_type, args, arg_count);
+ if (cmp_type == STRING_RESULT &&
+ agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV))
+ return;
+
for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++)
const_itm&= arg[0]->const_item();
-
- if (cmp_type == STRING_RESULT)
- {
- /*
- We allow consts character set conversion for
-
- item IN (const1, const2, const3, ...)
-
- if item is in a superset for all arguments,
- and if it is a stong side according to coercibility rules.
-
- TODO: add covnersion for non-constant IN values
- via creating Item_func_conv_charset().
- */
-
- if (agg_arg_collations_for_comparison(cmp_collation,
- args, arg_count, TRUE))
- return;
- if ((!my_charset_same(args[0]->collation.collation,
- cmp_collation.collation) || !const_itm))
- {
- if (agg_arg_collations_for_comparison(cmp_collation,
- args, arg_count, FALSE))
- return;
- }
- else
- {
- /*
- Conversion is possible:
- All IN arguments are constants.
- */
- Item_arena *arena= thd->current_arena, backup;
- if (arena->is_stmt_prepare())
- thd->set_n_backup_item_arena(arena, &backup);
- for (arg= args+1, arg_end= args+arg_count; arg < arg_end; arg++)
- {
- if (!my_charset_same(cmp_collation.collation,
- arg[0]->collation.collation))
- {
- Item_string *conv;
- String tmp, cstr, *ostr= arg[0]->val_str(&tmp);
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(),
- cmp_collation.collation);
- conv= new Item_string(cstr.ptr(),cstr.length(), cstr.charset(),
- arg[0]->collation.derivation);
- conv->str_value.copy();
- arg[0]= conv;
- }
- }
- if (arena->is_stmt_prepare())
- thd->restore_backup_item_arena(arena, &backup);
- }
- }
-
/*
Row item with NULLs inside can return NULL or FALSE =>
they can't be processed as static
@@ -2025,30 +1969,33 @@ bool Item_cond::walk(Item_processor processor, byte *arg)
return Item_func::walk(processor, arg);
}
+
+/*
+ Move SUM items out from item tree and replace with reference
+
+ SYNOPSIS
+ split_sum_func()
+ thd Thread handler
+ ref_pointer_array Pointer to array of reference fields
+ fields All fields in select
+
+ NOTES
+ This function is run on all expression (SELECT list, WHERE, HAVING etc)
+ that have or refer (HAVING) to a SUM expression.
+
+ The split is done to get an unique item for each SUM function
+ so that we can easily find and calculate them.
+ (Calculation done by update_sum_func() and copy_sum_funcs() in
+ sql_select.cc)
+*/
+
void Item_cond::split_sum_func(THD *thd, Item **ref_pointer_array,
List<Item> &fields)
{
List_iterator<Item> li(list);
Item *item;
- used_tables_cache=0;
- const_item_cache=0;
- while ((item=li++))
- {
- if (item->with_sum_func && item->type() != SUM_FUNC_ITEM)
- item->split_sum_func(thd, ref_pointer_array, fields);
- else if (item->used_tables() || item->type() == SUM_FUNC_ITEM)
- {
- Item **ref= li.ref();
- uint el= fields.elements;
- Item *new_item= new Item_ref(ref_pointer_array + el, 0, item->name);
- fields.push_front(item);
- ref_pointer_array[el]= item;
- thd->change_item_tree(ref, new_item);
- }
- item->update_used_tables();
- used_tables_cache|=item->used_tables();
- const_item_cache&=item->const_item();
- }
+ while ((item= li++))
+ item->split_sum_func2(thd, ref_pointer_array, fields, li.ref());
}
@@ -2391,14 +2338,16 @@ bool
Item_func_regex::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
{
DBUG_ASSERT(fixed == 0);
- if (args[0]->fix_fields(thd, tables, args) || args[0]->check_cols(1) ||
- args[1]->fix_fields(thd,tables, args + 1) || args[1]->check_cols(1))
+ if ((!args[0]->fixed &&
+ args[0]->fix_fields(thd, tables, args)) || args[0]->check_cols(1) ||
+ (!args[1]->fixed &&
+ args[1]->fix_fields(thd,tables, args + 1)) || args[1]->check_cols(1))
return 1; /* purecov: inspected */
with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func;
max_length= 1;
decimals= 0;
- if (agg_arg_collations(cmp_collation, args, 2))
+ if (agg_arg_charsets(cmp_collation, args, 2, MY_COLL_CMP_CONV))
return 1;
used_tables_cache=args[0]->used_tables() | args[1]->used_tables();
@@ -2416,11 +2365,12 @@ Item_func_regex::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
return 0;
}
int error;
- if ((error=regcomp(&preg,res->c_ptr(),
- (cmp_collation.collation->state & MY_CS_BINSORT) ?
- REG_EXTENDED | REG_NOSUB :
- REG_EXTENDED | REG_NOSUB | REG_ICASE,
- cmp_collation.collation)))
+ if ((error= regcomp(&preg,res->c_ptr(),
+ ((cmp_collation.collation->state &
+ (MY_CS_BINSORT | MY_CS_CSSORT)) ?
+ REG_EXTENDED | REG_NOSUB :
+ REG_EXTENDED | REG_NOSUB | REG_ICASE),
+ cmp_collation.collation)))
{
(void) regerror(error,&preg,buff,sizeof(buff));
my_printf_error(ER_REGEXP_ERROR,ER(ER_REGEXP_ERROR),MYF(0),buff);
@@ -2468,10 +2418,11 @@ longlong Item_func_regex::val_int()
regex_compiled=0;
}
if (regcomp(&preg,res2->c_ptr(),
- (cmp_collation.collation->state & MY_CS_BINSORT) ?
- REG_EXTENDED | REG_NOSUB :
- REG_EXTENDED | REG_NOSUB | REG_ICASE,
- cmp_collation.collation))
+ ((cmp_collation.collation->state &
+ (MY_CS_BINSORT | MY_CS_CSSORT)) ?
+ REG_EXTENDED | REG_NOSUB :
+ REG_EXTENDED | REG_NOSUB | REG_ICASE),
+ cmp_collation.collation))
{
null_value=1;
return 0;
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 69528099aa1..061ed468b78 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -213,7 +213,7 @@ class Item_bool_rowready_func2 :public Item_bool_func2
public:
Item_bool_rowready_func2(Item *a, Item *b) :Item_bool_func2(a, b)
{
- allowed_arg_cols= a->cols();
+ allowed_arg_cols= 0; // Fetch this value from first argument
}
Item *neg_transformer(THD *thd);
virtual Item *negated_item();
@@ -229,21 +229,43 @@ public:
Item *neg_transformer(THD *thd);
};
+class Item_maxmin_subselect;
class Item_func_not_all :public Item_func_not
{
+ /* allow to check presence od values in max/min optimisation */
+ Item_sum_hybrid *test_sum_item;
+ Item_maxmin_subselect *test_sub_item;
+
bool abort_on_null;
public:
bool show;
- Item_func_not_all(Item *a) :Item_func_not(a), abort_on_null(0), show(0) {}
+ Item_func_not_all(Item *a)
+ :Item_func_not(a), test_sum_item(0), test_sub_item(0), abort_on_null(0),
+ show(0)
+ {}
virtual void top_level_item() { abort_on_null= 1; }
bool top_level() { return abort_on_null; }
longlong val_int();
enum Functype functype() const { return NOT_ALL_FUNC; }
const char *func_name() const { return "<not>"; }
void print(String *str);
+ void set_sum_test(Item_sum_hybrid *item) { test_sum_item= item; };
+ void set_sub_test(Item_maxmin_subselect *item) { test_sub_item= item; };
+ bool empty_underlying_subquery();
};
+
+class Item_func_nop_all :public Item_func_not_all
+{
+public:
+
+ Item_func_nop_all(Item *a) :Item_func_not_all(a) {}
+ longlong val_int();
+ const char *func_name() const { return "<nop>"; }
+};
+
+
class Item_func_eq :public Item_bool_rowready_func2
{
public:
@@ -368,7 +390,10 @@ class Item_func_interval :public Item_int_func
double *intervals;
public:
Item_func_interval(Item_row *a)
- :Item_int_func(a),row(a),intervals(0) { allowed_arg_cols= a->cols(); }
+ :Item_int_func(a),row(a),intervals(0)
+ {
+ allowed_arg_cols= 0; // Fetch this value from first argument
+ }
longlong val_int();
void fix_length_and_dec();
const char *func_name() const { return "interval"; }
@@ -721,7 +746,7 @@ class Item_func_in :public Item_int_func
Item_func_in(List<Item> &list)
:Item_int_func(list), array(0), in_item(0), have_null(0)
{
- allowed_arg_cols= args[0]->cols();
+ allowed_arg_cols= 0; // Fetch this value from first argument
}
longlong val_int();
void fix_length_and_dec();
diff --git a/sql/item_create.cc b/sql/item_create.cc
index c98c7892c26..d959a6f393a 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -365,25 +365,26 @@ Item *create_func_sin(Item* a)
Item *create_func_sha(Item* a)
{
- return new Item_func_sha(a);
+ return new Item_func_sha(a);
}
-
+
Item *create_func_space(Item *a)
{
CHARSET_INFO *cs= current_thd->variables.collation_connection;
Item *sp;
-
+
if (cs->mbminlen > 1)
{
+ uint dummy_errors;
sp= new Item_string("",0,cs);
if (sp)
- sp->str_value.copy(" ",1,&my_charset_latin1,cs);
+ sp->str_value.copy(" ", 1, &my_charset_latin1, cs, &dummy_errors);
}
else
{
sp= new Item_string(" ",1,cs);
}
- return new Item_func_repeat(sp, a);
+ return sp ? new Item_func_repeat(sp, a) : 0;
}
Item *create_func_soundex(Item* a)
diff --git a/sql/item_create.h b/sql/item_create.h
index 7577627ef04..faff6f45220 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -87,7 +87,7 @@ Item *create_func_soundex(Item* a);
Item *create_func_space(Item *);
Item *create_func_sqrt(Item* a);
Item *create_func_strcmp(Item* a, Item *b);
-Item *create_func_tan(Item* a);;
+Item *create_func_tan(Item* a);
Item *create_func_time_format(Item *a, Item *b);
Item *create_func_time_to_sec(Item* a);
Item *create_func_to_days(Item* a);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 17cf8642ce5..895740d2e5e 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -76,7 +76,7 @@ static void my_coll_agg_error(Item** args, uint count, const char *fname)
bool Item_func::agg_arg_collations(DTCollation &c, Item **av, uint count,
- bool allow_superset_conversion)
+ uint flags)
{
uint i;
c.nagg= 0;
@@ -84,29 +84,27 @@ bool Item_func::agg_arg_collations(DTCollation &c, Item **av, uint count,
c.set(av[0]->collation);
for (i= 1; i < count; i++)
{
- if (c.aggregate(av[i]->collation, allow_superset_conversion))
+ if (c.aggregate(av[i]->collation, flags))
{
my_coll_agg_error(av, count, func_name());
return TRUE;
}
}
+ if ((flags & MY_COLL_DISALLOW_NONE) &&
+ c.derivation == DERIVATION_NONE)
+ {
+ my_coll_agg_error(av, count, func_name());
+ return TRUE;
+ }
return FALSE;
}
bool Item_func::agg_arg_collations_for_comparison(DTCollation &c,
Item **av, uint count,
- bool allow_superset_conv)
+ uint flags)
{
- if (agg_arg_collations(c, av, count, allow_superset_conv))
- return TRUE;
-
- if (c.derivation == DERIVATION_NONE)
- {
- my_coll_agg_error(av, count, func_name());
- return TRUE;
- }
- return FALSE;
+ return (agg_arg_collations(c, av, count, flags | MY_COLL_DISALLOW_NONE));
}
@@ -119,6 +117,88 @@ eval_const_cond(COND *cond)
}
+
+/*
+ Collect arguments' character sets together.
+ We allow to apply automatic character set conversion in some cases.
+ The conditions when conversion is possible are:
+ - arguments A and B have different charsets
+ - A wins according to coercibility rules
+ (i.e. a column is stronger than a string constant,
+ an explicit COLLATE clause is stronger than a column)
+ - character set of A is either superset for character set of B,
+ or B is a string constant which can be converted into the
+ character set of A without data loss.
+
+ If all of the above is true, then it's possible to convert
+ B into the character set of A, and then compare according
+ to the collation of A.
+
+ For functions with more than two arguments:
+
+ collect(A,B,C) ::= collect(collect(A,B),C)
+*/
+
+bool Item_func::agg_arg_charsets(DTCollation &coll,
+ Item **args, uint nargs, uint flags)
+{
+ Item **arg, **last, *safe_args[2];
+ if (agg_arg_collations(coll, args, nargs, flags))
+ return TRUE;
+
+ /*
+ For better error reporting: save the first and the second argument.
+ We need this only if the the number of args is 3 or 2:
+ - for a longer argument list, "Illegal mix of collations"
+ doesn't display each argument's characteristics.
+ - if nargs is 1, then this error cannot happen.
+ */
+ if (nargs >=2 && nargs <= 3)
+ {
+ safe_args[0]= args[0];
+ safe_args[1]= args[1];
+ }
+
+ THD *thd= current_thd;
+ Item_arena *arena, backup;
+ bool res= FALSE;
+ /*
+ In case we're in statement prepare, create conversion item
+ in its memory: it will be reused on each execute.
+ */
+ arena= thd->change_arena_if_needed(&backup);
+
+ for (arg= args, last= args + nargs; arg < last; arg++)
+ {
+ Item* conv;
+ uint32 dummy_offset;
+ if (!String::needs_conversion(0, coll.collation,
+ (*arg)->collation.collation,
+ &dummy_offset))
+ continue;
+
+ if (!(conv= (*arg)->safe_charset_converter(coll.collation)))
+ {
+ if (nargs >=2 && nargs <= 3)
+ {
+ /* restore the original arguments for better error message */
+ args[0]= safe_args[0];
+ args[1]= safe_args[1];
+ }
+ my_coll_agg_error(args, nargs, func_name());
+ res= TRUE;
+ break; // we cannot return here, we need to restore "arena".
+ }
+ conv->fix_fields(thd, 0, &conv);
+ *arg= conv;
+ }
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ return res;
+}
+
+
+
void Item_func::set_arguments(List<Item> &list)
{
allowed_arg_cols= 1;
@@ -184,7 +264,7 @@ Item_func::Item_func(THD *thd, Item_func *item)
Sets as a side effect the following class variables:
maybe_null Set if any argument may return NULL
with_sum_func Set if any of the arguments contains a sum function
- used_table_cache Set to union of the arguments used table
+ used_tables_cache Set to union of the tables used by arguments
str_value.charset If this is a string function, set this to the
character set for the first argument.
@@ -223,10 +303,24 @@ Item_func::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
We can't yet set item to *arg as fix_fields may change *arg
We shouldn't call fix_fields() twice, so check 'fixed' field first
*/
- if ((!(*arg)->fixed && (*arg)->fix_fields(thd, tables, arg)) ||
- (*arg)->check_cols(allowed_arg_cols))
+ if ((!(*arg)->fixed && (*arg)->fix_fields(thd, tables, arg)))
return 1; /* purecov: inspected */
+
item= *arg;
+
+ if (allowed_arg_cols)
+ {
+ if (item->check_cols(allowed_arg_cols))
+ return 1;
+ }
+ else
+ {
+ /* we have to fetch allowed_arg_cols from first argument */
+ DBUG_ASSERT(arg == args); // it is first argument
+ allowed_arg_cols= item->cols();
+ DBUG_ASSERT(allowed_arg_cols); // Can't be 0 any more
+ }
+
if (item->maybe_null)
maybe_null=1;
@@ -257,24 +351,15 @@ bool Item_func::walk (Item_processor processor, byte *argument)
return (this->*processor)(argument);
}
+
+/* See comments in Item_cmp_func::split_sum_func() */
+
void Item_func::split_sum_func(THD *thd, Item **ref_pointer_array,
List<Item> &fields)
{
Item **arg, **arg_end;
for (arg= args, arg_end= args+arg_count; arg != arg_end ; arg++)
- {
- Item *item=* arg;
- if (item->with_sum_func && item->type() != SUM_FUNC_ITEM)
- item->split_sum_func(thd, ref_pointer_array, fields);
- else if (item->used_tables() || item->type() == SUM_FUNC_ITEM)
- {
- uint el= fields.elements;
- Item *new_item= new Item_ref(ref_pointer_array + el, 0, item->name);
- fields.push_front(item);
- ref_pointer_array[el]= item;
- thd->change_item_tree(arg, new_item);
- }
- }
+ (*arg)->split_sum_func2(thd, ref_pointer_array, fields, arg);
}
@@ -694,9 +779,25 @@ longlong Item_func_neg::val_int()
void Item_func_neg::fix_length_and_dec()
{
+ enum Item_result arg_result= args[0]->result_type();
+ enum Item::Type arg_type= args[0]->type();
decimals=args[0]->decimals;
max_length=args[0]->max_length;
hybrid_type= REAL_RESULT;
+
+ /*
+ We need to account for added '-' in the following cases:
+ A) argument is a real or integer positive constant - in this case
+ argument's max_length is set to actual number of bytes occupied, and not
+ maximum number of bytes real or integer may require. Note that all
+ constants are non negative so we don't need to account for removed '-'.
+ B) argument returns a string.
+ */
+ if (arg_result == STRING_RESULT ||
+ (arg_type == REAL_ITEM && ((Item_real*)args[0])->value >= 0) ||
+ (arg_type == INT_ITEM && ((Item_int*)args[0])->value > 0))
+ max_length++;
+
if (args[0]->result_type() == INT_RESULT)
{
/*
@@ -1010,7 +1111,8 @@ double Item_func_round::val()
bool Item_func_rand::fix_fields(THD *thd, struct st_table_list *tables,
Item **ref)
{
- Item_real_func::fix_fields(thd, tables, ref);
+ if (Item_real_func::fix_fields(thd, tables, ref))
+ return TRUE;
used_tables_cache|= RAND_TABLE_BIT;
if (arg_count)
{ // Only use argument once in query
@@ -1105,7 +1207,7 @@ void Item_func_min_max::fix_length_and_dec()
cmp_type=item_cmp_type(cmp_type,args[i]->result_type());
}
if (cmp_type == STRING_RESULT)
- agg_arg_collations_for_comparison(collation, args, arg_count);
+ agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV);
}
@@ -1259,7 +1361,7 @@ longlong Item_func_coercibility::val_int()
void Item_func_locate::fix_length_and_dec()
{
maybe_null=0; max_length=11;
- agg_arg_collations_for_comparison(cmp_collation, args, 2);
+ agg_arg_charsets(cmp_collation, args, 2, MY_COLL_CMP_CONV);
}
@@ -1358,7 +1460,7 @@ void Item_func_field::fix_length_and_dec()
for (uint i=1; i < arg_count ; i++)
cmp_type= item_cmp_type(cmp_type, args[i]->result_type());
if (cmp_type == STRING_RESULT)
- agg_arg_collations_for_comparison(cmp_collation, args, arg_count);
+ agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV);
}
@@ -1521,18 +1623,21 @@ longlong Item_func_bit_count::val_int()
udf_handler::~udf_handler()
{
- if (initialized)
+ if (!not_original)
{
- if (u_d->func_deinit != NULL)
+ if (initialized)
{
- void (*deinit)(UDF_INIT *) = (void (*)(UDF_INIT*))
- u_d->func_deinit;
- (*deinit)(&initid);
+ if (u_d->func_deinit != NULL)
+ {
+ void (*deinit)(UDF_INIT *) = (void (*)(UDF_INIT*))
+ u_d->func_deinit;
+ (*deinit)(&initid);
+ }
+ free_udf(u_d);
}
- free_udf(u_d);
+ if (buffers) // Because of bug in ecc
+ delete [] buffers;
}
- if (buffers) // Because of bug in ecc
- delete [] buffers;
}
@@ -1579,7 +1684,8 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func,
arg != arg_end ;
arg++,i++)
{
- if ((*arg)->fix_fields(thd, tables, arg))
+ if (!(*arg)->fixed &&
+ (*arg)->fix_fields(thd, tables, arg))
DBUG_RETURN(1);
// we can't assign 'item' before, because fix_fields() can change arg
Item *item= *arg;
@@ -1926,7 +2032,7 @@ void item_user_lock_release(User_level_lock *ull)
tmp.copy(command, strlen(command), tmp.charset());
tmp.append(ull->key,ull->key_length);
tmp.append("\")", 2);
- Query_log_event qev(current_thd, tmp.ptr(), tmp.length(),1);
+ Query_log_event qev(current_thd, tmp.ptr(), tmp.length(),1, FALSE);
qev.error_code=0; // this query is always safe to run on slave
mysql_bin_log.write(&qev);
}
@@ -2182,14 +2288,10 @@ longlong Item_func_last_insert_id::val_int()
longlong value=args[0]->val_int();
current_thd->insert_id(value);
null_value=args[0]->null_value;
- return value;
}
else
- {
- Item *it= get_system_var(current_thd, OPT_SESSION, "last_insert_id", 14,
- "last_insert_id()");
- return it->val_int();
- }
+ current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ return current_thd->insert_id();
}
/* This function is just used to test speed of different functions */
@@ -2259,6 +2361,7 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
entry->value=0;
entry->length=0;
entry->update_query_id=0;
+ entry->collation.set(NULL, DERIVATION_IMPLICIT);
/*
If we are here, we were called from a SET or a query which sets a
variable. Imagine it is this:
@@ -2300,7 +2403,24 @@ bool Item_func_set_user_var::fix_fields(THD *thd, TABLE_LIST *tables,
is different from query_id).
*/
entry->update_query_id= thd->query_id;
- entry->collation.set(args[0]->collation);
+ /*
+ As it is wrong and confusing to associate any
+ character set with NULL, @a should be latin2
+ after this query sequence:
+
+ SET @a=_latin2'string';
+ SET @a=NULL;
+
+ I.e. the second query should not change the charset
+ to the current default value, but should keep the
+ original value assigned during the first query.
+ In order to do it, we don't copy charset
+ from the argument if the argument is NULL
+ and the variable has previously been initialized.
+ */
+ if (!entry->collation.collation || !args[0]->null_value)
+ entry->collation.set(args[0]->collation.collation, DERIVATION_IMPLICIT);
+ collation.set(entry->collation.collation, DERIVATION_IMPLICIT);
cached_result_type= args[0]->result_type();
return 0;
}
@@ -2312,7 +2432,7 @@ Item_func_set_user_var::fix_length_and_dec()
maybe_null=args[0]->maybe_null;
max_length=args[0]->max_length;
decimals=args[0]->decimals;
- collation.set(args[0]->collation);
+ collation.set(args[0]->collation.collation, DERIVATION_IMPLICIT);
}
@@ -2328,7 +2448,6 @@ bool Item_func_set_user_var::update_hash(void *ptr, uint length,
my_free(entry->value,MYF(0));
entry->value=0;
entry->length=0;
- entry->collation.set(cs, dv);
}
else
{
@@ -2540,7 +2659,7 @@ Item_func_set_user_var::update()
res= update_hash((void*) save_result.vstr->ptr(),
save_result.vstr->length(), STRING_RESULT,
save_result.vstr->charset(),
- args[0]->collation.derivation);
+ DERIVATION_IMPLICIT);
break;
}
case ROW_RESULT:
@@ -2873,10 +2992,10 @@ void Item_func_match::init_search(bool no_order)
if (key == NO_SUCH_KEY)
{
List<Item> fields;
+ fields.push_back(new Item_string(" ",1, cmp_collation.collation));
for (uint i=1; i < arg_count; i++)
fields.push_back(args[i]);
- concat=new Item_func_concat_ws(new Item_string(" ",1,
- cmp_collation.collation), fields);
+ concat=new Item_func_concat_ws(fields);
/*
Above function used only to get value and do not need fix_fields for it:
Item_string - basic constant
@@ -2906,16 +3025,15 @@ void Item_func_match::init_search(bool no_order)
if (ft_tmp->charset() != cmp_collation.collation)
{
+ uint dummy_errors;
search_value.copy(ft_tmp->ptr(), ft_tmp->length(), ft_tmp->charset(),
- cmp_collation.collation);
+ cmp_collation.collation, &dummy_errors);
ft_tmp= &search_value;
}
if (join_key && !no_order)
flags|=FT_SORTED;
- ft_handler=table->file->ft_init_ext(flags, key,
- (byte*) ft_tmp->ptr(),
- ft_tmp->length());
+ ft_handler=table->file->ft_init_ext(flags, key, ft_tmp);
if (join_key)
table->file->ft_handler=ft_handler;
@@ -2957,12 +3075,12 @@ bool Item_func_match::fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref)
}
/*
Check that all columns come from the same table.
- We've already checked that columns in MATCH are fields so
+ We've already checked that columns in MATCH are fields so
PARAM_TABLE_BIT can only appear from AGAINST argument.
*/
if ((used_tables_cache & ~PARAM_TABLE_BIT) != item->used_tables())
key=NO_SUCH_KEY;
-
+
if (key == NO_SUCH_KEY && !(flags & FT_BOOL))
{
my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH");
diff --git a/sql/item_func.h b/sql/item_func.h
index 6e43d66a32d..2738c7419ca 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -32,6 +32,10 @@ class Item_func :public Item_result_field
{
protected:
Item **args, *tmp_arg[2];
+ /*
+ Allowed numbers of columns in result (usually 1, which means scalar value)
+ 0 means get this number from first argument
+ */
uint allowed_arg_cols;
public:
uint arg_count;
@@ -141,11 +145,12 @@ public:
Item *get_tmp_table_item(THD *thd);
bool agg_arg_collations(DTCollation &c, Item **items, uint nitems,
- bool allow_superset_conversion= FALSE);
+ uint flags= 0);
bool agg_arg_collations_for_comparison(DTCollation &c,
Item **items, uint nitems,
- bool allow_superset_comversion= FALSE);
-
+ uint flags= 0);
+ bool agg_arg_charsets(DTCollation &c, Item **items, uint nitems,
+ uint flags= 0);
bool walk(Item_processor processor, byte *arg);
};
@@ -823,8 +828,11 @@ public:
double val()
{
int err;
- String *res; res=val_str(&str_value);
- return res ? my_strntod(res->charset(),(char*) res->ptr(),res->length(),0,&err) : 0.0;
+ String *res;
+ char *end_not_used;
+ res=val_str(&str_value);
+ return res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(),
+ &end_not_used, &err) : 0.0;
}
longlong val_int()
{
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index 935925c1e83..2f00416bddf 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -24,7 +24,6 @@
#include "mysql_priv.h"
#ifdef HAVE_SPATIAL
-#include "sql_acl.h"
#include <m_ctype.h>
void Item_geometry_func::fix_length_and_dec()
@@ -148,9 +147,9 @@ String *Item_func_geometry_type::val_str(String *str)
swkb->length() - SRID_SIZE)))))
return 0;
/* String will not move */
- str->set(geom->get_class_info()->m_name.str,
- geom->get_class_info()->m_name.length,
- default_charset());
+ str->copy(geom->get_class_info()->m_name.str,
+ geom->get_class_info()->m_name.length,
+ default_charset());
return str;
}
diff --git a/sql/item_row.cc b/sql/item_row.cc
index 289efe45300..12d202a1699 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -84,25 +84,16 @@ bool Item_row::fix_fields(THD *thd, TABLE_LIST *tabl, Item **ref)
return 0;
}
+
void Item_row::split_sum_func(THD *thd, Item **ref_pointer_array,
List<Item> &fields)
{
Item **arg, **arg_end;
for (arg= items, arg_end= items+arg_count; arg != arg_end ; arg++)
- {
- if ((*arg)->with_sum_func && (*arg)->type() != SUM_FUNC_ITEM)
- (*arg)->split_sum_func(thd, ref_pointer_array, fields);
- else if ((*arg)->used_tables() || (*arg)->type() == SUM_FUNC_ITEM)
- {
- uint el= fields.elements;
- Item *new_item= new Item_ref(ref_pointer_array + el, 0, (*arg)->name);
- fields.push_front(*arg);
- ref_pointer_array[el]= *arg;
- thd->change_item_tree(arg, new_item);
- }
- }
+ (*arg)->split_sum_func2(thd, ref_pointer_array, fields, arg);
}
+
void Item_row::update_used_tables()
{
used_tables_cache= 0;
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index d78a3ac88a4..0023d7b1f20 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -25,7 +25,6 @@
#endif
#include "mysql_priv.h"
-#include "sql_acl.h"
#include <m_ctype.h>
#ifdef HAVE_OPENSSL
#include <openssl/des.h>
@@ -64,10 +63,11 @@ double Item_str_func::val()
DBUG_ASSERT(fixed == 1);
int err;
char buff[64];
+ char *end_not_used;
String *res, tmp(buff,sizeof(buff), &my_charset_bin);
res= val_str(&tmp);
return res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(),
- NULL, &err) : 0.0;
+ &end_not_used, &err) : 0.0;
}
longlong Item_str_func::val_int()
@@ -276,7 +276,8 @@ String *Item_func_concat::val_str(String *str)
current_thd->variables.max_allowed_packet);
goto null;
}
- if (res->alloced_length() >= res->length()+res2->length())
+ if (!args[0]->const_item() &&
+ res->alloced_length() >= res->length()+res2->length())
{ // Use old buffer
res->append(*res2);
}
@@ -346,7 +347,7 @@ void Item_func_concat::fix_length_and_dec()
{
max_length=0;
- if (agg_arg_collations(collation, args, arg_count))
+ if (agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV))
return;
for (uint i=0 ; i < arg_count ; i++)
@@ -532,7 +533,7 @@ String *Item_func_concat_ws::val_str(String *str)
uint i;
null_value=0;
- if (!(sep_str= separator->val_str(&tmp_sep_str)))
+ if (!(sep_str= args[0]->val_str(&tmp_sep_str)))
goto null;
use_as_buff= &tmp_value;
@@ -541,7 +542,7 @@ String *Item_func_concat_ws::val_str(String *str)
// Skip until non-null argument is found.
// If not, return the empty string
- for (i=0; i < arg_count; i++)
+ for (i=1; i < arg_count; i++)
if ((res= args[i]->val_str(str)))
break;
if (i == arg_count)
@@ -635,67 +636,30 @@ null:
return 0;
}
-void Item_func_concat_ws::split_sum_func(THD *thd, Item **ref_pointer_array,
- List<Item> &fields)
-{
- if (separator->with_sum_func && separator->type() != SUM_FUNC_ITEM)
- separator->split_sum_func(thd, ref_pointer_array, fields);
- else if (separator->used_tables() || separator->type() == SUM_FUNC_ITEM)
- {
- uint el= fields.elements;
- Item *new_item= new Item_ref(ref_pointer_array + el, 0, separator->name);
- fields.push_front(separator);
- ref_pointer_array[el]= separator;
- thd->change_item_tree(&separator, new_item);
- }
- Item_str_func::split_sum_func(thd, ref_pointer_array, fields);
-}
void Item_func_concat_ws::fix_length_and_dec()
{
- collation.set(separator->collation);
- max_length=separator->max_length*(arg_count-1);
- for (uint i=0 ; i < arg_count ; i++)
- {
- DTCollation tmp(collation.collation, collation.derivation);
+ max_length=0;
+
+ if (agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV))
+ return;
+
+ /*
+ arg_count cannot be less than 2,
+ it is done on parser level in sql_yacc.yy
+ so, (arg_count - 2) is safe here.
+ */
+ max_length= args[0]->max_length * (arg_count - 2);
+ for (uint i=1 ; i < arg_count ; i++)
max_length+=args[i]->max_length;
- if (collation.aggregate(args[i]->collation))
- {
- collation.set(tmp); // Restore the previous value
- my_coll_agg_error(collation, args[i]->collation, func_name());
- break;
- }
- }
+
if (max_length > MAX_BLOB_WIDTH)
{
max_length=MAX_BLOB_WIDTH;
maybe_null=1;
}
- used_tables_cache|= separator->used_tables();
- not_null_tables_cache&= separator->not_null_tables();
- const_item_cache&= separator->const_item();
- with_sum_func= with_sum_func || separator->with_sum_func;
-}
-
-void Item_func_concat_ws::update_used_tables()
-{
- Item_func::update_used_tables();
- separator->update_used_tables();
- used_tables_cache|=separator->used_tables();
- const_item_cache&=separator->const_item();
}
-void Item_func_concat_ws::print(String *str)
-{
- str->append("concat_ws(", 10);
- separator->print(str);
- if (arg_count)
- {
- str->append(',');
- print_args(str, 0);
- }
- str->append(')');
-}
String *Item_func_reverse::val_str(String *str)
{
@@ -885,7 +849,7 @@ void Item_func_replace::fix_length_and_dec()
maybe_null=1;
}
- if (agg_arg_collations_for_comparison(collation, args, 3))
+ if (agg_arg_charsets(collation, args, 3, MY_COLL_CMP_CONV))
return;
}
@@ -930,11 +894,13 @@ null:
void Item_func_insert::fix_length_and_dec()
{
- if (collation.set(args[0]->collation, args[3]->collation))
- {
- my_coll_agg_error(args[0]->collation, args[3]->collation, func_name());
- return;
- }
+ Item *cargs[2];
+ cargs[0]= args[0];
+ cargs[1]= args[3];
+ if (agg_arg_charsets(collation, cargs, 2, MY_COLL_ALLOW_CONV))
+ return;
+ args[0]= cargs[0];
+ args[3]= cargs[1];
max_length=args[0]->max_length+args[3]->max_length;
if (max_length > MAX_BLOB_WIDTH)
{
@@ -990,8 +956,9 @@ String *Item_func_left::val_str(String *str)
if (res->length() <= (uint) length ||
res->length() <= (char_pos= res->charpos(length)))
return res;
- str_value.set(*res, 0, char_pos);
- return &str_value;
+
+ tmp_value.set(*res, 0, char_pos);
+ return &tmp_value;
}
@@ -1100,7 +1067,7 @@ void Item_func_substr_index::fix_length_and_dec()
{
max_length= args[0]->max_length;
- if (agg_arg_collations_for_comparison(collation, args, 2))
+ if (agg_arg_charsets(collation, args, 2, MY_COLL_CMP_CONV))
return;
}
@@ -1337,9 +1304,18 @@ String *Item_func_trim::val_str(String *str)
return 0; /* purecov: inspected */
char buff[MAX_FIELD_WIDTH];
String tmp(buff,sizeof(buff),res->charset());
- String *remove_str= (arg_count==2) ? args[1]->val_str(&tmp) : &remove;
uint remove_length;
LINT_INIT(remove_length);
+ String *remove_str; /* The string to remove from res. */
+
+ if (arg_count == 2)
+ {
+ remove_str= args[1]->val_str(&tmp);
+ if ((null_value= args[1]->null_value))
+ return 0;
+ }
+ else
+ remove_str= &remove; /* Default value. */
if (!remove_str || (remove_length=remove_str->length()) == 0 ||
remove_length > res->length())
@@ -1392,10 +1368,14 @@ void Item_func_trim::fix_length_and_dec()
remove.set_ascii(" ",1);
}
else
- if (collation.set(args[1]->collation, args[0]->collation) ||
- collation.derivation == DERIVATION_NONE)
{
- my_coll_agg_error(args[1]->collation, args[0]->collation, func_name());
+ Item *cargs[2];
+ cargs[0]= args[1];
+ cargs[1]= args[0];
+ if (agg_arg_charsets(collation, cargs, 2, MY_COLL_CMP_CONV))
+ return;
+ args[0]= cargs[1];
+ args[1]= cargs[0];
}
}
@@ -1584,9 +1564,14 @@ void Item_func_soundex::fix_length_and_dec()
else return 0
*/
-static char get_scode(CHARSET_INFO *cs,char *ptr)
+static char soundex_toupper(char ch)
{
- uchar ch=my_toupper(cs,*ptr);
+ return (ch >= 'a' && ch <= 'z') ? ch - 'a' + 'A' : ch;
+}
+
+static char get_scode(char *ptr)
+{
+ uchar ch= soundex_toupper(*ptr);
if (ch < 'A' || ch > 'Z')
{
// Thread extended alfa (country spec)
@@ -1616,8 +1601,8 @@ String *Item_func_soundex::val_str(String *str)
from++; /* purecov: inspected */
if (from == end)
return &my_empty_string; // No alpha characters.
- *to++ = my_toupper(cs,*from); // Copy first letter
- last_ch = get_scode(cs,from); // code of the first letter
+ *to++ = soundex_toupper(*from); // Copy first letter
+ last_ch = get_scode(from); // code of the first letter
// for the first 'double-letter check.
// Loop on input letters until
// end of input (null) or output
@@ -1626,7 +1611,7 @@ String *Item_func_soundex::val_str(String *str)
{
if (!my_isalpha(cs,*from))
continue;
- ch=get_scode(cs,from);
+ ch=get_scode(from);
if ((ch != '0') && (ch != last_ch)) // if not skipped or double
{
*to++ = ch; // letter, copy to output
@@ -1661,7 +1646,8 @@ String *Item_func_format::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
double nr =args[0]->val();
- uint32 diff,length,str_length;
+ int diff;
+ uint32 length, str_length;
uint dec;
if ((null_value=args[0]->null_value))
return 0; /* purecov: inspected */
@@ -1686,9 +1672,12 @@ String *Item_func_format::val_str(String *str)
pos[0]= pos[-(int) diff];
while (diff)
{
- pos[0]=pos[-(int) diff]; pos--;
- pos[0]=pos[-(int) diff]; pos--;
- pos[0]=pos[-(int) diff]; pos--;
+ *pos= *(pos - diff);
+ pos--;
+ *pos= *(pos - diff);
+ pos--;
+ *pos= *(pos - diff);
+ pos--;
pos[0]=',';
pos--;
diff--;
@@ -1716,7 +1705,7 @@ void Item_func_elt::fix_length_and_dec()
max_length=0;
decimals=0;
- if (agg_arg_collations(collation, args+1, arg_count-1))
+ if (agg_arg_charsets(collation, args+1, arg_count-1, MY_COLL_ALLOW_CONV))
return;
for (uint i= 1 ; i < arg_count ; i++)
@@ -1774,16 +1763,7 @@ String *Item_func_elt::val_str(String *str)
void Item_func_make_set::split_sum_func(THD *thd, Item **ref_pointer_array,
List<Item> &fields)
{
- if (item->with_sum_func && item->type() != SUM_FUNC_ITEM)
- item->split_sum_func(thd, ref_pointer_array, fields);
- else if (item->used_tables() || item->type() == SUM_FUNC_ITEM)
- {
- uint el= fields.elements;
- Item *new_item= new Item_ref(ref_pointer_array + el, 0, item->name);
- fields.push_front(item);
- ref_pointer_array[el]= item;
- thd->change_item_tree(&item, new_item);
- }
+ item->split_sum_func2(thd, ref_pointer_array, fields, &item);
Item_str_func::split_sum_func(thd, ref_pointer_array, fields);
}
@@ -1792,7 +1772,7 @@ void Item_func_make_set::fix_length_and_dec()
{
max_length=arg_count-1;
- if (agg_arg_collations(collation, args, arg_count))
+ if (agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV))
return;
for (uint i=0 ; i < arg_count ; i++)
@@ -1888,6 +1868,7 @@ String *Item_func_char::val_str(String *str)
{
int32 num=(int32) args[i]->val_int();
if (!args[i]->null_value)
+ {
#ifdef USE_MB
if (use_mb(collation.collation))
{
@@ -1903,7 +1884,9 @@ b1: str->append((char)(num>>8));
}
#endif
str->append((char)num);
+ }
}
+ str->set_charset(collation.collation);
str->realloc(str->length()); // Add end 0 (for Purify)
return str;
}
@@ -2000,12 +1983,13 @@ err:
void Item_func_rpad::fix_length_and_dec()
{
- if (collation.set(args[0]->collation, args[2]->collation))
- {
- my_coll_agg_error(args[0]->collation, args[2]->collation, func_name());
+ Item *cargs[2];
+ cargs[0]= args[0];
+ cargs[1]= args[2];
+ if (agg_arg_charsets(collation, cargs, 2, MY_COLL_ALLOW_CONV))
return;
- }
-
+ args[0]= cargs[0];
+ args[2]= cargs[1];
if (args[1]->const_item())
{
uint32 length= (uint32) args[1]->val_int() * collation.collation->mbmaxlen;
@@ -2084,11 +2068,13 @@ String *Item_func_rpad::val_str(String *str)
void Item_func_lpad::fix_length_and_dec()
{
- if (collation.set(args[0]->collation, args[2]->collation))
- {
- my_coll_agg_error(args[0]->collation, args[2]->collation, func_name());
+ Item *cargs[2];
+ cargs[0]= args[0];
+ cargs[1]= args[2];
+ if (agg_arg_charsets(collation, cargs, 2, MY_COLL_ALLOW_CONV))
return;
- }
+ args[0]= cargs[0];
+ args[2]= cargs[1];
if (args[1]->const_item())
{
@@ -2182,6 +2168,7 @@ String *Item_func_conv::val_str(String *str)
return 0;
}
null_value=0;
+ unsigned_flag= !(from_base < 0);
if (from_base < 0)
dec= my_strntoll(res->charset(),res->ptr(),res->length(),-from_base,&endptr,&err);
else
@@ -2197,13 +2184,14 @@ String *Item_func_conv_charset::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
String *arg= args[0]->val_str(str);
+ uint dummy_errors;
if (!arg)
{
null_value=1;
return 0;
}
null_value= str_value.copy(arg->ptr(),arg->length(),arg->charset(),
- conv_charset);
+ conv_charset, &dummy_errors);
return null_value ? 0 : &str_value;
}
@@ -2286,11 +2274,12 @@ String *Item_func_charset::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
+ uint dummy_errors;
if ((null_value=(args[0]->null_value || !res->charset())))
return 0;
str->copy(res->charset()->csname,strlen(res->charset()->csname),
- &my_charset_latin1, collation.collation);
+ &my_charset_latin1, collation.collation, &dummy_errors);
return str;
}
@@ -2298,11 +2287,12 @@ String *Item_func_collation::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
+ uint dummy_errors;
if ((null_value=(args[0]->null_value || !res->charset())))
return 0;
str->copy(res->charset()->name,strlen(res->charset()->name),
- &my_charset_latin1, collation.collation);
+ &my_charset_latin1, collation.collation, &dummy_errors);
return str;
}
@@ -2345,17 +2335,6 @@ String *Item_func_hex::val_str(String *str)
return &tmp_value;
}
-inline int hexchar_to_int(char c)
-{
- if (c <= '9' && c >= '0')
- return c-'0';
- c|=32;
- if (c <= 'f' && c >= 'a')
- return c-'a'+10;
- return -1;
-}
-
-
/* Convert given hex string to a binary string */
String *Item_func_unhex::val_str(String *str)
@@ -2508,6 +2487,9 @@ String* Item_func_export_set::val_str(String* str)
case 3:
sep_buf.set(",", 1, default_charset());
sep = &sep_buf;
+ break;
+ default:
+ DBUG_ASSERT(0); // cannot happen
}
null_value=0;
@@ -2529,7 +2511,8 @@ void Item_func_export_set::fix_length_and_dec()
uint sep_length=(arg_count > 3 ? args[3]->max_length : 1);
max_length=length*64+sep_length*63;
- if (agg_arg_collations(collation, args+1, min(4,arg_count)-1))
+ if (agg_arg_charsets(collation, args+1, min(4,arg_count)-1),
+ MY_COLL_ALLOW_CONV)
return;
}
@@ -2586,9 +2569,12 @@ String* Item_func_inet_ntoa::val_str(String* str)
This function is very useful when you want to generate SQL statements
- RETURN VALUES
+ NOTE
+ QUOTE(NULL) returns the string 'NULL' (4 letters, without quotes).
+
+ RETURN VALUES
str Quoted string
- NULL Argument to QUOTE() was NULL or out of memory.
+ NULL Out of memory.
*/
#define get_esc_bit(mask, num) (1 & (*((mask) + ((num) >> 3))) >> ((num) & 7))
@@ -2613,25 +2599,25 @@ String *Item_func_quote::val_str(String *str)
String *arg= args[0]->val_str(str);
uint arg_length, new_length;
if (!arg) // Null argument
- goto null;
+ {
+ str->copy("NULL", 4, collation.collation); // Return the string 'NULL'
+ null_value= 0;
+ return str;
+ }
+
arg_length= arg->length();
new_length= arg_length+2; /* for beginning and ending ' signs */
for (from= (char*) arg->ptr(), end= from + arg_length; from < end; from++)
new_length+= get_esc_bit(escmask, (uchar) *from);
- /*
- We have to use realloc() instead of alloc() as we want to keep the
- old result in str
- */
- if (str->realloc(new_length))
+ if (tmp_value.alloc(new_length))
goto null;
/*
- As 'arg' and 'str' may be the same string, we must replace characters
- from the end to the beginning
+ We replace characters from the end to the beginning
*/
- to= (char*) str->ptr() + new_length - 1;
+ to= (char*) tmp_value.ptr() + new_length - 1;
*to--= '\'';
for (start= (char*) arg->ptr(),end= start + arg_length; end-- != start; to--)
{
@@ -2659,10 +2645,10 @@ String *Item_func_quote::val_str(String *str)
}
}
*to= '\'';
- str->length(new_length);
- str->set_charset(collation.collation);
+ tmp_value.length(new_length);
+ tmp_value.set_charset(collation.collation);
null_value= 0;
- return str;
+ return &tmp_value;
null:
null_value= 1;
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 77c1caec9fc..c1c0969672c 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -89,30 +89,12 @@ public:
class Item_func_concat_ws :public Item_str_func
{
- Item *separator;
String tmp_value;
-
public:
- Item_func_concat_ws(Item *a,List<Item> &list)
- :Item_str_func(list),separator(a) {}
+ Item_func_concat_ws(List<Item> &list) :Item_str_func(list) {}
String *val_str(String *);
void fix_length_and_dec();
- void update_used_tables();
- bool fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref)
- {
- DBUG_ASSERT(fixed == 0);
- return (separator->fix_fields(thd, tlist, &separator) ||
- separator->check_cols(1) ||
- Item_func::fix_fields(thd, tlist, ref));
- }
- void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields);
const char *func_name() const { return "concat_ws"; }
- bool walk(Item_processor processor, byte *arg)
- {
- return separator->walk(processor, arg) ||
- Item_str_func::walk(processor, arg);
- }
- void print(String *str);
};
class Item_func_reverse :public Item_str_func
@@ -180,6 +162,7 @@ public:
class Item_func_left :public Item_str_func
{
+ String tmp_value;
public:
Item_func_left(Item *a,Item *b) :Item_str_func(a,b) {}
String *val_str(String *);
@@ -414,7 +397,8 @@ public:
bool fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref)
{
DBUG_ASSERT(fixed == 0);
- return (item->fix_fields(thd, tlist, &item) ||
+ return (!item->fixed &&
+ item->fix_fields(thd, tlist, &item) ||
item->check_cols(1) ||
Item_func::fix_fields(thd, tlist, ref));
}
@@ -604,6 +588,7 @@ public:
class Item_func_quote :public Item_str_func
{
+ String tmp_value;
public:
Item_func_quote(Item *a) :Item_str_func(a) {}
const char *func_name() const { return "quote"; }
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index a869e2d24fb..3a1e1918e55 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -155,6 +155,8 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref)
// did we changed top item of WHERE condition
if (unit->outer_select()->where == (*ref))
unit->outer_select()->where= substitution; // correct WHERE for PS
+ else if (unit->outer_select()->having == (*ref))
+ unit->outer_select()->having= substitution; // correct HAVING for PS
(*ref)= substitution;
substitution->name= name;
@@ -164,12 +166,7 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref)
thd->where= "checking transformed subquery";
if (!(*ref)->fixed)
ret= (*ref)->fix_fields(thd, tables, ref);
- // We can't substitute aggregate functions like "SELECT (max(i))"
- if (substype() == SINGLEROW_SUBS && (*ref)->with_sum_func)
- {
- my_error(ER_INVALID_GROUP_FUNC_USE, MYF(0));
- return 1;
- }
+ thd->where= save_where;
return ret;
}
// Is it one field subselect?
@@ -180,6 +177,8 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref)
}
fix_length_and_dec();
}
+ else
+ return 1;
uint8 uncacheable= engine->uncacheable();
if (uncacheable)
{
@@ -195,15 +194,16 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref)
bool Item_subselect::exec()
{
int res;
- MEM_ROOT *old_root= my_pthread_getspecific_ptr(MEM_ROOT*, THR_MALLOC);
- if (&thd->mem_root != old_root)
- {
- my_pthread_setspecific_ptr(THR_MALLOC, &thd->mem_root);
- res= engine->exec();
- my_pthread_setspecific_ptr(THR_MALLOC, old_root);
- }
- else
- res= engine->exec();
+ MEM_ROOT *old_root= thd->mem_root;
+
+ /*
+ As this is execution, all objects should be allocated through the main
+ mem root
+ */
+ thd->mem_root= &thd->main_mem_root;
+ res= engine->exec();
+ thd->mem_root= old_root;
+
if (engine_changed)
{
engine_changed= 0;
@@ -266,7 +266,6 @@ Item_singlerow_subselect::Item_singlerow_subselect(st_select_lex *select_lex)
{
DBUG_ENTER("Item_singlerow_subselect::Item_singlerow_subselect");
init(select_lex, new select_singlerow_subselect(this));
- max_columns= 1;
maybe_null= 1;
max_columns= UINT_MAX;
DBUG_VOID_RETURN;
@@ -275,7 +274,7 @@ Item_singlerow_subselect::Item_singlerow_subselect(st_select_lex *select_lex)
Item_maxmin_subselect::Item_maxmin_subselect(Item_subselect *parent,
st_select_lex *select_lex,
bool max_arg)
- :Item_singlerow_subselect()
+ :Item_singlerow_subselect(), was_values(TRUE)
{
DBUG_ENTER("Item_maxmin_subselect::Item_maxmin_subselect");
max= max_arg;
@@ -294,12 +293,31 @@ Item_maxmin_subselect::Item_maxmin_subselect(Item_subselect *parent,
DBUG_VOID_RETURN;
}
+void Item_maxmin_subselect::cleanup()
+{
+ DBUG_ENTER("Item_maxmin_subselect::cleanup");
+ Item_singlerow_subselect::cleanup();
+
+ /*
+ By default it is TRUE to avoid TRUE reporting by
+ Item_func_not_all/Item_func_nop_all if this item was never called.
+
+ Engine exec() set it to FALSE by reset_value_registration() call.
+ select_max_min_finder_subselect::send_data() set it back to TRUE if some
+ value will be found.
+ */
+ was_values= TRUE;
+ DBUG_VOID_RETURN;
+}
+
+
void Item_maxmin_subselect::print(String *str)
{
str->append(max?"<max>":"<min>", 5);
Item_singlerow_subselect::print(str);
}
+
void Item_singlerow_subselect::reset()
{
null_value= 1;
@@ -307,6 +325,7 @@ void Item_singlerow_subselect::reset()
value->null_value= 1;
}
+
Item_subselect::trans_res
Item_singlerow_subselect::select_transformer(JOIN *join)
{
@@ -317,11 +336,11 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
/* Juggle with current arena only if we're in prepared statement prepare */
Item_arena *arena= join->thd->current_arena;
- Item_arena backup;
if (!select_lex->master_unit()->first_select()->next_select() &&
!select_lex->table_list.elements &&
select_lex->item_list.elements == 1 &&
+ !select_lex->item_list.head()->with_sum_func &&
/*
We cant change name of Item_field or Item_ref, because it will
prevent it's correct resolving, but we should save name of
@@ -330,7 +349,13 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
TODO: solve above problem
*/
!(select_lex->item_list.head()->type() == FIELD_ITEM ||
- select_lex->item_list.head()->type() == REF_ITEM)
+ select_lex->item_list.head()->type() == REF_ITEM) &&
+ /*
+ switch off this optimisation for prepare statement,
+ because we do not rollback this changes
+ TODO: make rollback for it, or special name resolving mode in 5.0.
+ */
+ !arena->is_stmt_prepare()
)
{
@@ -352,9 +377,6 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
if (join->conds || join->having)
{
Item *cond;
- if (arena->is_stmt_prepare())
- thd->set_n_backup_item_arena(arena, &backup);
-
if (!join->having)
cond= join->conds;
else if (!join->conds)
@@ -365,16 +387,12 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
if (!(substitution= new Item_func_if(cond, substitution,
new Item_null())))
goto err;
- if (arena->is_stmt_prepare())
- thd->restore_backup_item_arena(arena, &backup);
- }
+ }
return RES_REDUCE;
}
return RES_OK;
err:
- if (arena->is_stmt_prepare())
- thd->restore_backup_item_arena(arena, &backup);
return RES_ERROR;
}
@@ -401,6 +419,13 @@ void Item_singlerow_subselect::fix_length_and_dec()
engine->fix_length_and_dec(row);
value= *row;
}
+ /*
+ If there are not tables in subquery then ability to have NULL value
+ depends on SELECT list (if single row subquery have tables then it
+ always can be NULL if there are not records fetched).
+ */
+ if (engine->no_tables())
+ maybe_null= engine->may_be_null();
}
uint Item_singlerow_subselect::cols()
@@ -517,7 +542,7 @@ bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit)
Item_in_subselect::Item_in_subselect(Item * left_exp,
st_select_lex *select_lex):
- Item_exists_subselect(), transformed(0), upper_not(0)
+ Item_exists_subselect(), transformed(0), upper_item(0)
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
left_expr= left_exp;
@@ -644,6 +669,7 @@ Item_subselect::trans_res
Item_in_subselect::single_value_transformer(JOIN *join,
Comp_creator *func)
{
+ const char *save_where= thd->where;
DBUG_ENTER("Item_in_subselect::single_value_transformer");
if (changed)
@@ -652,11 +678,9 @@ Item_in_subselect::single_value_transformer(JOIN *join,
}
SELECT_LEX *select_lex= join->select_lex;
- Item_arena *arena= join->thd->current_arena, backup;
-
+ Item_arena *arena, backup;
+ arena= thd->change_arena_if_needed(&backup);
thd->where= "scalar IN/ALL/ANY subquery";
- if (arena->is_stmt_prepare())
- thd->set_n_backup_item_arena(arena, &backup);
/*
Check that the right part of the subselect contains no more than one
@@ -679,7 +703,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
NULL/IS NOT NULL functions). If so, we rewrite ALL/ANY with NOT EXISTS
later in this method.
*/
- if ((abort_on_null || (upper_not && upper_not->top_level())) &&
+ if ((abort_on_null || (upper_item && upper_item->top_level())) &&
!select_lex->master_unit()->uncacheable && !func->eqne_op())
{
if (substitution)
@@ -693,7 +717,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
!select_lex->with_sum_func &&
!(select_lex->next_select()))
{
- Item *item;
+ Item_sum_hybrid *item;
if (func->l_op())
{
/*
@@ -710,6 +734,8 @@ Item_in_subselect::single_value_transformer(JOIN *join,
*/
item= new Item_sum_min(*select_lex->ref_pointer_array);
}
+ if (upper_item)
+ upper_item->set_sum_test(item);
*select_lex->ref_pointer_array= item;
{
List_iterator<Item> it(select_lex->item_list);
@@ -730,15 +756,19 @@ Item_in_subselect::single_value_transformer(JOIN *join,
}
else
{
+ Item_maxmin_subselect *item;
// remove LIMIT placed by ALL/ANY subquery
select_lex->master_unit()->global_parameters->select_limit=
HA_POS_ERROR;
- subs= new Item_maxmin_subselect(this, select_lex, func->l_op());
+ subs= item= new Item_maxmin_subselect(this, select_lex, func->l_op());
+ if (upper_item)
+ upper_item->set_sub_test(item);
}
// left expression belong to outer select
SELECT_LEX *current= thd->lex->current_select, *up;
thd->lex->current_select= up= current->return_after_parsing();
- if (left_expr->fix_fields(thd, up->get_table_list(), &left_expr))
+ if (!left_expr->fixed &&
+ left_expr->fix_fields(thd, up->get_table_list(), &left_expr))
{
thd->lex->current_select= current;
goto err;
@@ -769,9 +799,9 @@ Item_in_subselect::single_value_transformer(JOIN *join,
As far as Item_ref_in_optimizer do not substitude itself on fix_fields
we can use same item for all selects.
*/
- expr= new Item_ref((Item**)optimizer->get_cache(),
- (char *)"<no matter>",
- (char *)in_left_expr_name);
+ expr= new Item_direct_ref((Item**)optimizer->get_cache(),
+ (char *)"<no matter>",
+ (char *)in_left_expr_name);
unit->uncacheable|= UNCACHEABLE_DEPENDENT;
}
@@ -862,7 +892,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
*/
select_lex->having=
join->having=
- func->create(expr,
+ func->create(expr,
new Item_null_helper(this, item,
(char *)"<no matter>",
(char *)"<result>"));
@@ -889,7 +919,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_SELECT_REDUCED, warn_buff);
}
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(RES_REDUCE);
}
@@ -897,12 +927,13 @@ Item_in_subselect::single_value_transformer(JOIN *join,
}
ok:
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
+ thd->where= save_where;
DBUG_RETURN(RES_OK);
err:
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(RES_ERROR);
}
@@ -911,20 +942,19 @@ err:
Item_subselect::trans_res
Item_in_subselect::row_value_transformer(JOIN *join)
{
+ const char *save_where= thd->where;
DBUG_ENTER("Item_in_subselect::row_value_transformer");
if (changed)
{
DBUG_RETURN(RES_OK);
}
- Item_arena *arena= join->thd->current_arena, backup;
+ Item_arena *arena, backup;
Item *item= 0;
+ SELECT_LEX *select_lex= join->select_lex;
thd->where= "row IN/ALL/ANY subquery";
- if (arena->is_stmt_prepare())
- thd->set_n_backup_item_arena(arena, &backup);
-
- SELECT_LEX *select_lex= join->select_lex;
+ arena= thd->change_arena_if_needed(&backup);
if (select_lex->item_list.elements != left_expr->cols())
{
@@ -960,14 +990,19 @@ Item_in_subselect::row_value_transformer(JOIN *join)
List_iterator_fast<Item> li(select_lex->item_list);
for (uint i= 0; i < n; i++)
{
+ DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed);
+ if (select_lex->ref_pointer_array[i]->
+ check_cols(left_expr->el(i)->cols()))
+ goto err;
Item *func= new Item_ref_null_helper(this,
select_lex->ref_pointer_array+i,
(char *) "<no matter>",
(char *) "<list ref>");
func=
- eq_creator.create(new Item_ref((*optimizer->get_cache())->addr(i),
- (char *)"<no matter>",
- (char *)in_left_expr_name),
+ eq_creator.create(new Item_direct_ref((*optimizer->get_cache())->
+ addr(i),
+ (char *)"<no matter>",
+ (char *)in_left_expr_name),
func);
item= and_items(item, func);
}
@@ -1001,12 +1036,13 @@ Item_in_subselect::row_value_transformer(JOIN *join)
if (join->conds->fix_fields(thd, join->tables_list, 0))
goto err;
}
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
+ thd->where= save_where;
DBUG_RETURN(RES_OK);
err:
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(RES_ERROR);
}
@@ -1039,8 +1075,8 @@ Item_subselect::trans_res
Item_allany_subselect::select_transformer(JOIN *join)
{
transformed= 1;
- if (upper_not)
- upper_not->show= 1;
+ if (upper_item)
+ upper_item->show= 1;
return single_value_transformer(join, func);
}
@@ -1086,6 +1122,7 @@ void subselect_single_select_engine::cleanup()
DBUG_ENTER("subselect_single_select_engine::cleanup");
prepared= optimized= executed= 0;
join= 0;
+ result->cleanup();
DBUG_VOID_RETURN;
}
@@ -1094,6 +1131,7 @@ void subselect_union_engine::cleanup()
{
DBUG_ENTER("subselect_union_engine::cleanup");
unit->reinit_exec_mechanism();
+ result->cleanup();
DBUG_VOID_RETURN;
}
@@ -1101,6 +1139,10 @@ void subselect_union_engine::cleanup()
void subselect_uniquesubquery_engine::cleanup()
{
DBUG_ENTER("subselect_uniquesubquery_engine::cleanup");
+ /*
+ subselect_uniquesubquery_engine have not 'result' assigbed, so we do not
+ cleanup() it
+ */
DBUG_VOID_RETURN;
}
@@ -1245,6 +1287,7 @@ int subselect_single_select_engine::exec()
}
if (!executed)
{
+ item->reset_value_registration();
join->exec();
executed= 1;
join->thd->where= save_where;
@@ -1383,13 +1426,15 @@ int subselect_indexsubquery_engine::exec()
uint subselect_single_select_engine::cols()
{
- return select_lex->item_list.elements;
+ DBUG_ASSERT(select_lex->join); // should be called after fix_fields()
+ return select_lex->join->fields_list.elements;
}
uint subselect_union_engine::cols()
{
- return unit->first_select()->item_list.elements;
+ DBUG_ASSERT(unit->is_prepared()); // should be called after fix_fields()
+ return unit->types.elements;
}
@@ -1562,3 +1607,58 @@ int subselect_uniquesubquery_engine::change_item(Item_subselect *si,
DBUG_ASSERT(0);
return -1;
}
+
+
+/*
+ Report about presence of tables in subquery
+
+ SINOPSYS
+ subselect_single_select_engine::no_tables()
+
+ RETURN
+ TRUE there are not tables used in subquery
+ FALSE there are some tables in subquery
+*/
+bool subselect_single_select_engine::no_tables()
+{
+ return(select_lex->table_list.elements == 0);
+}
+
+
+/*
+ Report about presence of tables in subquery
+
+ SINOPSYS
+ subselect_union_engine::no_tables()
+
+ RETURN
+ TRUE there are not tables used in subquery
+ FALSE there are some tables in subquery
+*/
+bool subselect_union_engine::no_tables()
+{
+ for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
+ {
+ if (sl->table_list.elements)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*
+ Report about presence of tables in subquery
+
+ SINOPSYS
+ subselect_uniquesubquery_engine::no_tables()
+
+ RETURN
+ TRUE there are not tables used in subquery
+ FALSE there are some tables in subquery
+*/
+
+bool subselect_uniquesubquery_engine::no_tables()
+{
+ /* returning value is correct, but this method should never be called */
+ return 0;
+}
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index f570d89f28f..ab2d441ed7a 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -93,7 +93,7 @@ public:
return null_value;
}
bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref);
- bool exec();
+ virtual bool exec();
virtual void fix_length_and_dec();
table_map used_tables() const;
bool const_item() const;
@@ -109,6 +109,11 @@ public:
engine_changed= 1;
return eng == 0;
}
+ /*
+ Used by max/min subquery to initialize value presence registration
+ mechanism. Engine call this method before rexecution query.
+ */
+ virtual void reset_value_registration() {}
friend class select_subselect;
friend class Item_in_optimizer;
@@ -150,13 +155,20 @@ public:
};
/* used in static ALL/ANY optimisation */
+class select_max_min_finder_subselect;
class Item_maxmin_subselect :public Item_singlerow_subselect
{
+protected:
bool max;
+ bool was_values; // Set if we have found at least one row
public:
Item_maxmin_subselect(Item_subselect *parent,
st_select_lex *select_lex, bool max);
void print(String *str);
+ void cleanup();
+ bool any_value() { return was_values; }
+ void register_value() { was_values= TRUE; }
+ void reset_value_registration() { was_values= FALSE; }
};
/* exists subselect */
@@ -204,11 +216,11 @@ protected:
bool abort_on_null;
bool transformed;
public:
- Item_func_not_all *upper_not; // point on NOT before ALL subquery
+ Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery
Item_in_subselect(Item * left_expr, st_select_lex *select_lex);
Item_in_subselect()
- :Item_exists_subselect(), abort_on_null(0), transformed(0), upper_not(0)
+ :Item_exists_subselect(), abort_on_null(0), transformed(0), upper_item(0)
{}
@@ -249,7 +261,7 @@ public:
st_select_lex *select_lex, bool all);
// only ALL subquery has upper not
- subs_type substype() { return upper_not?ALL_SUBS:ANY_SUBS; }
+ subs_type substype() { return all?ALL_SUBS:ANY_SUBS; }
trans_res select_transformer(JOIN *join);
void print(String *str);
};
@@ -291,6 +303,7 @@ public:
static table_map calc_const_tables(TABLE_LIST *);
virtual void print(String *str)= 0;
virtual int change_item(Item_subselect *si, select_subselect *result)= 0;
+ virtual bool no_tables()= 0;
};
@@ -315,6 +328,7 @@ public:
table_map upper_select_const_tables();
void print (String *str);
int change_item(Item_subselect *si, select_subselect *result);
+ bool no_tables();
};
@@ -335,6 +349,7 @@ public:
table_map upper_select_const_tables();
void print (String *str);
int change_item(Item_subselect *si, select_subselect *result);
+ bool no_tables();
};
@@ -364,6 +379,7 @@ public:
table_map upper_select_const_tables() { return 0; }
void print (String *str);
int change_item(Item_subselect *si, select_subselect *result);
+ bool no_tables();
};
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index e6c96dd6a9a..6bd2cc00b3e 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -218,16 +218,13 @@ Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
hybrid_type= item->result_type();
if (hybrid_type == INT_RESULT)
{
- cmp_charset= &my_charset_bin;
max_length=20;
}
else if (hybrid_type == REAL_RESULT)
{
- cmp_charset= &my_charset_bin;
max_length=float_length(decimals);
}else
{
- cmp_charset= item->collation.collation;
max_length=item->max_length;
}
decimals=item->decimals;
@@ -254,7 +251,7 @@ Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
Item *Item_sum_sum::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_sum(thd, this);
+ return new (thd->mem_root) Item_sum_sum(thd, this);
}
@@ -282,7 +279,7 @@ double Item_sum_sum::val()
Item *Item_sum_count::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_count(thd, this);
+ return new (thd->mem_root) Item_sum_count(thd, this);
}
@@ -327,7 +324,7 @@ void Item_sum_count::cleanup()
Item *Item_sum_avg::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_avg(thd, this);
+ return new (thd->mem_root) Item_sum_avg(thd, this);
}
@@ -374,7 +371,7 @@ double Item_sum_std::val()
Item *Item_sum_std::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_std(thd, this);
+ return new (thd->mem_root) Item_sum_std(thd, this);
}
@@ -384,7 +381,7 @@ Item *Item_sum_std::copy_or_same(THD* thd)
Item *Item_sum_variance::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_variance(thd, this);
+ return new (thd->mem_root) Item_sum_variance(thd, this);
}
@@ -474,13 +471,14 @@ double Item_sum_hybrid::val()
{
DBUG_ASSERT(fixed == 1);
int err;
+ char *end_not_used;
if (null_value)
return 0.0;
switch (hybrid_type) {
case STRING_RESULT:
String *res; res=val_str(&str_value);
return (res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(),
- (char**) 0, &err) : 0.0);
+ &end_not_used, &err) : 0.0);
case INT_RESULT:
if (unsigned_flag)
return ulonglong2double(sum_int);
@@ -540,13 +538,28 @@ void Item_sum_hybrid::cleanup()
DBUG_ENTER("Item_sum_hybrid::cleanup");
Item_sum::cleanup();
used_table_cache= ~(table_map) 0;
+
+ /*
+ by default it is TRUE to avoid TRUE reporting by
+ Item_func_not_all/Item_func_nop_all if this item was never called.
+
+ no_rows_in_result() set it to FALSE if was not results found.
+ If some results found it will be left unchanged.
+ */
+ was_values= TRUE;
DBUG_VOID_RETURN;
}
+void Item_sum_hybrid::no_rows_in_result()
+{
+ Item_sum::no_rows_in_result();
+ was_values= FALSE;
+}
+
Item *Item_sum_min::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_min(thd, this);
+ return new (thd->mem_root) Item_sum_min(thd, this);
}
@@ -557,7 +570,7 @@ bool Item_sum_min::add()
{
String *result=args[0]->val_str(&tmp_value);
if (!args[0]->null_value &&
- (null_value || sortcmp(&value,result,cmp_charset) > 0))
+ (null_value || sortcmp(&value,result,collation.collation) > 0))
{
value.copy(*result);
null_value=0;
@@ -599,7 +612,7 @@ bool Item_sum_min::add()
Item *Item_sum_max::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_max(thd, this);
+ return new (thd->mem_root) Item_sum_max(thd, this);
}
@@ -610,7 +623,7 @@ bool Item_sum_max::add()
{
String *result=args[0]->val_str(&tmp_value);
if (!args[0]->null_value &&
- (null_value || sortcmp(&value,result,cmp_charset) < 0))
+ (null_value || sortcmp(&value,result,collation.collation) < 0))
{
value.copy(*result);
null_value=0;
@@ -666,7 +679,7 @@ void Item_sum_bit::clear()
Item *Item_sum_or::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_or(thd, this);
+ return new (thd->mem_root) Item_sum_or(thd, this);
}
@@ -680,7 +693,7 @@ bool Item_sum_or::add()
Item *Item_sum_xor::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_xor(thd, this);
+ return new (thd->mem_root) Item_sum_xor(thd, this);
}
@@ -694,7 +707,7 @@ bool Item_sum_xor::add()
Item *Item_sum_and::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_and(thd, this);
+ return new (thd->mem_root) Item_sum_and(thd, this);
}
@@ -921,7 +934,7 @@ Item_sum_hybrid::min_max_update_str_field()
result_field->val_str(&tmp_value);
if (result_field->is_null() ||
- (cmp_sign * sortcmp(res_str,&tmp_value,cmp_charset)) < 0)
+ (cmp_sign * sortcmp(res_str,&tmp_value,collation.collation)) < 0)
result_field->store(res_str->ptr(),res_str->length(),res_str->charset());
result_field->set_notnull();
}
@@ -1337,7 +1350,7 @@ int Item_sum_count_distinct::tree_to_myisam()
Item *Item_sum_count_distinct::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_count_distinct(thd, this);
+ return new (thd->mem_root) Item_sum_count_distinct(thd, this);
}
@@ -1438,7 +1451,7 @@ bool Item_udf_sum::add()
Item *Item_sum_udf_float::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_udf_float(thd, this);
+ return new (thd->mem_root) Item_sum_udf_float(thd, this);
}
double Item_sum_udf_float::val()
@@ -1463,7 +1476,7 @@ String *Item_sum_udf_float::val_str(String *str)
Item *Item_sum_udf_int::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_udf_int(thd, this);
+ return new (thd->mem_root) Item_sum_udf_int(thd, this);
}
@@ -1501,7 +1514,7 @@ void Item_sum_udf_str::fix_length_and_dec()
Item *Item_sum_udf_str::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_sum_udf_str(thd, this);
+ return new (thd->mem_root) Item_sum_udf_str(thd, this);
}
@@ -1815,7 +1828,7 @@ Item_func_group_concat::~Item_func_group_concat()
Item *Item_func_group_concat::copy_or_same(THD* thd)
{
- return new (&thd->mem_root) Item_func_group_concat(thd, this);
+ return new (thd->mem_root) Item_func_group_concat(thd, this);
}
@@ -1906,7 +1919,9 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
for (i=0 ; i < arg_count ; i++)
{
- if (args[i]->fix_fields(thd, tables, args + i) || args[i]->check_cols(1))
+ if ((!args[i]->fixed &&
+ args[i]->fix_fields(thd, tables, args + i)) ||
+ args[i]->check_cols(1))
return 1;
if (i < arg_count_field)
maybe_null|= args[i]->maybe_null;
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 5aa0d37190b..dab136e4716 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -402,20 +402,21 @@ class Item_sum_hybrid :public Item_sum
enum_field_types hybrid_field_type;
int cmp_sign;
table_map used_table_cache;
- CHARSET_INFO *cmp_charset;
+ bool was_values; // Set if we have found at least one row (for max/min only)
public:
Item_sum_hybrid(Item *item_par,int sign)
:Item_sum(item_par), sum(0.0), sum_int(0),
hybrid_type(INT_RESULT), hybrid_field_type(FIELD_TYPE_LONGLONG),
- cmp_sign(sign), used_table_cache(~(table_map) 0),
- cmp_charset(&my_charset_bin)
- {}
+ cmp_sign(sign), used_table_cache(~(table_map) 0), was_values(TRUE)
+ { collation.set(&my_charset_bin); }
Item_sum_hybrid(THD *thd, Item_sum_hybrid *item):
Item_sum(thd, item), value(item->value),
sum(item->sum), sum_int(item->sum_int), hybrid_type(item->hybrid_type),
hybrid_field_type(item->hybrid_field_type),cmp_sign(item->cmp_sign),
- used_table_cache(item->used_table_cache), cmp_charset(item->cmp_charset) {}
+ used_table_cache(item->used_table_cache),
+ was_values(TRUE)
+ { collation.set(item->collation); }
bool fix_fields(THD *, TABLE_LIST *, Item **);
table_map used_tables() const { return used_table_cache; }
bool const_item() const { return !used_table_cache; }
@@ -434,6 +435,8 @@ class Item_sum_hybrid :public Item_sum
void min_max_update_real_field();
void min_max_update_int_field();
void cleanup();
+ bool any_value() { return was_values; }
+ void no_rows_in_result();
};
@@ -532,7 +535,7 @@ public:
:Item_sum( list ), udf(udf_arg)
{ quick_group=0;}
Item_udf_sum(THD *thd, Item_udf_sum *item)
- :Item_sum(thd, item), udf(item->udf) {}
+ :Item_sum(thd, item), udf(item->udf) { udf.not_original= TRUE; }
const char *func_name() const { return udf.name(); }
bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
{
@@ -597,9 +600,11 @@ public:
double val()
{
int err;
- String *res; res=val_str(&str_value);
+ char *end_not_used;
+ String *res;
+ res=val_str(&str_value);
return res ? my_strntod(res->charset(),(char*) res->ptr(),res->length(),
- (char**) 0, &err) : 0.0;
+ &end_not_used, &err) : 0.0;
}
longlong val_int()
{
@@ -736,9 +741,10 @@ class Item_func_group_concat : public Item_sum
String *res;
char *end_ptr;
int error;
- res= val_str(&str_value);
+ if (!(res= val_str(&str_value)))
+ return (longlong) 0;
end_ptr= (char*) res->ptr()+ res->length();
- return res ? my_strtoll10(res->ptr(), &end_ptr, &error) : (longlong) 0;
+ return my_strtoll10(res->ptr(), &end_ptr, &error);
}
String* val_str(String* str);
Item *copy_or_same(THD* thd);
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 3c8dbb013a9..2c500f16bf3 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -149,6 +149,9 @@ static DATE_TIME_FORMAT time_24hrs_format= {{0}, '\0', 0,
conversion specifiers that can be used in such sub-patterns is limited.
Also most of checks are skipped in this case.
+ If one adds new format specifiers to this function he should also
+ consider adding them to get_date_time_result_type() function.
+
RETURN
0 ok
1 error
@@ -161,23 +164,24 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
{
int weekday= 0, yearday= 0, daypart= 0;
int week_number= -1;
- CHARSET_INFO *cs= &my_charset_bin;
int error= 0;
+ int strict_week_number_year= -1;
+ int frac_part;
bool usa_time= 0;
bool sunday_first_n_first_week_non_iso;
bool strict_week_number;
- int strict_week_number_year= -1;
bool strict_week_number_year_type;
- int frac_part;
const char *val_begin= val;
const char *val_end= val + length;
const char *ptr= format->format.str;
const char *end= ptr + format->format.length;
+ CHARSET_INFO *cs= &my_charset_bin;
DBUG_ENTER("extract_date_time");
- LINT_INIT(sunday_first_n_first_week_non_iso);
LINT_INIT(strict_week_number);
- LINT_INIT(strict_week_number_year_type);
+ /* Remove valgrind varnings when using gcc 3.3 and -O1 */
+ PURIFY_OR_LINT_INIT(strict_week_number_year_type);
+ PURIFY_OR_LINT_INIT(sunday_first_n_first_week_non_iso);
if (!sub_pattern_end)
bzero((char*) l_time, sizeof(*l_time));
@@ -1593,6 +1597,7 @@ void Item_func_from_unixtime::fix_length_and_dec()
collation.set(&my_charset_bin);
decimals=0;
max_length=MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
+ maybe_null= 1;
thd->time_zone_used= 1;
}
@@ -1600,50 +1605,47 @@ void Item_func_from_unixtime::fix_length_and_dec()
String *Item_func_from_unixtime::val_str(String *str)
{
TIME time_tmp;
- my_time_t tmp;
-
+
DBUG_ASSERT(fixed == 1);
- tmp= (time_t) args[0]->val_int();
- if ((null_value=args[0]->null_value))
- goto null_date;
-
- thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, tmp);
-
+
+ if (get_date(&time_tmp, 0))
+ return 0;
+
if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN))
- goto null_date;
+ {
+ null_value= 1;
+ return 0;
+ }
+
make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str);
return str;
-
-null_date:
- null_value=1;
- return 0;
}
longlong Item_func_from_unixtime::val_int()
{
TIME time_tmp;
- my_time_t tmp;
-
+
DBUG_ASSERT(fixed == 1);
- tmp= (time_t) (ulong) args[0]->val_int();
- if ((null_value=args[0]->null_value))
+ if (get_date(&time_tmp, 0))
return 0;
-
- current_thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, tmp);
-
+
return (longlong) TIME_to_ulonglong_datetime(&time_tmp);
}
bool Item_func_from_unixtime::get_date(TIME *ltime,
uint fuzzy_date __attribute__((unused)))
{
- my_time_t tmp=(my_time_t) args[0]->val_int();
- if ((null_value=args[0]->null_value))
+ ulonglong tmp= (ulonglong)(args[0]->val_int());
+ /*
+ "tmp > TIMESTAMP_MAX_VALUE" check also covers case of negative
+ from_unixtime() argument since tmp is unsigned.
+ */
+ if ((null_value= (args[0]->null_value || tmp > TIMESTAMP_MAX_VALUE)))
return 1;
-
- current_thd->variables.time_zone->gmt_sec_to_TIME(ltime, tmp);
+
+ thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)tmp);
return 0;
}
@@ -1654,6 +1656,7 @@ void Item_func_convert_tz::fix_length_and_dec()
collation.set(&my_charset_bin);
decimals= 0;
max_length= MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
+ maybe_null= 1;
}
@@ -1666,12 +1669,6 @@ Item_func_convert_tz::fix_fields(THD *thd_arg, TABLE_LIST *tables_arg, Item **re
tz_tables= thd_arg->lex->time_zone_tables_used;
- if (args[1]->const_item())
- from_tz= my_tz_find(args[1]->val_str(&str), tz_tables);
-
- if (args[2]->const_item())
- to_tz= my_tz_find(args[2]->val_str(&str), tz_tables);
-
return 0;
}
@@ -1711,13 +1708,19 @@ bool Item_func_convert_tz::get_date(TIME *ltime,
my_time_t my_time_tmp;
bool not_used;
String str;
-
- if (!args[1]->const_item())
+
+ if (!from_tz_cached)
+ {
from_tz= my_tz_find(args[1]->val_str(&str), tz_tables);
-
- if (!args[2]->const_item())
+ from_tz_cached= args[1]->const_item();
+ }
+
+ if (!to_tz_cached)
+ {
to_tz= my_tz_find(args[2]->val_str(&str), tz_tables);
-
+ to_tz_cached= args[2]->const_item();
+ }
+
if (from_tz==0 || to_tz==0 || get_arg0_date(ltime, 0))
{
null_value= 1;
@@ -1739,6 +1742,13 @@ bool Item_func_convert_tz::get_date(TIME *ltime,
}
+void Item_func_convert_tz::cleanup()
+{
+ from_tz_cached= to_tz_cached= 0;
+ Item_date_func::cleanup();
+}
+
+
void Item_date_add_interval::fix_length_and_dec()
{
enum_field_types arg0_field_type;
@@ -2110,19 +2120,24 @@ void Item_char_typecast::print(String *str)
String *Item_char_typecast::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
- String *res, *res1;
+ String *res;
uint32 length;
- if (!charset_conversion && !(res= args[0]->val_str(str)))
+ if (!charset_conversion)
{
- null_value= 1;
- return 0;
+ if (!(res= args[0]->val_str(str)))
+ {
+ null_value= 1;
+ return 0;
+ }
}
else
{
// Convert character set if differ
- if (!(res1= args[0]->val_str(&tmp_value)) ||
- str->copy(res1->ptr(), res1->length(),res1->charset(), cast_cs))
+ uint dummy_errors;
+ if (!(res= args[0]->val_str(&tmp_value)) ||
+ str->copy(res->ptr(), res->length(), res->charset(),
+ cast_cs, &dummy_errors))
{
null_value= 1;
return 0;
@@ -2131,13 +2146,13 @@ String *Item_char_typecast::val_str(String *str)
}
res->set_charset(cast_cs);
-
+
/*
Cut the tail if cast with length
and the result is longer than cast length, e.g.
CAST('string' AS CHAR(1))
*/
- if (cast_length >= 0 &&
+ if (cast_length >= 0 &&
(res->length() > (length= (uint32) res->charpos(cast_length))))
{ // Safe even if const arg
if (!res->alloced_length())
@@ -2146,7 +2161,7 @@ String *Item_char_typecast::val_str(String *str)
res= &str_value;
}
res->length((uint) length);
- }
+ }
null_value= 0;
return res;
}
@@ -2154,9 +2169,18 @@ String *Item_char_typecast::val_str(String *str)
void Item_char_typecast::fix_length_and_dec()
{
uint32 char_length;
- charset_conversion= !my_charset_same(args[0]->collation.collation, cast_cs) &&
- args[0]->collation.collation != &my_charset_bin &&
- cast_cs != &my_charset_bin;
+ /*
+ We always force character set conversion if cast_cs
+ is a multi-byte character set. It garantees that the
+ result of CAST is a well-formed string.
+ For single-byte character sets we allow just to copy
+ from the argument. A single-byte character sets string
+ is always well-formed.
+ */
+ charset_conversion= (cast_cs->mbmaxlen > 1) ||
+ !my_charset_same(args[0]->collation.collation, cast_cs) &&
+ args[0]->collation.collation != &my_charset_bin &&
+ cast_cs != &my_charset_bin;
collation.set(cast_cs, DERIVATION_IMPLICIT);
char_length= (cast_length >= 0) ? cast_length :
args[0]->max_length/args[0]->collation.collation->mbmaxlen;
@@ -2181,6 +2205,12 @@ String *Item_datetime_typecast::val_str(String *str)
bool Item_time_typecast::get_time(TIME *ltime)
{
bool res= get_arg0_time(ltime);
+ /*
+ For MYSQL_TIMESTAMP_TIME value we can have non-zero day part,
+ which we should not lose.
+ */
+ if (ltime->time_type == MYSQL_TIMESTAMP_DATETIME)
+ ltime->year= ltime->month= ltime->day= 0;
ltime->time_type= MYSQL_TIMESTAMP_TIME;
return res;
}
@@ -2204,6 +2234,7 @@ String *Item_time_typecast::val_str(String *str)
bool Item_date_typecast::get_date(TIME *ltime, uint fuzzy_date)
{
bool res= get_arg0_date(ltime,1);
+ ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0;
ltime->time_type= MYSQL_TIMESTAMP_DATE;
return res;
}
@@ -2421,8 +2452,7 @@ void Item_func_add_time::print(String *str)
String *Item_func_timediff::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
- longlong seconds;
- long microseconds;
+ longlong microseconds;
long days;
int l_sign= 1;
TIME l_time1 ,l_time2, l_time3;
@@ -2446,32 +2476,23 @@ String *Item_func_timediff::val_str(String *str)
(uint) l_time2.month,
(uint) l_time2.day));
- microseconds= l_time1.second_part - l_sign*l_time2.second_part;
- seconds= ((longlong) days*86400L + l_time1.hour*3600L +
- l_time1.minute*60L + l_time1.second + microseconds/1000000L -
- (longlong)l_sign*(l_time2.hour*3600L+l_time2.minute*60L+
- l_time2.second));
+ microseconds= ((longlong)days*86400L +
+ l_time1.hour*3600L + l_time1.minute*60L + l_time1.second -
+ (longlong)l_sign*(l_time2.hour*3600L + l_time2.minute*60L +
+ l_time2.second))*1000000 +
+ l_time1.second_part - l_sign*l_time2.second_part;
l_time3.neg= 0;
- if (seconds < 0)
- {
- seconds= -seconds;
- l_time3.neg= 1;
- }
- else if (seconds == 0 && microseconds < 0)
+ if (microseconds < 0)
{
microseconds= -microseconds;
l_time3.neg= 1;
}
- if (microseconds < 0)
- {
- microseconds+= 1000000L;
- seconds--;
- }
- if ((l_time2.neg == l_time1.neg) && l_time1.neg)
+ if ((l_time2.neg == l_time1.neg) && l_time1.neg && microseconds)
l_time3.neg= l_time3.neg ? 0 : 1;
- calc_time_from_sec(&l_time3, (long) seconds, microseconds);
+ calc_time_from_sec(&l_time3, (long)(microseconds/1000000),
+ (long)(microseconds%1000000));
if (!make_datetime(l_time1.second_part || l_time2.second_part ?
TIME_MICROSECOND : TIME_ONLY,
@@ -2596,25 +2617,31 @@ void Item_func_get_format::print(String *str)
/*
- check_result_type(s, l) returns DATE/TIME type
- according to format string
-
- s: DATE/TIME format string
- l: length of s
- Result: date_time_format_types value:
- DATE_TIME_MICROSECOND, DATE_TIME,
- TIME_MICROSECOND, TIME_ONLY
-
- We don't process day format's characters('D', 'd', 'e')
- because day may be a member of all date/time types.
- If only day format's character and no time part present
- the result type is MYSQL_TYPE_DATE
+ Get type of datetime value (DATE/TIME/...) which will be produced
+ according to format string.
+
+ SYNOPSIS
+ get_date_time_result_type()
+ format - format string
+ length - length of format string
+
+ NOTE
+ We don't process day format's characters('D', 'd', 'e') because day
+ may be a member of all date/time types.
+
+ Format specifiers supported by this function should be in sync with
+ specifiers supported by extract_date_time() function.
+
+ RETURN VALUE
+ One of date_time_format_types values:
+ DATE_TIME_MICROSECOND, DATE_TIME, DATE_ONLY, TIME_MICROSECOND, TIME_ONLY
*/
-date_time_format_types check_result_type(const char *format, uint length)
+static date_time_format_types
+get_date_time_result_type(const char *format, uint length)
{
const char *time_part_frms= "HISThiklrs";
- const char *date_part_frms= "MUYWabcjmuyw";
+ const char *date_part_frms= "MVUXYWabcjmvuxyw";
bool date_part_used= 0, time_part_used= 0, frac_second_used= 0;
const char *val= format;
@@ -2625,22 +2652,30 @@ date_time_format_types check_result_type(const char *format, uint length)
if (*val == '%' && val+1 != end)
{
val++;
- if ((frac_second_used= (*val == 'f')) ||
- (!time_part_used && strchr(time_part_frms, *val)))
+ if (*val == 'f')
+ frac_second_used= time_part_used= 1;
+ else if (!time_part_used && strchr(time_part_frms, *val))
time_part_used= 1;
else if (!date_part_used && strchr(date_part_frms, *val))
date_part_used= 1;
- if (time_part_used && date_part_used && frac_second_used)
+ if (date_part_used && frac_second_used)
+ {
+ /*
+ frac_second_used implies time_part_used, and thus we already
+ have all types of date-time components and can end our search.
+ */
return DATE_TIME_MICROSECOND;
+ }
}
}
+ /* We don't have all three types of date-time components */
+ if (frac_second_used)
+ return TIME_MICROSECOND;
if (time_part_used)
{
if (date_part_used)
return DATE_TIME;
- if (frac_second_used)
- return TIME_MICROSECOND;
return TIME_ONLY;
}
return DATE_ONLY;
@@ -2671,7 +2706,8 @@ void Item_func_str_to_date::fix_length_and_dec()
if ((const_item= args[1]->const_item()))
{
format= args[1]->val_str(&format_str);
- cached_format_type= check_result_type(format->ptr(), format->length());
+ cached_format_type= get_date_time_result_type(format->ptr(),
+ format->length());
switch (cached_format_type) {
case DATE_ONLY:
cached_timestamp_type= MYSQL_TIMESTAMP_DATE;
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index df0b05d6d42..cc2709bf555 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -545,12 +545,15 @@ class Item_func_convert_tz :public Item_date_func
TABLE_LIST *tz_tables;
/*
If time zone parameters are constants we are caching objects that
- represent them.
+ represent them (we use separate from_tz_cached/to_tz_cached members
+ to indicate this fact, since NULL is legal value for from_tz/to_tz
+ members.
*/
+ bool from_tz_cached, to_tz_cached;
Time_zone *from_tz, *to_tz;
public:
Item_func_convert_tz(Item *a, Item *b, Item *c):
- Item_date_func(a, b, c) {}
+ Item_date_func(a, b, c), from_tz_cached(0), to_tz_cached(0) {}
longlong val_int();
double val() { return (double) val_int(); }
String *val_str(String *str);
@@ -558,6 +561,7 @@ class Item_func_convert_tz :public Item_date_func
bool fix_fields(THD *, struct st_table_list *, Item **);
void fix_length_and_dec();
bool get_date(TIME *res, uint fuzzy_date);
+ void cleanup();
};
diff --git a/sql/lex.h b/sql/lex.h
index c64a7069c32..325d052de90 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -114,6 +114,7 @@ static SYMBOL symbols[] = {
{ "COMMITTED", SYM(COMMITTED_SYM)},
{ "COMPRESSED", SYM(COMPRESSED_SYM)},
{ "CONCURRENT", SYM(CONCURRENT)},
+ { "CONSISTENT", SYM(CONSISTENT_SYM)},
{ "CONSTRAINT", SYM(CONSTRAINT)},
{ "CONVERT", SYM(CONVERT_SYM)},
{ "CREATE", SYM(CREATE)},
@@ -382,6 +383,7 @@ static SYMBOL symbols[] = {
{ "SIGNED", SYM(SIGNED_SYM)},
{ "SIMPLE", SYM(SIMPLE_SYM)},
{ "SLAVE", SYM(SLAVE)},
+ { "SNAPSHOT", SYM(SNAPSHOT_SYM)},
{ "SMALLINT", SYM(SMALLINT)},
{ "SOME", SYM(ANY_SYM)},
{ "SONAME", SYM(UDF_SONAME_SYM)},
diff --git a/sql/lock.cc b/sql/lock.cc
index 646babea6a1..8f1cd080db7 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -705,15 +705,70 @@ static void print_lock_error(int error)
/****************************************************************************
Handling of global read locks
+ Taking the global read lock is TWO steps (2nd step is optional; without
+ it, COMMIT of existing transactions will be allowed):
+ lock_global_read_lock() THEN make_global_read_lock_block_commit().
+
The global locks are handled through the global variables:
global_read_lock
+ count of threads which have the global read lock (i.e. have completed at
+ least the first step above)
global_read_lock_blocks_commit
- waiting_for_read_lock
+ count of threads which have the global read lock and block
+ commits (i.e. are in or have completed the second step above)
+ waiting_for_read_lock
+ count of threads which want to take a global read lock but cannot
protect_against_global_read_lock
+ count of threads which have set protection against global read lock.
+
+ How blocking of threads by global read lock is achieved: that's
+ advisory. Any piece of code which should be blocked by global read lock must
+ be designed like this:
+ - call to wait_if_global_read_lock(). When this returns 0, no global read
+ lock is owned; if argument abort_on_refresh was 0, none can be obtained.
+ - job
+ - if abort_on_refresh was 0, call to start_waiting_global_read_lock() to
+ allow other threads to get the global read lock. I.e. removal of the
+ protection.
+ (Note: it's a bit like an implementation of rwlock).
+
+ [ I am sorry to mention some SQL syntaxes below I know I shouldn't but found
+ no better descriptive way ]
+
+ Why does FLUSH TABLES WITH READ LOCK need to block COMMIT: because it's used
+ to read a non-moving SHOW MASTER STATUS, and a COMMIT writes to the binary
+ log.
+
+ Why getting the global read lock is two steps and not one. Because FLUSH
+ TABLES WITH READ LOCK needs to insert one other step between the two:
+ flushing tables. So the order is
+ 1) lock_global_read_lock() (prevents any new table write locks, i.e. stalls
+ all new updates)
+ 2) close_cached_tables() (the FLUSH TABLES), which will wait for tables
+ currently opened and being updated to close (so it's possible that there is
+ a moment where all new updates of server are stalled *and* FLUSH TABLES WITH
+ READ LOCK is, too).
+ 3) make_global_read_lock_block_commit().
+ If we have merged 1) and 3) into 1), we would have had this deadlock:
+ imagine thread 1 and 2, in non-autocommit mode, thread 3, and an InnoDB
+ table t.
+ thd1: SELECT * FROM t FOR UPDATE;
+ thd2: UPDATE t SET a=1; # blocked by row-level locks of thd1
+ thd3: FLUSH TABLES WITH READ LOCK; # blocked in close_cached_tables() by the
+ table instance of thd2
+ thd1: COMMIT; # blocked by thd3.
+ thd1 blocks thd2 which blocks thd3 which blocks thd1: deadlock.
+
+ Note that we need to support that one thread does
+ FLUSH TABLES WITH READ LOCK; and then COMMIT;
+ (that's what innobackup does, for some good reason).
+ So in this exceptional case the COMMIT should not be blocked by the FLUSH
+ TABLES WITH READ LOCK.
+
+ TODO in MySQL 5.x: make_global_read_lock_block_commit() should be
+ killable. Normally CPU does not spend a long time in this function (COMMITs
+ are quite fast), but it would still be nice.
- Taking the global read lock is TWO steps (2nd step is optional; without
- it, COMMIT of existing transactions will be allowed):
- lock_global_read_lock() THEN make_global_read_lock_block_commit().
****************************************************************************/
volatile uint global_read_lock=0;
@@ -828,8 +883,11 @@ void start_waiting_global_read_lock(THD *thd)
{
bool tmp;
DBUG_ENTER("start_waiting_global_read_lock");
+ if (unlikely(thd->global_read_lock))
+ DBUG_VOID_RETURN;
(void) pthread_mutex_lock(&LOCK_open);
- tmp= (!--protect_against_global_read_lock && waiting_for_read_lock);
+ tmp= (!--protect_against_global_read_lock &&
+ (waiting_for_read_lock || global_read_lock_blocks_commit));
(void) pthread_mutex_unlock(&LOCK_open);
if (tmp)
pthread_cond_broadcast(&COND_refresh);
diff --git a/sql/log.cc b/sql/log.cc
index ef57a57d3b4..c8a3b512b6d 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -23,7 +23,6 @@
#endif
#include "mysql_priv.h"
-#include "sql_acl.h"
#include "sql_repl.h"
#include "ha_innodb.h" // necessary to cut the binlog when crash recovery
@@ -256,7 +255,9 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
if ((file=my_open(log_file_name,open_flags,
MYF(MY_WME | ME_WAITTANG))) < 0 ||
init_io_cache(&log_file, file, IO_SIZE, io_cache_type,
- my_tell(file,MYF(MY_WME)), 0, MYF(MY_WME | MY_NABP)))
+ my_tell(file,MYF(MY_WME)), 0,
+ MYF(MY_WME | MY_NABP |
+ ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0))))
goto err;
switch (log_type) {
@@ -334,6 +335,8 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
First open of this class instance
Create an index file that will hold all file names uses for logging.
Add new entries to the end of it.
+ Index file (and binlog) are so critical for recovery/replication
+ that we create them with MY_WAIT_IF_FULL.
*/
fn_format(index_file_name, index_file_name_arg, mysql_data_home,
".index", opt);
@@ -344,7 +347,7 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
init_io_cache(&index_file, index_file_nr,
IO_SIZE, WRITE_CACHE,
my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)),
- 0, MYF(MY_WME)))
+ 0, MYF(MY_WME | MY_WAIT_IF_FULL)))
goto err;
}
else
@@ -1271,10 +1274,10 @@ bool MYSQL_LOG::write(Log_event* event_info)
binlog_[wild_]{do|ignore}_table?" (WL#1049)"
*/
if ((thd && !(thd->options & OPTION_BIN_LOG)) ||
- (local_db && !db_ok(local_db, binlog_do_db, binlog_ignore_db)))
+ (!db_ok(local_db, binlog_do_db, binlog_ignore_db)))
{
VOID(pthread_mutex_unlock(&LOCK_log));
- DBUG_PRINT("error",("!db_ok"));
+ DBUG_PRINT("error",("!db_ok('%s')", local_db));
DBUG_RETURN(0);
}
#endif /* HAVE_REPLICATION */
@@ -1317,8 +1320,9 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
(uint) thd->variables.collation_connection->number,
(uint) thd->variables.collation_database->number,
(uint) thd->variables.collation_server->number);
- Query_log_event e(thd, buf, written, 0);
+ Query_log_event e(thd, buf, written, 0, FALSE);
e.set_log_pos(this);
+ e.error_code = 0; // This statement cannot fail (see [1]).
if (e.write(file))
goto err;
}
@@ -1333,8 +1337,9 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
char *buf_end= strxmov(buf, "SET ONE_SHOT TIME_ZONE='",
thd->variables.time_zone->get_name()->ptr(),
"'", NullS);
- Query_log_event e(thd, buf, buf_end - buf, 0);
+ Query_log_event e(thd, buf, buf_end - buf, 0, FALSE);
e.set_log_pos(this);
+ e.error_code = 0; // This statement cannot fail (see [1]).
if (e.write(file))
goto err;
}
@@ -1387,6 +1392,7 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
thd->variables.convert_set->name);
Query_log_event e(thd, buf, (ulong) (p - buf), 0);
e.set_log_pos(this);
+ e.error_code = 0; // This statement cannot fail (see [1]).
if (e.write(file))
goto err;
}
@@ -1402,14 +1408,24 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS)
{
- Query_log_event e(thd, "SET FOREIGN_KEY_CHECKS=0", 24, 0);
+ Query_log_event e(thd, "SET FOREIGN_KEY_CHECKS=0", 24, 0, FALSE);
e.set_log_pos(this);
+ e.error_code = 0; // This statement cannot fail (see [1]).
if (e.write(file))
goto err;
}
}
- /* Write the SQL command */
+ /*
+ Write the SQL command
+
+ [1] If this statement has an error code, the slave is required to fail
+ with the same error code or stop. The preamble and epilogue should
+ *not* have this error code since the execution of those is
+ guaranteed *not* to produce any error code. This would therefore
+ stop the slave even if the execution of the real statement can be
+ handled gracefully by the slave.
+ */
event_info->set_log_pos(this);
if (event_info->write(file))
@@ -1421,8 +1437,9 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
{
if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS)
{
- Query_log_event e(thd, "SET FOREIGN_KEY_CHECKS=1", 24, 0);
+ Query_log_event e(thd, "SET FOREIGN_KEY_CHECKS=1", 24, 0, FALSE);
e.set_log_pos(this);
+ e.error_code = 0; // This statement cannot fail (see [1]).
if (e.write(file))
goto err;
}
@@ -1450,7 +1467,8 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
if (flush_io_cache(file) || sync_binlog(file))
goto err;
- if (opt_using_transactions && !my_b_tell(&thd->transaction.trans_log))
+ if (opt_using_transactions &&
+ !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
{
/*
LOAD DATA INFILE in AUTOCOMMIT=1 mode writes to the binlog
@@ -1574,6 +1592,7 @@ uint MYSQL_LOG::next_file_id()
bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, bool commit_or_rollback)
{
+ bool should_rotate= 0, error= 0;
VOID(pthread_mutex_lock(&LOCK_log));
DBUG_ENTER("MYSQL_LOG::write(cache");
@@ -1596,7 +1615,15 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, bool commit_or_rollback)
we will add the "COMMIT mark and write the buffer to the binlog.
*/
{
- Query_log_event qinfo(thd, "BEGIN", 5, TRUE);
+ Query_log_event qinfo(thd, "BEGIN", 5, TRUE, FALSE);
+ /*
+ Imagine this is rollback due to net timeout, after all statements of
+ the transaction succeeded. Then we want a zero-error code in BEGIN.
+ In other words, if there was a really serious error code it's already
+ in the statement's events.
+ This is safer than thd->clear_error() against kills at shutdown.
+ */
+ qinfo.error_code= 0;
/*
Now this Query_log_event has artificial log_pos 0. It must be adjusted
to reflect the real position in the log. Not doing it would confuse the
@@ -1629,7 +1656,8 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, bool commit_or_rollback)
Query_log_event qinfo(thd,
commit_or_rollback ? "COMMIT" : "ROLLBACK",
commit_or_rollback ? 6 : 8,
- TRUE);
+ TRUE, FALSE);
+ qinfo.error_code= 0;
qinfo.set_log_pos(this);
if (qinfo.write(&log_file) || flush_io_cache(&log_file) ||
sync_binlog(&log_file))
@@ -1662,7 +1690,7 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, bool commit_or_rollback)
goto err;
signal_update();
DBUG_PRINT("info",("max_size: %lu",max_size));
- if (my_b_tell(&log_file) >= (my_off_t) max_size)
+ if (should_rotate= (my_b_tell(&log_file) >= (my_off_t) max_size))
{
pthread_mutex_lock(&LOCK_index);
new_file(0); // inside mutex
@@ -1678,7 +1706,16 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, bool commit_or_rollback)
ha_commit_complete(thd);
- DBUG_RETURN(0);
+#ifdef HAVE_REPLICATION
+ if (should_rotate && expire_logs_days)
+ {
+ long purge_time= time(0) - expire_logs_days*24*60*60;
+ if (purge_time >= 0)
+ error= purge_logs_before_date(purge_time);
+ }
+#endif
+
+ DBUG_RETURN(error);
err:
if (!write_error)
@@ -1703,6 +1740,8 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
time_t current_time;
if (!is_open())
return 0;
+ DBUG_ENTER("MYSQL_LOG::write");
+
VOID(pthread_mutex_lock(&LOCK_log));
if (is_open())
{ // Safety agains reopen
@@ -1712,7 +1751,7 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
if (!(thd->options & OPTION_UPDATE_LOG))
{
VOID(pthread_mutex_unlock(&LOCK_log));
- return 0;
+ DBUG_RETURN(0);
}
if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT) || query_start_arg)
{
@@ -1812,7 +1851,7 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
}
}
VOID(pthread_mutex_unlock(&LOCK_log));
- return error;
+ DBUG_RETURN(error);
}
@@ -1832,16 +1871,19 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
THD::enter_cond() (see NOTES in sql_class.h).
*/
-void MYSQL_LOG:: wait_for_update(THD* thd, bool master_or_slave)
+void MYSQL_LOG::wait_for_update(THD* thd, bool master_or_slave)
{
- const char* old_msg = thd->enter_cond(&update_cond, &LOCK_log,
- master_or_slave ?
- "Has read all relay log; waiting for \
-the slave I/O thread to update it" :
- "Has sent all binlog to slave; \
-waiting for binlog to be updated");
+ const char *old_msg;
+ DBUG_ENTER("wait_for_update");
+ old_msg= thd->enter_cond(&update_cond, &LOCK_log,
+ master_or_slave ?
+ "Has read all relay log; waiting for the slave I/O "
+ "thread to update it" :
+ "Has sent all binlog to slave; waiting for binlog "
+ "to be updated");
pthread_cond_wait(&update_cond, &LOCK_log);
thd->exit_cond(old_msg);
+ DBUG_VOID_RETURN;
}
@@ -2022,6 +2064,7 @@ bool flush_error_log()
char err_renamed[FN_REFLEN], *end;
end= strmake(err_renamed,log_error_file,FN_REFLEN-4);
strmov(end, "-old");
+ VOID(pthread_mutex_lock(&LOCK_error_log));
#ifdef __WIN__
char err_temp[FN_REFLEN+4];
/*
@@ -2042,7 +2085,7 @@ bool flush_error_log()
if ((fd = my_open(err_temp, O_RDONLY, MYF(0))) >= 0)
{
while ((bytes = (int) my_read(fd, (byte*) buf, IO_SIZE, MYF(0))) > 0)
- my_fwrite(stderr, (byte*) buf, (uint) strlen(buf),MYF(0));
+ my_fwrite(stderr, (byte*) buf, bytes, MYF(0));
my_close(fd, MYF(0));
}
(void) my_delete(err_temp, MYF(0));
@@ -2056,6 +2099,7 @@ bool flush_error_log()
else
result= 1;
#endif
+ VOID(pthread_mutex_unlock(&LOCK_error_log));
}
return result;
}
@@ -2198,6 +2242,15 @@ void MYSQL_LOG::report_pos_in_innodb()
DBUG_VOID_RETURN;
}
+
+void MYSQL_LOG::signal_update()
+{
+ DBUG_ENTER("MYSQL_LOG::signal_update");
+ pthread_cond_broadcast(&update_cond);
+ DBUG_VOID_RETURN;
+}
+
+
#ifdef __NT__
void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,
uint length, int buffLen)
@@ -2208,9 +2261,9 @@ void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,
DBUG_ENTER("print_buffer_to_nt_eventlog");
buffptr= buff;
- if (length > (uint)(buffLen-4))
+ if (length > (uint)(buffLen-5))
{
- char *newBuff= new char[length + 4];
+ char *newBuff= new char[length + 5];
strcpy(newBuff, buff);
buffptr= newBuff;
}
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 326f2fc5c59..d1af78dd751 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -373,6 +373,9 @@ int Log_event::exec_event(struct st_relay_log_info* rli)
Note that Rotate_log_event::exec_event() does not call this function,
so there is no chance that a fake rotate event resets
last_master_timestamp.
+ Note that we update without mutex (probably ok - except in some very
+ rare cases, only consequence is that value may take some time to
+ display in Seconds_Behind_Master - not critical).
*/
rli->last_master_timestamp= when;
}
@@ -780,7 +783,8 @@ void Query_log_event::pack_info(Protocol *protocol)
if (!(buf= my_malloc(9 + db_len + q_len, MYF(MY_WME))))
return;
pos= buf;
- if (db && db_len)
+ if (!(flags & LOG_EVENT_SUPPRESS_USE_F)
+ && db && db_len)
{
pos= strmov(buf, "use `");
memcpy(pos, db, db_len);
@@ -872,9 +876,12 @@ int Query_log_event::write_data(IO_CACHE* file)
#ifndef MYSQL_CLIENT
Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
- ulong query_length, bool using_trans)
- :Log_event(thd_arg, !thd_arg->tmp_table_used ?
- 0 : LOG_EVENT_THREAD_SPECIFIC_F, using_trans),
+ ulong query_length, bool using_trans,
+ bool suppress_use)
+ :Log_event(thd_arg,
+ ((thd_arg->tmp_table_used ? LOG_EVENT_THREAD_SPECIFIC_F : 0)
+ | (suppress_use ? LOG_EVENT_SUPPRESS_USE_F : 0)),
+ using_trans),
data_buf(0), query(query_arg),
db(thd_arg->db), q_len((uint32) query_length),
error_code(thd_arg->killed ?
@@ -949,14 +956,20 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db)
bool different_db= 1;
- if (db && last_db)
+ if (!(flags & LOG_EVENT_SUPPRESS_USE_F))
{
- if (different_db= memcmp(last_db, db, db_len + 1))
- memcpy(last_db, db, db_len + 1);
+ if (db && last_db)
+ {
+ if (different_db= memcmp(last_db, db, db_len + 1))
+ memcpy(last_db, db, db_len + 1);
+ }
+
+ if (db && db[0] && different_db)
+ {
+ fprintf(file, "use %s;\n", db);
+ }
}
-
- if (db && db[0] && different_db)
- fprintf(file, "use %s;\n", db);
+
end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10);
*end++=';';
*end++='\n';
@@ -977,7 +990,8 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db)
int Query_log_event::exec_event(struct st_relay_log_info* rli)
{
int expected_error,actual_error= 0;
- thd->db= (char*) rewrite_db(db); // thd->db_length is set later if needed
+ thd->db_length= db_len;
+ thd->db= (char*) rewrite_db(db, &thd->db_length);
/*
InnoDB internally stores the master log position it has processed so far;
@@ -995,11 +1009,6 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli)
if (db_ok(thd->db, replicate_do_db, replicate_ignore_db))
{
thd->set_time((time_t)when);
- /*
- We cannot use db_len from event to fill thd->db_length, because
- rewrite_db() may have changed db.
- */
- thd->db_length= thd->db ? strlen(thd->db) : 0;
thd->query_length= q_len;
thd->query = (char*)query;
VOID(pthread_mutex_lock(&LOCK_thread_count));
@@ -1007,7 +1016,6 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli)
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->variables.pseudo_thread_id= thread_id; // for temp tables
- mysql_log.write(thd,COM_QUERY,"%s",thd->query);
DBUG_PRINT("query",("%s",thd->query));
if (ignored_error_code((expected_error= error_code)) ||
!check_expected_error(thd,rli,expected_error))
@@ -1037,6 +1045,10 @@ START SLAVE; . Query: '%s'", expected_error, thd->query);
goto end;
}
+ /* If the query was not ignored, it is printed to the general log */
+ if (thd->net.last_errno != ER_SLAVE_IGNORED_TABLE)
+ mysql_log.write(thd,COM_QUERY,"%s",thd->query);
+
/*
If we expected a non-zero error code, and we don't get the same error
code, and none of them should be ignored.
@@ -1057,7 +1069,7 @@ Default database: '%s'. Query: '%s'",
expected_error,
actual_error ? thd->net.last_error: "no error",
actual_error,
- print_slave_db_safe(db), query);
+ print_slave_db_safe(thd->db), query);
thd->query_error= 1;
}
/*
@@ -1078,7 +1090,7 @@ Default database: '%s'. Query: '%s'",
"Error '%s' on query. Default database: '%s'. Query: '%s'",
(actual_error ? thd->net.last_error :
"unexpected success or fatal error"),
- print_slave_db_safe(db), query);
+ print_slave_db_safe(thd->db), query);
thd->query_error= 1;
}
} /* End of if (db_ok(... */
@@ -1090,7 +1102,7 @@ end:
thd->query_length= thd->db_length =0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
close_thread_tables(thd);
- free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
+ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
/*
If there was an error we stop. Otherwise we increment positions. Note that
we will not increment group* positions if we are just after a SET
@@ -1409,8 +1421,10 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
const char *db_arg, const char *table_name_arg,
List<Item> &fields_arg,
enum enum_duplicates handle_dup,
- bool using_trans)
- :Log_event(thd_arg, 0, using_trans), thread_id(thd_arg->thread_id),
+ bool ignore, bool using_trans)
+ :Log_event(thd_arg, !thd_arg->tmp_table_used ?
+ 0 : LOG_EVENT_THREAD_SPECIFIC_F, using_trans),
+ thread_id(thd_arg->thread_id),
slave_proxy_id(thd_arg->variables.pseudo_thread_id),
num_fields(0),fields(0),
field_lens(0),field_block_len(0),
@@ -1445,9 +1459,6 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
sql_ex.empty_flags= 0;
switch (handle_dup) {
- case DUP_IGNORE:
- sql_ex.opt_flags|= IGNORE_FLAG;
- break;
case DUP_REPLACE:
sql_ex.opt_flags|= REPLACE_FLAG;
break;
@@ -1455,6 +1466,8 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
case DUP_ERROR:
break;
}
+ if (ignore)
+ sql_ex.opt_flags|= IGNORE_FLAG;
if (!ex->field_term->length())
sql_ex.empty_flags |= FIELD_TERM_EMPTY;
@@ -1600,6 +1613,9 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db,
commented ? "# " : "",
db);
+ if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
+ fprintf(file,"%sSET @@session.pseudo_thread_id=%lu;\n",
+ commented ? "# " : "", (ulong)thread_id);
fprintf(file, "%sLOAD DATA ",
commented ? "# " : "");
if (check_fname_outside_temp_buf())
@@ -1659,16 +1675,22 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db,
/*
Load_log_event::set_fields()
+
+ Note that this function can not use the member variable
+ for the database, since LOAD DATA INFILE on the slave
+ can be for a different database than the current one.
+ This is the reason for the affected_db argument to this method.
*/
#ifndef MYSQL_CLIENT
-void Load_log_event::set_fields(List<Item> &field_list)
+void Load_log_event::set_fields(const char* affected_db,
+ List<Item> &field_list)
{
uint i;
const char* field = fields;
for (i= 0; i < num_fields; i++)
{
- field_list.push_back(new Item_field(db, table_name, field));
+ field_list.push_back(new Item_field(affected_db, table_name, field));
field+= field_lens[i] + 1;
}
}
@@ -1706,7 +1728,8 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
bool use_rli_only_for_errors)
{
char *load_data_query= 0;
- thd->db= (char*) rewrite_db(db); // thd->db_length is set later if needed
+ thd->db_length= db_len;
+ thd->db= (char*) rewrite_db(db, &thd->db_length);
DBUG_ASSERT(thd->query == 0);
thd->query_length= 0; // Should not be needed
thd->query_error= 0;
@@ -1741,7 +1764,6 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
if (db_ok(thd->db, replicate_do_db, replicate_ignore_db))
{
thd->set_time((time_t)when);
- thd->db_length= thd->db ? strlen(thd->db) : 0;
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->query_id = query_id++;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
@@ -1771,6 +1793,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
{
char llbuff[22];
enum enum_duplicates handle_dup;
+ bool ignore= 0;
/*
Make a simplified LOAD DATA INFILE query, for the information of the
user in SHOW PROCESSLIST. Note that db is known in the 'db' column.
@@ -1787,21 +1810,24 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
if (sql_ex.opt_flags & REPLACE_FLAG)
handle_dup= DUP_REPLACE;
else if (sql_ex.opt_flags & IGNORE_FLAG)
- handle_dup= DUP_IGNORE;
+ {
+ ignore= 1;
+ handle_dup= DUP_ERROR;
+ }
else
{
/*
When replication is running fine, if it was DUP_ERROR on the
- master then we could choose DUP_IGNORE here, because if DUP_ERROR
+ master then we could choose IGNORE here, because if DUP_ERROR
suceeded on master, and data is identical on the master and slave,
- then there should be no uniqueness errors on slave, so DUP_IGNORE is
+ then there should be no uniqueness errors on slave, so IGNORE is
the same as DUP_ERROR. But in the unlikely case of uniqueness errors
(because the data on the master and slave happen to be different
(user error or bug), we want LOAD DATA to print an error message on
the slave to discover the problem.
If reading from net (a 3.23 master), mysql_load() will change this
- to DUP_IGNORE.
+ to IGNORE.
*/
handle_dup= DUP_ERROR;
}
@@ -1824,7 +1850,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
ex.skip_lines = skip_lines;
List<Item> field_list;
- set_fields(field_list);
+ set_fields(thd->db,field_list);
thd->variables.pseudo_thread_id= thread_id;
if (net)
{
@@ -1835,19 +1861,19 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
*/
thd->net.pkt_nr = net->pkt_nr;
}
- if (mysql_load(thd, &ex, &tables, field_list, handle_dup, net != 0,
+ if (mysql_load(thd, &ex, &tables, field_list, handle_dup, ignore, net != 0,
TL_WRITE))
thd->query_error = 1;
if (thd->cuted_fields)
{
/* log_pos is the position of the LOAD event in the master log */
- sql_print_error("\
-Slave: load data infile on table '%s' at log position %s in log \
-'%s' produced %ld warning(s). Default database: '%s'",
- (char*) table_name,
- llstr(log_pos,llbuff), RPL_LOG_NAME,
- (ulong) thd->cuted_fields,
- print_slave_db_safe(db));
+ sql_print_warning("Slave: load data infile on table '%s' at "
+ "log position %s in log '%s' produced %ld "
+ "warning(s). Default database: '%s'",
+ (char*) table_name,
+ llstr(log_pos,llbuff), RPL_LOG_NAME,
+ (ulong) thd->cuted_fields,
+ print_slave_db_safe(thd->db));
}
if (net)
net->pkt_nr= thd->net.pkt_nr;
@@ -1865,6 +1891,7 @@ Slave: load data infile on table '%s' at log position %s in log \
}
thd->net.vio = 0;
+ char *save_db= thd->db;
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->db= 0;
thd->query= 0;
@@ -1887,17 +1914,17 @@ Slave: load data infile on table '%s' at log position %s in log \
}
slave_print_error(rli,sql_errno,"\
Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
- err, (char*)table_name, print_slave_db_safe(db));
- free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
+ err, (char*)table_name, print_slave_db_safe(save_db));
+ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
return 1;
}
- free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
+ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
if (thd->is_fatal_error)
{
slave_print_error(rli,ER_UNKNOWN_ERROR, "\
Fatal error running LOAD DATA INFILE on table '%s'. Default database: '%s'",
- (char*)table_name, print_slave_db_safe(db));
+ (char*)table_name, print_slave_db_safe(save_db));
return 1;
}
@@ -2438,7 +2465,7 @@ void User_var_log_event::print(FILE* file, bool short_form, char* last_db)
*/
fprintf(file, ":=???;\n");
else
- fprintf(file, ":=_%s %s COLLATE %s;\n", cs->csname, hex_str, cs->name);
+ fprintf(file, ":=_%s %s COLLATE `%s`;\n", cs->csname, hex_str, cs->name);
my_afree(hex_str);
}
break;
@@ -2504,8 +2531,13 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli)
0 can be passed as last argument (reference on item)
*/
e.fix_fields(thd, 0, 0);
- e.update_hash(val, val_len, type, charset, DERIVATION_NONE);
- free_root(&thd->mem_root,0);
+ /*
+ A variable can just be considered as a table with
+ a single record and with a single column. Thus, like
+ a column value, it could always have IMPLICIT derivation.
+ */
+ e.update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT);
+ free_root(thd->mem_root,0);
rli->inc_event_relay_log_pos(get_event_len());
return 0;
@@ -2726,8 +2758,9 @@ Create_file_log_event::
Create_file_log_event(THD* thd_arg, sql_exchange* ex,
const char* db_arg, const char* table_name_arg,
List<Item>& fields_arg, enum enum_duplicates handle_dup,
+ bool ignore,
char* block_arg, uint block_len_arg, bool using_trans)
- :Load_log_event(thd_arg,ex,db_arg,table_name_arg,fields_arg,handle_dup,
+ :Load_log_event(thd_arg,ex,db_arg,table_name_arg,fields_arg,handle_dup, ignore,
using_trans),
fake_base(0), block(block_arg), event_buf(0), block_len(block_len_arg),
file_id(thd_arg->file_id = mysql_bin_log.next_file_id())
@@ -2839,7 +2872,7 @@ void Create_file_log_event::print(FILE* file, bool short_form,
if (enable_local)
{
- Load_log_event::print(file, 1, last_db, !check_fname_outside_temp_buf());
+ Load_log_event::print(file, short_form, last_db, !check_fname_outside_temp_buf());
/*
That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
SHOW BINLOG EVENTS we don't.
diff --git a/sql/log_event.h b/sql/log_event.h
index 8070c334d8b..f848f2ae1b9 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -264,6 +264,19 @@ struct sql_ex_info
*/
#define LOG_EVENT_THREAD_SPECIFIC_F 0x4
+/*
+ Suppress the generation of 'USE' statements before the actual
+ statement. This flag should be set for any events that does not need
+ the current database set to function correctly. Most notable cases
+ are 'CREATE DATABASE' and 'DROP DATABASE'.
+
+ This flags should only be used in exceptional circumstances, since
+ it introduce a significant change in behaviour regarding the
+ replication logic together with the flags --binlog-do-db and
+ --replicated-do-db.
+ */
+#define LOG_EVENT_SUPPRESS_USE_F 0x8
+
enum Log_event_type
{
UNKNOWN_EVENT= 0, START_EVENT= 1, QUERY_EVENT= 2, STOP_EVENT= 3,
@@ -331,8 +344,9 @@ public:
/*
Some 16 flags. Only one is really used now; look above for
- LOG_EVENT_TIME_F, LOG_EVENT_FORCED_ROTATE_F, LOG_EVENT_THREAD_SPECIFIC_F
- for notes.
+ LOG_EVENT_TIME_F, LOG_EVENT_FORCED_ROTATE_F,
+ LOG_EVENT_THREAD_SPECIFIC_F, and LOG_EVENT_SUPPRESS_USE_F for
+ notes.
*/
uint16 flags;
@@ -465,7 +479,7 @@ public:
#ifndef MYSQL_CLIENT
Query_log_event(THD* thd_arg, const char* query_arg, ulong query_length,
- bool using_trans);
+ bool using_trans, bool suppress_use);
const char* get_db() { return db; }
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
@@ -585,9 +599,9 @@ public:
Load_log_event(THD* thd, sql_exchange* ex, const char* db_arg,
const char* table_name_arg,
- List<Item>& fields_arg, enum enum_duplicates handle_dup,
+ List<Item>& fields_arg, enum enum_duplicates handle_dup, bool ignore,
bool using_trans);
- void set_fields(List<Item> &fields_arg);
+ void set_fields(const char* db, List<Item> &fields_arg);
const char* get_db() { return db; }
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
@@ -894,7 +908,7 @@ public:
Create_file_log_event(THD* thd, sql_exchange* ex, const char* db_arg,
const char* table_name_arg,
List<Item>& fields_arg,
- enum enum_duplicates handle_dup,
+ enum enum_duplicates handle_dup, bool ignore,
char* block_arg, uint block_len_arg,
bool using_trans);
#ifdef HAVE_REPLICATION
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 18944aebad9..6c77a8934dd 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -212,6 +212,10 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
#define OPTION_RELAXED_UNIQUE_CHECKS (1L << 27)
#define SELECT_NO_UNLOCK (1L << 28)
+/* If set to 0, then the thread will ignore all warnings with level notes.
+ Set by executing SET SQL_NOTES=1 */
+#define OPTION_SQL_NOTES (1L << 31)
+
/* Bits for different SQL modes modes (including ANSI mode) */
#define MODE_REAL_AS_FLOAT 1
#define MODE_PIPES_AS_CONCAT 2
@@ -350,7 +354,6 @@ inline THD *_current_thd(void)
#include "sql_udf.h"
class user_var_entry;
#include "item.h"
-#include "tztime.h"
typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
/* sql_parse.cc */
void free_items(Item *item);
@@ -363,7 +366,6 @@ bool check_merge_table_access(THD *thd, char *db,
TABLE_LIST *table_list);
int multi_update_precheck(THD *thd, TABLE_LIST *tables);
int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count);
-int insert_select_precheck(THD *thd, TABLE_LIST *tables);
int update_precheck(THD *thd, TABLE_LIST *tables);
int delete_precheck(THD *thd, TABLE_LIST *tables);
int insert_precheck(THD *thd, TABLE_LIST *tables);
@@ -371,12 +373,15 @@ int create_table_precheck(THD *thd, TABLE_LIST *tables,
TABLE_LIST *create_table);
Item *negate_expression(THD *thd, Item *expr);
#include "sql_class.h"
+#include "sql_acl.h"
+#include "tztime.h"
#include "opt_range.h"
#ifdef HAVE_QUERY_CACHE
struct Query_cache_query_flags
{
unsigned int client_long_flag:1;
+ unsigned int client_protocol_41:1;
uint character_set_client_num;
uint character_set_results_num;
uint collation_connection_num;
@@ -538,6 +543,7 @@ int mysql_alter_table(THD *thd, char *new_db, char *new_name,
List<Key> &keys,
uint order_num, ORDER *order,
enum enum_duplicates handle_duplicates,
+ bool ignore,
ALTER_INFO *alter_info, bool do_send_ok=1);
int mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool do_send_ok);
int mysql_create_like_table(THD *thd, TABLE_LIST *table,
@@ -557,12 +563,16 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields,
List<Item> &values,COND *conds,
uint order_num, ORDER *order, ha_rows limit,
- enum enum_duplicates handle_duplicates);
+ enum enum_duplicates handle_duplicates, bool ignore);
int mysql_multi_update(THD *thd, TABLE_LIST *table_list,
List<Item> *fields, List<Item> *values,
COND *conds, ulong options,
- enum enum_duplicates handle_duplicates,
+ enum enum_duplicates handle_duplicates, bool ignore,
SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex);
+int mysql_multi_update_lock(THD *thd,
+ TABLE_LIST *table_list,
+ List<Item> *fields,
+ SELECT_LEX *select_lex);
int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
TABLE_LIST *insert_table_list, TABLE *table,
List<Item> &fields, List_item *values,
@@ -570,7 +580,7 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
List<Item> &update_values, enum_duplicates duplic);
int mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
List<List_item> &values, List<Item> &update_fields,
- List<Item> &update_values, enum_duplicates flag);
+ List<Item> &update_values, enum_duplicates flag, bool ignore);
int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds);
int mysql_delete(THD *thd, TABLE_LIST *table, COND *conds, SQL_LIST *order,
ha_rows rows, ulong options);
@@ -681,13 +691,15 @@ int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags);
#define MYSQL_HA_FLUSH_ALL 0x02
/* sql_base.cc */
+#define TMP_TABLE_KEY_EXTRA 8
void set_item_name(Item *item,char *pos,uint length);
bool add_field_to_list(THD *thd, char *field_name, enum enum_field_types type,
char *length, char *decimal,
uint type_modifier,
Item *default_value, Item *on_update_value,
LEX_STRING *comment,
- char *change, TYPELIB *interval,CHARSET_INFO *cs,
+ char *change, List<String> *interval_list,
+ CHARSET_INFO *cs,
uint uint_geom_type);
void store_position_for_column(const char *name);
bool add_to_list(THD *thd, SQL_LIST &list,Item *group,bool asc=0);
@@ -722,6 +734,7 @@ void wait_for_refresh(THD *thd);
int open_tables(THD *thd, TABLE_LIST *tables, uint *counter);
int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
int open_and_lock_tables(THD *thd,TABLE_LIST *tables);
+int open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables);
void relink_tables_for_derived(THD *thd);
int lock_tables(THD *thd, TABLE_LIST *tables, uint counter);
TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
@@ -741,7 +754,7 @@ bool close_temporary_table(THD *thd, const char *db, const char *table_name);
void close_temporary(TABLE *table, bool delete_table=1);
bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db,
const char *table_name);
-void remove_db_from_cache(const my_string db);
+void remove_db_from_cache(const char *db);
void flush_tables();
bool remove_table_from_cache(THD *thd, const char *db, const char *table,
bool return_if_owned_by_thd=0);
@@ -757,6 +770,7 @@ bool eval_const_cond(COND *cond);
/* sql_load.cc */
int mysql_load(THD *thd,sql_exchange *ex, TABLE_LIST *table_list,
List<Item> &fields, enum enum_duplicates handle_duplicates,
+ bool ignore,
bool local_file,thr_lock_type lock_type);
int write_record(TABLE *table,COPY_INFO *info);
@@ -813,6 +827,7 @@ ulonglong find_set(TYPELIB *lib, const char *x, uint length, CHARSET_INFO *cs,
char **err_pos, uint *err_len, bool *set_warning);
uint find_type(TYPELIB *lib, const char *find, uint length, bool part_match);
uint find_type2(TYPELIB *lib, const char *find, uint length, CHARSET_INFO *cs);
+void unhex_type2(TYPELIB *lib);
uint check_word(TYPELIB *lib, const char *val, const char *end,
const char **end_of_word);
@@ -846,7 +861,7 @@ extern Gt_creator gt_creator;
extern Lt_creator lt_creator;
extern Ge_creator ge_creator;
extern Le_creator le_creator;
-extern char language[LIBLEN],reg_ext[FN_EXTLEN];
+extern char language[FN_REFLEN], reg_ext[FN_EXTLEN];
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];
@@ -909,7 +924,7 @@ extern char *default_tz_name;
extern MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log;
extern FILE *bootstrap_file;
-extern pthread_key(MEM_ROOT*,THR_MALLOC);
+extern pthread_key(MEM_ROOT**,THR_MALLOC);
extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open,
LOCK_thread_count,LOCK_mapped_file,LOCK_user_locks, LOCK_status,
LOCK_error_log, LOCK_delayed_insert, LOCK_uuid_generator,
@@ -932,7 +947,6 @@ extern SHOW_COMP_OPTION have_ndbcluster;
extern struct system_variables global_system_variables;
extern struct system_variables max_system_variables;
extern struct rand_struct sql_rand;
-extern KEY_CACHE *sql_key_cache;
extern const char *opt_date_time_formats[];
extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
@@ -1199,6 +1213,23 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr)
/*
+ SYNOPSYS
+ hexchar_to_int()
+ convert a hex digit into number
+*/
+
+inline int hexchar_to_int(char c)
+{
+ if (c <= '9' && c >= '0')
+ return c-'0';
+ c|=32;
+ if (c <= 'f' && c >= 'a')
+ return c-'a'+10;
+ return -1;
+}
+
+
+/*
Some functions that are different in the embedded library and the normal
server
*/
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 7062334edbb..f1b1d8a7d86 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -17,7 +17,6 @@
#include "mysql_priv.h"
#include <m_ctype.h>
#include <my_dir.h>
-#include "sql_acl.h"
#include "slave.h"
#include "sql_repl.h"
#include "repl_failsafe.h"
@@ -36,6 +35,33 @@
#ifdef HAVE_NDBCLUSTER_DB
#include "ha_ndbcluster.h"
#endif
+
+#ifdef HAVE_INNOBASE_DB
+#define OPT_INNODB_DEFAULT 1
+#else
+#define OPT_INNODB_DEFAULT 0
+#endif
+#ifdef HAVE_BERKLEY_DB
+#define OPT_BDB_DEFAULT 1
+#else
+#define OPT_BDB_DEFAULT 0
+#endif
+#ifdef HAVE_ISAM_DB
+#define OPT_ISAM_DEFAULT 1
+#else
+#define OPT_ISAM_DEFAULT 0
+#endif
+#ifdef HAVE_NDBCLUSTER_DB
+#define OPT_NDBCLUSTER_DEFAULT 0
+#if defined(NDB_SHM_TRANSPORTER) && MYSQL_VERSION_ID >= 50000
+#define OPT_NDB_SHM_DEFAULT 1
+#else
+#define OPT_NDB_SHM_DEFAULT 0
+#endif
+#else
+#define OPT_NDBCLUSTER_DEFAULT 0
+#endif
+
#include <nisam.h>
#include <thr_alarm.h>
#include <ft_global.h>
@@ -115,21 +141,16 @@ extern "C" { // Because of SCO 3.2V4.2
int allow_severity = LOG_INFO;
int deny_severity = LOG_WARNING;
-#ifdef __STDC__
-#define my_fromhost(A) fromhost(A)
-#define my_hosts_access(A) hosts_access(A)
-#define my_eval_client(A) eval_client(A)
-#else
-#define my_fromhost(A) fromhost()
-#define my_hosts_access(A) hosts_access()
-#define my_eval_client(A) eval_client()
-#endif /* __STDC__ */
#endif /* HAVE_LIBWRAP */
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
+#define zVOLSTATE_ACTIVE 6
+#define zVOLSTATE_DEACTIVE 2
+#define zVOLSTATE_MAINTENANCE 3
+
#ifdef __NETWARE__
#include <nks/vm.h>
#include <library.h>
@@ -146,6 +167,7 @@ static VolumeID_t datavolid;
static event_handle_t eh;
static Report_t ref;
static void *refneb= NULL;
+my_bool event_flag= FALSE;
static int volumeid= -1;
/* NEB event callback */
@@ -263,6 +285,10 @@ my_bool opt_safe_user_create = 0, opt_no_mix_types = 0;
my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0;
my_bool opt_log_slave_updates= 0;
my_bool opt_console= 0, opt_bdb, opt_innodb, opt_isam, opt_ndbcluster;
+#ifdef HAVE_NDBCLUSTER_DB
+const char *opt_ndbcluster_connectstring= 0;
+my_bool opt_ndb_shm, opt_ndb_optimized_node_selection;
+#endif
my_bool opt_readonly, use_temp_pool, relay_log_purge;
my_bool opt_sync_bdb_logs, opt_sync_frm;
my_bool opt_secure_auth= 0;
@@ -272,12 +298,14 @@ my_bool lower_case_file_system= 0;
my_bool opt_innodb_safe_binlog= 0;
volatile bool mqh_used = 0;
+#ifdef HAVE_INITGROUPS
+static bool calling_initgroups= FALSE; /* Used in SIGSEGV handler. */
+#endif
uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options;
uint delay_key_write_options, protocol_version;
uint lower_case_table_names;
uint opt_crash_binlog_innodb;
uint volatile thread_count, thread_running, kill_cached_threads, wake_thread;
-
ulong back_log, connect_timeout, concurrency;
ulong server_id, thd_startup_options;
ulong table_cache_size, thread_stack, thread_stack_min, what_to_log;
@@ -318,7 +346,7 @@ char *default_tz_name;
char log_error_file[FN_REFLEN], glob_hostname[FN_REFLEN];
char* log_error_file_ptr= log_error_file;
char mysql_real_data_home[FN_REFLEN],
- language[LIBLEN],reg_ext[FN_EXTLEN], mysql_charsets_dir[FN_REFLEN],
+ language[FN_REFLEN], reg_ext[FN_EXTLEN], mysql_charsets_dir[FN_REFLEN],
*mysqld_user,*mysqld_chroot, *opt_init_file,
*opt_init_connect, *opt_init_slave,
def_ft_boolean_syntax[sizeof(ft_boolean_syntax)];
@@ -361,7 +389,6 @@ struct system_variables max_system_variables;
MY_TMPDIR mysql_tmpdir_list;
MY_BITMAP temp_pool;
-KEY_CACHE *sql_key_cache;
CHARSET_INFO *system_charset_info, *files_charset_info ;
CHARSET_INFO *national_charset_info, *table_alias_charset;
@@ -374,7 +401,7 @@ SHOW_COMP_OPTION have_crypt, have_compress;
/* Thread specific variables */
-pthread_key(MEM_ROOT*,THR_MALLOC);
+pthread_key(MEM_ROOT**,THR_MALLOC);
pthread_key(THD*, THR_THD);
pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count,
LOCK_mapped_file, LOCK_status,
@@ -468,6 +495,7 @@ Query_cache query_cache;
#ifdef HAVE_SMEM
char *shared_memory_base_name= default_shared_memory_base_name;
bool opt_enable_shared_memory;
+HANDLE smem_event_connect_request= 0;
#endif
#include "sslopt-vars.h"
@@ -502,6 +530,7 @@ extern "C" pthread_handler_decl(handle_slave,arg);
static ulong find_bit_type(const char *x, TYPELIB *bit_lib);
static void clean_up(bool print_message);
static void clean_up_mutexes(void);
+static void wait_for_signal_thread_to_end(void);
static int test_if_case_insensitive(const char *dir_name);
static void create_pid_file();
@@ -540,7 +569,7 @@ static void close_connections(void)
struct timespec abstime;
int error;
LINT_INIT(error);
- DBUG_PRINT("info",("Waiting for select_thread"));
+ DBUG_PRINT("info",("Waiting for select thread"));
#ifndef DONT_USE_THR_ALARM
if (pthread_kill(select_thread,THR_CLIENT_ALARM))
@@ -743,6 +772,15 @@ void kill_mysql(void)
CloseHandle(hEvent);
*/
}
+#ifdef HAVE_SMEM
+ /*
+ Send event to smem_event_connect_request for aborting
+ */
+ if (!SetEvent(smem_event_connect_request))
+ {
+ DBUG_PRINT("error",("Got error: %ld from SetEvent of smem_event_connect_request",GetLastError()));
+ }
+#endif
#endif
#elif defined(OS2)
pthread_cond_signal( &eventShutdown); // post semaphore
@@ -805,7 +843,8 @@ static void __cdecl kill_server(int sig_ptr)
else
unireg_end();
#ifdef __NETWARE__
- pthread_join(select_thread, NULL); // wait for main thread
+ if (!event_flag)
+ pthread_join(select_thread, NULL); // wait for main thread
#endif /* __NETWARE__ */
pthread_exit(0); /* purecov: deadcode */
@@ -880,6 +919,7 @@ extern "C" void unireg_abort(int exit_code)
sql_print_error("Aborting\n");
clean_up(exit_code || !opt_bootstrap); /* purecov: inspected */
DBUG_PRINT("quit",("done with cleanup in unireg_abort"));
+ wait_for_signal_thread_to_end();
clean_up_mutexes();
my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
exit(exit_code); /* purecov: inspected */
@@ -914,6 +954,7 @@ void clean_up(bool print_message)
item_user_lock_free();
lex_free(); /* Free some memory */
set_var_free();
+ free_charsets();
#ifdef HAVE_DLOPEN
if (!opt_noacl)
udf_free();
@@ -984,6 +1025,29 @@ void clean_up(bool print_message)
} /* clean_up */
+/*
+ This is mainly needed when running with purify, but it's still nice to
+ know that all child threads have died when mysqld exits
+*/
+
+static void wait_for_signal_thread_to_end()
+{
+#ifndef __NETWARE__
+ uint i;
+ /*
+ Wait up to 10 seconds for signal thread to die. We use this mainly to
+ avoid getting warnings that my_thread_end has not been called
+ */
+ for (i= 0 ; i < 100 && signal_thread_in_use; i++)
+ {
+ if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL))
+ break;
+ my_sleep(100); // Give it time to die
+ }
+#endif
+}
+
+
static void clean_up_mutexes()
{
(void) pthread_mutex_destroy(&LOCK_mysql_create_db);
@@ -1106,7 +1170,15 @@ static void set_user(const char *user, struct passwd *user_info)
#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
DBUG_ASSERT(user_info);
#ifdef HAVE_INITGROUPS
- initgroups((char*) user,user_info->pw_gid);
+ /*
+ We can get a SIGSEGV when calling initgroups() on some systems when NSS
+ is configured to use LDAP and the server is statically linked. We set
+ calling_initgroups as a flag to the SIGSEGV handler that is then used to
+ output a specific message to help the user resolve this problem.
+ */
+ calling_initgroups= TRUE;
+ initgroups((char*) user, user_info->pw_gid);
+ calling_initgroups= FALSE;
#endif
if (setgid(user_info->pw_gid) == -1)
{
@@ -1515,20 +1587,20 @@ static void check_data_home(const char *path)
// down server event callback
void mysql_down_server_cb(void *, void *)
{
+ event_flag = TRUE;
kill_server(0);
}
// destroy callback resources
void mysql_cb_destroy(void *)
-{
- UnRegisterEventNotification(eh); // cleanup down event notification
+{
+ UnRegisterEventNotification(eh); // cleanup down event notification
NX_UNWRAP_INTERFACE(ref);
-
- /* Deregister NSS volume deactivation event */
- NX_UNWRAP_INTERFACE(refneb);
+ /* Deregister NSS volume deactivation event */
+ NX_UNWRAP_INTERFACE(refneb);
if (neb_consumer_id)
- UnRegisterConsumer(neb_consumer_id, NULL);
+ UnRegisterConsumer(neb_consumer_id, NULL);
}
@@ -1548,7 +1620,7 @@ void mysql_cb_init()
Register for volume deactivation event
Wrap the callback function, as it is called by non-LibC thread
*/
- (void)NX_WRAP_INTERFACE(neb_event_callback, 1, &refneb);
+ (void *) NX_WRAP_INTERFACE(neb_event_callback, 1, &refneb);
registerwithneb();
NXVmRegisterExitHandler(mysql_cb_destroy, NULL); // clean-up
@@ -1635,7 +1707,9 @@ ulong neb_event_callback(struct EventBlock *eblock)
voldata= (EventChangeVolStateEnter_s *)eblock->EBEventData;
/* Deactivation of a volume */
- if ((voldata->oldState == 6 && voldata->newState == 2))
+ if ((voldata->oldState == zVOLSTATE_ACTIVE &&
+ voldata->newState == zVOLSTATE_DEACTIVE ||
+ voldata->newState == zVOLSTATE_MAINTENANCE))
{
/*
Ensure that we bring down MySQL server only for MySQL data
@@ -1645,7 +1719,9 @@ ulong neb_event_callback(struct EventBlock *eblock)
{
consoleprintf("MySQL data volume is deactivated, shutting down MySQL Server \n");
nw_panic = TRUE;
+ event_flag= TRUE;
kill_server(0);
+
}
}
return 0;
@@ -1719,8 +1795,8 @@ static void init_signals(void)
for (uint i=0 ; i < sizeof(signals)/sizeof(int) ; i++)
signal(signals[i], kill_server);
mysql_cb_init(); // initialize callbacks
-}
+}
static void start_signal_handler(void)
{
@@ -1811,14 +1887,14 @@ We will try our best to scrape up some info that will hopefully help diagnose\n\
the problem, but since we have already crashed, something is definitely wrong\n\
and this may fail.\n\n");
fprintf(stderr, "key_buffer_size=%lu\n",
- (ulong) sql_key_cache->key_cache_mem_size);
+ (ulong) dflt_key_cache->key_cache_mem_size);
fprintf(stderr, "read_buffer_size=%ld\n", global_system_variables.read_buff_size);
fprintf(stderr, "max_used_connections=%ld\n", max_used_connections);
fprintf(stderr, "max_connections=%ld\n", max_connections);
fprintf(stderr, "threads_connected=%d\n", thread_count);
fprintf(stderr, "It is possible that mysqld could use up to \n\
key_buffer_size + (read_buffer_size + sort_buffer_size)*max_connections = %ld K\n\
-bytes of memory\n", ((ulong) sql_key_cache->key_cache_mem_size +
+bytes of memory\n", ((ulong) dflt_key_cache->key_cache_mem_size +
(global_system_variables.read_buff_size +
global_system_variables.sortbuff_size) *
max_connections)/ 1024);
@@ -1856,6 +1932,17 @@ information that should help you find out what is causing the crash.\n");
fflush(stderr);
#endif /* HAVE_STACKTRACE */
+#ifdef HAVE_INITGROUPS
+ if (calling_initgroups)
+ fprintf(stderr, "\n\
+This crash occured while the server was calling initgroups(). This is\n\
+often due to the use of a mysqld that is statically linked against glibc\n\
+and configured to use LDAP in /etc/nsswitch.conf. You will need to either\n\
+upgrade to a version of glibc that does not have this problem (2.3.4 or\n\
+later when used with nscd), disable LDAP in your nsswitch.conf, or use a\n\
+mysqld that is not statically linked.\n");
+#endif
+
if (test_flags & TEST_CORE_ON_SIGNAL)
{
fprintf(stderr, "Writing a core file\n");
@@ -2055,6 +2142,7 @@ extern "C" void *signal_hand(void *arg __attribute__((unused)))
while ((error=my_sigwait(&set,&sig)) == EINTR) ;
if (cleanup_done)
{
+ DBUG_PRINT("quit",("signal_handler: calling my_thread_end()"));
my_thread_end();
signal_thread_in_use= 0;
pthread_exit(0); // Safety
@@ -2085,12 +2173,12 @@ extern "C" void *signal_hand(void *arg __attribute__((unused)))
case SIGHUP:
if (!abort_loop)
{
+ mysql_print_status((THD*) 0); // Print some debug info
reload_acl_and_cache((THD*) 0,
(REFRESH_LOG | REFRESH_TABLES | REFRESH_FAST |
REFRESH_STATUS | REFRESH_GRANT |
REFRESH_THREADS | REFRESH_HOSTS),
(TABLE_LIST*) 0, NULL); // Flush logs
- mysql_print_status((THD*) 0); // Send debug some info
}
break;
#ifdef USE_ONE_SIGNAL_HAND
@@ -2227,7 +2315,13 @@ extern "C" pthread_handler_decl(handle_shutdown,arg)
#endif
-const char *load_default_groups[]= { "mysqld","server",MYSQL_BASE_VERSION,0,0};
+const char *load_default_groups[]= {
+#ifdef HAVE_NDBCLUSTER_DB
+"mysql_cluster",
+#endif
+"mysqld","server",MYSQL_BASE_VERSION,0,0};
+static const int load_default_groups_sz=
+sizeof(load_default_groups)/sizeof(load_default_groups[0]);
bool open_log(MYSQL_LOG *log, const char *hostname,
const char *opt_name, const char *extension,
@@ -2305,8 +2399,6 @@ bool init_global_datetime_format(timestamp_type format_type,
static int init_common_variables(const char *conf_file_name, int argc,
char **argv, const char **groups)
{
- my_umask=0660; // Default umask for new files
- my_umask_dir=0700; // Default umask for new directories
umask(((~my_umask) & 0666));
tzset(); // Set tzname
@@ -2552,8 +2644,10 @@ static int init_server_components()
if (opt_bin_log)
{
- open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin",
- opt_binlog_index_name, LOG_BIN, 0, 0, max_binlog_size);
+ /* If we fail to open binlog, it's going to hinder our recovery, so die */
+ if (open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin",
+ opt_binlog_index_name, LOG_BIN, 0, 0, max_binlog_size))
+ unireg_abort(1);
using_update_log=1;
#ifdef HAVE_REPLICATION
if (expire_logs_days)
@@ -2658,8 +2752,6 @@ server.");
/* call ha_init_key_cache() on all key caches to init them */
process_key_caches(&ha_init_key_cache);
- /* We must set dflt_key_cache in case we are using ISAM tables */
- dflt_key_cache= sql_key_cache;
#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && !defined(EMBEDDED_LIBRARY)
if (locked_in_memory && !getuid())
@@ -2805,6 +2897,7 @@ int win_main(int argc, char **argv)
int main(int argc, char **argv)
#endif
{
+
DEBUGGER_OFF;
MY_INIT(argv[0]); // init my_sys library & pthreads
@@ -2871,6 +2964,17 @@ You should consider changing lower_case_table_names to 1 or 2",
lower_case_table_names= 2;
}
}
+ else if (lower_case_table_names == 2 &&
+ !(lower_case_file_system=
+ (test_if_case_insensitive(mysql_real_data_home) == 1)))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_warning("lower_case_table_names was set to 2, even though your "
+ "the file system '%s' is case sensitive. Now setting "
+ "lower_case_table_names to 0 to avoid future problems.",
+ mysql_real_data_home);
+ lower_case_table_names= 0;
+ }
select_thread=pthread_self();
select_thread_in_use=1;
@@ -2918,11 +3022,10 @@ we force server id to 2, but this MySQL server will not act as a slave.");
exit(1);
#ifdef __WIN__
-#define MYSQL_ERR_FILE "mysql.err"
if (!opt_console)
{
- freopen(MYSQL_ERR_FILE,"a+",stdout);
- freopen(MYSQL_ERR_FILE,"a+",stderr);
+ freopen(log_error_file,"a+",stdout);
+ freopen(log_error_file,"a+",stderr);
FreeConsole(); // Remove window
}
#endif
@@ -3001,7 +3104,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
#endif /* __NT__ */
/* (void) pthread_attr_destroy(&connection_attrib); */
-
+
DBUG_PRINT("quit",("Exiting main thread"));
#ifndef __WIN__
@@ -3034,23 +3137,10 @@ we force server id to 2, but this MySQL server will not act as a slave.");
CloseHandle(hEventShutdown);
}
#endif
-#ifndef __NETWARE__
- {
- uint i;
- /*
- Wait up to 10 seconds for signal thread to die. We use this mainly to
- avoid getting warnings that my_thread_end has not been called
- */
- for (i= 0 ; i < 100 && signal_thread_in_use; i++)
- {
- if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL))
- break;
- my_sleep(100); // Give it time to die
- }
- }
-#endif
+ wait_for_signal_thread_to_end();
clean_up_mutexes();
my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
+
exit(0);
return(0); /* purecov: deadcode */
}
@@ -3177,7 +3267,7 @@ int main(int argc, char **argv)
and we are now stuck with it.
*/
if (my_strcasecmp(system_charset_info, argv[1],"mysql"))
- load_default_groups[3]= argv[1];
+ load_default_groups[load_default_groups_sz-2]= argv[1];
start_mode= 1;
Service.Init(argv[1], mysql_service);
return 0;
@@ -3198,7 +3288,7 @@ int main(int argc, char **argv)
opt_argv=argv;
start_mode= 1;
if (my_strcasecmp(system_charset_info, argv[2],"mysql"))
- load_default_groups[3]= argv[2];
+ load_default_groups[load_default_groups_sz-2]= argv[2];
Service.Init(argv[2], mysql_service);
return 0;
}
@@ -3525,8 +3615,8 @@ extern "C" pthread_handler_decl(handle_connections_sockets,
struct request_info req;
signal(SIGCHLD, SIG_DFL);
request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE, new_sock, NULL);
- my_fromhost(&req);
- if (!my_hosts_access(&req))
+ fromhost(&req);
+ if (!hosts_access(&req))
{
/*
This may be stupid but refuse() includes an exit(0)
@@ -3534,7 +3624,7 @@ extern "C" pthread_handler_decl(handle_connections_sockets,
clean_exit() - same stupid thing ...
*/
syslog(deny_severity, "refused connect from %s",
- my_eval_client(&req));
+ eval_client(&req));
/*
C++ sucks (the gibberish in front just translates the supplied
@@ -3708,7 +3798,6 @@ pthread_handler_decl(handle_connections_shared_memory,arg)
/* file-mapping object, use for create shared memory */
HANDLE handle_connect_file_map= 0;
char *handle_connect_map= 0; // pointer on shared memory
- HANDLE event_connect_request= 0; // for start connection actions
HANDLE event_connect_answer= 0;
ulong smem_buffer_length= shared_memory_buffer_length + 4;
ulong connect_number= 1;
@@ -3729,7 +3818,7 @@ pthread_handler_decl(handle_connections_shared_memory,arg)
*/
suffix_pos= strxmov(tmp,shared_memory_base_name,"_",NullS);
strmov(suffix_pos, "CONNECT_REQUEST");
- if ((event_connect_request= CreateEvent(0,FALSE,FALSE,tmp)) == 0)
+ if ((smem_event_connect_request= CreateEvent(0,FALSE,FALSE,tmp)) == 0)
{
errmsg= "Could not create request event";
goto error;
@@ -3760,7 +3849,13 @@ pthread_handler_decl(handle_connections_shared_memory,arg)
while (!abort_loop)
{
/* Wait a request from client */
- WaitForSingleObject(event_connect_request,INFINITE);
+ WaitForSingleObject(smem_event_connect_request,INFINITE);
+
+ /*
+ it can be after shutdown command
+ */
+ if (abort_loop)
+ goto error;
HANDLE handle_client_file_map= 0;
char *handle_client_map= 0;
@@ -3768,6 +3863,7 @@ pthread_handler_decl(handle_connections_shared_memory,arg)
HANDLE event_client_read= 0; // for transfer data server <-> client
HANDLE event_server_wrote= 0;
HANDLE event_server_read= 0;
+ HANDLE event_conn_closed= 0;
THD *thd= 0;
p= int10_to_str(connect_number, connect_number_char, 10);
@@ -3798,29 +3894,35 @@ pthread_handler_decl(handle_connections_shared_memory,arg)
goto errorconn;
}
strmov(suffix_pos, "CLIENT_WROTE");
- if ((event_client_wrote= CreateEvent(0,FALSE,FALSE,tmp)) == 0)
+ if ((event_client_wrote= CreateEvent(0, FALSE, FALSE, tmp)) == 0)
{
errmsg= "Could not create client write event";
goto errorconn;
}
strmov(suffix_pos, "CLIENT_READ");
- if ((event_client_read= CreateEvent(0,FALSE,FALSE,tmp)) == 0)
+ if ((event_client_read= CreateEvent(0, FALSE, FALSE, tmp)) == 0)
{
errmsg= "Could not create client read event";
goto errorconn;
}
strmov(suffix_pos, "SERVER_READ");
- if ((event_server_read= CreateEvent(0,FALSE,FALSE,tmp)) == 0)
+ if ((event_server_read= CreateEvent(0, FALSE, FALSE, tmp)) == 0)
{
errmsg= "Could not create server read event";
goto errorconn;
}
strmov(suffix_pos, "SERVER_WROTE");
- if ((event_server_wrote= CreateEvent(0,FALSE,FALSE,tmp)) == 0)
+ if ((event_server_wrote= CreateEvent(0, FALSE, FALSE, tmp)) == 0)
{
errmsg= "Could not create server write event";
goto errorconn;
}
+ strmov(suffix_pos, "CONNECTION_CLOSED");
+ if ((event_conn_closed= CreateEvent(0, TRUE , FALSE, tmp)) == 0)
+ {
+ errmsg= "Could not create closed connection event";
+ goto errorconn;
+ }
if (abort_loop)
goto errorconn;
if (!(thd= new THD))
@@ -3839,13 +3941,14 @@ pthread_handler_decl(handle_connections_shared_memory,arg)
goto errorconn;
}
if (!(thd->net.vio= vio_new_win32shared_memory(&thd->net,
- handle_client_file_map,
- handle_client_map,
- event_client_wrote,
- event_client_read,
- event_server_wrote,
- event_server_read)) ||
- my_net_init(&thd->net, thd->net.vio))
+ handle_client_file_map,
+ handle_client_map,
+ event_client_wrote,
+ event_client_read,
+ event_server_wrote,
+ event_server_read,
+ event_conn_closed)) ||
+ my_net_init(&thd->net, thd->net.vio))
{
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
errmsg= 0;
@@ -3865,12 +3968,20 @@ errorconn:
NullS);
sql_perror(buff);
}
- if (handle_client_file_map) CloseHandle(handle_client_file_map);
- if (handle_client_map) UnmapViewOfFile(handle_client_map);
- if (event_server_wrote) CloseHandle(event_server_wrote);
- if (event_server_read) CloseHandle(event_server_read);
- if (event_client_wrote) CloseHandle(event_client_wrote);
- if (event_client_read) CloseHandle(event_client_read);
+ if (handle_client_file_map)
+ CloseHandle(handle_client_file_map);
+ if (handle_client_map)
+ UnmapViewOfFile(handle_client_map);
+ if (event_server_wrote)
+ CloseHandle(event_server_wrote);
+ if (event_server_read)
+ CloseHandle(event_server_read);
+ if (event_client_wrote)
+ CloseHandle(event_client_wrote);
+ if (event_client_read)
+ CloseHandle(event_client_read);
+ if (event_conn_closed)
+ CloseHandle(event_conn_closed);
delete thd;
}
@@ -3885,7 +3996,7 @@ error:
if (handle_connect_map) UnmapViewOfFile(handle_connect_map);
if (handle_connect_file_map) CloseHandle(handle_connect_file_map);
if (event_connect_answer) CloseHandle(event_connect_answer);
- if (event_connect_request) CloseHandle(event_connect_request);
+ if (smem_event_connect_request) CloseHandle(smem_event_connect_request);
decrement_handler_count();
DBUG_RETURN(0);
@@ -3950,7 +4061,11 @@ enum options_mysqld
OPT_INNODB_FILE_PER_TABLE, OPT_CRASH_BINLOG_INNODB,
OPT_INNODB_LOCKS_UNSAFE_FOR_BINLOG,
OPT_SAFE_SHOW_DB, OPT_INNODB_SAFE_BINLOG,
- OPT_INNODB, OPT_ISAM, OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, OPT_SKIP_SAFEMALLOC,
+ OPT_INNODB, OPT_ISAM,
+ OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, OPT_NDB_USE_EXACT_COUNT,
+ OPT_NDB_FORCE_SEND, OPT_NDB_AUTOINCREMENT_PREFETCH_SZ,
+ OPT_NDB_SHM, OPT_NDB_OPTIMIZED_NODE_SELECTION,
+ OPT_SKIP_SAFEMALLOC,
OPT_TEMP_POOL, OPT_TX_ISOLATION,
OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS,
OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL,
@@ -4023,7 +4138,11 @@ enum options_mysqld
OPT_RANGE_ALLOC_BLOCK_SIZE,
OPT_QUERY_ALLOC_BLOCK_SIZE, OPT_QUERY_PREALLOC_SIZE,
OPT_TRANS_ALLOC_BLOCK_SIZE, OPT_TRANS_PREALLOC_SIZE,
- OPT_SYNC_FRM, OPT_SYNC_BINLOG, OPT_BDB_NOSYNC,
+ OPT_SYNC_FRM, OPT_SYNC_BINLOG,
+ OPT_SYNC_REPLICATION,
+ OPT_SYNC_REPLICATION_SLAVE_ID,
+ OPT_SYNC_REPLICATION_TIMEOUT,
+ OPT_BDB_NOSYNC,
OPT_ENABLE_SHARED_MEMORY,
OPT_SHARED_MEMORY_BASE_NAME,
OPT_OLD_PASSWORDS,
@@ -4062,7 +4181,7 @@ struct my_option my_long_options[] =
0, 0, 0, 0, 0, 0},
{"bdb", OPT_BDB, "Enable Berkeley DB (if this version of MySQL supports it). \
Disable with --skip-bdb (will save memory).",
- (gptr*) &opt_bdb, (gptr*) &opt_bdb, 0, GET_BOOL, NO_ARG, 1, 0, 0,
+ (gptr*) &opt_bdb, (gptr*) &opt_bdb, 0, GET_BOOL, NO_ARG, OPT_BDB_DEFAULT, 0, 0,
0, 0, 0},
#ifdef HAVE_BERKELEY_DB
{"bdb-home", OPT_BDB_HOME, "Berkeley home directory.", (gptr*) &berkeley_home,
@@ -4199,7 +4318,7 @@ Disable with --skip-bdb (will save memory).",
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"innodb", OPT_INNODB, "Enable InnoDB (if this version of MySQL supports it). \
Disable with --skip-innodb (will save memory).",
- (gptr*) &opt_innodb, (gptr*) &opt_innodb, 0, GET_BOOL, NO_ARG, 1, 0, 0,
+ (gptr*) &opt_innodb, (gptr*) &opt_innodb, 0, GET_BOOL, NO_ARG, OPT_INNODB_DEFAULT, 0, 0,
0, 0, 0},
{"innodb_data_file_path", OPT_INNODB_DATA_FILE_PATH,
"Path to individual files and their sizes.",
@@ -4243,7 +4362,7 @@ Disable with --skip-innodb (will save memory).",
"Percentage of dirty pages allowed in bufferpool.", (gptr*) &srv_max_buf_pool_modified_pct,
(gptr*) &srv_max_buf_pool_modified_pct, 0, GET_ULONG, REQUIRED_ARG, 90, 0, 100, 0, 0, 0},
{"innodb_max_purge_lag", OPT_INNODB_MAX_PURGE_LAG,
- "",
+ "Desired maximum length of the purge queue (0 = no limit)",
(gptr*) &srv_max_purge_lag,
(gptr*) &srv_max_purge_lag, 0, GET_LONG, REQUIRED_ARG, 0, 0, ~0L,
0, 1L, 0},
@@ -4252,14 +4371,14 @@ Disable with --skip-innodb (will save memory).",
(gptr*) &innobase_create_status_file, (gptr*) &innobase_create_status_file,
0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"innodb_table_locks", OPT_INNODB_TABLE_LOCKS,
- "If Innodb should enforce LOCK TABLE",
+ "Enable InnoDB locking in LOCK TABLES",
(gptr*) &global_system_variables.innodb_table_locks,
(gptr*) &global_system_variables.innodb_table_locks,
0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
#endif /* End HAVE_INNOBASE_DB */
{"isam", OPT_ISAM, "Enable ISAM (if this version of MySQL supports it). \
Disable with --skip-isam.",
- (gptr*) &opt_isam, (gptr*) &opt_isam, 0, GET_BOOL, NO_ARG, 1, 0, 0,
+ (gptr*) &opt_isam, (gptr*) &opt_isam, 0, GET_BOOL, NO_ARG, OPT_ISAM_DEFAULT, 0, 0,
0, 0, 0},
{"language", 'L',
"Client error messages in given language. May be given as a full path.",
@@ -4386,12 +4505,51 @@ master-ssl",
GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"ndbcluster", OPT_NDBCLUSTER, "Enable NDB Cluster (if this version of MySQL supports it). \
Disable with --skip-ndbcluster (will save memory).",
- (gptr*) &opt_ndbcluster, (gptr*) &opt_ndbcluster, 0, GET_BOOL, NO_ARG, 1, 0, 0,
- 0, 0, 0},
+ (gptr*) &opt_ndbcluster, (gptr*) &opt_ndbcluster, 0, GET_BOOL, NO_ARG,
+ OPT_NDBCLUSTER_DEFAULT, 0, 0, 0, 0, 0},
#ifdef HAVE_NDBCLUSTER_DB
- {"ndb-connectstring", OPT_NDB_CONNECTSTRING, "Connect string for ndbcluster.",
- (gptr*) &ndbcluster_connectstring, (gptr*) &ndbcluster_connectstring, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"ndb-connectstring", OPT_NDB_CONNECTSTRING,
+ "Connect string for ndbcluster.",
+ (gptr*) &opt_ndbcluster_connectstring,
+ (gptr*) &opt_ndbcluster_connectstring,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"ndb-autoincrement-prefetch-sz", OPT_NDB_AUTOINCREMENT_PREFETCH_SZ,
+ "Specify number of autoincrement values that are prefetched.",
+ (gptr*) &global_system_variables.ndb_autoincrement_prefetch_sz,
+ (gptr*) &global_system_variables.ndb_autoincrement_prefetch_sz,
+ 0, GET_INT, REQUIRED_ARG, 32, 1, 256, 0, 0, 0},
+ {"ndb-force-send", OPT_NDB_FORCE_SEND,
+ "Force send of buffers to ndb immediately without waiting for "
+ "other threads.",
+ (gptr*) &global_system_variables.ndb_force_send,
+ (gptr*) &global_system_variables.ndb_force_send,
+ 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
+ {"ndb_force_send", OPT_NDB_FORCE_SEND,
+ "same as --ndb-force-send.",
+ (gptr*) &global_system_variables.ndb_force_send,
+ (gptr*) &global_system_variables.ndb_force_send,
+ 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
+ {"ndb-use-exact-count", OPT_NDB_USE_EXACT_COUNT,
+ "Use exact records count during query planning and for fast "
+ "select count(*), disable for faster queries.",
+ (gptr*) &global_system_variables.ndb_use_exact_count,
+ (gptr*) &global_system_variables.ndb_use_exact_count,
+ 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
+ {"ndb_use_exact_count", OPT_NDB_USE_EXACT_COUNT,
+ "same as --ndb-use-exact-count.",
+ (gptr*) &global_system_variables.ndb_use_exact_count,
+ (gptr*) &global_system_variables.ndb_use_exact_count,
+ 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
+ {"ndb-shm", OPT_NDB_SHM,
+ "Use shared memory connections when available.",
+ (gptr*) &opt_ndb_shm,
+ (gptr*) &opt_ndb_shm,
+ 0, GET_BOOL, OPT_ARG, OPT_NDB_SHM_DEFAULT, 0, 0, 0, 0, 0},
+ {"ndb-optimized-node-selection", OPT_NDB_OPTIMIZED_NODE_SELECTION,
+ "Select nodes for transactions in a more optimal way.",
+ (gptr*) &opt_ndb_optimized_node_selection,
+ (gptr*) &opt_ndb_optimized_node_selection,
+ 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
#endif
{"new", 'n', "Use very new possible 'unsafe' functions.",
(gptr*) &global_system_variables.new_mode,
@@ -4692,7 +4850,8 @@ replicating a LOAD DATA INFILE command.",
(gptr*) &delayed_queue_size, (gptr*) &delayed_queue_size, 0, GET_ULONG,
REQUIRED_ARG, DELAYED_QUEUE_SIZE, 1, ~0L, 0, 1, 0},
{"expire_logs_days", OPT_EXPIRE_LOGS_DAYS,
- "Binary logs will be rotated after expire-log-days days ",
+ "If non-zero, binary logs will be purged after expire_logs_days "
+ "days; possible purges happen at startup and at binary log rotation.",
(gptr*) &expire_logs_days,
(gptr*) &expire_logs_days, 0, GET_ULONG,
REQUIRED_ARG, 0, 0, 99, 0, 1, 0},
@@ -4735,7 +4894,7 @@ replicating a LOAD DATA INFILE command.",
"Data file autoextend increment in megabytes",
(gptr*) &srv_auto_extend_increment,
(gptr*) &srv_auto_extend_increment,
- 0, GET_LONG, REQUIRED_ARG, 8L, 1L, ~0L, 0, 1L, 0},
+ 0, GET_LONG, REQUIRED_ARG, 8L, 1L, 1000L, 0, 1L, 0},
{"innodb_buffer_pool_awe_mem_mb", OPT_INNODB_BUFFER_POOL_AWE_MEM_MB,
"If Windows AWE is used, the size of InnoDB buffer pool allocated from the AWE memory.",
(gptr*) &innobase_buffer_pool_awe_mem_mb, (gptr*) &innobase_buffer_pool_awe_mem_mb, 0,
@@ -4938,7 +5097,7 @@ The minimum value for this variable is 4096.",
"Default pointer size to be used for MyISAM tables.",
(gptr*) &myisam_data_pointer_size,
(gptr*) &myisam_data_pointer_size, 0, GET_ULONG, REQUIRED_ARG,
- 4, 2, 7, 0, 1, 0},
+ 4, 2, 8, 0, 1, 0},
{"myisam_max_extra_sort_file_size", OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE,
"Used to help MySQL to decide when to use the slow but safe key cache index create method.",
(gptr*) &global_system_variables.myisam_max_extra_sort_file_size,
@@ -5026,12 +5185,12 @@ The minimum value for this variable is 4096.",
"Persistent buffer for query parsing and execution",
(gptr*) &global_system_variables.query_prealloc_size,
(gptr*) &max_system_variables.query_prealloc_size, 0, GET_ULONG,
- REQUIRED_ARG, QUERY_ALLOC_PREALLOC_SIZE, 1024, ~0L, 0, 1024, 0},
+ REQUIRED_ARG, QUERY_ALLOC_PREALLOC_SIZE, 16384, ~0L, 0, 1024, 0},
{"range_alloc_block_size", OPT_RANGE_ALLOC_BLOCK_SIZE,
"Allocation block size for storing ranges during optimization",
(gptr*) &global_system_variables.range_alloc_block_size,
(gptr*) &max_system_variables.range_alloc_block_size, 0, GET_ULONG,
- REQUIRED_ARG, RANGE_ALLOC_BLOCK_SIZE, 1024, ~0L, 0, 1024, 0},
+ REQUIRED_ARG, RANGE_ALLOC_BLOCK_SIZE, 4096, ~0L, 0, 1024, 0},
{"read_buffer_size", OPT_RECORD_BUFFER,
"Each thread that does a sequential scan allocates a buffer of this size for each table it scans. If you do many sequential scans, you may want to increase this value.",
(gptr*) &global_system_variables.read_buff_size,
@@ -5096,6 +5255,23 @@ The minimum value for this variable is 4096.",
(gptr*) &sync_binlog_period,
(gptr*) &sync_binlog_period, 0, GET_ULONG, REQUIRED_ARG, 0, 0, ~0L, 0, 1,
0},
+#ifdef DOES_NOTHING_YET
+ {"sync-replication", OPT_SYNC_REPLICATION,
+ "Enable synchronous replication",
+ (gptr*) &global_system_variables.sync_replication,
+ (gptr*) &global_system_variables.sync_replication,
+ 0, GET_ULONG, REQUIRED_ARG, 0, 0, 1, 0, 1, 0},
+ {"sync-replication-slave-id", OPT_SYNC_REPLICATION_SLAVE_ID,
+ "Synchronous replication is wished for this slave",
+ (gptr*) &global_system_variables.sync_replication_slave_id,
+ (gptr*) &global_system_variables.sync_replication_slave_id,
+ 0, GET_ULONG, REQUIRED_ARG, 0, 0, ~0L, 0, 1, 0},
+ {"sync-replication-timeout", OPT_SYNC_REPLICATION_TIMEOUT,
+ "Synchronous replication timeout",
+ (gptr*) &global_system_variables.sync_replication_timeout,
+ (gptr*) &global_system_variables.sync_replication_timeout,
+ 0, GET_ULONG, REQUIRED_ARG, 10, 0, ~0L, 0, 1, 0},
+#endif
{"sync-frm", OPT_SYNC_FRM, "Sync .frm to disk on create. Enabled by default",
(gptr*) &opt_sync_frm, (gptr*) &opt_sync_frm, 0, GET_BOOL, NO_ARG, 1, 0,
0, 0, 0, 0},
@@ -5480,7 +5656,8 @@ static void mysql_init_variables(void)
language_ptr= language;
mysql_data_home= mysql_real_data_home;
thd_startup_options= (OPTION_UPDATE_LOG | OPTION_AUTO_IS_NULL |
- OPTION_BIN_LOG | OPTION_QUOTE_SHOW_CREATE);
+ OPTION_BIN_LOG | OPTION_QUOTE_SHOW_CREATE |
+ OPTION_SQL_NOTES);
protocol_version= PROTOCOL_VERSION;
what_to_log= ~ (1L << (uint) COM_TIME);
refresh_version= flush_version= 1L; /* Increments on each reload */
@@ -5491,10 +5668,10 @@ static void mysql_init_variables(void)
threads.empty();
thread_cache.empty();
key_caches.empty();
- multi_keycache_init();
- if (!(sql_key_cache= get_or_create_key_cache(default_key_cache_base.str,
+ if (!(dflt_key_cache= get_or_create_key_cache(default_key_cache_base.str,
default_key_cache_base.length)))
exit(1);
+ multi_keycache_init(); /* set key_cache_hash.default_value = dflt_key_cache */
/* Initialize structures that is used when processing options */
replicate_rewrite_db.empty();
@@ -6204,6 +6381,24 @@ static void get_options(int argc,char **argv)
if ((ho_error= handle_options(&argc, &argv, my_long_options,
get_one_option)))
exit(ho_error);
+
+#ifndef HAVE_NDBCLUSTER_DB
+ if (opt_ndbcluster)
+ sql_print_warning("this binary does not contain NDBCLUSTER storage engine");
+#endif
+#ifndef HAVE_INNOBASE_DB
+ if (opt_innodb)
+ sql_print_warning("this binary does not contain INNODB storage engine");
+#endif
+#ifndef HAVE_ISAM
+ if (opt_isam)
+ sql_print_warning("this binary does not contain ISAM storage engine");
+#endif
+#ifndef HAVE_BERKELEY_DB
+ if (opt_bdb)
+ sql_print_warning("this binary does not contain BDB storage engine");
+#endif
+
if (argc > 0)
{
fprintf(stderr, "%s: Too many arguments (first extra is '%s').\nUse --help to get a list of available options\n", my_progname, *argv);
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 457b2052a45..5699b5a2f55 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -53,19 +53,9 @@
#include <errno.h>
#ifdef EMBEDDED_LIBRARY
-
#undef MYSQL_SERVER
-
-#ifndef MYSQL_CLIENT
+#undef MYSQL_CLIENT
#define MYSQL_CLIENT
-#endif
-
-#undef net_flush
-
-extern "C" {
-my_bool net_flush(NET *net);
-}
-
#endif /*EMBEDDED_LIBRARY */
@@ -175,8 +165,8 @@ my_bool net_realloc(NET *net, ulong length)
if (length >= net->max_packet_size)
{
- DBUG_PRINT("error",("Packet too large. Max sixe: %lu",
- net->max_packet_size));
+ DBUG_PRINT("error", ("Packet too large. Max size: %lu",
+ net->max_packet_size));
net->error= 1;
net->report_error= 1;
net->last_errno= ER_NET_PACKET_TOO_LARGE;
@@ -261,6 +251,8 @@ my_bool
my_net_write(NET *net,const char *packet,ulong len)
{
uchar buff[NET_HEADER_SIZE];
+ if (unlikely(!net->vio)) /* nowhere to write */
+ return 0;
/*
Big packets are handled by splitting them in packets of MAX_PACKET_LENGTH
length. The last packet is always a packet that is < MAX_PACKET_LENGTH.
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 541acc69ec7..d25901e56b1 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -400,7 +400,7 @@ QUICK_SELECT::QUICK_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc)
{
// Allocates everything through the internal memroot
init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
- my_pthread_setspecific_ptr(THR_MALLOC,&alloc);
+ thd->mem_root= &alloc;
}
else
bzero((char*) &alloc,sizeof(alloc));
@@ -668,8 +668,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
DBUG_RETURN(0); // Can't use range
}
key_parts= param.key_parts;
- old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
- my_pthread_setspecific_ptr(THR_MALLOC,&alloc);
+ old_root= thd->mem_root;
+ thd->mem_root= &alloc;
key_info= head->key_info;
for (idx=0 ; idx < head->keys ; idx++, key_info++)
@@ -769,7 +769,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
}
}
free_root(&alloc,MYF(0)); // Return memory & allocator
- my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ thd->mem_root= old_root;
thd->no_errors=0;
}
DBUG_EXECUTE("info",print_quick(quick,&needed_reg););
@@ -1013,13 +1013,22 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
}
/*
- We can't use an index when comparing strings of
- different collations
+ 1. Usually we can't use an index if the column collation
+ differ from the operation collation.
+
+ 2. However, we can reuse a case insensitive index for
+ the binary searches:
+
+ WHERE latin1_swedish_ci_column = 'a' COLLATE lati1_bin;
+
+ WHERE latin1_swedish_ci_colimn = BINARY 'a '
+
*/
if (field->result_type() == STRING_RESULT &&
value->result_type() == STRING_RESULT &&
key_part->image_type == Field::itRAW &&
- ((Field_str*)field)->charset() != conf_func->compare_collation())
+ ((Field_str*)field)->charset() != conf_func->compare_collation() &&
+ !(conf_func->compare_collation()->state & MY_CS_BINSORT))
DBUG_RETURN(0);
if (type == Item_func::LIKE_FUNC)
@@ -2554,7 +2563,8 @@ static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length)
QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref)
{
- MEM_ROOT *old_root= my_pthread_getspecific_ptr(MEM_ROOT*, THR_MALLOC);
+ MEM_ROOT *old_root= thd->mem_root;
+ /* The following call may change thd->mem_root */
QUICK_SELECT *quick= new QUICK_SELECT(thd, table, ref->key);
KEY *key_info = &table->key_info[ref->key];
KEY_PART *key_part;
@@ -2615,11 +2625,11 @@ QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref)
}
ok:
- my_pthread_setspecific_ptr(THR_MALLOC, old_root);
+ thd->mem_root= old_root;
return quick;
err:
- my_pthread_setspecific_ptr(THR_MALLOC, old_root);
+ thd->mem_root= old_root;
delete quick;
return 0;
}
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 9b2e9e45bac..5a2044a59f4 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -154,7 +154,7 @@ class FT_SELECT: public QUICK_SELECT {
public:
FT_SELECT(THD *thd, TABLE *table, uint key):
QUICK_SELECT (thd, table, key, 1) { init(); }
-
+ ~FT_SELECT() { file->ft_end(); }
int init() { return error= file->ft_init(); }
int get_next() { return error= file->ft_read(record); }
};
diff --git a/sql/password.c b/sql/password.c
index b9f3a07e596..04b3a46bd48 100644
--- a/sql/password.c
+++ b/sql/password.c
@@ -211,12 +211,13 @@ check_scramble_323(const char *scrambled, const char *message,
ulong hash_message[2];
char buff[16],*to,extra; /* Big enough for check */
const char *pos;
-
+
hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
randominit(&rand_st,hash_pass[0] ^ hash_message[0],
hash_pass[1] ^ hash_message[1]);
to=buff;
- for (pos=scrambled ; *pos ; pos++)
+ DBUG_ASSERT(sizeof(buff) > SCRAMBLE_LENGTH_323);
+ for (pos=scrambled ; *pos && to < buff+sizeof(buff) ; pos++)
*to++=(char) (floor(my_rnd(&rand_st)*31)+64);
if (pos-scrambled != SCRAMBLE_LENGTH_323)
return 1;
diff --git a/sql/procedure.h b/sql/procedure.h
index 5365b2e1102..abe50bdc0a0 100644
--- a/sql/procedure.h
+++ b/sql/procedure.h
@@ -59,7 +59,11 @@ public:
void set(double nr) { value=nr; }
void set(longlong nr) { value=(double) nr; }
void set(const char *str,uint length,CHARSET_INFO *cs)
- { int err; value=my_strntod(cs,(char*) str,length,(char**)0,&err); }
+ {
+ int err;
+ char *end_not_used;
+ value= my_strntod(cs, (char*) str, length, &end_not_used, &err);
+ }
double val() { return value; }
longlong val_int() { return (longlong) value; }
String *val_str(String *s) { s->set(value,decimals,default_charset()); return s; }
@@ -99,9 +103,10 @@ public:
double val()
{
int err;
- CHARSET_INFO *cs=str_value.charset();
+ CHARSET_INFO *cs= str_value.charset();
+ char *end_not_used;
return my_strntod(cs, (char*) str_value.ptr(), str_value.length(),
- (char**) 0, &err);
+ &end_not_used, &err);
}
longlong val_int()
{
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 887177c0a19..4cecd016553 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -58,6 +58,7 @@ void send_error(THD *thd, uint sql_errno, const char *err)
uint length;
char buff[MYSQL_ERRMSG_SIZE+2], *pos;
#endif
+ const char *orig_err= err;
NET *net= &thd->net;
DBUG_ENTER("send_error");
DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno,
@@ -82,6 +83,7 @@ void send_error(THD *thd, uint sql_errno, const char *err)
err=ER(sql_errno); /* purecov: inspected */
}
}
+ orig_err= err;
}
#ifdef EMBEDDED_LIBRARY
@@ -120,6 +122,8 @@ void send_error(THD *thd, uint sql_errno, const char *err)
}
VOID(net_write_command(net,(uchar) 255, "", 0, (char*) err,length));
#endif /* EMBEDDED_LIBRARY*/
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, sql_errno,
+ orig_err ? orig_err : ER(sql_errno));
thd->is_fatal_error=0; // Error message is given
thd->net.report_error= 0;
@@ -247,6 +251,8 @@ net_printf(THD *thd, uint errcode, ...)
strmake(net->last_error, text_pos, length);
strmake(net->sqlstate, mysql_errno_to_sqlstate(errcode), SQLSTATE_LENGTH);
#endif
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, errcode,
+ text_pos ? text_pos : ER(errcode));
thd->is_fatal_error=0; // Error message is given
DBUG_VOID_RETURN;
}
@@ -470,6 +476,15 @@ void Protocol::init(THD *thd_arg)
}
+bool Protocol::flush()
+{
+#ifndef EMBEDDED_LIBRARY
+ return net_flush(&thd->net);
+#else
+ return 0;
+#endif
+}
+
/*
Send name and type of result to client.
@@ -540,10 +555,18 @@ bool Protocol::send_fields(List<Item> *list, uint flag)
pos= (char*) local_packet->ptr()+local_packet->length();
*pos++= 12; // Length of packed fields
if (item->collation.collation == &my_charset_bin || thd_charset == NULL)
+ {
+ /* No conversion */
int2store(pos, field.charsetnr);
+ int4store(pos+2, field.length);
+ }
else
- int2store(pos, thd_charset->number);
- int4store(pos+2, field.length);
+ {
+ /* With conversion */
+ int2store(pos, thd_charset->number);
+ uint char_len= field.length / item->collation.collation->mbmaxlen;
+ int4store(pos+2, char_len * thd_charset->mbmaxlen);
+ }
pos[6]= field.type;
int2store(pos+7,field.flags);
pos[9]= (char) field.decimals;
@@ -713,7 +736,8 @@ bool Protocol::store_string_aux(const char *from, uint length,
fromcs != &my_charset_bin &&
tocs != &my_charset_bin)
{
- return convert->copy(from, length, fromcs, tocs) ||
+ uint dummy_errors;
+ return convert->copy(from, length, fromcs, tocs, &dummy_errors) ||
net_store_data(convert->ptr(), convert->length());
}
return net_store_data(from, length);
diff --git a/sql/protocol.h b/sql/protocol.h
index d7ce5425ad1..a3b6da55da3 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -75,6 +75,7 @@ public:
field_count=item_list->elements;
return 0;
}
+ virtual bool flush();
virtual void prepare_for_resend()=0;
virtual bool store_null()=0;
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 83fceca80ef..85a51ba9b51 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -20,7 +20,6 @@
#include "repl_failsafe.h"
#include "sql_repl.h"
#include "slave.h"
-#include "sql_acl.h"
#include "log_event.h"
#include <mysql.h>
@@ -92,7 +91,7 @@ static int init_failsafe_rpl_thread(THD* thd)
VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
#endif
- thd->mem_root.free=thd->mem_root.used=0;
+ thd->mem_root->free= thd->mem_root->used= 0;
if (thd->variables.max_join_size == HA_POS_ERROR)
thd->options|= OPTION_BIG_SELECTS;
@@ -921,8 +920,8 @@ int load_master_data(THD* thd)
*/
int error;
- if (init_master_info(active_mi, master_info_file, relay_log_info_file,
- 0))
+ if (init_master_info(active_mi, master_info_file, relay_log_info_file,
+ 0, (SLAVE_IO | SLAVE_SQL)))
send_error(thd, ER_MASTER_INFO);
strmake(active_mi->master_log_name, row[0],
sizeof(active_mi->master_log_name));
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 66ed273d531..29ebb2c8817 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -55,7 +55,6 @@
#include "mysql_priv.h"
#include <mysql.h>
#include "slave.h"
-#include "sql_acl.h"
#include <my_getopt.h>
#include <thr_alarm.h>
#include <myisam.h>
@@ -334,6 +333,14 @@ sys_var_thd_storage_engine sys_storage_engine("storage_engine",
&SV::table_type);
#ifdef HAVE_REPLICATION
sys_var_sync_binlog_period sys_sync_binlog_period("sync_binlog", &sync_binlog_period);
+sys_var_thd_ulong sys_sync_replication("sync_replication",
+ &SV::sync_replication);
+sys_var_thd_ulong sys_sync_replication_slave_id(
+ "sync_replication_slave_id",
+ &SV::sync_replication_slave_id);
+sys_var_thd_ulong sys_sync_replication_timeout(
+ "sync_replication_timeout",
+ &SV::sync_replication_timeout);
#endif
sys_var_bool_ptr sys_sync_frm("sync_frm", &opt_sync_frm);
sys_var_long_ptr sys_table_cache_size("table_cache",
@@ -352,12 +359,25 @@ sys_var_thd_ulong sys_net_wait_timeout("wait_timeout",
#ifdef HAVE_INNOBASE_DB
sys_var_long_ptr sys_innodb_max_dirty_pages_pct("innodb_max_dirty_pages_pct",
&srv_max_buf_pool_modified_pct);
+sys_var_long_ptr sys_innodb_max_purge_lag("innodb_max_purge_lag",
+ &srv_max_purge_lag);
sys_var_thd_bool sys_innodb_table_locks("innodb_table_locks",
&SV::innodb_table_locks);
sys_var_long_ptr sys_innodb_autoextend_increment("innodb_autoextend_increment",
&srv_auto_extend_increment);
-sys_var_long_ptr sys_innodb_max_purge_lag("innodb_max_purge_lag",
- &srv_max_purge_lag);
+#endif
+
+#ifdef HAVE_NDBCLUSTER_DB
+/* ndb thread specific variable settings */
+sys_var_thd_ulong
+sys_ndb_autoincrement_prefetch_sz("ndb_autoincrement_prefetch_sz",
+ &SV::ndb_autoincrement_prefetch_sz);
+sys_var_thd_bool
+sys_ndb_force_send("ndb_force_send", &SV::ndb_force_send);
+sys_var_thd_bool
+sys_ndb_use_exact_count("ndb_use_exact_count", &SV::ndb_use_exact_count);
+sys_var_thd_bool
+sys_ndb_use_transactions("ndb_use_transactions", &SV::ndb_use_transactions);
#endif
/* Time/date/datetime formats */
@@ -403,6 +423,9 @@ static sys_var_thd_bit sys_log_binlog("sql_log_bin",
static sys_var_thd_bit sys_sql_warnings("sql_warnings", 0,
set_option_bit,
OPTION_WARNINGS);
+static sys_var_thd_bit sys_sql_notes("sql_notes", 0,
+ set_option_bit,
+ OPTION_SQL_NOTES);
static sys_var_thd_bit sys_auto_is_null("sql_auto_is_null", 0,
set_option_bit,
OPTION_AUTO_IS_NULL);
@@ -590,9 +613,13 @@ sys_var *sys_variables[]=
&sys_sql_max_join_size,
&sys_sql_mode,
&sys_sql_warnings,
+ &sys_sql_notes,
&sys_storage_engine,
#ifdef HAVE_REPLICATION
&sys_sync_binlog_period,
+ &sys_sync_replication,
+ &sys_sync_replication_slave_id,
+ &sys_sync_replication_timeout,
#endif
&sys_sync_frm,
&sys_table_cache_size,
@@ -608,10 +635,17 @@ sys_var *sys_variables[]=
&sys_os,
#ifdef HAVE_INNOBASE_DB
&sys_innodb_max_dirty_pages_pct,
+ &sys_innodb_max_purge_lag,
&sys_innodb_table_locks,
&sys_innodb_max_purge_lag,
&sys_innodb_autoextend_increment,
-#endif
+#endif
+#ifdef HAVE_NDBCLUSTER_DB
+ &sys_ndb_autoincrement_prefetch_sz,
+ &sys_ndb_force_send,
+ &sys_ndb_use_exact_count,
+ &sys_ndb_use_transactions,
+#endif
&sys_unique_checks,
&sys_warning_count
};
@@ -692,11 +726,11 @@ struct show_var_st init_vars[]= {
{"innodb_fast_shutdown", (char*) &innobase_fast_shutdown, SHOW_MY_BOOL},
{"innodb_file_io_threads", (char*) &innobase_file_io_threads, SHOW_LONG },
{"innodb_file_per_table", (char*) &innobase_file_per_table, SHOW_MY_BOOL},
- {"innodb_locks_unsafe_for_binlog", (char*) &innobase_locks_unsafe_for_binlog, SHOW_MY_BOOL},
{"innodb_flush_log_at_trx_commit", (char*) &innobase_flush_log_at_trx_commit, SHOW_INT},
{"innodb_flush_method", (char*) &innobase_unix_file_flush_method, SHOW_CHAR_PTR},
{"innodb_force_recovery", (char*) &innobase_force_recovery, SHOW_LONG },
{"innodb_lock_wait_timeout", (char*) &innobase_lock_wait_timeout, SHOW_LONG },
+ {"innodb_locks_unsafe_for_binlog", (char*) &innobase_locks_unsafe_for_binlog, SHOW_MY_BOOL},
{"innodb_log_arch_dir", (char*) &innobase_log_arch_dir, SHOW_CHAR_PTR},
{"innodb_log_archive", (char*) &innobase_log_archive, SHOW_MY_BOOL},
{"innodb_log_buffer_size", (char*) &innobase_log_buffer_size, SHOW_LONG },
@@ -704,10 +738,10 @@ struct show_var_st init_vars[]= {
{"innodb_log_files_in_group", (char*) &innobase_log_files_in_group, SHOW_LONG},
{"innodb_log_group_home_dir", (char*) &innobase_log_group_home_dir, SHOW_CHAR_PTR},
{sys_innodb_max_dirty_pages_pct.name, (char*) &sys_innodb_max_dirty_pages_pct, SHOW_SYS},
- {sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS},
{sys_innodb_max_purge_lag.name, (char*) &sys_innodb_max_purge_lag, SHOW_SYS},
{"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG},
{"innodb_open_files", (char*) &innobase_open_files, SHOW_LONG },
+ {sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS},
{"innodb_thread_concurrency", (char*) &innobase_thread_concurrency, SHOW_LONG },
#endif
{sys_interactive_timeout.name,(char*) &sys_interactive_timeout, SHOW_SYS},
@@ -771,6 +805,13 @@ struct show_var_st init_vars[]= {
#ifdef __NT__
{"named_pipe", (char*) &opt_enable_named_pipe, SHOW_MY_BOOL},
#endif
+#ifdef HAVE_NDBCLUSTER_DB
+ {sys_ndb_autoincrement_prefetch_sz.name,
+ (char*) &sys_ndb_autoincrement_prefetch_sz, SHOW_SYS},
+ {sys_ndb_force_send.name, (char*) &sys_ndb_force_send, SHOW_SYS},
+ {sys_ndb_use_exact_count.name,(char*) &sys_ndb_use_exact_count, SHOW_SYS},
+ {sys_ndb_use_transactions.name,(char*) &sys_ndb_use_transactions, SHOW_SYS},
+#endif
{sys_net_buffer_length.name,(char*) &sys_net_buffer_length, SHOW_SYS},
{sys_net_read_timeout.name, (char*) &sys_net_read_timeout, SHOW_SYS},
{sys_net_retry_count.name, (char*) &sys_net_retry_count, SHOW_SYS},
@@ -822,8 +863,13 @@ struct show_var_st init_vars[]= {
{sys_sort_buffer.name, (char*) &sys_sort_buffer, SHOW_SYS},
{sys_sql_mode.name, (char*) &sys_sql_mode, SHOW_SYS},
{sys_storage_engine.name, (char*) &sys_storage_engine, SHOW_SYS},
+ {"sql_notes", (char*) &sys_sql_notes, SHOW_BOOL},
+ {"sql_warnings", (char*) &sys_sql_warnings, SHOW_BOOL},
#ifdef HAVE_REPLICATION
{sys_sync_binlog_period.name,(char*) &sys_sync_binlog_period, SHOW_SYS},
+ {sys_sync_replication.name, (char*) &sys_sync_replication, SHOW_SYS},
+ {sys_sync_replication_slave_id.name, (char*) &sys_sync_replication_slave_id,SHOW_SYS},
+ {sys_sync_replication_timeout.name, (char*) &sys_sync_replication_timeout,SHOW_SYS},
#endif
{sys_sync_frm.name, (char*) &sys_sync_frm, SHOW_SYS},
#ifdef HAVE_TZNAME
@@ -1163,7 +1209,7 @@ static void fix_max_connections(THD *thd, enum_var_type type)
static void fix_thd_mem_root(THD *thd, enum_var_type type)
{
if (type != OPT_GLOBAL)
- reset_root_defaults(&thd->mem_root,
+ reset_root_defaults(thd->mem_root,
thd->variables.query_alloc_block_size,
thd->variables.query_prealloc_size);
}
@@ -1183,6 +1229,12 @@ static void fix_server_id(THD *thd, enum_var_type type)
server_id_supplied = 1;
}
+bool sys_var_long_ptr::check(THD *thd, set_var *var)
+{
+ longlong v= var->value->val_int();
+ var->save_result.ulonglong_value= v < 0 ? 0 : v;
+ return 0;
+}
bool sys_var_long_ptr::update(THD *thd, set_var *var)
{
@@ -2148,7 +2200,7 @@ bool sys_var_key_buffer_size::update(THD *thd, set_var *var)
if (!tmp) // Zero size means delete
{
- if (key_cache == sql_key_cache)
+ if (key_cache == dflt_key_cache)
goto end; // Ignore default key cache
if (key_cache->key_cache_inited) // If initied
@@ -2162,7 +2214,7 @@ bool sys_var_key_buffer_size::update(THD *thd, set_var *var)
base_name->length, &list);
key_cache->in_init= 1;
pthread_mutex_unlock(&LOCK_global_system_variables);
- error= reassign_keycache_tables(thd, key_cache, sql_key_cache);
+ error= reassign_keycache_tables(thd, key_cache, dflt_key_cache);
pthread_mutex_lock(&LOCK_global_system_variables);
key_cache->in_init= 0;
}
@@ -2412,8 +2464,15 @@ bool sys_var_thd_time_zone::check(THD *thd, set_var *var)
bool sys_var_thd_time_zone::update(THD *thd, set_var *var)
{
- /* We are using Time_zone object found during check() phase */
- *get_tz_ptr(thd,var->type)= var->save_result.time_zone;
+ /* We are using Time_zone object found during check() phase. */
+ if (var->type == OPT_GLOBAL)
+ {
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ global_system_variables.time_zone= var->save_result.time_zone;
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ }
+ else
+ thd->variables.time_zone= var->save_result.time_zone;
return 0;
}
@@ -2425,27 +2484,25 @@ byte *sys_var_thd_time_zone::value_ptr(THD *thd, enum_var_type type,
We can use ptr() instead of c_ptr() here because String contaning
time zone name is guaranteed to be zero ended.
*/
- return (byte *)((*get_tz_ptr(thd,type))->get_name()->ptr());
-}
-
-
-Time_zone** sys_var_thd_time_zone::get_tz_ptr(THD *thd,
- enum_var_type type)
-{
if (type == OPT_GLOBAL)
- return &global_system_variables.time_zone;
+ return (byte *)(global_system_variables.time_zone->get_name()->ptr());
else
- return &thd->variables.time_zone;
+ return (byte *)(thd->variables.time_zone->get_name()->ptr());
}
void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type)
{
+ pthread_mutex_lock(&LOCK_global_system_variables);
if (type == OPT_GLOBAL)
{
if (default_tz_name)
{
String str(default_tz_name, &my_charset_latin1);
+ /*
+ We are guaranteed to find this time zone since its existence
+ is checked during start-up.
+ */
global_system_variables.time_zone=
my_tz_find(&str, thd->lex->time_zone_tables_used);
}
@@ -2454,6 +2511,7 @@ void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type)
}
else
thd->variables.time_zone= global_system_variables.time_zone;
+ pthread_mutex_unlock(&LOCK_global_system_variables);
}
/*
@@ -2693,21 +2751,25 @@ sys_var *find_sys_var(const char *str, uint length)
int sql_set_variables(THD *thd, List<set_var_base> *var_list)
{
- int error= 0;
+ int error;
List_iterator_fast<set_var_base> it(*var_list);
DBUG_ENTER("sql_set_variables");
set_var_base *var;
while ((var=it++))
{
- if ((error=var->check(thd)))
- DBUG_RETURN(error);
+ if ((error= var->check(thd)))
+ goto err;
}
- if (thd->net.report_error)
- DBUG_RETURN(1);
- it.rewind();
- while ((var=it++))
- error|= var->update(thd); // Returns 0, -1 or 1
+ if (!(error= test(thd->net.report_error)))
+ {
+ it.rewind();
+ while ((var= it++))
+ error|= var->update(thd); // Returns 0, -1 or 1
+ }
+
+err:
+ free_underlaid_joins(thd, &thd->lex->select_lex);
DBUG_RETURN(error);
}
@@ -2768,7 +2830,8 @@ int set_var::check(THD *thd)
return 0;
}
- if (value->fix_fields(thd, 0, &value) || value->check_cols(1))
+ if ((!value->fixed &&
+ value->fix_fields(thd, 0, &value)) || value->check_cols(1))
return -1;
if (var->check_update_type(value->result_type()))
{
@@ -2803,7 +2866,8 @@ int set_var::light_check(THD *thd)
if (type == OPT_GLOBAL && check_global_access(thd, SUPER_ACL))
return 1;
- if (value && (value->fix_fields(thd, 0, &value) || value->check_cols(1)))
+ if (value && ((!value->fixed && value->fix_fields(thd, 0, &value)) ||
+ value->check_cols(1)))
return -1;
return 0;
}
@@ -2880,8 +2944,8 @@ int set_var_password::check(THD *thd)
if (!user->host.str)
user->host.str= (char*) thd->host_or_ip;
/* Returns 1 as the function sends error to client */
- return check_change_password(thd, user->host.str, user->user.str, password) ?
- 1 : 0;
+ return check_change_password(thd, user->host.str, user->user.str,
+ password, strlen(password)) ? 1 : 0;
#else
return 0;
#endif
@@ -2912,9 +2976,11 @@ bool sys_var_thd_storage_engine::check(THD *thd, set_var *var)
if (var->value->result_type() == STRING_RESULT)
{
+ enum db_type db_type;
if (!(res=var->value->val_str(&str)) ||
!(var->save_result.ulong_value=
- (ulong) ha_resolve_by_name(res->ptr(), res->length())))
+ (ulong) (db_type= ha_resolve_by_name(res->ptr(), res->length()))) ||
+ ha_checktype(db_type) != db_type)
{
value= res ? res->c_ptr() : "NULL";
goto err;
diff --git a/sql/set_var.h b/sql/set_var.h
index 4a4e631d88c..080a2a95ae0 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -93,6 +93,7 @@ public:
sys_var_long_ptr(const char *name_arg, ulong *value_ptr,
sys_after_update_func func)
:sys_var(name_arg,func), value(value_ptr) {}
+ bool check(THD *thd, set_var *var);
bool update(THD *thd, set_var *var);
void set_default(THD *thd, enum_var_type type);
SHOW_TYPE type() { return SHOW_LONG; }
@@ -736,7 +737,6 @@ public:
bool update(THD *thd, set_var *var);
byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
virtual void set_default(THD *thd, enum_var_type type);
- Time_zone **get_tz_ptr(THD *thd, enum_var_type type);
};
/****************************************************************************
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
index 355e784b156..bb2bd29171a 100644
--- a/sql/share/french/errmsg.txt
+++ b/sql/share/french/errmsg.txt
@@ -209,7 +209,7 @@ character-set=latin1
"Erreur d'écriture réseau reçue du maître",
"Impossible de trouver un index FULLTEXT correspondant à cette liste de colonnes",
"Impossible d'exécuter la commande car vous avez des tables verrouillées ou une transaction active",
-"Variable système '%-.64' inconnue",
+"Variable système '%-.64s' inconnue",
"La table '%-.64s' est marquée 'crashed' et devrait être réparée",
"La table '%-.64s' est marquée 'crashed' et le dernier 'repair' a échoué",
"Attention: certaines tables ne supportant pas les transactions ont été changées et elles ne pourront pas être restituées",
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
index 06f31a79a73..87168431595 100644
--- a/sql/share/greek/errmsg.txt
+++ b/sql/share/greek/errmsg.txt
@@ -168,7 +168,7 @@ character-set=greek
"You have an error in your SQL syntax",
"Delayed insert thread couldn't get requested lock for table %-.64s",
"Too many delayed threads in use",
-"Aborted connection %ld to db: '%-.64s' user: '%-32s' (%-.64s)",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)",
"Got a packet bigger than 'max_allowed_packet' bytes",
"Got a read error from the connection pipe",
"Got an error from fcntl()",
diff --git a/sql/share/japanese-sjis/errmsg.txt b/sql/share/japanese-sjis/errmsg.txt
new file mode 100644
index 00000000000..a06723727b7
--- /dev/null
+++ b/sql/share/japanese-sjis/errmsg.txt
@@ -0,0 +1,325 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Shift-JIS Japanese
+*/
+
+character-set=sjis
+
+"hashchk",
+"isamchk",
+"NO",
+"YES",
+"'%-.64s' ƒtƒ@ƒCƒ‹‚ªì‚ê‚Ü‚¹‚ñ (errno: %d)",
+"'%-.64s' ƒe[ƒuƒ‹‚ªì‚ê‚Ü‚¹‚ñ.(errno: %d)",
+"'%-.64s' ƒf[ƒ^ƒx[ƒX‚ªì‚ê‚Ü‚¹‚ñ (errno: %d)",
+"'%-.64s' ƒf[ƒ^ƒx[ƒX‚ªì‚ê‚Ü‚¹‚ñ.Šù‚É‚»‚̃f[ƒ^ƒx[ƒX‚ª‘¶Ý‚µ‚Ü‚·",
+"'%-.64s' ƒf[ƒ^ƒx[ƒX‚ð”jŠü‚Å‚«‚Ü‚¹‚ñ. ‚»‚̃f[ƒ^ƒx[ƒX‚ª‚È‚¢‚Ì‚Å‚·.",
+"ƒf[ƒ^ƒx[ƒX”jŠüƒGƒ‰[ ('%-.64s' ‚ð휂ł«‚Ü‚¹‚ñ, errno: %d)",
+"ƒf[ƒ^ƒx[ƒX”jŠüƒGƒ‰[ ('%-.64s' ‚ð rmdir ‚Å‚«‚Ü‚¹‚ñ, errno: %d)",
+"'%-.64s' ‚Ì휂ªƒGƒ‰[ (errno: %d)",
+"system table ‚̃ŒƒR[ƒh‚ð“Ç‚ÞŽ–‚ª‚Å‚«‚Ü‚¹‚ñ‚Å‚µ‚½",
+"'%-.64s' ‚̃XƒeƒCƒ^ƒX‚ª“¾‚ç‚ê‚Ü‚¹‚ñ. (errno: %d)",
+"working directory ‚𓾂鎖‚ª‚Å‚«‚Ü‚¹‚ñ‚Å‚µ‚½ (errno: %d)",
+"ƒtƒ@ƒCƒ‹‚ðƒƒbƒN‚Å‚«‚Ü‚¹‚ñ (errno: %d)",
+"'%-.64s' ƒtƒ@ƒCƒ‹‚ðŠJ‚­Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d)",
+"'%-.64s' ƒtƒ@ƒCƒ‹‚ðŒ©•t‚¯‚鎖‚ª‚Å‚«‚Ü‚¹‚ñ.(errno: %d)",
+"'%-.64s' ƒfƒBƒŒƒNƒgƒŠ‚ª“Ç‚ß‚Ü‚¹‚ñ.(errno: %d)",
+"'%-.64s' ƒfƒBƒŒƒNƒgƒŠ‚É chdir ‚Å‚«‚Ü‚¹‚ñ.(errno: %d)",
+"Record has changed since last read in table '%-.64s'",
+"Disk full (%s). ’N‚©‚ª‰½‚©‚ðŒ¸‚ç‚·‚Ü‚Å‚Ü‚Á‚Ä‚­‚¾‚³‚¢...",
+"table '%-.64s' ‚É key ‚ªd•¡‚µ‚Ä‚¢‚Ä‘‚«‚±‚ß‚Ü‚¹‚ñ",
+"Error on close of '%-.64s' (errno: %d)",
+"'%-.64s' ƒtƒ@ƒCƒ‹‚Ì“Ç‚Ýž‚݃Gƒ‰[ (errno: %d)",
+"'%-.64s' ‚ð '%-.64s' ‚É rename ‚Å‚«‚Ü‚¹‚ñ (errno: %d)",
+"'%-.64s' ƒtƒ@ƒCƒ‹‚ð‘‚­Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d)",
+"'%-.64s' ‚̓ƒbƒN‚³‚ê‚Ä‚¢‚Ü‚·",
+"Sort ’†’f",
+"View '%-.64s' ‚ª '%-.64s' ‚É’è‹`‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
+"Got error %d from table handler",
+"Table handler for '%-.64s' doesn't have this option",
+"'%-.64s'‚Ì‚È‚©‚ɃŒƒR[ƒh‚ªŒ©•t‚©‚è‚Ü‚¹‚ñ",
+"ƒtƒ@ƒCƒ‹ '%-.64s' ‚Ì info ‚ªŠÔˆá‚Á‚Ä‚¢‚é‚悤‚Å‚·",
+"'%-.64s' ƒe[ƒuƒ‹‚Ì key file ‚ªŠÔˆá‚Á‚Ä‚¢‚é‚悤‚Å‚·. C•œ‚ð‚µ‚Ä‚­‚¾‚³‚¢",
+"'%-.64s' ƒe[ƒuƒ‹‚͌¢Œ`Ž®‚Ì key file ‚̂悤‚Å‚·; C•œ‚ð‚µ‚Ä‚­‚¾‚³‚¢",
+"'%-.64s' ‚Í“Ç‚Ýž‚Ýê—p‚Å‚·",
+"Out of memory. ƒf[ƒ‚ƒ“‚ðƒŠƒXƒ^[ƒg‚µ‚Ä‚Ý‚Ä‚­‚¾‚³‚¢ (%d bytes •K—v)",
+"Out of sort memory. sort buffer size ‚ª‘«‚è‚È‚¢‚悤‚Å‚·.",
+"'%-.64s' ƒtƒ@ƒCƒ‹‚ð“Ç‚Ýž‚Ý’†‚É EOF ‚ª—\Šú‚¹‚ÊŠ‚ÅŒ»‚ê‚Ü‚µ‚½. (errno: %d)",
+"Ú‘±‚ª‘½‚·‚¬‚Ü‚·",
+"Out of memory; mysqld ‚©‚»‚Ì‘¼‚̃vƒƒZƒX‚ªƒƒ‚ƒŠ[‚ð‘S‚ÄŽg‚Á‚Ä‚¢‚é‚©Šm”F‚µ‚Ä‚­‚¾‚³‚¢. ƒƒ‚ƒŠ[‚ðŽg‚¢Ø‚Á‚Ä‚¢‚È‚¢ê‡A'ulimit' ‚ðݒ肵‚Ä mysqld ‚̃ƒ‚ƒŠ[Žg—pŒÀŠE—ʂ𑽂­‚·‚é‚©Aswap space ‚ð‘‚₵‚Ä‚Ý‚Ä‚­‚¾‚³‚¢",
+"‚»‚Ì address ‚Ì hostname ‚ªˆø‚¯‚Ü‚¹‚ñ.",
+"Bad handshake",
+"ƒ†[ƒU[ '%-.32s'@'%-.64s' ‚Ì '%-.64s' ƒf[ƒ^ƒx[ƒX‚ւ̃AƒNƒZƒX‚ð‹‘”Û‚µ‚Ü‚·",
+"ƒ†[ƒU[ '%-.32s'@'%-.64s' ‚ð‹‘”Û‚µ‚Ü‚·.uUsing password: %s)",
+"ƒf[ƒ^ƒx[ƒX‚ª‘I‘ð‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ.",
+"‚»‚̃Rƒ}ƒ“ƒh‚͉½H",
+"Column '%-.64s' ‚Í null ‚É‚Í‚Å‚«‚È‚¢‚Ì‚Å‚·",
+"'%-.64s' ‚È‚ñ‚ăf[ƒ^ƒx[ƒX‚Í’m‚è‚Ü‚¹‚ñ.",
+"Table '%-.64s' ‚ÍŠù‚É‚ ‚è‚Ü‚·",
+"table '%-.64s' ‚Í‚ ‚è‚Ü‚¹‚ñ.",
+"Column: '%-.64s' in %-.64s is ambiguous",
+"Server ‚ð shutdown ’†...",
+"'%-.64s' column ‚Í '%-.64s' ‚É‚Í‚ ‚è‚Ü‚¹‚ñ.",
+"'%-.64s' isn't in GROUP BY",
+"Can't group on '%-.64s'",
+"Statement has sum functions and columns in same statement",
+"Column count doesn't match value count",
+"Identifier name '%-.100s' ‚Í’·‚·‚¬‚Ü‚·",
+"'%-.64s' ‚Æ‚¢‚¤ column –¼‚Íd•¡‚µ‚Ä‚Ü‚·",
+"'%-.64s' ‚Æ‚¢‚¤ key ‚Ì–¼‘O‚Íd•¡‚µ‚Ä‚¢‚Ü‚·",
+"'%-.64s' ‚Í key %d ‚É‚¨‚¢‚Äd•¡‚µ‚Ä‚¢‚Ü‚·",
+"Incorrect column specifier for column '%-.64s'",
+"%s : '%-.80s' •t‹ß : %d s–Ú",
+"Query ‚ª‹ó‚Å‚·.",
+"'%-.64s' ‚͈êˆÓ‚Ì table/alias –¼‚Å‚Í‚ ‚è‚Ü‚¹‚ñ",
+"Invalid default value for '%-.64s'",
+"•¡”‚Ì primary key ‚ª’è‹`‚³‚ê‚Ü‚µ‚½",
+"key ‚ÌŽw’肪‘½‚·‚¬‚Ü‚·. key ‚ÍÅ‘å %d ‚Ü‚Å‚Å‚·",
+"Too many key parts specified; max %d parts allowed",
+"key ‚ª’·‚·‚¬‚Ü‚·. key ‚Ì’·‚³‚ÍÅ‘å %d ‚Å‚·",
+"Key column '%-.64s' ‚ªƒe[ƒuƒ‹‚É‚ ‚è‚Ü‚¹‚ñ.",
+"BLOB column '%-.64s' can't be used in key specification with the used table type",
+"column '%-.64s' ‚Í,Šm•Û‚·‚é column ‚Ì‘å‚«‚³‚ª‘½‚·‚¬‚Ü‚·. (Å‘å %d ‚Ü‚Å). BLOB ‚ð‚©‚í‚è‚ÉŽg—p‚µ‚Ä‚­‚¾‚³‚¢.",
+"ƒe[ƒuƒ‹‚Ì’è‹`‚ªˆá‚¢‚Ü‚·; there can be only one auto column and it must be defined as a key",
+"%s: €”õŠ®—¹",
+"%s: Normal shutdown\n",
+"%s: Got signal %d. ’†’f!\n",
+"%s: Shutdown Š®—¹\n",
+"%s: ƒXƒŒƒbƒh %ld ‹­§I—¹ user: '%-.64s'\n",
+"IP socket ‚ªì‚ê‚Ü‚¹‚ñ",
+"Table '%-.64s' ‚Í‚»‚̂悤‚È index ‚ðŽ‚Á‚Ä‚¢‚Ü‚¹‚ñ(CREATE INDEX ŽÀsŽž‚ÉŽw’肳‚ê‚Ä‚¢‚Ü‚¹‚ñ). ƒe[ƒuƒ‹‚ðì‚è’¼‚µ‚Ä‚­‚¾‚³‚¢",
+"Field separator argument is not what is expected; check the manual",
+"You can't use fixed rowlength with BLOBs; please use 'fields terminated by'.",
+"ƒtƒ@ƒCƒ‹ '%-.64s' ‚Í databse ‚Ì directory ‚É‚ ‚é‚©‘S‚Ẵ†[ƒU[‚ª“Ç‚ß‚é‚悤‚É‹–‰Â‚³‚ê‚Ä‚¢‚È‚¯‚ê‚΂Ȃè‚Ü‚¹‚ñ.",
+"File '%-.64s' ‚ÍŠù‚É‘¶Ý‚µ‚Ü‚·",
+"ƒŒƒR[ƒh”: %ld íœ: %ld Skipped: %ld Warnings: %ld",
+"ƒŒƒR[ƒh”: %ld d•¡: %ld",
+"Incorrect sub part key; the used key part isn't a string or the used length is longer than the key part",
+"ALTER TABLE ‚Å‘S‚Ä‚Ì column ‚Í휂ł«‚Ü‚¹‚ñ. DROP TABLE ‚ðŽg—p‚µ‚Ä‚­‚¾‚³‚¢",
+"'%-.64s' ‚ð”jŠü‚Å‚«‚Ü‚¹‚ñ‚Å‚µ‚½; check that column/key exists",
+"ƒŒƒR[ƒh”: %ld d•¡”: %ld Warnings: %ld",
+"You can't specify target table '%-.64s' for update in FROM clause",
+"thread id: %lu ‚Í‚ ‚è‚Ü‚¹‚ñ",
+"thread %lu ‚̃I[ƒi[‚Å‚Í‚ ‚è‚Ü‚¹‚ñ",
+"No tables used",
+"Too many strings for column %-.64s and SET",
+"Can't generate a unique log-filename %-.64s.(1-999)\n",
+"Table '%-.64s' ‚Í READ lock ‚É‚È‚Á‚Ä‚¢‚ÄAXV‚Í‚Å‚«‚Ü‚¹‚ñ",
+"Table '%-.64s' ‚Í LOCK TABLES ‚É‚æ‚Á‚ăƒbƒN‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
+"BLOB column '%-.64s' can't have a default value",
+"Žw’肵‚½ database –¼ '%-.100s' ‚ªŠÔˆá‚Á‚Ä‚¢‚Ü‚·",
+"Žw’肵‚½ table –¼ '%-.100s' ‚Í‚Ü‚¿‚ª‚Á‚Ä‚¢‚Ü‚·",
+"The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET SQL_MAX_JOIN_SIZE=# if the SELECT is okay",
+"Unknown error",
+"Unknown procedure '%-.64s'",
+"Incorrect parameter count to procedure '%-.64s'",
+"Incorrect parameters to procedure '%-.64s'",
+"Unknown table '%-.64s' in %s",
+"Column '%-.64s' specified twice",
+"Invalid use of group function",
+"Table '%-.64s' uses an extension that doesn't exist in this MySQL version",
+"ƒe[ƒuƒ‹‚ÍÅ’á 1 ŒÂ‚Ì column ‚ª•K—v‚Å‚·",
+"table '%-.64s' ‚Í‚¢‚Á‚Ï‚¢‚Å‚·",
+"character set '%-.64s' ‚̓Tƒ|[ƒg‚µ‚Ä‚¢‚Ü‚¹‚ñ",
+"ƒe[ƒuƒ‹‚ª‘½‚·‚¬‚Ü‚·; MySQL can only use %d tables in a join",
+"column ‚ª‘½‚·‚¬‚Ü‚·",
+"row size ‚ª‘å‚«‚·‚¬‚Ü‚·. BLOB ‚ðŠÜ‚Ü‚È‚¢ê‡‚Ì row size ‚ÌÅ‘å‚Í %d ‚Å‚·. ‚¢‚­‚‚©‚Ì field ‚ð BLOB ‚É•Ï‚¦‚Ä‚­‚¾‚³‚¢.",
+"Thread stack overrun: Used: %ld of a %ld stack. ƒXƒ^ƒbƒN—̈æ‚𑽂­‚Ƃ肽‚¢ê‡A'mysqld -O thread_stack=#' ‚ÆŽw’肵‚Ä‚­‚¾‚³‚¢",
+"Cross dependency found in OUTER JOIN; examine your ON conditions",
+"Column '%-.64s' ‚ª UNIQUE ‚© INDEX ‚ÅŽg—p‚³‚ê‚Ü‚µ‚½. ‚±‚̃Jƒ‰ƒ€‚Í NOT NULL ‚Æ’è‹`‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ.",
+"function '%-.64s' ‚ð ƒ[ƒh‚Å‚«‚Ü‚¹‚ñ",
+"function '%-.64s' ‚ð‰Šú‰»‚Å‚«‚Ü‚¹‚ñ; %-.80s",
+"shared library ‚ւ̃pƒX‚ª’Ê‚Á‚Ä‚¢‚Ü‚¹‚ñ",
+"Function '%-.64s' ‚ÍŠù‚É’è‹`‚³‚ê‚Ä‚¢‚Ü‚·",
+"shared library '%-.64s' ‚ðŠJ‚­Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d %s)",
+"function '%-.64s' ‚ðƒ‰ƒCƒuƒ‰ƒŠ[’†‚ÉŒ©•t‚¯‚鎖‚ª‚Å‚«‚Ü‚¹‚ñ",
+"Function '%-.64s' ‚Í’è‹`‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
+"Host '%-.64s' ‚Í many connection error ‚Ì‚½‚ßA‹‘”Û‚³‚ê‚Ü‚µ‚½. 'mysqladmin flush-hosts' ‚ʼn𜂵‚Ä‚­‚¾‚³‚¢",
+"Host '%-.64s' ‚Í MySQL server ‚ÉÚ‘±‚ð‹–‰Â‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
+"MySQL ‚ð anonymous users ‚ÅŽg—p‚µ‚Ä‚¢‚éó‘Ô‚Å‚ÍAƒpƒXƒ[ƒh‚Ì•ÏX‚Í‚Å‚«‚Ü‚¹‚ñ",
+"‘¼‚̃†[ƒU[‚̃pƒXƒ[ƒh‚ð•ÏX‚·‚邽‚ß‚É‚Í, mysql ƒf[ƒ^ƒx[ƒX‚ɑ΂µ‚Ä update ‚Ì‹–‰Â‚ª‚È‚¯‚ê‚΂Ȃè‚Ü‚¹‚ñ.",
+"Can't find any matching row in the user table",
+"ˆê’v”(Rows matched): %ld •ÏX: %ld Warnings: %ld",
+"V‹K‚ɃXƒŒƒbƒh‚ªì‚ê‚Ü‚¹‚ñ‚Å‚µ‚½ (errno %d). ‚à‚µÅ‘åŽg—p‹–‰Âƒƒ‚ƒŠ[”‚ð‰z‚¦‚Ä‚¢‚È‚¢‚̂ɃGƒ‰[‚ª”­¶‚µ‚Ä‚¢‚é‚È‚ç, ƒ}ƒjƒ…ƒAƒ‹‚Ì’†‚©‚ç 'possible OS-dependent bug' ‚Æ‚¢‚¤•¶Žš‚ð’T‚µ‚Ä‚­‚Ý‚Ä‚¾‚³‚¢.",
+"Column count doesn't match value count at row %ld",
+"Can't reopen table: '%-.64s'",
+"NULL ’l‚ÌŽg—p•û–@‚ª•s“KØ‚Å‚·",
+"Got error '%-.64s' from regexp",
+"Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause",
+"ƒ†[ƒU[ '%-.32s' (ƒzƒXƒg '%-.64s' ‚̃†[ƒU[) ‚Í‹–‰Â‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
+"ƒRƒ}ƒ“ƒh %-.16s ‚Í ƒ†[ƒU[ '%-.32s'@'%-.64s' ,ƒe[ƒuƒ‹ '%-.64s' ‚ɑ΂µ‚Ä‹–‰Â‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
+"ƒRƒ}ƒ“ƒh %-.16s ‚Í ƒ†[ƒU[ '%-.32s'@'%-.64s'\n ƒJƒ‰ƒ€ '%-.64s' ƒe[ƒuƒ‹ '%-.64s' ‚ɑ΂µ‚Ä‹–‰Â‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
+"Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used.",
+"The host or user argument to GRANT is too long",
+"Table '%-.64s.%s' doesn't exist",
+"There is no such grant defined for user '%-.32s' on host '%-.64s' on table '%-.64s'",
+"The used command is not allowed with this MySQL version",
+"Something is wrong in your syntax",
+"Delayed insert thread couldn't get requested lock for table %-.64s",
+"Too many delayed threads in use",
+"Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)",
+"Got a packet bigger than 'max_allowed_packet' bytes",
+"Got a read error from the connection pipe",
+"Got an error from fcntl()",
+"Got packets out of order",
+"Couldn't uncompress communication packet",
+"Got an error reading communication packets",
+"Got timeout reading communication packets",
+"Got an error writing communication packets",
+"Got timeout writing communication packets",
+"Result string is longer than 'max_allowed_packet' bytes",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"Can't write, because of unique constraint, to table '%-.64s'",
+"BLOB column '%-.64s' used in key specification without a key length",
+"All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column",
+"Key '%-.64s' doesn't exist in table '%-.64s'",
+"Can't open table",
+"The handler for the table doesn't support %s",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.64s' (%-.64s)",
+"The handler for the table does not support binary table dump",
+"Binlog closed while trying to FLUSH MASTER",
+"Failed rebuilding the index of dumped table '%-.64s'",
+"Error from master: '%-.64s'",
+"Net error reading from master",
+"Net error writing to master",
+"Can't find FULLTEXT index matching the column list",
+"Can't execute the given command because you have active locked tables or an active transaction",
+"Unknown system variable '%-.64s'",
+"Table '%-.64s' is marked as crashed and should be repaired",
+"Table '%-.64s' is marked as crashed and last (automatic?) repair failed",
+"Some non-transactional changed tables couldn't be rolled back",
+"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again",
+"This operation cannot be performed with a running slave; run STOP SLAVE first",
+"This operation requires a running slave; configure slave and do START SLAVE",
+"The server is not configured as slave; fix in config file or with CHANGE MASTER TO",
+"Could not initialize master info structure; more error messages can be found in the MySQL error log",
+"Could not create slave thread; check system resources",
+"User %-.64s has already more than 'max_user_connections' active connections",
+"You may only use constant expressions with SET",
+"Lock wait timeout exceeded; try restarting transaction",
+"The total number of locks exceeds the lock table size",
+"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
+"DROP DATABASE not allowed while thread is holding global read lock",
+"CREATE DATABASE not allowed while thread is holding global read lock",
+"Incorrect arguments to %s",
+"'%-.32s'@'%-.64s' is not allowed to create new users",
+"Incorrect table definition; all MERGE tables must be in the same database",
+"Deadlock found when trying to get lock; try restarting transaction",
+"The used table type doesn't support FULLTEXT indexes",
+"Cannot add foreign key constraint",
+"Cannot add a child row: a foreign key constraint fails",
+"Cannot delete a parent row: a foreign key constraint fails",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error when executing command %s: %-.128s",
+"Incorrect usage of %s and %s",
+"The used SELECT statements have a different number of columns",
+"Can't execute the query because you have a conflicting read lock",
+"Mixing of transactional and non-transactional tables is disabled",
+"Option '%s' used twice in statement",
+"User '%-.64s' has exceeded the '%s' resource (current value: %ld)",
+"Access denied; you need the %-.128s privilege for this operation",
+"Variable '%-.64s' is a SESSION variable and can't be used with SET GLOBAL",
+"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL",
+"Variable '%-.64s' doesn't have a default value",
+"Variable '%-.64s' can't be set to the value of '%-.64s'",
+"Incorrect argument type to variable '%-.64s'",
+"Variable '%-.64s' can only be set, not read",
+"Incorrect usage/placement of '%s'",
+"This version of MySQL doesn't yet support '%s'",
+"Got fatal error %d: '%-.128s' from master when reading data from binary log",
+"Slave SQL thread ignored the query because of replicate-*-table rules",
+"Variable '%-.64s' is a %s variable",
+"Incorrect foreign key definition for '%-.64s': %s",
+"Key reference and table reference don't match",
+"Operand should contain %d column(s)",
+"Subquery returns more than 1 row",
+"Unknown prepared statement handler (%.*s) given to %s",
+"Help database is corrupt or does not exist",
+"Cyclic reference on subqueries",
+"Converting column '%s' from %s to %s",
+"Reference '%-.64s' not supported (%s)",
+"Every derived table must have its own alias",
+"Select %u was reduced during optimization",
+"Table '%-.64s' from one of the SELECTs cannot be used in %-.32s",
+"Client does not support authentication protocol requested by server; consider upgrading MySQL client",
+"All parts of a SPATIAL index must be NOT NULL",
+"COLLATION '%s' is not valid for CHARACTER SET '%s'",
+"Slave is already running",
+"Slave has already been stopped",
+"Uncompressed data size too large; the maximum size is %d (probably, length of uncompressed data was corrupted)",
+"ZLIB: Not enough memory",
+"ZLIB: Not enough room in the output buffer (probably, length of uncompressed data was corrupted)",
+"ZLIB: Input data corrupted",
+"%d line(s) were cut by GROUP_CONCAT()",
+"Row %ld doesn't contain data for all columns",
+"Row %ld was truncated; it contained more data than there were input columns",
+"Data truncated; NULL supplied to NOT NULL column '%s' at row %ld",
+"Data truncated; out of range for column '%s' at row %ld",
+"Data truncated for column '%s' at row %ld",
+"Using storage engine %s for table '%s'",
+"Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'",
+"Can't drop one or more of the requested users",
+"Can't revoke all privileges, grant for one or more of the requested users",
+"Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
+"Illegal mix of collations for operation '%s'",
+"Variable '%-.64s' is not a variable component (can't be used as XXXX.variable_name)",
+"Unknown collation: '%-.64s'",
+"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later if MySQL slave with SSL is started",
+"Server is running in --secure-auth mode, but '%s'@'%s' has a password in the old format; please change the password to the new format",
+"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d",
+"Incorrect parameter or combination of parameters for START SLAVE UNTIL",
+"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you are not safe in case of unexpected slave's mysqld restart",
+"SQL thread is not to be started so UNTIL options are ignored",
+"Incorrect index name '%-.100s'",
+"Incorrect catalog name '%-.100s'",
+"Query cache failed to set size %lu, new query cache size is %lu",
+"Column '%-.64s' cannot be part of FULLTEXT index",
+"Unknown key cache '%-.100s'",
+"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
+"Unknown table engine '%s'",
+"'%s' is deprecated, use '%s' instead",
+"The target table %-.100s of the %s is not updateable",
+"The '%s' feature was disabled; you need MySQL built with '%s' to have it working",
+"The MySQL server is running with the %s option so it cannot execute this statement",
+"Column '%-.100s' has duplicated value '%-.64s' in %s"
+"Truncated wrong %-.32s value: '%-.128s'"
+"Incorrect table definition; There can only be one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause"
+"Invalid ON UPDATE clause for '%-.64s' column",
+"This command is not supported in the prepared statement protocol yet",
+"Got NDB error %d '%-.100s'",
+"Got temporary NDB error %d '%-.100s'",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
+"Invalid %s character string: '%.64s'",
+"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
+"Conflicting declarations: '%s%s' and '%s%s'"
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
index c79c346008e..65d80918072 100644
--- a/sql/share/portuguese/errmsg.txt
+++ b/sql/share/portuguese/errmsg.txt
@@ -211,7 +211,7 @@ character-set=latin1
"Erro de rede gravando no 'master'",
"Não pode encontrar um índice para o texto todo que combine com a lista de colunas",
"Não pode executar o comando dado porque você tem tabelas ativas travadas ou uma transação ativa",
-"Variável de sistema '%-.64' desconhecida",
+"Variável de sistema '%-.64s' desconhecida",
"Tabela '%-.64s' está marcada como danificada e deve ser reparada",
"Tabela '%-.64s' está marcada como danificada e a última reparação (automática?) falhou",
"Aviso: Algumas tabelas não-transacionais alteradas não puderam ser reconstituídas (rolled back)",
diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt
index 7cb0427dc3f..01c22f00119 100644
--- a/sql/share/romanian/errmsg.txt
+++ b/sql/share/romanian/errmsg.txt
@@ -173,7 +173,7 @@ character-set=latin2
"Aveti o eroare in sintaxa RSQL",
"Thread-ul pentru inserarea aminata nu a putut obtine lacatul (lock) pentru tabela %-.64s",
"Prea multe threaduri aminate care sint in uz",
-"Conectie terminata %ld la baza de date: '%-.64s' utilizator: '%-32s' (%-.64s)",
+"Conectie terminata %ld la baza de date: '%-.64s' utilizator: '%-.32s' (%-.64s)",
"Un packet mai mare decit 'max_allowed_packet' a fost primit",
"Eroare la citire din cauza lui 'connection pipe'",
"Eroare obtinuta de la fcntl()",
diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt
index 45b56c8269c..06270f621e4 100644
--- a/sql/share/serbian/errmsg.txt
+++ b/sql/share/serbian/errmsg.txt
@@ -202,7 +202,7 @@ character-set=cp1250
"Greška u slanju mrežnih paketa na glavni server u klasteru",
"Ne mogu da pronaðem 'FULLTEXT' indeks koli odgovara listi kolona",
"Ne mogu da izvršim datu komandu zbog toga što su tabele zakljuèane ili je transakcija u toku",
-"Nepoznata sistemska promenljiva '%-.64'",
+"Nepoznata sistemska promenljiva '%-.64s'",
"Tabela '%-.64s' je markirana kao ošteæena i trebala bi biti popravljena",
"Tabela '%-.64s' je markirana kao ošteæena, a zadnja (automatska?) popravka je bila neuspela",
"Upozorenje: Neke izmenjene tabele ne podržavaju komandu 'ROLLBACK'",
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
index bd2439c44a6..c5b4fd34eca 100644
--- a/sql/share/spanish/errmsg.txt
+++ b/sql/share/spanish/errmsg.txt
@@ -238,9 +238,9 @@ character-set=latin1
"No puede adicionar clave extranjera constraint",
"No puede adicionar una línea hijo: falla de clave extranjera constraint",
"No puede deletar una línea padre: falla de clave extranjera constraint",
-"Error de coneccion a master: %-128s",
-"Error executando el query en master: %-128%",
-"Error de %s: %-128%",
+"Error de coneccion a master: %-.128s",
+"Error executando el query en master: %-.128s",
+"Error de %s: %-.128s",
"Equivocado uso de %s y %s",
"El comando SELECT usado tiene diferente número de columnas",
"No puedo ejecutar el query porque usted tiene conflicto de traba de lectura",
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
index da75e4fcede..4dc42389d89 100644
--- a/sql/share/swedish/errmsg.txt
+++ b/sql/share/swedish/errmsg.txt
@@ -101,7 +101,7 @@ character-set=latin1
"Tabellen '%-.64s' har inget index som motsvarar det angivna i CREATE INDEX. Skapa om tabellen",
"Fältseparatorerna är vad som förväntades. Kontrollera mot manualen",
"Man kan inte använda fast radlängd med blobs. Använd 'fields terminated by'",
-"Textfilen '%' måste finnas i databasbiblioteket eller vara läsbar för alla",
+"Textfilen '%.64s' måste finnas i databasbiblioteket eller vara läsbar för alla",
"Filen '%-.64s' existerar redan",
"Rader: %ld Bortagna: %ld Dubletter: %ld Varningar: %ld",
"Rader: %ld Dubletter: %ld",
@@ -200,7 +200,7 @@ character-set=latin1
"Fick fel %d vid ROLLBACK",
"Fick fel %d vid FLUSH_LOGS",
"Fick fel %d vid CHECKPOINT",
-"Avbröt länken för tråd %ld till db '%-.64s', användare '%-.32s', host '%-.64s' (%.-64s)",
+"Avbröt länken för tråd %ld till db '%-.64s', användare '%-.32s', host '%-.64s' (%-.64s)",
"Tabellhanteraren klarar inte en binär kopiering av tabellen",
"Binärloggen stängdes medan FLUSH MASTER utfördes",
"Failed rebuilding the index of dumped table '%-.64s'",
diff --git a/sql/slave.cc b/sql/slave.cc
index 4ef8715f1e6..a39cbdbe14b 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -161,7 +161,7 @@ int init_slave()
}
if (init_master_info(active_mi,master_info_file,relay_log_info_file,
- !master_host))
+ !master_host, (SLAVE_IO | SLAVE_SQL)))
{
sql_print_error("Failed to initialize the master info structure");
goto err;
@@ -545,7 +545,7 @@ int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock)
int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock,
pthread_mutex_t *cond_lock,
pthread_cond_t* term_cond,
- volatile bool* slave_running)
+ volatile uint *slave_running)
{
if (term_lock)
{
@@ -583,7 +583,7 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock,
int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock,
pthread_mutex_t *cond_lock,
pthread_cond_t *start_cond,
- volatile bool *slave_running,
+ volatile uint *slave_running,
volatile ulong *slave_run_id,
MASTER_INFO* mi,
bool high_priority)
@@ -759,7 +759,7 @@ static TABLE_RULE_ENT* find_wild(DYNAMIC_ARRAY *a, const char* key, int len)
1 should be logged/replicated
*/
-int tables_ok(THD* thd, TABLE_LIST* tables)
+bool tables_ok(THD* thd, TABLE_LIST* tables)
{
bool some_tables_updating= 0;
DBUG_ENTER("tables_ok");
@@ -963,7 +963,7 @@ void end_slave()
static bool io_slave_killed(THD* thd, MASTER_INFO* mi)
{
DBUG_ASSERT(mi->io_thd == thd);
- DBUG_ASSERT(mi->slave_running == 1); // tracking buffer overrun
+ DBUG_ASSERT(mi->slave_running); // tracking buffer overrun
return mi->abort_slave || abort_loop || thd->killed;
}
@@ -1032,7 +1032,7 @@ bool net_request_file(NET* net, const char* fname)
}
-const char *rewrite_db(const char* db)
+const char *rewrite_db(const char* db, uint32 *new_len)
{
if (replicate_rewrite_db.is_empty() || !db)
return db;
@@ -1042,7 +1042,10 @@ const char *rewrite_db(const char* db)
while ((tmp=it++))
{
if (!strcmp(tmp->key, db))
+ {
+ *new_len= (uint32)strlen(tmp->val);
return tmp->val;
+ }
}
return db;
}
@@ -1056,7 +1059,7 @@ const char *rewrite_db(const char* db)
const char *print_slave_db_safe(const char* db)
{
- return (db ? rewrite_db(db) : "");
+ return (db ? db : "");
}
/*
@@ -1240,7 +1243,11 @@ not always make sense; please check the manual before using it).";
values of these 2 are never used (new connections don't use them).
We don't test equality of global collation_database either as it's is
going to be deprecated (made read-only) in 4.1 very soon.
+ We don't do it for <3.23.57 because masters <3.23.50 hang on
+ SELECT @@unknown_var (BUG#7965 - see changelog of 3.23.50).
*/
+ if (mi->old_format == BINLOG_FORMAT_323_LESS_57)
+ goto err;
if (!mysql_real_query(mysql, "SELECT @@GLOBAL.COLLATION_SERVER", 32) &&
(master_res= mysql_store_result(mysql)))
{
@@ -1277,6 +1284,7 @@ be equal for replication to work";
mysql_free_result(master_res);
}
+err:
if (errmsg)
{
sql_print_error(errmsg);
@@ -1764,19 +1772,13 @@ void init_master_info_with_options(MASTER_INFO* mi)
strmake(mi->ssl_key, master_ssl_key, sizeof(mi->ssl_key)-1);
}
-static void clear_slave_error(RELAY_LOG_INFO* rli)
+void clear_slave_error(RELAY_LOG_INFO* rli)
{
/* Clear the errors displayed by SHOW SLAVE STATUS */
rli->last_slave_error[0]= 0;
rli->last_slave_errno= 0;
}
-void clear_slave_error_timestamp(RELAY_LOG_INFO* rli)
-{
- rli->last_master_timestamp= 0;
- clear_slave_error(rli);
-}
-
/*
Reset UNTIL condition for RELAY_LOG_INFO
SYNOPSYS
@@ -1796,7 +1798,8 @@ void clear_until_condition(RELAY_LOG_INFO* rli)
int init_master_info(MASTER_INFO* mi, const char* master_info_fname,
const char* slave_info_fname,
- bool abort_if_no_master_info_file)
+ bool abort_if_no_master_info_file,
+ int thread_mask)
{
int fd,error;
char fname[FN_REFLEN+128];
@@ -1810,8 +1813,16 @@ int init_master_info(MASTER_INFO* mi, const char* master_info_fname,
last time. If this case pos_in_file would be set and we would
get a crash when trying to read the signature for the binary
relay log.
+
+ We only rewind the read position if we are starting the SQL
+ thread. The handle_slave_sql thread assumes that the read
+ position is at the beginning of the file, and will read the
+ "signature" and then fast-forward to the last position read.
*/
- my_b_seek(mi->rli.cur_log, (my_off_t) 0);
+ if (thread_mask & SLAVE_SQL)
+ {
+ my_b_seek(mi->rli.cur_log, (my_off_t) 0);
+ }
DBUG_RETURN(0);
}
@@ -2154,6 +2165,11 @@ int show_master_info(THD* thd, MASTER_INFO* mi)
String *packet= &thd->packet;
protocol->prepare_for_resend();
+ /*
+ TODO: we read slave_running without run_lock, whereas these variables
+ are updated under run_lock and not data_lock. In 5.0 we should lock
+ run_lock on top of data_lock (with good order).
+ */
pthread_mutex_lock(&mi->data_lock);
pthread_mutex_lock(&mi->rli.data_lock);
@@ -2214,7 +2230,12 @@ int show_master_info(THD* thd, MASTER_INFO* mi)
protocol->store(mi->ssl_cipher, &my_charset_bin);
protocol->store(mi->ssl_key, &my_charset_bin);
- if (mi->rli.last_master_timestamp)
+ /*
+ Seconds_Behind_Master: if SQL thread is running and I/O thread is
+ connected, we can compute it otherwise show NULL (i.e. unknown).
+ */
+ if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) &&
+ mi->rli.slave_running)
{
long tmp= (long)((time_t)time((time_t*) 0)
- mi->rli.last_master_timestamp)
@@ -2234,9 +2255,13 @@ int show_master_info(THD* thd, MASTER_INFO* mi)
slave is 2. At SHOW SLAVE STATUS time, assume that the difference
between timestamp of slave and rli->last_master_timestamp is 0
(i.e. they are in the same second), then we get 0-(2-1)=-1 as a result.
- This confuses users, so we don't go below 0.
+ This confuses users, so we don't go below 0: hence the max().
+
+ last_master_timestamp == 0 (an "impossible" timestamp 1970) is a
+ special marker to say "consider we have caught up".
*/
- protocol->store((longlong)(max(0, tmp)));
+ protocol->store((longlong)(mi->rli.last_master_timestamp ? max(0, tmp)
+ : 0));
}
else
protocol->store_null();
@@ -2402,18 +2427,19 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name,
init_abort_pos_wait= abort_pos_wait;
/*
- We'll need to
+ We'll need to
handle all possible log names comparisons (e.g. 999 vs 1000).
- We use ulong for string->number conversion ; this is no
+ We use ulong for string->number conversion ; this is no
stronger limitation than in find_uniq_filename in sql/log.cc
*/
ulong log_name_extension;
char log_name_tmp[FN_REFLEN]; //make a char[] from String
- char *end= strmake(log_name_tmp, log_name->ptr(), min(log_name->length(),
- FN_REFLEN-1));
+
+ strmake(log_name_tmp, log_name->ptr(), min(log_name->length(), FN_REFLEN-1));
+
char *p= fn_ext(log_name_tmp);
char *p_end;
- if (!*p || log_pos<0)
+ if (!*p || log_pos<0)
{
error= -2; //means improper arguments
goto err;
@@ -2632,7 +2658,7 @@ static int request_dump(MYSQL* mysql, MASTER_INFO* mi,
DBUG_ENTER("request_dump");
// TODO if big log files: Change next to int8store()
- int4store(buf, (longlong) mi->master_log_pos);
+ int4store(buf, (ulong) mi->master_log_pos);
int2store(buf + 4, binlog_flags);
int4store(buf + 6, server_id);
len = (uint) strlen(logname);
@@ -3029,6 +3055,8 @@ slave_begin:
connected:
+ // TODO: the assignment below should be under mutex (5.0)
+ mi->slave_running= MYSQL_SLAVE_RUN_CONNECT;
thd->slave_net = &mysql->net;
thd->proc_info = "Checking master version";
if (get_master_version_and_clock(mysql, mi))
@@ -3060,6 +3088,7 @@ 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();
@@ -3136,6 +3165,7 @@ max_allowed_packet",
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();
@@ -3311,6 +3341,14 @@ slave_begin:
pthread_mutex_lock(&LOCK_thread_count);
threads.append(thd);
pthread_mutex_unlock(&LOCK_thread_count);
+ /*
+ We are going to set slave_running to 1. Assuming slave I/O thread is
+ alive and connected, this is going to make Seconds_Behind_Master be 0
+ i.e. "caught up". Even if we're just at start of thread. Well it's ok, at
+ the moment we start we can think we are caught up, and the next second we
+ start receiving data so we realize we are not caught up and
+ Seconds_Behind_Master grows. No big deal.
+ */
rli->slave_running = 1;
rli->abort_slave = 0;
pthread_mutex_unlock(&rli->run_lock);
@@ -4157,6 +4195,21 @@ Before assert, my_b_tell(cur_log)=%s rli->event_relay_log_pos=%s",
*/
if (hot_log)
{
+ /*
+ We say in Seconds_Behind_Master that we have "caught up". Note that
+ for example if network link is broken but I/O slave thread hasn't
+ noticed it (slave_net_timeout not elapsed), then we'll say "caught
+ up" whereas we're not really caught up. Fixing that would require
+ internally cutting timeout in smaller pieces in network read, no
+ thanks. Another example: SQL has caught up on I/O, now I/O has read
+ a new event and is queuing it; the false "0" will exist until SQL
+ finishes executing the new event; it will be look abnormal only if
+ the events have old timestamps (then you get "many", 0, "many").
+ Transient phases like this can't really be fixed.
+ */
+ time_t save_timestamp= rli->last_master_timestamp;
+ rli->last_master_timestamp= 0;
+
DBUG_ASSERT(rli->relay_log.get_open_count() == rli->cur_log_old_open_count);
/*
We can, and should release data_lock while we are waiting for
@@ -4203,6 +4256,7 @@ Before assert, my_b_tell(cur_log)=%s rli->event_relay_log_pos=%s",
rli->relay_log.wait_for_update(rli->sql_thd, 1);
// re-acquire data lock since we released it earlier
pthread_mutex_lock(&rli->data_lock);
+ rli->last_master_timestamp= save_timestamp;
continue;
}
/*
diff --git a/sql/slave.h b/sql/slave.h
index 20167094453..bcd79dd4a39 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -98,6 +98,21 @@ enum enum_binlog_formats {
BINLOG_FORMAT_323_LESS_57,
BINLOG_FORMAT_323_GEQ_57 };
+/*
+ 3 possible values for MASTER_INFO::slave_running and
+ RELAY_LOG_INFO::slave_running.
+ The values 0,1,2 are very important: to keep the diff small, I didn't
+ substitute places where we use 0/1 with the newly defined symbols. So don't change
+ these values.
+ The same way, code is assuming that in RELAY_LOG_INFO we use only values
+ 0/1.
+ I started with using an enum, but
+ enum_variable=1; is not legal so would have required many line changes.
+*/
+#define MYSQL_SLAVE_NOT_RUN 0
+#define MYSQL_SLAVE_RUN_NOT_CONNECT 1
+#define MYSQL_SLAVE_RUN_CONNECT 2
+
/****************************************************************************
Replication SQL Thread
@@ -251,7 +266,8 @@ typedef struct st_relay_log_info
/* if not set, the value of other members of the structure are undefined */
bool inited;
- volatile bool abort_slave, slave_running;
+ volatile bool abort_slave;
+ volatile uint slave_running;
/*
Condition and its parameters from START SLAVE UNTIL clause.
@@ -385,7 +401,8 @@ typedef struct st_master_info
#endif
bool inited;
enum enum_binlog_formats old_format;
- volatile bool abort_slave, slave_running;
+ volatile bool abort_slave;
+ volatile uint slave_running;
volatile ulong slave_run_id;
/*
The difference in seconds between the clock of the master and the clock of
@@ -464,7 +481,7 @@ int terminate_slave_threads(MASTER_INFO* mi, int thread_mask,
int terminate_slave_thread(THD* thd, pthread_mutex_t* term_mutex,
pthread_mutex_t* cond_lock,
pthread_cond_t* term_cond,
- volatile bool* slave_running);
+ volatile uint* slave_running);
int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
MASTER_INFO* mi, const char* master_info_fname,
const char* slave_info_fname, int thread_mask);
@@ -477,7 +494,7 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
int start_slave_thread(pthread_handler h_func, pthread_mutex_t* start_lock,
pthread_mutex_t *cond_lock,
pthread_cond_t* start_cond,
- volatile bool *slave_running,
+ volatile uint *slave_running,
volatile ulong *slave_run_id,
MASTER_INFO* mi,
bool high_priority);
@@ -496,7 +513,7 @@ int show_master_info(THD* thd, MASTER_INFO* mi);
int show_binlog_info(THD* thd);
/* See if the query uses any tables that should not be replicated */
-int tables_ok(THD* thd, TABLE_LIST* tables);
+bool tables_ok(THD* thd, TABLE_LIST* tables);
/*
Check to see if the database is ok to operate on with respect to the
@@ -510,8 +527,8 @@ int add_table_rule(HASH* h, const char* table_spec);
int add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec);
void init_table_rule_hash(HASH* h, bool* h_inited);
void init_table_rule_array(DYNAMIC_ARRAY* a, bool* a_inited);
-const char *rewrite_db(const char* db);
-const char *print_slave_db_safe(const char* db);
+const char *rewrite_db(const char* db, uint32 *new_db_len);
+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, ...);
@@ -519,10 +536,11 @@ void slave_print_error(RELAY_LOG_INFO* rli, int err_code, const char* msg, ...);
void end_slave(); /* clean up */
void init_master_info_with_options(MASTER_INFO* mi);
void clear_until_condition(RELAY_LOG_INFO* rli);
-void clear_slave_error_timestamp(RELAY_LOG_INFO* rli);
+void clear_slave_error(RELAY_LOG_INFO* rli);
int init_master_info(MASTER_INFO* mi, const char* master_info_fname,
const char* slave_info_fname,
- bool abort_if_no_master_info_file);
+ bool abort_if_no_master_info_file,
+ int thread_mask);
void end_master_info(MASTER_INFO* mi);
int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname);
void end_relay_log_info(RELAY_LOG_INFO* rli);
diff --git a/sql/spatial.cc b/sql/spatial.cc
index 0668dd2faab..bcfefd9dde8 100644
--- a/sql/spatial.cc
+++ b/sql/spatial.cc
@@ -828,9 +828,7 @@ int Gis_polygon::centroid_xy(double *x, double *y) const
if (!first_loop)
{
- double d_area= res_area - cur_area;
- if (d_area <= 0)
- return 1;
+ double d_area= fabs(res_area - cur_area);
res_cx= (res_area * res_cx - cur_area * cur_cx) / d_area;
res_cy= (res_area * res_cy - cur_area * cur_cy) / d_area;
}
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index ff2dfedc30b..46114e2748b 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -26,7 +26,6 @@
*/
#include "mysql_priv.h"
-#include "sql_acl.h"
#include "hash_filo.h"
#ifdef HAVE_REPLICATION
#include "sql_repl.h" //for tables_ok()
@@ -140,6 +139,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
MYSQL_LOCK *lock;
my_bool return_val=1;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
+ char tmp_name[NAME_LEN+1];
DBUG_ENTER("acl_init");
@@ -198,6 +198,23 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
ACL_HOST host;
update_hostname(&host.host,get_field(&mem, table->field[0]));
host.db= get_field(&mem, table->field[1]);
+ if (lower_case_table_names)
+ {
+ /*
+ We make a temporary copy of the database, force it to lower case,
+ and then check it against the original name.
+ */
+ (void)strnmov(tmp_name, host.db, sizeof(tmp_name));
+ my_casedn_str(files_charset_info, host.db);
+ if (strcmp(host.db, tmp_name) != 0)
+ {
+ sql_print_warning("'host' entry '%s|%s' had database in mixed "
+ "case that has been forced to lowercase because "
+ "lower_case_table_names is set. It will not be "
+ "possible to remove this privilege using REVOKE.",
+ host.host.hostname, host.db);
+ }
+ }
host.access= get_access(table,2);
host.access= fix_rights_for_db(host.access);
host.sort= get_sort(2,host.host.hostname,host.db);
@@ -381,6 +398,23 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
}
db.access=get_access(table,3);
db.access=fix_rights_for_db(db.access);
+ if (lower_case_table_names)
+ {
+ /*
+ We make a temporary copy of the database, force it to lower case,
+ and then check it against the original name.
+ */
+ (void)strnmov(tmp_name, db.db, sizeof(tmp_name));
+ my_casedn_str(files_charset_info, db.db);
+ if (strcmp(db.db, tmp_name) != 0)
+ {
+ sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
+ "case that has been forced to lowercase because "
+ "lower_case_table_names is set. It will not be "
+ "possible to remove this privilege using REVOKE.",
+ db.db, db.user, db.host.hostname, db.host.hostname);
+ }
+ }
db.sort=get_sort(3,db.host.hostname,db.db,db.user);
#ifndef TO_BE_REMOVED
if (table->fields <= 9)
@@ -439,7 +473,7 @@ void acl_free(bool end)
SYNOPSIS
acl_reload()
- thd Thread handle
+ thd Thread handle (can be NULL)
*/
void acl_reload(THD *thd)
@@ -965,11 +999,11 @@ static void acl_insert_db(const char *user, const char *host, const char *db,
ulong acl_get(const char *host, const char *ip,
const char *user, const char *db, my_bool db_is_pattern)
{
- ulong host_access,db_access;
+ ulong host_access= ~0,db_access= 0;
uint i,key_length;
- db_access=0; host_access= ~0;
char key[ACL_KEY_LENGTH],*tmp_db,*end;
acl_entry *entry;
+ DBUG_ENTER("acl_get");
VOID(pthread_mutex_lock(&acl_cache->lock));
end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db);
@@ -983,7 +1017,8 @@ ulong acl_get(const char *host, const char *ip,
{
db_access=entry->access;
VOID(pthread_mutex_unlock(&acl_cache->lock));
- return db_access;
+ DBUG_PRINT("exit", ("access: 0x%lx", db_access));
+ DBUG_RETURN(db_access);
}
/*
@@ -1035,7 +1070,8 @@ exit:
acl_cache->add(entry);
}
VOID(pthread_mutex_unlock(&acl_cache->lock));
- return (db_access & host_access);
+ DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
+ DBUG_RETURN(db_access & host_access);
}
/*
@@ -1127,6 +1163,10 @@ bool acl_check_host(const char *host, const char *ip)
thd THD
host hostname for the user
user user name
+ new_password new password
+
+ NOTE:
+ new_password cannot be NULL
RETURN VALUE
0 OK
@@ -1134,7 +1174,7 @@ bool acl_check_host(const char *host, const char *ip)
*/
bool check_change_password(THD *thd, const char *host, const char *user,
- char *new_password)
+ char *new_password, uint new_password_len)
{
if (!initialized)
{
@@ -1185,12 +1225,13 @@ bool check_change_password(THD *thd, const char *host, const char *user,
bool change_password(THD *thd, const char *host, const char *user,
char *new_password)
{
+ uint new_password_len= strlen(new_password);
DBUG_ENTER("change_password");
DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
host,user,new_password));
DBUG_ASSERT(host != 0); // Ensured by parent
- if (check_change_password(thd, host, user, new_password))
+ if (check_change_password(thd, host, user, new_password, new_password_len))
DBUG_RETURN(1);
VOID(pthread_mutex_lock(&acl_cache->lock));
@@ -1202,7 +1243,6 @@ bool change_password(THD *thd, const char *host, const char *user,
DBUG_RETURN(1);
}
/* update loaded acl entry: */
- uint new_password_len= new_password ? strlen(new_password) : 0;
set_user_salt(acl_user, new_password, new_password_len);
if (update_user_table(thd,
@@ -1227,7 +1267,7 @@ bool change_password(THD *thd, const char *host, const char *user,
new_password));
thd->clear_error();
mysql_update_log.write(thd, buff, query_length);
- Query_log_event qinfo(thd, buff, query_length, 0);
+ Query_log_event qinfo(thd, buff, query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
DBUG_RETURN(0);
}
@@ -1334,7 +1374,7 @@ bool hostname_requires_resolving(const char *hostname)
return FALSE;
for (; (cur=*hostname); hostname++)
{
- if ((cur != '%') && (cur != '_') && (cur != '.') &&
+ if ((cur != '%') && (cur != '_') && (cur != '.') && (cur != '/') &&
((cur < '0') || (cur > '9')))
return TRUE;
}
@@ -1381,8 +1421,10 @@ static bool update_user_table(THD *thd, const char *host, const char *user,
table->field[0]->store(host,(uint) strlen(host), &my_charset_latin1);
table->field[1]->store(user,(uint) strlen(user), &my_charset_latin1);
+ table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
if (table->file->index_read_idx(table->record[0],0,
- (byte*) table->field[0]->ptr,0,
+ (byte*) table->field[0]->ptr,
+ table->key_info[0].key_length,
HA_READ_KEY_EXACT))
{
my_error(ER_PASSWORD_NO_MATCH,MYF(0)); /* purecov: deadcode */
@@ -1460,9 +1502,11 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
table->field[1]->store(combo.user.str,combo.user.length, &my_charset_latin1);
+ table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
if (table->file->index_read_idx(table->record[0], 0,
- (byte*) table->field[0]->ptr,0,
- HA_READ_KEY_EXACT))
+ (byte*) table->field[0]->ptr,
+ table->key_info[0].key_length,
+ HA_READ_KEY_EXACT))
{
if (!create_user)
{
@@ -1499,6 +1543,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
Field **tmp_field;
ulong priv;
+ uint next_field;
for (tmp_field= table->field+3, priv = SELECT_ACL;
*tmp_field && (*tmp_field)->real_type() == FIELD_TYPE_ENUM &&
((Field_enum*) (*tmp_field))->typelib->count == 2 ;
@@ -1507,56 +1552,62 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
if (priv & rights) // set requested privileges
(*tmp_field)->store(&what, 1, &my_charset_latin1);
}
- rights=get_access(table,3);
+ rights= get_access(table, 3, &next_field);
DBUG_PRINT("info",("table->fields: %d",table->fields));
if (table->fields >= 31) /* From 4.0.0 we have more fields */
{
/* We write down SSL related ACL stuff */
switch (thd->lex->ssl_type) {
case SSL_TYPE_ANY:
- table->field[24]->store("ANY",3, &my_charset_latin1);
- table->field[25]->store("", 0, &my_charset_latin1);
- table->field[26]->store("", 0, &my_charset_latin1);
- table->field[27]->store("", 0, &my_charset_latin1);
+ table->field[next_field]->store("ANY", 3, &my_charset_latin1);
+ table->field[next_field+1]->store("", 0, &my_charset_latin1);
+ table->field[next_field+2]->store("", 0, &my_charset_latin1);
+ table->field[next_field+3]->store("", 0, &my_charset_latin1);
break;
case SSL_TYPE_X509:
- table->field[24]->store("X509",4, &my_charset_latin1);
- table->field[25]->store("", 0, &my_charset_latin1);
- table->field[26]->store("", 0, &my_charset_latin1);
- table->field[27]->store("", 0, &my_charset_latin1);
+ table->field[next_field]->store("X509", 4, &my_charset_latin1);
+ table->field[next_field+1]->store("", 0, &my_charset_latin1);
+ table->field[next_field+2]->store("", 0, &my_charset_latin1);
+ table->field[next_field+3]->store("", 0, &my_charset_latin1);
break;
case SSL_TYPE_SPECIFIED:
- table->field[24]->store("SPECIFIED",9, &my_charset_latin1);
- table->field[25]->store("", 0, &my_charset_latin1);
- table->field[26]->store("", 0, &my_charset_latin1);
- table->field[27]->store("", 0, &my_charset_latin1);
+ table->field[next_field]->store("SPECIFIED", 9, &my_charset_latin1);
+ table->field[next_field+1]->store("", 0, &my_charset_latin1);
+ table->field[next_field+2]->store("", 0, &my_charset_latin1);
+ table->field[next_field+3]->store("", 0, &my_charset_latin1);
if (thd->lex->ssl_cipher)
- table->field[25]->store(thd->lex->ssl_cipher,
- strlen(thd->lex->ssl_cipher), &my_charset_latin1);
+ table->field[next_field+1]->store(thd->lex->ssl_cipher,
+ strlen(thd->lex->ssl_cipher),
+ &my_charset_latin1);
if (thd->lex->x509_issuer)
- table->field[26]->store(thd->lex->x509_issuer,
- strlen(thd->lex->x509_issuer), &my_charset_latin1);
+ table->field[next_field+2]->store(thd->lex->x509_issuer,
+ strlen(thd->lex->x509_issuer),
+ &my_charset_latin1);
if (thd->lex->x509_subject)
- table->field[27]->store(thd->lex->x509_subject,
- strlen(thd->lex->x509_subject), &my_charset_latin1);
+ table->field[next_field+3]->store(thd->lex->x509_subject,
+ strlen(thd->lex->x509_subject),
+ &my_charset_latin1);
break;
case SSL_TYPE_NOT_SPECIFIED:
break;
case SSL_TYPE_NONE:
- table->field[24]->store("", 0, &my_charset_latin1);
- table->field[25]->store("", 0, &my_charset_latin1);
- table->field[26]->store("", 0, &my_charset_latin1);
- table->field[27]->store("", 0, &my_charset_latin1);
+ table->field[next_field]->store("", 0, &my_charset_latin1);
+ table->field[next_field+1]->store("", 0, &my_charset_latin1);
+ table->field[next_field+2]->store("", 0, &my_charset_latin1);
+ table->field[next_field+3]->store("", 0, &my_charset_latin1);
break;
}
+ /* Skip over SSL related fields to first user limits related field */
+ next_field+= 4;
+
USER_RESOURCES mqh= thd->lex->mqh;
if (mqh.bits & 1)
- table->field[28]->store((longlong) mqh.questions);
+ table->field[next_field]->store((longlong) mqh.questions);
if (mqh.bits & 2)
- table->field[29]->store((longlong) mqh.updates);
+ table->field[next_field+1]->store((longlong) mqh.updates);
if (mqh.bits & 4)
- table->field[30]->store((longlong) mqh.connections);
+ table->field[next_field+2]->store((longlong) mqh.connections);
mqh_used = mqh_used || mqh.questions || mqh.updates || mqh.connections;
}
if (old_row_exists)
@@ -1565,6 +1616,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
We should NEVER delete from the user table, as a uses can still
use mysqld even if he doesn't have any privileges in the user table!
*/
+ table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
if (cmp_record(table,record[1]) &&
(error=table->file->update_row(table->record[1],table->record[0])))
{ // This should never happen
@@ -1642,8 +1694,11 @@ static int replace_db_table(TABLE *table, const char *db,
table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
- if (table->file->index_read_idx(table->record[0],0,(byte*) table->field[0]->ptr,0,
- HA_READ_KEY_EXACT))
+ table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
+ if (table->file->index_read_idx(table->record[0],0,
+ (byte*) table->field[0]->ptr,
+ table->key_info[0].key_length,
+ HA_READ_KEY_EXACT))
{
if (what == 'N')
{ // no row, no revoke
@@ -1676,6 +1731,7 @@ static int replace_db_table(TABLE *table, const char *db,
/* update old existing row */
if (rights)
{
+ table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
if ((error=table->file->update_row(table->record[1],table->record[0])))
goto table_error; /* purecov: deadcode */
}
@@ -1685,7 +1741,7 @@ static int replace_db_table(TABLE *table, const char *db,
goto table_error; /* purecov: deadcode */
}
}
- else if ((error=table->file->write_row(table->record[0])))
+ else if (rights && (error=table->file->write_row(table->record[0])))
{
if (error && error != HA_ERR_FOUND_DUPP_KEY) /* purecov: inspected */
goto table_error; /* purecov: deadcode */
@@ -1695,6 +1751,7 @@ static int replace_db_table(TABLE *table, const char *db,
if (old_row_exists)
acl_update_db(combo.user.str,combo.host.str,db,rights);
else
+ if (rights)
acl_insert_db(combo.user.str,combo.host.str,db,rights);
DBUG_RETURN(0);
@@ -1949,8 +2006,10 @@ static int replace_column_table(GRANT_TABLE *g_t,
table->field[4]->store(xx->column.ptr(),xx->column.length(),
&my_charset_latin1);
+ table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
if (table->file->index_read(table->record[0],(byte*) table->field[0]->ptr,
- 0, HA_READ_KEY_EXACT))
+ table->key_info[0].key_length,
+ HA_READ_KEY_EXACT))
{
if (revoke_grant)
{
@@ -2018,8 +2077,10 @@ static int replace_column_table(GRANT_TABLE *g_t,
if (revoke_grant)
{
+ table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
if (table->file->index_read(table->record[0], (byte*) table->field[0]->ptr,
- key_length, HA_READ_KEY_EXACT))
+ key_length,
+ HA_READ_KEY_EXACT))
goto end;
/* Scan through all rows with the same host,db,user and table */
@@ -2108,9 +2169,10 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
table->field[3]->store(table_name,(uint) strlen(table_name), &my_charset_latin1);
store_record(table,record[1]); // store at pos 1
-
+ table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
if (table->file->index_read_idx(table->record[0],0,
- (byte*) table->field[0]->ptr,0,
+ (byte*) table->field[0]->ptr,
+ table->key_info[0].key_length,
HA_READ_KEY_EXACT))
{
/*
@@ -2232,39 +2294,58 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
DBUG_RETURN(-1);
}
- if (columns.elements && !revoke_grant)
+ if (!revoke_grant)
{
- TABLE *table;
- class LEX_COLUMN *column;
- List_iterator <LEX_COLUMN> column_iter(columns);
-
- if (!(table=open_ltable(thd,table_list,TL_READ)))
- DBUG_RETURN(-1);
- while ((column = column_iter++))
+ if (columns.elements && !revoke_grant)
{
- uint unused_field_idx= NO_CACHED_FIELD_INDEX;
- if (!find_field_in_table(thd,table,column->column.ptr(),
- column->column.length(),0,0,
- &unused_field_idx))
+ TABLE *table;
+ class LEX_COLUMN *column;
+ List_iterator <LEX_COLUMN> column_iter(columns);
+
+ if (!(table=open_ltable(thd,table_list,TL_READ)))
+ DBUG_RETURN(-1);
+ while ((column = column_iter++))
{
- my_error(ER_BAD_FIELD_ERROR, MYF(0),
- column->column.c_ptr(), table_list->alias);
- DBUG_RETURN(-1);
+ uint unused_field_idx= NO_CACHED_FIELD_INDEX;
+ Field *f= find_field_in_table(thd,table,column->column.ptr(),
+ column->column.length(),1,0,&unused_field_idx);
+ if (!f)
+ {
+ my_error(ER_BAD_FIELD_ERROR, MYF(0),
+ column->column.c_ptr(), table_list->alias);
+ DBUG_RETURN(-1);
+ }
+ if (f == (Field*)-1)
+ {
+ DBUG_RETURN(-1);
+ }
+ column_priv|= column->rights;
}
- column_priv|= column->rights;
+ close_thread_tables(thd);
}
- close_thread_tables(thd);
- }
- else if (!(rights & CREATE_ACL) && !revoke_grant)
- {
- char buf[FN_REFLEN];
- sprintf(buf,"%s/%s/%s.frm",mysql_data_home, table_list->db,
- table_list->real_name);
- fn_format(buf,buf,"","",4+16+32);
- if (access(buf,F_OK))
+ else
{
- my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
- DBUG_RETURN(-1);
+ if (!(rights & CREATE_ACL))
+ {
+ char buf[FN_REFLEN];
+ sprintf(buf,"%s/%s/%s.frm",mysql_data_home, table_list->db,
+ table_list->real_name);
+ fn_format(buf,buf,"","",4+16+32);
+ if (access(buf,F_OK))
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
+ DBUG_RETURN(-1);
+ }
+ }
+ if (table_list->grant.want_privilege)
+ {
+ char command[128];
+ get_privilege_desc(command, sizeof(command),
+ table_list->grant.want_privilege);
+ my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
+ command, thd->priv_user, thd->host_or_ip, table_list->alias);
+ DBUG_RETURN(-1);
+ }
}
}
@@ -2309,8 +2390,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
create_new_users= test_if_create_new_users(thd);
int result=0;
rw_wrlock(&LOCK_grant);
- MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
- my_pthread_setspecific_ptr(THR_MALLOC,&memex);
+ MEM_ROOT *old_root= thd->mem_root;
+ thd->mem_root= &memex;
while ((Str = str_list++))
{
@@ -2415,7 +2496,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
}
}
grant_option=TRUE;
- my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ thd->mem_root= old_root;
rw_unlock(&LOCK_grant);
if (!result)
send_ok(thd);
@@ -2549,6 +2630,7 @@ my_bool grant_init(THD *org_thd)
THD *thd;
TABLE_LIST tables[2];
MYSQL_LOCK *lock;
+ MEM_ROOT *memex_ptr;
my_bool return_val= 1;
TABLE *t_table, *c_table;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
@@ -2596,7 +2678,8 @@ my_bool grant_init(THD *org_thd)
grant_option= TRUE;
/* Will be restored by org_thd->store_globals() */
- my_pthread_setspecific_ptr(THR_MALLOC,&memex);
+ memex_ptr= &memex;
+ my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
do
{
GRANT_TABLE *mem_check;
@@ -2653,7 +2736,7 @@ end:
SYNOPSIS
grant_reload()
- thd Thread handler
+ thd Thread handler (can be NULL)
NOTES
Locked tables are checked by acl_init and doesn't have to be checked here
@@ -2747,25 +2830,8 @@ err:
rw_unlock(&LOCK_grant);
if (!no_errors) // Not a silent skip of table
{
- const char *command="";
- if (want_access & SELECT_ACL)
- command= "select";
- else if (want_access & INSERT_ACL)
- command= "insert";
- else if (want_access & UPDATE_ACL)
- command= "update";
- else if (want_access & DELETE_ACL)
- command= "delete";
- else if (want_access & DROP_ACL)
- command= "drop";
- else if (want_access & CREATE_ACL)
- command= "create";
- else if (want_access & ALTER_ACL)
- command= "alter";
- else if (want_access & INDEX_ACL)
- command= "index";
- else if (want_access & GRANT_ACL)
- command= "grant";
+ char command[128];
+ get_privilege_desc(command, sizeof(command), want_access);
net_printf(thd,ER_TABLEACCESS_DENIED_ERROR,
command,
thd->priv_user,
@@ -2880,11 +2946,8 @@ bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table)
err:
rw_unlock(&LOCK_grant);
err2:
- const char *command= "";
- if (want_access & SELECT_ACL)
- command= "select";
- else if (want_access & INSERT_ACL)
- command= "insert";
+ char command[128];
+ get_privilege_desc(command, sizeof(command), want_access);
my_printf_error(ER_COLUMNACCESS_DENIED_ERROR,
ER(ER_COLUMNACCESS_DENIED_ERROR),
MYF(0),
@@ -3565,9 +3628,12 @@ int mysql_drop_user(THD *thd, List <LEX_USER> &list)
tables[0].table->field[1]->store(user_name->user.str,(uint)
user_name->user.length,
system_charset_info);
+ tables[0].table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
if (!tables[0].table->file->index_read_idx(tables[0].table->record[0],0,
(byte*) tables[0].table->
- field[0]->ptr,0,
+ field[0]->ptr,
+ tables[0].table->
+ key_info[0].key_length,
HA_READ_KEY_EXACT))
{
int error;
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 68cb1476eb5..dc1b04c063a 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -143,7 +143,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd,
uint passwd_len);
bool acl_check_host(const char *host, const char *ip);
bool check_change_password(THD *thd, const char *host, const char *user,
- char *password);
+ char *password, uint password_len);
bool change_password(THD *thd, const char *host, const char *user,
char *password);
int mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list,
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc
index 1e0aebbc1ec..7ac9a0866df 100644
--- a/sql/sql_analyse.cc
+++ b/sql/sql_analyse.cc
@@ -59,6 +59,7 @@ int compare_ulonglong2(void* cmp_arg __attribute__((unused)),
return compare_ulonglong(s,t);
}
+static bool append_escaped(String *to_str, String *from_str);
Procedure *
proc_analyse_init(THD *thd, ORDER *param, select_result *result,
@@ -69,6 +70,9 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result,
field_info **f_info;
DBUG_ENTER("proc_analyse_init");
+ if (!pc)
+ DBUG_RETURN(0);
+
if (!(param = param->next))
{
pc->max_tree_elements = MAX_TREE_ELEMENTS;
@@ -80,33 +84,30 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result,
if ((*param->item)->type() != Item::INT_ITEM ||
(*param->item)->val() < 0)
{
- delete pc;
my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name);
- DBUG_RETURN(0);
+ goto err;
}
pc->max_tree_elements = (uint) (*param->item)->val_int();
param = param->next;
if (param->next) // no third parameter possible
{
my_error(ER_WRONG_PARAMCOUNT_TO_PROCEDURE, MYF(0), proc_name);
- DBUG_RETURN(0);
+ goto err;
}
// second parameter
if ((*param->item)->type() != Item::INT_ITEM ||
(*param->item)->val() < 0)
{
- delete pc;
my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name);
- DBUG_RETURN(0);
+ goto err;
}
pc->max_treemem = (uint) (*param->item)->val_int();
}
else if ((*param->item)->type() != Item::INT_ITEM ||
(*param->item)->val() < 0)
{
- delete pc;
my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name);
- DBUG_RETURN(0);
+ goto err;
}
// if only one parameter was given, it will be the value of max_tree_elements
else
@@ -115,34 +116,39 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result,
pc->max_treemem = MAX_TREEMEM;
}
- if (!pc || !(pc->f_info = (field_info**)
- sql_alloc(sizeof(field_info*)*field_list.elements)))
- DBUG_RETURN(0);
+ if (!(pc->f_info=
+ (field_info**)sql_alloc(sizeof(field_info*)*field_list.elements)))
+ goto err;
pc->f_end = pc->f_info + field_list.elements;
pc->fields = field_list;
- List_iterator_fast<Item> it(pc->fields);
- f_info = pc->f_info;
-
- Item *item;
- while ((item = it++))
{
- if (item->result_type() == INT_RESULT)
+ List_iterator_fast<Item> it(pc->fields);
+ f_info = pc->f_info;
+
+ Item *item;
+ while ((item = it++))
{
- // Check if fieldtype is ulonglong
- if (item->type() == Item::FIELD_ITEM &&
- ((Item_field*) item)->field->type() == FIELD_TYPE_LONGLONG &&
- ((Field_longlong*) ((Item_field*) item)->field)->unsigned_flag)
- *f_info++ = new field_ulonglong(item, pc);
- else
- *f_info++ = new field_longlong(item, pc);
+ if (item->result_type() == INT_RESULT)
+ {
+ // Check if fieldtype is ulonglong
+ if (item->type() == Item::FIELD_ITEM &&
+ ((Item_field*) item)->field->type() == FIELD_TYPE_LONGLONG &&
+ ((Field_longlong*) ((Item_field*) item)->field)->unsigned_flag)
+ *f_info++ = new field_ulonglong(item, pc);
+ else
+ *f_info++ = new field_longlong(item, pc);
+ }
+ if (item->result_type() == REAL_RESULT)
+ *f_info++ = new field_real(item, pc);
+ if (item->result_type() == STRING_RESULT)
+ *f_info++ = new field_str(item, pc);
}
- if (item->result_type() == REAL_RESULT)
- *f_info++ = new field_real(item, pc);
- if (item->result_type() == STRING_RESULT)
- *f_info++ = new field_str(item, pc);
}
DBUG_RETURN(pc);
+err:
+ delete pc;
+ DBUG_RETURN(0);
}
@@ -890,7 +896,8 @@ int collect_string(String *element,
else
info->found = 1;
info->str->append('\'');
- info->str->append(*element);
+ if (append_escaped(info->str, element))
+ return 1;
info->str->append('\'');
return 0;
} // collect_string
@@ -1025,3 +1032,57 @@ uint check_ulonglong(const char *str, uint length)
while (*cmp && *cmp++ == *str++) ;
return ((uchar) str[-1] <= (uchar) cmp[-1]) ? smaller : bigger;
} /* check_ulonlong */
+
+
+/*
+ Quote special characters in a string.
+
+ SYNOPSIS
+ append_escaped(to_str, from_str)
+ to_str (in) A pointer to a String.
+ from_str (to) A pointer to an allocated string
+
+ DESCRIPTION
+ append_escaped() takes a String type variable, where it appends
+ escaped the second argument. Only characters that require escaping
+ will be escaped.
+
+ RETURN VALUES
+ 0 Success
+ 1 Out of memory
+*/
+
+static bool append_escaped(String *to_str, String *from_str)
+{
+ char *from, *end, c;
+
+ if (to_str->realloc(to_str->length() + from_str->length()))
+ return 1;
+
+ from= (char*) from_str->ptr();
+ end= from + from_str->length();
+ for (; from < end; from++)
+ {
+ c= *from;
+ switch (c) {
+ case '\0':
+ c= '0';
+ break;
+ case '\032':
+ c= 'Z';
+ break;
+ case '\\':
+ case '\'':
+ break;
+ default:
+ goto normal_character;
+ }
+ if (to_str->append('\\'))
+ return 1;
+
+ normal_character:
+ if (to_str->append(c))
+ return 1;
+ }
+ return 0;
+}
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 9313f8e2c1b..8d694c48849 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -18,7 +18,6 @@
/* Basic functions needed by many modules */
#include "mysql_priv.h"
-#include "sql_acl.h"
#include "sql_select.h"
#include <m_ctype.h>
#include <my_dir.h>
@@ -252,13 +251,19 @@ void free_io_cache(TABLE *table)
DBUG_VOID_RETURN;
}
- /* Close all tables which aren't in use by any thread */
+/*
+ Close all tables which aren't in use by any thread
+
+ THD can be NULL, but then if_wait_for_refresh must be FALSE
+ and tables must be NULL.
+*/
bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
TABLE_LIST *tables)
{
bool result=0;
DBUG_ENTER("close_cached_tables");
+ DBUG_ASSERT(thd || (!if_wait_for_refresh && !tables));
VOID(pthread_mutex_lock(&LOCK_open));
if (!tables)
@@ -334,7 +339,6 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
VOID(pthread_mutex_unlock(&LOCK_open));
if (if_wait_for_refresh)
{
- THD *thd=current_thd;
pthread_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
@@ -499,7 +503,7 @@ void close_temporary_tables(THD *thd)
*/
query_buf_size+= table->key_length+1;
- if ((query = alloc_root(&thd->mem_root, query_buf_size)))
+ if ((query = alloc_root(thd->mem_root, query_buf_size)))
// Better add "if exists", in case a RESET MASTER has been done
end=strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ");
@@ -524,7 +528,7 @@ void close_temporary_tables(THD *thd)
{
/* The -1 is to remove last ',' */
thd->clear_error();
- Query_log_event qinfo(thd, query, (ulong)(end-query)-1, 0);
+ Query_log_event qinfo(thd, query, (ulong)(end-query)-1, 0, FALSE);
/*
Imagine the thread had created a temp table, then was doing a SELECT, and
the SELECT was killed. Then it's not clever to mark the statement above as
@@ -806,8 +810,9 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name,
for (table=thd->temporary_tables; table ; table=table->next)
{
- if (table->key_length == key_length+8 &&
- !memcmp(table->table_cache_key,key,key_length+8))
+ if (table->key_length == key_length + TMP_TABLE_KEY_EXTRA &&
+ !memcmp(table->table_cache_key, key,
+ key_length + TMP_TABLE_KEY_EXTRA))
{
if (table->query_id == thd->query_id)
{
@@ -1440,7 +1445,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
{
end = strxmov(strmov(query, "DELETE FROM `"),
db,"`.`",name,"`", NullS);
- Query_log_event qinfo(thd, query, (ulong)(end-query), 0);
+ Query_log_event qinfo(thd, query, (ulong)(end-query), 0, FALSE);
mysql_bin_log.write(&qinfo);
my_free(query, MYF(0));
}
@@ -1703,6 +1708,34 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables)
/*
+ Open all tables in list and process derived tables
+
+ SYNOPSIS
+ open_normal_and_derived_tables
+ thd - thread handler
+ tables - list of tables for open&locking
+
+ RETURN
+ FALSE - ok
+ TRUE - error
+
+ NOTE
+ This is to be used on prepare stage when you don't read any
+ data from the tables.
+*/
+
+int open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables)
+{
+ uint counter;
+ DBUG_ENTER("open_normal_and_derived_tables");
+ if (open_tables(thd, tables, &counter))
+ DBUG_RETURN(-1); /* purecov: inspected */
+ relink_tables_for_derived(thd);
+ DBUG_RETURN(mysql_handle_derived(thd->lex));
+}
+
+
+/*
Let us propagate pointers to open tables from global table list
to table lists in particular selects if needed.
*/
@@ -2040,13 +2073,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
strxnmov(buff,sizeof(buff)-1,db,".",table_name,NullS);
table_name=buff;
}
- if (report_error)
- {
- my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0),
- table_name, thd->where);
- }
- else
- return (Field*) not_found_field;
+ my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0),
+ table_name, thd->where);
}
else
if (report_error)
@@ -2305,23 +2333,20 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
List<Item> *sum_func_list,
uint wild_num)
{
- bool is_stmt_prepare;
DBUG_ENTER("setup_wild");
if (!wild_num)
DBUG_RETURN(0);
- Item_arena *arena= thd->current_arena, backup;
-
+ reg2 Item *item;
+ List_iterator<Item> it(fields);
+ Item_arena *arena, backup;
/*
If we are in preparing prepared statement phase then we have change
temporary mem_root to statement mem root to save changes of SELECT list
*/
- if ((is_stmt_prepare= arena->is_stmt_prepare()))
- thd->set_n_backup_item_arena(arena, &backup);
+ arena= thd->change_arena_if_needed(&backup);
- reg2 Item *item;
- List_iterator<Item> it(fields);
- while ( wild_num && (item= it++))
+ while (wild_num && (item= it++))
{
if (item->type() == Item::FIELD_ITEM &&
((Item_field*) item)->field_name &&
@@ -2343,7 +2368,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
else if (insert_fields(thd,tables,((Item_field*) item)->db_name,
((Item_field*) item)->table_name, &it))
{
- if (is_stmt_prepare)
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(-1);
}
@@ -2359,7 +2384,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
wild_num--;
}
}
- if (is_stmt_prepare)
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(0);
}
@@ -2380,6 +2405,20 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
thd->allow_sum_func= allow_sum_func;
thd->where="field list";
+ /*
+ To prevent fail on forward lookup we fill it with zerows,
+ then if we got pointer on zero after find_item_in_list we will know
+ that it is forward lookup.
+
+ There is other way to solve problem: fill array with pointers to list,
+ but it will be slower.
+
+ TODO: remove it when (if) we made one list for allfields and
+ ref_pointer_array
+ */
+ if (ref_pointer_array)
+ bzero(ref_pointer_array, sizeof(Item *) * fields.elements);
+
Item **ref= ref_pointer_array;
while ((item= it++))
{
@@ -2588,11 +2627,10 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name,
int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
{
table_map not_null_tables= 0;
- Item_arena *arena= thd->current_arena, backup;
- bool is_stmt_prepare= arena->is_stmt_prepare();
+ Item_arena *arena= 0, backup;
DBUG_ENTER("setup_conds");
+
thd->set_query_id=1;
-
thd->lex->current_select->cond_count= 0;
if (*conds)
{
@@ -2627,12 +2665,14 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
!(specialflag & SPECIAL_NO_NEW_FUNC)))
{
table->outer_join= 0;
- if (is_stmt_prepare)
- thd->set_n_backup_item_arena(arena, &backup);
+ arena= thd->change_arena_if_needed(&backup);
*conds= and_conds(*conds, table->on_expr);
table->on_expr=0;
- if (is_stmt_prepare)
+ if (arena)
+ {
thd->restore_backup_item_arena(arena, &backup);
+ arena= 0; // Safety if goto err
+ }
if ((*conds) && !(*conds)->fixed &&
(*conds)->fix_fields(thd, tables, conds))
DBUG_RETURN(1);
@@ -2640,8 +2680,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
}
if (table->natural_join)
{
- if (is_stmt_prepare)
- thd->set_n_backup_item_arena(arena, &backup);
+ arena= thd->change_arena_if_needed(&backup);
/* Make a join of all fields with have the same name */
TABLE *t1= table->table;
TABLE *t2= table->natural_join->table;
@@ -2682,11 +2721,12 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
{
*conds= and_conds(*conds, cond_and);
// fix_fields() should be made with temporary memory pool
- if (is_stmt_prepare)
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
if (*conds && !(*conds)->fixed)
{
- if ((*conds)->fix_fields(thd, tables, conds))
+ if (!(*conds)->fixed &&
+ (*conds)->fix_fields(thd, tables, conds))
DBUG_RETURN(1);
}
}
@@ -2694,21 +2734,25 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
{
table->on_expr= and_conds(table->on_expr, cond_and);
// fix_fields() should be made with temporary memory pool
- if (is_stmt_prepare)
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
if (table->on_expr && !table->on_expr->fixed)
{
- if (table->on_expr->fix_fields(thd, tables, &table->on_expr))
+ if (!table->on_expr->fixed &&
+ table->on_expr->fix_fields(thd, tables, &table->on_expr))
DBUG_RETURN(1);
}
}
}
- else if (is_stmt_prepare)
+ else if (arena)
+ {
thd->restore_backup_item_arena(arena, &backup);
+ arena= 0; // Safety if goto err
+ }
}
}
- if (is_stmt_prepare)
+ if (thd->current_arena->is_stmt_prepare())
{
/*
We are in prepared statement preparation code => we should store
@@ -2721,8 +2765,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
DBUG_RETURN(test(thd->net.report_error));
err:
- if (is_stmt_prepare)
- thd->restore_backup_item_arena(arena, &backup);
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(1);
}
@@ -2824,8 +2868,18 @@ static void mysql_rm_tmp_tables(void)
** and afterwards delete those marked unused.
*/
-void remove_db_from_cache(const my_string db)
+void remove_db_from_cache(const char *db)
{
+ char name_buff[NAME_LEN+1];
+ if (db && lower_case_table_names)
+ {
+ /*
+ convert database to lower case for comparision.
+ */
+ strmake(name_buff, db, sizeof(name_buff)-1);
+ my_casedn_str(files_charset_info, name_buff);
+ db= name_buff;
+ }
for (uint idx=0 ; idx < open_cache.records ; idx++)
{
TABLE *table=(TABLE*) hash_element(&open_cache,idx);
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 0b338ebccb8..6eff958257b 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -300,7 +300,6 @@ TODO list:
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
-#include "sql_acl.h"
#include "ha_myisammrg.h"
#ifndef MASTER
#include "../srclib/myisammrg/myrg_def.h"
@@ -375,7 +374,7 @@ inline Query_cache_block * Query_cache_block_table::block()
return (Query_cache_block *)(((byte*)this) -
ALIGN_SIZE(sizeof(Query_cache_block_table)*n) -
ALIGN_SIZE(sizeof(Query_cache_block)));
-};
+}
/*****************************************************************************
Query_cache_block method(s)
@@ -777,6 +776,8 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
bzero(&flags, QUERY_CACHE_FLAGS_SIZE);
flags.client_long_flag= (thd->client_capabilities & CLIENT_LONG_FLAG ?
1 : 0);
+ flags.client_protocol_41= (thd->client_capabilities & CLIENT_PROTOCOL_41 ?
+ 1 : 0);
flags.character_set_client_num=
thd->variables.character_set_client->number;
flags.character_set_results_num=
@@ -969,6 +970,8 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
bzero(&flags, QUERY_CACHE_FLAGS_SIZE);
flags.client_long_flag= (thd->client_capabilities & CLIENT_LONG_FLAG ?
1 : 0);
+ flags.client_protocol_41= (thd->client_capabilities & CLIENT_PROTOCOL_41 ?
+ 1 : 0);
flags.character_set_client_num= thd->variables.character_set_client->number;
flags.character_set_results_num=
(thd->variables.character_set_results ?
@@ -1025,9 +1028,38 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
for (; block_table != block_table_end; block_table++)
{
TABLE_LIST table_list;
- bzero((char*) &table_list,sizeof(table_list));
+ TABLE *tmptable;
Query_cache_table *table = block_table->parent;
+
+ /*
+ Check that we have not temporary tables with same names of tables
+ of this query. If we have such tables, we will not send data from
+ query cache, because temporary tables hide real tables by which
+ query in query cache was made.
+ */
+ for (tmptable= thd->temporary_tables; tmptable ; tmptable= tmptable->next)
+ {
+ if (tmptable->key_length - TMP_TABLE_KEY_EXTRA == table->key_length() &&
+ !memcmp(tmptable->table_cache_key, table->data(),
+ table->key_length()))
+ {
+ DBUG_PRINT("qcache",
+ ("Temporary table detected: '%s.%s'",
+ table_list.db, table_list.alias));
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ /*
+ We should not store result of this query because it contain
+ temporary tables => assign following variable to make check
+ faster.
+ */
+ thd->lex->safe_to_cache_query=0;
+ BLOCK_UNLOCK_RD(query_block);
+ DBUG_RETURN(-1);
+ }
+ }
+
+ bzero((char*) &table_list,sizeof(table_list));
table_list.db = table->db();
table_list.alias= table_list.real_name= table->table();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -1129,12 +1161,12 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
DBUG_ASSERT(!using_transactions || tables_used->table!=0);
if (tables_used->derived)
continue;
- if (using_transactions &&
- (tables_used->table->file->table_cache_type() ==
+ 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'
+ Only 'drop' invalidate not opened table, but 'drop'
force transaction finish.
*/
thd->add_changed_table(tables_used->table);
@@ -1182,7 +1214,7 @@ 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 (changed table list)");
+ DBUG_ENTER("Query_cache::invalidate_locked_for_write");
if (query_cache_size > 0 && tables_used)
{
STRUCT_LOCK(&structure_guard_mutex);
@@ -1389,7 +1421,7 @@ ulong Query_cache::init_cache()
init();
approx_additional_data_size = (sizeof(Query_cache) +
sizeof(gptr)*(def_query_hash_size+
- def_query_hash_size));
+ def_table_hash_size));
if (query_cache_size < approx_additional_data_size)
goto err;
diff --git a/sql/sql_cache.h b/sql/sql_cache.h
index fc458f39e29..c933a2349af 100644
--- a/sql/sql_cache.h
+++ b/sql/sql_cache.h
@@ -143,14 +143,14 @@ struct Query_cache_query
struct Query_cache_table
{
char *tbl;
- uint key_len;
+ uint32 key_len;
uint8 table_type;
inline char *db() { return (char *) data(); }
inline char *table() { return tbl; }
inline void table(char *table) { tbl= table; }
- inline uint key_length() { return key_len; }
- inline void key_length(uint len) { key_len= len; }
+ inline uint32 key_length() { return key_len; }
+ inline void key_length(uint32 len) { key_len= len; }
inline uint8 type() { return table_type; }
inline void type(uint8 t) { table_type= t; }
inline gptr data()
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index b6fdac03526..c20d5f79277 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -27,7 +27,6 @@
#endif
#include "mysql_priv.h"
-#include "sql_acl.h"
#include <m_ctype.h>
#include <sys/stat.h>
#include <thr_alarm.h>
@@ -281,6 +280,9 @@ void THD::init(void)
variables.date_format);
variables.datetime_format= date_time_format_copy((THD*) 0,
variables.datetime_format);
+#ifdef HAVE_NDBCLUSTER_DB
+ variables.ndb_use_transactions= 1;
+#endif
pthread_mutex_unlock(&LOCK_global_system_variables);
server_status= SERVER_STATUS_AUTOCOMMIT;
options= thd_startup_options;
@@ -306,7 +308,7 @@ void THD::init_for_queries()
{
ha_enable_transaction(this,TRUE);
- reset_root_defaults(&mem_root, variables.query_alloc_block_size,
+ reset_root_defaults(mem_root, variables.query_alloc_block_size,
variables.query_prealloc_size);
reset_root_defaults(&transaction.mem_root,
variables.trans_alloc_block_size,
@@ -412,7 +414,7 @@ THD::~THD()
dbug_sentry = THD_SENTRY_GONE;
#endif
/* Reset stmt_backup.mem_root to not double-free memory from thd.mem_root */
- clear_alloc_root(&stmt_backup.mem_root);
+ clear_alloc_root(&stmt_backup.main_mem_root);
DBUG_VOID_RETURN;
}
@@ -509,13 +511,14 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
{
DBUG_ENTER("convert_string");
size_s new_length= to_cs->mbmaxlen * from_length;
+ uint dummy_errors;
if (!(to->str= alloc(new_length+1)))
{
to->length= 0; // Safety fix
DBUG_RETURN(1); // EOM
}
to->length= copy_and_convert((char*) to->str, new_length, to_cs,
- from, from_length, from_cs);
+ from, from_length, from_cs, &dummy_errors);
to->str[to->length]=0; // Safety
DBUG_RETURN(0);
}
@@ -538,7 +541,8 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
bool THD::convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
{
- if (convert_buffer.copy(s->ptr(), s->length(), from_cs, to_cs))
+ uint dummy_errors;
+ if (convert_buffer.copy(s->ptr(), s->length(), from_cs, to_cs, &dummy_errors))
return TRUE;
/* If convert_buffer >> s copying is more efficient long term */
if (convert_buffer.alloced_length() >= convert_buffer.length() * 2 ||
@@ -704,6 +708,8 @@ struct Item_change_record: public ilink
Item *old_value;
/* Placement new was hidden by `new' in ilink (TODO: check): */
static void *operator new(size_t size, void *mem) { return mem; }
+ static void operator delete(void *ptr, size_t size) {}
+ static void operator delete(void *ptr, void *mem) { /* never called */ }
};
@@ -958,7 +964,7 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange,
return -1;
}
/* Create the file world readable */
- if ((file= my_create(path, 0666, O_WRONLY, MYF(MY_WME))) < 0)
+ if ((file= my_create(path, 0666, O_WRONLY|O_EXCL, MYF(MY_WME))) < 0)
return file;
#ifdef HAVE_FCHMOD
(void) fchmod(file, 0666); // Because of umask()
@@ -1237,12 +1243,21 @@ bool select_singlerow_subselect::send_data(List<Item> &items)
}
+void select_max_min_finder_subselect::cleanup()
+{
+ DBUG_ENTER("select_max_min_finder_subselect::cleanup");
+ cache= 0;
+ DBUG_VOID_RETURN;
+}
+
+
bool select_max_min_finder_subselect::send_data(List<Item> &items)
{
DBUG_ENTER("select_max_min_finder_subselect::send_data");
- Item_singlerow_subselect *it= (Item_singlerow_subselect *)item;
+ Item_maxmin_subselect *it= (Item_maxmin_subselect *)item;
List_iterator_fast<Item> li(items);
Item *val_item= li++;
+ it->register_value();
if (it->assigned())
{
cache->store(val_item);
@@ -1393,10 +1408,10 @@ void select_dumpvar::cleanup()
for memory root initialization.
*/
Item_arena::Item_arena(THD* thd)
- :free_list(0),
- state(INITIALIZED)
+ :free_list(0), mem_root(&main_mem_root),
+ state(INITIALIZED)
{
- init_sql_alloc(&mem_root,
+ init_sql_alloc(&main_mem_root,
thd->variables.query_alloc_block_size,
thd->variables.query_prealloc_size);
}
@@ -1419,11 +1434,11 @@ Item_arena::Item_arena(THD* thd)
statements.
*/
Item_arena::Item_arena(bool init_mem_root)
- :free_list(0),
+ :free_list(0), mem_root(&main_mem_root),
state(CONVENTIONAL_EXECUTION)
{
if (init_mem_root)
- init_sql_alloc(&mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
+ init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
}
@@ -1517,13 +1532,16 @@ void THD::end_statement()
void Item_arena::set_n_backup_item_arena(Item_arena *set, Item_arena *backup)
{
+ DBUG_ENTER("Item_arena::set_n_backup_item_arena");
backup->set_item_arena(this);
set_item_arena(set);
+ DBUG_VOID_RETURN;
}
void Item_arena::restore_backup_item_arena(Item_arena *set, Item_arena *backup)
{
+ DBUG_ENTER("Item_arena::restore_backup_item_arena");
set->set_item_arena(this);
set_item_arena(backup);
#ifdef NOT_NEEDED_NOW
@@ -1536,18 +1554,19 @@ void Item_arena::restore_backup_item_arena(Item_arena *set, Item_arena *backup)
*/
clear_alloc_root(&backup->mem_root);
#endif
+ DBUG_VOID_RETURN;
}
void Item_arena::set_item_arena(Item_arena *set)
{
- mem_root= set->mem_root;
+ mem_root= set->mem_root;
free_list= set->free_list;
state= set->state;
}
Statement::~Statement()
{
- free_root(&mem_root, MYF(0));
+ free_root(&main_mem_root, MYF(0));
}
C_MODE_START
diff --git a/sql/sql_class.h b/sql/sql_class.h
index e73b35966a9..703bb030ab9 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -29,7 +29,7 @@ class Slave_log_event;
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
-enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_IGNORE, DUP_UPDATE };
+enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE };
enum enum_log_type { LOG_CLOSED, LOG_TO_BE_OPENED, LOG_NORMAL, LOG_NEW, LOG_BIN};
enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON,
DELAY_KEY_WRITE_ALL };
@@ -131,7 +131,7 @@ public:
DBUG_VOID_RETURN;
}
void set_max_size(ulong max_size_arg);
- void signal_update() { pthread_cond_broadcast(&update_cond);}
+ void signal_update();
void wait_for_update(THD* thd, bool master_or_slave);
void set_need_start_event() { need_start_event = 1; }
void init(enum_log_type log_type_arg,
@@ -201,7 +201,8 @@ typedef struct st_copy_info {
ha_rows error_count;
enum enum_duplicates handle_duplicates;
int escape_char, last_errno;
-/* for INSERT ... UPDATE */
+ bool ignore;
+ /* for INSERT ... UPDATE */
List<Item> *update_fields;
List<Item> *update_values;
} COPY_INFO;
@@ -396,9 +397,20 @@ struct system_variables
my_bool low_priority_updates;
my_bool new_mode;
my_bool query_cache_wlock_invalidate;
+#ifdef HAVE_REPLICATION
+ ulong sync_replication;
+ ulong sync_replication_slave_id;
+ ulong sync_replication_timeout;
+#endif /* HAVE_REPLICATION */
#ifdef HAVE_INNOBASE_DB
my_bool innodb_table_locks;
#endif /* HAVE_INNOBASE_DB */
+#ifdef HAVE_NDBCLUSTER_DB
+ ulong ndb_autoincrement_prefetch_sz;
+ my_bool ndb_force_send;
+ my_bool ndb_use_exact_count;
+ my_bool ndb_use_transactions;
+#endif /* HAVE_NDBCLUSTER_DB */
my_bool old_passwords;
/* Only charset part of these variables is sensible */
@@ -429,7 +441,8 @@ public:
itself to the list on creation (see Item::Item() for details))
*/
Item *free_list;
- MEM_ROOT mem_root;
+ MEM_ROOT main_mem_root;
+ MEM_ROOT *mem_root; // Pointer to current memroot
enum enum_state
{
INITIALIZED= 0, PREPARED= 1, EXECUTED= 3, CONVENTIONAL_EXECUTION= 2,
@@ -468,24 +481,24 @@ public:
{ return state == PREPARED || state == EXECUTED; }
inline bool is_conventional_execution() const
{ return state == CONVENTIONAL_EXECUTION; }
- inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); }
+ inline gptr alloc(unsigned int size) { return alloc_root(mem_root,size); }
inline gptr calloc(unsigned int size)
{
gptr ptr;
- if ((ptr=alloc_root(&mem_root,size)))
+ if ((ptr=alloc_root(mem_root,size)))
bzero((char*) ptr,size);
return ptr;
}
inline char *strdup(const char *str)
- { return strdup_root(&mem_root,str); }
+ { return strdup_root(mem_root,str); }
inline char *strmake(const char *str, uint size)
- { return strmake_root(&mem_root,str,size); }
+ { return strmake_root(mem_root,str,size); }
inline char *memdup(const char *str, uint size)
- { return memdup_root(&mem_root,str,size); }
+ { return memdup_root(mem_root,str,size); }
inline char *memdup_w_gap(const char *str, uint size, uint gap)
{
gptr ptr;
- if ((ptr=alloc_root(&mem_root,size+gap)))
+ if ((ptr=alloc_root(mem_root,size+gap)))
memcpy(ptr,str,size);
return ptr;
}
@@ -641,8 +654,8 @@ public:
/* Erase all statements (calls Statement destructor) */
void reset()
{
- hash_reset(&names_hash);
- hash_reset(&st_hash);
+ my_hash_reset(&names_hash);
+ my_hash_reset(&st_hash);
last_found_statement= 0;
}
@@ -1052,11 +1065,26 @@ public:
inline CHARSET_INFO *charset() { return variables.character_set_client; }
void update_charset();
+ inline Item_arena *change_arena_if_needed(Item_arena *backup)
+ {
+ /*
+ use new arena if we are in a prepared statements and we have not
+ already changed to use this arena.
+ */
+ if (current_arena->is_stmt_prepare() &&
+ mem_root != &current_arena->main_mem_root)
+ {
+ set_n_backup_item_arena(current_arena, backup);
+ return current_arena;
+ }
+ return 0;
+ }
+
void change_item_tree(Item **place, Item *new_value)
{
/* TODO: check for OOM condition here */
if (!current_arena->is_conventional_execution())
- nocheck_register_item_tree_change(place, *place, &mem_root);
+ nocheck_register_item_tree_change(place, *place, mem_root);
*place= new_value;
}
void nocheck_register_item_tree_change(Item **place, Item *old_value,
@@ -1070,6 +1098,12 @@ public:
void end_statement();
};
+#define tmp_disable_binlog(A) \
+ ulong save_options= (A)->options; \
+ (A)->options&= ~OPTION_BIN_LOG;
+
+#define reenable_binlog(A) (A)->options= save_options;
+
/* Flags for the THD::system_thread (bitmap) variable */
#define SYSTEM_THREAD_DELAYED_INSERT 1
#define SYSTEM_THREAD_SLAVE_IO 2
@@ -1204,15 +1238,28 @@ class select_insert :public select_result_interceptor {
COPY_INFO info;
select_insert(TABLE *table_par, List<Item> *fields_par,
- enum_duplicates duplic)
+ enum_duplicates duplic, bool ignore)
:table(table_par), fields(fields_par), last_insert_id(0)
{
bzero((char*) &info,sizeof(info));
+ info.ignore= ignore;
info.handle_duplicates=duplic;
}
+ select_insert(TABLE *table_par, List<Item> *fields_par,
+ List<Item> *update_fields, List<Item> *update_values,
+ enum_duplicates duplic, bool ignore)
+ :table(table_par), fields(fields_par), last_insert_id(0)
+ {
+ bzero((char*) &info,sizeof(info));
+ info.ignore= ignore;
+ info.handle_duplicates= duplic;
+ info.update_fields= update_fields;
+ info.update_values= update_values;
+ }
~select_insert();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_data(List<Item> &items);
+ virtual void store_values(List<Item> &values);
void send_error(uint errcode,const char *err);
bool send_eof();
/* not implemented: select_insert is never re-used in prepared statements */
@@ -1234,20 +1281,25 @@ public:
HA_CREATE_INFO *create_info_par,
List<create_field> &fields_par,
List<Key> &keys_par,
- List<Item> &select_fields,enum_duplicates duplic)
- :select_insert (NULL, &select_fields, duplic), db(db_name),
+ List<Item> &select_fields,enum_duplicates duplic, bool ignore)
+ :select_insert (NULL, &select_fields, duplic, ignore), db(db_name),
name(table_name), extra_fields(&fields_par),keys(&keys_par),
create_info(create_info_par), lock(0)
{}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- bool send_data(List<Item> &values);
+ void store_values(List<Item> &values);
+ void send_error(uint errcode,const char *err);
bool send_eof();
void abort();
};
#include <myisam.h>
-/* Param to create temporary tables when doing SELECT:s */
+/*
+ Param to create temporary tables when doing SELECT:s
+ NOTE
+ This structure is copied using memcpy as a part of JOIN.
+*/
class TMP_TABLE_PARAM :public Sql_alloc
{
@@ -1259,7 +1311,6 @@ private:
public:
List<Item> copy_funcs;
List<Item> save_copy_funcs;
- List_iterator_fast<Item> copy_funcs_it;
Copy_field *copy_field, *copy_field_end;
Copy_field *save_copy_field, *save_copy_field_end;
byte *group_buff;
@@ -1276,7 +1327,7 @@ public:
uint convert_blob_length;
TMP_TABLE_PARAM()
- :copy_funcs_it(copy_funcs), copy_field(0), group_parts(0),
+ :copy_field(0), group_parts(0),
group_length(0), group_null_parts(0), convert_blob_length(0)
{}
~TMP_TABLE_PARAM()
@@ -1289,7 +1340,7 @@ public:
if (copy_field) /* Fix for Intel compiler */
{
delete [] copy_field;
- copy_field=0;
+ save_copy_field= copy_field= 0;
}
}
};
@@ -1338,6 +1389,7 @@ public:
select_max_min_finder_subselect(Item_subselect *item, bool mx)
:select_subselect(item), cache(0), fmax(mx)
{}
+ void cleanup();
bool send_data(List<Item> &items);
bool cmp_real();
bool cmp_int();
@@ -1485,11 +1537,11 @@ class multi_update :public select_result_interceptor
uint table_count;
Copy_field *copy_field;
enum enum_duplicates handle_duplicates;
- bool do_update, trans_safe, transactional_tables, log_delayed;
+ bool do_update, trans_safe, transactional_tables, log_delayed, ignore;
public:
multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> *fields,
- List<Item> *values, enum_duplicates handle_duplicates);
+ List<Item> *values, enum_duplicates handle_duplicates, bool ignore);
~multi_update();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_data(List<Item> &items);
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index eb851e79d2e..c918480812c 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -19,7 +19,6 @@
#include "mysql_priv.h"
#include <mysys_err.h>
-#include "sql_acl.h"
#include <my_dir.h>
#include <m_ctype.h>
#ifdef __WIN__
@@ -30,11 +29,6 @@ const char *del_exts[]= {".frm", ".BAK", ".TMD",".opt", NullS};
static TYPELIB deletable_extentions=
{array_elements(del_exts)-1,"del_exts", del_exts, NULL};
-const char *known_exts[]=
-{".ISM",".ISD",".ISM",".MRG",".MYI",".MYD",".db", ".ibd", NullS};
-static TYPELIB known_extentions=
-{array_elements(known_exts)-1,"known_exts", known_exts, NULL};
-
static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
const char *db, const char *path,
uint level);
@@ -231,7 +225,7 @@ void del_dbopt(const char *path)
}
-/*
+/*
Create database options file:
DESCRIPTION
@@ -250,10 +244,10 @@ static bool write_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
if (!create->default_table_charset)
create->default_table_charset= thd->variables.collation_server;
-
+
if (put_dbopt(path, create))
return 1;
-
+
if ((file=my_create(path, CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0)
{
ulong length;
@@ -395,7 +389,7 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
- // do not create database if another thread is holding read lock
+ /* do not create database if another thread is holding read lock */
if (wait_if_global_read_lock(thd, 0, 1))
{
error= -1;
@@ -472,7 +466,29 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
mysql_update_log.write(thd, query, query_length);
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, query, query_length, 0);
+ Query_log_event qinfo(thd, query, query_length, 0,
+ /* suppress_use */ TRUE);
+
+ /*
+ Write should use the database being created as the "current
+ database" and not the threads current database, which is the
+ default. If we do not change the "current database" to the
+ database being created, the CREATE statement will not be
+ replicated when using --binlog-do-db to select databases to be
+ replicated.
+
+ An example (--binlog-do-db=sisyfos):
+
+ CREATE DATABASE bob; # Not replicated
+ USE bob; # 'bob' is the current database
+ CREATE DATABASE sisyfos; # Not replicated since 'bob' is
+ # current database.
+ USE sisyfos; # Will give error on slave since
+ # database does not exist.
+ */
+ qinfo.db = db;
+ qinfo.db_len = strlen(db);
+
mysql_bin_log.write(&qinfo);
}
send_ok(thd, result);
@@ -497,7 +513,7 @@ int mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
- // do not alter database if another thread is holding read lock
+ /* do not alter database if another thread is holding read lock */
if ((error=wait_if_global_read_lock(thd,0,1)))
goto exit2;
@@ -507,14 +523,14 @@ int mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
if ((error=write_db_opt(thd, path, create_info)))
goto exit;
- /*
+ /*
Change options if current database is being altered
TODO: Delete this code
*/
if (thd->db && !strcmp(thd->db,db))
{
- thd->db_charset= (create_info && create_info->default_table_charset) ?
- create_info->default_table_charset :
+ thd->db_charset= create_info->default_table_charset ?
+ create_info->default_table_charset :
thd->variables.collation_server;
thd->variables.collation_database= thd->db_charset;
}
@@ -522,7 +538,17 @@ int mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
mysql_update_log.write(thd,thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0,
+ /* suppress_use */ TRUE);
+
+ /*
+ Write should use the database being created as the "current
+ database" and not the threads current database, which is the
+ default.
+ */
+ qinfo.db = db;
+ qinfo.db_len = strlen(db);
+
thd->clear_error();
mysql_bin_log.write(&qinfo);
}
@@ -552,7 +578,6 @@ exit2:
-1 Error generated
*/
-
int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
{
long deleted=0;
@@ -564,7 +589,7 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
- // do not drop database if another thread is holding read lock
+ /* do not drop database if another thread is holding read lock */
if (wait_if_global_read_lock(thd, 0, 1))
{
error= -1;
@@ -595,12 +620,12 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
pthread_mutex_lock(&LOCK_open);
remove_db_from_cache(db);
pthread_mutex_unlock(&LOCK_open);
-
+
error= -1;
if ((deleted= mysql_rm_known_files(thd, dirp, db, path, 0)) >= 0)
{
ha_drop_database(path);
- query_cache_invalidate1(db);
+ query_cache_invalidate1(db);
error = 0;
}
}
@@ -611,7 +636,7 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
my_casedn_str(files_charset_info, tmp_db);
db= tmp_db;
}
- if (!silent && deleted>=0 && thd)
+ if (!silent && deleted>=0)
{
const char *query;
ulong query_length;
@@ -630,11 +655,22 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
mysql_update_log.write(thd, query, query_length);
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, query, query_length, 0);
+ Query_log_event qinfo(thd, query, query_length, 0,
+ /* suppress_use */ TRUE);
+ /*
+ Write should use the database being created as the "current
+ database" and not the threads current database, which is the
+ default.
+ */
+ qinfo.db = db;
+ qinfo.db_len = strlen(db);
+
thd->clear_error();
mysql_bin_log.write(&qinfo);
}
+ thd->server_status|= SERVER_STATUS_DB_DROPPED;
send_ok(thd, (ulong) deleted);
+ thd->server_status&= ~SERVER_STATUS_DB_DROPPED;
}
exit:
@@ -652,7 +688,7 @@ exit:
have 'if (data_buf) free(data_buf)' data_buf is !=0 so this makes a
DOUBLE free().
Side effects of this double free() are, randomly (depends on the machine),
- when the slave is replicating a DROP DATABASE:
+ when the slave is replicating a DROP DATABASE:
- garbage characters in the error message:
"Error 'Can't drop database 'test2'; database doesn't exist' on query
'h4zI©'"
@@ -725,7 +761,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
if ((mysql_rm_known_files(thd, new_dirp, NullS, newpath,1)) < 0)
goto err;
if (!(copy_of_path= thd->memdup(newpath, length+1)) ||
- !(dir= new (&thd->mem_root) String(copy_of_path, length,
+ !(dir= new (thd->mem_root) String(copy_of_path, length,
&my_charset_bin)) ||
raid_dirs.push_back(dir))
goto err;
@@ -737,11 +773,11 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
extension= fn_ext(file->name);
if (find_type(extension, &deletable_extentions,1+2) <= 0)
{
- if (find_type(extension, &known_extentions,1+2) <= 0)
+ if (find_type(extension, ha_known_exts(),1+2) <= 0)
found_other_files++;
continue;
}
- // just for safety we use files_charset_info
+ /* just for safety we use files_charset_info */
if (db && !my_strcasecmp(files_charset_info,
extension, reg_ext))
{
@@ -876,7 +912,7 @@ bool mysql_change_db(THD *thd, const char *name)
if (!dbname || !(db_length= strlen(dbname)))
{
x_free(dbname); /* purecov: inspected */
- send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */
+ send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */
DBUG_RETURN(1); /* purecov: inspected */
}
if (check_db_name(dbname))
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 09893970803..cecdf8d1c62 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -57,8 +57,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order,
DBUG_RETURN(1);
}
- if (thd->lex->duplicates == DUP_IGNORE)
- thd->lex->select_lex.no_error= 1;
+ thd->lex->select_lex.no_error= thd->lex->ignore;
/* Test if the user wants to delete all rows */
if (!using_limit && const_cond && (!conds || conds->val_int()) &&
@@ -118,6 +117,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order,
bzero((char*) &tables,sizeof(tables));
tables.table = table;
+ tables.alias = table_list->alias;
table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
MYF(MY_FAE | MY_ZEROFILL));
@@ -216,7 +216,7 @@ cleanup:
if (error <= 0)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
- log_delayed);
+ log_delayed, FALSE);
if (mysql_bin_log.write(&qinfo) && transactional_table)
error=1;
}
@@ -565,7 +565,7 @@ bool multi_delete::send_eof()
if (error <= 0)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
- log_delayed);
+ log_delayed, FALSE);
if (mysql_bin_log.write(&qinfo) && !normal_tables)
local_error=1; // Log write failed: roll back the SQL statement
}
@@ -674,7 +674,7 @@ end:
{
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
- thd->tmp_table);
+ thd->tmp_table, FALSE);
mysql_bin_log.write(&qinfo);
}
send_ok(thd); // This should return record count
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 3259e0a4f22..3e627243b9f 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -23,7 +23,6 @@
#include "mysql_priv.h"
#include "sql_select.h"
-#include "sql_acl.h"
static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *s,
TABLE_LIST *t);
@@ -133,10 +132,16 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit,
/*
Temp table is created so that it hounours if UNION without ALL is to be
processed
+
+ As 'distinct' parameter we always pass FALSE (0), because underlying
+ query will control distinct condition by itself. Correct test of
+ distinct underlying query will be is_union &&
+ !unit->union_distinct->next_select() (i.e. it is union and last distinct
+ SELECT is last SELECT of UNION).
*/
if (!(table= create_tmp_table(thd, &derived_result->tmp_table_param,
- unit->types, (ORDER*) 0,
- is_union && unit->union_distinct, 1,
+ unit->types, (ORDER*) 0,
+ FALSE, 1,
(first_select->options | thd->options |
TMP_TABLE_ALL_COLUMNS),
HA_POS_ERROR,
diff --git a/sql/sql_do.cc b/sql/sql_do.cc
index 25a8359f3d2..af72632199f 100644
--- a/sql/sql_do.cc
+++ b/sql/sql_do.cc
@@ -18,7 +18,6 @@
/* Execute DO statement */
#include "mysql_priv.h"
-#include "sql_acl.h"
int mysql_do(THD *thd, List<Item> &values)
{
@@ -29,6 +28,7 @@ int mysql_do(THD *thd, List<Item> &values)
DBUG_RETURN(-1);
while ((value = li++))
value->val_int();
+ free_underlaid_joins(thd, &thd->lex->select_lex);
thd->clear_error(); // DO always is OK
send_ok(thd);
DBUG_RETURN(0);
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index 87644300535..d19e9fbdb09 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -104,6 +104,10 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
{
MYSQL_ERROR *err= 0;
DBUG_ENTER("push_warning");
+
+ if (level == MYSQL_ERROR::WARN_LEVEL_NOTE && !(thd->options & OPTION_SQL_NOTES))
+ return(0);
+
if (thd->query_id != thd->warn_id)
mysql_reset_errors(thd);
@@ -113,12 +117,12 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
The following code is here to change the allocation to not
use the thd->mem_root, which is freed after each query
*/
- MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
- my_pthread_setspecific_ptr(THR_MALLOC, &thd->warn_root);
+ MEM_ROOT *old_root= thd->mem_root;
+ thd->mem_root= &thd->warn_root;
err= new MYSQL_ERROR(thd, code, level, msg);
if (err)
thd->warn_list.push_back(err);
- my_pthread_setspecific_ptr(THR_MALLOC, old_root);
+ thd->mem_root= old_root;
}
thd->warn_count[(uint) level]++;
thd->total_warn_count++;
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index f98bb0a9131..f250a00eca1 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -429,7 +429,8 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables,
}
tables->table=table;
- if (cond && (cond->fix_fields(thd, tables, &cond) || cond->check_cols(1)))
+ if (cond && ((!cond->fixed &&
+ cond->fix_fields(thd, tables, &cond)) || cond->check_cols(1)))
goto err0;
table->file->init_table_handle_for_HANDLER(); // Only InnoDB requires it
@@ -516,7 +517,8 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables,
for (key_len=0 ; (item=it_ke++) ; key_part++)
{
// 'item' can be changed by fix_fields() call
- if (item->fix_fields(thd, tables, it_ke.ref()) ||
+ if ((!item->fixed &&
+ item->fix_fields(thd, tables, it_ke.ref())) ||
(item= *it_ke.ref())->check_cols(1))
goto err;
if (item->used_tables() & ~RAND_TABLE_BIT)
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index ffaaafc26e6..0e0d32a922d 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -127,7 +127,7 @@ void memorize_variant_topic(THD *thd, TABLE *topics, int count,
String *name, String *description, String *example)
{
DBUG_ENTER("memorize_variant_topic");
- MEM_ROOT *mem_root= &thd->mem_root;
+ MEM_ROOT *mem_root= thd->mem_root;
if (count==0)
{
get_field(mem_root,find_fields[help_topic_name].field, name);
@@ -138,7 +138,7 @@ void memorize_variant_topic(THD *thd, TABLE *topics, int count,
{
if (count == 1)
names->push_back(name);
- String *new_name= new (&thd->mem_root) String;
+ String *new_name= new (thd->mem_root) String;
get_field(mem_root,find_fields[help_topic_name].field,new_name);
names->push_back(new_name);
}
@@ -351,8 +351,8 @@ int search_categories(THD *thd, TABLE *categories,
{
if (select && !select->cond->val_int())
continue;
- String *lname= new (&thd->mem_root) String;
- get_field(&thd->mem_root,pfname,lname);
+ String *lname= new (thd->mem_root) String;
+ get_field(thd->mem_root,pfname,lname);
if (++count == 1 && res_id)
*res_id= (int16) pcat_id->val_int();
names->push_back(lname);
@@ -385,8 +385,8 @@ void get_all_items_for_category(THD *thd, TABLE *items, Field *pfname,
{
if (!select->cond->val_int())
continue;
- String *name= new (&thd->mem_root) String();
- get_field(&thd->mem_root,pfname,name);
+ String *name= new (thd->mem_root) String();
+ get_field(thd->mem_root,pfname,name);
res->push_back(name);
}
end_read_record(&read_record_info);
@@ -557,7 +557,8 @@ int send_variant_2_list(MEM_ROOT *mem_root, Protocol *protocol,
SQL_SELECT *prepare_simple_select(THD *thd, Item *cond, TABLE_LIST *tables,
TABLE *table, int *error)
{
- cond->fix_fields(thd, tables, &cond); // can never fail
+ if (!cond->fixed)
+ cond->fix_fields(thd, tables, &cond); // can never fail
SQL_SELECT *res= make_select(table,0,0,cond,error);
if (*error || (res && res->check_quick(thd, 0, HA_POS_ERROR)))
{
@@ -638,7 +639,7 @@ int mysqld_help(THD *thd, const char *mask)
String name, description, example;
int res, count_topics, count_categories, error;
uint mlen= strlen(mask);
- MEM_ROOT *mem_root= &thd->mem_root;
+ MEM_ROOT *mem_root= thd->mem_root;
if (open_and_lock_tables(thd, tables))
{
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 2c48d1dca8f..1f190a450de 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -18,12 +18,11 @@
/* Insert of records */
#include "mysql_priv.h"
-#include "sql_acl.h"
static int check_null_fields(THD *thd,TABLE *entry);
#ifndef EMBEDDED_LIBRARY
static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list);
-static int write_delayed(THD *thd,TABLE *table, enum_duplicates dup,
+static int write_delayed(THD *thd,TABLE *table, enum_duplicates dup, bool ignore,
char *query, uint query_length, int log_on);
static void end_delayed_insert(THD *thd);
extern "C" pthread_handler_decl(handle_delayed_insert,arg);
@@ -112,7 +111,8 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
List<List_item> &values_list,
List<Item> &update_fields,
List<Item> &update_values,
- enum_duplicates duplic)
+ enum_duplicates duplic,
+ bool ignore)
{
int error, res;
/*
@@ -198,15 +198,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
thd->used_tables=0;
values= its++;
- if (duplic == DUP_UPDATE && !table->insert_values)
- {
- /* it should be allocated before Item::fix_fields() */
- table->insert_values=
- (byte *)alloc_root(&thd->mem_root, table->rec_buff_length);
- if (!table->insert_values)
- goto abort;
- }
-
if (mysql_prepare_insert(thd, table_list, insert_table_list, table,
fields, values, update_fields,
update_values, duplic))
@@ -232,9 +223,10 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
*/
info.records= info.deleted= info.copied= info.updated= 0;
+ info.ignore= ignore;
info.handle_duplicates=duplic;
- info.update_fields=&update_fields;
- info.update_values=&update_values;
+ info.update_fields= &update_fields;
+ info.update_values= &update_values;
/*
Count warnings for all inserts.
For single line insert, generate an error if try to set a NOT NULL field
@@ -249,7 +241,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
error=0;
id=0;
thd->proc_info="update";
- if (duplic != DUP_ERROR)
+ if (duplic != DUP_ERROR || ignore)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
/*
let's *try* to start bulk inserts. It won't necessary
@@ -299,7 +291,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
#ifndef EMBEDDED_LIBRARY
if (lock_type == TL_WRITE_DELAYED)
{
- error=write_delayed(thd,table,duplic,query, thd->query_length, log_on);
+ error=write_delayed(thd, table, duplic, ignore, query, thd->query_length, log_on);
query=0;
}
else
@@ -368,7 +360,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
if (error <= 0)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
- log_delayed);
+ log_delayed, FALSE);
if (mysql_bin_log.write(&qinfo) && transactional_table)
error=1;
}
@@ -388,7 +380,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
table->next_number_field=0;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
thd->next_insert_id=0; // Reset this if wrongly used
- if (duplic != DUP_ERROR)
+ if (duplic != DUP_ERROR || ignore)
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
/* Reset value of LAST_INSERT_ID if no rows where inserted */
@@ -405,7 +397,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
else
{
char buff[160];
- if (duplic == DUP_IGNORE)
+ if (ignore)
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
(lock_type == TL_WRITE_DELAYED) ? (ulong) 0 :
(ulong) (info.records - info.copied), (ulong) thd->cuted_fields);
@@ -437,6 +429,7 @@ abort:
thd - thread handler
table_list - global table list
insert_table_list - local table list of INSERT SELECT_LEX
+ values - values to insert. NULL for INSERT ... SELECT
RETURN VALUE
0 - OK
@@ -449,19 +442,30 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
enum_duplicates duplic)
{
DBUG_ENTER("mysql_prepare_insert");
- if (check_insert_fields(thd, table, fields, *values, 1) ||
+ if (duplic == DUP_UPDATE && !table->insert_values)
+ {
+ /* it should be allocated before Item::fix_fields() */
+ table->insert_values=
+ (byte *)alloc_root(thd->mem_root, table->rec_buff_length);
+ if (!table->insert_values)
+ DBUG_RETURN(-1);
+ }
+ if ((values && check_insert_fields(thd, table, fields, *values, 1)) ||
setup_tables(insert_table_list) ||
- setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0) ||
+ (values && setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0)) ||
(duplic == DUP_UPDATE &&
- (setup_fields(thd, 0, insert_table_list, update_fields, 0, 0, 0) ||
- setup_fields(thd, 0, insert_table_list, update_values, 0, 0, 0))))
+ (setup_fields(thd, 0, insert_table_list, update_fields, 1, 0, 0) ||
+ setup_fields(thd, 0, insert_table_list, update_values, 1, 0, 0))))
DBUG_RETURN(-1);
- if (find_real_table_in_list(table_list->next,
- table_list->db, table_list->real_name))
+ if (values && find_real_table_in_list(table_list->next, table_list->db,
+ table_list->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
}
+ if (duplic == DUP_UPDATE || duplic == DUP_REPLACE)
+ table->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY);
+
DBUG_RETURN(0);
}
@@ -548,8 +552,10 @@ int write_record(TABLE *table,COPY_INFO *info)
that matches, is updated. If update causes a conflict again,
an error is returned
*/
+ DBUG_ASSERT(table->insert_values != NULL);
store_record(table,insert_values);
restore_record(table,record[1]);
+ DBUG_ASSERT(info->update_fields->elements==info->update_values->elements);
if (fill_record(*info->update_fields, *info->update_values, 0))
goto err;
if ((error=table->file->update_row(table->record[1],table->record[0])))
@@ -587,7 +593,7 @@ int write_record(TABLE *table,COPY_INFO *info)
}
else if ((error=table->file->write_row(table->record[0])))
{
- if (info->handle_duplicates != DUP_IGNORE ||
+ if (!info->ignore ||
(error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE))
goto err;
}
@@ -643,14 +649,14 @@ public:
char *record,*query;
enum_duplicates dup;
time_t start_time;
- bool query_start_used,last_insert_id_used,insert_id_used;
+ bool query_start_used,last_insert_id_used,insert_id_used, ignore;
int log_query;
ulonglong last_insert_id;
timestamp_auto_set_type timestamp_field_type;
uint query_length;
- delayed_row(enum_duplicates dup_arg, int log_query_arg)
- :record(0),query(0),dup(dup_arg),log_query(log_query_arg) {}
+ delayed_row(enum_duplicates dup_arg, bool ignore_arg, int log_query_arg)
+ :record(0),query(0),dup(dup_arg),ignore(ignore_arg),log_query(log_query_arg) {}
~delayed_row()
{
x_free(record);
@@ -682,7 +688,8 @@ public:
thd.current_tablenr=0;
thd.version=refresh_version;
thd.command=COM_DELAYED_INSERT;
- thd.lex->current_select= 0; /* for my_message_sql */
+ thd.lex->current_select= 0; // for my_message_sql
+ thd.lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock()
bzero((char*) &thd.net, sizeof(thd.net)); // Safety
bzero((char*) &table_list, sizeof(table_list)); // Safety
@@ -924,7 +931,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd)
found_next_number_field=table->found_next_number_field;
for (org_field=table->field ; *org_field ; org_field++,field++)
{
- if (!(*field= (*org_field)->new_field(&client_thd->mem_root,copy)))
+ if (!(*field= (*org_field)->new_field(client_thd->mem_root,copy)))
return 0;
(*field)->orig_table= copy; // Remove connection
(*field)->move_field(adjust_ptrs); // Point at copy->record[0]
@@ -962,7 +969,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd)
/* Put a question in queue */
-static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic,
+static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic, bool ignore,
char *query, uint query_length, int log_on)
{
delayed_row *row=0;
@@ -975,7 +982,7 @@ static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic,
pthread_cond_wait(&di->cond_client,&di->mutex);
thd->proc_info="storing row into queue";
- if (thd->killed || !(row= new delayed_row(duplic, log_on)))
+ if (thd->killed || !(row= new delayed_row(duplic, ignore, log_on)))
goto err;
if (!query)
@@ -1336,9 +1343,10 @@ bool delayed_insert::handle_inserts(void)
thd.insert_id_used=row->insert_id_used;
table->timestamp_field_type= row->timestamp_field_type;
+ info.ignore= row->ignore;
info.handle_duplicates= row->dup;
- if (info.handle_duplicates == DUP_IGNORE ||
- info.handle_duplicates == DUP_REPLACE)
+ if (info.ignore ||
+ info.handle_duplicates != DUP_ERROR)
{
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
using_ignore=1;
@@ -1361,7 +1369,7 @@ bool delayed_insert::handle_inserts(void)
mysql_update_log.write(&thd,row->query, row->query_length);
if (row->log_query & DELAYED_LOG_BIN && using_bin_log)
{
- Query_log_event qinfo(&thd, row->query, row->query_length,0);
+ Query_log_event qinfo(&thd, row->query, row->query_length,0, FALSE);
mysql_bin_log.write(&qinfo);
}
}
@@ -1454,10 +1462,9 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
restore_record(table,default_values); // Get empty record
table->next_number_field=table->found_next_number_field;
- thd->count_cuted_fields= CHECK_FIELD_WARN; // calc cuted fields
thd->cuted_fields=0;
- if (info.handle_duplicates == DUP_IGNORE ||
- info.handle_duplicates == DUP_REPLACE)
+ if (info.ignore ||
+ info.handle_duplicates != DUP_ERROR)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
table->file->start_bulk_insert((ha_rows) 0);
DBUG_RETURN(0);
@@ -1484,27 +1491,34 @@ select_insert::~select_insert()
bool select_insert::send_data(List<Item> &values)
{
DBUG_ENTER("select_insert::send_data");
+ bool error=0;
if (unit->offset_limit_cnt)
{ // using limit offset,count
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
- if (fields->elements)
- fill_record(*fields, values, 1);
- else
- fill_record(table->field, values, 1);
- if (thd->net.report_error || write_record(table,&info))
- DBUG_RETURN(1);
- if (table->next_number_field) // Clear for next record
+ thd->count_cuted_fields= CHECK_FIELD_WARN; // calc cuted fields
+ store_values(values);
+ error=thd->net.report_error || write_record(table,&info);
+ thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+ if (!error && table->next_number_field) // Clear for next record
{
table->next_number_field->reset();
if (! last_insert_id && thd->insert_id_used)
last_insert_id=thd->insert_id();
}
- DBUG_RETURN(0);
+ DBUG_RETURN(error);
}
+void select_insert::store_values(List<Item> &values)
+{
+ if (fields->elements)
+ fill_record(*fields, values, 1);
+ else
+ fill_record(table->field, values, 1);
+}
+
void select_insert::send_error(uint errcode,const char *err)
{
DBUG_ENTER("select_insert::send_error");
@@ -1536,7 +1550,7 @@ void select_insert::send_error(uint errcode,const char *err)
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
- table->file->has_transactions());
+ table->file->has_transactions(), FALSE);
mysql_bin_log.write(&qinfo);
}
if (!table->tmp_table)
@@ -1578,7 +1592,7 @@ bool select_insert::send_eof()
if (!error)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
- table->file->has_transactions());
+ table->file->has_transactions(), FALSE);
mysql_bin_log.write(&qinfo);
}
if ((error2=ha_autocommit_or_rollback(thd,error)) && ! error)
@@ -1591,7 +1605,7 @@ bool select_insert::send_eof()
DBUG_RETURN(1);
}
char buff[160];
- if (info.handle_duplicates == DUP_IGNORE)
+ if (info.ignore)
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
(ulong) (info.records - info.copied), (ulong) thd->cuted_fields);
else
@@ -1634,33 +1648,30 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
table->next_number_field=table->found_next_number_field;
restore_record(table,default_values); // Get empty record
- thd->count_cuted_fields= CHECK_FIELD_WARN; // count warnings
thd->cuted_fields=0;
- if (info.handle_duplicates == DUP_IGNORE ||
- info.handle_duplicates == DUP_REPLACE)
+ if (info.ignore ||
+ info.handle_duplicates != DUP_ERROR)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
table->file->start_bulk_insert((ha_rows) 0);
DBUG_RETURN(0);
}
-bool select_create::send_data(List<Item> &values)
+void select_create::store_values(List<Item> &values)
{
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- return 0;
- }
fill_record(field, values, 1);
- if (thd->net.report_error ||write_record(table,&info))
- return 1;
- if (table->next_number_field) // Clear for next record
- {
- table->next_number_field->reset();
- if (! last_insert_id && thd->insert_id_used)
- last_insert_id=thd->insert_id();
- }
- return 0;
+}
+
+
+void select_create::send_error(uint errcode,const char *err)
+{
+ /*
+ Disable binlog, because we "roll back" partial inserts in ::abort
+ by removing the table, even for non-transactional tables.
+ */
+ tmp_disable_binlog(thd);
+ select_insert::send_error(errcode, err);
+ reenable_binlog(thd);
}
@@ -1681,9 +1692,10 @@ bool select_create::send_eof()
*/
if (!table->tmp_table)
{
+ ulong version= table->version;
hash_delete(&open_cache,(byte*) table);
/* Tell threads waiting for refresh that something has happened */
- if (table->version != refresh_version)
+ if (version != refresh_version)
VOID(pthread_cond_broadcast(&COND_refresh));
}
lock=0;
@@ -1707,11 +1719,12 @@ void select_create::abort()
enum db_type table_type=table->db_type;
if (!table->tmp_table)
{
+ ulong version= table->version;
hash_delete(&open_cache,(byte*) table);
if (!create_info->table_existed)
quick_rm_table(table_type, db, name);
/* Tell threads waiting for refresh that something has happened */
- if (table->version != refresh_version)
+ if (version != refresh_version)
VOID(pthread_cond_broadcast(&COND_refresh));
}
else if (!create_info->table_existed)
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 679ffb2140e..d6dcd9ce9ae 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -123,6 +123,7 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->unit.thd= thd;
lex->select_lex.init_query();
lex->value_list.empty();
+ lex->update_list.empty();
lex->param_list.empty();
lex->unit.next= lex->unit.master=
lex->unit.link_next= lex->unit.return_to= 0;
@@ -135,7 +136,7 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list);
lex->select_lex.options= 0;
lex->describe= 0;
- lex->derived_tables= FALSE;
+ lex->subqueries= lex->derived_tables= FALSE;
lex->lock_option= TL_READ;
lex->found_colon= 0;
lex->safe_to_cache_query= 1;
@@ -157,11 +158,16 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE);
lex->sql_command=SQLCOM_END;
lex->duplicates= DUP_ERROR;
+ lex->ignore= 0;
+ lex->proc_list.first= 0;
}
void lex_end(LEX *lex)
{
- lex->select_lex.expr_list.delete_elements(); // If error when parsing sql-varargs
+ for (SELECT_LEX *sl= lex->all_selects_list;
+ sl;
+ sl= sl->next_select_in_list())
+ sl->expr_list.delete_elements(); // If error when parsing sql-varargs
x_free(lex->yacc_yyss);
x_free(lex->yacc_yyvs);
}
@@ -289,7 +295,18 @@ static char *get_text(LEX *lex)
found_escape=1;
if (lex->ptr == lex->end_of_query)
return 0;
- yySkip();
+#ifdef USE_MB
+ int l;
+ if (use_mb(cs) &&
+ (l = my_ismbchar(cs,
+ (const char *)lex->ptr,
+ (const char *)lex->end_of_query))) {
+ lex->ptr += l;
+ continue;
+ }
+ else
+#endif
+ yySkip();
}
else if (c == sep)
{
@@ -317,6 +334,10 @@ static char *get_text(LEX *lex)
else
{
uchar *to;
+
+ /* Re-use found_escape for tracking state of escapes */
+ found_escape= 0;
+
for (to=start ; str != end ; str++)
{
#ifdef USE_MB
@@ -330,7 +351,7 @@ static char *get_text(LEX *lex)
continue;
}
#endif
- if (*str == '\\' && str+1 != end)
+ if (!found_escape && *str == '\\' && str+1 != end)
{
switch(*++str) {
case 'n':
@@ -356,15 +377,20 @@ static char *get_text(LEX *lex)
*to++= '\\'; // remember prefix for wildcard
/* Fall through */
default:
- *to++ = *str;
+ found_escape= 1;
+ str--;
break;
}
}
- else if (*str == sep)
- *to++= *str++; // Two ' or "
+ else if (!found_escape && *str == sep)
+ {
+ found_escape= 1;
+ }
else
+ {
*to++ = *str;
-
+ found_escape= 0;
+ }
}
*to=0;
lex->yytoklen=(uint) (to-start);
@@ -909,6 +935,7 @@ int yylex(void *arg, void *yythd)
if ((thd->client_capabilities & CLIENT_MULTI_STATEMENTS) &&
(thd->command != COM_PREPARE))
{
+ lex->safe_to_cache_query=0;
lex->found_colon=(char*)lex->ptr;
thd->server_status |= SERVER_MORE_RESULTS_EXISTS;
lex->next_state=MY_LEX_END;
@@ -1681,9 +1708,6 @@ void st_select_lex::print_order(String *str, ORDER *order)
void st_select_lex::print_limit(THD *thd, String *str)
{
- if (!thd)
- thd= current_thd;
-
if (explicit_limit)
{
str->append(" limit ", 7);
@@ -1717,8 +1741,8 @@ st_lex::st_lex()
global_first Save first global table here
local_first Save first local table here
- NORES
- global_first & local_first are used to save result for link_first_table_back
+ NOTES
+ This function assumes that outer select list is non-empty.
RETURN
global list without first table
@@ -1728,25 +1752,25 @@ TABLE_LIST *st_lex::unlink_first_table(TABLE_LIST *tables,
TABLE_LIST **global_first,
TABLE_LIST **local_first)
{
- *global_first= tables;
- *local_first= (TABLE_LIST*)select_lex.table_list.first;
+ DBUG_ASSERT(select_lex.table_list.first != 0);
/*
- Exclude from global table list
+ Save pointers to first elements of global table list and list
+ of tables used in outer select. It does not harm if these lists
+ are the same.
*/
+ *global_first= tables;
+ *local_first= (TABLE_LIST*)select_lex.table_list.first;
+
+ /* Exclude first elements from these lists */
+ select_lex.table_list.first= (byte*) (*local_first)->next;
tables= tables->next;
- /*
- and from local list if it is not the same
- */
- select_lex.table_list.first= ((&select_lex != all_selects_list) ?
- (byte*) (*local_first)->next :
- (byte*) tables);
(*global_first)->next= 0;
return tables;
}
/*
- Link table back that was unlinked with unlink_first_table()
+ Link table which was unlinked with unlink_first_table() back.
SYNOPSIS
link_first_table_back()
@@ -1762,16 +1786,7 @@ TABLE_LIST *st_lex::link_first_table_back(TABLE_LIST *tables,
TABLE_LIST *local_first)
{
global_first->next= tables;
- if (&select_lex != all_selects_list)
- {
- /*
- we do not touch local table 'next' field => we need just
- put the table in the list
- */
- select_lex.table_list.first= (byte*) local_first;
- }
- else
- select_lex.table_list.first= (byte*) global_first;
+ select_lex.table_list.first= (byte*) local_first;
return global_first;
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 268198f74a2..f48ff42bbf8 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -125,27 +125,46 @@ enum tablespace_op_type
/*
The state of the lex parsing for selects
+ master and slaves are pointers to select_lex.
+ master is pointer to upper level node.
+ slave is pointer to lower level node
+ select_lex is a SELECT without union
+ unit is container of either
+ - One SELECT
+ - UNION of selects
+ select_lex and unit are both inherited form select_lex_node
+ neighbors are two select_lex or units on the same level
+
All select describing structures linked with following pointers:
- - list of neighbors (next/prev) (prev of first element point to slave
+ - list of neighbors (next/prev) (prev of first element point to slave
pointer of upper structure)
- - one level units for unit (union) structure
- - member of one union(unit) for ordinary select_lex
- - pointer to master
- - outer select_lex for unit (union)
- - unit structure for ordinary select_lex
- - pointer to slave
- - first list element of select_lex belonged to this unit for unit
- - first unit in list of units that belong to this select_lex (as
- subselects or derived tables) for ordinary select_lex
- - list of all select_lex (for group operation like correcting list of opened
- tables)
- - if unit contain several selects (union) then it have special
- select_lex called fake_select_lex. It used for storing global parameters
- and executing union. subqueries of global ORDER BY clause will be
- attached to this fake_select_lex, which will allow them correctly
- resolve fields of 'upper' union and other more outer selects.
-
- for example for following query:
+ - For select this is a list of UNION's (or one element list)
+ - For units this is a list of sub queries for the upper level select
+
+ - pointer to master (master), which is
+ If this is a unit
+ - pointer to outer select_lex
+ If this is a select_lex
+ - pointer to outer unit structure for select
+
+ - pointer to slave (slave), which is either:
+ If this is a unit:
+ - first SELECT that belong to this unit
+ If this is a select_lex
+ - first unit that belong to this SELECT (subquries or derived tables)
+
+ - list of all select_lex (link_next/link_prev)
+ This is to be used for things like derived tables creation, where we
+ go through this list and create the derived tables.
+
+ If unit contain several selects (UNION now, INTERSECT etc later)
+ then it have special select_lex called fake_select_lex. It used for
+ storing global parameters (like ORDER BY, LIMIT) and executing union.
+ Subqueries used in global ORDER BY clause will be attached to this
+ fake_select_lex, which will allow them correctly resolve fields of
+ 'upper' UNION and outer selects.
+
+ For example for following query:
select *
from table1
@@ -163,6 +182,11 @@ enum tablespace_op_type
we will have following structure:
+ select1: (select * from table1 ...)
+ select2: (select * from table2 ...)
+ select3: (select * from table3)
+ select1.1.1: (select * from table1_1_1)
+ ...
main unit
fake0
@@ -185,7 +209,12 @@ enum tablespace_op_type
relation in main unit will be following:
-
+ (bigger picture for:
+ main unit
+ fake0
+ select1 select2 select3
+ in the above picture)
+
main unit
|^^^^|fake_select_lex
|||||+--------------------------------------------+
@@ -241,7 +270,7 @@ public:
static void *operator new(size_t size, MEM_ROOT *mem_root)
{ return (void*) alloc_root(mem_root, (uint) size); }
static void operator delete(void *ptr,size_t size) {}
- static void operator delete(void *ptr,size_t size, MEM_ROOT *mem_root) {}
+ static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
st_select_lex_node(): linkage(UNSPECIFIED_TYPE) {}
virtual ~st_select_lex_node() {}
inline st_select_lex_node* get_master() { return master; }
@@ -371,6 +400,7 @@ public:
ulong init_prepare_fake_select_lex(THD *thd);
int change_result(select_subselect *result, select_subselect *old_result);
+ inline bool is_prepared() { return prepared; }
friend void lex_start(THD *thd, uchar *buf, uint length);
friend int subselect_union_engine::exec();
@@ -381,7 +411,7 @@ private:
typedef class st_select_lex_unit SELECT_LEX_UNIT;
/*
- SELECT_LEX - store information of parsed SELECT_LEX statment
+ SELECT_LEX - store information of parsed SELECT statment
*/
class st_select_lex: public st_select_lex_node
{
@@ -587,12 +617,11 @@ typedef struct st_lex
List<LEX_COLUMN> columns;
List<Key> key_list;
List<create_field> create_list;
- List<Item> *insert_list,field_list,value_list;
+ List<Item> *insert_list,field_list,value_list,update_list;
List<List_item> many_values;
List<set_var_base> var_list;
List<Item_param> param_list;
SQL_LIST proc_list, auxilliary_table_list, save_list;
- TYPELIB *interval;
create_field *last_field;
char *savepoint_name; // Transaction savepoint id
udf_func udf;
@@ -613,12 +642,13 @@ typedef struct st_lex
uint uint_geom_type;
uint grant, grant_tot_col, which_columns;
uint fk_delete_opt, fk_update_opt, fk_match_option;
- uint slave_thd_opt;
+ uint slave_thd_opt, start_transaction_opt;
uint8 describe;
bool drop_if_exists, drop_temporary, local_file, one_shot_set;
bool in_comment, ignore_space, verbose, no_write_to_binlog;
bool derived_tables;
bool safe_to_cache_query;
+ bool subqueries, ignore;
ALTER_INFO alter_info;
/* Prepared statements SQL syntax:*/
LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 38c5af2a4f7..be3e29b0c62 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -41,8 +41,8 @@ public:
static void *operator new(size_t size, MEM_ROOT *mem_root)
{ return (void*) alloc_root(mem_root, (uint) size); }
static void operator delete(void *ptr, size_t size) { TRASH(ptr, size); }
- static void operator delete(void *ptr, size_t size, MEM_ROOT *mem_root)
- { TRASH(ptr, size); }
+ static void operator delete(void *ptr, MEM_ROOT *mem_root)
+ { /* never called */ }
static void operator delete[](void *ptr, size_t size) { TRASH(ptr, size); }
#ifdef HAVE_purify
bool dummy;
@@ -134,6 +134,15 @@ public:
if (!--elements)
last= &first;
}
+ inline void concat(base_list *list)
+ {
+ if (!list->is_empty())
+ {
+ *last= list->first;
+ last= list->last;
+ elements+= list->elements;
+ }
+ }
inline void *pop(void)
{
if (first == &end_of_list) return 0;
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 17ab472c87b..c4f5b1427af 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -80,6 +80,7 @@ static int read_sep_field(THD *thd,COPY_INFO &info,TABLE *table,
int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
List<Item> &fields, enum enum_duplicates handle_duplicates,
+ bool ignore,
bool read_file_from_client,thr_lock_type lock_type)
{
char name[FN_REFLEN];
@@ -165,7 +166,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
/* We can't give an error in the middle when using LOCAL files */
if (read_file_from_client && handle_duplicates == DUP_ERROR)
- handle_duplicates=DUP_IGNORE;
+ ignore= 1;
#ifndef EMBEDDED_LIBRARY
if (read_file_from_client)
@@ -216,6 +217,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
COPY_INFO info;
bzero((char*) &info,sizeof(info));
+ info.ignore= ignore;
info.handle_duplicates=handle_duplicates;
info.escape_char=escaped->length() ? (*escaped)[0] : INT_MAX;
@@ -237,6 +239,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
lf_info.db = db;
lf_info.table_name = table_list->real_name;
lf_info.fields = &fields;
+ lf_info.ignore= ignore;
lf_info.handle_dup = handle_duplicates;
lf_info.wrote_create_file = 0;
lf_info.last_pos_in_file = HA_POS_ERROR;
@@ -267,7 +270,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
table->next_number_field=table->found_next_number_field;
- if (handle_duplicates == DUP_IGNORE ||
+ if (ignore ||
handle_duplicates == DUP_REPLACE)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
ha_enable_transaction(thd, FALSE);
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index e061005ae61..81cc3f00b50 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -15,7 +15,6 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysql_priv.h"
-#include "sql_acl.h"
#include "sql_repl.h"
#include "repl_failsafe.h"
#include <m_ctype.h>
@@ -48,15 +47,19 @@
extern "C" int gethostname(char *name, int namelen);
#endif
+static void time_out_user_resource_limits(THD *thd, USER_CONN *uc);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static int check_for_max_user_connections(THD *thd, USER_CONN *uc);
#endif
static void decrease_user_connections(USER_CONN *uc);
static bool check_db_used(THD *thd,TABLE_LIST *tables);
+static bool check_multi_update_lock(THD *thd, TABLE_LIST *tables,
+ List<Item> *fields, SELECT_LEX *select_lex);
static void remove_escape(char *name);
static void refresh_status(void);
static bool append_file_to_dir(THD *thd, const char **filename_ptr,
const char *table_name);
+static void log_slow_query(THD *thd);
const char *any_db="*any*"; // Special symbol for check_access
@@ -65,7 +68,7 @@ const char *command_name[]={
"Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist",
"Connect","Kill","Debug","Ping","Time","Delayed insert","Change user",
"Binlog Dump","Table Dump", "Connect Out", "Register Slave",
- "Prepare", "Prepare Execute", "Long Data", "Close stmt",
+ "Prepare", "Execute", "Long Data", "Close stmt",
"Reset stmt", "Set option",
"Error" // Last command number
};
@@ -441,6 +444,7 @@ static int check_for_max_user_connections(THD *thd, USER_CONN *uc)
error=1;
goto end;
}
+ time_out_user_resource_limits(thd, uc);
if (uc->user_resources.connections &&
uc->user_resources.connections <= uc->conn_per_hour)
{
@@ -542,36 +546,56 @@ bool is_update_query(enum enum_sql_command command)
}
/*
- Check if maximum queries per hour limit has been reached
- returns 0 if OK.
+ Reset per-hour user resource limits when it has been more than
+ an hour since they were last checked
- In theory we would need a mutex in the USER_CONN structure for this to
- be 100 % safe, but as the worst scenario is that we would miss counting
- a couple of queries, this isn't critical.
-*/
+ SYNOPSIS:
+ time_out_user_resource_limits()
+ thd Thread handler
+ uc User connection details
+ NOTE:
+ This assumes that the LOCK_user_conn mutex has been acquired, so it is
+ safe to test and modify members of the USER_CONN structure.
+*/
-static bool check_mqh(THD *thd, uint check_command)
+static void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
{
-#ifdef NO_EMBEDDED_ACCESS_CHECKS
- return(0);
-#else
- bool error=0;
+ bool error= 0;
time_t check_time = thd->start_time ? thd->start_time : time(NULL);
- USER_CONN *uc=thd->user_connect;
- DBUG_ENTER("check_mqh");
- DBUG_ASSERT(uc != 0);
+ DBUG_ENTER("time_out_user_resource_limits");
/* If more than a hour since last check, reset resource checking */
if (check_time - uc->intime >= 3600)
{
- (void) pthread_mutex_lock(&LOCK_user_conn);
uc->questions=1;
uc->updates=0;
uc->conn_per_hour=0;
uc->intime=check_time;
- (void) pthread_mutex_unlock(&LOCK_user_conn);
}
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Check if maximum queries per hour limit has been reached
+ returns 0 if OK.
+*/
+
+static bool check_mqh(THD *thd, uint check_command)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ bool error= 0;
+ time_t check_time = thd->start_time ? thd->start_time : time(NULL);
+ USER_CONN *uc=thd->user_connect;
+ DBUG_ENTER("check_mqh");
+ DBUG_ASSERT(uc != 0);
+
+ (void) pthread_mutex_lock(&LOCK_user_conn);
+
+ time_out_user_resource_limits(thd, uc);
+
/* Check that we have not done too many questions / hour */
if (uc->user_resources.questions &&
uc->questions++ >= uc->user_resources.questions)
@@ -594,7 +618,10 @@ static bool check_mqh(THD *thd, uint check_command)
}
}
end:
+ (void) pthread_mutex_unlock(&LOCK_user_conn);
DBUG_RETURN(error);
+#else
+ return (0);
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
}
@@ -655,6 +682,8 @@ static int check_connection(THD *thd)
{
uint connect_errors= 0;
NET *net= &thd->net;
+ ulong pkt_len= 0;
+ char *end;
DBUG_PRINT("info",
("New connection received on %s", vio_description(net->vio)));
@@ -668,6 +697,7 @@ static int check_connection(THD *thd)
if (!(thd->ip= my_strdup(ip,MYF(0))))
return (ER_OUT_OF_RESOURCES);
thd->host_or_ip= thd->ip;
+ vio_in_addr(net->vio,&thd->remote.sin_addr);
#if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread)
/* Fast local hostname resolve for Win32 */
if (!strcmp(thd->ip,"127.0.0.1"))
@@ -703,11 +733,10 @@ static int check_connection(THD *thd)
DBUG_PRINT("info",("Host: %s",thd->host));
thd->host_or_ip= thd->host;
thd->ip= 0;
- bzero((char*) &thd->remote, sizeof(struct sockaddr));
+ /* Reset sin_addr */
+ bzero((char*) &thd->remote, sizeof(thd->remote));
}
vio_keepalive(net->vio, TRUE);
- ulong pkt_len= 0;
- char *end;
{
/* buff[] needs to big enough to hold the server_version variable */
char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
@@ -867,14 +896,16 @@ static int check_connection(THD *thd)
char *user= end;
char *passwd= strend(user)+1;
char *db= passwd;
- char db_buff[NAME_LEN+1]; // buffer to store db in utf8
+ char db_buff[NAME_LEN+1]; // buffer to store db in utf8
char user_buff[USERNAME_LENGTH+1]; // buffer to store user in utf8
- /*
+ uint dummy_errors;
+
+ /*
Old clients send null-terminated string as password; new clients send
the size (1 byte) + string (not null-terminated). Hence in case of empty
password both send '\0'.
*/
- uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
+ uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
*passwd++ : strlen(passwd);
db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
db + passwd_len + 1 : 0;
@@ -885,17 +916,14 @@ static int check_connection(THD *thd)
db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
system_charset_info,
db, strlen(db),
- thd->charset())]= 0;
+ thd->charset(), &dummy_errors)]= 0;
db= db_buff;
}
- if (user)
- {
- user_buff[copy_and_convert(user_buff, sizeof(user_buff)-1,
- system_charset_info, user, strlen(user),
- thd->charset())]= '\0';
- user= user_buff;
- }
+ user_buff[copy_and_convert(user_buff, sizeof(user_buff)-1,
+ system_charset_info, user, strlen(user),
+ thd->charset(), &dummy_errors)]= '\0';
+ user= user_buff;
if (thd->user)
x_free(thd->user);
@@ -1024,7 +1052,7 @@ pthread_handler_decl(handle_one_connection,arg)
}
if (thd->user_connect)
decrease_user_connections(thd->user_connect);
- free_root(&thd->mem_root,MYF(0));
+ free_root(thd->mem_root,MYF(0));
if (net->error && net->vio != 0 && net->report_error)
{
if (!thd->killed && thd->variables.log_warnings > 1)
@@ -1100,13 +1128,25 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg)
thd->init_for_queries();
while (fgets(buff, thd->net.max_packet, file))
{
- uint length=(uint) strlen(buff);
- if (buff[length-1]!='\n' && !feof(file))
+ ulong length= (ulong) strlen(buff);
+ while (buff[length-1] != '\n' && !feof(file))
{
- send_error(thd,ER_NET_PACKET_TOO_LARGE, NullS);
- thd->is_fatal_error= 1;
- break;
+ /*
+ We got only a part of the current string. Will try to increase
+ net buffer then read the rest of the current string.
+ */
+ if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
+ {
+ send_error(thd, thd->net.last_errno, NullS);
+ thd->is_fatal_error= 1;
+ break;
+ }
+ buff= (char*) thd->net.buff;
+ fgets(buff + length, thd->net.max_packet - length, file);
+ length+= (ulong) strlen(buff + length);
}
+ if (thd->is_fatal_error)
+ break;
while (length && (my_isspace(thd->charset(), buff[length-1]) ||
buff[length-1] == ';'))
length--;
@@ -1119,14 +1159,14 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg)
{
thd->net.error = 0;
close_thread_tables(thd); // Free tables
- free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
+ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
break;
}
mysql_parse(thd,thd->query,length);
close_thread_tables(thd); // Free tables
if (thd->is_fatal_error)
break;
- free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
+ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
}
@@ -1380,9 +1420,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
#endif
/* Convert database name to utf8 */
+ uint dummy_errors;
db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
system_charset_info, db, strlen(db),
- thd->charset())]= 0;
+ thd->charset(), &dummy_errors)]= 0;
db= db_buff;
/* Save user and privileges */
@@ -1458,6 +1499,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
if (alloc_query(thd, packet, packet_length))
break; // fatal error is set
+ char *packet_end= thd->query + thd->query_length;
mysql_log.write(thd,command,"%s",thd->query);
DBUG_PRINT("query",("%-.4096s",thd->query));
mysql_parse(thd,thd->query, thd->query_length);
@@ -1473,7 +1515,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (thd->lock || thd->open_tables || thd->derived_tables)
close_thread_tables(thd);
#endif
- ulong length= thd->query_length-(ulong)(packet-thd->query);
+ ulong length= (ulong)(packet_end-packet);
+
+ log_slow_query(thd);
/* Remove garbage at start of query */
while (my_isspace(thd->charset(), *packet) && length > 0)
@@ -1485,6 +1529,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->query_length= length;
thd->query= packet;
thd->query_id= query_id++;
+ thd->set_time(); /* Reset the query start time. */
/* TODO: set thd->lex->sql_command to SQLCOM_END here */
VOID(pthread_mutex_unlock(&LOCK_thread_count));
#ifndef EMBEDDED_LIBRARY
@@ -1533,7 +1578,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
packet, (uint) (pend-packet), thd->charset());
table_list.alias= table_list.real_name= conv_name.str;
packet= pend+1;
- // command not cachable => no gap for data base name
+ thd->query_length= strlen(packet); // for simplicity: don't optimize
if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1)))
break;
mysql_log.write(thd,command,"%s %s",table_list.real_name,fields);
@@ -1607,15 +1652,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#ifndef EMBEDDED_LIBRARY
case COM_BINLOG_DUMP:
{
+ ulong pos;
+ ushort flags;
+ uint32 slave_server_id;
+
statistic_increment(com_other,&LOCK_status);
thd->slow_command = TRUE;
if (check_global_access(thd, REPL_SLAVE_ACL))
break;
- mysql_log.write(thd,command, 0);
- ulong pos;
- ushort flags;
- uint32 slave_server_id;
/* TODO: The following has to be changed to an 8 byte integer */
pos = uint4korr(packet);
flags = uint2korr(packet + 4);
@@ -1623,6 +1668,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
kill_zombie_dump_threads(slave_server_id);
thd->server_id = slave_server_id;
+
+ mysql_log.write(thd, command, "Log: '%s' Pos: %ld", packet+10,
+ (long) pos);
mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
unregister_slave(thd,1,1);
// fake COM_QUIT -- if we get here, the thread needs to terminate
@@ -1678,7 +1726,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
close_connection(thd, 0, 1);
close_thread_tables(thd); // Free before kill
- free_root(&thd->mem_root,MYF(0));
+ free_root(thd->mem_root,MYF(0));
free_root(&thd->transaction.mem_root,MYF(0));
kill_mysql();
error=TRUE;
@@ -1778,6 +1826,24 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (thd->is_fatal_error)
send_error(thd,0); // End of memory ?
+ log_slow_query(thd);
+
+ thd->proc_info="cleaning up";
+ VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
+ thd->proc_info=0;
+ thd->command=COM_SLEEP;
+ thd->query=0;
+ thd->query_length=0;
+ thread_running--;
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
+ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
+ DBUG_RETURN(error);
+}
+
+
+static void log_slow_query(THD *thd)
+{
time_t start_of_query=thd->start_time;
thd->end_time(); // Set start time
@@ -1796,17 +1862,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query);
}
}
- thd->proc_info="cleaning up";
- VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
- thd->proc_info=0;
- thd->command=COM_SLEEP;
- thd->query=0;
- thd->query_length=0;
- thread_running--;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
- thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
- free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
- DBUG_RETURN(error);
}
@@ -1869,6 +1924,8 @@ mysql_execute_command(THD *thd)
{
int res= 0;
LEX *lex= thd->lex;
+ bool slave_fake_lock= 0;
+ MYSQL_LOCK *fake_prev_lock= 0;
SELECT_LEX *select_lex= &lex->select_lex;
TABLE_LIST *tables= (TABLE_LIST*) select_lex->table_list.first;
SELECT_LEX_UNIT *unit= &lex->unit;
@@ -1886,11 +1943,33 @@ mysql_execute_command(THD *thd)
#ifdef HAVE_REPLICATION
if (thd->slave_thread)
{
+ if (lex->sql_command == SQLCOM_UPDATE_MULTI)
+ {
+ DBUG_PRINT("info",("need faked locked tables"));
+
+ if (check_multi_update_lock(thd, tables, &select_lex->item_list,
+ select_lex))
+ goto error;
+
+ /* Fix for replication, the tables are opened and locked,
+ now we pretend that we have performed a LOCK TABLES action */
+
+ fake_prev_lock= thd->locked_tables;
+ if (thd->lock)
+ thd->locked_tables= thd->lock;
+ thd->lock= 0;
+ slave_fake_lock= 1;
+ }
/*
Skip if we are in the slave thread, some table rules have been
- given and the table list says the query should not be replicated
+ given and the table list says the query should not be replicated.
+ Exception is DROP TEMPORARY TABLE IF EXISTS: we always execute it
+ (otherwise we have stale files on slave caused by exclusion of one tmp
+ table).
*/
- if (all_tables_not_ok(thd,tables))
+ if (!(lex->sql_command == SQLCOM_DROP_TABLE &&
+ lex->drop_temporary && lex->drop_if_exists) &&
+ all_tables_not_ok(thd,tables))
{
/* we warn the slave SQL thread */
my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
@@ -2059,15 +2138,19 @@ mysql_execute_command(THD *thd)
query_len= need_conversion? (pstr->length() * to_cs->mbmaxlen) :
pstr->length();
- if (!(query_str= alloc_root(&thd->mem_root, query_len+1)))
+ if (!(query_str= alloc_root(thd->mem_root, query_len+1)))
{
res= -1;
break; // EOM (error should be reported by allocator)
}
if (need_conversion)
- query_len= copy_and_convert(query_str, query_len, to_cs, pstr->ptr(),
- pstr->length(), pstr->charset());
+ {
+ uint dummy_errors;
+ query_len= copy_and_convert(query_str, query_len, to_cs,
+ pstr->ptr(), pstr->length(),
+ pstr->charset(), &dummy_errors);
+ }
else
memcpy(query_str, pstr->ptr(), pstr->length());
query_str[query_len]= 0;
@@ -2372,7 +2455,8 @@ mysql_execute_command(THD *thd)
&lex->create_info,
lex->create_list,
lex->key_list,
- select_lex->item_list,lex->duplicates)))
+ select_lex->item_list, lex->duplicates,
+ lex->ignore)))
{
/*
CREATE from SELECT give its SELECT_LEX for SELECT,
@@ -2408,7 +2492,6 @@ mysql_execute_command(THD *thd)
create_table_local);
break;
-create_error:
res= 1; //error reported
unsent_create_error:
// put tables back for PS rexecuting
@@ -2471,7 +2554,7 @@ unsent_create_error:
if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN))
{
net_printf(thd, ER_WRONG_TABLE_NAME, lex->name);
- res=0;
+ res= 1;
break;
}
if (!select_lex->db)
@@ -2512,7 +2595,7 @@ unsent_create_error:
lex->key_list,
select_lex->order_list.elements,
(ORDER *) select_lex->order_list.first,
- lex->duplicates, &lex->alter_info);
+ lex->duplicates, lex->ignore, &lex->alter_info);
}
break;
}
@@ -2578,7 +2661,9 @@ unsent_create_error:
check_access(thd, SELECT_ACL | EXTRA_ACL, tables->db,
&tables->grant.privilege,0,0))
goto error;
- res = mysqld_show_create(thd, tables);
+ if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
+ goto error;
+ res= mysqld_show_create(thd, tables);
break;
}
#endif
@@ -2603,7 +2688,8 @@ unsent_create_error:
mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ thd->clear_error(); // No binlog error generated
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
}
@@ -2631,7 +2717,8 @@ unsent_create_error:
mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ thd->clear_error(); // No binlog error generated
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
}
@@ -2653,7 +2740,8 @@ unsent_create_error:
mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ thd->clear_error(); // No binlog error generated
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
}
@@ -2669,7 +2757,7 @@ unsent_create_error:
select_lex->order_list.elements,
(ORDER *) select_lex->order_list.first,
select_lex->select_limit,
- lex->duplicates);
+ lex->duplicates, lex->ignore);
if (thd->net.report_error)
res= -1;
break;
@@ -2682,7 +2770,7 @@ unsent_create_error:
&lex->value_list,
select_lex->where,
select_lex->options,
- lex->duplicates, unit, select_lex);
+ lex->duplicates, lex->ignore, unit, select_lex);
break;
}
case SQLCOM_REPLACE:
@@ -2690,9 +2778,9 @@ unsent_create_error:
{
if ((res= insert_precheck(thd, tables)))
break;
- res = mysql_insert(thd,tables,lex->field_list,lex->many_values,
- select_lex->item_list, lex->value_list,
- lex->duplicates);
+ res= mysql_insert(thd,tables,lex->field_list,lex->many_values,
+ lex->update_list, lex->value_list,
+ lex->duplicates, lex->ignore);
if (thd->net.report_error)
res= -1;
break;
@@ -2701,7 +2789,7 @@ unsent_create_error:
case SQLCOM_INSERT_SELECT:
{
TABLE_LIST *first_local_table= (TABLE_LIST *) select_lex->table_list.first;
- if ((res= insert_select_precheck(thd, tables)))
+ if ((res= insert_precheck(thd, tables)))
break;
/* Fix lock for first table */
@@ -2723,13 +2811,23 @@ unsent_create_error:
select_lex->options |= OPTION_BUFFER_RESULT;
}
-
- if (!(res= open_and_lock_tables(thd, tables)) &&
- (result= new select_insert(tables->table, &lex->field_list,
- lex->duplicates)))
+ if ((res= open_and_lock_tables(thd, tables)))
+ break;
+
+ TABLE *table= tables->table;
+ /* Skip first table, which is the table we are inserting in */
+ lex->select_lex.table_list.first= (byte*) first_local_table->next;
+ tables= (TABLE_LIST *) lex->select_lex.table_list.first;
+ first_local_table->next= 0;
+
+ if (!(res= mysql_prepare_insert(thd, tables, first_local_table,
+ table, lex->field_list, 0,
+ lex->update_list, lex->value_list,
+ lex->duplicates)) &&
+ (result= new select_insert(table, &lex->field_list,
+ &lex->update_list, &lex->value_list,
+ lex->duplicates, lex->ignore)))
{
- /* Skip first table, which is the table we are inserting in */
- lex->select_lex.table_list.first= (byte*) first_local_table->next;
/*
insert/replace from SELECT give its SELECT_LEX for SELECT,
and item_list belong to SELECT
@@ -2737,14 +2835,16 @@ unsent_create_error:
lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
res= handle_select(thd, lex, result);
/* revert changes for SP */
- lex->select_lex.table_list.first= (byte*) first_local_table;
lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
delete result;
+ table->insert_values= 0;
if (thd->net.report_error)
res= -1;
}
else
res= -1;
+ first_local_table->next= tables;
+ lex->select_lex.table_list.first= (byte*) first_local_table;
break;
}
case SQLCOM_TRUNCATE:
@@ -3034,7 +3134,7 @@ unsent_create_error:
goto error;
}
res=mysql_load(thd, lex->exchange, tables, lex->field_list,
- lex->duplicates, (bool) lex->local_file, lex->lock_option);
+ lex->duplicates, lex->ignore, (bool) lex->local_file, lex->lock_option);
break;
}
@@ -3066,6 +3166,12 @@ purposes internal to the MySQL server", MYF(0));
}
case SQLCOM_UNLOCK_TABLES:
+ /*
+ It is critical for mysqldump --single-transaction --master-data that
+ UNLOCK TABLES does not implicitely commit a connection which has only
+ done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes
+ false, mysqldump will not work.
+ */
unlock_locked_tables(thd);
if (thd->options & OPTION_TABLE_LOCK)
{
@@ -3165,35 +3271,41 @@ purposes internal to the MySQL server", MYF(0));
}
case SQLCOM_ALTER_DB:
{
- if (!strip_sp(lex->name) || check_db_name(lex->name))
+ char *db= lex->name ? lex->name : thd->db;
+ if (!db)
{
- net_printf(thd, ER_WRONG_DB_NAME, lex->name);
+ send_error(thd, ER_NO_DB_ERROR);
+ goto error;
+ }
+ if (!strip_sp(db) || check_db_name(db))
+ {
+ net_printf(thd, ER_WRONG_DB_NAME, db);
break;
}
/*
If in a slave thread :
ALTER DATABASE DB may not be preceded by USE DB.
- For that reason, maybe db_ok() in sql/slave.cc did not check the
+ For that reason, maybe db_ok() in sql/slave.cc did not check the
do_db/ignore_db. And as this query involves no tables, tables_ok()
above was not called. So we have to check rules again here.
*/
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
- !db_ok_with_wild_table(lex->name)))
+ if (thd->slave_thread &&
+ (!db_ok(db, replicate_do_db, replicate_ignore_db) ||
+ !db_ok_with_wild_table(db)))
{
my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
break;
}
#endif
- if (check_access(thd,ALTER_ACL,lex->name,0,1,0))
+ if (check_access(thd, ALTER_ACL, db, 0, 1, 0))
break;
if (thd->locked_tables || thd->active_transaction())
{
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
goto error;
}
- res=mysql_alter_db(thd,lex->name,&lex->create_info);
+ res= mysql_alter_db(thd, db, &lex->create_info);
break;
}
case SQLCOM_SHOW_CREATE_DB:
@@ -3205,11 +3317,6 @@ purposes internal to the MySQL server", MYF(0));
}
if (check_access(thd,SELECT_ACL,lex->name,0,1,0))
break;
- if (thd->locked_tables || thd->active_transaction())
- {
- send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
- goto error;
- }
res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
break;
}
@@ -3243,7 +3350,7 @@ purposes internal to the MySQL server", MYF(0));
mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
send_ok(thd);
@@ -3259,7 +3366,7 @@ purposes internal to the MySQL server", MYF(0));
mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
send_ok(thd);
@@ -3270,9 +3377,9 @@ purposes internal to the MySQL server", MYF(0));
case SQLCOM_GRANT:
{
if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
- tables && tables->db ? tables->db : select_lex->db,
+ tables ? tables->db : select_lex->db,
tables ? &tables->grant.privilege : 0,
- tables ? 0 : 1,0))
+ tables ? 0 : 1, 0))
goto error;
/*
@@ -3326,7 +3433,7 @@ purposes internal to the MySQL server", MYF(0));
if (mysql_bin_log.is_open())
{
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
}
@@ -3347,7 +3454,7 @@ purposes internal to the MySQL server", MYF(0));
if (mysql_bin_log.is_open())
{
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
if (mqh_used && lex->sql_command == SQLCOM_GRANT)
@@ -3390,7 +3497,7 @@ purposes internal to the MySQL server", MYF(0));
mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
}
@@ -3452,7 +3559,9 @@ purposes internal to the MySQL server", MYF(0));
thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
OPTION_BEGIN);
thd->server_status|= SERVER_STATUS_IN_TRANS;
- send_ok(thd);
+ if (!(lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) ||
+ !(res= ha_start_consistent_snapshot(thd)))
+ send_ok(thd);
}
break;
case SQLCOM_COMMIT:
@@ -3544,13 +3653,21 @@ purposes internal to the MySQL server", MYF(0));
send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
error:
+ if (unlikely(slave_fake_lock))
+ {
+ DBUG_PRINT("info",("undoing faked lock"));
+ thd->lock= thd->locked_tables;
+ thd->locked_tables= fake_prev_lock;
+ if (thd->lock == thd->locked_tables)
+ thd->lock= 0;
+ }
DBUG_VOID_RETURN;
}
/*
Check grants for commands which work only with one table and all other
- tables belong to subselects.
+ tables belonging to subselects or implicitly opened tables.
SYNOPSIS
check_one_table_access()
@@ -3572,7 +3689,7 @@ int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
if (grant_option && check_grant(thd, privilege, tables, 0, 1, 0))
return 1;
- /* Check rights on tables of subselect (if exists) */
+ /* Check rights on tables of subselects and implictly opened tables */
TABLE_LIST *subselects_tables;
if ((subselects_tables= tables->next))
{
@@ -3919,7 +4036,6 @@ mysql_init_select(LEX *lex)
{
DBUG_ASSERT(lex->result == 0);
lex->exchange= 0;
- lex->proc_list.first= 0;
}
}
@@ -3927,18 +4043,20 @@ mysql_init_select(LEX *lex)
bool
mysql_new_select(LEX *lex, bool move_down)
{
- SELECT_LEX *select_lex = new(&lex->thd->mem_root) SELECT_LEX();
- if (!select_lex)
+ SELECT_LEX *select_lex;
+ if (!(select_lex= new(lex->thd->mem_root) SELECT_LEX()))
return 1;
select_lex->select_number= ++lex->thd->select_number;
select_lex->init_query();
select_lex->init_select();
if (move_down)
{
+ lex->subqueries= TRUE;
/* first select_lex of subselect or derived table */
- SELECT_LEX_UNIT *unit= new(&lex->thd->mem_root) SELECT_LEX_UNIT();
- if (!unit)
+ SELECT_LEX_UNIT *unit;
+ if (!(unit= new(lex->thd->mem_root) SELECT_LEX_UNIT()))
return 1;
+
unit->init_query();
unit->init_select();
unit->thd= lex->thd;
@@ -3960,7 +4078,8 @@ mysql_new_select(LEX *lex, bool move_down)
as far as we included SELECT_LEX for UNION unit should have
fake SELECT_LEX for UNION processing
*/
- fake= unit->fake_select_lex= new(&lex->thd->mem_root) SELECT_LEX();
+ if (!(fake= unit->fake_select_lex= new(lex->thd->mem_root) SELECT_LEX()))
+ return 1;
fake->include_standalone(unit,
(SELECT_LEX_NODE**)&unit->fake_select_lex);
fake->select_number= INT_MAX;
@@ -4048,6 +4167,20 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
send_error(thd, 0, NullS);
else
{
+ /*
+ Binlog logs a string starting from thd->query and having length
+ thd->query_length; so we set thd->query_length correctly (to not
+ log several statements in one event, when we executed only first).
+ We set it to not see the ';' (otherwise it would get into binlog
+ and Query_log_event::print() would give ';;' output).
+ This also helps display only the current query in SHOW
+ PROCESSLIST.
+ Note that we don't need LOCK_thread_count to modify query_length.
+ */
+ if (lex->found_colon &&
+ (thd->query_length= (ulong)(lex->found_colon - thd->query)))
+ thd->query_length--;
+ /* Actually execute the query */
mysql_execute_command(thd);
query_cache_end_of_result(thd);
}
@@ -4092,31 +4225,6 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
#endif
-/*
- Calculate interval lengths.
- Strip trailing spaces from all strings.
- After this function call:
- - ENUM uses max_length
- - SET uses tot_length.
-*/
-void calculate_interval_lengths(THD *thd, TYPELIB *interval,
- uint *max_length, uint *tot_length)
-{
- const char **pos;
- uint *len;
- CHARSET_INFO *cs= thd->variables.character_set_client;
- *max_length= *tot_length= 0;
- for (pos= interval->type_names, len= interval->type_lengths;
- *pos ; pos++, len++)
- {
- *len= (uint) strip_sp((char*) *pos);
- uint length= cs->cset->numchars(cs, *pos, *pos + *len);
- *tot_length+= length;
- set_if_bigger(*max_length, length);
- }
-}
-
-
/*****************************************************************************
** Store field definition for create
** Return 0 if ok
@@ -4127,7 +4235,8 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
uint type_modifier,
Item *default_value, Item *on_update_value,
LEX_STRING *comment,
- char *change, TYPELIB *interval, CHARSET_INFO *cs,
+ char *change,
+ List<String> *interval_list, CHARSET_INFO *cs,
uint uint_geom_type)
{
register create_field *new_field;
@@ -4422,62 +4531,39 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
break;
case FIELD_TYPE_SET:
{
- if (interval->count > sizeof(longlong)*8)
+ if (interval_list->elements > sizeof(longlong)*8)
{
- net_printf(thd,ER_TOO_BIG_SET,field_name); /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
+ net_printf(thd,ER_TOO_BIG_SET,field_name); /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
}
- new_field->pack_length=(interval->count+7)/8;
+ new_field->pack_length= (interval_list->elements + 7) / 8;
if (new_field->pack_length > 4)
- new_field->pack_length=8;
- new_field->interval=interval;
- uint dummy_max_length;
- calculate_interval_lengths(thd, interval,
- &dummy_max_length, &new_field->length);
- new_field->length+= (interval->count - 1);
- set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1);
- if (default_value)
- {
- char *not_used;
- uint not_used2;
- bool not_used3;
-
- thd->cuted_fields=0;
- String str,*res;
- res=default_value->val_str(&str);
- (void) find_set(interval, res->ptr(), res->length(),
- &my_charset_bin,
- &not_used, &not_used2, &not_used3);
- if (thd->cuted_fields)
- {
- net_printf(thd,ER_INVALID_DEFAULT,field_name);
- DBUG_RETURN(1);
- }
- }
+ new_field->pack_length=8;
+
+ List_iterator<String> it(*interval_list);
+ String *tmp;
+ while ((tmp= it++))
+ new_field->interval_list.push_back(tmp);
+ /*
+ Set fake length to 1 to pass the below conditions.
+ Real length will be set in mysql_prepare_table()
+ when we know the character set of the column
+ */
+ new_field->length= 1;
}
break;
case FIELD_TYPE_ENUM:
{
- new_field->interval=interval;
- new_field->pack_length=interval->count < 256 ? 1 : 2; // Should be safe
+ // Should be safe
+ new_field->pack_length= interval_list->elements < 256 ? 1 : 2;
- uint dummy_tot_length;
- calculate_interval_lengths(thd, interval,
- &new_field->length, &dummy_tot_length);
- set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1);
- if (default_value)
- {
- String str,*res;
- res=default_value->val_str(&str);
- res->strip_sp();
- if (!find_type(interval, res->ptr(), res->length(), 0))
- {
- net_printf(thd,ER_INVALID_DEFAULT,field_name);
- DBUG_RETURN(1);
- }
- }
- break;
+ List_iterator<String> it(*interval_list);
+ String *tmp;
+ while ((tmp= it++))
+ new_field->interval_list.push_back(tmp);
+ new_field->length= 1; // See comment for FIELD_TYPE_SET above.
}
+ break;
}
if ((new_field->length > MAX_FIELD_CHARLENGTH && type != FIELD_TYPE_SET &&
@@ -4950,6 +5036,7 @@ void kill_one_thread(THD *thd, ulong id)
net_printf(thd,error,id);
}
+
/* Clear most status variables */
static void refresh_status(void)
@@ -4959,18 +5046,9 @@ static void refresh_status(void)
{
if (ptr->type == SHOW_LONG)
*(ulong*) ptr->value= 0;
- else if (ptr->type == SHOW_KEY_CACHE_LONG)
- {
- /*
- Reset value in 'default' key cache.
- This needs to be recoded when we have thread specific key values
- */
- char *value= (((char*) sql_key_cache) +
- (uint) ((char*) (ptr->value) -
- (char*) &dflt_key_cache_var));
- *(ulong*) value= 0;
- }
}
+ /* Reset the counters of all key caches (default and named). */
+ process_key_caches(reset_key_cache_counters);
pthread_mutex_unlock(&LOCK_status);
}
@@ -5027,6 +5105,58 @@ bool check_simple_select()
return 0;
}
+/*
+ Setup locking for multi-table updates. Used by the replication slave.
+ Replication slave SQL thread examines (all_tables_not_ok()) the
+ locking state of referenced tables to determine if the query has to
+ be executed or ignored. Since in multi-table update, the
+ 'default' lock is read-only, this lock is corrected early enough by
+ calling this function, before the slave decides to execute/ignore.
+
+ SYNOPSIS
+ check_multi_update_lock()
+ thd Current thread
+ tables List of user-supplied tables
+ fields List of fields requiring update
+
+ RETURN VALUES
+ 0 ok
+ 1 error
+*/
+static bool check_multi_update_lock(THD *thd, TABLE_LIST *tables,
+ List<Item> *fields, SELECT_LEX *select_lex)
+{
+ bool res= 1;
+ TABLE_LIST *table;
+ DBUG_ENTER("check_multi_update_lock");
+
+ if (check_db_used(thd, tables))
+ goto error;
+
+ /*
+ Ensure that we have UPDATE or SELECT privilege for each table
+ The exact privilege is checked in mysql_multi_update()
+ */
+ for (table= tables ; table ; table= table->next)
+ {
+ TABLE_LIST *save= table->next;
+ table->next= 0;
+ if ((check_access(thd, UPDATE_ACL, table->db, &table->grant.privilege,0,1) ||
+ (grant_option && check_grant(thd, UPDATE_ACL, table,0,1,1))) &&
+ check_one_table_access(thd, SELECT_ACL, table))
+ goto error;
+ table->next= save;
+ }
+
+ if (mysql_multi_update_lock(thd, tables, fields, select_lex))
+ goto error;
+
+ res= 0;
+
+error:
+ DBUG_RETURN(res);
+}
+
Comp_creator *comp_eq_creator(bool invert)
{
@@ -5091,9 +5221,9 @@ Item * all_any_subquery_creator(Item *left_expr,
Item_allany_subselect *it=
new Item_allany_subselect(left_expr, (*cmp)(all), select_lex, all);
if (all)
- return it->upper_not= new Item_func_not_all(it); /* ALL */
+ return it->upper_item= new Item_func_not_all(it); /* ALL */
- return it; /* ANY/SOME */
+ return it->upper_item= new Item_func_nop_all(it); /* ANY/SOME */
}
@@ -5123,7 +5253,7 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
&create_info, table_list,
fields, keys, 0, (ORDER*)0,
- DUP_ERROR, &alter_info));
+ DUP_ERROR, 0, &alter_info));
}
@@ -5142,7 +5272,7 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
&create_info, table_list,
fields, keys, 0, (ORDER*)0,
- DUP_ERROR, alter_info));
+ DUP_ERROR, 0, alter_info));
}
@@ -5206,7 +5336,10 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
DBUG_PRINT("info",("Checking sub query list"));
for (table= tables; table; table= table->next)
{
- if (table->table_in_update_from_clause)
+ if (my_tz_check_n_skip_implicit_tables(&table,
+ lex->time_zone_tables_used))
+ continue;
+ else if (table->table_in_update_from_clause)
{
/*
If we check table by local TABLE_LIST copy then we should copy
@@ -5307,33 +5440,6 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
/*
- INSERT ... SELECT query pre-check
-
- SYNOPSIS
- insert_delete_precheck()
- thd Thread handler
- tables Global table list
-
- RETURN VALUE
- 0 OK
- 1 Error (message is sent to user)
- -1 Error (message is not sent to user)
-*/
-
-int insert_select_precheck(THD *thd, TABLE_LIST *tables)
-{
- DBUG_ENTER("insert_select_precheck");
- /*
- Check that we have modify privileges for the first table and
- select privileges for the rest
- */
- ulong privilege= (thd->lex->duplicates == DUP_REPLACE ?
- INSERT_ACL | DELETE_ACL : INSERT_ACL);
- DBUG_RETURN(check_one_table_access(thd, privilege, tables) ? 1 : 0);
-}
-
-
-/*
simple UPDATE query pre-check
SYNOPSIS
@@ -5404,6 +5510,10 @@ int insert_precheck(THD *thd, TABLE_LIST *tables)
LEX *lex= thd->lex;
DBUG_ENTER("insert_precheck");
+ /*
+ Check that we have modify privileges for the first table and
+ select privileges for the rest
+ */
ulong privilege= INSERT_ACL |
(lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
(lex->duplicates == DUP_UPDATE ? UPDATE_ACL : 0);
@@ -5411,7 +5521,7 @@ int insert_precheck(THD *thd, TABLE_LIST *tables)
if (check_one_table_access(thd, privilege, tables))
DBUG_RETURN(1);
- if (lex->select_lex.item_list.elements != lex->value_list.elements)
+ if (lex->update_list.elements != lex->value_list.elements)
{
my_error(ER_WRONG_VALUE_COUNT, MYF(0));
DBUG_RETURN(-1);
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 5a311eefacd..1dc46aef4da 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -68,7 +68,6 @@ Long data handling:
***********************************************************************/
#include "mysql_priv.h"
-#include "sql_acl.h"
#include "sql_select.h" // for JOIN
#include <m_ctype.h> // for isspace()
#ifdef EMBEDDED_LIBRARY
@@ -153,6 +152,8 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
{
NET *net= &stmt->thd->net;
char buff[9];
+ DBUG_ENTER("send_prep_stmt");
+
buff[0]= 0; /* OK packet indicator */
int4store(buff+1, stmt->id);
int2store(buff+5, columns);
@@ -161,12 +162,11 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
Send types and names of placeholders to the client
XXX: fix this nasty upcast from List<Item_param> to List<Item>
*/
- return my_net_write(net, buff, sizeof(buff)) ||
- (stmt->param_count &&
- stmt->thd->protocol_simple.send_fields((List<Item> *)
- &stmt->lex->param_list, 0)) ||
- net_flush(net);
- return 0;
+ DBUG_RETURN(my_net_write(net, buff, sizeof(buff)) ||
+ (stmt->param_count &&
+ stmt->thd->protocol_simple.send_fields((List<Item> *)
+ &stmt->lex->param_list,
+ 0)));
}
#else
static bool send_prep_stmt(Prepared_statement *stmt,
@@ -348,12 +348,6 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len)
tm.neg= (bool) to[0];
day= (uint) sint4korr(to+1);
- /*
- Note, that though ranges of hour, minute and second are not checked
- here we rely on them being < 256: otherwise
- we'll get buffer overflow in make_{date,time} functions,
- which are called when time value is converted to string.
- */
tm.hour= (uint) to[5] + day * 24;
tm.minute= (uint) to[6];
tm.second= (uint) to[7];
@@ -368,7 +362,7 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len)
tm.day= tm.year= tm.month= 0;
}
else
- set_zero_time(&tm);
+ set_zero_time(&tm, MYSQL_TIMESTAMP_TIME);
param->set_time(&tm, MYSQL_TIMESTAMP_TIME,
MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
*pos+= length;
@@ -387,11 +381,6 @@ static void set_param_datetime(Item_param *param, uchar **pos, ulong len)
tm.year= (uint) sint2korr(to);
tm.month= (uint) to[2];
tm.day= (uint) to[3];
- /*
- Note, that though ranges of hour, minute and second are not checked
- here we rely on them being < 256: otherwise
- we'll get buffer overflow in make_{date,time} functions.
- */
if (length > 4)
{
tm.hour= (uint) to[4];
@@ -404,7 +393,7 @@ static void set_param_datetime(Item_param *param, uchar **pos, ulong len)
tm.second_part= (length > 7) ? (ulong) sint4korr(to+7) : 0;
}
else
- set_zero_time(&tm);
+ set_zero_time(&tm, MYSQL_TIMESTAMP_DATETIME);
param->set_time(&tm, MYSQL_TIMESTAMP_DATETIME,
MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
*pos+= length;
@@ -418,11 +407,7 @@ static void set_param_date(Item_param *param, uchar **pos, ulong len)
if (length >= 4)
{
uchar *to= *pos;
- /*
- Note, that though ranges of hour, minute and second are not checked
- here we rely on them being < 256: otherwise
- we'll get buffer overflow in make_{date,time} functions.
- */
+
tm.year= (uint) sint2korr(to);
tm.month= (uint) to[2];
tm.day= (uint) to[3];
@@ -432,7 +417,7 @@ static void set_param_date(Item_param *param, uchar **pos, ulong len)
tm.neg= 0;
}
else
- set_zero_time(&tm);
+ set_zero_time(&tm, MYSQL_TIMESTAMP_DATE);
param->set_time(&tm, MYSQL_TIMESTAMP_DATE,
MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
*pos+= length;
@@ -441,8 +426,17 @@ static void set_param_date(Item_param *param, uchar **pos, ulong len)
#else/*!EMBEDDED_LIBRARY*/
void set_param_time(Item_param *param, uchar **pos, ulong len)
{
- MYSQL_TIME *to= (MYSQL_TIME*)*pos;
- param->set_time(to, MYSQL_TIMESTAMP_TIME,
+ MYSQL_TIME tm= *((MYSQL_TIME*)*pos);
+ tm.hour+= tm.day * 24;
+ tm.day= tm.year= tm.month= 0;
+ if (tm.hour > 838)
+ {
+ /* TODO: add warning 'Data truncated' here */
+ tm.hour= 838;
+ tm.minute= 59;
+ tm.second= 59;
+ }
+ param->set_time(&tm, MYSQL_TIMESTAMP_TIME,
MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
}
@@ -903,8 +897,12 @@ static int mysql_test_insert(Prepared_statement *stmt,
/*
open temporary memory pool for temporary data allocated by derived
tables & preparation procedure
+ Note that this is done without locks (should not be needed as we will not
+ access any data here)
+ If we would use locks, then we have to ensure we are not using
+ TL_WRITE_DELAYED as having two such locks can cause table corruption.
*/
- if (open_and_lock_tables(thd, table_list))
+ if (open_normal_and_derived_tables(thd, table_list))
{
DBUG_RETURN(-1);
}
@@ -914,14 +912,15 @@ static int mysql_test_insert(Prepared_statement *stmt,
uint value_count;
ulong counter= 0;
+ table_list->table->insert_values=(byte *)1; // don't allocate insert_values
if ((res= mysql_prepare_insert(thd, table_list, insert_table_list,
table_list->table, fields, values,
update_fields, update_values, duplic)))
goto error;
-
+
value_count= values->elements;
its.rewind();
-
+
while ((values= its++))
{
counter++;
@@ -940,6 +939,7 @@ static int mysql_test_insert(Prepared_statement *stmt,
res= 0;
error:
lex->unit.cleanup();
+ table_list->table->insert_values=0;
DBUG_RETURN(res);
}
@@ -1064,7 +1064,7 @@ static int mysql_test_select(Prepared_statement *stmt,
DBUG_RETURN(1);
#endif
- if (!lex->result && !(lex->result= new (&stmt->mem_root) select_send))
+ if (!lex->result && !(lex->result= new (stmt->mem_root) select_send))
{
send_error(thd);
goto err;
@@ -1088,7 +1088,7 @@ static int mysql_test_select(Prepared_statement *stmt,
{
if (lex->describe)
{
- if (send_prep_stmt(stmt, 0))
+ if (send_prep_stmt(stmt, 0) || thd->protocol->flush())
goto err_prep;
}
else
@@ -1106,11 +1106,8 @@ static int mysql_test_select(Prepared_statement *stmt,
prepared in unit->prepare call above.
*/
if (send_prep_stmt(stmt, lex->result->field_count(fields)) ||
- lex->result->send_fields(fields, 0)
-#ifndef EMBEDDED_LIBRARY
- || net_flush(&thd->net)
-#endif
- )
+ lex->result->send_fields(fields, 0) ||
+ thd->protocol->flush())
goto err_prep;
}
}
@@ -1352,7 +1349,7 @@ static int mysql_test_insert_select(Prepared_statement *stmt,
{
int res;
LEX *lex= stmt->lex;
- if ((res= insert_select_precheck(stmt->thd, tables)))
+ if ((res= insert_precheck(stmt->thd, tables)))
return res;
TABLE_LIST *first_local_table=
(TABLE_LIST *)lex->select_lex.table_list.first;
@@ -1389,7 +1386,6 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
enum enum_sql_command sql_command= lex->sql_command;
int res= 0;
DBUG_ENTER("send_prepare_results");
-
DBUG_PRINT("enter",("command: %d, param_count: %ld",
sql_command, stmt->param_count));
@@ -1442,6 +1438,7 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
break;
case SQLCOM_INSERT_SELECT:
+ case SQLCOM_REPLACE_SELECT:
res= mysql_test_insert_select(stmt, tables);
break;
@@ -1474,7 +1471,8 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
goto error;
}
if (res == 0)
- DBUG_RETURN(text_protocol? 0 : send_prep_stmt(stmt, 0));
+ DBUG_RETURN(text_protocol? 0 : (send_prep_stmt(stmt, 0) ||
+ thd->protocol->flush()));
error:
if (res < 0)
send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
@@ -1504,7 +1502,7 @@ static bool init_param_array(Prepared_statement *stmt)
List_iterator<Item_param> param_iterator(lex->param_list);
/* Use thd->mem_root as it points at statement mem_root */
stmt->param_array= (Item_param **)
- alloc_root(&stmt->thd->mem_root,
+ alloc_root(stmt->thd->mem_root,
sizeof(Item_param*) * stmt->param_count);
if (!stmt->param_array)
{
@@ -1521,7 +1519,6 @@ static bool init_param_array(Prepared_statement *stmt)
return 0;
}
-
/*
Given a query string with parameter markers, create a Prepared Statement
from it and send PS info back to the client.
@@ -1570,7 +1567,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
if (name)
{
stmt->name.length= name->length;
- if (!(stmt->name.str= memdup_root(&stmt->mem_root, (char*)name->str,
+ if (!(stmt->name.str= memdup_root(stmt->mem_root, (char*)name->str,
name->length)))
{
delete stmt;
@@ -1599,7 +1596,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
DBUG_RETURN(1);
}
- mysql_log.write(thd, COM_PREPARE, "%s", packet);
+ mysql_log.write(thd, COM_PREPARE, "[%lu] %s", stmt->id, packet);
thd->current_arena= stmt;
mysql_init_query(thd, (uchar *) thd->query, thd->query_length);
@@ -1609,7 +1606,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
lex->safe_to_cache_query= 0;
error= yyparse((void *)thd) || thd->is_fatal_error ||
- init_param_array(stmt);
+ thd->net.report_error || init_param_array(stmt);
/*
While doing context analysis of the query (in send_prepare_results) we
allocate a lot of additional memory: for open tables, JOINs, derived
@@ -1640,7 +1637,9 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
stmt= NULL;
- /* error is sent inside yyparse/send_prepare_results */
+ if (thd->net.report_error)
+ send_error(thd);
+ /* otherwise the error is sent inside yyparse/send_prepare_results */
}
else
{
@@ -1797,6 +1796,9 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query))
goto set_params_data_err;
#endif
+ mysql_log.write(thd, COM_EXECUTE, "[%lu] %s", stmt->id,
+ expanded_query.length() ? expanded_query.c_ptr() :
+ stmt->query);
thd->protocol= &thd->protocol_prep; // Switch to binary protocol
execute_stmt(thd, stmt, &expanded_query, TRUE);
thd->protocol= &thd->protocol_simple; // Use normal protocol
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index afaf2ed0923..388034e0f1a 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -84,7 +84,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
if (mysql_bin_log.is_open())
{
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
send_ok(thd);
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index a84b63c270b..d02bb5ff0a3 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -18,7 +18,6 @@
#ifdef HAVE_REPLICATION
#include "sql_repl.h"
-#include "sql_acl.h"
#include "log_event.h"
#include <my_dir.h>
@@ -247,7 +246,7 @@ bool log_in_use(const char* log_name)
if ((linfo = tmp->current_linfo))
{
pthread_mutex_lock(&linfo->lock);
- result = !memcmp(log_name, linfo->log_file_name, log_name_len);
+ result = !bcmp(log_name, linfo->log_file_name, log_name_len);
pthread_mutex_unlock(&linfo->lock);
if (result)
break;
@@ -683,7 +682,8 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report)
thread_mask&= thd->lex->slave_thd_opt;
if (thread_mask) //some threads are stopped, start them
{
- if (init_master_info(mi,master_info_file,relay_log_info_file, 0))
+ if (init_master_info(mi,master_info_file,relay_log_info_file, 0,
+ thread_mask))
slave_errno=ER_MASTER_INFO;
else if (server_id_supplied && *mi->host)
{
@@ -880,10 +880,10 @@ int reset_slave(THD *thd, MASTER_INFO* mi)
*/
init_master_info_with_options(mi);
/*
- Reset errors, and master timestamp (the idea is that we forget about the
+ Reset errors (the idea is that we forget about the
old master).
*/
- clear_slave_error_timestamp(&mi->rli);
+ clear_slave_error(&mi->rli);
clear_until_condition(&mi->rli);
// close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0
@@ -978,7 +978,8 @@ int change_master(THD* thd, MASTER_INFO* mi)
thd->proc_info = "Changing master";
LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
// TODO: see if needs re-write
- if (init_master_info(mi, master_info_file, relay_log_info_file, 0))
+ if (init_master_info(mi, master_info_file, relay_log_info_file, 0,
+ thread_mask))
{
send_error(thd, ER_MASTER_INFO);
unlock_slave_threads(mi);
@@ -1142,8 +1143,8 @@ int change_master(THD* thd, MASTER_INFO* mi)
pthread_mutex_lock(&mi->rli.data_lock);
mi->rli.abort_pos_wait++; /* for MASTER_POS_WAIT() to abort */
- /* Clear the errors, for a clean start, and master timestamp */
- clear_slave_error_timestamp(&mi->rli);
+ /* Clear the errors, for a clean start */
+ clear_slave_error(&mi->rli);
clear_until_condition(&mi->rli);
/*
If we don't write new coordinates to disk now, then old will remain in
@@ -1379,7 +1380,7 @@ err:
int log_loaded_block(IO_CACHE* file)
{
- LOAD_FILE_INFO* lf_info;
+ LOAD_FILE_INFO *lf_info;
uint block_len ;
/* file->request_pos contains position where we started last read */
@@ -1401,7 +1402,7 @@ int log_loaded_block(IO_CACHE* file)
{
Create_file_log_event c(lf_info->thd,lf_info->ex,lf_info->db,
lf_info->table_name, *lf_info->fields,
- lf_info->handle_dup, buffer,
+ lf_info->handle_dup, lf_info->ignore, buffer,
block_len, lf_info->log_delayed);
mysql_bin_log.write(&c);
lf_info->wrote_create_file = 1;
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index 3c17540b664..21b3d2955f7 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -67,7 +67,7 @@ typedef struct st_load_file_info
enum enum_duplicates handle_dup;
char* db;
char* table_name;
- bool wrote_create_file, log_delayed;
+ bool wrote_create_file, log_delayed, ignore;
} LOAD_FILE_INFO;
int log_loaded_block(IO_CACHE* file);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 1610057f877..533a070de23 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -398,7 +398,16 @@ JOIN::prepare(Item ***rref_pointer_array,
goto err;
}
#endif
- if (!procedure && result && result->prepare(fields_list, unit_arg))
+ /*
+ We must not yet prepare the result table if it is the same as one of the
+ source tables (INSERT SELECT). This is checked in mysql_execute_command()
+ and OPTION_BUFFER_RESULT is added to the select_options. A temporary
+ table is then used to hold the result. The preparation may disable
+ indexes on the result table, which may be used during the select, if it
+ is the same table (Bug #6034). Do the preparation after the select phase.
+ */
+ if (! procedure && ! test(select_options & OPTION_BUFFER_RESULT) &&
+ result && result->prepare(fields_list, unit_arg))
goto err; /* purecov: inspected */
if (select_lex->olap == ROLLUP_TYPE && rollup_init())
@@ -468,7 +477,7 @@ JOIN::optimize()
optimized= 1;
// Ignore errors of execution if option IGNORE present
- if (thd->lex->duplicates == DUP_IGNORE)
+ if (thd->lex->ignore)
thd->lex->current_select->no_error= 1;
#ifdef HAVE_REF_TO_FIELDS // Not done yet
/* Add HAVING to WHERE if possible */
@@ -936,7 +945,7 @@ JOIN::optimize()
}
}
- if (select_lex->master_unit()->uncacheable)
+ if (thd->lex->subqueries)
{
if (!(tmp_join= (JOIN*)thd->alloc(sizeof(JOIN))))
DBUG_RETURN(-1);
@@ -1043,6 +1052,13 @@ JOIN::exec()
DBUG_VOID_RETURN;
}
}
+ else if (test(select_options & OPTION_BUFFER_RESULT) &&
+ result && result->prepare(fields_list, unit))
+ {
+ error= 1;
+ thd->limit_found_rows= thd->examined_row_count= 0;
+ DBUG_VOID_RETURN;
+ }
if (!tables_list)
{ // Only test of functions
@@ -1306,7 +1322,7 @@ JOIN::exec()
curr_join->select_distinct=0; /* Each row is unique */
curr_join->join_free(0); /* Free quick selects */
- if (select_distinct && ! group_list)
+ if (curr_join->select_distinct && ! curr_join->group_list)
{
thd->proc_info="Removing duplicates";
if (curr_join->tmp_having)
@@ -1420,7 +1436,7 @@ JOIN::exec()
WHERE clause for any tables after the sorted one.
*/
JOIN_TAB *curr_table= &curr_join->join_tab[curr_join->const_tables+1];
- JOIN_TAB *end_table= &curr_join->join_tab[tables];
+ JOIN_TAB *end_table= &curr_join->join_tab[curr_join->tables];
for (; curr_table < end_table ; curr_table++)
{
/*
@@ -2156,7 +2172,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond,
bool is_const=1;
for (uint i=0; i<num_values; i++)
- is_const&= (*value)->const_item();
+ is_const&= value[i]->const_item();
if (is_const)
stat[0].const_keys.merge(possible_keys);
/*
@@ -2830,10 +2846,9 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
x = used key parts (1 <= x <= c)
*/
double rec_per_key;
- if (!(rec_per_key=(double)
- keyinfo->rec_per_key[keyinfo->key_parts-1]))
- rec_per_key=(double) s->records/rec+1;
-
+ rec_per_key= keyinfo->rec_per_key[keyinfo->key_parts-1] ?
+ (double) keyinfo->rec_per_key[keyinfo->key_parts-1] :
+ (double) s->records/rec+1;
if (!s->records)
tmp=0;
else if (rec_per_key/(double) s->records >= 0.01)
@@ -3509,8 +3524,17 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
/* Join with outer join condition */
COND *orig_cond=sel->cond;
sel->cond= and_conds(sel->cond, tab->on_expr);
+
+ /*
+ We can't call sel->cond->fix_fields,
+ as it will break tab->on_expr if it's AND condition
+ (fix_fields currently removes extra AND/OR levels).
+ Yet attributes of the just built condition are not needed.
+ Thus we call sel->cond->quick_fix_field for safety.
+ */
if (sel->cond && !sel->cond->fixed)
- sel->cond->fix_fields(join->thd, 0, &sel->cond);
+ sel->cond->quick_fix_field();
+
if (sel->test_quick_select(join->thd, tab->keys,
used_tables & ~ current_map,
(join->select_options &
@@ -3826,7 +3850,9 @@ JOIN::join_free(bool full)
JOIN_TAB *tab,*end;
DBUG_ENTER("JOIN::join_free");
- full= full || !select_lex->uncacheable;
+ full= full || (!select_lex->uncacheable &&
+ !thd->lex->subqueries &&
+ !thd->lex->describe); // do not cleanup too early on EXPLAIN
if (table)
{
@@ -3855,6 +3881,7 @@ JOIN::join_free(bool full)
for (tab= join_tab, end= tab+tables; tab != end; tab++)
tab->cleanup();
table= 0;
+ tables= 0;
}
else
{
@@ -4605,7 +4632,7 @@ static Field* create_tmp_field_from_field(THD *thd, Field* org_field,
org_field->field_name, table,
org_field->charset());
else
- new_field= org_field->new_field(&thd->mem_root, table);
+ new_field= org_field->new_field(thd->mem_root, table);
if (new_field)
{
if (modify_item)
@@ -5208,7 +5235,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
if (!using_unique_constraint)
{
group->buff=(char*) group_buff;
- if (!(group->field=field->new_field(&thd->mem_root,table)))
+ if (!(group->field=field->new_field(thd->mem_root,table)))
goto err; /* purecov: inspected */
if (maybe_null)
{
@@ -5264,6 +5291,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
keyinfo->key_length=(uint16) reclength;
keyinfo->name=(char*) "tmp";
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
+ keyinfo->rec_per_key=0;
if (null_pack_length)
{
key_part_info->null_bit=0;
@@ -5995,7 +6023,7 @@ join_read_system(JOIN_TAB *tab)
{
if (error != HA_ERR_END_OF_FILE)
return report_error(table, error);
- table->null_row=1; // This is ok.
+ mark_as_null_row(tab->table);
empty_record(table); // Make empty record
return -1;
}
@@ -6025,7 +6053,7 @@ join_read_const(JOIN_TAB *tab)
}
if (error)
{
- table->null_row=1;
+ mark_as_null_row(tab->table);
empty_record(table);
if (error != HA_ERR_KEY_NOT_FOUND)
return report_error(table, error);
@@ -7143,11 +7171,24 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
}
else
{
- select->quick->file->ha_index_end();
- select->quick->index= new_ref_key;
- select->quick->init();
+ /*
+ The range optimizer constructed QUICK_RANGE for ref_key, and
+ we want to use instead new_ref_key as the index. We can't
+ just change the index of the quick select, because this may
+ result in an incosistent QUICK_SELECT object. Below we
+ create a new QUICK_SELECT from scratch so that all its
+ parameres are set correctly by the range optimizer.
+ */
+ key_map new_ref_key_map;
+ new_ref_key_map.clear_all(); /* Force the creation of quick select */
+ new_ref_key_map.set_bit(new_ref_key); /* only for new_ref_key. */
+
+ if (select->test_quick_select(tab->join->thd, new_ref_key_map, 0,
+ (tab->join->select_options & OPTION_FOUND_ROWS) ?
+ HA_POS_ERROR : tab->join->unit->select_limit_cnt) <= 0)
+ DBUG_RETURN(0);
}
- ref_key= new_ref_key;
+ ref_key= new_ref_key;
}
}
/* Check if we get the rows in requested sorted order by using the key */
@@ -7457,13 +7498,14 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
field_count++;
}
- if (!field_count)
- { // only const items
+ if (!field_count && !(join->select_options & OPTION_FOUND_ROWS))
+ { // only const items with no OPTION_FOUND_ROWS
join->unit->select_limit_cnt= 1; // Only send first row
DBUG_RETURN(0);
}
Field **first_field=entry->field+entry->fields - field_count;
- offset=entry->field[entry->fields - field_count]->offset();
+ offset= field_count ?
+ entry->field[entry->fields - field_count]->offset() : 0;
reclength=entry->reclength-offset;
free_io_cache(entry); // Safety
@@ -7582,8 +7624,8 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
{
byte *key_buffer, *key_pos, *record=table->record[0];
int error;
- handler *file=table->file;
- ulong extra_length=ALIGN_SIZE(key_length)-key_length;
+ handler *file= table->file;
+ ulong extra_length= ALIGN_SIZE(key_length)-key_length;
uint *field_lengths,*field_length;
HASH hash;
DBUG_ENTER("remove_dup_with_hash_index");
@@ -7597,22 +7639,34 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
NullS))
DBUG_RETURN(1);
+ {
+ Field **ptr;
+ ulong total_length= 0;
+ for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++)
+ {
+ uint length= (*ptr)->pack_length();
+ (*field_length++)= length;
+ total_length+= length;
+ }
+ DBUG_PRINT("info",("field_count: %u key_length: %lu total_length: %lu",
+ field_count, key_length, total_length));
+ DBUG_ASSERT(total_length <= key_length);
+ key_length= total_length;
+ extra_length= ALIGN_SIZE(key_length)-key_length;
+ }
+
if (hash_init(&hash, &my_charset_bin, (uint) file->records, 0,
- key_length,(hash_get_key) 0, 0, 0))
+ key_length, (hash_get_key) 0, 0, 0))
{
my_free((char*) key_buffer,MYF(0));
DBUG_RETURN(1);
}
- {
- Field **ptr;
- for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++)
- (*field_length++)= (*ptr)->pack_length();
- }
file->ha_rnd_init(1);
key_pos=key_buffer;
for (;;)
{
+ byte *org_key_pos;
if (thd->killed)
{
my_error(ER_SERVER_SHUTDOWN,MYF(0));
@@ -7635,6 +7689,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
}
/* copy fields to key buffer */
+ org_key_pos= key_pos;
field_length=field_lengths;
for (Field **ptr= first_field ; *ptr ; ptr++)
{
@@ -7642,14 +7697,14 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
key_pos+= *field_length++;
}
/* Check if it exists before */
- if (hash_search(&hash,key_pos-key_length,key_length))
+ if (hash_search(&hash, org_key_pos, key_length))
{
/* Duplicated found ; Remove the row */
if ((error=file->delete_row(record)))
goto err;
}
else
- (void) my_hash_insert(&hash, key_pos-key_length);
+ (void) my_hash_insert(&hash, org_key_pos);
key_pos+=extra_length;
}
my_free((char*) key_buffer,MYF(0));
@@ -8463,6 +8518,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
res_selected_fields.empty();
res_all_fields.empty();
List_iterator_fast<Item> itr(res_all_fields);
+ List<Item> extra_funcs;
uint i, border= all_fields.elements - elements;
DBUG_ENTER("setup_copy_fields");
@@ -8501,7 +8557,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
saved value
*/
Field *field= item->field;
- item->result_field=field->new_field(&thd->mem_root,field->table);
+ item->result_field=field->new_field(thd->mem_root,field->table);
char *tmp=(char*) sql_alloc(field->pack_length()+1);
if (!tmp)
goto err;
@@ -8524,7 +8580,12 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
*/
if (!(pos=new Item_copy_string(pos)))
goto err;
- if (param->copy_funcs.push_back(pos))
+ if (i < border) // HAVING, ORDER and GROUP BY
+ {
+ if (extra_funcs.push_back(pos))
+ goto err;
+ }
+ else if (param->copy_funcs.push_back(pos))
goto err;
}
res_all_fields.push_back(pos);
@@ -8536,6 +8597,12 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
for (i= 0; i < border; i++)
itr++;
itr.sublist(res_selected_fields, elements);
+ /*
+ Put elements from HAVING, ORDER BY and GROUP BY last to ensure that any
+ reference used in these will resolve to a item that is already calculated
+ */
+ param->copy_funcs.concat(&extra_funcs);
+
DBUG_RETURN(0);
err:
@@ -8562,8 +8629,7 @@ copy_fields(TMP_TABLE_PARAM *param)
for (; ptr != end; ptr++)
(*ptr->do_copy)(ptr);
- List_iterator_fast<Item> &it=param->copy_funcs_it;
- it.rewind();
+ List_iterator_fast<Item> it(param->copy_funcs);
Item_copy_string *item;
while ((item = (Item_copy_string*) it++))
item->copy();
@@ -8888,7 +8954,8 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab)
if (thd->is_fatal_error)
DBUG_RETURN(TRUE);
- cond->fix_fields(thd,(TABLE_LIST *) 0, (Item**)&cond);
+ if (!cond->fixed)
+ cond->fix_fields(thd,(TABLE_LIST *) 0, (Item**)&cond);
if (join_tab->select)
{
error=(int) cond->add(join_tab->select->cond);
@@ -8944,7 +9011,7 @@ bool JOIN::rollup_init()
return 1;
rollup.ref_pointer_arrays= (Item***) (rollup.fields + send_group_parts);
ref_array= (Item**) (rollup.ref_pointer_arrays+send_group_parts);
- rollup.item_null= new (&thd->mem_root) Item_null();
+ rollup.item_null= new (thd->mem_root) Item_null();
/*
Prepare space for field list for the different levels
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 34eaa7e272d..bbd169d1850 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -354,7 +354,7 @@ class store_key :public Sql_alloc
field_arg->table, field_arg->charset());
else
{
- to_field=field_arg->new_field(&thd->mem_root,field_arg->table);
+ to_field=field_arg->new_field(thd->mem_root,field_arg->table);
if (to_field)
to_field->move_field(ptr, (uchar*) null, 1);
}
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 3bf11c0d6b8..8d741b4dc67 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -19,7 +19,6 @@
#include "mysql_priv.h"
#include "sql_select.h" // For select_describe
-#include "sql_acl.h"
#include "repl_failsafe.h"
#include <my_dir.h>
@@ -752,8 +751,9 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild,
String def(tmp1,sizeof(tmp1), system_charset_info);
type.set(tmp, sizeof(tmp), field->charset());
field->val_str(&type);
+ uint dummy_errors;
def.copy(type.ptr(), type.length(), type.charset(),
- system_charset_info);
+ system_charset_info, &dummy_errors);
protocol->store(def.ptr(), def.length(), def.charset());
}
else if (field->unireg_check == Field::NEXT_NUMBER ||
@@ -817,6 +817,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
DBUG_RETURN(1);
}
+ buffer.length(0);
if (store_create_info(thd, table, &buffer))
DBUG_RETURN(-1);
@@ -830,9 +831,6 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
DBUG_RETURN(1);
protocol->prepare_for_resend();
protocol->store(table->table_name, system_charset_info);
- buffer.length(0);
- if (store_create_info(thd, table, &buffer))
- DBUG_RETURN(-1);
protocol->store(buffer.ptr(), buffer.length(), buffer.charset());
if (protocol->write())
DBUG_RETURN(1);
@@ -1027,7 +1025,8 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list)
/* Check if we have a key part that only uses part of the field */
if (!(key_info->flags & HA_FULLTEXT) && (!key_part->field ||
key_part->length != table->field[key_part->fieldnr-1]->key_length()))
- protocol->store_tiny((longlong) key_part->length);
+ protocol->store_tiny((longlong) key_part->length /
+ key_part->field->charset()->mbmaxlen);
else
protocol->store_null();
protocol->store_null(); // No pack_information yet
@@ -1080,7 +1079,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
restore_record(table,default_values); // Get empty record
if (thd->protocol->send_fields(&field_list,2))
DBUG_VOID_RETURN;
- net_flush(&thd->net);
+ thd->protocol->flush();
DBUG_VOID_RETURN;
}
@@ -1097,13 +1096,11 @@ mysqld_dump_create_info(THD *thd, TABLE *table, int fd)
if (store_create_info(thd, table, packet))
DBUG_RETURN(-1);
- //if (protocol->convert)
- // protocol->convert->convert((char*) packet->ptr(), packet->length());
if (fd < 0)
{
if (protocol->write())
DBUG_RETURN(-1);
- net_flush(&thd->net);
+ protocol->flush();
}
else
{
@@ -1170,6 +1167,15 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
{
uchar chr= (uchar) *name;
length= my_mbcharlen(system_charset_info, chr);
+ /*
+ my_mbcharlen can retur 0 on a wrong multibyte
+ sequence. It is possible when upgrading from 4.0,
+ and identifier contains some accented characters.
+ The manual says it does not work. So we'll just
+ change length to 1 not to hang in the endless loop.
+ */
+ if (!length)
+ length= 1;
if (length == 1 && chr == (uchar) quote_char)
packet->append(&quote_char, 1, system_charset_info);
packet->append(name, length, packet->charset());
@@ -1338,9 +1344,10 @@ store_create_info(THD *thd, TABLE *table, String *packet)
if (type.length())
{
String def_val;
+ uint dummy_errors;
/* convert to system_charset_info == utf8 */
def_val.copy(type.ptr(), type.length(), field->charset(),
- system_charset_info);
+ system_charset_info, &dummy_errors);
append_unescaped(packet, def_val.ptr(), def_val.length());
}
else
@@ -1398,14 +1405,18 @@ store_create_info(THD *thd, TABLE *table, String *packet)
if (!(thd->variables.sql_mode & MODE_NO_KEY_OPTIONS) &&
!limited_mysql_mode && !foreign_db_mode)
{
- if (table->db_type == DB_TYPE_HEAP &&
- key_info->algorithm == HA_KEY_ALG_BTREE)
+ if (key_info->algorithm == HA_KEY_ALG_BTREE)
packet->append(" TYPE BTREE", 11);
+ if (key_info->algorithm == HA_KEY_ALG_HASH)
+ packet->append(" TYPE HASH", 10);
+
// +BAR: send USING only in non-default case: non-spatial rtree
if ((key_info->algorithm == HA_KEY_ALG_RTREE) &&
!(key_info->flags & HA_SPATIAL))
packet->append(" TYPE RTREE", 11);
+
+ // No need to send TYPE FULLTEXT, it is sent as FULLTEXT KEY
}
packet->append(" (", 2);
@@ -2056,7 +2067,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables,
#endif /* HAVE_OPENSSL */
case SHOW_KEY_CACHE_LONG:
case SHOW_KEY_CACHE_CONST_LONG:
- value= (value-(char*) &dflt_key_cache_var)+ (char*) sql_key_cache;
+ value= (value-(char*) &dflt_key_cache_var)+ (char*) dflt_key_cache;
end= int10_to_str(*(long*) value, buff, 10);
break;
case SHOW_UNDEF: // Show never happen
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 1044dc4e58e..c1701e7e9bf 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -121,12 +121,13 @@ bool String::set(ulonglong num, CHARSET_INFO *cs)
bool String::set(double num,uint decimals, CHARSET_INFO *cs)
{
char buff[331];
+ uint dummy_errors;
str_charset=cs;
if (decimals >= NOT_FIXED_DEC)
{
uint32 len= my_sprintf(buff,(buff, "%.14g",num));// Enough for a DATETIME
- return copy(buff, len, &my_charset_latin1, cs);
+ return copy(buff, len, &my_charset_latin1, cs, &dummy_errors);
}
#ifdef HAVE_FCONVERT
int decpt,sign;
@@ -141,7 +142,8 @@ bool String::set(double num,uint decimals, CHARSET_INFO *cs)
buff[0]='-';
pos=buff;
}
- return copy(pos,(uint32) strlen(pos), &my_charset_latin1, cs);
+ uint dummy_errors;
+ return copy(pos,(uint32) strlen(pos), &my_charset_latin1, cs, &dummy_errors);
}
if (alloc((uint32) ((uint32) decpt+3+decimals)))
return TRUE;
@@ -191,7 +193,8 @@ end:
#else
sprintf(buff,"%.*f",(int) decimals,num);
#endif
- return copy(buff,(uint32) strlen(buff), &my_charset_latin1, cs);
+ return copy(buff,(uint32) strlen(buff), &my_charset_latin1, cs,
+ &dummy_errors);
#endif
}
@@ -331,19 +334,24 @@ bool String::set_or_copy_aligned(const char *str,uint32 arg_length,
/* Copy with charset convertion */
bool String::copy(const char *str, uint32 arg_length,
- CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
+ CHARSET_INFO *from_cs, CHARSET_INFO *to_cs, uint *errors)
{
uint32 offset;
if (!needs_conversion(arg_length, from_cs, to_cs, &offset))
+ {
+ *errors= 0;
return copy(str, arg_length, to_cs);
+ }
if ((from_cs == &my_charset_bin) && offset)
+ {
+ *errors= 0;
return copy_aligned(str, arg_length, offset, to_cs);
-
+ }
uint32 new_length= to_cs->mbmaxlen*arg_length;
if (alloc(new_length))
return TRUE;
str_length=copy_and_convert((char*) Ptr, new_length, to_cs,
- str, arg_length, from_cs);
+ str, arg_length, from_cs, errors);
str_charset=to_cs;
return FALSE;
}
@@ -375,7 +383,8 @@ bool String::set_ascii(const char *str, uint32 arg_length)
set(str, arg_length, str_charset);
return 0;
}
- return copy(str, arg_length, &my_charset_latin1, str_charset);
+ uint dummy_errors;
+ return copy(str, arg_length, &my_charset_latin1, str_charset, &dummy_errors);
}
@@ -429,10 +438,12 @@ bool String::append(const char *s,uint32 arg_length)
if (str_charset->mbminlen > 1)
{
uint32 add_length=arg_length * str_charset->mbmaxlen;
+ uint dummy_errors;
if (realloc(str_length+ add_length))
return TRUE;
str_length+= copy_and_convert(Ptr+str_length, add_length, str_charset,
- s, arg_length, &my_charset_latin1);
+ s, arg_length, &my_charset_latin1,
+ &dummy_errors);
return FALSE;
}
@@ -469,10 +480,11 @@ bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs)
if (needs_conversion(arg_length, cs, str_charset, &dummy_offset))
{
uint32 add_length= arg_length / cs->mbminlen * str_charset->mbmaxlen;
+ uint dummy_errors;
if (realloc(str_length + add_length))
return TRUE;
str_length+= copy_and_convert(Ptr+str_length, add_length, str_charset,
- s, arg_length, cs);
+ s, arg_length, cs, &dummy_errors);
}
else
{
@@ -769,7 +781,8 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length)
uint32
copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
- const char *from, uint32 from_length, CHARSET_INFO *from_cs)
+ const char *from, uint32 from_length, CHARSET_INFO *from_cs,
+ uint *errors)
{
int cnvres;
my_wc_t wc;
@@ -780,6 +793,7 @@ copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
const uchar *) = from_cs->cset->mb_wc;
int (*wc_mb)(struct charset_info_st *, my_wc_t, uchar *s, uchar *e)=
to_cs->cset->wc_mb;
+ uint error_count= 0;
while (1)
{
@@ -788,6 +802,7 @@ copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
from+= cnvres;
else if (cnvres == MY_CS_ILSEQ)
{
+ error_count++;
from++;
wc= '?';
}
@@ -799,12 +814,14 @@ outp:
to+= cnvres;
else if (cnvres == MY_CS_ILUNI && wc != '?')
{
+ error_count++;
wc= '?';
goto outp;
}
else
break;
}
+ *errors= error_count;
return (uint32) (to - to_start);
}
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 2d368991159..3ad4689cf36 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -29,7 +29,7 @@ int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
const char *from, uint32 from_length,
- CHARSET_INFO *from_cs);
+ CHARSET_INFO *from_cs, uint *errors);
class String
{
@@ -73,7 +73,7 @@ public:
{ return (void*) alloc_root(mem_root, (uint) size); }
static void operator delete(void *ptr_arg,size_t size)
{}
- static void operator delete(void *ptr_arg,size_t size, MEM_ROOT *mem_root)
+ static void operator delete(void *ptr_arg, MEM_ROOT *mem_root)
{}
~String() { free(); }
@@ -182,6 +182,11 @@ public:
{
if (&s != this)
{
+ /*
+ It is forbidden to do assignments like
+ some_string = substring_of_that_string
+ */
+ DBUG_ASSERT(!s.uses_buffer_owned_by(this));
free();
Ptr=s.Ptr ; str_length=s.str_length ; Alloced_length=s.Alloced_length;
alloced=0;
@@ -199,7 +204,7 @@ public:
CHARSET_INFO *cs);
bool set_or_copy_aligned(const char *s, uint32 arg_length, CHARSET_INFO *cs);
bool copy(const char*s,uint32 arg_length, CHARSET_INFO *csfrom,
- CHARSET_INFO *csto);
+ CHARSET_INFO *csto, uint *errors);
bool append(const String &s);
bool append(const char *s);
bool append(const char *s,uint32 arg_length);
@@ -313,4 +318,9 @@ public:
/* Swap two string objects. Efficient way to exchange data without memcpy. */
void swap(String &s);
+
+ inline bool uses_buffer_owned_by(const String *s) const
+ {
+ return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length);
+ }
};
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 79cec85927e..5f3875ba934 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -29,12 +29,6 @@
#include <io.h>
#endif
-#define tmp_disable_binlog(A) \
- ulong save_options= (A)->options; \
- (A)->options&= ~OPTION_BIN_LOG;
-
-#define reenable_binlog(A) (A)->options= save_options;
-
const char *primary_key_name="PRIMARY";
static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
@@ -42,6 +36,7 @@ static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end);
static int copy_data_between_tables(TABLE *from,TABLE *to,
List<create_field> &create,
enum enum_duplicates handle_duplicates,
+ bool ignore,
uint order_num, ORDER *order,
ha_rows *copied,ha_rows *deleted);
@@ -282,7 +277,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
if (!error)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
- tmp_table_deleted && !some_tables_deleted);
+ tmp_table_deleted && !some_tables_deleted,
+ FALSE);
mysql_bin_log.write(&qinfo);
}
}
@@ -392,6 +388,41 @@ void check_duplicates_in_interval(const char *set_or_name,
}
}
+
+/*
+ Check TYPELIB (set or enum) max and total lengths
+
+ SYNOPSIS
+ calculate_interval_lengths()
+ cs charset+collation pair of the interval
+ typelib list of values for the column
+ max_length length of the longest item
+ tot_length sum of the item lengths
+
+ DESCRIPTION
+ After this function call:
+ - ENUM uses max_length
+ - SET uses tot_length.
+
+ RETURN VALUES
+ void
+*/
+void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
+ uint32 *max_length, uint32 *tot_length)
+{
+ const char **pos;
+ uint *len;
+ *max_length= *tot_length= 0;
+ for (pos= interval->type_names, len= interval->type_lengths;
+ *pos ; pos++, len++)
+ {
+ uint length= cs->cset->numchars(cs, *pos, *pos + *len);
+ *tot_length+= length;
+ set_if_bigger(*max_length, (uint32)length);
+ }
+}
+
+
/*
Preparation for table creation
@@ -455,6 +486,93 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_RETURN(-1);
}
+ if (sql_field->sql_type == FIELD_TYPE_SET ||
+ sql_field->sql_type == FIELD_TYPE_ENUM)
+ {
+ uint32 dummy;
+ CHARSET_INFO *cs= sql_field->charset;
+ TYPELIB *interval= sql_field->interval;
+
+ /*
+ Create typelib from interval_list, and if necessary
+ convert strings from client character set to the
+ column character set.
+ */
+ if (!interval)
+ {
+ interval= sql_field->interval= typelib(sql_field->interval_list);
+ List_iterator<String> it(sql_field->interval_list);
+ String conv, *tmp;
+ for (uint i= 0; (tmp= it++); i++)
+ {
+ if (String::needs_conversion(tmp->length(), tmp->charset(),
+ cs, &dummy))
+ {
+ uint cnv_errs;
+ conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
+ char *buf= (char*) sql_alloc(conv.length()+1);
+ memcpy(buf, conv.ptr(), conv.length());
+ buf[conv.length()]= '\0';
+ interval->type_names[i]= buf;
+ interval->type_lengths[i]= conv.length();
+ }
+
+ // Strip trailing spaces.
+ uint lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
+ interval->type_lengths[i]);
+ interval->type_lengths[i]= lengthsp;
+ ((uchar *)interval->type_names[i])[lengthsp]= '\0';
+ }
+ sql_field->interval_list.empty(); // Don't need interval_list anymore
+ }
+
+ /*
+ Convert the default value from client character
+ set into the column character set if necessary.
+ */
+ if (sql_field->def)
+ {
+ sql_field->def=
+ sql_field->def->safe_charset_converter(cs);
+ }
+
+ if (sql_field->sql_type == FIELD_TYPE_SET)
+ {
+ if (sql_field->def)
+ {
+ char *not_used;
+ uint not_used2;
+ bool not_found= 0;
+ String str, *def= sql_field->def->val_str(&str);
+ def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
+ (void) find_set(interval, def->ptr(), def->length(),
+ cs, &not_used, &not_used2, &not_found);
+ if (not_found)
+ {
+ my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ calculate_interval_lengths(cs, interval, &dummy, &sql_field->length);
+ sql_field->length+= (interval->count - 1);
+ }
+ else /* FIELD_TYPE_ENUM */
+ {
+ if (sql_field->def)
+ {
+ String str, *def= sql_field->def->val_str(&str);
+ def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
+ if (!find_type2(interval, def->ptr(), def->length(), cs))
+ {
+ my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ calculate_interval_lengths(cs, interval, &sql_field->length, &dummy);
+ }
+ set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
+ }
+
sql_field->create_length_to_internal_length();
/* Don't pack keys in old tables if the user has requested this */
@@ -814,8 +932,7 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_RETURN(-1);
}
}
- else
- if (key_info->algorithm == HA_KEY_ALG_RTREE)
+ else if (key_info->algorithm == HA_KEY_ALG_RTREE)
{
#ifdef HAVE_RTREE_KEYS
if ((key_info->key_parts & 1) == 1)
@@ -835,10 +952,12 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
#endif
}
- List_iterator<key_part_spec> cols(key->columns);
+ List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
CHARSET_INFO *ft_key_charset=0; // for FULLTEXT
for (uint column_nr=0 ; (column=cols++) ; column_nr++)
{
+ key_part_spec *dup_column;
+
it.rewind();
field=0;
while ((sql_field=it++) &&
@@ -853,12 +972,18 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
column->field_name);
DBUG_RETURN(-1);
}
- /* for fulltext keys keyseg length is 1 for blobs (it's ignored in
- ft code anyway, and 0 (set to column width later) for char's.
- it has to be correct col width for char's, as char data are not
- prefixed with length (unlike blobs, where ft code takes data length
- from a data prefix, ignoring column->length).
- */
+ while ((dup_column= cols2++) != column)
+ {
+ if (!my_strcasecmp(system_charset_info,
+ column->field_name, dup_column->field_name))
+ {
+ my_printf_error(ER_DUP_FIELDNAME,
+ ER(ER_DUP_FIELDNAME),MYF(0),
+ column->field_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ cols2.rewind();
if (key->type == Key::FULLTEXT)
{
if ((sql_field->sql_type != FIELD_TYPE_STRING &&
@@ -1292,7 +1417,8 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
test(create_info->options &
- HA_LEX_CREATE_TMP_TABLE));
+ HA_LEX_CREATE_TMP_TABLE),
+ FALSE);
mysql_bin_log.write(&qinfo);
}
}
@@ -1418,7 +1544,6 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
}
table->file->extra(HA_EXTRA_WRITE_CACHE);
DBUG_RETURN(table);
- /* Note that leaving the function resets binlogging properties */
}
@@ -1734,6 +1859,12 @@ end:
}
+/*
+ RETURN VALUES
+ 0 Message sent to net (admin operation went ok)
+ -1 Message should be sent by caller
+ (admin operation or network communication failed)
+*/
static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
HA_CHECK_OPT* check_opt,
const char *operator_name,
@@ -1766,9 +1897,9 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
for (table = tables; table; table = table->next)
{
char table_name[NAME_LEN*2+2];
- char* db = (table->db) ? table->db : thd->db;
+ char* db = table->db;
bool fatal_error=0;
- strxmov(table_name,db ? db : "",".",table->real_name,NullS);
+ strxmov(table_name, db, ".", table->real_name, NullS);
thd->open_options|= extra_open_options;
table->table = open_ltable(thd, table, lock_type);
@@ -1780,9 +1911,13 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
if (prepare_func)
{
switch ((*prepare_func)(thd, table, check_opt)) {
- case 1: continue; // error, message written to net
- case -1: goto err; // error, message could be written to net
- default: ; // should be 0 otherwise
+ case 1: // error, message written to net
+ close_thread_tables(thd);
+ continue;
+ case -1: // error, message could be written to net
+ goto err;
+ default: // should be 0 otherwise
+ ;
}
}
@@ -2139,6 +2274,8 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table,
{
strxmov(src_path, mysql_data_home, "/", src_db, "/", src_table,
reg_ext, NullS);
+ /* Resolve symlinks (for windows) */
+ fn_format(src_path, src_path, "", "", MYF(MY_UNPACK_FILENAME));
if (access(src_path, F_OK))
{
my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table);
@@ -2167,6 +2304,7 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table,
{
strxmov(dst_path, mysql_data_home, "/", db, "/", table_name,
reg_ext, NullS);
+ fn_format(dst_path, dst_path, "", "", MYF(MY_UNPACK_FILENAME));
if (!access(dst_path, F_OK))
goto table_exists;
}
@@ -2208,7 +2346,8 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table,
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
test(create_info->options &
- HA_LEX_CREATE_TMP_TABLE));
+ HA_LEX_CREATE_TMP_TABLE),
+ FALSE);
mysql_bin_log.write(&qinfo);
}
res= 0;
@@ -2319,7 +2458,7 @@ mysql_discard_or_import_tablespace(THD *thd,
mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
err:
@@ -2330,7 +2469,11 @@ err:
send_ok(thd);
DBUG_RETURN(0);
}
- DBUG_RETURN(error);
+
+ if (error == HA_ERR_ROW_IS_REFERENCED)
+ my_error(ER_ROW_IS_REFERENCED, MYF(0));
+
+ DBUG_RETURN(-1);
}
@@ -2547,7 +2690,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
TABLE_LIST *table_list,
List<create_field> &fields, List<Key> &keys,
uint order_num, ORDER *order,
- enum enum_duplicates handle_duplicates,
+ enum enum_duplicates handle_duplicates, bool ignore,
ALTER_INFO *alter_info, bool do_send_ok)
{
TABLE *table,*new_table;
@@ -2706,7 +2849,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (mysql_bin_log.is_open())
{
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
if (do_send_ok)
@@ -3066,7 +3209,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
copied=deleted=0;
if (!new_table->is_view)
error=copy_data_between_tables(table,new_table,create_list,
- handle_duplicates,
+ handle_duplicates, ignore,
order_num, order, &copied, &deleted);
thd->last_insert_id=next_insert_id; // Needed for correct log
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
@@ -3091,7 +3234,8 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
/* Remove link to old table and rename the new one */
close_temporary_table(thd,table->table_cache_key,table_name);
- if (rename_temporary_table(thd, new_table, new_db, new_alias))
+ /* Should pass the 'new_name' as we store table name in the cache */
+ if (rename_temporary_table(thd, new_table, new_db, new_name))
{ // Fatal error
close_temporary_table(thd,new_db,tmp_name);
my_free((gptr) new_table,MYF(0));
@@ -3101,7 +3245,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (mysql_bin_log.is_open())
{
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
goto end_temporary;
@@ -3236,7 +3380,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (mysql_bin_log.is_open())
{
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
}
VOID(pthread_cond_broadcast(&COND_refresh));
@@ -3286,6 +3430,7 @@ static int
copy_data_between_tables(TABLE *from,TABLE *to,
List<create_field> &create,
enum enum_duplicates handle_duplicates,
+ bool ignore,
uint order_num, ORDER *order,
ha_rows *copied,
ha_rows *deleted)
@@ -3305,6 +3450,16 @@ copy_data_between_tables(TABLE *from,TABLE *to,
ulong save_sql_mode;
DBUG_ENTER("copy_data_between_tables");
+ /*
+ Turn off recovery logging since rollback of an alter table is to
+ delete the new table so there is no need to log the changes to it.
+
+ This needs to be done before external_lock
+ */
+ error= ha_enable_transaction(thd, FALSE);
+ if (error)
+ DBUG_RETURN(-1);
+
if (!(copy= new Copy_field[to->fields]))
DBUG_RETURN(-1); /* purecov: inspected */
@@ -3363,23 +3518,12 @@ copy_data_between_tables(TABLE *from,TABLE *to,
goto err;
};
- /*
- Turn off recovery logging since rollback of an alter table is to
- delete the new table so there is no need to log the changes to it.
- */
- error= ha_enable_transaction(thd,FALSE);
- if (error)
- {
- error= 1;
- goto err;
- }
-
/* Handler must be told explicitly to retrieve all columns, because
this function does not set field->query_id in the columns to the
current query id */
from->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
- if (handle_duplicates == DUP_IGNORE ||
+ if (ignore ||
handle_duplicates == DUP_REPLACE)
to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
thd->row_count= 0;
@@ -3405,7 +3549,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
}
if ((error=to->file->write_row((byte*) to->record[0])))
{
- if ((handle_duplicates != DUP_IGNORE &&
+ if ((!ignore &&
handle_duplicates != DUP_REPLACE) ||
(error != HA_ERR_FOUND_DUPP_KEY &&
error != HA_ERR_FOUND_DUPP_UNIQUE))
@@ -3481,7 +3625,7 @@ int mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
table_list, lex->create_list,
lex->key_list, 0, (ORDER *) 0,
- DUP_ERROR, &lex->alter_info, do_send_ok));
+ DUP_ERROR, 0, &lex->alter_info, do_send_ok));
}
diff --git a/sql/sql_udf.h b/sql/sql_udf.h
index 7b10b80f148..d1f99a6d232 100644
--- a/sql/sql_udf.h
+++ b/sql/sql_udf.h
@@ -56,8 +56,9 @@ class udf_handler :public Sql_alloc
public:
table_map used_tables_cache;
bool const_item_cache;
+ bool not_original;
udf_handler(udf_func *udf_arg) :u_d(udf_arg), buffers(0), error(0),
- is_null(0), initialized(0)
+ is_null(0), initialized(0), not_original(0)
{}
~udf_handler();
const char *name() const { return u_d ? u_d->name.str : "?"; }
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 0be554f58a2..882316d57d7 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -45,10 +45,10 @@ select_union::select_union(TABLE *table_par)
{
bzero((char*) &info,sizeof(info));
/*
- We can always use DUP_IGNORE because the temporary table will only
+ We can always use IGNORE because the temporary table will only
contain a unique key if we are using not using UNION ALL
*/
- info.handle_duplicates= DUP_IGNORE;
+ info.ignore= 1;
}
select_union::~select_union()
@@ -148,6 +148,7 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
SELECT_LEX *sl, *first_select;
select_result *tmp_result;
bool is_union;
+ TABLE *empty_table= 0;
DBUG_ENTER("st_select_lex_unit::prepare");
describe= test(additional_options & SELECT_DESCRIBE);
@@ -239,13 +240,21 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
goto err;
if (sl == first_select)
{
+ /*
+ We need to create an empty table object. It is used
+ to create tmp_table fields in Item_type_holder.
+ The main reason of this is that we can't create
+ field object without table.
+ */
+ DBUG_ASSERT(!empty_table);
+ empty_table= (TABLE*) thd->calloc(sizeof(TABLE));
types.empty();
List_iterator_fast<Item> it(sl->item_list);
Item *item_tmp;
while ((item_tmp= it++))
{
/* Error's in 'new' will be detected after loop */
- types.push_back(new Item_type_holder(thd_arg, item_tmp));
+ types.push_back(new Item_type_holder(thd_arg, item_tmp, empty_table));
}
if (thd_arg->is_fatal_error)
@@ -264,7 +273,8 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
Item *type, *item_tmp;
while ((type= tp++, item_tmp= it++))
{
- if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp))
+ if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp,
+ empty_table))
DBUG_RETURN(-1);
}
}
@@ -313,24 +323,24 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
We're in statement prepare or in execution
of a conventional statement.
*/
- Item_arena backup;
- if (arena->is_stmt_prepare())
- thd->set_n_backup_item_arena(arena, &backup);
+ Item_arena *tmp_arena,backup;
+ tmp_arena= thd->change_arena_if_needed(&backup);
+
Field **field;
for (field= table->field; *field; field++)
{
Item_field *item= new Item_field(*field);
if (!item || item_list.push_back(item))
{
- if (arena->is_stmt_prepare())
- thd->restore_backup_item_arena(arena, &backup);
+ if (tmp_arena)
+ thd->restore_backup_item_arena(tmp_arena, &backup);
DBUG_RETURN(-1);
}
}
+ if (tmp_arena)
+ thd->restore_backup_item_arena(tmp_arena, &backup);
if (arena->is_stmt_prepare())
{
- thd->restore_backup_item_arena(arena, &backup);
-
/* prepare fake select to initialize it correctly */
ulong options_tmp= init_prepare_fake_select_lex(thd);
if (!(fake_select_lex->join= new JOIN(thd, item_list, thd->options,
@@ -394,6 +404,8 @@ int st_select_lex_unit::exec()
if (uncacheable || !item || !item->assigned() || describe)
{
+ if (item)
+ item->reset_value_registration();
if (optimized && item)
{
if (item->assigned())
@@ -464,11 +476,14 @@ int st_select_lex_unit::exec()
}
res= sl->join->error;
offset_limit_cnt= sl->offset_limit;
- if (!res && union_result->flush())
+ if (!res)
{
- examined_rows+= thd->examined_row_count;
- thd->lex->current_select= lex_select_save;
- DBUG_RETURN(1);
+ examined_rows+= thd->examined_row_count;
+ if (union_result->flush())
+ {
+ thd->lex->current_select= lex_select_save;
+ DBUG_RETURN(1);
+ }
}
}
if (res)
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index d3597f274dc..6c12381b4bd 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -21,7 +21,6 @@
*/
#include "mysql_priv.h"
-#include "sql_acl.h"
#include "sql_select.h"
static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields);
@@ -55,9 +54,10 @@ int mysql_update(THD *thd,
COND *conds,
uint order_num, ORDER *order,
ha_rows limit,
- enum enum_duplicates handle_duplicates)
+ enum enum_duplicates handle_duplicates,
+ bool ignore)
{
- bool using_limit=limit != HA_POS_ERROR;
+ bool using_limit=limit != HA_POS_ERROR;
bool safe_update= thd->options & OPTION_SAFE_UPDATES;
bool used_key_is_modified, transactional_table, log_delayed;
int error=0;
@@ -71,11 +71,10 @@ int mysql_update(THD *thd,
TABLE *table;
SQL_SELECT *select;
READ_RECORD info;
- TABLE_LIST *update_table_list= ((TABLE_LIST*)
+ TABLE_LIST *update_table_list= ((TABLE_LIST*)
thd->lex->select_lex.table_list.first);
DBUG_ENTER("mysql_update");
- LINT_INIT(used_index);
LINT_INIT(timestamp_query_id);
if ((open_and_lock_tables(thd, table_list)))
@@ -125,7 +124,7 @@ int mysql_update(THD *thd,
/* Check values */
table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege);
#endif
- if (setup_fields(thd, 0, update_table_list, values, 0, 0, 0))
+ if (setup_fields(thd, 0, update_table_list, values, 1, 0, 0))
{
free_underlaid_joins(thd, &thd->lex->select_lex);
DBUG_RETURN(-1); /* purecov: inspected */
@@ -160,14 +159,18 @@ int mysql_update(THD *thd,
init_ftfuncs(thd, &thd->lex->select_lex, 1);
/* Check if we are modifying a key that we are used to search with */
if (select && select->quick)
+ {
+ used_index=select->quick->index;
used_key_is_modified= (!select->quick->unique_key_range() &&
- check_if_key_used(table,
- (used_index=select->quick->index),
- fields));
+ check_if_key_used(table, used_index, fields));
+ }
else if ((used_index=table->file->key_used_on_scan) < MAX_KEY)
used_key_is_modified=check_if_key_used(table, used_index, fields);
else
+ {
used_key_is_modified=0;
+ used_index= MAX_KEY;
+ }
if (used_key_is_modified || order)
{
/*
@@ -175,7 +178,7 @@ int mysql_update(THD *thd,
matching rows before updating the table!
*/
table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (old_used_keys.is_set(used_index))
+ if (used_index < MAX_KEY && old_used_keys.is_set(used_index))
{
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
@@ -275,7 +278,7 @@ int mysql_update(THD *thd,
}
}
- if (handle_duplicates == DUP_IGNORE)
+ if (ignore)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
init_read_record(&info,thd,table,select,0,1);
@@ -300,8 +303,7 @@ int mysql_update(THD *thd,
{
updated++;
}
- else if (handle_duplicates != DUP_IGNORE ||
- error != HA_ERR_FOUND_DUPP_KEY)
+ else if (!ignore || error != HA_ERR_FOUND_DUPP_KEY)
{
thd->fatal_error(); // Force error message
table->file->print_error(error,MYF(0));
@@ -344,7 +346,7 @@ int mysql_update(THD *thd,
if (error <= 0)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
- log_delayed);
+ log_delayed, FALSE);
if (mysql_bin_log.write(&qinfo) && transactional_table)
error=1; // Rollback update
}
@@ -466,30 +468,23 @@ static table_map get_table_map(List<Item> *items)
}
-
/*
- Setup multi-update handling and call SELECT to do the join
+ Prepare tables for multi-update
+ Analyse which tables need specific privileges and perform locking
+ as required
*/
-int mysql_multi_update(THD *thd,
- TABLE_LIST *table_list,
- List<Item> *fields,
- List<Item> *values,
- COND *conds,
- ulong options,
- enum enum_duplicates handle_duplicates,
- SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex)
+int mysql_multi_update_lock(THD *thd,
+ TABLE_LIST *table_list,
+ List<Item> *fields,
+ SELECT_LEX *select_lex)
{
int res;
- multi_update *result;
TABLE_LIST *tl;
TABLE_LIST *update_list= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
- List<Item> total_list;
const bool using_lock_tables= thd->locked_tables != 0;
bool initialized_dervied= 0;
- DBUG_ENTER("mysql_multi_update");
-
- select_lex->select_limit= HA_POS_ERROR;
+ DBUG_ENTER("mysql_multi_update_lock");
/*
The following loop is here to to ensure that we only lock tables
@@ -543,8 +538,8 @@ int mysql_multi_update(THD *thd,
/* Unlock the tables in preparation for relocking */
if (!using_lock_tables)
- {
- mysql_unlock_tables(thd, thd->lock);
+ {
+ mysql_unlock_tables(thd, thd->lock);
thd->lock= 0;
}
@@ -554,7 +549,9 @@ int mysql_multi_update(THD *thd,
*/
for (tl= update_list; tl; tl= tl->next)
{
+ TABLE_LIST *save= tl->next;
TABLE *table= tl->table;
+ uint wants;
/* if table will be updated then check that it is unique */
if (table->map & update_tables)
{
@@ -572,17 +569,34 @@ int mysql_multi_update(THD *thd,
DBUG_PRINT("info",("setting table `%s` for update", tl->alias));
tl->lock_type= thd->lex->multi_lock_option;
tl->updating= 1;
+ wants= UPDATE_ACL;
}
else
{
DBUG_PRINT("info",("setting table `%s` for read-only", tl->alias));
- tl->lock_type= TL_READ;
+ // If we are using the binary log, we need TL_READ_NO_INSERT to get
+ // correct order of statements. Otherwise, we use a TL_READ lock to
+ // improve performance.
+ tl->lock_type= using_update_log ? TL_READ_NO_INSERT : TL_READ;
tl->updating= 0;
+ wants= SELECT_ACL;
}
+
if (tl->derived)
derived_tables|= table->map;
- else if (!using_lock_tables)
- tl->table->reginfo.lock_type= tl->lock_type;
+ else
+ {
+ tl->next= 0;
+ if (!using_lock_tables)
+ tl->table->reginfo.lock_type= tl->lock_type;
+ if (check_access(thd, wants, tl->db, &tl->grant.privilege, 0, 0) ||
+ (grant_option && check_grant(thd, wants, tl, 0, 0, 0)))
+ {
+ tl->next= save;
+ DBUG_RETURN(1);
+ }
+ tl->next= save;
+ }
}
if (thd->lex->derived_tables && (update_tables & derived_tables))
@@ -602,11 +616,7 @@ int mysql_multi_update(THD *thd,
/* Relock the tables with the correct modes */
res= lock_tables(thd, table_list, table_count);
if (using_lock_tables)
- {
- if (res)
- DBUG_RETURN(res);
break; // Don't have to do setup_field()
- }
/*
We must setup fields again as the file may have been reopened
@@ -637,6 +647,32 @@ int mysql_multi_update(THD *thd,
*/
close_thread_tables(thd);
}
+
+ DBUG_RETURN(res);
+}
+
+/*
+ Setup multi-update handling and call SELECT to do the join
+*/
+
+int mysql_multi_update(THD *thd,
+ TABLE_LIST *table_list,
+ List<Item> *fields,
+ List<Item> *values,
+ COND *conds,
+ ulong options,
+ enum enum_duplicates handle_duplicates, bool ignore,
+ SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex)
+{
+ int res;
+ TABLE_LIST *tl;
+ TABLE_LIST *update_list= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
+ List<Item> total_list;
+ multi_update *result;
+ DBUG_ENTER("mysql_multi_update");
+
+ if ((res= mysql_multi_update_lock(thd, table_list, fields, select_lex)))
+ DBUG_RETURN(res);
/* Setup timestamp handling */
for (tl= update_list; tl; tl= tl->next)
@@ -652,7 +688,7 @@ int mysql_multi_update(THD *thd,
}
if (!(result=new multi_update(thd, update_list, fields, values,
- handle_duplicates)))
+ handle_duplicates, ignore)))
DBUG_RETURN(-1);
res= mysql_select(thd, &select_lex->ref_pointer_array,
@@ -669,11 +705,11 @@ int mysql_multi_update(THD *thd,
multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list,
List<Item> *field_list, List<Item> *value_list,
- enum enum_duplicates handle_duplicates_arg)
+ enum enum_duplicates handle_duplicates_arg, bool ignore_arg)
:all_tables(table_list), update_tables(0), thd(thd_arg), tmp_tables(0),
updated(0), found(0), fields(field_list), values(value_list),
table_count(0), copy_field(0), handle_duplicates(handle_duplicates_arg),
- do_update(1), trans_safe(0), transactional_tables(1)
+ do_update(1), trans_safe(0), transactional_tables(1), ignore(ignore_arg)
{}
@@ -1006,8 +1042,7 @@ bool multi_update::send_data(List<Item> &not_used_values)
table->record[0])))
{
updated--;
- if (handle_duplicates != DUP_IGNORE ||
- error != HA_ERR_FOUND_DUPP_KEY)
+ if (!ignore || error != HA_ERR_FOUND_DUPP_KEY)
{
thd->fatal_error(); // Force error message
table->file->print_error(error,MYF(0));
@@ -1139,8 +1174,7 @@ int multi_update::do_updates(bool from_send_error)
if ((local_error=table->file->update_row(table->record[1],
table->record[0])))
{
- if (local_error != HA_ERR_FOUND_DUPP_KEY ||
- handle_duplicates != DUP_IGNORE)
+ if (!ignore || local_error != HA_ERR_FOUND_DUPP_KEY)
goto err;
}
updated++;
@@ -1221,7 +1255,7 @@ bool multi_update::send_eof()
if (local_error <= 0)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
- log_delayed);
+ log_delayed, FALSE);
if (mysql_bin_log.write(&qinfo) && trans_safe)
local_error= 1; // Rollback update
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 142f52b87ab..9f25e17b6fd 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -32,7 +32,6 @@
#define Select Lex->current_select
#include "mysql_priv.h"
#include "slave.h"
-#include "sql_acl.h"
#include "lex_symbol.h"
#include "item_create.h"
#include <myisam.h>
@@ -131,6 +130,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token CLIENT_SYM
%token COMMENT_SYM
%token COMMIT_SYM
+%token CONSISTENT_SYM
%token COUNT_SYM
%token CREATE
%token CROSS
@@ -165,6 +165,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SELECT_SYM
%token SHOW
%token SLAVE
+%token SNAPSHOT_SYM
%token SQL_THREAD
%token START_SYM
%token STD_SYM
@@ -607,7 +608,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <simple_string>
remember_name remember_end opt_ident opt_db text_or_password
- opt_constraint constraint
+ opt_constraint constraint ident_or_empty
%type <string>
text_string opt_gconcat_separator
@@ -618,6 +619,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
table_option opt_if_not_exists opt_no_write_to_binlog opt_var_type
opt_var_ident_type delete_option opt_temporary all_or_any opt_distinct
opt_ignore_leaves fulltext_options spatial_type union_option
+ start_transaction_opts
%type <ulong_num>
ULONG_NUM raid_types merge_insert_types
@@ -1109,7 +1111,7 @@ create_select:
SELECT_SYM
{
LEX *lex=Lex;
- lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ;
+ lex->lock_option= using_update_log ? TL_READ_NO_INSERT : TL_READ;
if (lex->sql_command == SQLCOM_INSERT)
lex->sql_command= SQLCOM_INSERT_SELECT;
else if (lex->sql_command == SQLCOM_REPLACE)
@@ -1358,7 +1360,7 @@ field_spec:
field_ident
{
LEX *lex=Lex;
- lex->length=lex->dec=0; lex->type=0; lex->interval=0;
+ lex->length=lex->dec=0; lex->type=0;
lex->default_value= lex->on_update_value= 0;
lex->comment=0;
lex->charset=NULL;
@@ -1371,7 +1373,7 @@ field_spec:
lex->length,lex->dec,lex->type,
lex->default_value, lex->on_update_value,
lex->comment,
- lex->change,lex->interval,lex->charset,
+ lex->change,&lex->interval_list,lex->charset,
lex->uint_geom_type))
YYABORT;
};
@@ -1399,6 +1401,9 @@ type:
| BINARY '(' NUM ')' { Lex->length=$3.str;
Lex->charset=&my_charset_bin;
$$=FIELD_TYPE_STRING; }
+ | BINARY { Lex->length= (char*) "1";
+ Lex->charset=&my_charset_bin;
+ $$=FIELD_TYPE_STRING; }
| varchar '(' NUM ')' opt_binary { Lex->length=$3.str;
$$=FIELD_TYPE_VAR_STRING; }
| nvarchar '(' NUM ')' { Lex->length=$3.str;
@@ -1410,7 +1415,7 @@ type:
| YEAR_SYM opt_len field_options { $$=FIELD_TYPE_YEAR; }
| DATE_SYM { $$=FIELD_TYPE_DATE; }
| TIME_SYM { $$=FIELD_TYPE_TIME; }
- | TIMESTAMP
+ | TIMESTAMP opt_len
{
if (YYTHD->variables.sql_mode & MODE_MAXDB)
$$=FIELD_TYPE_DATETIME;
@@ -1423,13 +1428,6 @@ type:
$$=FIELD_TYPE_TIMESTAMP;
}
}
- | TIMESTAMP '(' NUM ')'
- {
- LEX *lex= Lex;
- lex->length= $3.str;
- lex->type|= NOT_NULL_FLAG;
- $$= FIELD_TYPE_TIMESTAMP;
- }
| DATETIME { $$=FIELD_TYPE_DATETIME; }
| TINYBLOB { Lex->charset=&my_charset_bin;
$$=FIELD_TYPE_TINY_BLOB; }
@@ -1465,17 +1463,9 @@ type:
| FIXED_SYM float_options field_options
{ $$=FIELD_TYPE_DECIMAL;}
| ENUM {Lex->interval_list.empty();} '(' string_list ')' opt_binary
- {
- LEX *lex=Lex;
- lex->interval=typelib(lex->interval_list);
- $$=FIELD_TYPE_ENUM;
- }
+ { $$=FIELD_TYPE_ENUM; }
| SET { Lex->interval_list.empty();} '(' string_list ')' opt_binary
- {
- LEX *lex=Lex;
- lex->interval=typelib(lex->interval_list);
- $$=FIELD_TYPE_SET;
- }
+ { $$=FIELD_TYPE_SET; }
| LONG_SYM opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; }
| SERIAL_SYM
{
@@ -1852,8 +1842,9 @@ alter:
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
- lex->sql_command = SQLCOM_ALTER_TABLE;
- lex->name=0;
+ lex->sql_command= SQLCOM_ALTER_TABLE;
+ lex->name= 0;
+ lex->duplicates= DUP_ERROR;
if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
TL_OPTION_UPDATING))
YYABORT;
@@ -1872,7 +1863,7 @@ alter:
}
alter_list
{}
- | ALTER DATABASE ident
+ | ALTER DATABASE ident_or_empty
{
Lex->create_info.default_table_charset= NULL;
Lex->create_info.used_fields= 0;
@@ -1881,10 +1872,15 @@ alter:
{
LEX *lex=Lex;
lex->sql_command=SQLCOM_ALTER_DB;
- lex->name=$3.str;
+ lex->name= $3;
};
+ident_or_empty:
+ /* empty */ { $$= 0; }
+ | ident { $$= $1.str; };
+
+
alter_list:
| DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; }
| IMPORT TABLESPACE { Lex->alter_info.tablespace_op= IMPORT_TABLESPACE; }
@@ -1919,7 +1915,7 @@ alter_list_item:
| MODIFY_SYM opt_column field_ident
{
LEX *lex=Lex;
- lex->length=lex->dec=0; lex->type=0; lex->interval=0;
+ lex->length=lex->dec=0; lex->type=0;
lex->default_value= lex->on_update_value= 0;
lex->comment=0;
lex->charset= NULL;
@@ -1934,7 +1930,7 @@ alter_list_item:
lex->length,lex->dec,lex->type,
lex->default_value, lex->on_update_value,
lex->comment,
- $3.str, lex->interval, lex->charset,
+ $3.str, &lex->interval_list, lex->charset,
lex->uint_geom_type))
YYABORT;
}
@@ -2033,8 +2029,9 @@ opt_column:
| COLUMN_SYM {};
opt_ignore:
- /* empty */ { Lex->duplicates=DUP_ERROR; }
- | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; };
+ /* empty */ { Lex->ignore= 0;}
+ | IGNORE_SYM { Lex->ignore= 1;}
+ ;
opt_restrict:
/* empty */ {}
@@ -2095,10 +2092,21 @@ slave:
start:
- START_SYM TRANSACTION_SYM { Lex->sql_command = SQLCOM_BEGIN;}
- {}
+ START_SYM TRANSACTION_SYM start_transaction_opts
+ {
+ Lex->sql_command = SQLCOM_BEGIN;
+ Lex->start_transaction_opt= $3;
+ }
;
+start_transaction_opts:
+ /*empty*/ { $$ = 0; }
+ | WITH CONSISTENT_SYM SNAPSHOT_SYM
+ {
+ $$= MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT;
+ }
+ ;
+
slave_thread_opts:
{ Lex->slave_thd_opt= 0; }
slave_thread_opt_list
@@ -2381,7 +2389,10 @@ select:
select_init:
SELECT_SYM select_init2
|
- '(' SELECT_SYM select_part2 ')'
+ '(' select_paren ')' union_opt;
+
+select_paren:
+ SELECT_SYM select_part2
{
LEX *lex= Lex;
SELECT_LEX * sel= lex->current_select;
@@ -2400,7 +2411,8 @@ select_init:
if (sel->master_unit()->fake_select_lex)
sel->master_unit()->global_parameters=
sel->master_unit()->fake_select_lex;
- } union_opt;
+ }
+ | '(' select_paren ')';
select_init2:
select_part2
@@ -2447,10 +2459,11 @@ select_into:
select_from:
FROM join_table_list where_clause group_clause having_clause
opt_order_clause opt_limit_clause procedure_clause
- | FROM DUAL_SYM /* oracle compatibility: oracle always requires FROM
- clause, and DUAL is system table without fields.
- Is "SELECT 1 FROM DUAL" any better than
- "SELECT 1" ? Hmmm :) */
+ | FROM DUAL_SYM opt_limit_clause
+ /* oracle compatibility: oracle always requires FROM clause,
+ and DUAL is system table without fields.
+ Is "SELECT 1 FROM DUAL" any better than "SELECT 1" ?
+ Hmmm :) */
;
select_options:
@@ -2889,7 +2902,7 @@ simple_expr:
| CONCAT '(' expr_list ')'
{ $$= new Item_func_concat(* $3); }
| CONCAT_WS '(' expr ',' expr_list ')'
- { $$= new Item_func_concat_ws($3, *$5); }
+ { $5->push_front($3); $$= new Item_func_concat_ws(*$5); }
| CONVERT_TZ_SYM '(' expr ',' expr ',' expr ')'
{
Lex->time_zone_tables_used= &fake_time_zone_tables_list;
@@ -3297,7 +3310,7 @@ opt_distinct:
|DISTINCT { $$ = 1; };
opt_gconcat_separator:
- /* empty */ { $$ = new (&YYTHD->mem_root) String(",",1,default_charset_info); }
+ /* empty */ { $$ = new (YYTHD->mem_root) String(",",1,default_charset_info); }
|SEPARATOR_SYM text_string { $$ = $2; };
@@ -3395,8 +3408,7 @@ when_list2:
};
join_table_list:
- '(' join_table_list ')' { $$=$2; }
- | join_table { $$=$1; }
+ join_table { $$=$1; }
| join_table_list ',' join_table_list { $$=$3; }
| join_table_list normal_join join_table_list { $$=$3; }
| join_table_list STRAIGHT_JOIN join_table_list
@@ -3473,7 +3485,7 @@ join_table:
}
| '{' ident join_table LEFT OUTER JOIN_SYM join_table ON expr '}'
{ add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; }
- | '(' SELECT_SYM select_derived ')' opt_table_alias
+ | '(' select_derived union_opt ')' opt_table_alias
{
LEX *lex=Lex;
SELECT_LEX_UNIT *unit= lex->current_select->master_unit();
@@ -3484,9 +3496,27 @@ join_table:
(List<String> *)0)))
YYABORT;
- };
+ }
+ | '(' join_table_list ')' { $$=$2; };
select_derived:
+ SELECT_SYM select_derived2
+ | '(' select_derived ')'
+ {
+ LEX *lex= Lex;
+ SELECT_LEX * sel= lex->current_select;
+ if (sel->set_braces(1))
+ {
+ yyerror(ER(ER_SYNTAX_ERROR));
+ YYABORT;
+ }
+ /* select in braces, can't contain global parameters */
+ if (sel->master_unit()->fake_select_lex)
+ sel->master_unit()->global_parameters=
+ sel->master_unit()->fake_select_lex;
+ };
+
+select_derived2:
{
LEX *lex= Lex;
lex->derived_tables= 1;
@@ -3508,7 +3538,7 @@ select_derived:
{
Select->parsing_place= NO_MATTER;
}
- opt_select_from union_opt
+ opt_select_from
;
opt_outer:
@@ -3551,15 +3581,15 @@ key_list_or_empty:
key_usage_list2:
key_usage_list2 ',' ident
{ Select->
- interval_list.push_back(new (&YYTHD->mem_root) String((const char*) $3.str, $3.length,
+ interval_list.push_back(new (YYTHD->mem_root) String((const char*) $3.str, $3.length,
system_charset_info)); }
| ident
{ Select->
- interval_list.push_back(new (&YYTHD->mem_root) String((const char*) $1.str, $1.length,
+ interval_list.push_back(new (YYTHD->mem_root) String((const char*) $1.str, $1.length,
system_charset_info)); }
| PRIMARY_SYM
{ Select->
- interval_list.push_back(new (&YYTHD->mem_root) String("PRIMARY", 7,
+ interval_list.push_back(new (YYTHD->mem_root) String("PRIMARY", 7,
system_charset_info)); };
using_list:
@@ -3915,11 +3945,12 @@ do: DO_SYM
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_DO;
- if (!(lex->insert_list = new List_item))
- YYABORT;
+ mysql_init_select(lex);
+ }
+ expr_list
+ {
+ Lex->insert_list= $3;
}
- values
- {}
;
/*
@@ -3998,7 +4029,9 @@ insert:
INSERT
{
LEX *lex= Lex;
- lex->sql_command = SQLCOM_INSERT;
+ lex->sql_command= SQLCOM_INSERT;
+ lex->duplicates= DUP_ERROR;
+ mysql_init_select(lex);
/* for subselects */
lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ;
lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
@@ -4018,6 +4051,7 @@ replace:
LEX *lex=Lex;
lex->sql_command = SQLCOM_REPLACE;
lex->duplicates= DUP_REPLACE;
+ mysql_init_select(lex);
lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
}
replace_lock_option insert2
@@ -4147,21 +4181,8 @@ expr_or_default:
opt_insert_update:
/* empty */
- | ON DUPLICATE_SYM
- {
- LEX *lex= Lex;
- /*
- For simplicity, let's forget about INSERT ... SELECT ... UPDATE
- for a moment.
- */
- if (lex->sql_command != SQLCOM_INSERT)
- {
- yyerror(ER(ER_SYNTAX_ERROR));
- YYABORT;
- }
- lex->duplicates= DUP_UPDATE;
- }
- KEY_SYM UPDATE_SYM update_list
+ | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; }
+ KEY_SYM UPDATE_SYM insert_update_list
;
/* Update rows in a table */
@@ -4173,6 +4194,7 @@ update:
mysql_init_select(lex);
lex->sql_command= SQLCOM_UPDATE;
lex->lock_option= TL_UNLOCK; /* Will be set later */
+ lex->duplicates= DUP_ERROR;
}
opt_low_priority opt_ignore join_table_list
SET update_list
@@ -4197,16 +4219,28 @@ update:
;
update_list:
- update_list ',' simple_ident equal expr_or_default
+ update_list ',' update_elem
+ | update_elem;
+
+update_elem:
+ simple_ident equal expr_or_default
{
- if (add_item_to_list(YYTHD, $3) || add_value_to_list(YYTHD, $5))
+ if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3))
YYABORT;
- }
- | simple_ident equal expr_or_default
- {
- if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3))
- YYABORT;
- };
+ };
+
+insert_update_list:
+ insert_update_list ',' insert_update_elem
+ | insert_update_elem;
+
+insert_update_elem:
+ simple_ident equal expr_or_default
+ {
+ LEX *lex= Lex;
+ if (lex->update_list.push_back($1) ||
+ lex->value_list.push_back($3))
+ YYABORT;
+ };
opt_low_priority:
/* empty */ { $$= YYTHD->update_lock_default; }
@@ -4219,7 +4253,9 @@ delete:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_DELETE;
+ mysql_init_select(lex);
lex->lock_option= lex->thd->update_lock_default;
+ lex->ignore= 0;
lex->select_lex.init_order();
}
opt_delete_options single_multi {}
@@ -4276,7 +4312,7 @@ opt_delete_options:
opt_delete_option:
QUICK { Select->options|= OPTION_QUICK; }
| LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; }
- | IGNORE_SYM { Lex->duplicates= DUP_IGNORE; };
+ | IGNORE_SYM { Lex->ignore= 1; };
truncate:
TRUNCATE_SYM opt_table_sym table_name
@@ -4507,7 +4543,7 @@ opt_db:
wild:
/* empty */
| LIKE TEXT_STRING_sys
- { Lex->wild= new (&YYTHD->mem_root) String($2.str, $2.length,
+ { Lex->wild= new (YYTHD->mem_root) String($2.str, $2.length,
system_charset_info); };
opt_full:
@@ -4561,7 +4597,7 @@ opt_describe_column:
/* empty */ {}
| text_string { Lex->wild= $1; }
| ident
- { Lex->wild= new (&YYTHD->mem_root) String((const char*) $1.str,$1.length,system_charset_info); };
+ { Lex->wild= new (YYTHD->mem_root) String((const char*) $1.str,$1.length,system_charset_info); };
/* flush things */
@@ -4685,6 +4721,8 @@ load: LOAD DATA_SYM load_data_lock opt_local INFILE TEXT_STRING_sys
lex->sql_command= SQLCOM_LOAD;
lex->lock_option= $3;
lex->local_file= $4;
+ lex->duplicates= DUP_ERROR;
+ lex->ignore= 0;
if (!(lex->exchange= new sql_exchange($6.str,0)))
YYABORT;
lex->field_list.empty();
@@ -4722,7 +4760,7 @@ load_data_lock:
opt_duplicate:
/* empty */ { Lex->duplicates=DUP_ERROR; }
| REPLACE { Lex->duplicates=DUP_REPLACE; }
- | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; };
+ | IGNORE_SYM { Lex->ignore= 1; };
opt_field_term:
/* empty */
@@ -4802,7 +4840,7 @@ text_literal:
text_string:
TEXT_STRING_literal
- { $$= new (&YYTHD->mem_root) String($1.str,$1.length,YYTHD->variables.collation_connection); }
+ { $$= new (YYTHD->mem_root) String($1.str,$1.length,YYTHD->variables.collation_connection); }
| HEX_NUM
{
Item *tmp = new Item_varbinary($1.str,$1.length);
@@ -5122,6 +5160,7 @@ keyword:
| COMMIT_SYM {}
| COMPRESSED_SYM {}
| CONCURRENT {}
+ | CONSISTENT_SYM {}
| CUBE_SYM {}
| DATA_SYM {}
| DATETIME {}
@@ -5262,6 +5301,7 @@ keyword:
| SHARE_SYM {}
| SHUTDOWN {}
| SLAVE {}
+ | SNAPSHOT_SYM {}
| SOUNDS_SYM {}
| SQL_CACHE_SYM {}
| SQL_BUFFER_RESULT {}
@@ -5306,6 +5346,7 @@ set:
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_SET_OPTION;
+ mysql_init_select(lex);
lex->option_type=OPT_SESSION;
lex->var_list.empty();
lex->one_shot_set= 0;
@@ -5612,7 +5653,7 @@ revoke_command:
grant_privileges ON opt_table FROM user_list
{}
|
- ALL PRIVILEGES ',' GRANT OPTION FROM user_list
+ ALL opt_privileges ',' GRANT OPTION FROM user_list
{
Lex->sql_command = SQLCOM_REVOKE_ALL;
}
@@ -5638,10 +5679,14 @@ grant:
grant_privileges:
grant_privilege_list {}
- | ALL PRIVILEGES { Lex->grant = GLOBAL_ACLS;}
- | ALL { Lex->grant = GLOBAL_ACLS;}
+ | ALL opt_privileges { Lex->grant = GLOBAL_ACLS;}
;
+opt_privileges:
+ /* empty */
+ | PRIVILEGES
+ ;
+
grant_privilege_list:
grant_privilege
| grant_privilege_list ',' grant_privilege;
@@ -5821,7 +5866,7 @@ column_list:
column_list_id:
ident
{
- String *new_str = new (&YYTHD->mem_root) String((const char*) $1.str,$1.length,system_charset_info);
+ String *new_str = new (YYTHD->mem_root) String((const char*) $1.str,$1.length,system_charset_info);
List_iterator <LEX_COLUMN> iter(Lex->columns);
class LEX_COLUMN *point;
LEX *lex=Lex;
@@ -5888,7 +5933,7 @@ grant_option:
;
begin:
- BEGIN_SYM { Lex->sql_command = SQLCOM_BEGIN;} opt_work {}
+ BEGIN_SYM { Lex->sql_command = SQLCOM_BEGIN; Lex->start_transaction_opt= 0;} opt_work {}
;
opt_work:
diff --git a/sql/strfunc.cc b/sql/strfunc.cc
index b5255e9be06..81aca092cec 100644
--- a/sql/strfunc.cc
+++ b/sql/strfunc.cc
@@ -53,8 +53,22 @@ ulonglong find_set(TYPELIB *lib, const char *str, uint length, CHARSET_INFO *cs,
{
const char *pos= start;
uint var_len;
+ int mblen= 1;
- for (; pos != end && *pos != field_separator; pos++) ;
+ if (cs && cs->mbminlen > 1)
+ {
+ for ( ; pos < end; pos+= mblen)
+ {
+ my_wc_t wc;
+ if ((mblen= cs->cset->mb_wc(cs, &wc, (const uchar *) pos,
+ (const uchar *) end)) < 1)
+ mblen= 1; // Not to hang on a wrong multibyte sequence
+ if (wc == (my_wc_t) field_separator)
+ break;
+ }
+ }
+ else
+ for (; pos != end && *pos != field_separator; pos++) ;
var_len= (uint) (pos - start);
uint find= cs ? find_type2(lib, start, var_len, cs) :
find_type(lib, start, var_len, (bool) 0);
@@ -66,9 +80,9 @@ ulonglong find_set(TYPELIB *lib, const char *str, uint length, CHARSET_INFO *cs,
}
else
found|= ((longlong) 1 << (find - 1));
- if (pos == end)
+ if (pos >= end)
break;
- start= pos + 1;
+ start= pos + mblen;
}
}
return found;
@@ -133,7 +147,7 @@ uint find_type(TYPELIB *lib, const char *find, uint length, bool part_match)
uint find_type2(TYPELIB *typelib, const char *x, uint length, CHARSET_INFO *cs)
{
- int find,pos,findpos;
+ int find,pos;
const char *j;
DBUG_ENTER("find_type2");
DBUG_PRINT("enter",("x: '%s' lib: 0x%lx",x,typelib));
@@ -143,7 +157,7 @@ uint find_type2(TYPELIB *typelib, const char *x, uint length, CHARSET_INFO *cs)
DBUG_PRINT("exit",("no count"));
DBUG_RETURN(0);
}
- LINT_INIT(findpos);
+
for (find=0, pos=0 ; (j=typelib->type_names[pos]) ; pos++)
{
if (!my_strnncoll(cs, (const uchar*) x, length,
@@ -156,6 +170,43 @@ uint find_type2(TYPELIB *typelib, const char *x, uint length, CHARSET_INFO *cs)
/*
+ Un-hex all elements in a typelib
+
+ SYNOPSIS
+ unhex_type2()
+ interval TYPELIB (struct of pointer to values + lengths + count)
+
+ NOTES
+
+ RETURN
+ N/A
+*/
+
+void unhex_type2(TYPELIB *interval)
+{
+ for (uint pos= 0; pos < interval->count; pos++)
+ {
+ char *from, *to;
+ for (from= to= (char*) interval->type_names[pos]; *from; )
+ {
+ /*
+ Note, hexchar_to_int(*from++) doesn't work
+ one some compilers, e.g. IRIX. Looks like a compiler
+ bug in inline functions in combination with arguments
+ that have a side effect. So, let's use from[0] and from[1]
+ and increment 'from' by two later.
+ */
+
+ *to++= (char) (hexchar_to_int(from[0]) << 4) +
+ hexchar_to_int(from[1]);
+ from+= 2;
+ }
+ interval->type_lengths[pos] /= 2;
+ }
+}
+
+
+/*
Check if the first word in a string is one of the ones in TYPELIB
SYNOPSIS
diff --git a/sql/structs.h b/sql/structs.h
index c30d85f59cb..846b3400fab 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -89,7 +89,12 @@ typedef struct st_key {
enum ha_key_alg algorithm;
KEY_PART_INFO *key_part;
char *name; /* Name of key */
- ulong *rec_per_key; /* Key part distribution */
+ /*
+ Array of AVG(#records with the same field value) for 1st ... Nth key part.
+ 0 means 'not known'.
+ For temporary heap tables this member is NULL.
+ */
+ ulong *rec_per_key;
union {
int bdb_return_if_eq;
} handler;
diff --git a/sql/table.cc b/sql/table.cc
index 3ae3d668407..064d7f1afc1 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -24,7 +24,8 @@
/* Functions defined in this file */
-static void frm_error(int error,TABLE *form,const char *name,int errortype);
+static void frm_error(int error,TABLE *form,const char *name,
+ int errortype, int errarg);
static void fix_type_pointers(const char ***array, TYPELIB *point_to_type,
uint types, char **names);
static uint find_field(TABLE *form,uint start,uint length);
@@ -57,6 +58,7 @@ static byte* get_field_name(Field **buff,uint *length,
2 Error (see frm_error)
3 Wrong data in .frm file
4 Error (see frm_error)
+ 5 Error (see frm_error: charset unavailable)
*/
int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
@@ -64,7 +66,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
{
reg1 uint i;
reg2 uchar *strpos;
- int j,error;
+ int j,error, errarg= 0;
uint rec_buff_length,n_length,int_length,records,key_parts,keys,
interval_count,interval_parts,read_length,db_create_options;
uint key_info_length, com_length;
@@ -81,6 +83,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
uchar *null_pos;
uint null_bit, new_frm_ver, field_pack_length;
SQL_CRYPT *crypted=0;
+ MEM_ROOT **root_ptr, *old_root;
DBUG_ENTER("openfrm");
DBUG_PRINT("enter",("name: '%s' form: %lx",name,outparam));
@@ -91,15 +94,16 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
error=1;
init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
- MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
- my_pthread_setspecific_ptr(THR_MALLOC,&outparam->mem_root);
+ root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
+ old_root= *root_ptr;
+ *root_ptr= &outparam->mem_root;
outparam->real_name=strdup_root(&outparam->mem_root,
- name+dirname_length(name));
- *fn_ext(outparam->real_name)='\0'; // Remove extension
+ name+dirname_length(name));
outparam->table_name=my_strdup(alias,MYF(MY_WME));
if (!outparam->real_name || !outparam->table_name)
goto err_end;
+ *fn_ext(outparam->real_name)='\0'; // Remove extension
if ((file=my_open(fn_format(index_file,name,"",reg_ext,MY_UNPACK_FILENAME),
O_RDONLY | O_SHARE,
@@ -141,8 +145,19 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
outparam->table_charset=get_charset((uint) head[38],MYF(0));
null_field_first=1;
}
- if (!outparam->table_charset) /* unknown charset in head[38] or pre-3.23 frm */
+ if (!outparam->table_charset)
+ {
+ /* unknown charset in head[38] or pre-3.23 frm */
+ if (use_mb(default_charset_info))
+ {
+ /* Warn that we may be changing the size of character columns */
+ sql_print_warning("'%s' had no or invalid character set, "
+ "and default character set is multi-byte, "
+ "so character column sizes may have changed",
+ name);
+ }
outparam->table_charset=default_charset_info;
+ }
outparam->db_record_offset=1;
if (db_create_options & HA_OPTION_LONG_BLOB_PTR)
outparam->blob_ptr_size=portable_sizeof_char_ptr;
@@ -180,7 +195,6 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
goto err_not_open; /* purecov: inspected */
bzero((char*) keyinfo,n_length);
outparam->key_info=keyinfo;
- outparam->max_key_length= outparam->total_key_length= 0;
key_part= my_reinterpret_cast(KEY_PART_INFO*) (keyinfo+keys);
strpos=disk_buff+6;
@@ -236,11 +250,6 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
}
key_part->store_length=key_part->length;
}
- set_if_bigger(outparam->max_key_length,keyinfo->key_length+
- keyinfo->key_parts);
- outparam->total_key_length+= keyinfo->key_length;
- if (keyinfo->flags & HA_NOSAME)
- set_if_bigger(outparam->max_unique_length,keyinfo->key_length);
}
keynames=(char*) key_part;
strpos+= (strmov(keynames, (char *) strpos) - keynames)+1;
@@ -251,9 +260,9 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
#ifdef HAVE_CRYPTED_FRM
else if (*(head+26) == 2)
{
- my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ *root_ptr= old_root
crypted=get_crypt_for_frm();
- my_pthread_setspecific_ptr(THR_MALLOC,&outparam->mem_root);
+ *root_ptr= &outparam->mem_root;
outparam->crypted=1;
}
#endif
@@ -301,12 +310,14 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0)));
if (my_read(file,(byte*) head,288,MYF(MY_NABP))) goto err_not_open;
+#ifdef HAVE_CRYPTED_FRM
if (crypted)
{
crypted->decode((char*) head+256,288-256);
if (sint2korr(head+284) != 0) // Should be 0
goto err_not_open; // Wrong password
}
+#endif
outparam->fields= uint2korr(head+258);
pos=uint2korr(head+260); /* Length of all screens */
@@ -335,12 +346,14 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
pos+ (uint) (n_length+int_length+com_length));
if (read_string(file,(gptr*) &disk_buff,read_length))
goto err_not_open; /* purecov: inspected */
+#ifdef HAVE_CRYPTED_FRM
if (crypted)
{
crypted->decode((char*) disk_buff,read_length);
delete crypted;
crypted=0;
}
+#endif
strpos= disk_buff+pos;
outparam->intervals= (TYPELIB*) (field_ptr+outparam->fields+1);
@@ -434,10 +447,14 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
}
else
{
- if (!strpos[14])
- charset= &my_charset_bin;
- else if (!(charset=get_charset((uint) strpos[14], MYF(0))))
- charset= outparam->table_charset;
+ if (!strpos[14])
+ charset= &my_charset_bin;
+ else if (!(charset=get_charset((uint) strpos[14], MYF(0))))
+ {
+ error= 5; // Unknown or unavailable charset
+ errarg= (int) strpos[14];
+ goto err_not_open;
+ }
}
if (!comment_length)
{
@@ -461,9 +478,36 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
/* old frm file */
field_type= (enum_field_types) f_packtype(pack_flag);
- charset=f_is_binary(pack_flag) ? &my_charset_bin : outparam->table_charset;
+ if (f_is_binary(pack_flag))
+ {
+ /*
+ Try to choose the best 4.1 type:
+ - for 4.0 "CHAR(N) BINARY" or "VARCHAR(N) BINARY"
+ try to find a binary collation for character set.
+ - for other types (e.g. BLOB) just use my_charset_bin.
+ */
+ if (!f_is_blob(pack_flag))
+ {
+ // 3.23 or 4.0 string
+ if (!(charset= get_charset_by_csname(outparam->table_charset->csname,
+ MY_CS_BINSORT, MYF(0))))
+ charset= &my_charset_bin;
+ }
+ else
+ charset= &my_charset_bin;
+ }
+ else
+ charset= outparam->table_charset;
bzero((char*) &comment, sizeof(comment));
}
+
+ if (interval_nr && charset->mbminlen > 1)
+ {
+ /* Unescape UCS2 intervals from HEX notation */
+ TYPELIB *interval= outparam->intervals + interval_nr - 1;
+ unhex_type2(interval);
+ }
+
*field_ptr=reg_field=
make_field(record+recpos,
(uint32) field_length,
@@ -635,6 +679,12 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
}
}
keyinfo->usable_key_parts=usable_parts; // Filesort
+
+ set_if_bigger(outparam->max_key_length,keyinfo->key_length+
+ keyinfo->key_parts);
+ outparam->total_key_length+= keyinfo->key_length;
+ if (keyinfo->flags & HA_NOSAME)
+ set_if_bigger(outparam->max_unique_length,keyinfo->key_length);
}
if (primary_key < MAX_KEY &&
(outparam->keys_in_use.is_set(primary_key)))
@@ -736,7 +786,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
}
outparam->db_low_byte_first=outparam->file->low_byte_first();
- my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ *root_ptr= old_root;
opened_tables++;
#ifndef DBUG_OFF
if (use_hash)
@@ -751,8 +801,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
err_end: /* Here when no file */
delete crypted;
- my_pthread_setspecific_ptr(THR_MALLOC,old_root);
- frm_error(error,outparam,name,ME_ERROR+ME_WAITTANG);
+ *root_ptr= old_root;
+ frm_error(error, outparam, name, ME_ERROR + ME_WAITTANG, errarg);
delete outparam->file;
outparam->file=0; // For easyer errorchecking
outparam->db_stat=0;
@@ -937,7 +987,8 @@ ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
/* error message when opening a form file */
-static void frm_error(int error, TABLE *form, const char *name, myf errortype)
+static void frm_error(int error, TABLE *form, const char *name,
+ myf errortype, int errarg)
{
int err_no;
char buff[FN_REFLEN];
@@ -968,6 +1019,20 @@ static void frm_error(int error, TABLE *form, const char *name, myf errortype)
fn_format(buff,form->real_name,form_dev,datext,2),my_errno);
break;
}
+ case 5:
+ {
+ const char *csname= get_charset_name((uint) errarg);
+ char tmp[10];
+ if (!csname || csname[0] =='?')
+ {
+ my_snprintf(tmp, sizeof(tmp), "#%d", errarg);
+ csname= tmp;
+ }
+ my_printf_error(ER_UNKNOWN_COLLATION,
+ "Unknown collation '%s' in table '%-.64s' definition",
+ MYF(0), csname, form->real_name);
+ break;
+ }
default: /* Better wrong error than none */
case 4:
my_error(ER_NOT_FORM_FILE,errortype,
@@ -1403,7 +1468,7 @@ bool check_column_name(const char *name)
{
const char *start= name;
bool last_char_is_space= TRUE;
-
+
while (*name)
{
#if defined(USE_MB) && defined(USE_MB_IDENT)
diff --git a/sql/table.h b/sql/table.h
index 2eb854f553d..eed9969dac8 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -131,8 +131,14 @@ struct st_table {
int current_lock; /* Type of lock on table */
enum tmp_table_type tmp_table;
my_bool copy_blobs; /* copy_blobs when storing */
- my_bool null_row; /* All columns are null */
- my_bool maybe_null,outer_join; /* Used with OUTER JOIN */
+ /*
+ Used in outer joins: if true, all columns are considered to have NULL
+ values, including columns declared as "not null".
+ */
+ my_bool null_row;
+ /* 0 or JOIN_TYPE_{LEFT|RIGHT}, same as TABLE_LIST::outer_join */
+ my_bool outer_join;
+ my_bool maybe_null; /* true if (outer_join != 0) */
my_bool force_index;
my_bool distinct,const_table,no_rows;
my_bool key_read;
diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc
index fa678ec7de2..3a9ca397bba 100644
--- a/sql/thr_malloc.cc
+++ b/sql/thr_malloc.cc
@@ -38,7 +38,7 @@ void init_sql_alloc(MEM_ROOT *mem_root, uint block_size, uint pre_alloc)
gptr sql_alloc(uint Size)
{
- MEM_ROOT *root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
+ MEM_ROOT *root= *my_pthread_getspecific_ptr(MEM_ROOT**,THR_MALLOC);
char *ptr= (char*) alloc_root(root,Size);
return ptr;
}
@@ -108,8 +108,11 @@ char *sql_strmake_with_convert(const char *str, uint32 arg_length,
memcpy(pos, str, new_length);
}
else
+ {
+ uint dummy_errors;
new_length= copy_and_convert((char*) pos, new_length, to_cs, str,
- arg_length, from_cs);
+ arg_length, from_cs, &dummy_errors);
+ }
pos[new_length]= 0;
*result_length= new_length;
return pos;
diff --git a/sql/tztime.cc b/sql/tztime.cc
index c2143b0d5dd..c45271966f9 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -1773,7 +1773,13 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr,
0, HA_READ_KEY_EXACT))
{
- sql_print_error("Can't find description of time zone.");
+#ifdef EXTRA_DEBUG
+ /*
+ Most probably user has mistyped time zone name, so no need to bark here
+ unless we need it for debugging.
+ */
+ sql_print_error("Can't find description of time zone '%s'", tz_name_buff);
+#endif
goto end;
}
@@ -1794,7 +1800,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr,
0, HA_READ_KEY_EXACT))
{
- sql_print_error("Can't find description of time zone.");
+ sql_print_error("Can't find description of time zone '%u'", tzid);
goto end;
}
@@ -1825,7 +1831,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
{
ttid= (uint)table->field[1]->val_int();
- if (ttid > TZ_MAX_TYPES)
+ if (ttid >= TZ_MAX_TYPES)
{
sql_print_error("Error while loading time zone description from "
"mysql.time_zone_transition_type table: too big "
diff --git a/sql/tztime.h b/sql/tztime.h
index 9f969639bd0..2214c1b29d6 100644
--- a/sql/tztime.h
+++ b/sql/tztime.h
@@ -66,8 +66,8 @@ extern void my_tz_free();
/*
- Check if we have pointer to the beggining of list of implictly used
- time zone tables and fast-forward to its end.
+ Check if we have pointer to the begining of list of implicitly used time
+ zone tables, set SELECT_ACL for them and fast-forward to its end.
SYNOPSIS
my_tz_check_n_skip_implicit_tables()
@@ -87,6 +87,8 @@ inline bool my_tz_check_n_skip_implicit_tables(TABLE_LIST **table,
{
if (*table == tz_tables)
{
+ for (int i= 0; i < 4; i++)
+ (*table)[i].grant.privilege= SELECT_ACL;
(*table)+= 3;
return TRUE;
}
diff --git a/sql/udf_example.cc b/sql/udf_example.cc
index 7e2ee9113b2..50de0f187fe 100644
--- a/sql/udf_example.cc
+++ b/sql/udf_example.cc
@@ -615,10 +615,12 @@ my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
return 1;
}
bzero(initid->ptr,sizeof(longlong));
- // Fool MySQL to think that this function is a constant
- // This will ensure that MySQL only evalutes the function
- // when the rows are sent to the client and not before any ORDER BY
- // clauses
+ /*
+ Fool MySQL to think that this function is a constant
+ This will ensure that MySQL only evalutes the function
+ when the rows are sent to the client and not before any ORDER BY
+ clauses
+ */
initid->const_item=1;
return 0;
}
@@ -635,9 +637,10 @@ longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
ulonglong val=0;
if (args->arg_count)
val= *((longlong*) args->args[0]);
- return ++ *((longlong*) initid->ptr) + val;
+ return ++*((longlong*) initid->ptr) + val;
}
+
/****************************************************************************
** Some functions that handles IP and hostname conversions
** The orignal function was from Zeev Suraski.
diff --git a/sql/unireg.cc b/sql/unireg.cc
index c82fcc4abef..a550b06a466 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -174,6 +174,17 @@ bool mysql_create_frm(THD *thd, my_string file_name,
goto err2;
if (my_close(file,MYF(MY_WME)))
goto err3;
+
+ {
+ /* Unescape all UCS2 intervals: were escaped in pack_headers */
+ List_iterator<create_field> it(create_fields);
+ create_field *field;
+ while ((field=it++))
+ {
+ if (field->interval && field->charset->mbminlen > 1)
+ unhex_type2(field->interval);
+ }
+ }
DBUG_RETURN(0);
err:
@@ -423,6 +434,28 @@ static bool pack_header(uchar *forminfo, enum db_type table_type,
if (field->interval)
{
uint old_int_count=int_count;
+
+ if (field->charset->mbminlen > 1)
+ {
+ /* Escape UCS2 intervals using HEX notation */
+ for (uint pos= 0; pos < field->interval->count; pos++)
+ {
+ char *dst;
+ uint length= field->interval->type_lengths[pos], hex_length;
+ const char *src= field->interval->type_names[pos];
+ const char *srcend= src + length;
+ hex_length= length * 2;
+ field->interval->type_lengths[pos]= hex_length;
+ field->interval->type_names[pos]= dst= sql_alloc(hex_length + 1);
+ for ( ; src < srcend; src++)
+ {
+ *dst++= _dig_vec_upper[((uchar) *src) >> 4];
+ *dst++= _dig_vec_upper[((uchar) *src) & 15];
+ }
+ *dst= '\0';
+ }
+ }
+
field->interval_id=get_interval_id(&int_count,create_fields,field);
if (old_int_count != int_count)
{
diff --git a/sql/unireg.h b/sql/unireg.h
index 4ab2ba26b15..70df9a89c8f 100644
--- a/sql/unireg.h
+++ b/sql/unireg.h
@@ -72,6 +72,8 @@
#define PARAM_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-3))
#define OUTER_REF_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-2))
#define RAND_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-1))
+#define PSEUDO_TABLE_BITS (PARAM_TABLE_BIT | OUTER_REF_TABLE_BIT | \
+ RAND_TABLE_BIT)
#define MAX_FIELDS 4096 /* Limit in the .frm file */
#define MAX_SORT_MEMORY (2048*1024-MALLOC_OVERHEAD)