diff options
author | unknown <pem@mysql.com> | 2004-04-07 19:07:44 +0200 |
---|---|---|
committer | unknown <pem@mysql.com> | 2004-04-07 19:07:44 +0200 |
commit | 0a4401153a71c5e7cf18dc6d1641613dd52c7a3d (patch) | |
tree | e5ac1517ff3dccc42352b7bac39ecd1c093d0ae6 /sql | |
parent | 0a42fa7f723d6c6b3f67bede359b5b990f6f3ead (diff) | |
parent | 01bcc6ed3c4c1c6515592364b4aec0cae5d2083c (diff) | |
download | mariadb-git-0a4401153a71c5e7cf18dc6d1641613dd52c7a3d.tar.gz |
Merge 4.1 -> 5.0.
BitKeeper/etc/ignore:
auto-union
BitKeeper/etc/logging_ok:
auto-union
VC++Files/sql/mysqld.dsp:
Auto merged
client/Makefile.am:
Auto merged
client/mysql.cc:
Auto merged
client/mysqltest.c:
Auto merged
configure.in:
Auto merged
include/my_global.h:
Auto merged
include/my_pthread.h:
Auto merged
include/my_sys.h:
Auto merged
include/mysql_com.h:
Auto merged
libmysqld/Makefile.am:
Auto merged
libmysqld/lib_sql.cc:
Auto merged
myisam/mi_check.c:
Auto merged
myisam/myisamchk.c:
Auto merged
myisam/myisamdef.h:
Auto merged
mysql-test/install_test_db.sh:
Auto merged
mysql-test/r/insert_select.result:
Auto merged
mysql-test/r/join_outer.result:
Auto merged
mysql-test/r/null.result:
Auto merged
mysql-test/r/connect.result:
Auto merged
mysql-test/r/func_sapdb.result:
Auto merged
mysql-test/r/mix_innodb_myisam_binlog.result:
Auto merged
mysql-test/r/mysqldump.result:
Auto merged
mysql-test/r/rpl_change_master.result:
Auto merged
mysql-test/r/rpl_log.result:
Auto merged
mysql-test/r/show_check.result:
Auto merged
mysql-test/r/symlink.result:
Auto merged
mysql-test/r/rpl_flush_log_loop.result:
Auto merged
mysql-test/r/rpl_flush_tables.result:
Auto merged
mysql-test/r/rpl_loaddata.result:
Auto merged
mysql-test/r/rpl_loaddata_rule_m.result:
Auto merged
mysql-test/r/rpl_loaddata_rule_s.result:
Auto merged
mysql-test/r/rpl_max_relay_size.result:
Auto merged
mysql-test/r/rpl_reset_slave.result:
Auto merged
mysql-test/r/rpl_temporary.result:
Auto merged
mysql-test/r/rpl_until.result:
Auto merged
mysql-test/r/rpl_user_variables.result:
Auto merged
mysql-test/r/subselect.result:
Auto merged
mysql-test/r/union.result:
Auto merged
mysql-test/r/variables.result:
Auto merged
mysql-test/t/func_sapdb.test:
Auto merged
mysql-test/t/mix_innodb_myisam_binlog.test:
Auto merged
mysql-test/t/mysqlbinlog.test:
Auto merged
mysql-test/t/rpl_change_master.test:
Auto merged
mysql-test/t/rpl_log.test:
Auto merged
mysql-test/t/variables.test:
Auto merged
netware/BUILD/compile-linux-tools:
Auto merged
netware/BUILD/compile-netware-END:
Auto merged
netware/BUILD/compile-netware-all:
Auto merged
netware/BUILD/compile-netware-standard:
Auto merged
mysql-test/t/rpl_empty_master_crash.test:
Auto merged
mysql-test/t/rpl_error_ignored_table.test:
Auto merged
mysql-test/t/rpl_flush_log_loop.test:
Auto merged
mysql-test/t/rpl_loaddata.test:
Auto merged
mysql-test/t/rpl_loaddata_rule_m.test:
Auto merged
mysql-test/t/rpl_loaddata_rule_s.test:
Auto merged
mysql-test/t/rpl_max_relay_size.test:
Auto merged
mysql-test/t/rpl_openssl.test:
Auto merged
mysql-test/t/rpl_relayrotate-slave.opt:
Auto merged
mysql-test/t/rpl_reset_slave.test:
Auto merged
mysql-test/t/rpl_trunc_binlog.test:
Auto merged
mysql-test/t/rpl_until.test:
Auto merged
mysql-test/t/rpl_user_variables.test:
Auto merged
mysql-test/t/subselect.test:
Auto merged
scripts/make_binary_distribution.sh:
Auto merged
scripts/mysql_create_system_tables.sh:
Auto merged
scripts/mysql_fix_privilege_tables.sql:
Auto merged
scripts/mysql_install_db.sh:
Auto merged
sql/filesort.cc:
Auto merged
sql/ha_berkeley.cc:
Auto merged
sql/ha_berkeley.h:
Auto merged
sql/ha_innodb.h:
Auto merged
sql/ha_myisam.cc:
Auto merged
sql/handler.cc:
Auto merged
sql/handler.h:
Auto merged
sql/item.cc:
Auto merged
sql/item.h:
Auto merged
sql/item_cmpfunc.cc:
Auto merged
sql/item_cmpfunc.h:
Auto merged
sql/item_create.cc:
Auto merged
sql/item_create.h:
Auto merged
sql/item_subselect.cc:
Auto merged
sql/item_sum.cc:
Auto merged
sql/item_sum.h:
Auto merged
sql/item_timefunc.h:
Auto merged
sql/lock.cc:
Auto merged
sql/log_event.h:
Auto merged
sql/mysqld.cc:
Auto merged
sql/net_serv.cc:
Auto merged
sql/protocol.cc:
Auto merged
sql/protocol.h:
Auto merged
sql/repl_failsafe.cc:
Auto merged
sql/sql_acl.cc:
Auto merged
sql/sql_acl.h:
Auto merged
sql/sql_base.cc:
Auto merged
sql/sql_cache.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_db.cc:
Auto merged
sql/sql_delete.cc:
Auto merged
sql/sql_derived.cc:
Auto merged
sql/sql_lex.h:
Auto merged
sql/sql_list.h:
Auto merged
sql/sql_load.cc:
Auto merged
sql/sql_rename.cc:
Auto merged
sql/sql_repl.cc:
Auto merged
sql/sql_repl.h:
Auto merged
sql/sql_select.cc:
Auto merged
sql/sql_select.h:
Auto merged
sql/sql_show.cc:
Auto merged
sql/sql_table.cc:
Auto merged
sql/sql_test.cc:
Auto merged
sql/sql_update.cc:
Auto merged
sql-common/client.c:
Auto merged
tests/client_test.c:
Auto merged
Diffstat (limited to 'sql')
129 files changed, 8287 insertions, 4820 deletions
diff --git a/sql/derror.cc b/sql/derror.cc index 53d0dc5b7e5..09f43d20044 100644 --- a/sql/derror.cc +++ b/sql/derror.cc @@ -50,7 +50,7 @@ static bool read_texts(const char *file_name,const char ***point, char name[FN_REFLEN]; const char *buff; uchar head[32],*pos; - CHARSET_INFO *cset; + CHARSET_INFO *cset; // For future DBUG_ENTER("read_texts"); *point=0; // If something goes wrong @@ -137,7 +137,7 @@ err1: if (file != FERR) VOID(my_close(file,MYF(MY_WME))); unireg_abort(1); - DBUG_RETURN(1); // Impossible + DBUG_RETURN(1); // keep compiler happy } /* read_texts */ diff --git a/sql/field.cc b/sql/field.cc index d26534b5ac7..d099da2d959 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -107,7 +107,7 @@ bool test_if_int(const char *str, int length, const char *int_end, return 1; } - +#ifdef NOT_USED static bool test_if_real(const char *str,int length, CHARSET_INFO *cs) { cs= system_charset_info; // QQ move test_if_real into CHARSET_INFO struct @@ -159,16 +159,9 @@ static bool test_if_real(const char *str,int length, CHARSET_INFO *cs) } return 1; } +#endif -static inline uint field_length_without_space(const char *ptr, uint length) -{ - const char *end= ptr+length; - while (end > ptr && end[-1] == ' ') - end--; - return (uint) (end-ptr); -} - /* Tables of filed type compatibility. @@ -301,11 +294,12 @@ Field::Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :ptr(ptr_arg),null_ptr(null_ptr_arg), - table(table_arg),table_name(table_arg ? table_arg->table_name : 0), + table(table_arg),orig_table(table_arg), + table_name(table_arg ? table_arg->table_name : 0), field_name(field_name_arg), query_id(0), key_start(0), part_of_key(0), part_of_sortkey(0), unireg_check(unireg_check_arg), - field_length(length_arg),null_bit(null_bit_arg),abs_offset(0) + field_length(length_arg),null_bit(null_bit_arg) { flags=null_ptr ? 0: NOT_NULL_FLAG; comment.str= (char*) ""; @@ -349,9 +343,10 @@ void Field_num::add_zerofill_and_unsigned(String &res) const void Field_num::make_field(Send_field *field) { /* table_cache_key is not set for temp tables */ - field->db_name=table->table_cache_key ? table->table_cache_key : ""; - field->org_table_name=table->real_name; - field->table_name=table_name; + field->db_name= (orig_table->table_cache_key ? orig_table->table_cache_key : + ""); + field->org_table_name= orig_table->real_name; + field->table_name= orig_table->table_name; field->col_name=field->org_col_name=field_name; field->charsetnr= charset()->number; field->length=field_length; @@ -364,9 +359,10 @@ void Field_num::make_field(Send_field *field) void Field_str::make_field(Send_field *field) { /* table_cache_key is not set for temp tables */ - field->db_name=table->table_cache_key ? table->table_cache_key : ""; - field->org_table_name=table->real_name; - field->table_name=table_name; + field->db_name= (orig_table->table_cache_key ? orig_table->table_cache_key : + ""); + field->org_table_name= orig_table->real_name; + field->table_name= orig_table->table_name; field->col_name=field->org_col_name=field_name; field->charsetnr= charset()->number; field->length=field_length; @@ -2264,20 +2260,23 @@ void Field_longlong::sql_type(String &res) const add_zerofill_and_unsigned(res); } + /**************************************************************************** -** single precision float + single precision float ****************************************************************************/ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs) { - int err; - Field_float::store(my_strntod(cs,(char*) from,len,(char**)NULL,&err)); - if (err || current_thd->count_cuted_fields && !test_if_real(from,len,cs)) + int error; + char *end; + double nr= my_strntod(cs,(char*) from,len,&end,&error); + if (error || ((uint) (end-from) != len && current_thd->count_cuted_fields)) { + error= 1; set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); - return 1; } - return (err) ? 1 : 0; + Field_float::store(nr); + return error; } @@ -2285,28 +2284,48 @@ int Field_float::store(double nr) { float j; int error= 0; - if (dec < NOT_FIXED_DEC) - nr=floor(nr*log_10[dec]+0.5)/log_10[dec]; // To fixed point - if (unsigned_flag && nr < 0) + + if (isnan(nr)) { + j= 0; + set_null(); set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); - nr=0; error= 1; } - if (nr < -FLT_MAX) + else if (unsigned_flag && nr < 0) { - j= -FLT_MAX; + j= 0; set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); error= 1; } - else if (nr > FLT_MAX) + else { - j=FLT_MAX; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); - error= 1; + double max_value; + if (dec >= NOT_FIXED_DEC) + { + max_value= FLT_MAX; + } + else + { + max_value= (log_10[field_length]-1)/log_10[dec]; + nr= floor(nr*log_10[dec]+0.5)/log_10[dec]; + } + if (nr < -max_value) + { + j= (float)-max_value; + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + error= 1; + } + else if (nr > max_value) + { + j= (float)max_value; + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + error= 1; + } + else + j= (float) nr; } - else - j= (float) nr; + #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -2537,48 +2556,69 @@ void Field_float::sql_type(String &res) const add_zerofill_and_unsigned(res); } + /**************************************************************************** -** double precision floating point numbers + double precision floating point numbers ****************************************************************************/ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) { - int err; - double j= my_strntod(cs,(char*) from,len,(char**)0,&err); - if (err || current_thd->count_cuted_fields && !test_if_real(from,len,cs)) + int error; + char *end; + double nr= my_strntod(cs,(char*) from, len, &end, &error); + if (error || ((uint) (end-from) != len && current_thd->count_cuted_fields)) { + error= 1; set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); - err= 1; } - if (unsigned_flag && j < 0) - { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); - j=0; - err= 1; - } -#ifdef WORDS_BIGENDIAN - if (table->db_low_byte_first) - { - float8store(ptr,j); - } - else -#endif - doublestore(ptr,j); - return err; + Field_double::store(nr); + return error; } int Field_double::store(double nr) { int error= 0; - if (dec < NOT_FIXED_DEC) - nr=floor(nr*log_10[dec]+0.5)/log_10[dec]; // To fixed point - if (unsigned_flag && nr < 0) + + if (isnan(nr)) { + nr= 0; + set_null(); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + error= 1; + } + else if (unsigned_flag && nr < 0) + { + nr= 0; set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); - nr=0; error= 1; } + else + { + double max_value; + if (dec >= NOT_FIXED_DEC) + { + max_value= DBL_MAX; + } + else + { + max_value= (log_10[field_length]-1)/log_10[dec]; + nr= floor(nr*log_10[dec]+0.5)/log_10[dec]; + } + if (nr < -max_value) + { + nr= -max_value; + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + error= 1; + } + else if (nr > max_value) + { + nr= max_value; + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + error= 1; + } + } + #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -2742,14 +2782,8 @@ int Field_double::cmp(const char *a_ptr, const char *b_ptr) else #endif { -/* could this ALWAYS be 2 calls to doubleget() ?? */ -#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) doubleget(a, a_ptr); doubleget(b, b_ptr); -#else - memcpy_fixed(&a,a_ptr,sizeof(double)); - memcpy_fixed(&b,b_ptr,sizeof(double)); -#endif } return (a < b) ? -1 : (a > b) ? 1 : 0; } @@ -2769,12 +2803,7 @@ void Field_double::sort_string(char *to,uint length __attribute__((unused))) } else #endif -/* could this ALWAYS be 2 calls to doubleget() ?? */ -#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) doubleget(nr,ptr); -#else - memcpy_fixed(&nr,ptr,sizeof(nr)); -#endif change_double_for_sort(nr, (byte*) to); } @@ -2795,11 +2824,50 @@ void Field_double::sql_type(String &res) const } -/**************************************************************************** -** timestamp -** The first timestamp in the table is automaticly updated -** by handler.cc. The form->timestamp points at the automatic timestamp. -****************************************************************************/ +/* + TIMESTAMP type. + Holds datetime values in range from 1970-01-01 00:00:01 UTC to + 2038-01-01 00:00:00 UTC stored as number of seconds since Unix + Epoch in UTC. + + Up to one of timestamps columns in the table can be automatically + set on row update and/or have NOW() as default value. + TABLE::timestamp_field points to Field object for such timestamp with + auto-set-on-update. TABLE::time_stamp holds offset in record + 1 for this + field, and is used by handler code which performs updates required. + + Actually SQL-99 says that we should allow niladic functions (like NOW()) + as defaults for any field. Current limitations (only NOW() and only + for one TIMESTAMP field) are because of restricted binary .frm format + and should go away in the future. + + Also because of this limitation of binary .frm format we use 5 different + unireg_check values with TIMESTAMP field to distinguish various cases of + DEFAULT or ON UPDATE values. These values are: + + TIMESTAMP_OLD_FIELD - old timestamp, if there was not any fields with + auto-set-on-update (or now() as default) in this table before, then this + field has NOW() as default and is updated when row changes, else it is + field which has 0 as default value and is not automaitcally updated. + TIMESTAMP_DN_FIELD - field with NOW() as default but not set on update + automatically (TIMESTAMP DEFAULT NOW()) + TIMESTAMP_UN_FIELD - field which is set on update automatically but has not + NOW() as default (but it may has 0 or some other const timestamp as + default) (TIMESTAMP ON UPDATE NOW()). + TIMESTAMP_DNUN_FIELD - field which has now() as default and is auto-set on + update. (TIMESTAMP DEFAULT NOW() ON UPDATE NOW()) + NONE - field which is not auto-set on update with some other than NOW() + default value (TIMESTAMP DEFAULT 0). + + Note that TIMESTAMP_OLD_FIELD's are never created explicitly now, they are + left only for preserving ability to read old tables. Such fields replaced + with their newer analogs in CREATE TABLE and in SHOW CREATE TABLE. This is + because we want to prefer NONE unireg_check before TIMESTAMP_OLD_FIELD for + "TIMESTAMP DEFAULT 'Const'" field. (Old timestamps allowed such + specification too but ignored default value for first timestamp, which of + course is non-standard.) In most cases user won't notice any change, only + exception is different behavior of old/new timestamps during ALTER TABLE. + */ Field_timestamp::Field_timestamp(char *ptr_arg, uint32 len_arg, enum utype unireg_check_arg, @@ -2810,15 +2878,37 @@ Field_timestamp::Field_timestamp(char *ptr_arg, uint32 len_arg, unireg_check_arg, field_name_arg, table_arg, cs) { flags|=ZEROFILL_FLAG; /* 4.0 MYD compatibility */ - if (table && !table->timestamp_field) + if (table && !table->timestamp_field && + unireg_check != NONE) { - table->timestamp_field= this; // Automatic timestamp - table->time_stamp=(ulong) (ptr_arg - (char*) table->record[0])+1; + /* This timestamp has auto-update */ + table->timestamp_field= this; flags|=TIMESTAMP_FLAG; } } +/* + Sets TABLE::timestamp_default_now and TABLE::timestamp_on_update_now + members according to unireg type of this TIMESTAMP field. + + SYNOPSIS + Field_timestamp::set_timestamp_offsets() + +*/ +void Field_timestamp::set_timestamp_offsets() +{ + ulong timestamp= (ulong) (ptr - (char*) table->record[0]) + 1; + + DBUG_ASSERT(table->timestamp_field == this && unireg_check != NONE); + + table->timestamp_default_now= + (unireg_check == TIMESTAMP_UN_FIELD)? 0 : timestamp; + table->timestamp_on_update_now= + (unireg_check == TIMESTAMP_DN_FIELD)? 0 : timestamp; +} + + int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) { long tmp=(long) str_to_timestamp(from,len); @@ -2933,10 +3023,10 @@ int Field_timestamp::store(longlong nr) { long not_used; - if (l_time.year >= TIMESTAMP_MAX_YEAR || l_time.year < 1900+YY_PART_YEAR-1) + if (!(timestamp= my_gmt_sec(&l_time, ¬_used))) goto err; - timestamp= my_gmt_sec(&l_time, ¬_used); } + #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -4075,10 +4165,10 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) Make sure we don't break a multibyte sequence as well as don't copy a malformed data. */ - copy_length= field_charset->cset->wellformedlen(field_charset, - from,from+length, - field_length/ - field_charset->mbmaxlen); + copy_length= field_charset->cset->well_formed_len(field_charset, + from,from+length, + field_length/ + field_charset->mbmaxlen); memcpy(ptr,from,copy_length); if (copy_length < field_length) // Append spaces if shorter field_charset->cset->fill(field_charset,ptr+copy_length, @@ -4165,10 +4255,10 @@ int Field_string::cmp(const char *a_ptr, const char *b_ptr) void Field_string::sort_string(char *to,uint length) { uint tmp=my_strnxfrm(field_charset, - (unsigned char *)to, length, - (unsigned char *) ptr, field_length); + (unsigned char *) to, length, + (unsigned char *) ptr, field_length); if (tmp < length) - bzero(to + tmp, length - tmp); + field_charset->cset->fill(field_charset, to + tmp, length - tmp, ' '); } @@ -4180,7 +4270,8 @@ void Field_string::sql_type(String &res) const (field_length > 3 && (table->db_options_in_use & HA_OPTION_PACK_RECORD) ? - "varchar" : "char"), + (has_charset() ? "varchar" : "varbinary") : + (has_charset() ? "char" : "binary")), (int) field_length / charset()->mbmaxlen); res.length(length); } @@ -4340,7 +4431,8 @@ void Field_varstring::sort_string(char *to,uint length) (unsigned char *) to, length, (unsigned char *)ptr+2, tot_length); if (tot_length < length) - bzero(to+tot_length,length-tot_length); + field_charset->cset->fill(field_charset, to+tot_length,length-tot_length, + binary() ? (char) 0 : ' '); } @@ -4571,11 +4663,15 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) } copy_length= max_data_length(); - if (copy_length > length) - copy_length= length; - copy_length= field_charset->cset->wellformedlen(field_charset, - from,from+copy_length, - field_length); + /* + copy_length is ok as last argument to well_formed_len as this is never + used to limit the length of the data. The cut of long data is done with + the 'min()' call below. + */ + copy_length= field_charset->cset->well_formed_len(field_charset, + from,from + + min(length, copy_length), + copy_length); if (copy_length < length) set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); @@ -4711,18 +4807,28 @@ void Field_blob::get_key_image(char *buff,uint length, #ifdef HAVE_SPATIAL if (type == itMBR) { - if (!blob_length) + const char *dummy; + MBR mbr; + Geometry_buffer buffer; + Geometry *gobj; + + if (blob_length < SRID_SIZE) + { + bzero(buff, SIZEOF_STORED_DOUBLE*4); return; + } get_ptr(&blob); - - MBR mbr; - Geometry gobj; - gobj.create_from_wkb(blob + SRID_SIZE, blob_length - SRID_SIZE); - gobj.get_mbr(&mbr); - float8store(buff, mbr.xmin); - float8store(buff+8, mbr.xmax); - float8store(buff+16, mbr.ymin); - float8store(buff+24, mbr.ymax); + gobj= Geometry::create_from_wkb(&buffer, + blob + SRID_SIZE, blob_length - SRID_SIZE); + if (gobj->get_mbr(&mbr, &dummy)) + bzero(buff, SIZEOF_STORED_DOUBLE*4); + else + { + float8store(buff, mbr.xmin); + float8store(buff+8, mbr.xmax); + float8store(buff+16, mbr.ymin); + float8store(buff+24, mbr.ymax); + } return; } #endif /*HAVE_SPATIAL*/ @@ -4780,7 +4886,9 @@ void Field_blob::sort_string(char *to,uint length) (unsigned char *)to, length, (unsigned char *)blob, blob_length); if (blob_length < length) - bzero(to+blob_length, length-blob_length); + field_charset->cset->fill(field_charset, to+blob_length, + length-blob_length, + binary() ? (char) 0 : ' '); } } @@ -4935,6 +5043,7 @@ uint Field_blob::max_packed_col_length(uint max_length) return (max_length > 255 ? 2 : 1)+max_length; } + #ifdef HAVE_SPATIAL void Field_geom::get_key_image(char *buff, uint length, CHARSET_INFO *cs, @@ -4943,17 +5052,28 @@ void Field_geom::get_key_image(char *buff, uint length, CHARSET_INFO *cs, length-= HA_KEY_BLOB_LENGTH; ulong blob_length= get_length(ptr); char *blob; - get_ptr(&blob); - + const char *dummy; MBR mbr; - Geometry gobj; - gobj.create_from_wkb(blob + SRID_SIZE, blob_length - SRID_SIZE); - gobj.get_mbr(&mbr); - float8store(buff, mbr.xmin); - float8store(buff + 8, mbr.xmax); - float8store(buff + 16, mbr.ymin); - float8store(buff + 24, mbr.ymax); - return; + + if (blob_length < SRID_SIZE) + { + bzero(buff, SIZEOF_STORED_DOUBLE*4); + return; + } + get_ptr(&blob); + Geometry_buffer buffer; + Geometry *gobj; + gobj= Geometry::create_from_wkb(&buffer, + blob + SRID_SIZE, blob_length - SRID_SIZE); + if (gobj->get_mbr(&mbr, &dummy)) + bzero(buff, SIZEOF_STORED_DOUBLE*4); + else + { + float8store(buff, mbr.xmin); + float8store(buff + 8, mbr.xmax); + float8store(buff + 16, mbr.ymin); + float8store(buff + 24, mbr.ymax); + } } @@ -4997,17 +5117,17 @@ void Field_geom::sql_type(String &res) const int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) { if (!length) - { bzero(ptr, Field_blob::pack_length()); - } else { - // Should check given WKB - if (length < 4 + 1 + 4 + 8 + 8) // SRID + WKB_HEADER + X + Y - return 1; - uint32 wkb_type= uint4korr(from + 5); - if (wkb_type < 1 || wkb_type > 7) - return 1; + // Check given WKB + uint32 wkb_type; + if (length < SRID_SIZE + WKB_HEADER_SIZE + SIZEOF_STORED_DOUBLE*2) + goto err; + wkb_type= uint4korr(from + WKB_HEADER_SIZE); + if (wkb_type < (uint32) Geometry::wkb_point || + wkb_type > (uint32) Geometry::wkb_end) + return -1; Field_blob::store_length(length); if (table->copy_blobs || length <= MAX_FIELD_WIDTH) { // Must make a copy @@ -5017,6 +5137,10 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) bmove(ptr + packlength, (char*) &from, sizeof(char*)); } return 0; + +err: + bzero(ptr, Field_blob::pack_length()); + return -1; } #endif /*HAVE_SPATIAL*/ @@ -5455,7 +5579,7 @@ uint32 calc_pack_length(enum_field_types type,uint32 length) case FIELD_TYPE_ENUM: abort(); return 0; // This shouldn't happen default: return 0; } - return 0; // This shouldn't happen + return 0; // Keep compiler happy } @@ -5488,6 +5612,18 @@ Field *make_field(char *ptr, uint32 field_length, null_pos=0; null_bit=0; } + + switch (field_type) + { + case FIELD_TYPE_DATE: + case FIELD_TYPE_NEWDATE: + case FIELD_TYPE_TIME: + case FIELD_TYPE_DATETIME: + case FIELD_TYPE_TIMESTAMP: + field_charset= &my_charset_bin; + default: break; + } + if (f_is_alpha(pack_flag)) { if (!f_is_packed(pack_flag)) @@ -5644,8 +5780,7 @@ create_field::create_field(Field *old_field,Field *orig_field) interval=0; def=0; if (!old_field->is_real_null() && ! (flags & BLOB_FLAG) && - old_field->type() != FIELD_TYPE_TIMESTAMP && old_field->ptr && - orig_field) + old_field->ptr && orig_field) { char buff[MAX_FIELD_WIDTH],*pos; String tmp(buff,sizeof(buff), charset); @@ -5660,7 +5795,7 @@ create_field::create_field(Field *old_field,Field *orig_field) { pos= (char*) sql_memdup(tmp.ptr(),tmp.length()+1); pos[tmp.length()]=0; - def=new Item_string(pos,tmp.length(), charset); + def= new Item_string(pos, tmp.length(), charset); } } #ifdef HAVE_SPATIAL diff --git a/sql/field.h b/sql/field.h index b62b7a7859e..75bb96f2f6d 100644 --- a/sql/field.h +++ b/sql/field.h @@ -46,14 +46,24 @@ public: char *ptr; // Position to field in record uchar *null_ptr; // Byte where null_bit is struct st_table *table; // Pointer for table + struct st_table *orig_table; // Pointer to original table const char *table_name,*field_name; LEX_STRING comment; ulong query_id; // For quick test of used fields /* Field is part of the following keys */ key_map key_start,part_of_key,part_of_sortkey; + /* + We use three additional unireg types for TIMESTAMP to overcome limitation + of current binary format of .frm file. We'd like to be able to support + NOW() as default and on update value for such fields but unable to hold + this info anywhere except unireg_check field. This issue will be resolved + in more clean way with transition to new text based .frm format. + See also comment for Field_timestamp::Field_timestamp(). + */ enum utype { NONE,DATE,SHIELD,NOEMPTY,CASEUP,PNR,BGNR,PGNR,YES,NO,REL, CHECK,EMPTY,UNKNOWN_FIELD,CASEDN,NEXT_NUMBER,INTERVAL_FIELD, - BIT_FIELD, TIMESTAMP_FIELD,CAPITALIZE,BLOB_FIELD}; + BIT_FIELD, TIMESTAMP_OLD_FIELD, CAPITALIZE, BLOB_FIELD, + TIMESTAMP_DN_FIELD, TIMESTAMP_UN_FIELD, TIMESTAMP_DNUN_FIELD}; enum geometry_type { GEOM_GEOMETRY = 0, GEOM_POINT = 1, GEOM_LINESTRING = 2, GEOM_POLYGON = 3, @@ -77,7 +87,6 @@ public: uint32 field_length; // Length of field uint16 flags; uchar null_bit; // Bit used to test null bit - uint abs_offset; // use only in group_concat Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, @@ -643,7 +652,11 @@ public: void set_time(); virtual void set_default() { - set_time(); + if (table->timestamp_field == this && + unireg_check != TIMESTAMP_UN_FIELD) + set_time(); + else + Field::set_default(); } inline long get_timestamp() { @@ -658,6 +671,7 @@ public: bool get_date(TIME *ltime,uint fuzzydate); bool get_time(TIME *ltime); field_cast_enum field_cast_type() { return FIELD_CAST_TIMESTAMP; } + void set_timestamp_offsets(); }; @@ -679,6 +693,7 @@ public: String *val_str(String*,String *); bool send_binary(Protocol *protocol); void sql_type(String &str) const; + bool store_for_compare() { return 1; } field_cast_enum field_cast_type() { return FIELD_CAST_YEAR; } }; @@ -860,7 +875,8 @@ public: uint max_packed_col_length(uint max_length); uint size_of() const { return sizeof(*this); } enum_field_types real_type() const { return FIELD_TYPE_STRING; } - bool has_charset(void) const { return TRUE; } + bool has_charset(void) const + { return charset() == &my_charset_bin ? FALSE : TRUE; } field_cast_enum field_cast_type() { return FIELD_CAST_STRING; } }; @@ -907,7 +923,8 @@ public: uint max_packed_col_length(uint max_length); uint size_of() const { return sizeof(*this); } enum_field_types real_type() const { return FIELD_TYPE_VAR_STRING; } - bool has_charset(void) const { return TRUE; } + bool has_charset(void) const + { return charset() == &my_charset_bin ? FALSE : TRUE; } field_cast_enum field_cast_type() { return FIELD_CAST_VARSTRING; } }; @@ -949,14 +966,9 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return (uint32) (packlength+table->blob_ptr_size); } - uint32 max_data_length() const + inline uint32 max_data_length() const { - switch (packlength) { - case 1: return 255; - case 2: return (uint32) 0xFFFFL; - case 3: return (uint32) 0xFFFFFF; - default: return (uint32) 0xFFFFFFFF; - } + return (uint32) (((ulonglong) 1 << (packlength*8)) -1); } void reset(void) { bzero(ptr, packlength+sizeof(char*)); } void reset_fields() { bzero((char*) &value,sizeof(value)); } @@ -1185,8 +1197,8 @@ Field *make_field(char *ptr, uint32 field_length, struct st_table *table); uint pack_length_to_packflag(uint type); uint32 calc_pack_length(enum_field_types type,uint32 length); -bool set_field_to_null(Field *field); -bool set_field_to_null_with_conversions(Field *field, bool no_conversions); +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); diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 67905d35e0d..5632c63c521 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -109,7 +109,7 @@ static void do_outer_field_to_null_str(Copy_field *copy) } -bool +int set_field_to_null(Field *field) { if (field->real_maybe_null()) @@ -127,7 +127,7 @@ set_field_to_null(Field *field) if (!current_thd->no_errors) my_printf_error(ER_BAD_NULL_ERROR,ER(ER_BAD_NULL_ERROR),MYF(0), field->field_name); - return 1; + return -1; } @@ -145,11 +145,11 @@ set_field_to_null(Field *field) RETURN VALUES 0 Field could take 0 or an automatic conversion was used - 1 Field could not take NULL and no conversion was used. + -1 Field could not take NULL and no conversion was used. If no_conversion was not set, an error message is printed */ -bool +int set_field_to_null_with_conversions(Field *field, bool no_conversions) { if (field->real_maybe_null()) @@ -159,7 +159,7 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions) return 0; } if (no_conversions) - return 1; + return -1; /* Check if this is a special type, which will get a special walue @@ -173,7 +173,7 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions) field->reset(); if (field == field->table->next_number_field) { - field->table->auto_increment_field_not_null= false; + field->table->auto_increment_field_not_null= FALSE; return 0; // field is set in handler.cc } if (current_thd->count_cuted_fields == CHECK_FIELD_WARN) @@ -184,7 +184,7 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions) if (!current_thd->no_errors) my_printf_error(ER_BAD_NULL_ERROR,ER(ER_BAD_NULL_ERROR),MYF(0), field->field_name); - return 1; + return -1; } diff --git a/sql/filesort.cc b/sql/filesort.cc index d09b8936d42..1a1547a9d67 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -57,17 +57,38 @@ static SORT_ADDON_FIELD *get_addon_fields(THD *thd, Field **ptabfield, static void unpack_addon_fields(struct st_sort_addon_field *addon_field, byte *buff); - /* - Creates a set of pointers that can be used to read the rows - in sorted order. This should be done with the functions - in records.cc +/* + Sort a table + + SYNOPSIS + filesort() + table Table to sort + sortorder How to sort the table + s_length Number of elements in sortorder + select condition to apply to the rows + special Not used. + (This could be used to sort the rows pointed on by + select->file) + examined_rows Store number of examined rows here + + IMPLEMENTATION + Creates a set of pointers that can be used to read the rows + in sorted order. This should be done with the functions + in records.cc + + REQUIREMENTS + Before calling filesort, one must have done + table->file->info(HA_STATUS_VARIABLE) - Before calling filesort, one must have done - table->file->info(HA_STATUS_VARIABLE) + RETURN + HA_POS_ERROR Error + # Number of rows + + examined_rows will be set to number of examined rows - The result set is stored in table->io_cache or - table->record_pointers - */ + The result set is stored in table->io_cache or + table->record_pointers +*/ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, SQL_SELECT *select, ha_rows max_rows, ha_rows *examined_rows) @@ -543,6 +564,8 @@ static void make_sortkey(register SORTPARAM *param, case STRING_RESULT: { CHARSET_INFO *cs=item->collation.collation; + char fill_char= ((cs->state & MY_CS_BINSORT) ? (char) 0 : ' '); + if ((maybe_null=item->maybe_null)) *to++=1; /* All item->str() to use some extra byte for end null.. */ @@ -579,14 +602,16 @@ 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) - bzero((char*) to+tmp_length,sort_field->length-tmp_length); + cs->cset->fill(cs, (char*) to+tmp_length, + sort_field->length-tmp_length, + fill_char); } else { my_strnxfrm(cs,(uchar*)to,length,(const uchar*)res->ptr(),length); - bzero((char *)to+length,diff); + cs->cset->fill(cs, (char *)to+length,diff,fill_char); } - break; + break; } case INT_RESULT: { @@ -674,9 +699,22 @@ static void make_sortkey(register SORTPARAM *param, for ( ; (field= addonf->field) ; addonf++) { if (addonf->null_bit && field->is_null()) + { nulls[addonf->null_offset]|= addonf->null_bit; +#ifdef HAVE_purify + bzero(to, addonf->length); +#endif + } else - field->pack((char *) to, field->ptr); + { + uchar *end= (uchar*) field->pack((char *) to, field->ptr); +#ifdef HAVE_purify + uint length= (uint) ((to + addonf->length) - end); + DBUG_ASSERT((int) length >= 0); + if (length) + bzero(end, length); +#endif + } to+= addonf->length; } } @@ -863,7 +901,8 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, strpos+= (uint) (error= (int) read_to_buffer(from_file, buffpek, rec_length)); if (error == -1) - goto err; /* purecov: inspected */ + goto err; /* purecov: inspected */ + buffpek->max_keys= buffpek->mem_count; // If less data in buffers than expected queue_insert(&queue, (byte*) buffpek); } diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc index 6df7d867a4d..5dc7c50e04c 100644 --- a/sql/gen_lex_hash.cc +++ b/sql/gen_lex_hash.cc @@ -81,7 +81,6 @@ TODO: #endif #include <my_getopt.h> #include "mysql_version.h" -#include "mysql_priv.h" #include "lex.h" struct my_option my_long_options[] = diff --git a/sql/gstream.cc b/sql/gstream.cc index a97ed9cae03..f7d11d76b0c 100644 --- a/sql/gstream.cc +++ b/sql/gstream.cc @@ -1,139 +1,120 @@ +/* Copyright (C) 2004 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 */ + +/* + Functions to read and parse geometrical data. + NOTE: These functions assumes that the string is end \0 terminated! +*/ + #include "mysql_priv.h" -int GTextReadStream::get_next_toc_type() const +enum Gis_read_stream::enum_tok_types Gis_read_stream::get_next_toc_type() { - const char *cur = m_cur; - while ((*cur)&&(strchr(" \t\r\n",*cur))) - { - cur++; - } - if (!(*cur)) - { + skip_space(); + if (m_cur >= m_limit) return eostream; - } - - if (((*cur>='a') && (*cur<='z')) || ((*cur>='A') && (*cur<='Z')) || - (*cur=='_')) - { + if (my_isvar_start(&my_charset_bin, *m_cur)) return word; - } - - if (((*cur>='0') && (*cur<='9')) || (*cur=='-') || (*cur=='+') || - (*cur=='.')) - { + if ((*m_cur >= '0' && *m_cur <= '9') || *m_cur == '-' || *m_cur == '+') return numeric; - } - - if (*cur == '(') - { + if (*m_cur == '(') return l_bra; - } - - if (*cur == ')') - { + if (*m_cur == ')') return r_bra; - } - - if (*cur == ',') - { + if (*m_cur == ',') return comma; - } - return unknown; } -const char *GTextReadStream::get_next_word(int *word_len) -{ - const char *cur = m_cur; - while ((*cur)&&(strchr(" \t\r\n",*cur))) - { - cur++; - } - m_last_text_position = cur; - - if (!(*cur)) - { - return 0; - } - const char *wd_start = cur; - - if (((*cur<'a') || (*cur>'z')) && ((*cur<'A') || (*cur>'Z')) && (*cur!='_')) - { - return NULL; - } +bool Gis_read_stream::get_next_word(LEX_STRING *res) +{ + skip_space(); + res->str= (char*) m_cur; + /* The following will also test for \0 */ + if (!my_isvar_start(&my_charset_bin, *m_cur)) + return 1; - ++cur; + /* + We can't combine the following increment with my_isvar() because + my_isvar() is a macro that would cause side effects + */ + m_cur++; + while ((m_cur < m_limit) && my_isvar(&my_charset_bin, *m_cur)) + m_cur++; - while (((*cur>='a') && (*cur<='z')) || ((*cur>='A') && (*cur<='Z')) || - (*cur=='_') || ((*cur>='0') && (*cur<='9'))) - { - ++cur; - } + res->length= (uint32) (m_cur - res->str); + return 0; +} - *word_len = cur - wd_start; - m_cur = cur; +/* + Read a floating point number - return wd_start; -} + NOTE: Number must start with a digit or sign. It can't start with a decimal + point +*/ -int GTextReadStream::get_next_number(double *d) +bool Gis_read_stream::get_next_number(double *d) { - const char *cur = m_cur; - while ((*cur)&&(strchr(" \t\r\n",*cur))) - { - cur++; - } + char *endptr; + int err; - m_last_text_position = cur; - if (!(*cur)) - { - set_error_msg("Numeric constant expected"); - return 1; - } + skip_space(); - if (((*cur<'0') || (*cur>'9')) && (*cur!='-') && (*cur!='+') && (*cur!='.')) + if ((m_cur >= m_limit) || + (*m_cur < '0' || *m_cur > '9') && *m_cur != '-' && *m_cur != '+') { set_error_msg("Numeric constant expected"); return 1; } - char *endptr; - - *d = strtod(cur, &endptr); - + *d = my_strntod(m_charset, (char *)m_cur, + m_limit-m_cur, &endptr, &err); + if (err) + return 1; if (endptr) - { m_cur = endptr; - } - return 0; } -char GTextReadStream::get_next_symbol() + +bool Gis_read_stream::check_next_symbol(char symbol) { - const char *cur = m_cur; - while ((*cur)&&(strchr(" \t\r\n",*cur))) + skip_space(); + if ((m_cur >= m_limit) || (*m_cur != symbol)) { - cur++; - } - if (!(*cur)) - { - return 0; + char buff[32]; + strmov(buff, "'?' expected"); + buff[2]= symbol; + set_error_msg(buff); + return 1; } + m_cur++; + return 0; +} - m_cur = cur + 1; - m_last_text_position = cur; - return *cur; -} +/* + Remember error message. +*/ -void GTextReadStream::set_error_msg(const char *msg) +void Gis_read_stream::set_error_msg(const char *msg) { - size_t len = strlen(msg); - m_err_msg = (char *)my_realloc(m_err_msg, len + 1, MYF(MY_ALLOW_ZERO_PTR)); + size_t len= strlen(msg); // ok in this context + m_err_msg= (char *) my_realloc(m_err_msg, len + 1, MYF(MY_ALLOW_ZERO_PTR)); memcpy(m_err_msg, msg, len + 1); } - - diff --git a/sql/gstream.h b/sql/gstream.h index a3914a534dd..bfbf28851ce 100644 --- a/sql/gstream.h +++ b/sql/gstream.h @@ -15,10 +15,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -class GTextReadStream +class Gis_read_stream { public: - enum TokTypes + enum enum_tok_types { unknown, eostream, @@ -29,41 +29,48 @@ public: comma }; - GTextReadStream(const char *buffer, int size) - :m_cur(buffer), m_limit(buffer + size), m_last_text_position(buffer), - m_err_msg(NULL) + Gis_read_stream(CHARSET_INFO *charset, const char *buffer, int size) + :m_cur(buffer), m_limit(buffer + size), m_err_msg(NULL), m_charset(charset) {} - GTextReadStream(): m_cur(NULL), m_limit(NULL), m_err_msg(NULL) + Gis_read_stream(): m_cur(NullS), m_limit(NullS), m_err_msg(NullS) {} - - ~GTextReadStream() + ~Gis_read_stream() { my_free(m_err_msg, MYF(MY_ALLOW_ZERO_PTR)); } - int get_next_toc_type() const; - const char *get_next_word(int *word_len); - int get_next_number(double *d); - char get_next_symbol(); + enum enum_tok_types get_next_toc_type(); + bool get_next_word(LEX_STRING *); + bool get_next_number(double *); + bool check_next_symbol(char); - const char *get_last_text_position() const + inline void skip_space() { - return m_last_text_position; + while ((m_cur < m_limit) && my_isspace(&my_charset_latin1, *m_cur)) + m_cur++; + } + /* Skip next character, if match. Return 1 if no match */ + inline bool skip_char(char skip) + { + skip_space(); + if ((m_cur >= m_limit) || *m_cur != skip) + return 1; /* Didn't find char */ + m_cur++; + return 0; } - void set_error_msg(const char *msg); // caller should free this pointer char *get_error_msg() { char *err_msg = m_err_msg; - m_err_msg = NULL; + m_err_msg= NullS; return err_msg; } protected: const char *m_cur; const char *m_limit; - const char *m_last_text_position; char *m_err_msg; + CHARSET_INFO *m_charset; }; diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index c4735403267..87eced3d149 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -72,7 +72,7 @@ #define STATUS_BDB_ANALYZE 4 const char *ha_berkeley_ext=".db"; -bool berkeley_skip=0,berkeley_shared_data=0; +bool berkeley_shared_data=0; u_int32_t berkeley_init_flags= DB_PRIVATE | DB_RECOVER, berkeley_env_flags=0, berkeley_lock_type=DB_LOCK_DEFAULT; ulong berkeley_cache_size, berkeley_log_buffer_size, berkeley_log_file_size=0; @@ -547,7 +547,10 @@ int ha_berkeley::open(const char *name, int mode, uint test_if_locked) (*ptr)->set_bt_compare(*ptr, berkeley_cmp_packed_key); (*ptr)->app_private= (void*) (table->key_info+i); if (!(table->key_info[i].flags & HA_NOSAME)) + { + DBUG_PRINT("bdb",("Setting DB_DUP for key %u", i)); (*ptr)->set_flags(*ptr, DB_DUP); + } if ((error= txn_begin(db_env, 0, (DB_TXN**) &transaction, 0)) || (error=((*ptr)->open(*ptr, transaction, name_buff, part, DB_BTREE, open_mode, 0))) || @@ -823,8 +826,8 @@ int ha_berkeley::write_row(byte * record) DBUG_ENTER("write_row"); statistic_increment(ha_write_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(record+table->time_stamp-1); + if (table->timestamp_default_now) + update_timestamp(record+table->timestamp_default_now-1); if (table->next_number_field && record == table->record[0]) update_auto_increment(); if ((error=pack_row(&row, record,1))) @@ -1070,8 +1073,8 @@ int ha_berkeley::update_row(const byte * old_row, byte * new_row) LINT_INIT(error); statistic_increment(ha_update_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(new_row+table->time_stamp-1); + if (table->timestamp_on_update_now) + update_timestamp(new_row+table->timestamp_on_update_now-1); if (hidden_primary_key) { @@ -1340,7 +1343,7 @@ int ha_berkeley::index_init(uint keynr) int ha_berkeley::index_end() { int error=0; - DBUG_ENTER("index_end"); + DBUG_ENTER("ha_berkely::index_end"); if (cursor) { DBUG_PRINT("enter",("table: '%s'", table->real_name)); @@ -1661,8 +1664,9 @@ void ha_berkeley::info(uint flag) share->rec_per_key[i]; } } - else if (flag & HA_STATUS_ERRKEY) - errkey=last_dup_key; + /* Don't return key if we got an error for the internal primary key */ + if (flag & HA_STATUS_ERRKEY && last_dup_key < table->keys) + errkey= last_dup_key; DBUG_VOID_RETURN; } @@ -1860,7 +1864,7 @@ static int create_sub_table(const char *table_name, const char *sub_name, int error; DB *file; DBUG_ENTER("create_sub_table"); - DBUG_PRINT("enter",("sub_name: %s",sub_name)); + DBUG_PRINT("enter",("sub_name: %s flags: %d",sub_name, flags)); if (!(error=db_create(&file, db_env, 0))) { @@ -1892,14 +1896,14 @@ int ha_berkeley::create(const char *name, register TABLE *form, char name_buff[FN_REFLEN]; char part[7]; uint index=1; - int error=1; + int error; DBUG_ENTER("ha_berkeley::create"); fn_format(name_buff,name,"", ha_berkeley_ext,2 | 4); /* Create the main table that will hold the real rows */ - if (create_sub_table(name_buff,"main",DB_BTREE,0)) - DBUG_RETURN(1); /* purecov: inspected */ + if ((error= create_sub_table(name_buff,"main",DB_BTREE,0))) + DBUG_RETURN(error); /* purecov: inspected */ primary_key=table->primary_key; /* Create the keys */ @@ -1908,10 +1912,10 @@ int ha_berkeley::create(const char *name, register TABLE *form, if (i != primary_key) { sprintf(part,"key%02d",index++); - if (create_sub_table(name_buff, part, DB_BTREE, - (table->key_info[i].flags & HA_NOSAME) ? 0 : - DB_DUP)) - DBUG_RETURN(1); /* purecov: inspected */ + if ((error= create_sub_table(name_buff, part, DB_BTREE, + (table->key_info[i].flags & HA_NOSAME) ? 0 : + DB_DUP))) + DBUG_RETURN(error); /* purecov: inspected */ } } @@ -1919,16 +1923,15 @@ int ha_berkeley::create(const char *name, register TABLE *form, /* Is DB_BTREE the best option here ? (QUEUE can't be used in sub tables) */ DB *status_block; - if (!db_create(&status_block, db_env, 0)) + if (!(error=(db_create(&status_block, db_env, 0)))) { - if (!status_block->open(status_block, NULL, name_buff, - "status", DB_BTREE, DB_CREATE, 0)) + if (!(error=(status_block->open(status_block, NULL, name_buff, + "status", DB_BTREE, DB_CREATE, 0)))) { char rec_buff[4+MAX_KEY*4]; uint length= 4+ table->keys*4; bzero(rec_buff, length); - if (!write_status(status_block, rec_buff, length)) - error=0; + error= write_status(status_block, rec_buff, length); status_block->close(status_block,0); } } @@ -1936,6 +1939,7 @@ int ha_berkeley::create(const char *name, register TABLE *form, } + int ha_berkeley::delete_table(const char *name) { int error; @@ -2119,8 +2123,46 @@ static void print_msg(THD *thd, const char *table_name, const char *op_name, int ha_berkeley::analyze(THD* thd, HA_CHECK_OPT* check_opt) { - DB_BTREE_STAT *stat=0; uint i; + DB_BTREE_STAT *stat=0; + DB_TXN_STAT *txn_stat_ptr= 0; + + /* + Original bdb documentation says: + "The DB->stat method cannot be transaction-protected. + For this reason, it should be called in a thread of + control that has no open cursors or active transactions." + So, let's check if there are any changes have been done since + the beginning of the transaction.. + */ + + if (!db_env->txn_stat(db_env, &txn_stat_ptr, 0) && + txn_stat_ptr && txn_stat_ptr->st_nactive>=2) + { + DB_TXN_ACTIVE *atxn_stmt= 0, *atxn_all= 0; + + DB_TXN *txn_all= (DB_TXN*) thd->transaction.all.bdb_tid; + u_int32_t all_id= txn_all->id(txn_all); + + DB_TXN *txn_stmt= (DB_TXN*) thd->transaction.stmt.bdb_tid; + u_int32_t stmt_id= txn_stmt->id(txn_stmt); + + DB_TXN_ACTIVE *cur= txn_stat_ptr->st_txnarray; + DB_TXN_ACTIVE *end= cur + txn_stat_ptr->st_nactive; + for (; cur!=end && (!atxn_stmt || !atxn_all); cur++) + { + if (cur->txnid==all_id) atxn_all= cur; + if (cur->txnid==stmt_id) atxn_stmt= cur; + } + + if (atxn_stmt && atxn_all && + log_compare(&atxn_stmt->lsn,&atxn_all->lsn)) + { + free(txn_stat_ptr); + return HA_ADMIN_REJECT; + } + free(txn_stat_ptr); + } for (i=0 ; i < table->keys ; i++) { diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h index f225c24eaf7..d25a9bbab69 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -88,10 +88,11 @@ class ha_berkeley: public handler public: ha_berkeley(TABLE *table): handler(table), alloc_ptr(0),rec_buff(0), file(0), int_table_flags(HA_REC_NOT_IN_SEQ | - HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | - HA_NULL_KEY | HA_BLOB_KEY | HA_NOT_EXACT_COUNT | - HA_PRIMARY_KEY_IN_READ_INDEX | HA_DROP_BEFORE_CREATE | - HA_AUTO_PART_KEY | HA_TABLE_SCAN_ON_INDEX), + HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | + HA_NULL_KEY | HA_BLOB_KEY | HA_NOT_EXACT_COUNT | + HA_PRIMARY_KEY_IN_READ_INDEX | HA_DROP_BEFORE_CREATE | + HA_AUTO_PART_KEY | HA_TABLE_SCAN_ON_INDEX | + HA_KEY_READ_WRONG_STR | HA_FILE_BASED), changed_rows(0),last_dup_key((uint) -1),version(0),using_ignore(0) { } @@ -170,7 +171,7 @@ class ha_berkeley: public handler bool primary_key_is_clustered() { return true; } }; -extern bool berkeley_skip, berkeley_shared_data; +extern bool berkeley_shared_data; extern u_int32_t berkeley_init_flags,berkeley_env_flags, berkeley_lock_type, berkeley_lock_types[]; extern ulong berkeley_cache_size, berkeley_max_lock, berkeley_log_buffer_size; diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc index c1228cbd319..94105fb9409 100644 --- a/sql/ha_heap.cc +++ b/sql/ha_heap.cc @@ -38,9 +38,22 @@ int ha_heap::open(const char *name, int mode, uint test_if_locked) HA_CREATE_INFO create_info; bzero(&create_info, sizeof(create_info)); if (!create(name, table, &create_info)) + { file= heap_open(name, mode); + implicit_emptied= 1; + } } ref_length= sizeof(HEAP_PTR); + if (file) + { + /* Initialize variables for the opened table */ + btree_keys.clear_all(); + for (uint i= 0 ; i < table->keys ; i++) + { + if (table->key_info[i].algorithm == HA_KEY_ALG_BTREE) + btree_keys.set_bit(i); + } + } return (file ? 0 : 1); } @@ -52,8 +65,8 @@ int ha_heap::close(void) int ha_heap::write_row(byte * buf) { statistic_increment(ha_write_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(buf+table->time_stamp-1); + if (table->timestamp_default_now) + update_timestamp(buf+table->timestamp_default_now-1); if (table->next_number_field && buf == table->record[0]) update_auto_increment(); return heap_write(file,buf); @@ -62,8 +75,8 @@ int ha_heap::write_row(byte * buf) int ha_heap::update_row(const byte * old_data, byte * new_data) { statistic_increment(ha_update_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(new_data+table->time_stamp-1); + if (table->timestamp_on_update_now) + update_timestamp(new_data+table->timestamp_on_update_now-1); return heap_update(file,old_data,new_data); } diff --git a/sql/ha_heap.h b/sql/ha_heap.h index c369c7029b4..feadc0c3c0f 100644 --- a/sql/ha_heap.h +++ b/sql/ha_heap.h @@ -26,6 +26,7 @@ class ha_heap: public handler { HP_INFO *file; + key_map btree_keys; public: ha_heap(TABLE *table): handler(table), file(0) {} @@ -49,6 +50,7 @@ class ha_heap: public handler (HA_ONLY_WHOLE_INDEX | HA_WRONG_ASCII_ORDER | HA_NOT_READ_PREFIX_LAST)); } + const key_map *keys_to_use_for_scanning() { return &btree_keys; } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 8bd43ea92cd..31b7fb807f3 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -80,7 +80,6 @@ extern "C" { #define HA_INNOBASE_ROWS_IN_TABLE 10000 /* to get optimization right */ #define HA_INNOBASE_RANGE_COUNT 100 -bool innodb_skip = 0; uint innobase_init_flags = 0; ulong innobase_cache_size = 0; @@ -135,7 +134,6 @@ static mysql_byte* innobase_get_key(INNOBASE_SHARE *share,uint *length, my_bool not_used __attribute__((unused))); static INNOBASE_SHARE *get_share(const char *table_name); static void free_share(INNOBASE_SHARE *share); -static void innobase_print_error(const char* db_errpfx, char* buffer); /* General functions */ @@ -290,7 +288,7 @@ convert_error_code_to_mysql( } else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) { - return(HA_WRONG_CREATE_OPTION); + return(HA_ERR_ROW_IS_REFERENCED); } else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) { @@ -552,7 +550,7 @@ innobase_query_caching_of_table_permitted( if (thd->variables.tx_isolation == ISO_SERIALIZABLE) { /* In the SERIALIZABLE mode we add LOCK IN SHARE MODE to every - plain SELECT */ + plain SELECT if AUTOCOMMIT is not on. */ return((my_bool)FALSE); } @@ -703,7 +701,7 @@ ha_innobase::init_table_handle_for_HANDLER(void) /* Always fetch all columns in the index record */ - prebuilt->hint_no_need_to_fetch_extra_cols = FALSE; + prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS; /* We want always to fetch all columns in the whole row? Or do we???? */ @@ -755,7 +753,7 @@ innobase_init(void) srv_set_thread_priorities = TRUE; srv_query_thread_priority = QUERY_PRIOR; } - + /* Set InnoDB initialization parameters according to the values read from MySQL .cnf file */ @@ -870,16 +868,22 @@ innobase_init(void) srv_print_verbose_log = mysql_embedded ? 0 : 1; - if (strcmp(default_charset_info->name, "latin1") == 0) { + /* Store the default charset-collation number of this MySQL + installation */ - /* Store the character ordering table to InnoDB. - For non-latin1 charsets we use the MySQL comparison - functions, and consequently we do not need to know - the ordering internally in InnoDB. */ + data_mysql_default_charset_coll = (ulint)default_charset_info->number; - memcpy(srv_latin1_ordering, - default_charset_info->sort_order, 256); - } + data_mysql_latin1_swedish_charset_coll = + (ulint)my_charset_latin1.number; + + /* Store the latin1_swedish_ci character ordering table to InnoDB. For + non-latin1_swedish_ci charsets we use the MySQL comparison functions, + and consequently we do not need to know the ordering internally in + InnoDB. */ + + ut_a(0 == strcmp((char*)my_charset_latin1.name, + (char*)"latin1_swedish_ci")); + memcpy(srv_latin1_ordering, my_charset_latin1.sort_order, 256); /* Since we in this module access directly the fields of a trx struct, and due to different headers and flags it might happen that @@ -976,28 +980,13 @@ innobase_commit_low( } #ifdef HAVE_REPLICATION - /* TODO: Guilhem should check if master_log_name, pending - etc. are right if the master log gets rotated! Possible bug here. - Comment by Heikki March 4, 2003. */ - if (current_thd->slave_thread) { /* Update the replication position info inside InnoDB */ trx->mysql_master_log_file_name = active_mi->rli.group_master_log_name; - /* - Guilhem to Heikki: in 5.0 we don't need to do a computation - (old_pos+len) to get the end_pos, because we already have the - end_pos under hand in the replication code - (Query_log_event::exec_event()). - I tested the code change below (simulated a crash with kill - -9) and got the good (binlog, position) displayed by InnoDB at - crash recovery, so this code change is ok. - */ - trx->mysql_master_log_pos = ((ib_longlong) - (active_mi->rli.future_group_master_log_pos - )); - + trx->mysql_master_log_pos= ((ib_longlong) + active_mi->rli.future_group_master_log_pos); } #endif /* HAVE_REPLICATION */ @@ -1302,18 +1291,6 @@ innobase_close_connection( return(0); } -/********************************************************************** -Prints an error message. */ -static -void -innobase_print_error( -/*=================*/ - const char* db_errpfx, /* in: error prefix text */ - char* buffer) /* in: error text */ -{ - sql_print_error("%s: %s", db_errpfx, buffer); -} - /***************************************************************************** ** InnoDB database tables @@ -1670,10 +1647,10 @@ reset_null_bits( extern "C" { /***************************************************************** -InnoDB uses this function is to compare two data fields for which the -data type is such that we must use MySQL code to compare them. NOTE that the -prototype of this function is in rem0cmp.c in InnoDB source code! -If you change this function, remember to update the prototype there! */ +InnoDB uses this function to compare two data fields for which the data type +is such that we must use MySQL code to compare them. NOTE that the prototype +of this function is in rem0cmp.c in InnoDB source code! If you change this +function, remember to update the prototype there! */ int innobase_mysql_cmp( @@ -1681,6 +1658,7 @@ innobase_mysql_cmp( /* out: 1, 0, -1, if a is greater, equal, less than b, respectively */ int mysql_type, /* in: MySQL type */ + uint charset_number, /* in: number of the charset */ unsigned char* a, /* in: data field */ unsigned int a_length, /* in: data field length, not UNIV_SQL_NULL */ @@ -1688,6 +1666,7 @@ innobase_mysql_cmp( unsigned int b_length) /* in: data field length, not UNIV_SQL_NULL */ { + CHARSET_INFO* charset; enum_field_types mysql_tp; int ret; @@ -1704,9 +1683,27 @@ innobase_mysql_cmp( case FIELD_TYPE_MEDIUM_BLOB: case FIELD_TYPE_BLOB: case FIELD_TYPE_LONG_BLOB: - // BAR TODO: Discuss with heikki.tuuri@innodb.com - // so that he sends CHARSET_INFO for the field to this function. - ret = my_strnncoll(default_charset_info, + /* Use the charset number to pick the right charset struct for + the comparison. Since the MySQL function get_charset may be + slow before Bar removes the mutex operation there, we first + look at 2 common charsets directly. */ + + if (charset_number == default_charset_info->number) { + charset = default_charset_info; + } else if (charset_number == my_charset_latin1.number) { + charset = &my_charset_latin1; + } else { + charset = get_charset(charset_number, MYF(MY_WME)); + + if (charset == NULL) { + fprintf(stderr, +"InnoDB: fatal error: InnoDB needs charset %lu for doing a comparison,\n" +"InnoDB: but MySQL cannot find that charset.\n", (ulong)charset_number); + ut_a(0); + } + } + + ret = my_strnncoll(charset, a, a_length, b, b_length); if (ret < 0) { @@ -1733,9 +1730,9 @@ get_innobase_type_from_mysql_type( /* out: DATA_BINARY, DATA_VARCHAR, ... */ Field* field) /* in: MySQL field */ { - /* The following asserts check that the MySQL type code fits in - 8 bits: this is used in ibuf and also when DATA_NOT_NULL is - ORed to the type */ + /* The following asserts try to check that the MySQL type code fits in + 8 bits: this is used in ibuf and also when DATA_NOT_NULL is ORed to + the type */ DBUG_ASSERT((ulint)FIELD_TYPE_STRING < 256); DBUG_ASSERT((ulint)FIELD_TYPE_VAR_STRING < 256); @@ -1750,8 +1747,8 @@ get_innobase_type_from_mysql_type( return(DATA_BINARY); } else if (strcmp( - default_charset_info->name, - "latin1") == 0) { + field->charset()->name, + "latin1_swedish_ci") == 0) { return(DATA_VARCHAR); } else { return(DATA_VARMYSQL); @@ -1760,8 +1757,8 @@ get_innobase_type_from_mysql_type( return(DATA_FIXBINARY); } else if (strcmp( - default_charset_info->name, - "latin1") == 0) { + field->charset()->name, + "latin1_swedish_ci") == 0) { return(DATA_CHAR); } else { return(DATA_MYSQL); @@ -1935,6 +1932,7 @@ build_template( ulint n_fields; ulint n_requested_fields = 0; ibool fetch_all_in_key = FALSE; + ibool fetch_primary_key_cols = FALSE; ulint i; if (prebuilt->select_lock_type == LOCK_X) { @@ -1945,8 +1943,9 @@ build_template( templ_type = ROW_MYSQL_WHOLE_ROW; } - if (templ_type == ROW_MYSQL_REC_FIELDS - && !prebuilt->hint_no_need_to_fetch_extra_cols) { + if (templ_type == ROW_MYSQL_REC_FIELDS) { + if (prebuilt->hint_need_to_fetch_extra_cols + == ROW_RETRIEVE_ALL_COLS) { /* We know we must at least fetch all columns in the key, or all columns in the table */ @@ -1961,15 +1960,18 @@ build_template( fetch_all_in_key = TRUE; } else { - /* We are building a temporary table: fetch all - columns; the reason is that MySQL may use the - clustered index key to store rows, but the mechanism - we use below to detect required columns does not - reveal that. Actually, it might be enough to - fetch only all in the key also in this case! */ - templ_type = ROW_MYSQL_WHOLE_ROW; } + } else if (prebuilt->hint_need_to_fetch_extra_cols + == ROW_RETRIEVE_PRIMARY_KEY) { + /* We must at least fetch all primary key cols. Note that if + the clustered index was internally generated by InnoDB on the + row id (no primary key was defined), then + row_search_for_mysql() will always retrieve the row id to a + special buffer in the prebuilt struct. */ + + fetch_primary_key_cols = TRUE; + } } clust_index = dict_table_get_first_index_noninline(prebuilt->table); @@ -1988,7 +1990,7 @@ build_template( the clustered index */ } - n_fields = (ulint)table->fields; + n_fields = (ulint)table->fields; /* number of columns */ if (!prebuilt->mysql_template) { prebuilt->mysql_template = (mysql_row_templ_t*) @@ -2001,6 +2003,8 @@ build_template( prebuilt->templ_contains_blob = FALSE; + /* Note that in InnoDB, i is the column number. MySQL calls columns + 'fields'. */ for (i = 0; i < n_fields; i++) { templ = prebuilt->mysql_template + n_requested_fields; field = table->field[i]; @@ -2016,7 +2020,9 @@ build_template( if (templ_type == ROW_MYSQL_REC_FIELDS && ((prebuilt->read_just_key && !index_contains_field) || (!(fetch_all_in_key && index_contains_field) - && thd->query_id != field->query_id))) { + !(fetch_primary_key_cols && + dict_table_col_in_clustered_key(index->table, i) && + thd->query_id != field->query_id))) { /* This field is not needed in the query, skip it */ @@ -2097,14 +2103,32 @@ ha_innobase::write_row( DBUG_ENTER("ha_innobase::write_row"); - ut_ad(prebuilt->trx == - (trx_t*) current_thd->transaction.all.innobase_tid); + if (prebuilt->trx != + (trx_t*) current_thd->transaction.all.innobase_tid) { + char err_buf[2000]; + + fprintf(stderr, +"InnoDB: Error: the transaction object for the table handle is at\n" +"InnoDB: %lx, but for the current thread it is at %lx\n", + (ulong)prebuilt->trx, + (ulong)current_thd->transaction.all.innobase_tid); + + ut_sprintf_buf(err_buf, ((byte*)prebuilt) - 100, 200); + fprintf(stderr, +"InnoDB: Dump of 200 bytes around prebuilt: %.1000s\n", err_buf); + + ut_sprintf_buf(err_buf, + ((byte*)(&(current_thd->transaction.all))) - 100, 200); + fprintf(stderr, +"InnoDB: Dump of 200 bytes around transaction.all: %.1000s\n", err_buf); + + ut_a(0); + } statistic_increment(ha_write_count, &LOCK_status); - if (table->time_stamp) { - update_timestamp(record + table->time_stamp - 1); - } + if (table->timestamp_default_now) + update_timestamp(record + table->timestamp_default_now - 1); if (last_query_id != user_thd->query_id) { prebuilt->sql_stat_start = TRUE; @@ -2475,9 +2499,8 @@ ha_innobase::update_row( ut_ad(prebuilt->trx == (trx_t*) current_thd->transaction.all.innobase_tid); - if (table->time_stamp) { - update_timestamp(new_row + table->time_stamp - 1); - } + if (table->timestamp_on_update_now) + update_timestamp(new_row + table->timestamp_on_update_now - 1); if (last_query_id != user_thd->query_id) { prebuilt->sql_stat_start = TRUE; @@ -3249,7 +3272,7 @@ create_table_def( ulint nulls_allowed; ulint unsigned_type; ulint binary_type; - ulint nonlatin1_type; + ulint charset_no; ulint i; DBUG_ENTER("create_table_def"); @@ -3278,24 +3301,28 @@ create_table_def( unsigned_type = 0; } - if (col_type == DATA_BLOB - && strcmp(default_charset_info->name, "latin1") != 0) { - nonlatin1_type = DATA_NONLATIN1; - } else { - nonlatin1_type = 0; - } - if (field->binary()) { binary_type = DATA_BINARY_TYPE; - nonlatin1_type = 0; } else { binary_type = 0; } + charset_no = 0; + + if (dtype_is_string_type(col_type)) { + + charset_no = (ulint)field->charset()->number; + + ut_a(charset_no < 256); /* in ut0type.h we assume that + the number fits in one byte */ + } + dict_mem_table_add_col(table, (char*) field->field_name, - col_type, (ulint)field->type() + col_type, dtype_form_prtype( + (ulint)field->type() | nulls_allowed | unsigned_type - | nonlatin1_type | binary_type, + | binary_type, + + charset_no), field->pack_length(), 0); } @@ -3479,7 +3506,7 @@ ha_innobase::create( /* The limit probably should be REC_MAX_N_FIELDS - 3 = 1020, but we play safe here */ - return(HA_ERR_TO_BIG_ROW); + DBUG_RETURN(HA_ERR_TO_BIG_ROW); } /* Get the transaction associated with the current thd, or create one @@ -3693,6 +3720,7 @@ ha_innobase::delete_table( int error; trx_t* parent_trx; trx_t* trx; + THD *thd= current_thd; char norm_name[1000]; DBUG_ENTER("ha_innobase::delete_table"); @@ -3718,6 +3746,14 @@ ha_innobase::delete_table( trx->mysql_thd = current_thd; trx->mysql_query_str = &((*current_thd).query); + if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) { + trx->check_foreigns = FALSE; + } + + if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) { + trx->check_unique_secondary = FALSE; + } + name_len = strlen(name); assert(name_len < 1000); @@ -3799,6 +3835,10 @@ innobase_drop_database( trx->mysql_thd = current_thd; trx->mysql_query_str = &((*current_thd).query); + if (current_thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) { + trx->check_foreigns = FALSE; + } + error = row_drop_database_for_mysql(namebuf, trx); /* Flush the log to reduce probability that the .frm files and @@ -4414,6 +4454,27 @@ ha_innobase::get_foreign_key_create_info(void) } /*********************************************************************** +Checks if a table is referenced by a foreign key. The MySQL manual states that +a REPLACE is either equivalent to an INSERT, or DELETE(s) + INSERT. Only a +delete is then allowed internally to resolve a duplicate key conflict in +REPLACE, not an update. */ + +uint +ha_innobase::referenced_by_foreign_key(void) +/*========================================*/ + /* out: > 0 if referenced by a FOREIGN KEY */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt; + + if (dict_table_referenced_by_foreign_key(prebuilt->table)) { + + return(1); + } + + return(0); +} + +/*********************************************************************** Frees the foreign key create info for a table stored in InnoDB, if it is non-NULL. */ @@ -4463,7 +4524,14 @@ ha_innobase::extra( prebuilt->read_just_key = 0; break; case HA_EXTRA_RETRIEVE_ALL_COLS: - prebuilt->hint_no_need_to_fetch_extra_cols = FALSE; + prebuilt->hint_need_to_fetch_extra_cols + = ROW_RETRIEVE_ALL_COLS; + break; + case HA_EXTRA_RETRIEVE_PRIMARY_KEY: + if (prebuilt->hint_need_to_fetch_extra_cols == 0) { + prebuilt->hint_need_to_fetch_extra_cols + = ROW_RETRIEVE_PRIMARY_KEY; + } break; case HA_EXTRA_KEYREAD: prebuilt->read_just_key = 1; @@ -4524,7 +4592,7 @@ ha_innobase::start_stmt( auto_inc_counter_for_this_stat = 0; prebuilt->sql_stat_start = TRUE; - prebuilt->hint_no_need_to_fetch_extra_cols = TRUE; + prebuilt->hint_need_to_fetch_extra_cols = 0; prebuilt->read_just_key = 0; if (!prebuilt->mysql_has_locked) { @@ -4601,7 +4669,7 @@ ha_innobase::external_lock( trx = prebuilt->trx; prebuilt->sql_stat_start = TRUE; - prebuilt->hint_no_need_to_fetch_extra_cols = TRUE; + prebuilt->hint_need_to_fetch_extra_cols = 0; prebuilt->read_just_key = 0; @@ -4629,11 +4697,17 @@ ha_innobase::external_lock( } if (trx->isolation_level == TRX_ISO_SERIALIZABLE - && prebuilt->select_lock_type == LOCK_NONE) { - - /* To get serializable execution we let InnoDB - conceptually add 'LOCK IN SHARE MODE' to all SELECTs - which otherwise would have been consistent reads */ + && prebuilt->select_lock_type == LOCK_NONE + && (thd->options + & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { + + /* To get serializable execution, we let InnoDB + conceptually add 'LOCK IN SHARE MODE' to all SELECTs + which otherwise would have been consistent reads. An + exception is consistent reads in the AUTOCOMMIT=1 mode: + we know that they are read-only transactions, and they + can be serialized also if performed as consistent + reads. */ prebuilt->select_lock_type = LOCK_S; } @@ -4700,7 +4774,7 @@ innodb_show_status( DBUG_ENTER("innodb_show_status"); - if (innodb_skip) { + if (have_innodb != SHOW_OPTION_YES) { my_message(ER_NOT_SUPPORTED_YET, "Cannot call SHOW INNODB STATUS because skip-innodb is defined", MYF(0)); @@ -4939,7 +5013,7 @@ ha_innobase::innobase_read_and_init_auto_inc( /* Play safe and also give in another way the hint to fetch all columns in the key: */ - prebuilt->hint_no_need_to_fetch_extra_cols = FALSE; + prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS; prebuilt->trx->mysql_n_tables_locked += 1; diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index c305a019fcd..c1a1b57472a 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -95,7 +95,6 @@ class ha_innobase: public handler HA_NOT_EXACT_COUNT | HA_NO_WRITE_DELAYED | HA_PRIMARY_KEY_IN_READ_INDEX | - HA_DROP_BEFORE_CREATE | HA_TABLE_SCAN_ON_INDEX), last_dup_key((uint) -1), start_of_scan(0) @@ -181,6 +180,7 @@ class ha_innobase: public handler int check(THD* thd, HA_CHECK_OPT* check_opt); char* update_table_comment(const char* comment); char* get_foreign_key_create_info(); + uint referenced_by_foreign_key(); void free_foreign_key_create_info(char* str); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); @@ -190,7 +190,6 @@ class ha_innobase: public handler bool primary_key_is_clustered() { return true; } }; -extern bool innodb_skip; extern uint innobase_init_flags, innobase_lock_type; extern uint innobase_flush_log_at_trx_commit; extern ulong innobase_cache_size; diff --git a/sql/ha_isam.cc b/sql/ha_isam.cc index 8025e5169c8..52fea2f7a15 100644 --- a/sql/ha_isam.cc +++ b/sql/ha_isam.cc @@ -34,7 +34,6 @@ ** isam tables *****************************************************************************/ -bool isam_skip; const char **ha_isam::bas_ext() const { static const char *ext[]= { ".ISM",".ISD", NullS }; return ext; } @@ -71,8 +70,8 @@ uint ha_isam::min_record_length(uint options) const int ha_isam::write_row(byte * buf) { statistic_increment(ha_write_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(buf+table->time_stamp-1); + if (table->timestamp_default_now) + update_timestamp(buf+table->timestamp_default_now-1); if (table->next_number_field && buf == table->record[0]) update_auto_increment(); return !nisam_write(file,buf) ? 0 : my_errno ? my_errno : -1; @@ -81,8 +80,8 @@ int ha_isam::write_row(byte * buf) int ha_isam::update_row(const byte * old_data, byte * new_data) { statistic_increment(ha_update_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(new_data+table->time_stamp-1); + if (table->timestamp_on_update_now) + update_timestamp(new_data+table->timestamp_on_update_now-1); return !nisam_update(file,old_data,new_data) ? 0 : my_errno ? my_errno : -1; } @@ -279,7 +278,7 @@ int ha_isam::create(const char *name, register TABLE *form, type=HA_KEYTYPE_BINARY; // Keep compiler happy if (!(recinfo= (N_RECINFO*) my_malloc((form->fields*2+2)*sizeof(N_RECINFO), MYF(MY_WME)))) - DBUG_RETURN(1); + DBUG_RETURN(HA_ERR_OUT_OF_MEM); pos=form->key_info; for (i=0; i < form->keys ; i++, pos++) diff --git a/sql/ha_isam.h b/sql/ha_isam.h index 3cea79da3ea..129777e68e5 100644 --- a/sql/ha_isam.h +++ b/sql/ha_isam.h @@ -34,7 +34,7 @@ class ha_isam: public handler :handler(table), file(0), int_table_flags(HA_READ_RND_SAME | HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | HA_KEY_READ_WRONG_STR | HA_DUPP_POS | - HA_NOT_DELETE_WITH_CACHE) + HA_NOT_DELETE_WITH_CACHE | HA_FILE_BASED) {} ~ha_isam() {} const char *table_type() const { return "ISAM"; } @@ -82,4 +82,3 @@ class ha_isam: public handler enum thr_lock_type lock_type); }; -extern bool isam_skip; diff --git a/sql/ha_isammrg.cc b/sql/ha_isammrg.cc index 9915c182e26..8f7056a15fa 100644 --- a/sql/ha_isammrg.cc +++ b/sql/ha_isammrg.cc @@ -78,8 +78,8 @@ int ha_isammrg::write_row(byte * buf) int ha_isammrg::update_row(const byte * old_data, byte * new_data) { statistic_increment(ha_update_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(new_data+table->time_stamp-1); + if (table->timestamp_on_update_now) + update_timestamp(new_data+table->timestamp_on_update_now-1); return !mrg_update(file,old_data,new_data) ? 0 : my_errno ? my_errno : -1; } diff --git a/sql/ha_isammrg.h b/sql/ha_isammrg.h index c936a15164a..e5846d20212 100644 --- a/sql/ha_isammrg.h +++ b/sql/ha_isammrg.h @@ -33,7 +33,7 @@ class ha_isammrg: public handler const char *table_type() const { return "MRG_ISAM"; } const char **bas_ext() const; ulong table_flags() const { return (HA_READ_RND_SAME | HA_KEYPOS_TO_RNDPOS | - HA_REC_NOT_IN_SEQ); } + HA_REC_NOT_IN_SEQ | HA_FILE_BASED); } ulong index_flags(uint idx) const { return HA_NOT_READ_PREFIX_LAST; } uint max_record_length() const { return HA_MAX_REC_LENGTH; } diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 1a597415ff3..c992e92edb8 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -253,9 +253,8 @@ int ha_myisam::write_row(byte * buf) statistic_increment(ha_write_count,&LOCK_status); /* If we have a timestamp column, update it to the current time */ - - if (table->time_stamp) - update_timestamp(buf+table->time_stamp-1); + if (table->timestamp_default_now) + update_timestamp(buf+table->timestamp_default_now-1); /* If we have an auto_increment column and we are writing a changed row @@ -818,26 +817,19 @@ int ha_myisam::preload_keys(THD* thd, HA_CHECK_OPT *check_opt) deactivate_non_unique_index() rows Rows to be inserted 0 if we don't know - HA_POS_ERROR if we want to disable all keys + HA_POS_ERROR if we want to force disabling + and make it permanent (save on disk) */ void ha_myisam::deactivate_non_unique_index(ha_rows rows) { MYISAM_SHARE* share = file->s; - bool do_warning=0; if (share->state.key_map == ((ulonglong) 1L << share->base.keys)-1) { if (!(specialflag & SPECIAL_SAFE_MODE)) { - if (rows == HA_POS_ERROR) - { - uint orig_update= file->update; - file->update ^= HA_STATE_CHANGED; - uint check_update= file->update; + if (rows == HA_POS_ERROR) // force disable and save it on disk! mi_extra(file, HA_EXTRA_NO_KEYS, 0); - do_warning= (file->update == check_update) && file->state->records; - file->update= orig_update; - } else { /* @@ -847,15 +839,13 @@ void ha_myisam::deactivate_non_unique_index(ha_rows rows) we don't want to update the key statistics based of only a few rows. */ if (file->state->records == 0 && - (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT)) + (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES)) mi_disable_non_unique_index(file,rows); - else - { + else if (!file->bulk_insert && + (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT)) mi_init_bulk_insert(file, - current_thd->variables.bulk_insert_buff_size, - rows); - table->bulk_insert= 1; - } + current_thd->variables.bulk_insert_buff_size, + rows); } } enable_activate_all_index=1; @@ -863,10 +853,6 @@ void ha_myisam::deactivate_non_unique_index(ha_rows rows) } else enable_activate_all_index=0; - if (do_warning) - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA, - ER(ER_ILLEGAL_HA), table->table_name); } @@ -878,7 +864,6 @@ bool ha_myisam::activate_all_index(THD *thd) DBUG_ENTER("activate_all_index"); mi_end_bulk_insert(file); - table->bulk_insert= 0; if (enable_activate_all_index && share->state.key_map != set_bits(ulonglong, share->base.keys)) { @@ -937,8 +922,8 @@ bool ha_myisam::is_crashed() const int ha_myisam::update_row(const byte * old_data, byte * new_data) { statistic_increment(ha_update_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(new_data+table->time_stamp-1); + if (table->timestamp_on_update_now) + update_timestamp(new_data+table->timestamp_on_update_now-1); return mi_update(file,old_data,new_data); } @@ -1205,7 +1190,7 @@ int ha_myisam::create(const char *name, register TABLE *table_arg, ((table_arg->key_parts + table_arg->keys) * sizeof(HA_KEYSEG)), NullS))) - DBUG_RETURN(1); + DBUG_RETURN(HA_ERR_OUT_OF_MEM); pos=table_arg->key_info; for (i=0; i < table_arg->keys ; i++, pos++) @@ -1362,6 +1347,7 @@ int ha_myisam::create(const char *name, register TABLE *table_arg, create_info.data_file_name= info->data_file_name; create_info.index_file_name=info->index_file_name; + /* TODO: Check that the following fn_format is really needed */ error=mi_create(fn_format(buff,name,"","",2+4), table_arg->keys,keydef, (uint) (recinfo_pos-recinfo), recinfo, @@ -1392,8 +1378,8 @@ longlong ha_myisam::get_auto_increment() return auto_increment_value; } - if (table->bulk_insert) - mi_flush_bulk_insert(file, table->next_number_index); + /* it's safe to call the following if bulk_insert isn't on */ + mi_flush_bulk_insert(file, table->next_number_index); longlong nr; int error; diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index e6297373fea..4d66639690d 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -37,7 +37,7 @@ extern ulong myisam_recover_options; class ha_myisam: public handler { MI_INFO *file; - uint int_table_flags; + ulong int_table_flags; char *data_file_name, *index_file_name; bool enable_activate_all_index; int repair(THD *thd, MI_CHECK ¶m, bool optimize); @@ -46,7 +46,8 @@ class ha_myisam: public handler ha_myisam(TABLE *table): handler(table), file(0), int_table_flags(HA_READ_RND_SAME | HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | HA_NULL_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | - HA_DUPP_POS | HA_BLOB_KEY | HA_AUTO_PART_KEY | HA_HAS_GEOMETRY), + HA_DUPP_POS | HA_BLOB_KEY | HA_AUTO_PART_KEY | + HA_FILE_BASED | HA_HAS_GEOMETRY), enable_activate_all_index(1) {} ~ha_myisam() {} @@ -64,6 +65,7 @@ class ha_myisam: public handler uint max_keys() const { return MI_MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } uint max_key_length() const { return MI_MAX_KEY_LENGTH; } + uint max_key_part_length() { return MI_MAX_KEY_LENGTH; } uint checksum() const; int open(const char *name, int mode, uint test_if_locked); diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 3cd5d96d5f3..7c36f6c6e0e 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -82,8 +82,8 @@ int ha_myisammrg::close(void) int ha_myisammrg::write_row(byte * buf) { statistic_increment(ha_write_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(buf+table->time_stamp-1); + if (table->timestamp_default_now) + update_timestamp(buf+table->timestamp_default_now-1); if (table->next_number_field && buf == table->record[0]) update_auto_increment(); return myrg_write(file,buf); @@ -92,8 +92,8 @@ int ha_myisammrg::write_row(byte * buf) int ha_myisammrg::update_row(const byte * old_data, byte * new_data) { statistic_increment(ha_update_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(new_data+table->time_stamp-1); + if (table->timestamp_on_update_now) + update_timestamp(new_data+table->timestamp_on_update_now); return myrg_update(file,old_data,new_data); } @@ -386,7 +386,7 @@ int ha_myisammrg::create(const char *name, register TABLE *form, if (!(table_names= (char**) thd->alloc((create_info->merge_list.elements+1)* sizeof(char*)))) - DBUG_RETURN(1); + DBUG_RETURN(HA_ERR_OUT_OF_MEM); for (pos=table_names ; tables ; tables=tables->next) { char *table_name; @@ -399,7 +399,7 @@ int ha_myisammrg::create(const char *name, register TABLE *form, mysql_real_data_home, tables->db, tables->real_name); if (!(table_name= thd->strmake(buff, length))) - DBUG_RETURN(1); + DBUG_RETURN(HA_ERR_OUT_OF_MEM); } else table_name=(*tbl)->path; diff --git a/sql/ha_myisammrg.h b/sql/ha_myisammrg.h index 008f5339caf..ea53b40739d 100644 --- a/sql/ha_myisammrg.h +++ b/sql/ha_myisammrg.h @@ -36,7 +36,7 @@ class ha_myisammrg: public handler { return (HA_REC_NOT_IN_SEQ | HA_READ_RND_SAME | HA_AUTO_PART_KEY | HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | - HA_NULL_KEY | HA_BLOB_KEY); + HA_NULL_KEY | HA_BLOB_KEY | HA_FILE_BASED); } ulong index_flags(uint inx) const { diff --git a/sql/handler.cc b/sql/handler.cc index 0191a7139cd..f9be90c9379 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -120,34 +120,21 @@ const char *ha_get_storage_engine(enum db_type db_type) enum db_type ha_checktype(enum db_type database_type) { + show_table_type_st *types; + for (types= sys_table_types; types->type; types++) + { + if ((database_type == types->db_type) && + (*types->value == SHOW_OPTION_YES)) + return database_type; + } + switch (database_type) { -#ifdef HAVE_BERKELEY_DB - case DB_TYPE_BERKELEY_DB: - if (berkeley_skip) break; - return (database_type); -#endif -#ifdef HAVE_INNOBASE_DB - case DB_TYPE_INNODB: - if (innodb_skip) break; - return (database_type); -#endif #ifndef NO_HASH case DB_TYPE_HASH: -#endif -#ifdef HAVE_ISAM - case DB_TYPE_ISAM: - if (isam_skip) break; return (database_type); - case DB_TYPE_MRG_ISAM: - return (isam_skip ? DB_TYPE_MRG_MYISAM : database_type); -#else +#endif case DB_TYPE_MRG_ISAM: return (DB_TYPE_MRG_MYISAM); -#endif - case DB_TYPE_HEAP: - case DB_TYPE_MYISAM: - case DB_TYPE_MRG_MYISAM: - return (database_type); /* Database exists on system */ default: break; } @@ -165,7 +152,8 @@ handler *get_new_handler(TABLE *table, enum db_type db_type) { switch (db_type) { #ifndef NO_HASH - return new ha_hash(table); + case DB_TYPE_HASH: + return new ha_hash(table); #endif #ifdef HAVE_ISAM case DB_TYPE_MRG_ISAM: @@ -203,30 +191,32 @@ handler *get_new_handler(TABLE *table, enum db_type db_type) int ha_init() { + int error= 0; #ifdef HAVE_BERKELEY_DB - if (!berkeley_skip) + if (have_berkeley_db == SHOW_OPTION_YES) { - int error; - if ((error=berkeley_init())) - return error; - if (!berkeley_skip) // If we couldn't use handler - opt_using_transactions=1; + if (berkeley_init()) + { + have_berkeley_db= SHOW_OPTION_DISABLED; // If we couldn't use handler + error= 1; + } else - have_berkeley_db=SHOW_OPTION_DISABLED; + opt_using_transactions=1; } #endif #ifdef HAVE_INNOBASE_DB - if (!innodb_skip) + if (have_innodb == SHOW_OPTION_YES) { if (innobase_init()) - return -1; - if (!innodb_skip) // If we couldn't use handler - opt_using_transactions=1; + { + have_innodb= SHOW_OPTION_DISABLED; // If we couldn't use handler + error= 1; + } else - have_innodb=SHOW_OPTION_DISABLED; + opt_using_transactions=1; } #endif - return 0; + return error; } /* close, flush or restart databases */ @@ -246,11 +236,11 @@ int ha_panic(enum ha_panic_function flag) error|=mi_panic(flag); error|=myrg_panic(flag); #ifdef HAVE_BERKELEY_DB - if (!berkeley_skip) + if (have_berkeley_db == SHOW_OPTION_YES) error|=berkeley_end(); #endif #ifdef HAVE_INNOBASE_DB - if (!innodb_skip) + if (have_innodb == SHOW_OPTION_YES) error|=innobase_end(); #endif return error; @@ -259,7 +249,7 @@ int ha_panic(enum ha_panic_function flag) void ha_drop_database(char* path) { #ifdef HAVE_INNOBASE_DB - if (!innodb_skip) + if (have_innodb == SHOW_OPTION_YES) innobase_drop_database(path); #endif } @@ -267,7 +257,7 @@ void ha_drop_database(char* path) void ha_close_connection(THD* thd) { #ifdef HAVE_INNOBASE_DB - if (!innodb_skip) + if (have_innodb == SHOW_OPTION_YES) innobase_close_connection(thd); #endif } @@ -415,6 +405,16 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) my_b_tell(&thd->transaction.trans_log)) { mysql_bin_log.write(thd, &thd->transaction.trans_log, 1); + statistic_increment(binlog_cache_use, &LOCK_status); + if (thd->transaction.trans_log.disk_writes != 0) + { + /* + We have to do this after addition of trans_log to main binlog since + this operation can cause flushing of end of trans_log to disk. + */ + statistic_increment(binlog_cache_disk_use, &LOCK_status); + thd->transaction.trans_log.disk_writes= 0; + } reinit_io_cache(&thd->transaction.trans_log, WRITE_CACHE, (my_off_t) 0, 0, 1); thd->transaction.trans_log.end_of_file= max_binlog_cache_size; @@ -496,17 +496,29 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) operation_done=1; } #endif - if (trans == &thd->transaction.all) + if ((trans == &thd->transaction.all) && mysql_bin_log.is_open()) { /* Update the binary log with a BEGIN/ROLLBACK block if we have cached some queries and we updated some non-transactional table. Such cases should be rare (updating a non-transactional table inside a transaction...). + Count disk writes to trans_log in any case. */ - if (unlikely((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && - mysql_bin_log.is_open() && - my_b_tell(&thd->transaction.trans_log))) - mysql_bin_log.write(thd, &thd->transaction.trans_log, 0); + if (my_b_tell(&thd->transaction.trans_log)) + { + if (unlikely(thd->options & OPTION_STATUS_NO_TRANS_UPDATE)) + mysql_bin_log.write(thd, &thd->transaction.trans_log, 0); + statistic_increment(binlog_cache_use, &LOCK_status); + if (thd->transaction.trans_log.disk_writes != 0) + { + /* + We have to do this after addition of trans_log to main binlog since + this operation can cause flushing of end of trans_log to disk. + */ + statistic_increment(binlog_cache_disk_use, &LOCK_status); + thd->transaction.trans_log.disk_writes= 0; + } + } /* Flushed or not, empty the binlog cache */ reinit_io_cache(&thd->transaction.trans_log, WRITE_CACHE, (my_off_t) 0, 0, 1); @@ -569,7 +581,7 @@ int ha_rollback_to_savepoint(THD *thd, char *savepoint_name) my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), error); error=1; } - else + else if (mysql_bin_log.is_open()) { /* Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some @@ -577,7 +589,6 @@ int ha_rollback_to_savepoint(THD *thd, char *savepoint_name) from the SAVEPOINT command. */ if (unlikely((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && - mysql_bin_log.is_open() && my_b_tell(&thd->transaction.trans_log))) { Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE); @@ -606,23 +617,26 @@ Return value: always 0, that is, succeeds always int ha_savepoint(THD *thd, char *savepoint_name) { - my_off_t binlog_cache_pos=0; int error=0; DBUG_ENTER("ha_savepoint"); #ifdef USING_TRANSACTIONS if (opt_using_transactions) { - binlog_cache_pos=my_b_tell(&thd->transaction.trans_log); -#ifdef HAVE_INNOBASE_DB - innobase_savepoint(thd,savepoint_name, binlog_cache_pos); -#endif - /* Write it to the binary log (see comments of ha_rollback_to_savepoint). */ + /* Write it to the binary log (see comments of ha_rollback_to_savepoint) */ if (mysql_bin_log.is_open()) { +#ifdef HAVE_INNOBASE_DB + innobase_savepoint(thd,savepoint_name, + my_b_tell(&thd->transaction.trans_log)); +#endif Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE); if (mysql_bin_log.write(&qinfo)) error= 1; } +#ifdef HAVE_INNOBASE_DB + else + innobase_savepoint(thd,savepoint_name,0); +#endif } #endif /* USING_TRANSACTIONS */ DBUG_RETURN(error); @@ -632,11 +646,13 @@ bool ha_flush_logs() { bool result=0; #ifdef HAVE_BERKELEY_DB - if (!berkeley_skip && berkeley_flush_logs()) + if ((have_berkeley_db == SHOW_OPTION_YES) && + berkeley_flush_logs()) result=1; #endif #ifdef HAVE_INNOBASE_DB - if (!innodb_skip && innobase_flush_logs()) + if ((have_innodb == SHOW_OPTION_YES) && + innobase_flush_logs()) result=1; #endif return result; @@ -649,14 +665,23 @@ bool ha_flush_logs() int ha_delete_table(enum db_type table_type, const char *path) { + char tmp_path[FN_REFLEN]; handler *file=get_new_handler((TABLE*) 0, table_type); if (!file) return ENOENT; + if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED)) + { + /* Ensure that table handler get path in lower case */ + strmov(tmp_path, path); + my_casedn_str(system_charset_info, tmp_path); + path= tmp_path; + } int error=file->delete_table(path); delete file; return error; } + void ha_store_ptr(byte *buff, uint pack_length, my_off_t pos) { switch (pack_length) { @@ -877,11 +902,11 @@ void handler::update_auto_increment() table->auto_increment_field_not_null && current_thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO) { - table->auto_increment_field_not_null= false; + table->auto_increment_field_not_null= FALSE; auto_increment_column_changed=0; DBUG_VOID_RETURN; } - table->auto_increment_field_not_null= false; + table->auto_increment_field_not_null= FALSE; thd=current_thd; if ((nr=thd->next_insert_id)) thd->next_insert_id=0; // Clear after use @@ -1136,6 +1161,7 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info, { int error; TABLE table; + char name_buff[FN_REFLEN]; DBUG_ENTER("ha_create_table"); if (openfrm(name,"",0,(uint) READ_ALL, 0, &table)) @@ -1146,19 +1172,19 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info, if (table.file->table_flags() & HA_DROP_BEFORE_CREATE) table.file->delete_table(name); // Needed for BDB tables } + if (lower_case_table_names == 2 && + !(table.file->table_flags() & HA_FILE_BASED)) + { + /* Ensure that handler gets name in lower case */ + strmov(name_buff, name); + my_casedn_str(system_charset_info, name_buff); + name= name_buff; + } + error=table.file->create(name,&table,create_info); VOID(closefrm(&table)); if (error) - { - if (table.db_type == DB_TYPE_INNODB) - { - /* Creation of InnoDB table cannot fail because of an OS error: - put error as the number */ - my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,error); - } - else - my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,my_errno); - } + my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,error); DBUG_RETURN(error != 0); } diff --git a/sql/handler.h b/sql/handler.h index 4a8abf5f131..182f84e523f 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -41,6 +41,7 @@ #define HA_ADMIN_CORRUPT -3 #define HA_ADMIN_INTERNAL_ERROR -4 #define HA_ADMIN_INVALID -5 +#define HA_ADMIN_REJECT -6 /* Bits in table_flags() to show what database can do */ #define HA_READ_RND_SAME 1 /* Read RND-record to KEY-record @@ -70,12 +71,14 @@ #define HA_CAN_SQL_HANDLER (1 << 22) #define HA_NO_AUTO_INCREMENT (1 << 23) #define HA_HAS_CHECKSUM (1 << 24) - /* Next record gives next record according last record read (even if database is updated after read). Not used at this point. */ -#define HA_LASTKEY_ORDER (1 << 25) +#define HA_LASTKEY_ORDER (1 << 25) +/* Table data are stored in separate files */ +#define HA_FILE_BASED (1 << 26) + /* bits in index_flags(index_number) for what you can do with index */ @@ -167,8 +170,9 @@ enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED, typedef struct st_ha_create_information { CHARSET_INFO *table_charset, *default_table_charset; - char *comment,*password; - char *data_file_name, *index_file_name; + const char *comment,*password; + const char *data_file_name, *index_file_name; + const char *alias; ulonglong max_rows,min_rows; ulonglong auto_increment_value; ulong table_options; @@ -230,6 +234,7 @@ public: uint raid_type,raid_chunks; FT_INFO *ft_handler; bool auto_increment_column_changed; + bool implicit_emptied; /* Can be !=0 only if HEAP */ handler(TABLE *table_arg) :table(table_arg), ref(0), data_file_length(0), max_data_file_length(0), index_file_length(0), @@ -238,7 +243,7 @@ public: create_time(0), check_time(0), update_time(0), key_used_on_scan(MAX_KEY), active_index(MAX_REF_PARTS), ref_length(sizeof(my_off_t)), block_size(0), - raid_type(0), ft_handler(0) + raid_type(0), ft_handler(0), implicit_emptied(0) {} virtual ~handler(void) {} int ha_open(const char *name, int mode, int test_if_locked); @@ -337,6 +342,8 @@ public: virtual void append_create_info(String *packet) {} virtual char* get_foreign_key_create_info() { return(NULL);} /* gets foreign key create string from InnoDB */ + /* used in REPLACE; is > 0 if table is referred by a FOREIGN KEY */ + virtual uint referenced_by_foreign_key() { return 0;} virtual void init_table_handle_for_HANDLER() { return; } /* prepare InnoDB for HANDLER */ virtual void free_foreign_key_create_info(char* str) {} diff --git a/sql/hostname.cc b/sql/hostname.cc index c9cb2a43963..c74d230bbcb 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -27,9 +27,6 @@ extern "C" { // Because of SCO 3.2V4.2 #endif #if !defined( __WIN__) && !defined(OS2) -#if !defined(__NETWARE__) -#include <sys/resource.h> -#endif /* __NETWARE__ */ #ifdef HAVE_SYS_UN_H #include <sys/un.h> #endif diff --git a/sql/init.cc b/sql/init.cc index 033dfd72843..4beb8db0c6f 100644 --- a/sql/init.cc +++ b/sql/init.cc @@ -34,9 +34,6 @@ void unireg_init(ulong options) current_pid=(ulong) getpid(); /* Save for later ref */ init_time(); /* Init time-functions (read zone) */ -#ifdef USE_MY_ATOF - init_my_atof(); /* use our atof */ -#endif #ifndef EMBEDDED_LIBRARY my_abort_hook=unireg_abort; /* Abort with close of databases */ #endif @@ -49,7 +46,5 @@ void unireg_init(ulong options) log_10[i]= nr ; nr*= 10.0; } specialflag|=options; /* Set options from argv */ - - thread_stack_min=thread_stack - STACK_MIN_SIZE; DBUG_VOID_RETURN; } diff --git a/sql/item.cc b/sql/item.cc index d0502b30d88..5f31f3fa6ec 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -104,21 +104,49 @@ void Item::print_item_w_name(String *str) Item_ident::Item_ident(const char *db_name_par,const char *table_name_par, const char *field_name_par) - :db_name(db_name_par),table_name(table_name_par),field_name(field_name_par), - depended_from(0) + :orig_db_name(db_name_par), orig_table_name(table_name_par), + orig_field_name(field_name_par), changed_during_fix_field(0), + db_name(db_name_par), table_name(table_name_par), + field_name(field_name_par), cached_field_index(NO_CACHED_FIELD_INDEX), + cached_table(0), depended_from(0) { name = (char*) field_name_par; } // Constructor used by Item_field & Item_ref (see Item comment) -Item_ident::Item_ident(THD *thd, Item_ident *item): - Item(thd, item), - db_name(item->db_name), - table_name(item->table_name), - field_name(item->field_name), - depended_from(item->depended_from) +Item_ident::Item_ident(THD *thd, Item_ident *item) + :Item(thd, item), + orig_db_name(item->orig_db_name), + orig_table_name(item->orig_table_name), + orig_field_name(item->orig_field_name), + changed_during_fix_field(0), + db_name(item->db_name), + table_name(item->table_name), + field_name(item->field_name), + cached_field_index(item->cached_field_index), + cached_table(item->cached_table), + depended_from(item->depended_from) {} +void Item_ident::cleanup() +{ + DBUG_ENTER("Item_ident::cleanup"); + DBUG_PRINT("enter", ("b:%s(%s), t:%s(%s), f:%s(%s)", + db_name, orig_db_name, + table_name, orig_table_name, + field_name, orig_field_name)); + Item::cleanup(); + if (changed_during_fix_field) + { + *changed_during_fix_field= this; + changed_during_fix_field= 0; + } + db_name= orig_db_name; + table_name= orig_table_name; + field_name= orig_field_name; + DBUG_VOID_RETURN; +} + bool Item_ident::remove_dependence_processor(byte * arg) { DBUG_ENTER("Item_ident::remove_dependence_processor"); @@ -177,12 +205,13 @@ bool Item::eq(const Item *item, bool binary_cmp) const !my_strcasecmp(system_charset_info,name,item->name); } + bool Item_string::eq(const Item *item, bool binary_cmp) const { if (type() == item->type()) { if (binary_cmp) - return !sortcmp(&str_value, &item->str_value, &my_charset_bin); + return !stringcmp(&str_value, &item->str_value); return !sortcmp(&str_value, &item->str_value, collation.collation); } return 0; @@ -318,11 +347,21 @@ bool DTCollation::aggregate(DTCollation &dt) return 0; } -Item_field::Item_field(Field *f) :Item_ident(NullS,f->table_name,f->field_name) +Item_field::Item_field(Field *f) + :Item_ident(NullS, f->table_name, f->field_name) { set_field(f); collation.set(DERIVATION_IMPLICIT); - fixed= 1; // This item is not needed in fix_fields + fixed= 1; +} + +Item_field::Item_field(THD *thd, Field *f) + :Item_ident(NullS, thd->strdup(f->table_name), + thd->strdup(f->field_name)) +{ + set_field(f); + collation.set(DERIVATION_IMPLICIT); + fixed= 1; } // Constructor need to process subselect with temporary tables (see Item) @@ -370,6 +409,7 @@ const char *Item_ident::full_name() const /* ARGSUSED */ String *Item_field::val_str(String *str) { + DBUG_ASSERT(fixed == 1); if ((null_value=field->is_null())) return 0; str->set_charset(str_value.charset()); @@ -378,6 +418,7 @@ String *Item_field::val_str(String *str) double Item_field::val() { + DBUG_ASSERT(fixed == 1); if ((null_value=field->is_null())) return 0.0; return field->val_real(); @@ -385,6 +426,7 @@ double Item_field::val() longlong Item_field::val_int() { + DBUG_ASSERT(fixed == 1); if ((null_value=field->is_null())) return 0; return field->val_int(); @@ -469,9 +511,8 @@ bool Item_field::eq(const Item *item, bool binary_cmp) const (!my_strcasecmp(table_alias_charset, item_field->table_name, table_name) && (!item_field->db_name || - (item_field->db_name && !my_strcasecmp(table_alias_charset, - item_field->db_name, - db_name)))))); + (item_field->db_name && !strcmp(item_field->db_name, + db_name)))))); } @@ -494,6 +535,8 @@ Item *Item_field::get_tmp_table_item(THD *thd) String *Item_int::val_str(String *str) { + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); str->set(value, &my_charset_bin); return str; } @@ -508,6 +551,8 @@ void Item_int::print(String *str) String *Item_uint::val_str(String *str) { + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); str->set((ulonglong) value, &my_charset_bin); return str; } @@ -523,6 +568,8 @@ void Item_uint::print(String *str) String *Item_real::val_str(String *str) { + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); str->set(value,decimals,&my_charset_bin); return str; } @@ -539,14 +586,57 @@ void Item_string::print(String *str) bool Item_null::eq(const Item *item, bool binary_cmp) const { return item->type() == type(); } -double Item_null::val() { null_value=1; return 0.0; } -longlong Item_null::val_int() { null_value=1; return 0; } +double Item_null::val() +{ + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + null_value=1; + return 0.0; +} +longlong Item_null::val_int() +{ + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + null_value=1; + return 0; +} /* ARGSUSED */ String *Item_null::val_str(String *str) -{ null_value=1; return 0;} +{ + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + null_value=1; + return 0; +} + + +/*********************** Item_param related ******************************/ + +/* + Default function of Item_param::set_param_func, so in case + of malformed packet the server won't SIGSEGV +*/ + +static void +default_set_param_func(Item_param *param, + uchar **pos __attribute__((unused)), + ulong len __attribute__((unused))) +{ + param->set_null(); +} +Item_param::Item_param(unsigned position) : + value_is_set(FALSE), + item_result_type(STRING_RESULT), + item_type(STRING_ITEM), + item_is_time(FALSE), + long_data_supplied(FALSE), + pos_in_query(position), + set_param_func(default_set_param_func) +{ + name= (char*) "?"; +} -/* Item_param related */ void Item_param::set_null() { DBUG_ENTER("Item_param::set_null"); @@ -602,7 +692,7 @@ void Item_param::set_time(TIME *tm, timestamp_type type) ltime.time_type= type; - item_is_time= true; + item_is_time= TRUE; item_type= STRING_ITEM; value_is_set= 1; } @@ -642,7 +732,7 @@ int Item_param::save_in_field(Field *field, bool no_conversions) return 0; } String *result=val_str(&str_value); - return (field->store(result->ptr(),result->length(),field->charset())) ? -1 : 0; + return field->store(result->ptr(),result->length(),field->charset()); } bool Item_param::get_time(TIME *res) @@ -653,6 +743,7 @@ bool Item_param::get_time(TIME *res) double Item_param::val() { + DBUG_ASSERT(value_is_set == 1); int err; switch (item_result_type) { case STRING_RESULT: @@ -668,8 +759,9 @@ double Item_param::val() longlong Item_param::val_int() { - int err; - switch (item_result_type) { + DBUG_ASSERT(value_is_set == 1); + int err; + switch (item_result_type) { case STRING_RESULT: return my_strntoll(str_value.charset(), str_value.ptr(),str_value.length(),10, @@ -684,6 +776,7 @@ longlong Item_param::val_int() String *Item_param::val_str(String* str) { + DBUG_ASSERT(value_is_set == 1); switch (item_result_type) { case INT_RESULT: str->set(int_value, &my_charset_bin); @@ -702,12 +795,12 @@ String *Item_param::val_str(String* str) */ String *Item_param::query_val_str(String* str) -{ +{ + DBUG_ASSERT(value_is_set == 1); switch (item_result_type) { case INT_RESULT: case REAL_RESULT: return val_str(str); - break; default: str->set("'", 1, default_charset()); @@ -775,11 +868,22 @@ void Item_copy_string::copy() /* ARGSUSED */ String *Item_copy_string::val_str(String *str) { + // Item_copy_string is used without fix_fields call if (null_value) return (String*) 0; return &str_value; } + +int Item_copy_string::save_in_field(Field *field, bool no_conversions) +{ + if (null_value) + return set_field_to_null(field); + field->set_notnull(); + return field->store(str_value.ptr(),str_value.length(), + collation.collation); +} + /* Functions to convert item to field (for send_fields) */ @@ -789,28 +893,40 @@ bool Item::fix_fields(THD *thd, struct st_table_list *list, Item ** ref) { + + // We do not check fields which are fixed during construction + DBUG_ASSERT(fixed == 0 || basic_const_item()); fixed= 1; return 0; } double Item_ref_null_helper::val() { + DBUG_ASSERT(fixed == 1); double tmp= (*ref)->val_result(); owner->was_null|= null_value= (*ref)->null_value; return tmp; } + + longlong Item_ref_null_helper::val_int() { + DBUG_ASSERT(fixed == 1); longlong tmp= (*ref)->val_int_result(); owner->was_null|= null_value= (*ref)->null_value; return tmp; } + + String* Item_ref_null_helper::val_str(String* s) { + DBUG_ASSERT(fixed == 1); String* tmp= (*ref)->str_result(s); owner->was_null|= null_value= (*ref)->null_value; return tmp; } + + bool Item_ref_null_helper::get_date(TIME *ltime, uint fuzzydate) { return (owner->was_null|= null_value= (*ref)->get_date(ltime, fuzzydate)); @@ -850,6 +966,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) { + DBUG_ASSERT(fixed == 0); if (!field) // If field is not checked { TABLE_LIST *where= 0; @@ -896,6 +1013,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) table_list, &where, 0)) != not_found_field) { + if (!tmp) + return -1; prev_subselect_item->used_tables_cache|= tmp->table->map; prev_subselect_item->const_item_cache= 0; break; @@ -955,8 +1074,13 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) ref, (char *)table_name, (char *)field_name); + register_item_tree_changing(ref); if (!rf) return 1; + /* + rf is Item_ref => never substitute other items (in this case) + during fix_fields() => we can use rf after fix_fields() + */ if (rf->fix_fields(thd, tables, ref) || rf->check_cols(1)) return 1; @@ -975,6 +1099,10 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) (char *)field_name); if (!rf) return 1; + /* + rf is Item_ref => never substitute other items (in this case) + during fix_fields() => we can use rf after fix_fields() + */ return rf->fix_fields(thd, tables, ref) || rf->check_cols(1); } } @@ -998,8 +1126,15 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) void Item_field::cleanup() { + DBUG_ENTER("Item_field::cleanup"); Item_ident::cleanup(); + /* + Even if this object was created by direct link to field in setup_wild() + it will be linked correctly next tyme by name of field and table alias. + I.e. we can drop 'field'. + */ field= result_field= 0; + DBUG_VOID_RETURN; } void Item::init_make_field(Send_field *tmp_field, @@ -1213,7 +1348,7 @@ int Item::save_in_field(Field *field, bool no_conversions) String *result; CHARSET_INFO *cs= collation.collation; char buff[MAX_FIELD_WIDTH]; // Alloc buffer for small columns - str_value.set_quick(buff,sizeof(buff),cs); + str_value.set_quick(buff, sizeof(buff), cs); result=val_str(&str_value); if (null_value) return set_field_to_null_with_conversions(field, no_conversions); @@ -1237,7 +1372,7 @@ int Item::save_in_field(Field *field, bool no_conversions) field->set_notnull(); error=field->store(nr); } - return (error) ? -1 : 0; + return error; } @@ -1248,8 +1383,7 @@ int Item_string::save_in_field(Field *field, bool no_conversions) if (null_value) return set_field_to_null(field); field->set_notnull(); - return (field->store(result->ptr(),result->length(),collation.collation)) ? - -1 : 0; + return field->store(result->ptr(),result->length(),collation.collation); } int Item_uint::save_in_field(Field *field, bool no_conversions) @@ -1268,9 +1402,13 @@ int Item_int::save_in_field(Field *field, bool no_conversions) if (null_value) return set_field_to_null(field); field->set_notnull(); - return (field->store(nr)) ? -1 : 0; + return field->store(nr); } +Item_num *Item_uint::neg() +{ + return new Item_real(name, - ((double) value), 0, max_length); +} int Item_real::save_in_field(Field *field, bool no_conversions) { @@ -1278,7 +1416,7 @@ int Item_real::save_in_field(Field *field, bool no_conversions) if (null_value) return set_field_to_null(field); field->set_notnull(); - return (field->store(nr)) ? -1 : 0; + return field->store(nr); } /**************************************************************************** @@ -1313,10 +1451,13 @@ Item_varbinary::Item_varbinary(const char *str, uint str_length) } *ptr=0; // Keep purify happy collation.set(&my_charset_bin, DERIVATION_COERCIBLE); + fixed= 1; } longlong Item_varbinary::val_int() { + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); char *end=(char*) str_value.ptr()+str_value.length(), *ptr=end-min(str_value.length(),sizeof(longlong)); @@ -1340,7 +1481,7 @@ int Item_varbinary::save_in_field(Field *field, bool no_conversions) longlong nr=val_int(); error=field->store(nr); } - return (error) ? -1 : 0; + return error; } @@ -1473,6 +1614,7 @@ bool Item_field::send(Protocol *protocol, String *buffer) bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) { + DBUG_ASSERT(fixed == 0); uint counter; if (!ref) { @@ -1578,6 +1720,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) Item_field* fld; if (!((*reference)= fld= new Item_field(tmp))) return 1; + register_item_tree_changing(reference); mark_as_dependent(thd, last, thd->lex->current_select, fld); return 0; } @@ -1642,9 +1785,11 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) void Item_ref::cleanup() { + DBUG_ENTER("Item_ref::cleanup"); Item_ident::cleanup(); if (hook_ptr) *hook_ptr= orig_item; + DBUG_VOID_RETURN; } @@ -1683,16 +1828,19 @@ bool Item_default_value::eq(const Item *item, bool binary_cmp) const } -bool Item_default_value::fix_fields(THD *thd, struct st_table_list *table_list, Item **items) +bool Item_default_value::fix_fields(THD *thd, + struct st_table_list *table_list, + Item **items) { + DBUG_ASSERT(fixed == 0); if (!arg) - return false; - bool res= arg->fix_fields(thd, table_list, items); - if (res) - return res; - /* arg->type() can be only REF_ITEM or FIELD_ITEM for it defined as - simple_ident in sql_yacc.yy - */ + { + fixed= 1; + return 0; + } + if (arg->fix_fields(thd, table_list, &arg)) + return 1; + if (arg->type() == REF_ITEM) { Item_ref *ref= (Item_ref *)arg; @@ -1710,6 +1858,7 @@ bool Item_default_value::fix_fields(THD *thd, struct st_table_list *table_list, def_field->move_field(def_field->table->default_values - def_field->table->record[0]); set_field(def_field); + fixed= 1; return 0; } @@ -1736,13 +1885,10 @@ bool Item_insert_value::fix_fields(THD *thd, struct st_table_list *table_list, Item **items) { - bool res= arg->fix_fields(thd, table_list, items); - if (res) - return res; - /* - arg->type() can be only REF_ITEM or FIELD_ITEM as arg is - a simple_ident in sql_yacc.yy - */ + DBUG_ASSERT(fixed == 0); + if (arg->fix_fields(thd, table_list, &arg)) + return 1; + if (arg->type() == REF_ITEM) { Item_ref *ref= (Item_ref *)arg; @@ -1770,6 +1916,7 @@ bool Item_insert_value::fix_fields(THD *thd, set_field(new Field_null(0, 0, Field::NONE, tmp_field->field_name, tmp_field->table, &my_charset_bin)); } + fixed= 1; return 0; } @@ -1855,7 +2002,7 @@ bool field_is_equal_to_item(Field *field,Item *item) if (item->null_value) return 1; // This must be true field->val_str(&field_tmp,&field_tmp); - return !sortcmp(&field_tmp,item_result,&my_charset_bin); + return !stringcmp(&field_tmp,item_result); } if (res_type == INT_RESULT) return 1; // Both where of type int @@ -1933,7 +2080,8 @@ void Item_cache_str::store(Item *item) double Item_cache_str::val() -{ +{ + DBUG_ASSERT(fixed == 1); int err; if (value) return my_strntod(value->charset(), (char*) value->ptr(), @@ -1945,6 +2093,7 @@ double Item_cache_str::val() longlong Item_cache_str::val_int() { + DBUG_ASSERT(fixed == 1); int err; if (value) return my_strntoll(value->charset(), value->ptr(), diff --git a/sql/item.h b/sql/item.h index c08c17ea602..9f6aa5a9a32 100644 --- a/sql/item.h +++ b/sql/item.h @@ -102,7 +102,11 @@ public: enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE }; - String str_value; /* used to store value */ + /* + str_values's main purpose is to be used to cache the value in + save_in_field + */ + String str_value; my_string name; /* Name from select */ Item *next; uint32 max_length; @@ -129,9 +133,20 @@ public: virtual ~Item() { name=0; } /*lint -e1509 */ void set_name(const char *str,uint length, CHARSET_INFO *cs); void init_make_field(Send_field *tmp_field,enum enum_field_types type); - virtual void cleanup() { fixed=0; } + virtual void cleanup() + { + DBUG_ENTER("Item::cleanup"); + DBUG_PRINT("info", ("Type: %d", (int)type())); + fixed=0; + DBUG_VOID_RETURN; + } virtual void make_field(Send_field *field); virtual bool fix_fields(THD *, struct st_table_list *, Item **); + /* + should be used in case where we are sure that we do not need + complete fix_fields() procedure. + */ + inline void quick_fix_field() { fixed= 1; } virtual int save_in_field(Field *field, bool no_conversions); virtual void save_org_in_field(Field *field) { (void) save_in_field(field, 1); } @@ -231,7 +246,7 @@ public: Field *tmp_table_field_from_field_type(TABLE *table); /* Used in sql_select.cc:eliminate_not_funcs() */ - virtual Item *neg_transformer() { return NULL; } + virtual Item *neg_transformer(THD *thd) { return NULL; } void delete_self() { cleanup(); @@ -335,19 +350,51 @@ public: }; +class Item_num: public Item +{ +public: + virtual Item_num *neg()= 0; +}; + +#define NO_CACHED_FIELD_INDEX ((uint)(-1)) + class st_select_lex; class Item_ident :public Item { + /* + We have to store initial values of db_name, table_name and field_name + to be able to restore them during cleanup() because they can be + updated during fix_fields() to values from Field object and life-time + of those is shorter than life-time of Item_field. + */ + const char *orig_db_name; + const char *orig_table_name; + const char *orig_field_name; + Item **changed_during_fix_field; public: const char *db_name; const char *table_name; const char *field_name; + /* + Cached value of index for this field in table->field array, used by prep. + stmts for speeding up their re-execution. Holds NO_CACHED_FIELD_INDEX + if index value is not known. + */ + uint cached_field_index; + /* + Cached pointer to table which contains this field, used for the same reason + by prep. stmt. too in case then we have not-fully qualified field. + 0 - means no cached value. + */ + TABLE_LIST *cached_table; st_select_lex *depended_from; Item_ident(const char *db_name_par,const char *table_name_par, const char *field_name_par); Item_ident(THD *thd, Item_ident *item); const char *full_name() const; - + void cleanup(); + void register_item_tree_changing(Item **ref) + { changed_during_fix_field= ref; } bool remove_dependence_processor(byte * arg); }; @@ -357,14 +404,25 @@ class Item_field :public Item_ident void set_field(Field *field); public: Field *field,*result_field; - // Item_field() {} +#ifndef DBUG_OFF + bool double_fix; +#endif Item_field(const char *db_par,const char *table_name_par, const char *field_name_par) - :Item_ident(db_par,table_name_par,field_name_par),field(0),result_field(0) + :Item_ident(db_par,table_name_par,field_name_par), + field(0), result_field(0) +#ifndef DBUG_OFF + ,double_fix(0) +#endif { collation.set(DERIVATION_IMPLICIT); } // Constructor need to process subselect with temporary tables (see Item) Item_field(THD *thd, Item_field *item); + /* + Constructor used inside setup_wild(), ensures that field and table + names will live as long as Item_field (important in prep. stmt.) + */ + Item_field(THD *thd, Field *field); Item_field(Field *field); enum Type type() const { return FIELD_ITEM; } bool eq(const Item *item, bool binary_cmp) const; @@ -398,13 +456,19 @@ public: void cleanup(); friend class Item_default_value; friend class Item_insert_value; + friend class st_select_lex_unit; }; class Item_null :public Item { public: Item_null(char *name_par=0) - { maybe_null=null_value=TRUE; name= name_par ? name_par : (char*) "NULL";} + { + maybe_null= null_value= TRUE; + max_length= 0; + name= name_par ? name_par : (char*) "NULL"; + fixed= 1; + } enum Type type() const { return NULL_ITEM; } bool eq(const Item *item, bool binary_cmp) const; double val(); @@ -415,12 +479,8 @@ public: bool send(Protocol *protocol, String *str); enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_NULL; } - bool fix_fields(THD *thd, struct st_table_list *list, Item **item) - { - bool res= Item::fix_fields(thd, list, item); - max_length=0; - return res; - } + // to prevent drop fixed flag (no need parent cleanup call) + void cleanup() {} bool basic_const_item() const { return 1; } Item *new_item() { return new Item_null(name); } bool is_null() { return 1; } @@ -441,16 +501,7 @@ public: bool long_data_supplied; uint pos_in_query; - Item_param(uint position) - { - name= (char*) "?"; - pos_in_query= position; - item_type= STRING_ITEM; - item_result_type = STRING_RESULT; - item_is_time= false; - long_data_supplied= false; - value_is_set= 0; - } + Item_param(uint position); enum Type type() const { return item_type; } double val(); longlong val_int(); @@ -467,11 +518,14 @@ public: void set_time(TIME *tm, timestamp_type type); bool get_time(TIME *tm); void reset() {} -#ifndef EMBEDDED_LIBRARY - void (*setup_param_func)(Item_param *param, uchar **pos); -#else - void (*setup_param_func)(Item_param *param, uchar **pos, ulong data_len); -#endif + /* + Assign placeholder value from bind data. + Note, that 'len' has different semantics in embedded library (as we + don't need to check that packet is not broken there). See + sql_prepare.cc for details. + */ + void (*set_param_func)(Item_param *param, uchar **pos, ulong len); + enum Item_result result_type () const { return item_result_type; } String *query_val_str(String *str); @@ -487,10 +541,10 @@ public: void print(String *str) { str->append('?'); } }; -class Item_int :public Item +class Item_int :public Item_num { public: - const longlong value; + longlong value; Item_int(int32 i,uint length=11) :value((longlong) i) { max_length=length; fixed= 1; } #ifdef HAVE_LONG_LONG @@ -506,13 +560,16 @@ public: enum Type type() const { return INT_ITEM; } enum Item_result result_type () const { return INT_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } - longlong val_int() { return value; } - double val() { return (double) value; } + longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } + double val() { DBUG_ASSERT(fixed == 1); return (double) value; } String *val_str(String*); int save_in_field(Field *field, bool no_conversions); bool basic_const_item() const { return 1; } Item *new_item() { return new Item_int(name,value,max_length); } + // to prevent drop fixed flag (no need parent cleanup call) + void cleanup() {} void print(String *str); + Item_num *neg() { value= -value; return this; } }; @@ -520,32 +577,31 @@ class Item_uint :public Item_int { public: Item_uint(const char *str_arg, uint length) : - Item_int(str_arg, (longlong) strtoull(str_arg,(char**) 0,10), length) {} - Item_uint(uint32 i) :Item_int((longlong) i, 10) {} - double val() { return ulonglong2double((ulonglong)value); } + Item_int(str_arg, (longlong) strtoull(str_arg, (char**) 0,10), length) + { unsigned_flag= 1; } + Item_uint(uint32 i) :Item_int((longlong) i, 10) + { unsigned_flag= 1; } + double val() + { DBUG_ASSERT(fixed == 1); return ulonglong2double((ulonglong)value); } String *val_str(String*); Item *new_item() { return new Item_uint(name,max_length); } int save_in_field(Field *field, bool no_conversions); - bool fix_fields(THD *thd, struct st_table_list *list, Item **item) - { - bool res= Item::fix_fields(thd, list, item); - unsigned_flag= 1; - return res; - } void print(String *str); + Item_num *neg (); }; -class Item_real :public Item +class Item_real :public Item_num { public: - const double value; + double value; // Item_real() :value(0) {} - Item_real(const char *str_arg,uint length) :value(atof(str_arg)) + Item_real(const char *str_arg, uint length) :value(my_atof(str_arg)) { name=(char*) str_arg; decimals=(uint8) nr_of_decimals(str_arg); max_length=length; + fixed= 1; } Item_real(const char *str,double val_arg,uint decimal_par,uint length) :value(val_arg) @@ -553,16 +609,24 @@ public: name=(char*) str; decimals=(uint8) decimal_par; max_length=length; + fixed= 1; } - Item_real(double value_par) :value(value_par) {} + Item_real(double value_par) :value(value_par) { fixed= 1; } int save_in_field(Field *field, bool no_conversions); enum Type type() const { return REAL_ITEM; } enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } - double val() { return value; } - longlong val_int() { return (longlong) (value+(value > 0 ? 0.5 : -0.5));} + double val() { DBUG_ASSERT(fixed == 1); return value; } + longlong val_int() + { + DBUG_ASSERT(fixed == 1); + return (longlong) (value+(value > 0 ? 0.5 : -0.5)); + } String *val_str(String*); bool basic_const_item() const { return 1; } + // to prevent drop fixed flag (no need parent cleanup call) + void cleanup() {} Item *new_item() { return new Item_real(name,value,decimals,max_length); } + Item_num *neg() { value= -value; return this; } }; @@ -594,6 +658,8 @@ public: max_length= str_value.numchars()*cs->mbmaxlen; set_name(str, length, cs); decimals=NOT_FIXED_DEC; + // it is constant => can be used without fix_fields (and frequently used) + fixed= 1; } Item_string(const char *name_par, const char *str, uint length, CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE) @@ -603,21 +669,29 @@ public: max_length= str_value.numchars()*cs->mbmaxlen; set_name(name_par,0,cs); decimals=NOT_FIXED_DEC; + // it is constant => can be used without fix_fields (and frequently used) + fixed= 1; } enum Type type() const { return STRING_ITEM; } double val() - { + { + DBUG_ASSERT(fixed == 1); int err; return my_strntod(str_value.charset(), (char*) str_value.ptr(), str_value.length(), (char**) 0, &err); } longlong val_int() { + DBUG_ASSERT(fixed == 1); int err; return my_strntoll(str_value.charset(), str_value.ptr(), str_value.length(), 10, (char**) 0, &err); } - String *val_str(String*) { return (String*) &str_value; } + String *val_str(String*) + { + DBUG_ASSERT(fixed == 1); + return (String*) &str_value; + } int save_in_field(Field *field, bool no_conversions); enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_STRING; } @@ -625,11 +699,14 @@ public: bool eq(const Item *item, bool binary_cmp) const; Item *new_item() { - return new Item_string(name, str_value.ptr(), max_length, &my_charset_bin); + return new Item_string(name, str_value.ptr(), + str_value.length(), &my_charset_bin); } String *const_string() { return &str_value; } inline void append(char *str, uint length) { str_value.append(str, length); } void print(String *str); + // to prevent drop fixed flag (no need parent cleanup call) + void cleanup() {} }; /* for show tables */ @@ -671,12 +748,16 @@ class Item_varbinary :public Item public: Item_varbinary(const char *str,uint str_length); enum Type type() const { return VARBIN_ITEM; } - double val() { return (double) Item_varbinary::val_int(); } + double val() + { DBUG_ASSERT(fixed == 1); return (double) Item_varbinary::val_int(); } longlong val_int(); - String *val_str(String*) { return &str_value; } + bool basic_const_item() const { return 1; } + String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; } int save_in_field(Field *field, bool no_conversions); enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_STRING; } + // to prevent drop fixed flag (no need parent cleanup call) + void cleanup() {} }; @@ -862,6 +943,7 @@ public: String *val_str(String*); void make_field(Send_field *field) { item->make_field(field); } void copy(); + int save_in_field(Field *field, bool no_conversions); table_map used_tables() const { return (table_map) 1L; } bool const_item() const { return 0; } bool is_null() { return null_value; } @@ -934,7 +1016,6 @@ public: bool eq(const Item *item, bool binary_cmp) const; bool fix_fields(THD *, struct st_table_list *, Item **); void print(String *str); - virtual bool basic_const_item() const { return true; } int save_in_field(Field *field_arg, bool no_conversions) { if (!arg) @@ -962,7 +1043,6 @@ public: bool eq(const Item *item, bool binary_cmp) const; bool fix_fields(THD *, struct st_table_list *, Item **); void print(String *str); - virtual bool basic_const_item() const { return true; } int save_in_field(Field *field_arg, bool no_conversions) { return Item_field::save_in_field(field_arg, no_conversions); @@ -986,7 +1066,7 @@ public: void set_used_tables(table_map map) { used_table_map= map; } - virtual bool allocate(uint i) { return 0; }; + virtual bool allocate(uint i) { return 0; } virtual bool setup(Item *item) { example= item; @@ -999,6 +1079,9 @@ public: enum Type type() const { return CACHE_ITEM; } static Item_cache* get_cache(Item_result type); table_map used_tables() const { return used_table_map; } + virtual void keep_array() {} + // to prevent drop fixed flag (no need parent cleanup call) + void cleanup() {} void print(String *str); }; @@ -1009,9 +1092,14 @@ public: Item_cache_int(): Item_cache() {} void store(Item *item); - double val() { return (double) value; } - longlong val_int() { return value; } - String* val_str(String *str) { str->set(value, default_charset()); return str; } + double val() { DBUG_ASSERT(fixed == 1); return (double) value; } + longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } + String* val_str(String *str) + { + DBUG_ASSERT(fixed == 1); + str->set(value, default_charset()); + return str; + } enum Item_result result_type() const { return INT_RESULT; } }; @@ -1022,8 +1110,12 @@ public: Item_cache_real(): Item_cache() {} void store(Item *item); - double val() { return value; } - longlong val_int() { return (longlong) (value+(value > 0 ? 0.5 : -0.5)); } + double val() { DBUG_ASSERT(fixed == 1); return value; } + longlong val_int() + { + DBUG_ASSERT(fixed == 1); + return (longlong) (value+(value > 0 ? 0.5 : -0.5)); + } String* val_str(String *str) { str->set(value, decimals, default_charset()); @@ -1042,7 +1134,7 @@ public: void store(Item *item); double val(); longlong val_int(); - String* val_str(String *) { return value; } + String* val_str(String *) { DBUG_ASSERT(fixed == 1); return value; } enum Item_result result_type() const { return STRING_RESULT; } CHARSET_INFO *charset() const { return value->charset(); }; }; @@ -1051,8 +1143,10 @@ class Item_cache_row: public Item_cache { Item_cache **values; uint item_count; + bool save_array; public: - Item_cache_row(): Item_cache(), values(0), item_count(2) {} + Item_cache_row() + :Item_cache(), values(0), item_count(2), save_array(0) {} /* 'allocate' used only in row transformer, to preallocate space for row @@ -1093,10 +1187,16 @@ public: bool check_cols(uint c); bool null_inside(); void bring_value(); + void keep_array() { save_array= 1; } void cleanup() { + DBUG_ENTER("Item_cache_row::cleanup"); Item_cache::cleanup(); - values= 0; + if (save_array) + bzero(values, item_count*sizeof(Item**)); + else + values= 0; + DBUG_VOID_RETURN; } }; @@ -1122,8 +1222,10 @@ public: Field *example() { return field_example; } void cleanup() { + DBUG_ENTER("Item_type_holder::cleanup"); Item::cleanup(); item_type= orig_type; + DBUG_VOID_RETURN; } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 4046a4d6414..ae6658c8e35 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -103,6 +103,7 @@ Item_bool_func2* Le_creator::create(Item *a, Item *b) const 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; @@ -113,7 +114,8 @@ longlong Item_func_not::val_int() */ longlong Item_func_not_all::val_int() -{ +{ + DBUG_ASSERT(fixed == 1); double value= args[0]->val(); if (abort_on_null) { @@ -225,6 +227,13 @@ void Item_bool_func2::fix_length_and_dec() } // Make a special case of compare with fields to get nicer DATE comparisons + + if (functype() == LIKE_FUNC) // Disable conversion in case of LIKE function. + { + set_cmp_func(); + return; + } + if (args[0]->type() == FIELD_ITEM) { Field *field=((Item_field*) args[0])->field; @@ -293,6 +302,17 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) my_coll_agg_error((*a)->collation, (*b)->collation, owner->func_name()); return 1; } + if (my_binary_compare(cmp_collation.collation)) + { + /* + We are using binary collation, change to compare byte by byte, + without removing end space + */ + if (func == &Arg_comparator::compare_string) + func= &Arg_comparator::compare_binary_string; + else if (func == &Arg_comparator::compare_e_string) + func= &Arg_comparator::compare_e_binary_string; + } } return 0; } @@ -313,6 +333,39 @@ int Arg_comparator::compare_string() return -1; } + +/* + Compare strings byte by byte. End spaces are also compared. + + RETURN + < 0 *a < *b + 0 *b == *b + > 0 *a > *b +*/ + +int Arg_comparator::compare_binary_string() +{ + String *res1,*res2; + if ((res1= (*a)->val_str(&owner->tmp_value1))) + { + if ((res2= (*b)->val_str(&owner->tmp_value2))) + { + owner->null_value= 0; + uint res1_length= res1->length(); + uint res2_length= res2->length(); + int cmp= memcmp(res1->ptr(), res2->ptr(), min(res1_length,res2_length)); + return cmp ? cmp : (int) (res1_length - res2_length); + } + } + owner->null_value= 1; + return -1; +} + + +/* + Compare strings, but take into account that NULL == NULL +*/ + int Arg_comparator::compare_e_string() { String *res1,*res2; @@ -324,6 +377,17 @@ int Arg_comparator::compare_e_string() } +int Arg_comparator::compare_e_binary_string() +{ + String *res1,*res2; + res1= (*a)->val_str(&owner->tmp_value1); + res2= (*b)->val_str(&owner->tmp_value2); + if (!res1 || !res2) + return test(res1 == res2); + return test(stringcmp(res1, res2) == 0); +} + + int Arg_comparator::compare_real() { double val1= (*a)->val(); @@ -440,16 +504,15 @@ bool Item_in_optimizer::fix_left(THD *thd, not_null_tables_cache= args[0]->not_null_tables(); with_sum_func= args[0]->with_sum_func; const_item_cache= args[0]->const_item(); - fixed= 1; return 0; } - bool Item_in_optimizer::fix_fields(THD *thd, struct st_table_list *tables, Item ** ref) { - if (fix_left(thd, tables, ref)) + DBUG_ASSERT(fixed == 0); + if (!args[0]->fixed && fix_left(thd, tables, ref)) return 1; if (args[0]->maybe_null) maybe_null=1; @@ -468,11 +531,14 @@ bool Item_in_optimizer::fix_fields(THD *thd, struct st_table_list *tables, used_tables_cache|= args[1]->used_tables(); not_null_tables_cache|= args[1]->not_null_tables(); const_item_cache&= args[1]->const_item(); + fixed= 1; return 0; } + longlong Item_in_optimizer::val_int() { + DBUG_ASSERT(fixed == 1); cache->store(args[0]); if (cache->null_value) { @@ -484,18 +550,39 @@ longlong Item_in_optimizer::val_int() return tmp; } + +void Item_in_optimizer::keep_top_level_cache() +{ + cache->keep_array(); + save_cache= 1; +} + + +void Item_in_optimizer::cleanup() +{ + DBUG_ENTER("Item_in_optimizer::cleanup"); + Item_bool_func::cleanup(); + if (!save_cache) + cache= 0; + DBUG_VOID_RETURN; +} + + bool Item_in_optimizer::is_null() { cache->store(args[0]); return (null_value= (cache->null_value || args[1]->is_null())); } + longlong Item_func_eq::val_int() { + DBUG_ASSERT(fixed == 1); int value= cmp.compare(); return value == 0 ? 1 : 0; } + /* Same as Item_func_eq, but NULL = NULL */ void Item_func_equal::fix_length_and_dec() @@ -506,11 +593,13 @@ void Item_func_equal::fix_length_and_dec() longlong Item_func_equal::val_int() { + DBUG_ASSERT(fixed == 1); return cmp.compare(); } longlong Item_func_ne::val_int() { + DBUG_ASSERT(fixed == 1); int value= cmp.compare(); return value != 0 && !null_value ? 1 : 0; } @@ -518,6 +607,7 @@ longlong Item_func_ne::val_int() longlong Item_func_ge::val_int() { + DBUG_ASSERT(fixed == 1); int value= cmp.compare(); return value >= 0 ? 1 : 0; } @@ -525,12 +615,14 @@ longlong Item_func_ge::val_int() longlong Item_func_gt::val_int() { + DBUG_ASSERT(fixed == 1); int value= cmp.compare(); return value > 0 ? 1 : 0; } longlong Item_func_le::val_int() { + DBUG_ASSERT(fixed == 1); int value= cmp.compare(); return value <= 0 && !null_value ? 1 : 0; } @@ -538,6 +630,7 @@ longlong Item_func_le::val_int() longlong Item_func_lt::val_int() { + DBUG_ASSERT(fixed == 1); int value= cmp.compare(); return value < 0 && !null_value ? 1 : 0; } @@ -545,6 +638,7 @@ longlong Item_func_lt::val_int() longlong Item_func_strcmp::val_int() { + DBUG_ASSERT(fixed == 1); String *a=args[0]->val_str(&tmp_value1); String *b=args[1]->val_str(&tmp_value2); if (!a || !b) @@ -594,6 +688,7 @@ void Item_func_interval::fix_length_and_dec() longlong Item_func_interval::val_int() { + DBUG_ASSERT(fixed == 1); double value= row->el(0)->val(); uint i; @@ -659,6 +754,7 @@ void Item_func_between::fix_length_and_dec() longlong Item_func_between::val_int() { // ANSI BETWEEN + DBUG_ASSERT(fixed == 1); if (cmp_type == STRING_RESULT) { String *value,*a,*b; @@ -674,11 +770,13 @@ longlong Item_func_between::val_int() null_value=1; else if (args[1]->null_value) { - null_value= sortcmp(value,b,cmp_collation.collation) <= 0; // not null if false range. + // Set to not null if false range. + null_value= sortcmp(value,b,cmp_collation.collation) <= 0; } else { - null_value= sortcmp(value,a,cmp_collation.collation) >= 0; // not null if false range. + // Set to not null if false range. + null_value= sortcmp(value,a,cmp_collation.collation) >= 0; } } else if (cmp_type == INT_RESULT) @@ -766,6 +864,7 @@ Field *Item_func_ifnull::tmp_table_field(TABLE *table) double Item_func_ifnull::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); if (!args[0]->null_value) { @@ -781,6 +880,7 @@ Item_func_ifnull::val() longlong Item_func_ifnull::val_int() { + DBUG_ASSERT(fixed == 1); longlong value=args[0]->val_int(); if (!args[0]->null_value) { @@ -796,6 +896,7 @@ Item_func_ifnull::val_int() String * Item_func_ifnull::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res =args[0]->val_str(str); if (!args[0]->null_value) { @@ -851,6 +952,7 @@ Item_func_if::fix_length_and_dec() double Item_func_if::val() { + DBUG_ASSERT(fixed == 1); Item *arg= args[0]->val_int() ? args[1] : args[2]; double value=arg->val(); null_value=arg->null_value; @@ -860,6 +962,7 @@ Item_func_if::val() longlong Item_func_if::val_int() { + DBUG_ASSERT(fixed == 1); Item *arg= args[0]->val_int() ? args[1] : args[2]; longlong value=arg->val_int(); null_value=arg->null_value; @@ -869,6 +972,7 @@ Item_func_if::val_int() String * Item_func_if::val_str(String *str) { + DBUG_ASSERT(fixed == 1); Item *arg= args[0]->val_int() ? args[1] : args[2]; String *res=arg->val_str(str); if (res) @@ -901,8 +1005,9 @@ Item_func_nullif::fix_length_and_dec() double Item_func_nullif::val() { + DBUG_ASSERT(fixed == 1); double value; - if (!cmp.compare() || null_value) + if (!cmp.compare()) { null_value=1; return 0.0; @@ -915,8 +1020,9 @@ Item_func_nullif::val() longlong Item_func_nullif::val_int() { + DBUG_ASSERT(fixed == 1); longlong value; - if (!cmp.compare() || null_value) + if (!cmp.compare()) { null_value=1; return 0; @@ -929,8 +1035,9 @@ Item_func_nullif::val_int() String * Item_func_nullif::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res; - if (!cmp.compare() || null_value) + if (!cmp.compare()) { null_value=1; return 0; @@ -1022,6 +1129,7 @@ Item *Item_func_case::find_item(String *str) String *Item_func_case::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res; Item *item=find_item(str); @@ -1039,6 +1147,7 @@ String *Item_func_case::val_str(String *str) longlong Item_func_case::val_int() { + DBUG_ASSERT(fixed == 1); char buff[MAX_FIELD_WIDTH]; String dummy_str(buff,sizeof(buff),default_charset()); Item *item=find_item(&dummy_str); @@ -1056,6 +1165,7 @@ longlong Item_func_case::val_int() double Item_func_case::val() { + DBUG_ASSERT(fixed == 1); char buff[MAX_FIELD_WIDTH]; String dummy_str(buff,sizeof(buff),default_charset()); Item *item=find_item(&dummy_str); @@ -1161,6 +1271,7 @@ void Item_func_case::print(String *str) String *Item_func_coalesce::val_str(String *str) { + DBUG_ASSERT(fixed == 1); null_value=0; for (uint i=0 ; i < arg_count ; i++) { @@ -1174,6 +1285,7 @@ String *Item_func_coalesce::val_str(String *str) longlong Item_func_coalesce::val_int() { + DBUG_ASSERT(fixed == 1); null_value=0; for (uint i=0 ; i < arg_count ; i++) { @@ -1187,6 +1299,7 @@ longlong Item_func_coalesce::val_int() double Item_func_coalesce::val() { + DBUG_ASSERT(fixed == 1); null_value=0; for (uint i=0 ; i < arg_count ; i++) { @@ -1359,16 +1472,12 @@ cmp_item* cmp_item::get_comparator(Item *item) switch (item->result_type()) { case STRING_RESULT: return new cmp_item_sort_string(item->collation.collation); - break; case INT_RESULT: return new cmp_item_int; - break; case REAL_RESULT: return new cmp_item_real; - break; case ROW_RESULT: return new cmp_item_row; - break; default: DBUG_ASSERT(0); break; @@ -1593,6 +1702,7 @@ void Item_func_in::print(String *str) longlong Item_func_in::val_int() { + DBUG_ASSERT(fixed == 1); if (array) { int tmp=array->find(args[0]); @@ -1616,6 +1726,7 @@ longlong Item_func_in::val_int() longlong Item_func_bit_or::val_int() { + DBUG_ASSERT(fixed == 1); ulonglong arg1= (ulonglong) args[0]->val_int(); if (args[0]->null_value) { @@ -1635,6 +1746,7 @@ longlong Item_func_bit_or::val_int() longlong Item_func_bit_and::val_int() { + DBUG_ASSERT(fixed == 1); ulonglong arg1= (ulonglong) args[0]->val_int(); if (args[0]->null_value) { @@ -1673,6 +1785,7 @@ void Item_cond::copy_andor_arguments(THD *thd, Item_cond *item) bool Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { + DBUG_ASSERT(fixed == 0); List_iterator<Item> li(list); Item *item; #ifndef EMBEDDED_LIBRARY @@ -1700,8 +1813,11 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) } if (abort_on_null) item->top_level_item(); + + // item can be substituted in fix_fields if ((!item->fixed && - item->fix_fields(thd, tables, li.ref())) || item->check_cols(1)) + item->fix_fields(thd, tables, li.ref())) || + (item= *li.ref())->check_cols(1)) return 1; /* purecov: inspected */ used_tables_cache|= item->used_tables(); tmp_table_map= item->not_null_tables(); @@ -1793,14 +1909,28 @@ void Item_cond::print(String *str) } -void Item_cond::neg_arguments() +void Item_cond::neg_arguments(THD *thd) { List_iterator<Item> li(list); Item *item; while ((item= li++)) /* Apply not transformation to the arguments */ { - Item *new_item= item->neg_transformer(); - VOID(li.replace(new_item ? new_item : new Item_func_not(item))); + Item *new_item= item->neg_transformer(thd); + if (!new_item) + { + if (!(new_item= new Item_func_not(item))) + return; // Fatal OEM error + /* + We can use 0 as tables list because Item_func_not do not use it + on fix_fields and its arguments are already fixed. + + We do not check results of fix_fields, because there are not way + to return error in this functions interface, thd->net.report_error + will be checked on upper level call. + */ + new_item->fix_fields(thd, 0, &new_item); + } + VOID(li.replace(new_item)); } } @@ -1825,6 +1955,7 @@ void Item_cond::neg_arguments() longlong Item_cond_and::val_int() { + DBUG_ASSERT(fixed == 1); List_iterator_fast<Item> li(list); Item *item; null_value= 0; @@ -1842,6 +1973,7 @@ longlong Item_cond_and::val_int() longlong Item_cond_or::val_int() { + DBUG_ASSERT(fixed == 1); List_iterator_fast<Item> li(list); Item *item; null_value=0; @@ -1903,6 +2035,7 @@ Item *and_expressions(Item *a, Item *b, Item **org_item) longlong Item_func_isnull::val_int() { + DBUG_ASSERT(fixed == 1); /* Handle optimization if the argument can't be null This has to be here because of the test in update_used_tables(). @@ -1914,6 +2047,7 @@ longlong Item_func_isnull::val_int() longlong Item_is_not_null_test::val_int() { + DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_is_not_null_test::val_int"); if (!used_tables_cache) { @@ -1953,6 +2087,7 @@ void Item_is_not_null_test::update_used_tables() longlong Item_func_isnotnull::val_int() { + DBUG_ASSERT(fixed == 1); return args[0]->is_null() ? 0 : 1; } @@ -1967,6 +2102,7 @@ void Item_func_isnotnull::print(String *str) longlong Item_func_like::val_int() { + DBUG_ASSERT(fixed == 1); String* res = args[0]->val_str(&tmp_value1); if (args[0]->null_value) { @@ -2012,6 +2148,7 @@ Item_func::optimize_type Item_func_like::select_optimize() const bool Item_func_like::fix_fields(THD *thd, TABLE_LIST *tlist, Item ** ref) { + DBUG_ASSERT(fixed == 0); if (Item_bool_func2::fix_fields(thd, tlist, ref)) return 1; @@ -2065,6 +2202,7 @@ bool Item_func_like::fix_fields(THD *thd, TABLE_LIST *tlist, Item ** ref) 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)) return 1; /* purecov: inspected */ @@ -2112,6 +2250,7 @@ Item_func_regex::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) longlong Item_func_regex::val_int() { + DBUG_ASSERT(fixed == 1); char buff[MAX_FIELD_WIDTH]; String *res, tmp(buff,sizeof(buff),&my_charset_bin); @@ -2132,7 +2271,7 @@ longlong Item_func_regex::val_int() null_value=1; return 0; } - if (!regex_compiled || sortcmp(res2,&prev_regexp,&my_charset_bin)) + if (!regex_compiled || stringcmp(res2,&prev_regexp)) { prev_regexp.copy(*res2); if (regex_compiled) @@ -2412,6 +2551,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const longlong Item_cond_xor::val_int() { + DBUG_ASSERT(fixed == 1); List_iterator<Item> li(list); Item *item; int result=0; @@ -2433,6 +2573,7 @@ longlong Item_cond_xor::val_int() SYNPOSIS neg_transformer() + thd thread handler DESCRIPTION Transform the item using next rules: @@ -2456,62 +2597,116 @@ longlong Item_cond_xor::val_int() NULL if we cannot apply NOT transformation (see Item::neg_transformer()). */ -Item *Item_func_not::neg_transformer() /* NOT(x) -> x */ +Item *Item_func_not::neg_transformer(THD *thd) /* NOT(x) -> x */ { - /* We should apply negation elimination to the argument of the NOT function */ - return eliminate_not_funcs(args[0]); + // We should apply negation elimination to the argument of the NOT function + return eliminate_not_funcs(thd, args[0]); } -Item *Item_func_eq::neg_transformer() /* a = b -> a != b */ + +Item *Item_bool_rowready_func2::neg_transformer(THD *thd) { - return new Item_func_ne(args[0], args[1]); + Item *item= negated_item(); + if (item) + { + /* + We can use 0 as tables list because Item_func* family do not use it + on fix_fields and its arguments are already fixed. + + We do not check results of fix_fields, because there are not way + to return error in this functions interface, thd->net.report_error + will be checked on upper level call. + */ + item->fix_fields(thd, 0, &item); + } + return item; } -Item *Item_func_ne::neg_transformer() /* a != b -> a = b */ + +/* a IS NULL -> a IS NOT NULL */ +Item *Item_func_isnull::neg_transformer(THD *thd) { - return new Item_func_eq(args[0], args[1]); + Item *item= new Item_func_isnotnull(args[0]); + // see comment before fix_fields in Item_bool_rowready_func2::neg_transformer + if (item) + item->fix_fields(thd, 0, &item); + return item; } -Item *Item_func_lt::neg_transformer() /* a < b -> a >= b */ + +/* a IS NOT NULL -> a IS NULL */ +Item *Item_func_isnotnull::neg_transformer(THD *thd) { - return new Item_func_ge(args[0], args[1]); + Item *item= new Item_func_isnull(args[0]); + // see comment before fix_fields in Item_bool_rowready_func2::neg_transformer + if (item) + item->fix_fields(thd, 0, &item); + return item; } -Item *Item_func_ge::neg_transformer() /* a >= b -> a < b */ + +Item *Item_cond_and::neg_transformer(THD *thd) /* NOT(a AND b AND ...) -> */ + /* NOT a OR NOT b OR ... */ { - return new Item_func_lt(args[0], args[1]); + neg_arguments(thd); + Item *item= new Item_cond_or(list); + // see comment before fix_fields in Item_bool_rowready_func2::neg_transformer + if (item) + item->fix_fields(thd, 0, &item); + return item; } -Item *Item_func_gt::neg_transformer() /* a > b -> a <= b */ + +Item *Item_cond_or::neg_transformer(THD *thd) /* NOT(a OR b OR ...) -> */ + /* NOT a AND NOT b AND ... */ { - return new Item_func_le(args[0], args[1]); + neg_arguments(thd); + Item *item= new Item_cond_and(list); + // see comment before fix_fields in Item_bool_rowready_func2::neg_transformer + if (item) + item->fix_fields(thd, 0, &item); + return item; } -Item *Item_func_le::neg_transformer() /* a <= b -> a > b */ + +Item *Item_func_eq::negated_item() /* a = b -> a != b */ { - return new Item_func_gt(args[0], args[1]); + return new Item_func_ne(args[0], args[1]); } -Item *Item_func_isnull::neg_transformer() /* a IS NULL -> a IS NOT NULL */ + +Item *Item_func_ne::negated_item() /* a != b -> a = b */ { - return new Item_func_isnotnull(args[0]); + return new Item_func_eq(args[0], args[1]); } -Item *Item_func_isnotnull::neg_transformer() /* a IS NOT NULL -> a IS NULL */ + +Item *Item_func_lt::negated_item() /* a < b -> a >= b */ { - return new Item_func_isnull(args[0]); + return new Item_func_ge(args[0], args[1]); } -Item *Item_cond_and::neg_transformer() /* NOT(a AND b AND ...) -> */ - /* NOT a OR NOT b OR ... */ + +Item *Item_func_ge::negated_item() /* a >= b -> a < b */ +{ + return new Item_func_lt(args[0], args[1]); +} + + +Item *Item_func_gt::negated_item() /* a > b -> a <= b */ { - neg_arguments(); - return new Item_cond_or(list); + return new Item_func_le(args[0], args[1]); } -Item *Item_cond_or::neg_transformer() /* NOT(a OR b OR ...) -> */ - /* NOT a AND NOT b AND ... */ + +Item *Item_func_le::negated_item() /* a <= b -> a > b */ +{ + return new Item_func_gt(args[0], args[1]); +} + +// just fake method, should never be called +Item *Item_bool_rowready_func2::negated_item() { - neg_arguments(); - return new Item_cond_and(list); + DBUG_ASSERT(0); + return 0; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 7e1749ef7a0..d654bec4f4e 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -63,10 +63,12 @@ public: inline int compare() { return (this->*func)(); } int compare_string(); // compare args[0] & args[1] + int compare_binary_string(); // compare args[0] & args[1] int compare_real(); // compare args[0] & args[1] int compare_int(); // compare args[0] & args[1] int compare_row(); // compare args[0] & args[1] int compare_e_string(); // compare args[0] & args[1] + int compare_e_binary_string(); // compare args[0] & args[1] int compare_e_real(); // compare args[0] & args[1] int compare_e_int(); // compare args[0] & args[1] int compare_e_row(); // compare args[0] & args[1] @@ -91,9 +93,10 @@ class Item_in_optimizer: public Item_bool_func { protected: Item_cache *cache; + bool save_cache; public: Item_in_optimizer(Item *a, Item_in_subselect *b): - Item_bool_func(a, (Item *)b), cache(0) {} + Item_bool_func(a, (Item *)b), cache(0), save_cache(0) {} bool fix_fields(THD *, struct st_table_list *, Item **); bool fix_left(THD *thd, struct st_table_list *tables, Item **ref); bool is_null(); @@ -105,8 +108,10 @@ public: Item_in_optimizer return NULL, else it evaluate Item_in_subselect. */ longlong val_int(); + void cleanup(); const char *func_name() const { return "<in_optimizer>"; } Item_cache **get_cache() { return &cache; } + void keep_top_level_cache(); }; class Comp_creator @@ -207,10 +212,14 @@ public: } void cleanup() { + DBUG_ENTER("Item_bool_rowready_func2::cleanup"); Item_bool_func2::cleanup(); tmp_arg[0]= orig_a; tmp_arg[1]= orig_b; + DBUG_VOID_RETURN; } + Item *neg_transformer(THD *thd); + virtual Item *negated_item(); }; class Item_func_not :public Item_bool_func @@ -220,7 +229,7 @@ public: longlong val_int(); enum Functype functype() const { return NOT_FUNC; } const char *func_name() const { return "not"; } - Item *neg_transformer(); + Item *neg_transformer(THD *thd); }; class Item_func_not_all :public Item_func_not @@ -247,7 +256,7 @@ public: enum Functype rev_functype() const { return EQ_FUNC; } cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return "="; } - Item *neg_transformer(); + Item *negated_item(); }; class Item_func_equal :public Item_bool_rowready_func2 @@ -260,6 +269,7 @@ public: enum Functype rev_functype() const { return EQUAL_FUNC; } cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return "<=>"; } + Item* neg_transformer(THD *thd) { return 0; } }; @@ -272,7 +282,7 @@ public: enum Functype rev_functype() const { return LE_FUNC; } cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return ">="; } - Item *neg_transformer(); + Item *negated_item(); }; @@ -285,7 +295,7 @@ public: enum Functype rev_functype() const { return LT_FUNC; } cond_result eq_cmp_result() const { return COND_FALSE; } const char *func_name() const { return ">"; } - Item *neg_transformer(); + Item *negated_item(); }; @@ -298,7 +308,7 @@ public: enum Functype rev_functype() const { return GE_FUNC; } cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return "<="; } - Item *neg_transformer(); + Item *negated_item(); }; @@ -311,7 +321,7 @@ public: enum Functype rev_functype() const { return GT_FUNC; } cond_result eq_cmp_result() const { return COND_FALSE; } const char *func_name() const { return "<"; } - Item *neg_transformer(); + Item *negated_item(); }; @@ -324,7 +334,7 @@ public: cond_result eq_cmp_result() const { return COND_FALSE; } optimize_type select_optimize() const { return OPTIMIZE_KEY; } const char *func_name() const { return "<>"; } - Item *neg_transformer(); + Item *negated_item(); }; @@ -402,6 +412,7 @@ public: enum Item_result result_type () const { return cached_result_type; } bool fix_fields(THD *thd,struct st_table_list *tlist, Item **ref) { + DBUG_ASSERT(fixed == 0); args[0]->top_level_item(); return Item_func::fix_fields(thd, tlist, ref); } @@ -718,10 +729,13 @@ class Item_func_in :public Item_int_func void fix_length_and_dec(); void cleanup() { + DBUG_ENTER("Item_func_in::cleanup"); + Item_int_func::cleanup(); delete array; delete in_item; array= 0; in_item= 0; + DBUG_VOID_RETURN; } optimize_type select_optimize() const { return array ? OPTIMIZE_KEY : OPTIMIZE_NONE; } @@ -769,7 +783,7 @@ public: } table_map not_null_tables() const { return 0; } optimize_type select_optimize() const { return OPTIMIZE_NULL; } - Item *neg_transformer(); + Item *neg_transformer(THD *thd); CHARSET_INFO *compare_collation() { return args[0]->collation.collation; } }; @@ -803,7 +817,7 @@ public: const char *func_name() const { return "isnotnull"; } optimize_type select_optimize() const { return OPTIMIZE_NULL; } table_map not_null_tables() const { return 0; } - Item *neg_transformer(); + Item *neg_transformer(THD *thd); void print(String *str); CHARSET_INFO *compare_collation() { return args[0]->collation.collation; } }; @@ -811,8 +825,6 @@ public: class Item_func_like :public Item_bool_func2 { - char escape; - // Turbo Boyer-Moore data bool canDoTurboBM; // pattern is '%abcd%' case const char* pattern; @@ -829,10 +841,11 @@ class Item_func_like :public Item_bool_func2 enum { alphabet_size = 256 }; public: + char escape; + Item_func_like(Item *a,Item *b, char* escape_arg) - :Item_bool_func2(a,b), escape(*escape_arg), canDoTurboBM(false), - pattern(0), pattern_len(0), bmGs(0), bmBc(0) - {} + :Item_bool_func2(a,b), canDoTurboBM(false), pattern(0), pattern_len(0), + bmGs(0), bmBc(0), escape(*escape_arg) {} longlong val_int(); enum Functype functype() const { return LIKE_FUNC; } optimize_type select_optimize() const; @@ -912,7 +925,7 @@ public: void top_level_item() { abort_on_null=1; } void copy_andor_arguments(THD *thd, Item_cond *item); bool walk(Item_processor processor, byte *arg); - void neg_arguments(); + void neg_arguments(THD *thd); }; @@ -933,7 +946,7 @@ public: item->copy_andor_arguments(thd, this); return item; } - Item *neg_transformer(); + Item *neg_transformer(THD *thd); }; class Item_cond_or :public Item_cond @@ -954,7 +967,7 @@ public: item->copy_andor_arguments(thd, this); return item; } - Item *neg_transformer(); + Item *neg_transformer(THD *thd); }; @@ -978,14 +991,11 @@ public: /* Some usefull inline functions */ -inline Item *and_conds(Item *a,Item *b) +inline Item *and_conds(Item *a, Item *b) { if (!b) return a; if (!a) return b; - Item *cond=new Item_cond_and(a,b); - if (cond) - cond->update_used_tables(); - return cond; + return new Item_cond_and(a, b); } Item *and_expressions(Item *a, Item *b, Item **org_item); diff --git a/sql/item_create.cc b/sql/item_create.cc index 6519e242465..e314bfc3e2c 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -30,14 +30,14 @@ Item *create_func_acos(Item* a) Item *create_func_aes_encrypt(Item* a, Item* b) { - return new Item_func_aes_encrypt(a, b); + return new Item_func_aes_encrypt(a, b); } - + Item *create_func_aes_decrypt(Item* a, Item* b) { return new Item_func_aes_decrypt(a, b); } - + Item *create_func_ascii(Item* a) { return new Item_func_ascii(a); @@ -417,6 +417,16 @@ Item *create_func_ucase(Item* a) return new Item_func_ucase(a); } +Item *create_func_unhex(Item* a) +{ + return new Item_func_unhex(a); +} + +Item *create_func_uuid(void) +{ + return new Item_func_uuid(); +} + Item *create_func_version(void) { return new Item_string(NullS,server_version, @@ -446,6 +456,7 @@ Item *create_func_cast(Item *a, Cast_target cast_type, int len, { Item *res; LINT_INIT(res); + switch (cast_type) { case ITEM_CAST_BINARY: res= new Item_func_binary(a); break; case ITEM_CAST_SIGNED_INT: res= new Item_func_signed(a); break; diff --git a/sql/item_create.h b/sql/item_create.h index 5dc53fb04b8..d48aed5284a 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -91,6 +91,8 @@ Item *create_func_time_format(Item *a, Item *b); Item *create_func_time_to_sec(Item* a); Item *create_func_to_days(Item* a); Item *create_func_ucase(Item* a); +Item *create_func_unhex(Item* a); +Item *create_func_uuid(void); Item *create_func_version(void); Item *create_func_weekday(Item* a); Item *create_load_file(Item* a); diff --git a/sql/item_func.cc b/sql/item_func.cc index 2a74f2801c0..c648b34efae 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -32,6 +32,16 @@ #include "sp_rcontext.h" #include "sp.h" +bool check_reserved_words(LEX_STRING *name) +{ + if (!my_strcasecmp(system_charset_info, name->str, "GLOBAL") || + !my_strcasecmp(system_charset_info, name->str, "LOCAL") || + !my_strcasecmp(system_charset_info, name->str, "SESSION")) + return TRUE; + return FALSE; +} + + static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, const char *fname) { @@ -192,6 +202,7 @@ Item_func::Item_func(THD *thd, Item_func *item) bool Item_func::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { + DBUG_ASSERT(fixed == 0); Item **arg,**arg_end; #ifndef EMBEDDED_LIBRARY // Avoid compiler warning char buff[STACK_BUFF_ALLOC]; // Max argument in function @@ -207,8 +218,11 @@ Item_func::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++) { Item *item; - /* We can't yet set item to *arg as fix_fields may change *arg */ - if ((*arg)->fix_fields(thd, tables, arg) || + /* + 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)) return 1; /* purecov: inspected */ item= *arg; @@ -373,6 +387,7 @@ Field *Item_func::tmp_table_field(TABLE *t_arg) String *Item_real_func::val_str(String *str) { + DBUG_ASSERT(fixed == 1); double nr=val(); if (null_value) return 0; /* purecov: inspected */ @@ -383,6 +398,7 @@ String *Item_real_func::val_str(String *str) String *Item_num_func::val_str(String *str) { + DBUG_ASSERT(fixed == 1); if (hybrid_type == INT_RESULT) { longlong nr=val_int(); @@ -421,6 +437,7 @@ Item *Item_func::get_tmp_table_item(THD *thd) String *Item_int_func::val_str(String *str) { + DBUG_ASSERT(fixed == 1); longlong nr=val_int(); if (null_value) return 0; @@ -448,6 +465,7 @@ void Item_num_op::find_num_type(void) String *Item_num_op::val_str(String *str) { + DBUG_ASSERT(fixed == 1); if (hybrid_type == INT_RESULT) { longlong nr=val_int(); @@ -489,6 +507,7 @@ void Item_func_unsigned::print(String *str) double Item_func_plus::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val()+args[1]->val(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; @@ -497,6 +516,7 @@ double Item_func_plus::val() longlong Item_func_plus::val_int() { + DBUG_ASSERT(fixed == 1); if (hybrid_type == INT_RESULT) { longlong value=args[0]->val_int()+args[1]->val_int(); @@ -524,6 +544,7 @@ void Item_func_minus::fix_length_and_dec() double Item_func_minus::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val() - args[1]->val(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; @@ -532,6 +553,7 @@ double Item_func_minus::val() longlong Item_func_minus::val_int() { + DBUG_ASSERT(fixed == 1); if (hybrid_type == INT_RESULT) { longlong value=args[0]->val_int() - args[1]->val_int(); @@ -545,6 +567,7 @@ longlong Item_func_minus::val_int() double Item_func_mul::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val()*args[1]->val(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; /* purecov: inspected */ @@ -553,6 +576,7 @@ double Item_func_mul::val() longlong Item_func_mul::val_int() { + DBUG_ASSERT(fixed == 1); if (hybrid_type == INT_RESULT) { longlong value=args[0]->val_int()*args[1]->val_int(); @@ -566,6 +590,7 @@ longlong Item_func_mul::val_int() double Item_func_div::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); double val2=args[1]->val(); if ((null_value= val2 == 0.0 || args[0]->null_value || args[1]->null_value)) @@ -575,6 +600,7 @@ double Item_func_div::val() longlong Item_func_div::val_int() { + DBUG_ASSERT(fixed == 1); if (hybrid_type == INT_RESULT) { longlong value=args[0]->val_int(); @@ -599,6 +625,7 @@ void Item_func_div::fix_length_and_dec() /* Integer division */ longlong Item_func_int_div::val_int() { + DBUG_ASSERT(fixed == 1); longlong value=args[0]->val_int(); longlong val2=args[1]->val_int(); if ((null_value= val2 == 0 || args[0]->null_value || args[1]->null_value)) @@ -619,6 +646,7 @@ void Item_func_int_div::fix_length_and_dec() double Item_func_mod::val() { + DBUG_ASSERT(fixed == 1); double value= floor(args[0]->val()+0.5); double val2=floor(args[1]->val()+0.5); if ((null_value=val2 == 0.0 || args[0]->null_value || args[1]->null_value)) @@ -628,6 +656,7 @@ double Item_func_mod::val() longlong Item_func_mod::val_int() { + DBUG_ASSERT(fixed == 1); longlong value= args[0]->val_int(); longlong val2= args[1]->val_int(); if ((null_value=val2 == 0 || args[0]->null_value || args[1]->null_value)) @@ -646,6 +675,7 @@ void Item_func_mod::fix_length_and_dec() double Item_func_neg::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); null_value=args[0]->null_value; return -value; @@ -654,6 +684,7 @@ double Item_func_neg::val() longlong Item_func_neg::val_int() { + DBUG_ASSERT(fixed == 1); longlong value=args[0]->val_int(); null_value=args[0]->null_value; return -value; @@ -687,6 +718,7 @@ void Item_func_neg::fix_length_and_dec() double Item_func_abs::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); null_value=args[0]->null_value; return fabs(value); @@ -695,6 +727,7 @@ double Item_func_abs::val() longlong Item_func_abs::val_int() { + DBUG_ASSERT(fixed == 1); longlong value=args[0]->val_int(); null_value=args[0]->null_value; return value >= 0 ? value : -value; @@ -717,6 +750,7 @@ void Item_func_abs::fix_length_and_dec() /* Gateway to natural LOG function */ double Item_func_ln::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); if ((null_value=(args[0]->null_value || value <= 0.0))) return 0.0; @@ -730,6 +764,7 @@ double Item_func_ln::val() */ double Item_func_log::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); if ((null_value=(args[0]->null_value || value <= 0.0))) return 0.0; @@ -745,6 +780,7 @@ double Item_func_log::val() double Item_func_log2::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); if ((null_value=(args[0]->null_value || value <= 0.0))) return 0.0; @@ -753,6 +789,7 @@ double Item_func_log2::val() double Item_func_log10::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); if ((null_value=(args[0]->null_value || value <= 0.0))) return 0.0; /* purecov: inspected */ @@ -761,6 +798,7 @@ double Item_func_log10::val() double Item_func_exp::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); if ((null_value=args[0]->null_value)) return 0.0; /* purecov: inspected */ @@ -769,6 +807,7 @@ double Item_func_exp::val() double Item_func_sqrt::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); if ((null_value=(args[0]->null_value || value < 0))) return 0.0; /* purecov: inspected */ @@ -777,6 +816,7 @@ double Item_func_sqrt::val() double Item_func_pow::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); double val2=args[1]->val(); if ((null_value=(args[0]->null_value || args[1]->null_value))) @@ -788,7 +828,9 @@ double Item_func_pow::val() double Item_func_acos::val() { - double value=args[0]->val(); + DBUG_ASSERT(fixed == 1); + // the volatile's for BUG #2338 to calm optimizer down (because of gcc's bug) + volatile double value=args[0]->val(); if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0)))) return 0.0; return fix_result(acos(value)); @@ -796,7 +838,9 @@ double Item_func_acos::val() double Item_func_asin::val() { - double value=args[0]->val(); + DBUG_ASSERT(fixed == 1); + // the volatile's for BUG #2338 to calm optimizer down (because of gcc's bug) + volatile double value=args[0]->val(); if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0)))) return 0.0; return fix_result(asin(value)); @@ -804,6 +848,7 @@ double Item_func_asin::val() double Item_func_atan::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); if ((null_value=args[0]->null_value)) return 0.0; @@ -819,6 +864,7 @@ double Item_func_atan::val() double Item_func_cos::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); if ((null_value=args[0]->null_value)) return 0.0; @@ -827,6 +873,7 @@ double Item_func_cos::val() double Item_func_sin::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); if ((null_value=args[0]->null_value)) return 0.0; @@ -835,6 +882,7 @@ double Item_func_sin::val() double Item_func_tan::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); if ((null_value=args[0]->null_value)) return 0.0; @@ -847,6 +895,7 @@ double Item_func_tan::val() longlong Item_func_shift_left::val_int() { + DBUG_ASSERT(fixed == 1); uint shift; ulonglong res= ((ulonglong) args[0]->val_int() << (shift=(uint) args[1]->val_int())); @@ -861,6 +910,7 @@ longlong Item_func_shift_left::val_int() longlong Item_func_shift_right::val_int() { + DBUG_ASSERT(fixed == 1); uint shift; ulonglong res= (ulonglong) args[0]->val_int() >> (shift=(uint) args[1]->val_int()); @@ -876,6 +926,7 @@ longlong Item_func_shift_right::val_int() longlong Item_func_bit_neg::val_int() { + DBUG_ASSERT(fixed == 1); ulonglong res= (ulonglong) args[0]->val_int(); if ((null_value=args[0]->null_value)) return 0; @@ -895,6 +946,7 @@ void Item_func_integer::fix_length_and_dec() longlong Item_func_ceiling::val_int() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); null_value=args[0]->null_value; return (longlong) ceil(value); @@ -902,7 +954,9 @@ longlong Item_func_ceiling::val_int() longlong Item_func_floor::val_int() { - double value=args[0]->val(); + DBUG_ASSERT(fixed == 1); + // the volatile's for BUG #3051 to calm optimizer down (because of gcc's bug) + volatile double value=args[0]->val(); null_value=args[0]->null_value; return (longlong) floor(value); } @@ -923,6 +977,7 @@ void Item_func_round::fix_length_and_dec() double Item_func_round::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); int dec=(int) args[1]->val_int(); uint abs_dec=abs(dec); @@ -955,6 +1010,7 @@ void Item_func_rand::fix_length_and_dec() { decimals=NOT_FIXED_DEC; max_length=float_length(decimals); + used_tables_cache|= RAND_TABLE_BIT; if (arg_count) { // Only use argument once in query uint32 tmp= (uint32) (args[0]->val_int()); @@ -980,14 +1036,22 @@ void Item_func_rand::fix_length_and_dec() } } +void Item_func_rand::update_used_tables() +{ + Item_real_func::update_used_tables(); + used_tables_cache|= RAND_TABLE_BIT; +} + double Item_func_rand::val() { + DBUG_ASSERT(fixed == 1); return my_rnd(rand); } longlong Item_func_sign::val_int() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); null_value=args[0]->null_value; return value < 0.0 ? -1 : (value > 0 ? 1 : 0); @@ -996,6 +1060,7 @@ longlong Item_func_sign::val_int() double Item_func_units::val() { + DBUG_ASSERT(fixed == 1); double value=args[0]->val(); if ((null_value=args[0]->null_value)) return 0; @@ -1027,6 +1092,7 @@ void Item_func_min_max::fix_length_and_dec() String *Item_func_min_max::val_str(String *str) { + DBUG_ASSERT(fixed == 1); switch (cmp_type) { case INT_RESULT: { @@ -1080,7 +1146,6 @@ String *Item_func_min_max::val_str(String *str) // This case should never be choosen DBUG_ASSERT(0); return 0; - } return 0; // Keep compiler happy } @@ -1088,6 +1153,7 @@ String *Item_func_min_max::val_str(String *str) double Item_func_min_max::val() { + DBUG_ASSERT(fixed == 1); double value=0.0; null_value=1; for (uint i=0; i < arg_count ; i++) @@ -1110,6 +1176,7 @@ double Item_func_min_max::val() longlong Item_func_min_max::val_int() { + DBUG_ASSERT(fixed == 1); longlong value=0; null_value=1; for (uint i=0; i < arg_count ; i++) @@ -1131,6 +1198,7 @@ longlong Item_func_min_max::val_int() longlong Item_func_length::val_int() { + DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); if (!res) { @@ -1144,6 +1212,7 @@ longlong Item_func_length::val_int() longlong Item_func_char_length::val_int() { + DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); if (!res) { @@ -1157,6 +1226,7 @@ longlong Item_func_char_length::val_int() longlong Item_func_coercibility::val_int() { + DBUG_ASSERT(fixed == 1); if (args[0]->null_value) { null_value= 1; @@ -1176,6 +1246,7 @@ void Item_func_locate::fix_length_and_dec() longlong Item_func_locate::val_int() { + DBUG_ASSERT(fixed == 1); String *a=args[0]->val_str(&value1); String *b=args[1]->val_str(&value2); if (!a || !b) @@ -1226,6 +1297,7 @@ void Item_func_locate::print(String *str) longlong Item_func_field::val_int() { + DBUG_ASSERT(fixed == 1); if (cmp_type == STRING_RESULT) { String *field; @@ -1273,6 +1345,7 @@ void Item_func_field::fix_length_and_dec() longlong Item_func_ascii::val_int() { + DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); if (!res) { @@ -1285,6 +1358,7 @@ longlong Item_func_ascii::val_int() longlong Item_func_ord::val_int() { + DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); if (!res) { @@ -1339,6 +1413,7 @@ static const char separator=','; longlong Item_func_find_in_set::val_int() { + DBUG_ASSERT(fixed == 1); if (enum_value) { ulonglong tmp=(ulonglong) args[1]->val_int(); @@ -1392,6 +1467,7 @@ longlong Item_func_find_in_set::val_int() longlong Item_func_bit_count::val_int() { + DBUG_ASSERT(fixed == 1); ulonglong value= (ulonglong) args[0]->val_int(); if (args[0]->null_value) { @@ -1470,9 +1546,12 @@ 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)) + DBUG_RETURN(1); + // we can't assign 'item' before, because fix_fields() can change arg Item *item= *arg; - if (item->fix_fields(thd, tables, arg) || item->check_cols(1)) - return 1; + if (item->check_cols(1)) + DBUG_RETURN(1); /* TODO: We should think about this. It is not always right way just to set an UDF result to return my_charset_bin @@ -1668,6 +1747,7 @@ String *udf_handler::val_str(String *str,String *save_str) double Item_func_udf_float::val() { + DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_func_udf_float::val"); DBUG_PRINT("info",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); @@ -1677,6 +1757,7 @@ double Item_func_udf_float::val() String *Item_func_udf_float::val_str(String *str) { + DBUG_ASSERT(fixed == 1); double nr=val(); if (null_value) return 0; /* purecov: inspected */ @@ -1687,6 +1768,7 @@ String *Item_func_udf_float::val_str(String *str) longlong Item_func_udf_int::val_int() { + DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_func_udf_int::val_int"); DBUG_PRINT("info",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); @@ -1697,6 +1779,7 @@ longlong Item_func_udf_int::val_int() String *Item_func_udf_int::val_str(String *str) { + DBUG_ASSERT(fixed == 1); longlong nr=val_int(); if (null_value) return 0; @@ -1720,6 +1803,7 @@ void Item_func_udf_str::fix_length_and_dec() String *Item_func_udf_str::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res=udf.val_str(str,&str_value); null_value = !res; return res; @@ -1736,7 +1820,7 @@ bool udf_handler::get_arguments() { return 0; } pthread_mutex_t LOCK_user_locks; static HASH hash_user_locks; -class ULL +class User_level_lock { char *key; uint key_length; @@ -1748,7 +1832,7 @@ public: pthread_t thread; ulong thread_id; - ULL(const char *key_arg,uint length, ulong id) + User_level_lock(const char *key_arg,uint length, ulong id) :key_length(length),count(1),locked(1), thread_id(id) { key=(char*) my_memdup((byte*) key_arg,length,MYF(0)); @@ -1762,7 +1846,7 @@ public: } } } - ~ULL() + ~User_level_lock() { if (key) { @@ -1772,11 +1856,12 @@ public: pthread_cond_destroy(&cond); } inline bool initialized() { return key != 0; } - friend void item_user_lock_release(ULL *ull); - friend char *ull_get_key(const ULL *ull,uint *length,my_bool not_used); + friend void item_user_lock_release(User_level_lock *ull); + friend char *ull_get_key(const User_level_lock *ull, uint *length, + my_bool not_used); }; -char *ull_get_key(const ULL *ull,uint *length, +char *ull_get_key(const User_level_lock *ull, uint *length, my_bool not_used __attribute__((unused))) { *length=(uint) ull->key_length; @@ -1804,7 +1889,7 @@ void item_user_lock_free(void) } } -void item_user_lock_release(ULL *ull) +void item_user_lock_release(User_level_lock *ull) { ull->locked=0; if (mysql_bin_log.is_open()) @@ -1832,6 +1917,7 @@ void item_user_lock_release(ULL *ull) longlong Item_master_pos_wait::val_int() { + DBUG_ASSERT(fixed == 1); THD* thd = current_thd; String *log_name = args[0]->val_str(&value); int event_count= 0; @@ -1845,13 +1931,11 @@ longlong Item_master_pos_wait::val_int() longlong pos = (ulong)args[1]->val_int(); longlong timeout = (arg_count==3) ? args[2]->val_int() : 0 ; #ifdef HAVE_REPLICATION - LOCK_ACTIVE_MI; - if ((event_count = active_mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2) - { + if ((event_count = active_mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2) + { null_value = 1; event_count=0; } - UNLOCK_ACTIVE_MI; #endif return event_count; } @@ -1860,7 +1944,7 @@ longlong Item_master_pos_wait::val_int() void debug_sync_point(const char* lock_name, uint lock_timeout) { THD* thd=current_thd; - ULL* ull; + User_level_lock* ull; struct timespec abstime; int lock_name_len,error=0; lock_name_len=strlen(lock_name); @@ -1878,7 +1962,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout) this case, we will not be waiting, but rather, just waste CPU and memory on the whole deal */ - if (!(ull= ((ULL*) hash_search(&hash_user_locks,lock_name, + if (!(ull= ((User_level_lock*) hash_search(&hash_user_locks, lock_name, lock_name_len)))) { pthread_mutex_unlock(&LOCK_user_locks); @@ -1935,11 +2019,12 @@ void debug_sync_point(const char* lock_name, uint lock_timeout) longlong Item_func_get_lock::val_int() { + DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); longlong timeout=args[1]->val_int(); struct timespec abstime; THD *thd=current_thd; - ULL *ull; + User_level_lock *ull; int error=0; pthread_mutex_lock(&LOCK_user_locks); @@ -1958,10 +2043,11 @@ longlong Item_func_get_lock::val_int() thd->ull=0; } - if (!(ull= ((ULL*) hash_search(&hash_user_locks,(byte*) res->ptr(), - res->length())))) + if (!(ull= ((User_level_lock *) hash_search(&hash_user_locks, + (byte*) res->ptr(), + res->length())))) { - ull=new ULL(res->ptr(),res->length(), thd->thread_id); + ull=new User_level_lock(res->ptr(),res->length(), thd->thread_id); if (!ull || !ull->initialized()) { delete ull; @@ -2029,8 +2115,9 @@ longlong Item_func_get_lock::val_int() longlong Item_func_release_lock::val_int() { + DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); - ULL *ull; + User_level_lock *ull; longlong result; if (!res || !res->length()) { @@ -2041,8 +2128,9 @@ longlong Item_func_release_lock::val_int() result=0; pthread_mutex_lock(&LOCK_user_locks); - if (!(ull= ((ULL*) hash_search(&hash_user_locks,(const byte*) res->ptr(), - res->length())))) + if (!(ull= ((User_level_lock*) hash_search(&hash_user_locks, + (const byte*) res->ptr(), + res->length())))) { null_value=1; } @@ -2060,18 +2148,29 @@ longlong Item_func_release_lock::val_int() } -longlong Item_func_set_last_insert_id::val_int() +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; + DBUG_ASSERT(fixed == 1); + if (arg_count) + { + 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(); + } } /* This function is just used to test speed of different functions */ longlong Item_func_benchmark::val_int() { + DBUG_ASSERT(fixed == 1); char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff), &my_charset_bin); THD *thd=current_thd; @@ -2164,6 +2263,7 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name, bool Item_func_set_user_var::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { + DBUG_ASSERT(fixed == 0); /* fix_fields will call Item_func_set_user_var::fix_length_and_dec */ if (Item_func::fix_fields(thd, tables, ref) || !(entry= get_variable(&thd->user_vars, name, 1))) @@ -2264,7 +2364,7 @@ double user_var_entry::val(my_bool *null_value) case INT_RESULT: return (double) *(longlong*) value; case STRING_RESULT: - return atof(value); // This is null terminated + return my_atof(value); // This is null terminated case ROW_RESULT: DBUG_ASSERT(1); // Impossible break; @@ -2351,7 +2451,6 @@ Item_func_set_user_var::check() save_result.vint= args[0]->val_int(); break; } - break; case STRING_RESULT: { save_result.vstr= args[0]->val_str(&value); @@ -2403,7 +2502,6 @@ Item_func_set_user_var::update() INT_RESULT, &my_charset_bin, DERIVATION_NONE); break; } - break; case STRING_RESULT: { if (!save_result.vstr) // Null value @@ -2428,6 +2526,7 @@ Item_func_set_user_var::update() double Item_func_set_user_var::val() { + DBUG_ASSERT(fixed == 1); check(); update(); // Store expression return entry->val(&null_value); @@ -2435,6 +2534,7 @@ double Item_func_set_user_var::val() longlong Item_func_set_user_var::val_int() { + DBUG_ASSERT(fixed == 1); check(); update(); // Store expression return entry->val_int(&null_value); @@ -2442,6 +2542,7 @@ longlong Item_func_set_user_var::val_int() String *Item_func_set_user_var::val_str(String *str) { + DBUG_ASSERT(fixed == 1); check(); update(); // Store expression return entry->val_str(&null_value, str, decimals); @@ -2461,6 +2562,7 @@ void Item_func_set_user_var::print(String *str) String * Item_func_get_user_var::val_str(String *str) { + DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_func_get_user_var::val_str"); if (!var_entry) DBUG_RETURN((String*) 0); // No such variable @@ -2470,6 +2572,7 @@ Item_func_get_user_var::val_str(String *str) double Item_func_get_user_var::val() { + DBUG_ASSERT(fixed == 1); if (!var_entry) return 0.0; // No such variable return (var_entry->val(&null_value)); @@ -2478,6 +2581,7 @@ double Item_func_get_user_var::val() longlong Item_func_get_user_var::val_int() { + DBUG_ASSERT(fixed == 1); if (!var_entry) return LL(0); // No such variable return (var_entry->val_int(&null_value)); @@ -2624,6 +2728,7 @@ bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const longlong Item_func_inet_aton::val_int() { + DBUG_ASSERT(fixed == 1); uint byte_result = 0; ulonglong result = 0; // We are ready for 64 bit addresses const char *p,* end; @@ -2691,6 +2796,13 @@ void Item_func_match::init_search(bool no_order) fields.push_back(args[i]); concat=new Item_func_concat_ws(new Item_string(" ",1, cmp_collation.collation), fields); + /* + Above function used only to get value and do not need fix_fields for it: + Item_string - basic constant + fields - fix_fields() was already called for this arguments + Item_func_concat_ws - do not need fix_fields() to produce value + */ + concat->quick_fix_field(); } if (master) @@ -2733,6 +2845,7 @@ void Item_func_match::init_search(bool no_order) bool Item_func_match::fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) { + DBUG_ASSERT(fixed == 0); Item *item; LINT_INIT(item); // Safe as arg_count is > 1 @@ -2877,6 +2990,7 @@ bool Item_func_match::eq(const Item *item, bool binary_cmp) const double Item_func_match::val() { + DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_func_match::val"); if (ft_handler == NULL) DBUG_RETURN(-1.0); @@ -2919,6 +3033,7 @@ void Item_func_match::print(String *str) longlong Item_func_bit_xor::val_int() { + DBUG_ASSERT(fixed == 1); ulonglong arg1= (ulonglong) args[0]->val_int(); ulonglong arg2= (ulonglong) args[1]->val_int(); if ((null_value= (args[0]->null_value || args[1]->null_value))) @@ -3047,9 +3162,10 @@ Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name, longlong Item_func_is_free_lock::val_int() { + DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); THD *thd=current_thd; - ULL *ull; + User_level_lock *ull; null_value=0; if (!res || !res->length()) @@ -3059,7 +3175,7 @@ longlong Item_func_is_free_lock::val_int() } pthread_mutex_lock(&LOCK_user_locks); - ull= (ULL*) hash_search(&hash_user_locks,(byte*) res->ptr(), + ull= (User_level_lock *) hash_search(&hash_user_locks, (byte*) res->ptr(), res->length()); pthread_mutex_unlock(&LOCK_user_locks); if (!ull || !ull->locked) @@ -3069,16 +3185,17 @@ longlong Item_func_is_free_lock::val_int() longlong Item_func_is_used_lock::val_int() { + DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); THD *thd=current_thd; - ULL *ull; + User_level_lock *ull; null_value=1; if (!res || !res->length()) return 0; pthread_mutex_lock(&LOCK_user_locks); - ull= (ULL*) hash_search(&hash_user_locks,(byte*) res->ptr(), + ull= (User_level_lock *) hash_search(&hash_user_locks, (byte*) res->ptr(), res->length()); pthread_mutex_unlock(&LOCK_user_locks); if (!ull || !ull->locked) diff --git a/sql/item_func.h b/sql/item_func.h index 6d313a8ea66..435615531b2 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -47,7 +47,7 @@ public: SP_CONTAINS_FUNC,SP_OVERLAPS_FUNC, SP_STARTPOINT,SP_ENDPOINT,SP_EXTERIORRING, SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN, - NOT_FUNC, NOT_ALL_FUNC, + NOT_FUNC, NOT_ALL_FUNC, NOW_FUNC, GUSERVAR_FUNC}; enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL }; enum Type type() const { return FUNC_ITEM; } @@ -156,7 +156,7 @@ public: Item_real_func(Item *a,Item *b) :Item_func(a,b) {} Item_real_func(List<Item> &list) :Item_func(list) {} String *val_str(String*str); - longlong val_int() { return (longlong) val(); } + longlong val_int() { DBUG_ASSERT(fixed == 1); return (longlong) val(); } enum Item_result result_type () const { return REAL_RESULT; } void fix_length_and_dec() { decimals=NOT_FIXED_DEC; max_length=float_length(decimals); } }; @@ -170,7 +170,7 @@ public: Item_num_func(Item *a) :Item_func(a),hybrid_type(REAL_RESULT) {} Item_num_func(Item *a,Item *b) :Item_func(a,b),hybrid_type(REAL_RESULT) {} String *val_str(String*str); - longlong val_int() { return (longlong) val(); } + longlong val_int() { DBUG_ASSERT(fixed == 1); return (longlong) val(); } enum Item_result result_type () const { return hybrid_type; } void fix_length_and_dec() { fix_num_length_and_dec(); } bool is_null() { (void) val(); return null_value; } @@ -201,7 +201,7 @@ public: Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c) { max_length=21; } Item_int_func(List<Item> &list) :Item_func(list) { max_length=21; } Item_int_func(THD *thd, Item_int_func *item) :Item_func(thd, item) {} - double val() { return (double) val_int(); } + double val() { DBUG_ASSERT(fixed == 1); return (double) val_int(); } String *val_str(String*str); enum Item_result result_type () const { return INT_RESULT; } void fix_length_and_dec() {} @@ -286,7 +286,7 @@ class Item_func_int_div :public Item_num_op public: Item_func_int_div(Item *a,Item *b) :Item_num_op(a,b) { hybrid_type=INT_RESULT; } - double val() { return (double) val_int(); } + double val() { DBUG_ASSERT(fixed == 1); return (double) val_int(); } longlong val_int(); const char *func_name() const { return "DIV"; } void fix_length_and_dec(); @@ -513,7 +513,7 @@ public: double val(); const char *func_name() const { return "rand"; } bool const_item() const { return 0; } - table_map used_tables() const { return RAND_TABLE_BIT; } + void update_used_tables(); void fix_length_and_dec(); }; @@ -585,7 +585,8 @@ class Item_func_bit_length :public Item_func_length { public: Item_func_bit_length(Item *a) :Item_func_length(a) {} - longlong val_int() { return Item_func_length::val_int()*8; } + longlong val_int() + { DBUG_ASSERT(fixed == 1); return Item_func_length::val_int()*8; } const char *func_name() const { return "bit_length"; } }; @@ -729,13 +730,14 @@ public: }; -class Item_func_set_last_insert_id :public Item_int_func +class Item_func_last_insert_id :public Item_int_func { public: - Item_func_set_last_insert_id(Item *a) :Item_int_func(a) {} + Item_func_last_insert_id() :Item_int_func() {} + Item_func_last_insert_id(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "last_insert_id"; } - void fix_length_and_dec() { max_length=args[0]->max_length; } + void fix_length_and_dec() { if (arg_count) max_length= args[0]->max_length; } }; class Item_func_benchmark :public Item_int_func @@ -766,6 +768,7 @@ public: const char *func_name() const { return udf.name(); } bool fix_fields(THD *thd, struct st_table_list *tables, Item **ref) { + DBUG_ASSERT(fixed == 0); bool res= udf.fix_fields(thd, tables, this, arg_count, args); used_tables_cache= udf.used_tables_cache; const_item_cache= udf.const_item_cache; @@ -783,7 +786,8 @@ class Item_func_udf_float :public Item_udf_func Item_func_udf_float(udf_func *udf_arg) :Item_udf_func(udf_arg) {} Item_func_udf_float(udf_func *udf_arg, List<Item> &list) :Item_udf_func(udf_arg,list) {} - longlong val_int() { return (longlong) Item_func_udf_float::val(); } + longlong val_int() + { DBUG_ASSERT(fixed == 1); return (longlong) Item_func_udf_float::val(); } double val(); String *val_str(String *str); void fix_length_and_dec() { fix_num_length_and_dec(); } @@ -834,7 +838,7 @@ class Item_func_udf_float :public Item_real_func public: Item_func_udf_float(udf_func *udf_arg) :Item_real_func() {} Item_func_udf_float(udf_func *udf_arg, List<Item> &list) :Item_real_func(list) {} - double val() { return 0.0; } + double val() { DBUG_ASSERT(fixed == 1); return 0.0; } }; @@ -843,7 +847,7 @@ class Item_func_udf_int :public Item_int_func public: Item_func_udf_int(udf_func *udf_arg) :Item_int_func() {} Item_func_udf_int(udf_func *udf_arg, List<Item> &list) :Item_int_func(list) {} - longlong val_int() { return 0; } + longlong val_int() { DBUG_ASSERT(fixed == 1); return 0; } }; @@ -852,9 +856,10 @@ class Item_func_udf_str :public Item_func public: Item_func_udf_str(udf_func *udf_arg) :Item_func() {} Item_func_udf_str(udf_func *udf_arg, List<Item> &list) :Item_func(list) {} - String *val_str(String *) { null_value=1; return 0; } - double val() { null_value=1; return 0.0; } - longlong val_int() { null_value=1; return 0; } + String *val_str(String *) + { DBUG_ASSERT(fixed == 1); null_value=1; return 0; } + double val() { DBUG_ASSERT(fixed == 1); null_value=1; return 0.0; } + longlong val_int() { DBUG_ASSERT(fixed == 1); null_value=1; return 0; } enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec() { maybe_null=1; max_length=0; } }; @@ -865,9 +870,9 @@ public: ** User level locks */ -class ULL; +class User_level_lock; void item_user_lock_init(void); -void item_user_lock_release(ULL *ull); +void item_user_lock_release(User_level_lock *ull); void item_user_lock_free(void); class Item_func_get_lock :public Item_int_func @@ -1002,6 +1007,8 @@ public: join_key(0), ft_handler(0), table(0), master(0), concat(0) { } void cleanup() { + DBUG_ENTER("Item_func_match"); + Item_real_func::cleanup(); if (!master && ft_handler) { ft_handler->please->close_search(ft_handler); @@ -1011,7 +1018,11 @@ public: table->fulltext_searched=0; } if (concat) + { delete concat; + concat= 0; + } + DBUG_VOID_RETURN; } enum Functype functype() const { return FT_FUNC; } const char *func_name() const { return "match"; } @@ -1019,7 +1030,7 @@ public: table_map not_null_tables() const { return 0; } bool fix_fields(THD *thd, struct st_table_list *tlist, Item **ref); bool eq(const Item *, bool binary_cmp) const; - longlong val_int() { return val()!=0.0; } + longlong val_int() { DBUG_ASSERT(fixed == 1); return val()!=0.0; } double val(); void print(String *str); diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 6934ad9d3b0..555c1a74eaf 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -27,24 +27,28 @@ #include "sql_acl.h" #include <m_ctype.h> + String *Item_func_geometry_from_text::val_str(String *str) { - Geometry geom; + DBUG_ASSERT(fixed == 1); + Geometry_buffer buffer; String arg_val; String *wkt= args[0]->val_str(&arg_val); - GTextReadStream trs(wkt->ptr(), wkt->length()); - uint32 srid; + + if ((null_value= args[0]->null_value)) + return 0; + + Gis_read_stream trs(wkt->charset(), wkt->ptr(), wkt->length()); + uint32 srid= 0; if ((arg_count == 2) && !args[1]->null_value) srid= (uint32)args[1]->val_int(); - else - srid= 0; if (str->reserve(SRID_SIZE, 512)) return 0; str->length(0); str->q_append(srid); - if ((null_value=(args[0]->null_value || geom.create_from_wkt(&trs, str, 0)))) + if ((null_value= !Geometry::create_from_wkt(&buffer, &trs, str, 0))) return 0; return str; } @@ -58,25 +62,24 @@ void Item_func_geometry_from_text::fix_length_and_dec() String *Item_func_geometry_from_wkb::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String arg_val; String *wkb= args[0]->val_str(&arg_val); - Geometry geom; - uint32 srid; + Geometry_buffer buffer; + uint32 srid= 0; if ((arg_count == 2) && !args[1]->null_value) srid= (uint32)args[1]->val_int(); - else - srid= 0; if (str->reserve(SRID_SIZE, 512)) return 0; str->length(0); str->q_append(srid); - if ((null_value= (args[0]->null_value || - geom.create_from_wkb(wkb->ptr(), wkb->length())))) + if ((null_value= + (args[0]->null_value || + !Geometry::create_from_wkb(&buffer, wkb->ptr(), wkb->length()) || + str->append(*wkb)))) return 0; - - str->append(*wkb); return str; } @@ -89,37 +92,44 @@ void Item_func_geometry_from_wkb::fix_length_and_dec() String *Item_func_as_wkt::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String arg_val; String *swkb= args[0]->val_str(&arg_val); - Geometry geom; - - if ((null_value= (args[0]->null_value || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)))) + Geometry_buffer buffer; + Geometry *geom= NULL; + const char *dummy; + + if ((null_value= + (args[0]->null_value || + !(geom= Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE))))) return 0; str->length(0); - - if ((null_value= geom.as_wkt(str))) + if ((null_value= geom->as_wkt(str, &dummy))) return 0; return str; } + void Item_func_as_wkt::fix_length_and_dec() { max_length=MAX_BLOB_WIDTH; } + String *Item_func_as_wkb::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String arg_val; String *swkb= args[0]->val_str(&arg_val); - Geometry geom; + Geometry_buffer buffer; - if ((null_value= (args[0]->null_value || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)))) + if ((null_value= + (args[0]->null_value || + !(Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE))))) return 0; str->copy(swkb->ptr() + SRID_SIZE, swkb->length() - SRID_SIZE, @@ -127,66 +137,78 @@ String *Item_func_as_wkb::val_str(String *str) return str; } + void Item_func_as_wkb::fix_length_and_dec() { max_length= MAX_BLOB_WIDTH; } + String *Item_func_geometry_type::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *swkb= args[0]->val_str(str); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom= NULL; - if ((null_value= (args[0]->null_value || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)))) + if ((null_value= + (args[0]->null_value || + !(geom= Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE))))) return 0; - str->copy(geom.get_class_info()->m_name, - strlen(geom.get_class_info()->m_name), - default_charset()); + /* String will not move */ + str->set(geom->get_class_info()->m_name.str, + geom->get_class_info()->m_name.length, + default_charset()); return str; } String *Item_func_envelope::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String arg_val; String *swkb= args[0]->val_str(&arg_val); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom= NULL; + uint32 srid; - if ((null_value= args[0]->null_value || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE))) + if ((null_value= + args[0]->null_value || + !(geom= Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE)))) return 0; - uint32 srid= uint4korr(swkb->ptr()); + srid= uint4korr(swkb->ptr()); str->length(0); if (str->reserve(SRID_SIZE, 512)) return 0; str->q_append(srid); - return (null_value= geom.envelope(str)) ? 0 : str; + return (null_value= geom->envelope(str)) ? 0 : str; } String *Item_func_centroid::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String arg_val; String *swkb= args[0]->val_str(&arg_val); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom= NULL; + uint32 srid; if ((null_value= args[0]->null_value || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE) || - !GEOM_METHOD_PRESENT(geom, centroid))) + !(geom= Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE)))) return 0; if (str->reserve(SRID_SIZE, 512)) return 0; str->length(0); - uint32 srid= uint4korr(swkb->ptr()); + srid= uint4korr(swkb->ptr()); str->q_append(srid); - return (null_value= geom.centroid(str)) ? 0 : str; + return (null_value= test(geom->centroid(str))) ? 0 : str; } @@ -196,91 +218,97 @@ String *Item_func_centroid::val_str(String *str) String *Item_func_spatial_decomp::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String arg_val; String *swkb= args[0]->val_str(&arg_val); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom= NULL; + uint32 srid; - if ((null_value= (args[0]->null_value || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)))) + if ((null_value= + (args[0]->null_value || + !(geom= Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE))))) return 0; - null_value= 1; + srid= uint4korr(swkb->ptr()); if (str->reserve(SRID_SIZE, 512)) - return 0; + goto err; str->length(0); - uint32 srid= uint4korr(swkb->ptr()); str->q_append(srid); - switch(decomp_func) - { + switch (decomp_func) { case SP_STARTPOINT: - if (!GEOM_METHOD_PRESENT(geom,start_point) || geom.start_point(str)) - goto ret; + if (geom->start_point(str)) + goto err; break; case SP_ENDPOINT: - if (!GEOM_METHOD_PRESENT(geom,end_point) || geom.end_point(str)) - goto ret; + if (geom->end_point(str)) + goto err; break; case SP_EXTERIORRING: - if (!GEOM_METHOD_PRESENT(geom,exterior_ring) || geom.exterior_ring(str)) - goto ret; + if (geom->exterior_ring(str)) + goto err; break; default: - goto ret; + goto err; } - null_value= 0; + return str; -ret: - return null_value ? 0 : str; +err: + null_value= 1; + return 0; } String *Item_func_spatial_decomp_n::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String arg_val; String *swkb= args[0]->val_str(&arg_val); long n= (long) args[1]->val_int(); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom= NULL; + uint32 srid; - if ((null_value= (args[0]->null_value || args[1]->null_value || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)))) + if ((null_value= + (args[0]->null_value || args[1]->null_value || + !(geom= Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE))))) return 0; - null_value= 1; if (str->reserve(SRID_SIZE, 512)) - return 0; + goto err; + srid= uint4korr(swkb->ptr()); str->length(0); - uint32 srid= uint4korr(swkb->ptr()); str->q_append(srid); - switch(decomp_func_n) + switch (decomp_func_n) { case SP_POINTN: - if (!GEOM_METHOD_PRESENT(geom,point_n) || geom.point_n(n,str)) - goto ret; + if (geom->point_n(n,str)) + goto err; break; case SP_GEOMETRYN: - if (!GEOM_METHOD_PRESENT(geom,geometry_n) || geom.geometry_n(n,str)) - goto ret; + if (geom->geometry_n(n,str)) + goto err; break; case SP_INTERIORRINGN: - if (!GEOM_METHOD_PRESENT(geom,interior_ring_n) || - geom.interior_ring_n(n,str)) - goto ret; + if (geom->interior_ring_n(n,str)) + goto err; break; default: - goto ret; + goto err; } - null_value= 0; + return str; -ret: - return null_value ? 0 : str; +err: + null_value=1; + return 0; } @@ -296,17 +324,18 @@ ret: String *Item_func_point::val_str(String *str) { + DBUG_ASSERT(fixed == 1); double x= args[0]->val(); double y= args[1]->val(); - if ( (null_value= (args[0]->null_value || - args[1]->null_value || - str->realloc(1 + 4 + 8 + 8)))) + if ((null_value= (args[0]->null_value || + args[1]->null_value || + str->realloc(1 + 4 + SIZEOF_STORED_DOUBLE*2)))) return 0; str->length(0); - str->q_append((char)Geometry::wkbNDR); - str->q_append((uint32)Geometry::wkbPoint); + str->q_append((char)Geometry::wkb_ndr); + str->q_append((uint32)Geometry::wkb_point); str->q_append(x); str->q_append(y); return str; @@ -325,16 +354,15 @@ String *Item_func_point::val_str(String *str) String *Item_func_spatial_collection::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String arg_value; uint i; - null_value= 1; - str->length(0); if (str->reserve(1 + 4 + 4, 512)) - return 0; + goto err; - str->q_append((char) Geometry::wkbNDR); + str->q_append((char) Geometry::wkb_ndr); str->q_append((uint32) coll_type); str->q_append((uint32) arg_count); @@ -342,19 +370,16 @@ String *Item_func_spatial_collection::val_str(String *str) { String *res= args[i]->val_str(&arg_value); if (args[i]->null_value) - goto ret; + goto err; - if ( coll_type == Geometry::wkbGeometryCollection ) + if (coll_type == Geometry::wkb_geometrycollection) { /* - In the case of GeometryCollection we don't need - any checkings for item types, so just copy them - into target collection + In the case of GeometryCollection we don't need any checkings + for item types, so just copy them into target collection */ - if ((null_value= str->reserve(res->length(), 512))) - goto ret; - - str->q_append(res->ptr(), res->length()); + if (str->append(res->ptr(), res->length(), (uint32) 512)) + goto err; } else { @@ -363,109 +388,101 @@ String *Item_func_spatial_collection::val_str(String *str) const char *data= res->ptr() + 1; /* - In the case of named collection we must to - check that items are of specific type, let's - do this checking now + In the case of named collection we must to check that items + are of specific type, let's do this checking now */ if (len < 5) - goto ret; + goto err; wkb_type= (Geometry::wkbType) uint4korr(data); data+= 4; len-= 5; if (wkb_type != item_type) - goto ret; + goto err; switch (coll_type) { - case Geometry::wkbMultiPoint: - case Geometry::wkbMultiLineString: - case Geometry::wkbMultiPolygon: - if (len < WKB_HEADER_SIZE) - goto ret; - - data-= WKB_HEADER_SIZE; - len+= WKB_HEADER_SIZE; - if (str->reserve(len, 512)) - goto ret; - str->q_append(data, len); + case Geometry::wkb_multipoint: + case Geometry::wkb_multilinestring: + case Geometry::wkb_multipolygon: + if (len < WKB_HEADER_SIZE || + str->append(data-WKB_HEADER_SIZE, len+WKB_HEADER_SIZE, 512)) + goto err; break; - case Geometry::wkbLineString: - if (str->reserve(POINT_DATA_SIZE, 512)) - goto ret; - str->q_append(data, POINT_DATA_SIZE); + case Geometry::wkb_linestring: + if (str->append(data, POINT_DATA_SIZE, 512)) + goto err; break; - - case Geometry::wkbPolygon: + case Geometry::wkb_polygon: { uint32 n_points; double x1, y1, x2, y2; + const char *org_data= data; if (len < 4 + 2 * POINT_DATA_SIZE) - goto ret; - - uint32 llen= len; - const char *ldata= data; + goto err; n_points= uint4korr(data); data+= 4; float8get(x1, data); - data+= 8; + data+= SIZEOF_STORED_DOUBLE; float8get(y1, data); - data+= 8; + data+= SIZEOF_STORED_DOUBLE; data+= (n_points - 2) * POINT_DATA_SIZE; float8get(x2, data); - float8get(y2, data + 8); - - if ((x1 != x2) || (y1 != y2)) - goto ret; + float8get(y2, data + SIZEOF_STORED_DOUBLE); - if (str->reserve(llen, 512)) - goto ret; - str->q_append(ldata, llen); + if ((x1 != x2) || (y1 != y2) || + str->append(org_data, len, 512)) + goto err; } break; default: - goto ret; + goto err; } } } - if (str->length() > current_thd->variables.max_allowed_packet) - goto ret; + goto err; null_value = 0; + return str; -ret: - return null_value ? 0 : str; +err: + null_value= 1; + return 0; } + /* Functions for spatial relations */ longlong Item_func_spatial_rel::val_int() { + DBUG_ASSERT(fixed == 1); String *res1= args[0]->val_str(&tmp_value1); String *res2= args[1]->val_str(&tmp_value2); - Geometry g1, g2; + Geometry_buffer buffer1, buffer2; + Geometry *g1, *g2; MBR mbr1, mbr2; - - if ((null_value= (args[0]->null_value || - args[1]->null_value || - g1.create_from_wkb(res1->ptr() + SRID_SIZE, - res1->length() - SRID_SIZE) || - g2.create_from_wkb(res2->ptr() + SRID_SIZE, - res2->length() - SRID_SIZE) || - g1.get_mbr(&mbr1) || - g2.get_mbr(&mbr2)))) + const char *dummy; + + if ((null_value= + (args[0]->null_value || + args[1]->null_value || + !(g1= Geometry::create_from_wkb(&buffer1, res1->ptr() + SRID_SIZE, + res1->length() - SRID_SIZE)) || + !(g2= Geometry::create_from_wkb(&buffer2, res2->ptr() + SRID_SIZE, + res2->length() - SRID_SIZE)) || + g1->get_mbr(&mbr1, &dummy) || + g2->get_mbr(&mbr2, &dummy)))) return 0; - switch (spatial_rel) - { + switch (spatial_rel) { case SP_CONTAINS_FUNC: return mbr1.contains(&mbr2); case SP_WITHIN_FUNC: @@ -490,37 +507,44 @@ longlong Item_func_spatial_rel::val_int() return 0; } + longlong Item_func_isempty::val_int() { + DBUG_ASSERT(fixed == 1); String tmp; null_value=0; return args[0]->null_value ? 1 : 0; } + longlong Item_func_issimple::val_int() { + DBUG_ASSERT(fixed == 1); String tmp; String *wkb=args[0]->val_str(&tmp); - if ((null_value= (!wkb || args[0]->null_value ))) + if ((null_value= (!wkb || args[0]->null_value))) return 0; /* TODO: Ramil or Holyfoot, add real IsSimple calculation */ return 0; } + longlong Item_func_isclosed::val_int() { + DBUG_ASSERT(fixed == 1); String tmp; String *swkb= args[0]->val_str(&tmp); - Geometry geom; - int isclosed; + Geometry_buffer buffer; + Geometry *geom; + int isclosed= 0; // In case of error null_value= (!swkb || args[0]->null_value || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE) || - !GEOM_METHOD_PRESENT(geom,is_closed) || - geom.is_closed(&isclosed)); + !(geom= + Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE)) || + geom->is_closed(&isclosed)); return (longlong) isclosed; } @@ -529,129 +553,160 @@ longlong Item_func_isclosed::val_int() Numerical functions */ + longlong Item_func_dimension::val_int() { - uint32 dim; + DBUG_ASSERT(fixed == 1); + uint32 dim= 0; // In case of error String *swkb= args[0]->val_str(&value); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom; + const char *dummy; null_value= (!swkb || args[0]->null_value || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE) || - geom.dimension(&dim)); + !(geom= Geometry::create_from_wkb(&buffer, + swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE)) || + geom->dimension(&dim, &dummy)); return (longlong) dim; } + longlong Item_func_numinteriorring::val_int() { - uint32 num; + DBUG_ASSERT(fixed == 1); + uint32 num= 0; // In case of error String *swkb= args[0]->val_str(&value); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom; null_value= (!swkb || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE) || - !GEOM_METHOD_PRESENT(geom, num_interior_ring) || - geom.num_interior_ring(&num)); + !(geom= Geometry::create_from_wkb(&buffer, + swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE)) || + geom->num_interior_ring(&num)); return (longlong) num; } + longlong Item_func_numgeometries::val_int() { - uint32 num= 0; + DBUG_ASSERT(fixed == 1); + uint32 num= 0; // In case of errors String *swkb= args[0]->val_str(&value); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom; null_value= (!swkb || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE) || - !GEOM_METHOD_PRESENT(geom, num_geometries) || - geom.num_geometries(&num)); + !(geom= Geometry::create_from_wkb(&buffer, + swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE)) || + geom->num_geometries(&num)); return (longlong) num; } + longlong Item_func_numpoints::val_int() { - uint32 num; + DBUG_ASSERT(fixed == 1); + uint32 num= 0; // In case of errors String *swkb= args[0]->val_str(&value); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom; null_value= (!swkb || args[0]->null_value || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE) || - !GEOM_METHOD_PRESENT(geom, num_points) || - geom.num_points(&num)); + !(geom= Geometry::create_from_wkb(&buffer, + swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE)) || + geom->num_points(&num)); return (longlong) num; } + double Item_func_x::val() { - double res; + DBUG_ASSERT(fixed == 1); + double res= 0.0; // In case of errors String *swkb= args[0]->val_str(&value); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom; null_value= (!swkb || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE) || - !GEOM_METHOD_PRESENT(geom, get_x) || - geom.get_x(&res)); + !(geom= Geometry::create_from_wkb(&buffer, + swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE)) || + geom->get_x(&res)); return res; } + double Item_func_y::val() { - double res; + DBUG_ASSERT(fixed == 1); + double res= 0; // In case of errors String *swkb= args[0]->val_str(&value); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom; null_value= (!swkb || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE) || - !GEOM_METHOD_PRESENT(geom, get_y) || - geom.get_y(&res)); + !(geom= Geometry::create_from_wkb(&buffer, + swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE)) || + geom->get_y(&res)); return res; } + double Item_func_area::val() { - double res; + DBUG_ASSERT(fixed == 1); + double res= 0; // In case of errors String *swkb= args[0]->val_str(&value); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom; + const char *dummy; null_value= (!swkb || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE) || - !GEOM_METHOD_PRESENT(geom, area) || - geom.area(&res)); + !(geom= Geometry::create_from_wkb(&buffer, + swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE)) || + geom->area(&res, &dummy)); return res; } double Item_func_glength::val() { - double res; + DBUG_ASSERT(fixed == 1); + double res= 0; // In case of errors String *swkb= args[0]->val_str(&value); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom; null_value= (!swkb || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE) || - !GEOM_METHOD_PRESENT(geom, length) || - geom.length(&res)); + !(geom= Geometry::create_from_wkb(&buffer, + swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE)) || + geom->length(&res)); return res; } longlong Item_func_srid::val_int() { + DBUG_ASSERT(fixed == 1); String *swkb= args[0]->val_str(&value); - Geometry geom; + Geometry_buffer buffer; + Geometry *geom; null_value= (!swkb || - geom.create_from_wkb(swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)); - uint32 res= uint4korr(swkb->ptr()); - return (longlong) res; + !Geometry::create_from_wkb(&buffer, + swkb->ptr() + SRID_SIZE, + swkb->length() - SRID_SIZE)); + if (null_value) + return 0; + + return (longlong) (uint4korr(swkb->ptr())); } #endif /*HAVE_SPATIAL*/ diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index 545052807ec..a1f36130152 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -23,8 +23,6 @@ #pragma interface /* gcc class implementation */ #endif -#define SRID_SIZE sizeof(uint32) - class Item_func_geometry_from_text: public Item_str_func { public: diff --git a/sql/item_row.cc b/sql/item_row.cc index 7f847bd1d4e..c7e4bc0acf4 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -55,6 +55,7 @@ void Item_row::illegal_method_call(const char *method) bool Item_row::fix_fields(THD *thd, TABLE_LIST *tabl, Item **ref) { + DBUG_ASSERT(fixed == 0); null_value= 0; maybe_null= 0; Item **arg, **arg_end; @@ -62,20 +63,23 @@ bool Item_row::fix_fields(THD *thd, TABLE_LIST *tabl, Item **ref) { if ((*arg)->fix_fields(thd, tabl, arg)) return 1; - used_tables_cache |= (*arg)->used_tables(); - if (const_item_cache&= (*arg)->const_item() && !with_null) + // we can't assign 'item' before, because fix_fields() can change arg + Item *item= *arg; + used_tables_cache |= item->used_tables(); + if (const_item_cache&= item->const_item() && !with_null) { - if ((*arg)->cols() > 1) - with_null|= (*arg)->null_inside(); + if (item->cols() > 1) + with_null|= item->null_inside(); else { - (*arg)->val_int(); - with_null|= (*arg)->null_value; + item->val_int(); + with_null|= item->null_value; } } - maybe_null|= (*arg)->maybe_null; - with_sum_func= with_sum_func || (*arg)->with_sum_func; + maybe_null|= item->maybe_null; + with_sum_func= with_sum_func || item->with_sum_func; } + fixed= 1; return 0; } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index ed6e44262c7..933995c1d22 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -60,6 +60,7 @@ uint nr_of_decimals(const char *str) double Item_str_func::val() { + DBUG_ASSERT(fixed == 1); int err; String *res; res=val_str(&str_value); @@ -69,15 +70,20 @@ double Item_str_func::val() longlong Item_str_func::val_int() { + DBUG_ASSERT(fixed == 1); int err; String *res; res=val_str(&str_value); - return res ? my_strntoll(res->charset(),res->ptr(),res->length(),10,NULL,&err) : (longlong) 0; + return (res ? + my_strntoll(res->charset(), res->ptr(), res->length(), 10, NULL, + &err) : + (longlong) 0); } String *Item_func_md5::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String * sptr= args[0]->val_str(str); if (sptr) { @@ -115,6 +121,7 @@ void Item_func_md5::fix_length_and_dec() String *Item_func_sha::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String * sptr= args[0]->val_str(str); if (sptr) /* If we got value different from NULL */ { @@ -155,6 +162,7 @@ void Item_func_sha::fix_length_and_dec() String *Item_func_aes_encrypt::val_str(String *str) { + DBUG_ASSERT(fixed == 1); char key_buff[80]; String tmp_key_value(key_buff, sizeof(key_buff), system_charset_info); String *sptr= args[0]->val_str(str); // String to encrypt @@ -190,6 +198,7 @@ void Item_func_aes_encrypt::fix_length_and_dec() String *Item_func_aes_decrypt::val_str(String *str) { + DBUG_ASSERT(fixed == 1); char key_buff[80]; String tmp_key_value(key_buff, sizeof(key_buff), system_charset_info); String *sptr, *key; @@ -234,6 +243,7 @@ void Item_func_aes_decrypt::fix_length_and_dec() String *Item_func_concat::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res,*res2,*use_as_buff; uint i; @@ -350,6 +360,7 @@ void Item_func_concat::fix_length_and_dec() String *Item_func_des_encrypt::val_str(String *str) { + DBUG_ASSERT(fixed == 1); #ifdef HAVE_OPENSSL DES_cblock ivec; struct st_des_keyblock keyblock; @@ -432,6 +443,7 @@ error: String *Item_func_des_decrypt::val_str(String *str) { + DBUG_ASSERT(fixed == 1); #ifdef HAVE_OPENSSL DES_key_schedule ks1, ks2, ks3; DES_cblock ivec; @@ -503,6 +515,7 @@ error: String *Item_func_concat_ws::val_str(String *str) { + DBUG_ASSERT(fixed == 1); char tmp_str_buff[10]; String tmp_sep_str(tmp_str_buff, sizeof(tmp_str_buff),default_charset_info), *sep_str, *res, *res2,*use_as_buff; @@ -628,9 +641,11 @@ void Item_func_concat_ws::fix_length_and_dec() max_length=separator->max_length*(arg_count-1); for (uint i=0 ; i < arg_count ; i++) { + DTCollation tmp(collation.collation, collation.derivation); 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; } @@ -668,6 +683,7 @@ void Item_func_concat_ws::print(String *str) String *Item_func_reverse::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res = args[0]->val_str(str); char *ptr,*end; @@ -725,6 +741,7 @@ void Item_func_reverse::fix_length_and_dec() String *Item_func_replace::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res,*res2,*res3; int offset; uint from_length,to_length; @@ -845,6 +862,7 @@ void Item_func_replace::fix_length_and_dec() String *Item_func_insert::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res,*res2; uint start,length; @@ -892,6 +910,7 @@ void Item_func_insert::fix_length_and_dec() String *Item_func_lcase::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res; if (!(res=args[0]->val_str(str))) { @@ -907,6 +926,7 @@ String *Item_func_lcase::val_str(String *str) String *Item_func_ucase::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res; if (!(res=args[0]->val_str(str))) { @@ -922,6 +942,7 @@ String *Item_func_ucase::val_str(String *str) String *Item_func_left::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res =args[0]->val_str(str); long length =(long) args[1]->val_int(); @@ -966,6 +987,7 @@ void Item_func_left::fix_length_and_dec() String *Item_func_right::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res =args[0]->val_str(str); long length =(long) args[1]->val_int(); @@ -994,6 +1016,7 @@ void Item_func_right::fix_length_and_dec() String *Item_func_substr::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res = args[0]->val_str(str); int32 start = (int32) args[1]->val_int(); int32 length = arg_count == 3 ? (int32) args[2]->val_int() : INT_MAX32; @@ -1033,7 +1056,7 @@ void Item_func_substr::fix_length_and_dec() } if (arg_count == 3 && args[2]->const_item()) { - int32 length= (int32) args[2]->val_int() * default_charset_info->mbmaxlen; + int32 length= (int32) args[2]->val_int() * collation.collation->mbmaxlen; if (length <= 0) max_length=0; /* purecov: inspected */ else @@ -1053,6 +1076,7 @@ void Item_func_substr_index::fix_length_and_dec() String *Item_func_substr_index::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res =args[0]->val_str(str); String *delimeter =args[1]->val_str(&tmp_value); int32 count = (int32) args[2]->val_int(); @@ -1164,6 +1188,7 @@ String *Item_func_substr_index::val_str(String *str) String *Item_func_ltrim::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res =args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ @@ -1202,6 +1227,7 @@ String *Item_func_ltrim::val_str(String *str) String *Item_func_rtrim::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res =args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ @@ -1274,6 +1300,7 @@ String *Item_func_rtrim::val_str(String *str) String *Item_func_trim::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res =args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ @@ -1346,6 +1373,7 @@ void Item_func_trim::fix_length_and_dec() String *Item_func_password::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res= args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; @@ -1368,6 +1396,7 @@ char *Item_func_password::alloc(THD *thd, const char *password) String *Item_func_old_password::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res= args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; @@ -1391,6 +1420,7 @@ char *Item_func_old_password::alloc(THD *thd, const char *password) String *Item_func_encrypt::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res =args[0]->val_str(str); #ifdef HAVE_CRYPT @@ -1435,6 +1465,7 @@ void Item_func_encode::fix_length_and_dec() String *Item_func_encode::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res; if (!(res=args[0]->val_str(str))) { @@ -1450,6 +1481,7 @@ String *Item_func_encode::val_str(String *str) String *Item_func_decode::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res; if (!(res=args[0]->val_str(str))) { @@ -1466,6 +1498,7 @@ String *Item_func_decode::val_str(String *str) String *Item_func_database::val_str(String *str) { + DBUG_ASSERT(fixed == 1); THD *thd= current_thd; if (!thd->db) { @@ -1481,6 +1514,7 @@ String *Item_func_database::val_str(String *str) String *Item_func_user::val_str(String *str) { + DBUG_ASSERT(fixed == 1); THD *thd=current_thd; CHARSET_INFO *cs= system_charset_info; const char *host= thd->host_or_ip; @@ -1531,6 +1565,7 @@ static char get_scode(CHARSET_INFO *cs,char *ptr) String *Item_func_soundex::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res =args[0]->val_str(str); char last_ch,ch; CHARSET_INFO *cs= collation.collation; @@ -1591,6 +1626,7 @@ Item_func_format::Item_func_format(Item *org,int dec) :Item_str_func(org) String *Item_func_format::val_str(String *str) { + DBUG_ASSERT(fixed == 1); double nr =args[0]->val(); uint32 diff,length,str_length; uint dec; @@ -1599,10 +1635,8 @@ String *Item_func_format::val_str(String *str) dec= decimals ? decimals+1 : 0; /* Here default_charset() is right as this is not an automatic conversion */ str->set(nr,decimals, default_charset()); -#ifdef HAVE_ISNAN if (isnan(nr)) return str; -#endif str_length=str->length(); if (nr < 0) str_length--; // Don't count sign @@ -1663,6 +1697,7 @@ void Item_func_elt::fix_length_and_dec() double Item_func_elt::val() { + DBUG_ASSERT(fixed == 1); uint tmp; null_value=1; if ((tmp=(uint) args[0]->val_int()) == 0 || tmp >= arg_count) @@ -1675,6 +1710,7 @@ double Item_func_elt::val() longlong Item_func_elt::val_int() { + DBUG_ASSERT(fixed == 1); uint tmp; null_value=1; if ((tmp=(uint) args[0]->val_int()) == 0 || tmp >= arg_count) @@ -1688,6 +1724,7 @@ longlong Item_func_elt::val_int() String *Item_func_elt::val_str(String *str) { + DBUG_ASSERT(fixed == 1); uint tmp; null_value=1; if ((tmp=(uint) args[0]->val_int()) == 0 || tmp >= arg_count) @@ -1745,6 +1782,7 @@ void Item_func_make_set::update_used_tables() String *Item_func_make_set::val_str(String *str) { + DBUG_ASSERT(fixed == 1); ulonglong bits; bool first_found=0; Item **ptr=args; @@ -1810,6 +1848,7 @@ void Item_func_make_set::print(String *str) String *Item_func_char::val_str(String *str) { + DBUG_ASSERT(fixed == 1); str->length(0); for (uint i=0 ; i < arg_count ; i++) { @@ -1884,6 +1923,7 @@ void Item_func_repeat::fix_length_and_dec() String *Item_func_repeat::val_str(String *str) { + DBUG_ASSERT(fixed == 1); uint length,tot_length; char *to; long count= (long) args[1]->val_int(); @@ -1946,6 +1986,7 @@ void Item_func_rpad::fix_length_and_dec() String *Item_func_rpad::val_str(String *str) { + DBUG_ASSERT(fixed == 1); uint32 res_byte_length,res_char_length,pad_char_length,pad_byte_length; char *to; const char *ptr_pad; @@ -2022,6 +2063,7 @@ void Item_func_lpad::fix_length_and_dec() String *Item_func_lpad::val_str(String *str) { + DBUG_ASSERT(fixed == 1); uint32 res_char_length,pad_char_length; ulong count= (long) args[1]->val_int(), byte_count; String *res= args[0]->val_str(&tmp_value); @@ -2071,6 +2113,7 @@ err: String *Item_func_conv::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res= args[0]->val_str(str); char *endptr,ans[65],*ptr; longlong dec; @@ -2099,6 +2142,7 @@ String *Item_func_conv::val_str(String *str) String *Item_func_conv_charset::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *arg= args[0]->val_str(str); if (!arg) { @@ -2126,6 +2170,7 @@ void Item_func_conv_charset::print(String *str) String *Item_func_set_collation::val_str(String *str) { + DBUG_ASSERT(fixed == 1); str=args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; @@ -2185,6 +2230,7 @@ bool Item_func_set_collation::eq(const Item *item, bool binary_cmp) const String *Item_func_charset::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res = args[0]->val_str(str); if ((null_value=(args[0]->null_value || !res->charset()))) @@ -2196,6 +2242,7 @@ String *Item_func_charset::val_str(String *str) String *Item_func_collation::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res = args[0]->val_str(str); if ((null_value=(args[0]->null_value || !res->charset()))) @@ -2208,6 +2255,7 @@ String *Item_func_collation::val_str(String *str) String *Item_func_hex::val_str(String *str) { + DBUG_ASSERT(fixed == 1); if (args[0]->result_type() != STRING_RESULT) { /* Return hex of unsigned longlong value */ @@ -2233,7 +2281,7 @@ String *Item_func_hex::val_str(String *str) null_value=0; tmp_value.length(res->length()*2); for (from=res->ptr(), end=from+res->length(), to= (char*) tmp_value.ptr(); - from != end ; + from < end ; from++, to+=2) { uint tmp=(uint) (uchar) *from; @@ -2243,6 +2291,49 @@ 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; +} + +String *Item_func_unhex::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + /* Convert given hex string to a binary string */ + String *res= args[0]->val_str(str); + const char *from=res->ptr(), *end; + char *to; + int r; + if (!res || tmp_value.alloc((1+res->length())/2)) + { + null_value=1; + return 0; + } + null_value=0; + tmp_value.length((1+res->length())/2); + to= (char*) tmp_value.ptr(); + if (res->length() % 2) + { + *to++= r= hexchar_to_int(*from++); + if ((null_value= (r == -1))) + return 0; + } + for (end=res->ptr()+res->length(); from < end ; from+=2, to++) + { + *to= (r= hexchar_to_int(from[0])) << 4; + if ((null_value= (r == -1))) + return 0; + *to|= r= hexchar_to_int(from[1]); + if ((null_value= (r == -1))) + return 0; + } + return &tmp_value; +} void Item_func_binary::print(String *str) { @@ -2256,6 +2347,7 @@ void Item_func_binary::print(String *str) String *Item_load_file::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *file_name; File file; MY_STAT stat_info; @@ -2299,6 +2391,7 @@ err: String* Item_func_export_set::val_str(String* str) { + DBUG_ASSERT(fixed == 1); ulonglong the_set = (ulonglong) args[0]->val_int(); String yes_buf, *yes; yes = args[1]->val_str(&yes_buf); @@ -2368,6 +2461,7 @@ void Item_func_export_set::fix_length_and_dec() String* Item_func_inet_ntoa::val_str(String* str) { + DBUG_ASSERT(fixed == 1); uchar buf[8], *p; ulonglong n = (ulonglong) args[0]->val_int(); char num[4]; @@ -2427,6 +2521,7 @@ String* Item_func_inet_ntoa::val_str(String* str) String *Item_func_quote::val_str(String *str) { + DBUG_ASSERT(fixed == 1); /* Bit mask that has 1 for set for the position of the following characters: 0, \, ' and ^Z @@ -2501,6 +2596,7 @@ null: longlong Item_func_uncompressed_length::val_int() { + DBUG_ASSERT(fixed == 1); String *res= args[0]->val_str(&value); if (!res) { @@ -2522,6 +2618,7 @@ longlong Item_func_uncompressed_length::val_int() longlong Item_func_crc32::val_int() { + DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); if (!res) { @@ -2537,6 +2634,7 @@ longlong Item_func_crc32::val_int() String *Item_func_compress::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res= args[0]->val_str(str); if (!res) { @@ -2556,7 +2654,7 @@ String *Item_func_compress::val_str(String *str) size of the destination buffer, which must be at least 0.1% larger than sourceLen plus 12 bytes. - Proportion 120/100 founded by Sinica with help of procedure + Proportion 120/100 founded by Sinisa with help of procedure compress(compress(compress(...))) I.e. zlib give number 'at least'.. */ @@ -2594,6 +2692,7 @@ String *Item_func_compress::val_str(String *str) String *Item_func_uncompress::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res= args[0]->val_str(str); ulong new_size; int err; @@ -2632,3 +2731,113 @@ err: return 0; } #endif + +/* + UUID, as in + DCE 1.1: Remote Procedure Call, + Open Group Technical Standard Document Number C706, October 1997, + (supersedes C309 DCE: Remote Procedure Call 8/1994, + which was basis for ISO/IEC 11578:1996 specification) +*/ + +static struct rand_struct uuid_rand; +static uint nanoseq; +static ulonglong uuid_time=0; +static char clock_seq_and_node_str[]="-0000-000000000000"; + +/* we cannot use _dig_vec[] as letters should be lowercase */ +static const char hex[] = "0123456789abcdef"; + +/* number of 100-nanosecond intervals between + 1582-10-15 00:00:00.00 and 1970-01-01 00:00:00.00 */ +#define UUID_TIME_OFFSET ((ulonglong) 141427 * 24 * 60 * 60 * 1000 * 10 ) + +#define UUID_VERSION 0x1000 +#define UUID_VARIANT 0x8000 + +static void tohex(char *to, uint from, uint len) +{ + to+= len; + while (len--) + { + *--to= hex[from & 15]; + from >>= 4; + } +} + +static void set_clock_seq_str() +{ + uint16 clock_seq= ((uint)(my_rnd(&uuid_rand)*16383)) | UUID_VARIANT; + tohex(clock_seq_and_node_str+1, clock_seq, 4); + nanoseq= 0; +} + +String *Item_func_uuid::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + char *s; + pthread_mutex_lock(&LOCK_uuid_generator); + if (! uuid_time) /* first UUID() call. initializing data */ + { + ulong tmp=sql_rnd_with_mutex(); + uchar mac[6]; + int i; + if (my_gethwaddr(mac)) + { + /* + generating random "hardware addr" + and because specs explicitly specify that it should NOT correlate + with a clock_seq value (initialized random below), we use a separate + randominit() here + */ + randominit(&uuid_rand, tmp + (ulong)current_thd, tmp + query_id); + for (i=0; i < (int)sizeof(mac); i++) + mac[i]=(uchar)(my_rnd(&uuid_rand)*255); + } + s=clock_seq_and_node_str+sizeof(clock_seq_and_node_str)-1; + for (i=sizeof(mac)-1 ; i>=0 ; i--) + { + *--s=hex[mac[i] & 15]; + *--s=hex[mac[i] >> 4]; + } + randominit(&uuid_rand, tmp + (ulong)start_time, tmp + bytes_sent); + set_clock_seq_str(); + } + + ulonglong tv=my_getsystime() + UUID_TIME_OFFSET + nanoseq; + if (unlikely(tv < uuid_time)) + set_clock_seq_str(); + else + if (unlikely(tv == uuid_time)) + { /* special protection from low-res system clocks */ + nanoseq++; + tv++; + } + else + { + if (nanoseq) + { + tv-=nanoseq; + nanoseq=0; + } + DBUG_ASSERT(tv > uuid_time); + } + uuid_time=tv; + pthread_mutex_unlock(&LOCK_uuid_generator); + + uint32 time_low= tv & 0xFFFFFFFF; + uint16 time_mid= (tv >> 32) & 0xFFFF; + uint16 time_hi_and_version= (tv >> 48) | UUID_VERSION; + + str->realloc(UUID_LENGTH+1); + str->length(UUID_LENGTH); + str->set_charset(system_charset_info); + s=(char *) str->ptr(); + s[8]=s[13]='-'; + tohex(s, time_low, 8); + tohex(s+9, time_mid, 4); + tohex(s+14, time_hi_and_version, 4); + strmov(s+18, clock_seq_and_node_str); + return str; +} + diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 4832ddbd1b1..22134733393 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -100,6 +100,7 @@ public: 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)); @@ -153,7 +154,7 @@ class Item_str_conv :public Item_str_func public: Item_str_conv(Item *item) :Item_str_func(item) {} void fix_length_and_dec() - { + { collation.set(args[0]->collation); max_length = args[0]->max_length; } @@ -411,6 +412,7 @@ public: String *val_str(String *str); bool fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) { + DBUG_ASSERT(fixed == 0); return (item->fix_fields(thd, tlist, &item) || item->check_cols(1) || Item_func::fix_fields(thd, tlist, ref)); @@ -500,10 +502,10 @@ public: Item_func_conv(Item *a,Item *b,Item *c) :Item_str_func(a,b,c) {} const char *func_name() const { return "conv"; } String *val_str(String *); - void fix_length_and_dec() - { + void fix_length_and_dec() + { collation.set(default_charset()); - decimals=0; max_length=64; + decimals=0; max_length=64; } }; @@ -515,14 +517,29 @@ public: Item_func_hex(Item *a) :Item_str_func(a) {} const char *func_name() const { return "hex"; } String *val_str(String *); - void fix_length_and_dec() - { + void fix_length_and_dec() + { collation.set(default_charset()); decimals=0; max_length=args[0]->max_length*2*collation.collation->mbmaxlen; } }; +class Item_func_unhex :public Item_str_func +{ + String tmp_value; +public: + Item_func_unhex(Item *a) :Item_str_func(a) {} + const char *func_name() const { return "unhex"; } + String *val_str(String *); + void fix_length_and_dec() + { + collation.set(&my_charset_bin); + decimals=0; + max_length=(1+args[0]->max_length)/2; + } +}; + class Item_func_binary :public Item_str_func { @@ -530,16 +547,17 @@ public: Item_func_binary(Item *a) :Item_str_func(a) {} String *val_str(String *a) { + DBUG_ASSERT(fixed == 1); String *tmp=args[0]->val_str(a); null_value=args[0]->null_value; if (tmp) tmp->set_charset(&my_charset_bin); return tmp; } - void fix_length_and_dec() - { - collation.set(&my_charset_bin); - max_length=args[0]->max_length; + void fix_length_and_dec() + { + collation.set(&my_charset_bin); + max_length=args[0]->max_length; } void print(String *str); }; @@ -553,7 +571,7 @@ public: String *val_str(String *); const char *func_name() const { return "load_file"; } void fix_length_and_dec() - { + { collation.set(&my_charset_bin, DERIVATION_COERCIBLE); maybe_null=1; max_length=MAX_BLOB_WIDTH; @@ -589,10 +607,10 @@ public: Item_func_quote(Item *a) :Item_str_func(a) {} const char *func_name() const { return "quote"; } String *val_str(String *); - void fix_length_and_dec() - { + void fix_length_and_dec() + { collation.set(args[0]->collation); - max_length= args[0]->max_length * 2 + 2; + max_length= args[0]->max_length * 2 + 2; } }; @@ -600,7 +618,7 @@ class Item_func_conv_charset :public Item_str_func { CHARSET_INFO *conv_charset; public: - Item_func_conv_charset(Item *a, CHARSET_INFO *cs) :Item_str_func(a) + Item_func_conv_charset(Item *a, CHARSET_INFO *cs) :Item_str_func(a) { conv_charset=cs; } String *val_str(String *); void fix_length_and_dec(); @@ -625,7 +643,7 @@ public: Item_func_charset(Item *a) :Item_str_func(a) {} String *val_str(String *); const char *func_name() const { return "charset"; } - void fix_length_and_dec() + void fix_length_and_dec() { collation.set(system_charset_info); max_length= 64 * collation.collation->mbmaxlen; // should be enough @@ -691,3 +709,16 @@ public: String *val_str(String *) ZLIB_DEPENDED_FUNCTION }; +#define UUID_LENGTH (8+1+4+1+4+1+4+1+12) +class Item_func_uuid: public Item_str_func +{ +public: + Item_func_uuid(): Item_str_func() {} + void fix_length_and_dec() { + collation.set(system_charset_info); + max_length= UUID_LENGTH; + } + const char *func_name() const{ return "uuid"; } + String *val_str(String *); +}; + diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index d4d9f832a3d..390c734a87a 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -35,9 +35,9 @@ inline Item * and_items(Item* cond, Item *item) } Item_subselect::Item_subselect(): - Item_result_field(), value_assigned(0), substitution(0), - engine(0), used_tables_cache(0), have_to_be_excluded(0), - const_item_cache(1), engine_changed(0) + Item_result_field(), value_assigned(0), thd(0), substitution(0), + engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0), + const_item_cache(1), engine_changed(0), changed(0) { reset(); /* @@ -54,10 +54,10 @@ void Item_subselect::init(st_select_lex *select_lex, DBUG_ENTER("Item_subselect::init"); DBUG_PRINT("subs", ("select_lex 0x%xl", (ulong) select_lex)); + unit= select_lex->master_unit(); if (select_lex->next_select()) - engine= new subselect_union_engine(select_lex->master_unit(), result, - this); + engine= new subselect_union_engine(unit, result, this); else engine= new subselect_single_select_engine(select_lex, result, this); DBUG_VOID_RETURN; @@ -65,8 +65,26 @@ void Item_subselect::init(st_select_lex *select_lex, void Item_subselect::cleanup() { + DBUG_ENTER("Item_subselect::cleanup"); Item_result_field::cleanup(); - engine->cleanup(); + if (old_engine) + { + engine->cleanup(); + engine= old_engine; + old_engine= 0; + } + engine->cleanup(); + reset(); + value_assigned= 0; + DBUG_VOID_RETURN; +} + +void Item_singlerow_subselect::cleanup() +{ + DBUG_ENTER("Item_singlerow_subselect::cleanup"); + value= 0; row= 0; + Item_subselect::cleanup(); + DBUG_VOID_RETURN; } Item_subselect::~Item_subselect() @@ -84,22 +102,35 @@ Item_subselect::select_transformer(JOIN *join) bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref) { + DBUG_ASSERT(fixed == 0); engine->set_thd((thd= thd_param)); + stmt= thd->current_statement; char const *save_where= thd->where; int res= engine->prepare(); + + // all transformetion is done (used by prepared statements) + changed= 1; + if (!res) { if (substitution) { + int ret= 0; + + // did we changed top item of WHERE condition + if (unit->outer_select()->where == (*ref)) + unit->outer_select()->where= substitution; // correct WHERE for PS + (*ref)= substitution; substitution->name= name; if (have_to_be_excluded) engine->exclude(); substitution= 0; fixed= 1; - thd->where= "checking transformed subquery"; - int ret= (*ref)->fix_fields(thd, tables, 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 ((*ref)->with_sum_func) { @@ -171,6 +202,12 @@ bool Item_subselect::const_item() const return const_item_cache; } +Item *Item_subselect::get_tmp_table_item(THD *thd) +{ + if (!with_sum_func && !const_item()) + return new Item_field(result_field); + return copy_or_same(thd); +} void Item_subselect::update_used_tables() { @@ -240,8 +277,12 @@ void Item_singlerow_subselect::reset() Item_subselect::trans_res Item_singlerow_subselect::select_transformer(JOIN *join) { + if (changed) + return RES_OK; + SELECT_LEX *select_lex= join->select_lex; - + Statement backup; + if (!select_lex->master_unit()->first_select()->next_select() && !select_lex->table_list.elements && select_lex->item_list.elements == 1 && @@ -275,20 +316,30 @@ Item_singlerow_subselect::select_transformer(JOIN *join) if (join->conds || join->having) { Item *cond; + if (stmt) + thd->set_n_backup_item_arena(stmt, &backup); + if (!join->having) cond= join->conds; else if (!join->conds) cond= join->having; else if (!(cond= new Item_cond_and(join->conds, join->having))) - return RES_ERROR; + goto err; if (!(substitution= new Item_func_if(cond, substitution, new Item_null()))) - return RES_ERROR; + goto err; } + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); return RES_REDUCE; } return RES_OK; + +err: + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); + return RES_ERROR; } void Item_singlerow_subselect::store(uint i, Item *item) @@ -349,6 +400,7 @@ void Item_singlerow_subselect::bring_value() double Item_singlerow_subselect::val() { + DBUG_ASSERT(fixed == 1); if (!exec() && !value->null_value) { null_value= 0; @@ -363,6 +415,7 @@ double Item_singlerow_subselect::val() longlong Item_singlerow_subselect::val_int() { + DBUG_ASSERT(fixed == 1); if (!exec() && !value->null_value) { null_value= 0; @@ -471,6 +524,7 @@ void Item_exists_subselect::fix_length_and_dec() double Item_exists_subselect::val() { + DBUG_ASSERT(fixed == 1); if (exec()) { reset(); @@ -481,6 +535,7 @@ double Item_exists_subselect::val() longlong Item_exists_subselect::val_int() { + DBUG_ASSERT(fixed == 1); if (exec()) { reset(); @@ -491,6 +546,7 @@ longlong Item_exists_subselect::val_int() String *Item_exists_subselect::val_str(String *str) { + DBUG_ASSERT(fixed == 1); if (exec()) { reset(); @@ -502,6 +558,7 @@ String *Item_exists_subselect::val_str(String *str) double Item_in_subselect::val() { + DBUG_ASSERT(fixed == 1); if (exec()) { reset(); @@ -515,6 +572,7 @@ double Item_in_subselect::val() longlong Item_in_subselect::val_int() { + DBUG_ASSERT(fixed == 1); if (exec()) { reset(); @@ -528,6 +586,7 @@ longlong Item_in_subselect::val_int() String *Item_in_subselect::val_str(String *str) { + DBUG_ASSERT(fixed == 1); if (exec()) { reset(); @@ -550,15 +609,22 @@ Item_in_subselect::single_value_transformer(JOIN *join, { DBUG_ENTER("Item_in_subselect::single_value_transformer"); + if (changed) + { + DBUG_RETURN(RES_OK); + } + SELECT_LEX *select_lex= join->select_lex; + Statement backup; - THD *thd_tmp= join->thd; - thd_tmp->where= "scalar IN/ALL/ANY subquery"; + thd->where= "scalar IN/ALL/ANY subquery"; + if (stmt) + thd->set_n_backup_item_arena(stmt, &backup); if (select_lex->item_list.elements > 1) { my_error(ER_OPERAND_COLUMNS, MYF(0), 1); - DBUG_RETURN(RES_ERROR); + goto err; } if ((abort_on_null || (upper_not && upper_not->top_level())) && @@ -567,7 +633,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (substitution) { // It is second (third, ...) SELECT of UNION => All is done - DBUG_RETURN(RES_OK); + goto ok; } Item *subs; @@ -596,11 +662,9 @@ Item_in_subselect::single_value_transformer(JOIN *join, *select_lex->ref_pointer_array= item; select_lex->item_list.empty(); select_lex->item_list.push_back(item); - - if (item->fix_fields(thd_tmp, join->tables_list, &item)) - { - DBUG_RETURN(RES_ERROR); - } + + // fix_fields call for 'item' will be made during new subquery fix_fields + subs= new Item_singlerow_subselect(select_lex); } else @@ -611,16 +675,16 @@ Item_in_subselect::single_value_transformer(JOIN *join, subs= new Item_maxmin_subselect(this, select_lex, func->l_op()); } // left expression belong to outer select - SELECT_LEX *current= thd_tmp->lex->current_select, *up; - thd_tmp->lex->current_select= up= current->return_after_parsing(); - if (left_expr->fix_fields(thd_tmp, up->get_table_list(), &left_expr)) + 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)) { - thd_tmp->lex->current_select= current; - DBUG_RETURN(RES_ERROR); + thd->lex->current_select= current; + goto err; } - thd_tmp->lex->current_select= current; + thd->lex->current_select= current; substitution= func->create(left_expr, subs); - DBUG_RETURN(RES_OK); + goto ok; } if (!substitution) @@ -629,16 +693,16 @@ Item_in_subselect::single_value_transformer(JOIN *join, SELECT_LEX_UNIT *unit= select_lex->master_unit(); substitution= optimizer= new Item_in_optimizer(left_expr, this); - SELECT_LEX *current= thd_tmp->lex->current_select, *up; + SELECT_LEX *current= thd->lex->current_select, *up; - thd_tmp->lex->current_select= up= current->return_after_parsing(); + thd->lex->current_select= up= current->return_after_parsing(); //optimizer never use Item **ref => we can pass 0 as parameter - if (!optimizer || optimizer->fix_left(thd_tmp, up->get_table_list(), 0)) + if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0)) { - thd_tmp->lex->current_select= current; - DBUG_RETURN(RES_ERROR); + thd->lex->current_select= current; + goto err; } - thd_tmp->lex->current_select= current; + thd->lex->current_select= current; /* As far as Item_ref_in_optimizer do not substitude itself on fix_fields @@ -665,12 +729,17 @@ Item_in_subselect::single_value_transformer(JOIN *join, select_lex->ref_pointer_array, (char *)"<ref>", this->full_name())); - join->having= and_items(join->having, item); + /* + AND and comparison functions can't be changed during fix_fields() + we can assign select_lex->having here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->having= join->having= and_items(join->having, item); select_lex->having_fix_field= 1; - if (join->having->fix_fields(thd_tmp, join->tables_list, &join->having)) + if (join->having->fix_fields(thd, join->tables_list, 0)) { select_lex->having_fix_field= 0; - DBUG_RETURN(RES_ERROR); + goto err; } select_lex->having_fix_field= 0; } @@ -682,44 +751,61 @@ Item_in_subselect::single_value_transformer(JOIN *join, select_lex->ref_pointer_array[0]= select_lex->item_list.head(); if (select_lex->table_list.elements) { - Item *having= item, *isnull= item; + Item *having= item, *orig_item= item; item= func->create(expr, item); - if (!abort_on_null) + if (!abort_on_null && orig_item->maybe_null) { having= new Item_is_not_null_test(this, having); - join->having= (join->having ? - new Item_cond_and(having, join->having) : - having); + /* + Item_is_not_null_test can't be changed during fix_fields() + we can assign select_lex->having here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->having= + join->having= (join->having ? + new Item_cond_and(having, join->having) : + having); select_lex->having_fix_field= 1; - if (join->having->fix_fields(thd_tmp, join->tables_list, - &join->having)) + if (join->having->fix_fields(thd, join->tables_list, 0)) { select_lex->having_fix_field= 0; - DBUG_RETURN(RES_ERROR); + goto err; } select_lex->having_fix_field= 0; item= new Item_cond_or(item, - new Item_func_isnull(isnull)); + new Item_func_isnull(orig_item)); } item->name= (char *)in_additional_cond; - join->conds= and_items(join->conds, item); - if (join->conds->fix_fields(thd_tmp, join->tables_list, &join->conds)) - DBUG_RETURN(RES_ERROR); + /* + AND can't be changed during fix_fields() + we can assign select_lex->having here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->where= join->conds= and_items(join->conds, item); + if (join->conds->fix_fields(thd, join->tables_list, 0)) + goto err; } else { if (select_lex->master_unit()->first_select()->next_select()) { - join->having= func->create(expr, - new Item_null_helper(this, item, - (char *)"<no matter>", - (char *)"<result>")); + /* + comparison functions can't be changed during fix_fields() + we can assign select_lex->having here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->having= + join->having= + func->create(expr, + new Item_null_helper(this, item, + (char *)"<no matter>", + (char *)"<result>")); select_lex->having_fix_field= 1; - if (join->having->fix_fields(thd_tmp, join->tables_list, - &join->having)) + if (join->having->fix_fields(thd, join->tables_list, + 0)) { select_lex->having_fix_field= 0; - DBUG_RETURN(RES_ERROR); + goto err; } select_lex->having_fix_field= 0; } @@ -730,18 +816,29 @@ Item_in_subselect::single_value_transformer(JOIN *join, // fix_field of item will be done in time of substituting substitution= item; have_to_be_excluded= 1; - if (thd_tmp->lex->describe) + if (thd->lex->describe) { char warn_buff[MYSQL_ERRMSG_SIZE]; sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number); - push_warning(thd_tmp, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SELECT_REDUCED, warn_buff); } + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); DBUG_RETURN(RES_REDUCE); } } } + +ok: + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); DBUG_RETURN(RES_OK); + +err: + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); + DBUG_RETURN(RES_ERROR); } @@ -750,15 +847,23 @@ Item_in_subselect::row_value_transformer(JOIN *join) { DBUG_ENTER("Item_in_subselect::row_value_transformer"); - THD *thd_tmp= join->thd; - thd_tmp->where= "row IN/ALL/ANY subquery"; + if (changed) + { + DBUG_RETURN(RES_OK); + } + Statement backup; + Item *item= 0; + + thd->where= "row IN/ALL/ANY subquery"; + if (stmt) + thd->set_n_backup_item_arena(stmt, &backup); SELECT_LEX *select_lex= join->select_lex; if (select_lex->item_list.elements != left_expr->cols()) { my_error(ER_OPERAND_COLUMNS, MYF(0), left_expr->cols()); - DBUG_RETURN(RES_ERROR); + goto err; } if (!substitution) @@ -767,62 +872,82 @@ Item_in_subselect::row_value_transformer(JOIN *join) SELECT_LEX_UNIT *unit= select_lex->master_unit(); substitution= optimizer= new Item_in_optimizer(left_expr, this); - SELECT_LEX *current= thd_tmp->lex->current_select, *up; - thd_tmp->lex->current_select= up= current->return_after_parsing(); + SELECT_LEX *current= thd->lex->current_select, *up; + thd->lex->current_select= up= current->return_after_parsing(); //optimizer never use Item **ref => we can pass 0 as parameter - if (!optimizer || optimizer->fix_left(thd_tmp, up->get_table_list(), 0)) + if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0)) { - thd_tmp->lex->current_select= current; - DBUG_RETURN(RES_ERROR); + thd->lex->current_select= current; + goto err; } - thd_tmp->lex->current_select= current; + + // we will refer to apper level cache array => we have to save it in PS + optimizer->keep_top_level_cache(); + + thd->lex->current_select= current; unit->uncacheable|= UNCACHEABLE_DEPENDENT; } - uint n= left_expr->cols(); - select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; - select_lex->setup_ref_array(thd_tmp, + select_lex->setup_ref_array(thd, select_lex->order_list.elements + select_lex->group_list.elements); - Item *item= 0; - List_iterator_fast<Item> li(select_lex->item_list); - for (uint i= 0; i < n; i++) { - 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), - NULL, - (char *)"<no matter>", + uint n= left_expr->cols(); + List_iterator_fast<Item> li(select_lex->item_list); + for (uint i= 0; i < n; i++) + { + 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), + NULL, + (char *)"<no matter>", (char *)in_left_expr_name), - func); - item= and_items(item, func); + func); + item= and_items(item, func); + } } - if (join->having || select_lex->with_sum_func || select_lex->group_list.first || !select_lex->table_list.elements) { - join->having= and_items(join->having, item); + /* + AND can't be changed during fix_fields() + we can assign select_lex->having here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->having= join->having= and_items(join->having, item); select_lex->having_fix_field= 1; - if (join->having->fix_fields(thd_tmp, join->tables_list, &join->having)) + if (join->having->fix_fields(thd, join->tables_list, 0)) { select_lex->having_fix_field= 0; - DBUG_RETURN(RES_ERROR); + goto err; } select_lex->having_fix_field= 0; } else { - join->conds= and_items(join->conds, item); - if (join->conds->fix_fields(thd_tmp, join->tables_list, &join->having)) - DBUG_RETURN(RES_ERROR); + /* + AND can't be changed during fix_fields() + we can assign select_lex->having here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->where= join->conds= and_items(join->conds, item); + if (join->conds->fix_fields(thd, join->tables_list, 0)) + goto err; } + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); DBUG_RETURN(RES_OK); + +err: + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); + DBUG_RETURN(RES_ERROR); } @@ -888,13 +1013,31 @@ subselect_single_select_engine(st_select_lex *select, this->select_lex= select_lex; } + void subselect_single_select_engine::cleanup() { - prepared= 0; - optimized= 0; - executed= 0; + DBUG_ENTER("subselect_single_select_engine::cleanup"); + prepared= optimized= executed= 0; + join= 0; + DBUG_VOID_RETURN; } + +void subselect_union_engine::cleanup() +{ + DBUG_ENTER("subselect_union_engine::cleanup"); + unit->reinit_exec_mechanism(); + DBUG_VOID_RETURN; +} + + +void subselect_uniquesubquery_engine::cleanup() +{ + DBUG_ENTER("subselect_uniquesubquery_engine::cleanup"); + DBUG_VOID_RETURN; +} + + subselect_union_engine::subselect_union_engine(st_select_lex_unit *u, select_subselect *result_arg, Item_subselect *item_arg) diff --git a/sql/item_subselect.h b/sql/item_subselect.h index dc3d07540da..e68c882ba3e 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -26,6 +26,7 @@ class JOIN; class select_subselect; class subselect_engine; class Item_bool_func2; +class Statement; /* base class for subselects */ @@ -35,22 +36,30 @@ class Item_subselect :public Item_result_field protected: /* thread handler, will be assigned in fix_fields only */ THD *thd; + /* prepared statement, or 0 */ + Statement *stmt; /* substitution instead of subselect in case of optimization */ Item *substitution; + /* unit of subquery */ + st_select_lex_unit *unit; /* engine that perform execution of subselect (single select or union) */ subselect_engine *engine; + /* old engine if engine was changed */ + subselect_engine *old_engine; /* cache of used external tables */ table_map used_tables_cache; /* allowed number of columns (1 for single value subqueries) */ uint max_columns; /* work with 'substitution' */ bool have_to_be_excluded; - /* cache of constante state */ + /* cache of constant state */ bool const_item_cache; public: /* changed engine indicator */ bool engine_changed; + /* subquery is transformed */ + bool changed; enum trans_res {RES_OK, RES_REDUCE, RES_ERROR}; enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS, @@ -90,10 +99,12 @@ public: bool const_item() const; inline table_map get_used_tables_cache() { return used_tables_cache; } inline bool get_const_item_cache() { return const_item_cache; } + Item *get_tmp_table_item(THD *thd); void update_used_tables(); void print(String *str); bool change_engine(subselect_engine *eng) { + old_engine= engine; engine= eng; engine_changed= 1; return eng == 0; @@ -116,6 +127,7 @@ public: Item_singlerow_subselect(st_select_lex *select_lex); Item_singlerow_subselect() :Item_subselect(), value(0), row (0) {} + void cleanup(); subs_type substype() { return SINGLEROW_SUBS; } void reset(); @@ -200,13 +212,6 @@ public: {} - void cleanup() - { - Item_exists_subselect::cleanup(); - abort_on_null= 0; - transformed= 0; - upper_not= 0; - } subs_type substype() { return IN_SUBS; } void reset() { @@ -269,7 +274,7 @@ public: maybe_null= 0; } virtual ~subselect_engine() {}; // to satisfy compiler - virtual void cleanup() {} + virtual void cleanup()= 0; // set_thd should be called before prepare() void set_thd(THD *thd_arg) { thd= thd_arg; } @@ -318,6 +323,7 @@ public: subselect_union_engine(st_select_lex_unit *u, select_subselect *result, Item_subselect *item); + void cleanup(); int prepare(); void fix_length_and_dec(Item_cache** row); int exec(); @@ -345,6 +351,7 @@ public: set_thd(thd_arg); } ~subselect_uniquesubquery_engine(); + void cleanup(); int prepare(); void fix_length_and_dec(Item_cache** row); int exec(); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 6507e826b7e..7b2b00b18c6 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -24,8 +24,8 @@ #include "mysql_priv.h" Item_sum::Item_sum(List<Item> &list) + :args_copy(0), arg_count(list.elements) { - arg_count=list.elements; if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count))) { uint i=0; @@ -41,20 +41,57 @@ Item_sum::Item_sum(List<Item> &list) list.empty(); // Fields are used } -// Constructor used in processing select with temporary tebles + +/* + Constructor used in processing select with temporary tebles +*/ + Item_sum::Item_sum(THD *thd, Item_sum *item): - Item_result_field(thd, item), quick_group(item->quick_group) + Item_result_field(thd, item), arg_count(item->arg_count), + quick_group(item->quick_group) { - arg_count= item->arg_count; if (arg_count <= 2) args=tmp_args; else - if (!(args=(Item**) sql_alloc(sizeof(Item*)*arg_count))) + if (!(args= (Item**) thd->alloc(sizeof(Item*)*arg_count))) return; - for (uint i= 0; i < arg_count; i++) - args[i]= item->args[i]; + memcpy(args, item->args, sizeof(Item*)*arg_count); + if (item->args_copy != 0) + save_args(thd); + else + args_copy= 0; } + +/* + Save copy of arguments if we are prepare prepared statement + (arguments can be rewritten in get_tmp_table_item()) + + SYNOPSIS + Item_sum::save_args_for_prepared_statements() + thd - thread handler + + RETURN + 0 - OK + 1 - Error +*/ +bool Item_sum::save_args_for_prepared_statements(THD *thd) +{ + if (thd->current_statement) + return save_args(thd->current_statement); + return 0; +} + + +bool Item_sum::save_args(Statement* stmt) +{ + if (!(args_copy= (Item**) stmt->alloc(sizeof(Item*)*arg_count))) + return 1; + memcpy(args_copy, args, sizeof(Item*)*arg_count); + return 0; +} + + void Item_sum::mark_as_sum_func() { current_thd->lex->current_select->with_sum_func= 1; @@ -62,6 +99,17 @@ void Item_sum::mark_as_sum_func() } +void Item_sum::cleanup() +{ + DBUG_ENTER("Item_sum::cleanup"); + Item_result_field::cleanup(); + if (args_copy != 0) + memcpy(args, args_copy, sizeof(Item*)*arg_count); + result_field=0; + DBUG_VOID_RETURN; +} + + void Item_sum::make_field(Send_field *tmp_field) { if (args[0]->type() == Item::FIELD_ITEM && keep_field_type()) @@ -137,6 +185,7 @@ bool Item_sum::walk (Item_processor processor, byte *argument) String * Item_sum_num::val_str(String *str) { + DBUG_ASSERT(fixed == 1); double nr=val(); if (null_value) return 0; @@ -148,6 +197,7 @@ Item_sum_num::val_str(String *str) String * Item_sum_int::val_str(String *str) { + DBUG_ASSERT(fixed == 1); longlong nr= val_int(); if (null_value) return 0; @@ -162,6 +212,11 @@ Item_sum_int::val_str(String *str) bool Item_sum_num::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { + DBUG_ASSERT(fixed == 0); + + if (save_args_for_prepared_statements(thd)) + return 1; + if (!thd->allow_sum_func) { my_error(ER_INVALID_GROUP_FUNC_USE,MYF(0)); @@ -191,17 +246,26 @@ Item_sum_num::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) bool Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { - Item *item=args[0]; + DBUG_ASSERT(fixed == 0); + + if (save_args_for_prepared_statements(thd)) + return 1; + + Item *item= args[0]; if (!thd->allow_sum_func) { my_error(ER_INVALID_GROUP_FUNC_USE,MYF(0)); return 1; } thd->allow_sum_func=0; // No included group funcs + + // 'item' can be changed during fix_fields if (!item->fixed && - item->fix_fields(thd, tables, args) || item->check_cols(1)) + item->fix_fields(thd, tables, args) || + (item= args[0])->check_cols(1)) return 1; - hybrid_type=item->result_type(); + + hybrid_type= item->result_type(); if (hybrid_type == INT_RESULT) { cmp_charset= &my_charset_bin; @@ -261,6 +325,7 @@ bool Item_sum_sum::add() double Item_sum_sum::val() { + DBUG_ASSERT(fixed == 1); return sum; } @@ -408,6 +473,7 @@ bool Item_sum_count::add() longlong Item_sum_count::val_int() { + DBUG_ASSERT(fixed == 1); return (longlong) count; } @@ -440,6 +506,7 @@ bool Item_sum_avg::add() double Item_sum_avg::val() { + DBUG_ASSERT(fixed == 1); if (!count) { null_value=1; @@ -456,6 +523,7 @@ double Item_sum_avg::val() double Item_sum_std::val() { + DBUG_ASSERT(fixed == 1); double tmp= Item_sum_variance::val(); return tmp <= 0.0 ? 0.0 : sqrt(tmp); } @@ -496,6 +564,7 @@ bool Item_sum_variance::add() double Item_sum_variance::val() { + DBUG_ASSERT(fixed == 1); if (!count) { null_value=1; @@ -551,6 +620,7 @@ void Item_sum_variance::update_field() double Item_sum_hybrid::val() { + DBUG_ASSERT(fixed == 1); int err; if (null_value) return 0.0; @@ -576,6 +646,7 @@ double Item_sum_hybrid::val() longlong Item_sum_hybrid::val_int() { + DBUG_ASSERT(fixed == 1); if (null_value) return 0; if (hybrid_type == INT_RESULT) @@ -587,6 +658,7 @@ longlong Item_sum_hybrid::val_int() String * Item_sum_hybrid::val_str(String *str) { + DBUG_ASSERT(fixed == 1); if (null_value) return 0; switch (hybrid_type) { @@ -721,6 +793,7 @@ bool Item_sum_max::add() longlong Item_sum_bit::val_int() { + DBUG_ASSERT(fixed == 1); return (longlong) bits; } @@ -1054,6 +1127,7 @@ Item_avg_field::Item_avg_field(Item_sum_avg *item) double Item_avg_field::val() { + // fix_fields() never calls for this Item double nr; longlong count; float8get(nr,field->ptr); @@ -1071,6 +1145,7 @@ double Item_avg_field::val() String *Item_avg_field::val_str(String *str) { + // fix_fields() never calls for this Item double nr=Item_avg_field::val(); if (null_value) return 0; @@ -1085,6 +1160,7 @@ Item_std_field::Item_std_field(Item_sum_std *item) double Item_std_field::val() { + // fix_fields() never calls for this Item double tmp= Item_variance_field::val(); return tmp <= 0.0 ? 0.0 : sqrt(tmp); } @@ -1100,6 +1176,7 @@ Item_variance_field::Item_variance_field(Item_sum_variance *item) double Item_variance_field::val() { + // fix_fields() never calls for this Item double sum,sum_sqr; longlong count; float8get(sum,field->ptr); @@ -1119,6 +1196,7 @@ double Item_variance_field::val() String *Item_variance_field::val_str(String *str) { + // fix_fields() never calls for this Item double nr=val(); if (null_value) return 0; @@ -1195,6 +1273,7 @@ int dump_leaf(byte* key, uint32 count __attribute__((unused)), void Item_sum_count_distinct::cleanup() { + DBUG_ENTER("Item_sum_count_distinct::cleanup"); Item_sum_int::cleanup(); /* Free table and tree if they belong to this item (if item have not pointer @@ -1215,17 +1294,10 @@ void Item_sum_count_distinct::cleanup() use_tree= 0; } } + DBUG_VOID_RETURN; } -bool Item_sum_count_distinct::fix_fields(THD *thd, TABLE_LIST *tables, - Item **ref) -{ - if (Item_sum_num::fix_fields(thd, tables, ref)) - return 1; - return 0; -} - /* This is used by rollup to create a separate usable copy of the function */ void Item_sum_count_distinct::make_unique() @@ -1458,6 +1530,7 @@ bool Item_sum_count_distinct::add() longlong Item_sum_count_distinct::val_int() { + DBUG_ASSERT(fixed == 1); if (!table) // Empty query return LL(0); if (use_tree) @@ -1504,6 +1577,7 @@ Item *Item_sum_udf_float::copy_or_same(THD* thd) double Item_sum_udf_float::val() { + DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_sum_udf_float::val"); DBUG_PRINT("info",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); @@ -1512,6 +1586,7 @@ double Item_sum_udf_float::val() String *Item_sum_udf_float::val_str(String *str) { + DBUG_ASSERT(fixed == 1); double nr=val(); if (null_value) return 0; /* purecov: inspected */ @@ -1528,6 +1603,7 @@ Item *Item_sum_udf_int::copy_or_same(THD* thd) longlong Item_sum_udf_int::val_int() { + DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_sum_udf_int::val_int"); DBUG_PRINT("info",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); @@ -1537,6 +1613,7 @@ longlong Item_sum_udf_int::val_int() String *Item_sum_udf_int::val_str(String *str) { + DBUG_ASSERT(fixed == 1); longlong nr=val_int(); if (null_value) return 0; @@ -1564,6 +1641,7 @@ Item *Item_sum_udf_str::copy_or_same(THD* thd) String *Item_sum_udf_str::val_str(String *str) { + DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_sum_udf_str::str"); String *res=udf.val_str(str,&str_value); null_value = !res; @@ -1575,10 +1653,17 @@ String *Item_sum_udf_str::val_str(String *str) /***************************************************************************** GROUP_CONCAT function - Syntax: - GROUP_CONCAT([DISTINCT] expr,... [ORDER BY col [ASC|DESC],...] - [SEPARATOR str_const]) + + SQL SYNTAX: + GROUP_CONCAT([DISTINCT] expr,... [ORDER BY col [ASC|DESC],...] + [SEPARATOR str_const]) + concat of values from "group by" operation + + BUGS + DISTINCT and ORDER BY only works if ORDER BY uses all fields and only fields + in expression list + Blobs doesn't work with DISTINCT or ORDER BY *****************************************************************************/ /* @@ -1589,25 +1674,28 @@ String *Item_sum_udf_str::val_str(String *str) int group_concat_key_cmp_with_distinct(void* arg, byte* key1, byte* key2) { - Item_func_group_concat* item= (Item_func_group_concat*)arg; + Item_func_group_concat* grp_item= (Item_func_group_concat*)arg; + Item **field_item, **end; + char *record= (char*) grp_item->table->record[0]; - for (uint i= 0; i < item->arg_count_field; i++) + for (field_item= grp_item->args, end= field_item + grp_item->arg_count_field; + field_item < end; + field_item++) { - Item *field_item= item->args[i]; - Field *field= field_item->real_item()->get_tmp_table_field(); + /* + We have to use get_tmp_table_field() instead of + real_item()->get_tmp_table_field() because we want the field in + the temporary table, not the original field + */ + Field *field= (*field_item)->get_tmp_table_field(); if (field) { - uint offset= field->abs_offset; - - int res= field->key_cmp(key1 + offset, key2 + offset); - /* - if key1 and key2 is not equal than field->key_cmp return offset. This - function must return value 1 for this case. - */ - if (res) - return 1; + int res; + uint offset= (uint) (field->ptr - record); + if ((res= field->key_cmp(key1 + offset, key2 + offset))) + return res; } - } + } return 0; } @@ -1619,26 +1707,34 @@ int group_concat_key_cmp_with_distinct(void* arg, byte* key1, int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2) { - Item_func_group_concat* item= (Item_func_group_concat*)arg; + Item_func_group_concat* grp_item= (Item_func_group_concat*) arg; + ORDER **order_item, **end; + char *record= (char*) grp_item->table->record[0]; - for (uint i=0; i < item->arg_count_order; i++) + for (order_item= grp_item->order, end=order_item+ grp_item->arg_count_order; + order_item < end; + order_item++) { - ORDER *order_item= item->order[i]; - Item *item= *order_item->item; - Field *field= item->real_item()->get_tmp_table_field(); + Item *item= *(*order_item)->item; + /* + We have to use get_tmp_table_field() instead of + real_item()->get_tmp_table_field() because we want the field in + the temporary table, not the original field + */ + Field *field= item->get_tmp_table_field(); if (field) { - uint offset= field->abs_offset; - - bool dir= order_item->asc; - int res= field->key_cmp(key1 + offset, key2 + offset); - if (res) - return dir ? res : -res; + int res; + uint offset= (uint) (field->ptr - record); + if ((res= field->key_cmp(key1 + offset, key2 + offset))) + return (*order_item)->asc ? res : -res; } - } + } /* - We can't return 0 because tree class remove this item as double value. - */ + We can't return 0 because in that case the tree class would remove this + item as double value. This would cause problems for case-changes and + if the the returned values are not the same we do the sort on. + */ return 1; } @@ -1646,6 +1742,11 @@ int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2) /* function of sort for syntax: GROUP_CONCAT(DISTINCT expr,... ORDER BY col,... ) + + BUG: + This doesn't work in the case when the order by contains data that + is not part of the field list because tree-insert will not notice + the duplicated values when inserting things sorted by ORDER BY */ int group_concat_key_cmp_with_distinct_and_order(void* arg,byte* key1, @@ -1658,58 +1759,61 @@ int group_concat_key_cmp_with_distinct_and_order(void* arg,byte* key1, /* - create result - item is pointer to Item_func_group_concat + Append data from current leaf to item->result */ int dump_leaf_key(byte* key, uint32 count __attribute__((unused)), - Item_func_group_concat *group_concat_item) + Item_func_group_concat *item) { char buff[MAX_FIELD_WIDTH]; - String tmp((char *)&buff,sizeof(buff),default_charset_info); - String tmp2((char *)&buff,sizeof(buff),default_charset_info); - + String tmp((char*) &buff, sizeof(buff), default_charset_info); + String tmp2((char *) &buff, sizeof(buff), default_charset_info); + char *record= (char*) item->table->record[0]; + tmp.length(0); - for (uint i= 0; i < group_concat_item->arg_show_fields; i++) + for (uint i= 0; i < item->arg_count_field; i++) { - Item *show_item= group_concat_item->args[i]; + Item *show_item= item->args[i]; if (!show_item->const_item()) { - Field *f= show_item->real_item()->get_tmp_table_field(); - char *sv= f->ptr; - f->ptr= (char *)key + f->abs_offset; - String *res= f->val_str(&tmp,&tmp2); - group_concat_item->result.append(*res); - f->ptr= sv; + /* + We have to use get_tmp_table_field() instead of + real_item()->get_tmp_table_field() because we want the field in + the temporary table, not the original field + */ + Field *field= show_item->get_tmp_table_field(); + String *res; + char *save_ptr= field->ptr; + uint offset= (uint) (save_ptr - record); + DBUG_ASSERT(offset < item->table->reclength); + field->ptr= (char *) key + offset; + res= field->val_str(&tmp,&tmp2); + item->result.append(*res); + field->ptr= save_ptr; } else { String *res= show_item->val_str(&tmp); if (res) - group_concat_item->result.append(*res); + item->result.append(*res); } } - if (group_concat_item->tree_mode) // Last item of tree + if (item->tree_mode) // Last item of tree { - group_concat_item->show_elements++; - if (group_concat_item->show_elements < - group_concat_item->tree->elements_in_tree) - group_concat_item->result.append(*group_concat_item->separator); + item->show_elements++; + if (item->show_elements < item->tree->elements_in_tree) + item->result.append(*item->separator); } else + item->result.append(*item->separator); + + /* stop if length of result more than group_concat_max_len */ + if (item->result.length() > item->group_concat_max_len) { - group_concat_item->result.append(*group_concat_item->separator); - } - /* - if length of result more than group_concat_max_len - stop ! - */ - if (group_concat_item->result.length() > - group_concat_item->group_concat_max_len) - { - group_concat_item->count_cut_values++; - group_concat_item->result.length(group_concat_item->group_concat_max_len); - group_concat_item->warning_for_row= TRUE; + item->count_cut_values++; + item->result.length(item->group_concat_max_len); + item->warning_for_row= TRUE; return 1; } return 0; @@ -1729,60 +1833,97 @@ Item_func_group_concat::Item_func_group_concat(bool is_distinct, SQL_LIST *is_order, String *is_separator) :Item_sum(), tmp_table_param(0), max_elements_in_tree(0), warning(0), - warning_available(0), key_length(0), rec_offset(0), + warning_available(0), key_length(0), tree_mode(0), distinct(is_distinct), warning_for_row(0), separator(is_separator), tree(&tree_base), table(0), order(0), tables_list(0), show_elements(0), arg_count_order(0), arg_count_field(0), - arg_show_fields(0), count_cut_values(0) - + count_cut_values(0) { + Item *item_select; + Item **arg_ptr; + original= 0; quick_group= 0; mark_as_sum_func(); order= 0; group_concat_max_len= current_thd->variables.group_concat_max_len; - - arg_show_fields= arg_count_field= is_select->elements; + arg_count_field= is_select->elements; arg_count_order= is_order ? is_order->elements : 0; - arg_count= arg_count_field; + arg_count= arg_count_field + arg_count_order; /* We need to allocate: - args - arg_count+arg_count_order (for possible order items in temporare - tables) + args - arg_count_field+arg_count_order + (for possible order items in temporare tables) order - arg_count_order */ - args= (Item**) sql_alloc(sizeof(Item*)*(arg_count+arg_count_order)+ - sizeof(ORDER*)*arg_count_order); - if (!args) + if (!(args= (Item**) sql_alloc(sizeof(Item*) * arg_count + + sizeof(ORDER*)*arg_count_order))) return; + order= (ORDER**)(args + arg_count); + /* fill args items of show and sort */ - int i= 0; List_iterator_fast<Item> li(*is_select); - Item *item_select; - for ( ; (item_select= li++) ; i++) - args[i]= item_select; + for (arg_ptr=args ; (item_select= li++) ; arg_ptr++) + *arg_ptr= item_select; if (arg_count_order) { - i= 0; - order= (ORDER**)(args + arg_count + arg_count_order); + ORDER **order_ptr= order; for (ORDER *order_item= (ORDER*) is_order->first; - order_item != NULL; - order_item= order_item->next) + order_item != NULL; + order_item= order_item->next) { - order[i++]= order_item; + (*order_ptr++)= order_item; + *arg_ptr= *order_item->item; + order_item->item= arg_ptr++; } } } + + +Item_func_group_concat::Item_func_group_concat(THD *thd, + Item_func_group_concat *item) + :Item_sum(thd, item),item_thd(thd), + tmp_table_param(item->tmp_table_param), + max_elements_in_tree(item->max_elements_in_tree), + warning(item->warning), + warning_available(item->warning_available), + key_length(item->key_length), + tree_mode(item->tree_mode), + distinct(item->distinct), + warning_for_row(item->warning_for_row), + separator(item->separator), + tree(item->tree), + table(item->table), + order(item->order), + tables_list(item->tables_list), + group_concat_max_len(item->group_concat_max_len), + show_elements(item->show_elements), + arg_count_order(item->arg_count_order), + arg_count_field(item->arg_count_field), + field_list_offset(item->field_list_offset), + count_cut_values(item->count_cut_values), + original(item) +{ + quick_group= item->quick_group; +} + void Item_func_group_concat::cleanup() { + DBUG_ENTER("Item_func_group_concat::cleanup"); + Item_sum::cleanup(); + + /* fix order list */ + for (uint i= 0; i < arg_count_order ; i++) + order[i]->item= &order[i]->item_ptr; + /* Free table and tree if they belong to this item (if item have not pointer to original item from which was made copy => it own its objects ) @@ -1803,6 +1944,7 @@ void Item_func_group_concat::cleanup() delete_tree(tree); } } + DBUG_VOID_RETURN; } @@ -1814,12 +1956,11 @@ Item_func_group_concat::~Item_func_group_concat() */ if (!original) { - THD *thd= current_thd; if (warning_available) { char warn_buff[MYSQL_ERRMSG_SIZE]; sprintf(warn_buff, ER(ER_CUT_VALUE_GROUP_CONCAT), count_cut_values); - warning->set_msg(thd, warn_buff); + warning->set_msg(current_thd, warn_buff); } } } @@ -1836,7 +1977,7 @@ void Item_func_group_concat::clear() result.length(0); result.copy(); null_value= TRUE; - warning_for_row= false; + warning_for_row= FALSE; if (table) { table->file->extra(HA_EXTRA_NO_CACHE); @@ -1855,33 +1996,31 @@ bool Item_func_group_concat::add() copy_fields(tmp_table_param); copy_funcs(tmp_table_param->items_to_copy); - bool record_is_null= TRUE; - for (uint i= 0; i < arg_show_fields; i++) + for (uint i= 0; i < arg_count_field; i++) { Item *show_item= args[i]; if (!show_item->const_item()) { + /* + Here we use real_item as we want the original field data that should + be written to table->record[0] + */ Field *f= show_item->real_item()->get_tmp_table_field(); - if (!f->is_null()) - { - record_is_null= FALSE; - break; - } + if (f->is_null()) + return 0; // Skip row if it contains null } } - if (record_is_null) - return 0; + null_value= FALSE; if (tree_mode) { - if (!tree_insert(tree, table->record[0] + rec_offset, 0, tree->custom_arg)) + if (!tree_insert(tree, table->record[0], 0, tree->custom_arg)) return 1; } else { if (result.length() <= group_concat_max_len && !warning_for_row) - dump_leaf_key(table->record[0] + rec_offset, 1, - (Item_func_group_concat*)this); + dump_leaf_key(table->record[0], 1, this); } return 0; } @@ -1897,6 +2036,11 @@ void Item_func_group_concat::reset_field() bool Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { + DBUG_ASSERT(fixed == 0); + + if (save_args_for_prepared_statements(thd)) + return 1; + uint i; /* for loop variable */ if (!thd->allow_sum_func) @@ -1908,24 +2052,19 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) thd->allow_sum_func= 0; maybe_null= 0; item_thd= thd; - for (i= 0 ; i < arg_count ; i++) - { - if (args[i]->fix_fields(thd, tables, args + i) || args[i]->check_cols(1)) - return 1; - maybe_null |= args[i]->maybe_null; - } + /* - Fix fields for order clause in function: - GROUP_CONCAT(expr,... ORDER BY col,... ) + Fix fields for select list and ORDER clause */ - for (i= 0 ; i < arg_count_order ; i++) + + for (i= 0 ; i < arg_count ; i++) { - // order_item->item can be changed by fix_fields() call - ORDER *order_item= order[i]; - if ((*order_item->item)->fix_fields(thd, tables, order_item->item) || - (*order_item->item)->check_cols(1)) + if (args[i]->fix_fields(thd, tables, args + i) || args[i]->check_cols(1)) return 1; + if (i < arg_count_field && args[i]->maybe_null) + maybe_null= 0; } + result_field= 0; null_value= 1; max_length= group_concat_max_len; @@ -1940,23 +2079,29 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) bool Item_func_group_concat::setup(THD *thd) { - DBUG_ENTER("Item_func_group_concat::setup"); List<Item> list; SELECT_LEX *select_lex= thd->lex->current_select; + uint const_fields; + byte *record; + qsort_cmp2 compare_key; + DBUG_ENTER("Item_func_group_concat::setup"); if (select_lex->linkage == GLOBAL_OPTIONS_TYPE) DBUG_RETURN(1); + /* push all not constant fields to list and create temp table */ + const_fields= 0; always_null= 0; - for (uint i= 0; i < arg_count; i++) + for (uint i= 0; i < arg_count_field; i++) { Item *item= args[i]; if (list.push_back(item)) DBUG_RETURN(1); if (item->const_item()) { + const_fields++; (void) item->val_int(); if (item->null_value) always_null= 1; @@ -1976,12 +2121,19 @@ bool Item_func_group_concat::setup(THD *thd) count_field_types(tmp_table_param,all_fields,0); if (table) { + /* + We come here when we are getting the result from a temporary table, + not the original tables used in the query + */ free_tmp_table(thd, table); tmp_table_param->cleanup(); } /* - We have to create a temporary table for that we get descriptions of fields + We have to create a temporary table to get descriptions of fields (types, sizes and so on). + + Note that in the table, we first have the ORDER BY fields, then the + field list. */ if (!(table=create_tmp_table(thd, tmp_table_param, all_fields, 0, 0, 0, 0,select_lex->options | thd->options, @@ -1990,27 +2142,17 @@ bool Item_func_group_concat::setup(THD *thd) table->file->extra(HA_EXTRA_NO_ROWS); table->no_rows= 1; + key_length= table->reclength; + record= table->record[0]; - Field** field, **field_end; - field_end = (field = table->field) + table->fields; - uint offset = 0; - for (key_length = 0; field < field_end; ++field) - { - uint32 length= (*field)->pack_length(); - (*field)->abs_offset= offset; - offset+= length; - key_length += length; - } - rec_offset = table->reclength - key_length; - + /* Offset to first result field in table */ + field_list_offset= table->fields - (list.elements - const_fields); if (tree_mode) delete_tree(tree); - /* - choise function of sort - */ + + /* choose function of sort */ tree_mode= distinct || arg_count_order; - qsort_cmp2 compare_key; if (tree_mode) { if (arg_count_order) @@ -2022,21 +2164,20 @@ bool Item_func_group_concat::setup(THD *thd) } else { + compare_key= NULL; if (distinct) compare_key= (qsort_cmp2) group_concat_key_cmp_with_distinct; - else - compare_key= NULL; } /* - Create a tree of sort. Tree is used for a sort and a remove dubl - values (according with syntax of the function). If function does't + Create a tree of sort. Tree is used for a sort and a remove double + values (according with syntax of the function). If function doesn't contain DISTINCT and ORDER BY clauses, we don't create this tree. */ init_tree(tree, min(thd->variables.max_heap_table_size, - thd->variables.sortbuff_size/16), 0, + thd->variables.sortbuff_size/16), 0, key_length, compare_key, 0, NULL, (void*) this); - max_elements_in_tree= ((key_length) ? - thd->variables.max_heap_table_size/key_length : 1); + max_elements_in_tree= (key_length ? + thd->variables.max_heap_table_size/key_length : 1); }; /* @@ -2051,6 +2192,7 @@ bool Item_func_group_concat::setup(THD *thd) DBUG_RETURN(0); } + /* This is used by rollup to create a separate usable copy of the function */ void Item_func_group_concat::make_unique() @@ -2064,6 +2206,7 @@ void Item_func_group_concat::make_unique() String* Item_func_group_concat::val_str(String* str) { + DBUG_ASSERT(fixed == 1); if (null_value) return 0; if (tree_mode) @@ -2092,7 +2235,7 @@ void Item_func_group_concat::print(String *str) str->append("group_concat(", 13); if (distinct) str->append("distinct ", 9); - for (uint i= 0; i < arg_count; i++) + for (uint i= 0; i < arg_count_field; i++) { if (i) str->append(','); diff --git a/sql/item_sum.h b/sql/item_sum.h index 1957f652afe..170a142545e 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -32,38 +32,32 @@ public: SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC }; - Item **args,*tmp_args[2]; + Item **args, *tmp_args[2]; + Item **args_copy; /* copy of arguments for PS */ uint arg_count; bool quick_group; /* If incremental update of fields */ void mark_as_sum_func(); - Item_sum() : arg_count(0),quick_group(1) + Item_sum() :args_copy(0), arg_count(0), quick_group(1) { mark_as_sum_func(); } - Item_sum(Item *a) :quick_group(1) + Item_sum(Item *a) + :args(tmp_args), args_copy(0), arg_count(1), quick_group(1) { - arg_count=1; - args=tmp_args; args[0]=a; mark_as_sum_func(); } - Item_sum( Item *a, Item *b ) :quick_group(1) + Item_sum( Item *a, Item *b ) + :args(tmp_args), args_copy(0), arg_count(2), quick_group(1) { - arg_count=2; - args=tmp_args; args[0]=a; args[1]=b; mark_as_sum_func(); } Item_sum(List<Item> &list); //Copy constructor, need to perform subselects with temporary tables Item_sum(THD *thd, Item_sum *item); - void cleanup() - { - Item_result_field::cleanup(); - result_field=0; - } - + void cleanup(); enum Type type() const { return SUM_FUNC_ITEM; } virtual enum Sumfunctype sum_func () const=0; inline bool reset() { clear(); return add(); }; @@ -86,7 +80,7 @@ public: virtual void fix_length_and_dec() { maybe_null=1; null_value=1; } virtual const char *func_name() const { return "?"; } virtual Item *result_item(Field *field) - { return new Item_field(field);} + { return new Item_field(field);} table_map used_tables() const { return ~(table_map) 0; } /* Not used */ bool const_item() const { return 0; } bool is_null() { return null_value; } @@ -98,6 +92,8 @@ public: virtual bool setup(THD *thd) {return 0;} virtual void make_unique() {} Item *get_tmp_table_item(THD *thd); + bool save_args_for_prepared_statements(THD *); + bool save_args(Statement* stmt); bool walk (Item_processor processor, byte *argument); }; @@ -112,7 +108,8 @@ public: Item_sum_num(List<Item> &list) :Item_sum(list) {} Item_sum_num(THD *thd, Item_sum_num *item) :Item_sum(thd, item) {} bool fix_fields(THD *, TABLE_LIST *, Item **); - longlong val_int() { return (longlong) val(); } /* Real as default */ + longlong val_int() + { DBUG_ASSERT(fixed == 1); return (longlong) val(); } /* Real as default */ String *val_str(String*str); void reset_field(); }; @@ -124,7 +121,7 @@ public: Item_sum_int(Item *item_par) :Item_sum_num(item_par) {} Item_sum_int(List<Item> &list) :Item_sum_num(list) {} Item_sum_int(THD *thd, Item_sum_int *item) :Item_sum_num(thd, item) {} - double val() { return (double) val_int(); } + double val() { DBUG_ASSERT(fixed == 1); return (double) val_int(); } String *val_str(String*str); enum Item_result result_type () const { return INT_RESULT; } void fix_length_and_dec() @@ -220,7 +217,6 @@ class Item_sum_count_distinct :public Item_sum_int { TABLE *table; table_map used_table_cache; - bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref); uint32 *field_lengths; TMP_TABLE_PARAM *tmp_table_param; TREE tree_base; @@ -234,18 +230,24 @@ class Item_sum_count_distinct :public Item_sum_int uint key_length; CHARSET_INFO *key_charset; - // calculated based on max_heap_table_size. If reached, - // walk the tree and dump it into MyISAM table + /* + Calculated based on max_heap_table_size. If reached, + walk the tree and dump it into MyISAM table + */ uint max_elements_in_tree; - // the first few bytes of record ( at least one) - // are just markers for deleted and NULLs. We want to skip them since - // they will just bloat the tree without providing any valuable info + /* + The first few bytes of record ( at least one) + are just markers for deleted and NULLs. We want to skip them since + they will just bloat the tree without providing any valuable info + */ int rec_offset; - // If there are no blobs, we can use a tree, which - // is faster than heap table. In that case, we still use the table - // to help get things set up, but we insert nothing in it + /* + If there are no blobs, we can use a tree, which + is faster than heap table. In that case, we still use the table + to help get things set up, but we insert nothing in it + */ bool use_tree; bool always_null; // Set to 1 if the result is always NULL @@ -266,7 +268,8 @@ class Item_sum_count_distinct :public Item_sum_int Item_sum_count_distinct(THD *thd, Item_sum_count_distinct *item) :Item_sum_int(thd, item), table(item->table), used_table_cache(item->used_table_cache), - field_lengths(item->field_lengths), tmp_table_param(item->tmp_table_param), + field_lengths(item->field_lengths), + tmp_table_param(item->tmp_table_param), tree(item->tree), original(item), key_length(item->key_length), max_elements_in_tree(item->max_elements_in_tree), rec_offset(item->rec_offset), use_tree(item->use_tree), @@ -301,7 +304,7 @@ public: Item_avg_field(Item_sum_avg *item); enum Type type() const { return FIELD_AVG_ITEM; } double val(); - longlong val_int() { return (longlong) val(); } + longlong val_int() { /* can't be fix_fields()ed */ return (longlong) val(); } bool is_null() { (void) val_int(); return null_value; } String *val_str(String*); enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } @@ -317,7 +320,7 @@ class Item_sum_avg :public Item_sum_num ulonglong count; public: - Item_sum_avg(Item *item_par) :Item_sum_num(item_par),count(0) {} + Item_sum_avg(Item *item_par) :Item_sum_num(item_par), sum(0.0), count(0) {} Item_sum_avg(THD *thd, Item_sum_avg *item) :Item_sum_num(thd, item), sum(item->sum), count(item->count) {} enum Sumfunctype sum_func () const {return AVG_FUNC;} @@ -341,25 +344,24 @@ public: Item_variance_field(Item_sum_variance *item); enum Type type() const {return FIELD_VARIANCE_ITEM; } double val(); - longlong val_int() { return (longlong) val(); } + longlong val_int() { /* can't be fix_fields()ed */ return (longlong) val(); } String *val_str(String*); bool is_null() { (void) val_int(); return null_value; } enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } void fix_length_and_dec() {} }; -/* - -variance(a) = - -= sum (ai - avg(a))^2 / count(a) ) -= sum (ai^2 - 2*ai*avg(a) + avg(a)^2) / count(a) -= (sum(ai^2) - sum(2*ai*avg(a)) + sum(avg(a)^2))/count(a) = -= (sum(ai^2) - 2*avg(a)*sum(a) + count(a)*avg(a)^2)/count(a) = -= (sum(ai^2) - 2*sum(a)*sum(a)/count(a) + count(a)*sum(a)^2/count(a)^2 )/count(a) = -= (sum(ai^2) - 2*sum(a)^2/count(a) + sum(a)^2/count(a) )/count(a) = -= (sum(ai^2) - sum(a)^2/count(a))/count(a) +/* + variance(a) = + + = sum (ai - avg(a))^2 / count(a) ) + = sum (ai^2 - 2*ai*avg(a) + avg(a)^2) / count(a) + = (sum(ai^2) - sum(2*ai*avg(a)) + sum(avg(a)^2))/count(a) = + = (sum(ai^2) - 2*avg(a)*sum(a) + count(a)*avg(a)^2)/count(a) = + = (sum(ai^2) - 2*sum(a)*sum(a)/count(a) + count(a)*sum(a)^2/count(a)^2 )/count(a) = + = (sum(ai^2) - 2*sum(a)^2/count(a) + sum(a)^2/count(a) )/count(a) = + = (sum(ai^2) - sum(a)^2/count(a))/count(a) */ class Item_sum_variance : public Item_sum_num @@ -545,8 +547,9 @@ class Item_sum_xor :public Item_sum_bit /* -** user defined aggregates + User defined aggregates */ + #ifdef HAVE_DLOPEN class Item_udf_sum : public Item_sum @@ -564,6 +567,7 @@ public: const char *func_name() const { return udf.name(); } bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { + DBUG_ASSERT(fixed == 0); fixed= 1; return udf.fix_fields(thd,tables,this,this->arg_count,this->args); } @@ -585,7 +589,8 @@ class Item_sum_udf_float :public Item_udf_sum :Item_udf_sum(udf_arg,list) {} Item_sum_udf_float(THD *thd, Item_sum_udf_float *item) :Item_udf_sum(thd, item) {} - longlong val_int() { return (longlong) Item_sum_udf_float::val(); } + longlong val_int() + { DBUG_ASSERT(fixed == 1); return (longlong) Item_sum_udf_float::val(); } double val(); String *val_str(String*str); void fix_length_and_dec() { fix_num_length_and_dec(); } @@ -602,7 +607,8 @@ public: Item_sum_udf_int(THD *thd, Item_sum_udf_int *item) :Item_udf_sum(thd, item) {} longlong val_int(); - double val() { return (double) Item_sum_udf_int::val_int(); } + double val() + { DBUG_ASSERT(fixed == 1); return (double) Item_sum_udf_int::val_int(); } String *val_str(String*str); enum Item_result result_type () const { return INT_RESULT; } void fix_length_and_dec() { decimals=0; max_length=21; } @@ -647,7 +653,7 @@ class Item_sum_udf_float :public Item_sum_num Item_sum_udf_float(THD *thd, Item_sum_udf_float *item) :Item_sum_num(thd, item) {} enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } - double val() { return 0.0; } + double val() { DBUG_ASSERT(fixed == 1); return 0.0; } void clear() {} bool add() { return 0; } void update_field() {} @@ -662,8 +668,8 @@ public: Item_sum_udf_int(THD *thd, Item_sum_udf_int *item) :Item_sum_num(thd, item) {} enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } - longlong val_int() { return 0; } - double val() { return 0; } + longlong val_int() { DBUG_ASSERT(fixed == 1); return 0; } + double val() { DBUG_ASSERT(fixed == 1); return 0; } void clear() {} bool add() { return 0; } void update_field() {} @@ -677,9 +683,10 @@ public: Item_sum_udf_str(udf_func *udf_arg, List<Item> &list) :Item_sum_num() {} Item_sum_udf_str(THD *thd, Item_sum_udf_str *item) :Item_sum_num(thd, item) {} - String *val_str(String *) { null_value=1; return 0; } - double val() { null_value=1; return 0.0; } - longlong val_int() { null_value=1; return 0; } + String *val_str(String *) + { DBUG_ASSERT(fixed == 1); null_value=1; return 0; } + double val() { DBUG_ASSERT(fixed == 1); null_value=1; return 0.0; } + longlong val_int() { DBUG_ASSERT(fixed == 1); null_value=1; return 0; } enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec() { maybe_null=1; max_length=0; } enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } @@ -700,7 +707,6 @@ class Item_func_group_concat : public Item_sum MYSQL_ERROR *warning; bool warning_available; uint key_length; - int rec_offset; bool tree_mode; bool distinct; bool warning_for_row; @@ -708,12 +714,13 @@ class Item_func_group_concat : public Item_sum friend int group_concat_key_cmp_with_distinct(void* arg, byte* key1, byte* key2); - friend int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2); + friend int group_concat_key_cmp_with_order(void* arg, byte* key1, + byte* key2); friend int group_concat_key_cmp_with_distinct_and_order(void* arg, byte* key1, byte* key2); friend int dump_leaf_key(byte* key, uint32 count __attribute__((unused)), - Item_func_group_concat *group_concat_item); + Item_func_group_concat *group_concat_item); public: String result; @@ -727,7 +734,7 @@ class Item_func_group_concat : public Item_sum uint show_elements; uint arg_count_order; uint arg_count_field; - uint arg_show_fields; + uint field_list_offset; uint count_cut_values; /* Following is 0 normal object and pointer to original one for copy @@ -738,38 +745,12 @@ class Item_func_group_concat : public Item_sum Item_func_group_concat(bool is_distinct,List<Item> *is_select, SQL_LIST *is_order,String *is_separator); - Item_func_group_concat(THD *thd, Item_func_group_concat *item) - :Item_sum(thd, item),item_thd(thd), - tmp_table_param(item->tmp_table_param), - max_elements_in_tree(item->max_elements_in_tree), - warning(item->warning), - warning_available(item->warning_available), - key_length(item->key_length), - rec_offset(item->rec_offset), - tree_mode(item->tree_mode), - distinct(item->distinct), - warning_for_row(item->warning_for_row), - separator(item->separator), - tree(item->tree), - table(item->table), - order(item->order), - tables_list(item->tables_list), - group_concat_max_len(item->group_concat_max_len), - show_elements(item->show_elements), - arg_count_order(item->arg_count_order), - arg_count_field(item->arg_count_field), - arg_show_fields(item->arg_show_fields), - count_cut_values(item->count_cut_values), - original(item) - { - quick_group= item->quick_group; - }; + Item_func_group_concat(THD *thd, Item_func_group_concat *item); ~Item_func_group_concat(); void cleanup(); enum Sumfunctype sum_func () const {return GROUP_CONCAT_FUNC;} const char *func_name() const { return "group_concat"; } - enum Type type() const { return SUM_FUNC_ITEM; } virtual Item_result result_type () const { return STRING_RESULT; } void clear(); bool add(); @@ -781,7 +762,7 @@ class Item_func_group_concat : public Item_sum double val() { String *res; res=val_str(&str_value); - return res ? atof(res->c_ptr()) : 0.0; + return res ? my_atof(res->c_ptr()) : 0.0; } longlong val_int() { diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index e49f2287f4b..ffa00832778 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -48,11 +48,6 @@ TYPELIB day_names_typelib= { array_elements(day_names)-1,"", day_names}; -enum date_time_format_types -{ - TIME_ONLY= 0, TIME_MICROSECOND, DATE_ONLY, DATE_TIME, DATE_TIME_MICROSECOND -}; - /* OPTIMIZATION TODO: - Replace the switch with a function that should be called for each @@ -128,6 +123,9 @@ static bool make_datetime(date_time_format_types format, TIME *ltime, val String to decode length Length of string l_time Store result here + cached_timestamp_type + It uses to get an appropriate warning + in the case when the value is truncated. RETURN 0 ok @@ -135,7 +133,8 @@ static bool make_datetime(date_time_format_types format, TIME *ltime, */ static bool extract_date_time(DATE_TIME_FORMAT *format, - const char *val, uint length, TIME *l_time) + const char *val, uint length, TIME *l_time, + timestamp_type cached_timestamp_type) { int weekday= 0, yearday= 0, daypart= 0; int week_number= -1; @@ -143,9 +142,11 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, int error= 0; bool usa_time= 0; bool sunday_first= 0; + 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; + const char *end= ptr + format->format.length; DBUG_ENTER("extract_date_time"); bzero((char*) l_time, sizeof(*l_time)); @@ -235,7 +236,12 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, /* Second part */ case 'f': tmp= (char*) val_end; + if (tmp - val > 6) + tmp= (char*) val + 6; l_time->second_part= (int) my_strtoll10(val, &tmp, &error); + frac_part= 6 - (tmp - val); + if (frac_part > 0) + l_time->second_part*= (ulong) log_10_int[frac_part]; val= tmp; break; @@ -251,6 +257,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, (const uchar *) val, 2, (const uchar *) "AM", 2)) goto err; + val+= 2; break; /* Exotic things */ @@ -281,6 +288,18 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, val= tmp; break; + case '.': + while (my_ispunct(cs, *val) && val != val_end) + val++; + break; + case '@': + while (my_isalpha(cs, *val) && val != val_end) + val++; + break; + case '#': + while (my_isdigit(cs, *val) && val != val_end) + val++; + break; default: goto err; } @@ -348,6 +367,18 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, l_time->minute > 59 || l_time->second > 59) goto err; + if (val != val_end) + { + do + { + if (!my_isspace(&my_charset_latin1,*val)) + { + make_truncated_value_warning(current_thd, val_begin, length, + cached_timestamp_type); + break; + } + } while (++val != val_end); + } DBUG_RETURN(0); err: @@ -584,16 +615,27 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, /* -** Get a array of positive numbers from a string object. -** Each number is separated by 1 non digit character -** Return error if there is too many numbers. -** If there is too few numbers, assume that the numbers are left out -** from the high end. This allows one to give: -** DAY_TO_SECOND as "D MM:HH:SS", "MM:HH:SS" "HH:SS" or as seconds. + Get a array of positive numbers from a string object. + Each number is separated by 1 non digit character + Return error if there is too many numbers. + If there is too few numbers, assume that the numbers are left out + from the high end. This allows one to give: + DAY_TO_SECOND as "D MM:HH:SS", "MM:HH:SS" "HH:SS" or as seconds. + + SYNOPSIS + str: string value + length: length of str + cs: charset of str + values: array of results + count: count of elements in result array + transform_msec: if value is true we suppose + that the last part of string value is microseconds + and we should transform value to six digit value. + For example, '1.1' -> '1.100000' */ bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs, - uint count, long *values) + uint count, long *values, bool transform_msec) { const char *end=str+length; uint i; @@ -603,8 +645,15 @@ bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs, for (i=0 ; i < count ; i++) { long value; + const char *start= str; for (value=0; str != end && my_isdigit(cs,*str) ; str++) value=value*10L + (long) (*str - '0'); + if (transform_msec && i == count - 1) // microseconds always last + { + long msec_length= 6 - (str - start); + if (msec_length > 0) + value*= (long) log_10_int[msec_length]; + } values[i]= value; while (str != end && !my_isdigit(cs,*str)) str++; @@ -623,6 +672,7 @@ bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs, longlong Item_func_period_add::val_int() { + DBUG_ASSERT(fixed == 1); ulong period=(ulong) args[0]->val_int(); int months=(int) args[1]->val_int(); @@ -637,6 +687,7 @@ longlong Item_func_period_add::val_int() longlong Item_func_period_diff::val_int() { + DBUG_ASSERT(fixed == 1); ulong period1=(ulong) args[0]->val_int(); ulong period2=(ulong) args[1]->val_int(); @@ -650,6 +701,7 @@ longlong Item_func_period_diff::val_int() longlong Item_func_to_days::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; if (get_arg0_date(<ime,0)) return 0; @@ -658,6 +710,7 @@ longlong Item_func_to_days::val_int() longlong Item_func_dayofyear::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; if (get_arg0_date(<ime,0)) return 0; @@ -667,6 +720,7 @@ longlong Item_func_dayofyear::val_int() longlong Item_func_dayofmonth::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; (void) get_arg0_date(<ime,1); return (longlong) ltime.day; @@ -674,6 +728,7 @@ longlong Item_func_dayofmonth::val_int() longlong Item_func_month::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; (void) get_arg0_date(<ime,1); return (longlong) ltime.month; @@ -682,6 +737,7 @@ longlong Item_func_month::val_int() String* Item_func_monthname::val_str(String* str) { + DBUG_ASSERT(fixed == 1); const char *month_name; uint month=(uint) Item_func_month::val_int(); @@ -701,6 +757,7 @@ String* Item_func_monthname::val_str(String* str) longlong Item_func_quarter::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; (void) get_arg0_date(<ime,1); return (longlong) ((ltime.month+2)/3); @@ -708,6 +765,7 @@ longlong Item_func_quarter::val_int() longlong Item_func_hour::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; (void) get_arg0_time(<ime); return ltime.hour; @@ -715,6 +773,7 @@ longlong Item_func_hour::val_int() longlong Item_func_minute::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; (void) get_arg0_time(<ime); return ltime.minute; @@ -723,6 +782,7 @@ longlong Item_func_minute::val_int() longlong Item_func_second::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; (void) get_arg0_time(<ime); return ltime.second; @@ -768,6 +828,7 @@ uint week_mode(uint mode) longlong Item_func_week::val_int() { + DBUG_ASSERT(fixed == 1); uint year; TIME ltime; if (get_arg0_date(<ime,0)) @@ -780,6 +841,7 @@ longlong Item_func_week::val_int() longlong Item_func_yearweek::val_int() { + DBUG_ASSERT(fixed == 1); uint year,week; TIME ltime; if (get_arg0_date(<ime,0)) @@ -795,6 +857,7 @@ longlong Item_func_yearweek::val_int() longlong Item_func_weekday::val_int() { + DBUG_ASSERT(fixed == 1); ulong tmp_value=(ulong) args[0]->val_int(); if ((null_value=(args[0]->null_value || !tmp_value))) return 0; /* purecov: inspected */ @@ -805,6 +868,7 @@ longlong Item_func_weekday::val_int() String* Item_func_dayname::val_str(String* str) { + DBUG_ASSERT(fixed == 1); uint weekday=(uint) val_int(); // Always Item_func_daynr() const char *name; @@ -819,6 +883,7 @@ String* Item_func_dayname::val_str(String* str) longlong Item_func_year::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; (void) get_arg0_date(<ime,1); return (longlong) ltime.year; @@ -827,6 +892,7 @@ longlong Item_func_year::val_int() longlong Item_func_unix_timestamp::val_int() { + DBUG_ASSERT(fixed == 1); if (arg_count == 0) return (longlong) current_thd->query_start(); if (args[0]->type() == FIELD_ITEM) @@ -846,6 +912,7 @@ longlong Item_func_unix_timestamp::val_int() longlong Item_func_time_to_sec::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; longlong seconds; (void) get_arg0_time(<ime); @@ -931,19 +998,19 @@ static bool get_interval_value(Item *args,interval_type int_type, interval->second=value; break; case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM - if (get_interval_info(str,length,cs,2,array)) + if (get_interval_info(str,length,cs,2,array,0)) return (1); interval->year=array[0]; interval->month=array[1]; break; case INTERVAL_DAY_HOUR: - if (get_interval_info(str,length,cs,2,array)) + if (get_interval_info(str,length,cs,2,array,0)) return (1); interval->day=array[0]; interval->hour=array[1]; break; case INTERVAL_DAY_MICROSECOND: - if (get_interval_info(str,length,cs,5,array)) + if (get_interval_info(str,length,cs,5,array,1)) return (1); interval->day=array[0]; interval->hour=array[1]; @@ -952,14 +1019,14 @@ static bool get_interval_value(Item *args,interval_type int_type, interval->second_part=array[4]; break; case INTERVAL_DAY_MINUTE: - if (get_interval_info(str,length,cs,3,array)) + if (get_interval_info(str,length,cs,3,array,0)) return (1); interval->day=array[0]; interval->hour=array[1]; interval->minute=array[2]; break; case INTERVAL_DAY_SECOND: - if (get_interval_info(str,length,cs,4,array)) + if (get_interval_info(str,length,cs,4,array,0)) return (1); interval->day=array[0]; interval->hour=array[1]; @@ -967,7 +1034,7 @@ static bool get_interval_value(Item *args,interval_type int_type, interval->second=array[3]; break; case INTERVAL_HOUR_MICROSECOND: - if (get_interval_info(str,length,cs,4,array)) + if (get_interval_info(str,length,cs,4,array,1)) return (1); interval->hour=array[0]; interval->minute=array[1]; @@ -975,33 +1042,33 @@ static bool get_interval_value(Item *args,interval_type int_type, interval->second_part=array[3]; break; case INTERVAL_HOUR_MINUTE: - if (get_interval_info(str,length,cs,2,array)) + if (get_interval_info(str,length,cs,2,array,0)) return (1); interval->hour=array[0]; interval->minute=array[1]; break; case INTERVAL_HOUR_SECOND: - if (get_interval_info(str,length,cs,3,array)) + if (get_interval_info(str,length,cs,3,array,0)) return (1); interval->hour=array[0]; interval->minute=array[1]; interval->second=array[2]; break; case INTERVAL_MINUTE_MICROSECOND: - if (get_interval_info(str,length,cs,3,array)) + if (get_interval_info(str,length,cs,3,array,1)) return (1); interval->minute=array[0]; interval->second=array[1]; interval->second_part=array[2]; break; case INTERVAL_MINUTE_SECOND: - if (get_interval_info(str,length,cs,2,array)) + if (get_interval_info(str,length,cs,2,array,0)) return (1); interval->minute=array[0]; interval->second=array[1]; break; case INTERVAL_SECOND_MICROSECOND: - if (get_interval_info(str,length,cs,2,array)) + if (get_interval_info(str,length,cs,2,array,1)) return (1); interval->second=array[0]; interval->second_part=array[1]; @@ -1013,23 +1080,15 @@ static bool get_interval_value(Item *args,interval_type int_type, String *Item_date::val_str(String *str) { + DBUG_ASSERT(fixed == 1); TIME ltime; - ulong value=(ulong) val_int(); - if (null_value) - return (String*) 0; - + if (get_date(<ime, TIME_FUZZY_DATE)) + return (String *) 0; if (str->alloc(11)) { null_value= 1; return (String *) 0; } - - ltime.year= (value/10000L) % 10000; - ltime.month= (value/100)%100; - ltime.day= (value%100); - ltime.neg= 0; - ltime.time_type=TIMESTAMP_DATE; - make_date((DATE_TIME_FORMAT *) 0, <ime, str); return str; } @@ -1038,28 +1097,33 @@ String *Item_date::val_str(String *str) int Item_date::save_in_field(Field *field, bool no_conversions) { TIME ltime; - timestamp_type t_type=TIMESTAMP_DATETIME; if (get_date(<ime, TIME_FUZZY_DATE)) - { - if (null_value) - return set_field_to_null(field); - t_type=TIMESTAMP_NONE; // Error - } + return set_field_to_null(field); field->set_notnull(); - field->store_time(<ime,t_type); + field->store_time(<ime, TIMESTAMP_DATE); return 0; } -longlong Item_func_from_days::val_int() +longlong Item_date::val_int() +{ + DBUG_ASSERT(fixed == 1); + TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) + return 0; + return (longlong) (ltime.year*10000L+ltime.month*100+ltime.day); +} + + +bool Item_func_from_days::get_date(TIME *ltime, uint fuzzy_date) { longlong value=args[0]->val_int(); if ((null_value=args[0]->null_value)) - return 0; /* purecov: inspected */ - - uint year,month,day; - get_date_from_daynr((long) value,&year,&month,&day); - return (longlong) (year*10000L+month*100+day); + return 1; + bzero(ltime, sizeof(TIME)); + get_date_from_daynr((long) value, <ime->year, <ime->month, <ime->day); + ltime->time_type= TIMESTAMP_DATE; + return 0; } @@ -1088,6 +1152,17 @@ void Item_func_curdate::fix_length_and_dec() ltime.time_type=TIMESTAMP_DATE; } +String *Item_func_curdate::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + if (str->alloc(11)) + { + null_value= 1; + return (String *) 0; + } + make_date((DATE_TIME_FORMAT *) 0, <ime, str); + return str; +} bool Item_func_curdate::get_date(TIME *res, uint fuzzy_date __attribute__((unused))) @@ -1118,7 +1193,8 @@ void Item_func_curdate_utc::store_now_in_tm(time_t now, struct tm *now_tm) String *Item_func_curtime::val_str(String *str) -{ +{ + DBUG_ASSERT(fixed == 1); str_value.set(buff, buff_length, &my_charset_bin); return &str_value; } @@ -1168,6 +1244,7 @@ void Item_func_curtime_utc::store_now_in_tm(time_t now, struct tm *now_tm) String *Item_func_now::val_str(String *str) { + DBUG_ASSERT(fixed == 1); str_value.set(buff,buff_length, &my_charset_bin); return &str_value; } @@ -1243,6 +1320,7 @@ void Item_func_now_utc::store_now_in_tm(time_t now, struct tm *now_tm) String *Item_func_sec_to_time::val_str(String *str) { + DBUG_ASSERT(fixed == 1); longlong seconds=(longlong) args[0]->val_int(); uint sec; TIME ltime; @@ -1273,6 +1351,7 @@ String *Item_func_sec_to_time::val_str(String *str) longlong Item_func_sec_to_time::val_int() { + DBUG_ASSERT(fixed == 1); longlong seconds=args[0]->val_int(); longlong sign=1; if ((null_value=args[0]->null_value)) @@ -1381,6 +1460,7 @@ uint Item_func_date_format::format_length(const String *format) String *Item_func_date_format::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *format; TIME l_time; uint size; @@ -1430,6 +1510,7 @@ null_date: String *Item_func_from_unixtime::val_str(String *str) { + DBUG_ASSERT(fixed == 1); struct tm tm_tmp,*start; time_t tmp=(time_t) args[0]->val_int(); TIME ltime; @@ -1462,6 +1543,7 @@ null_date: longlong Item_func_from_unixtime::val_int() { + DBUG_ASSERT(fixed == 1); time_t tmp=(time_t) (ulong) args[0]->val_int(); if ((null_value=args[0]->null_value)) return 0; @@ -1640,6 +1722,7 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date) String *Item_date_add_interval::val_str(String *str) { + DBUG_ASSERT(fixed == 1); TIME ltime; enum date_time_format_types format; @@ -1663,6 +1746,7 @@ String *Item_date_add_interval::val_str(String *str) longlong Item_date_add_interval::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; longlong date; if (Item_date_add_interval::get_date(<ime,0)) @@ -1735,6 +1819,7 @@ void Item_extract::fix_length_and_dec() longlong Item_extract::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; uint year; ulong week_format; @@ -1854,6 +1939,7 @@ void Item_char_typecast::print(String *str) String *Item_char_typecast::val_str(String *str) { + DBUG_ASSERT(fixed == 1); String *res, *res1; uint32 length; @@ -1910,6 +1996,7 @@ void Item_char_typecast::fix_length_and_dec() String *Item_datetime_typecast::val_str(String *str) { + DBUG_ASSERT(fixed == 1); TIME ltime; if (!get_arg0_date(<ime,1) && !make_datetime(ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME, @@ -1931,6 +2018,7 @@ bool Item_time_typecast::get_time(TIME *ltime) String *Item_time_typecast::val_str(String *str) { + DBUG_ASSERT(fixed == 1); TIME ltime; if (!get_arg0_time(<ime) && @@ -1953,6 +2041,7 @@ bool Item_date_typecast::get_date(TIME *ltime, uint fuzzy_date) String *Item_date_typecast::val_str(String *str) { + DBUG_ASSERT(fixed == 1); TIME ltime; if (!get_arg0_date(<ime,1) && !str->alloc(11)) @@ -1973,6 +2062,7 @@ String *Item_date_typecast::val_str(String *str) String *Item_func_makedate::val_str(String *str) { + DBUG_ASSERT(fixed == 1); TIME l_time; long daynr= (long) args[1]->val_int(); long yearnr= (long) args[0]->val_int(); @@ -2038,6 +2128,7 @@ void Item_func_add_time::fix_length_and_dec() String *Item_func_add_time::val_str(String *str) { + DBUG_ASSERT(fixed == 1); TIME l_time1, l_time2, l_time3; bool is_time= 0; long microseconds, seconds, days= 0; @@ -2232,6 +2323,7 @@ bool calc_time_diff(TIME *l_time1,TIME *l_time2, int l_sign, String *Item_func_timediff::val_str(String *str) { + DBUG_ASSERT(fixed == 1); longlong seconds; long microseconds; int l_sign= 1; @@ -2277,6 +2369,7 @@ null_date: String *Item_func_maketime::val_str(String *str) { + DBUG_ASSERT(fixed == 1); TIME ltime; long hour= (long) args[0]->val_int(); @@ -2315,6 +2408,7 @@ String *Item_func_maketime::val_str(String *str) longlong Item_func_microsecond::val_int() { + DBUG_ASSERT(fixed == 1); TIME ltime; if (!get_arg0_time(<ime)) return ltime.second_part; @@ -2484,6 +2578,7 @@ void Item_func_timestamp_diff::print(String *str) String *Item_func_get_format::val_str(String *str) { + DBUG_ASSERT(fixed == 1); const char *format_name; KNOWN_DATE_TIME_FORMAT *format; String *val= args[0]->val_str(str); @@ -2538,6 +2633,103 @@ 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 +*/ + +date_time_format_types check_result_type(const char *format, uint length) +{ + const char *time_part_frms= "HISThiklrs"; + const char *date_part_frms= "MUYWabcjmuyw"; + bool date_part_used= 0, time_part_used= 0, frac_second_used= 0; + + const char *val= format; + const char *end= format + length; + + for (; val != end && val != end; val++) + { + if (*val == '%' && val+1 != end) + { + val++; + if ((frac_second_used= (*val == 'f')) || + (!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) + return DATE_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; +} + + +Field *Item_func_str_to_date::tmp_table_field(TABLE *t_arg) +{ + if (cached_field_type == MYSQL_TYPE_TIME) + return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); + if (cached_field_type == MYSQL_TYPE_DATE) + return (new Field_date(maybe_null, name, t_arg, &my_charset_bin)); + if (cached_field_type == MYSQL_TYPE_DATETIME) + return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin)); + return (new Field_string(max_length, maybe_null, name, t_arg, &my_charset_bin)); +} + + +void Item_func_str_to_date::fix_length_and_dec() +{ + char format_buff[64]; + String format_str(format_buff, sizeof(format_buff), &my_charset_bin), *format; + maybe_null= 1; + decimals=0; + cached_field_type= MYSQL_TYPE_STRING; + max_length= MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + cached_timestamp_type= TIMESTAMP_NONE; + if ((const_item= args[1]->const_item())) + { + format= args[1]->val_str(&format_str); + cached_format_type= check_result_type(format->ptr(), format->length()); + switch (cached_format_type) { + case DATE_ONLY: + cached_timestamp_type= TIMESTAMP_DATE; + cached_field_type= MYSQL_TYPE_DATE; + max_length= MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + break; + case TIME_ONLY: + case TIME_MICROSECOND: + cached_timestamp_type= TIMESTAMP_TIME; + cached_field_type= MYSQL_TYPE_TIME; + max_length= MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + break; + default: + cached_timestamp_type= TIMESTAMP_DATETIME; + cached_field_type= MYSQL_TYPE_DATETIME; + break; + } + } +} + bool Item_func_str_to_date::get_date(TIME *ltime, uint fuzzy_date) { DATE_TIME_FORMAT date_time_format; @@ -2555,8 +2747,18 @@ bool Item_func_str_to_date::get_date(TIME *ltime, uint fuzzy_date) date_time_format.format.str= (char*) format->ptr(); date_time_format.format.length= format->length(); if (extract_date_time(&date_time_format, val->ptr(), val->length(), - ltime)) + ltime, cached_timestamp_type)) goto null_date; + if (cached_timestamp_type == TIMESTAMP_TIME && ltime->day) + { + /* + Day part for time type can be nonzero value and so + we should add hours from day part to hour part to + keep valid time value. + */ + ltime->hour+= ltime->day*24; + ltime->day= 0; + } return 0; null_date: @@ -2566,34 +2768,28 @@ null_date: String *Item_func_str_to_date::val_str(String *str) { + DBUG_ASSERT(fixed == 1); TIME ltime; if (Item_func_str_to_date::get_date(<ime, TIME_FUZZY_DATE)) return 0; - /* - The following DATE_TIME should be done dynamicly based on the - format string (wen it's a constant). For example, we should only return - microseconds if there was an %f in the format - */ - if (!make_datetime(ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME, + if (!make_datetime((const_item ? cached_format_type : + (ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME)), <ime, str)) return str; return 0; } -String *Item_func_last_day::val_str(String *str) +bool Item_func_last_day::get_date(TIME *ltime, uint fuzzy_date) { - TIME ltime; - if (!get_arg0_date(<ime,0)) - { - uint month_idx= ltime.month-1; - ltime.day= days_in_month[month_idx]; - if ( month_idx == 1 && calc_days_in_year(ltime.year) == 366) - ltime.day+= 1; - if (!make_datetime(DATE_ONLY, <ime, str)) - return str; - } + if (get_arg0_date(ltime,fuzzy_date)) + return 1; + uint month_idx= ltime->month-1; + ltime->day= days_in_month[month_idx]; + if ( month_idx == 1 && calc_days_in_year(ltime->year) == 366) + ltime->day= 29; + ltime->time_type= TIMESTAMP_DATE; return 0; } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 9f1d7dbd85a..263969cd26b 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -21,6 +21,11 @@ #pragma interface /* gcc class implementation */ #endif +enum date_time_format_types +{ + TIME_ONLY= 0, TIME_MICROSECOND, DATE_ONLY, DATE_TIME, DATE_TIME_MICROSECOND +}; + class Item_func_period_add :public Item_int_func { public: @@ -83,7 +88,8 @@ class Item_func_month :public Item_func public: Item_func_month(Item *a) :Item_func(a) {} longlong val_int(); - double val() { return (double) Item_func_month::val_int(); } + double val() + { DBUG_ASSERT(fixed == 1); return (double) Item_func_month::val_int(); } String *val_str(String *str) { str->set(val_int(), &my_charset_bin); @@ -244,9 +250,10 @@ public: Item_func_weekday(Item *a,bool type_arg) :Item_func(a), odbc_type(type_arg) {} longlong val_int(); - double val() { return (double) val_int(); } + double val() { DBUG_ASSERT(fixed == 1); return (double) val_int(); } String *val_str(String *str) - { + { + DBUG_ASSERT(fixed == 1); str->set(val_int(), &my_charset_bin); return null_value ? 0 : str; } @@ -318,7 +325,8 @@ public: enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_DATE; } String *val_str(String *str); - double val() { return (double) val_int(); } + longlong val_int(); + double val() { DBUG_ASSERT(fixed == 1); return (double) val_int(); } const char *func_name() const { return "date"; } void fix_length_and_dec() { @@ -360,8 +368,8 @@ public: Item_func_curtime(Item *a) :Item_func(a) {} enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_TIME; } - double val() { return (double) value; } - longlong val_int() { return value; } + double val() { DBUG_ASSERT(fixed == 1); return (double) value; } + longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } String *val_str(String *str); void fix_length_and_dec(); Field *tmp_table_field(TABLE *t_arg) @@ -406,7 +414,8 @@ class Item_func_curdate :public Item_date public: Item_func_curdate() :Item_date() {} void set_result_from_tm(struct tm *now); - longlong val_int() { return (value) ; } + longlong val_int() { DBUG_ASSERT(fixed == 1); return (value) ; } + String *val_str(String *str); void fix_length_and_dec(); bool get_date(TIME *res, uint fuzzy_date); virtual void store_now_in_tm(time_t now, struct tm *now_tm)=0; @@ -443,8 +452,8 @@ public: Item_func_now() :Item_date_func() {} Item_func_now(Item *a) :Item_date_func(a) {} enum Item_result result_type () const { return STRING_RESULT; } - double val() { return (double) value; } - longlong val_int() { return value; } + double val() { DBUG_ASSERT(fixed == 1); return (double) value; } + longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } int save_in_field(Field *to, bool no_conversions); String *val_str(String *str); void fix_length_and_dec(); @@ -460,6 +469,7 @@ public: Item_func_now_local(Item *a) :Item_func_now(a) {} const char *func_name() const { return "now"; } void store_now_in_tm(time_t now, struct tm *now_tm); + virtual enum Functype functype() const { return NOW_FUNC; } }; @@ -477,8 +487,8 @@ class Item_func_from_days :public Item_date { public: Item_func_from_days(Item *a) :Item_date(a) {} - longlong val_int(); const char *func_name() const { return "from_days"; } + bool get_date(TIME *res, uint fuzzy_date); }; @@ -501,7 +511,11 @@ class Item_func_from_unixtime :public Item_date_func { public: Item_func_from_unixtime(Item *a) :Item_date_func(a) {} - double val() { return (double) Item_func_from_unixtime::val_int(); } + double val() + { + DBUG_ASSERT(fixed == 1); + return (double) Item_func_from_unixtime::val_int(); + } longlong val_int(); String *val_str(String *str); const char *func_name() const { return "from_unixtime"; } @@ -519,7 +533,11 @@ class Item_func_sec_to_time :public Item_str_func { public: Item_func_sec_to_time(Item *item) :Item_str_func(item) {} - double val() { return (double) Item_func_sec_to_time::val_int(); } + double val() + { + DBUG_ASSERT(fixed == 1); + return (double) Item_func_sec_to_time::val_int(); + } longlong val_int(); String *val_str(String *); void fix_length_and_dec() @@ -565,7 +583,7 @@ public: const char *func_name() const { return "date_add_interval"; } void fix_length_and_dec(); enum_field_types field_type() const { return cached_field_type; } - double val() { return (double) val_int(); } + double val() { DBUG_ASSERT(fixed == 1); return (double) val_int(); } longlong val_int(); bool get_date(TIME *res, uint fuzzy_date); void print(String *str); @@ -594,6 +612,7 @@ public: Item_typecast(Item *a) :Item_str_func(a) {} String *val_str(String *a) { + DBUG_ASSERT(fixed == 1); String *tmp=args[0]->val_str(a); null_value=args[0]->null_value; if (tmp) @@ -610,6 +629,19 @@ public: }; +class Item_typecast_maybe_null :public Item_typecast +{ +public: + Item_typecast_maybe_null(Item *a) :Item_typecast(a) {} + void fix_length_and_dec() + { + collation.set(&my_charset_bin); + max_length=args[0]->max_length; + maybe_null= 1; + } +}; + + class Item_char_typecast :public Item_typecast { int cast_length; @@ -626,10 +658,10 @@ public: }; -class Item_date_typecast :public Item_typecast +class Item_date_typecast :public Item_typecast_maybe_null { public: - Item_date_typecast(Item *a) :Item_typecast(a) {} + Item_date_typecast(Item *a) :Item_typecast_maybe_null(a) {} String *val_str(String *str); bool get_date(TIME *ltime, uint fuzzy_date); const char *cast_type() const { return "date"; } @@ -641,10 +673,10 @@ public: }; -class Item_time_typecast :public Item_typecast +class Item_time_typecast :public Item_typecast_maybe_null { public: - Item_time_typecast(Item *a) :Item_typecast(a) {} + Item_time_typecast(Item *a) :Item_typecast_maybe_null(a) {} String *val_str(String *str); bool get_time(TIME *ltime); const char *cast_type() const { return "time"; } @@ -656,10 +688,10 @@ public: }; -class Item_datetime_typecast :public Item_typecast +class Item_datetime_typecast :public Item_typecast_maybe_null { public: - Item_datetime_typecast(Item *a) :Item_typecast(a) {} + Item_datetime_typecast(Item *a) :Item_typecast_maybe_null(a) {} String *val_str(String *str); const char *cast_type() const { return "datetime"; } enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } @@ -810,37 +842,29 @@ public: }; -class Item_func_str_to_date :public Item_date_func +class Item_func_str_to_date :public Item_str_func { + enum_field_types cached_field_type; + date_time_format_types cached_format_type; + timestamp_type cached_timestamp_type; + bool const_item; public: Item_func_str_to_date(Item *a, Item *b) - :Item_date_func(a, b) + :Item_str_func(a, b) {} String *val_str(String *str); bool get_date(TIME *ltime, uint fuzzy_date); const char *func_name() const { return "str_to_date"; } - void fix_length_and_dec() - { - maybe_null= 1; - decimals=0; - max_length=MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - } + enum_field_types field_type() const { return cached_field_type; } + void fix_length_and_dec(); + Field *tmp_table_field(TABLE *t_arg); }; -class Item_func_last_day :public Item_str_func + +class Item_func_last_day :public Item_date { public: - Item_func_last_day(Item *a) :Item_str_func(a) {} - String *val_str(String *str); + Item_func_last_day(Item *a) :Item_date(a) {} const char *func_name() const { return "last_day"; } - enum_field_types field_type() const { return MYSQL_TYPE_DATE; } - void fix_length_and_dec() - { - decimals=0; - max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - } - Field *tmp_table_field(TABLE *t_arg) - { - return (new Field_date(maybe_null, name, t_arg, &my_charset_bin)); - } + bool get_date(TIME *res, uint fuzzy_date); }; diff --git a/sql/item_uniq.h b/sql/item_uniq.h index 47f967b52c6..5582537bdbb 100644 --- a/sql/item_uniq.h +++ b/sql/item_uniq.h @@ -27,7 +27,7 @@ class Item_func_unique_users :public Item_real_func public: Item_func_unique_users(Item *name_arg,int start,int end,List<Item> &list) :Item_real_func(list) {} - double val() { return 0.0; } + double val() { DBUG_ASSERT(fixed == 1); return 0.0; } void fix_length_and_dec() { decimals=0; max_length=6; } void print(String *str) { str->append("0.0", 3); } }; @@ -40,7 +40,7 @@ public: :Item_sum_num(item_arg) {} Item_sum_unique_users(THD *thd, Item_sum_unique_users *item) :Item_sum_num(thd, item) {} - double val() { return 0.0; } + double val() { DBUG_ASSERT(fixed == 1); return 0.0; } enum Sumfunctype sum_func () const {return UNIQUE_USERS_FUNC;} void clear() {} bool add() { return 0; } @@ -48,6 +48,7 @@ public: void update_field() {} bool fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) { + DBUG_ASSERT(fixed == 0); fixed= 1; return 0; } diff --git a/sql/key.cc b/sql/key.cc index 639b1e535a6..d4499573e8e 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -182,9 +182,9 @@ int key_cmp(TABLE *table,const byte *key,uint idx,uint key_length) } if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH)) { - if (key_part->field->key_cmp(key, key_part->length+2)) + if (key_part->field->key_cmp(key, key_part->length+ HA_KEY_BLOB_LENGTH)) return 1; - length=key_part->length+2; + length=key_part->length+HA_KEY_BLOB_LENGTH; } else { diff --git a/sql/lex.h b/sql/lex.h index 72888f7d89e..32552172a01 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -120,12 +120,14 @@ static SYMBOL symbols[] = { { "CONNECTION", SYM(CONNECTION_SYM)}, { "CONSTRAINT", SYM(CONSTRAINT)}, { "CONTINUE", SYM(CONTINUE_SYM)}, + { "CONVERT", SYM(CONVERT_SYM)}, { "CREATE", SYM(CREATE)}, { "CROSS", SYM(CROSS)}, { "CUBE", SYM(CUBE_SYM)}, { "CURRENT_DATE", SYM(CURDATE)}, { "CURRENT_TIME", SYM(CURTIME)}, { "CURRENT_TIMESTAMP", SYM(NOW_SYM)}, + { "CURRENT_USER", SYM(CURRENT_USER)}, { "CURSOR", SYM(CURSOR_SYM)}, { "DATA", SYM(DATA_SYM)}, { "DATABASE", SYM(DATABASE)}, @@ -527,7 +529,6 @@ static SYMBOL sql_functions[] = { { "CAST", SYM(CAST_SYM)}, { "CEIL", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ceiling)}, { "CEILING", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ceiling)}, - { "CURRENT_USER", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_current_user)}, { "BIT_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bit_length)}, { "CENTROID", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_centroid)}, { "CHAR_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_char_length)}, @@ -540,7 +541,6 @@ static SYMBOL sql_functions[] = { { "CONNECTION_ID", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_connection_id)}, { "CONTAINS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_contains)}, { "CONV", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_conv)}, - { "CONVERT", SYM(CONVERT_SYM)}, { "COUNT", SYM(COUNT_SYM)}, { "COS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cos)}, { "COT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cot)}, @@ -699,7 +699,7 @@ static SYMBOL sql_functions[] = { { "STDDEV", SYM(STD_SYM)}, { "STR_TO_DATE", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_str_to_date)}, { "STRCMP", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_strcmp)}, - { "SUBSTR", SYM(SUBSTRING)}, + { "SUBSTR", SYM(SUBSTRING)}, { "SUBSTRING", SYM(SUBSTRING)}, { "SUBSTRING_INDEX", SYM(SUBSTRING_INDEX)}, { "SUBTIME", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_subtime)}, @@ -716,9 +716,11 @@ static SYMBOL sql_functions[] = { { "UCASE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)}, { "UNCOMPRESS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_uncompress)}, { "UNCOMPRESSED_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_uncompressed_length)}, + { "UNHEX", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_unhex)}, { "UNIQUE_USERS", SYM(UNIQUE_USERS)}, { "UNIX_TIMESTAMP", SYM(UNIX_TIMESTAMP)}, { "UPPER", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)}, + { "UUID", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_uuid)}, { "VARIANCE", SYM(VARIANCE_SYM)}, { "VERSION", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_version)}, { "WEEKDAY", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekday)}, diff --git a/sql/lex_symbol.h b/sql/lex_symbol.h index d1d30a73669..3074a489b6a 100644 --- a/sql/lex_symbol.h +++ b/sql/lex_symbol.h @@ -37,4 +37,13 @@ typedef struct st_lex_symbol uint length; } LEX_SYMBOL; +typedef struct st_sym_group { + const char *name; + const char *needed_define; +} SYM_GROUP; + +extern SYM_GROUP sym_group_common; +extern SYM_GROUP sym_group_geom; +extern SYM_GROUP sym_group_rtree; + #endif /* _lex_symbol_h */ diff --git a/sql/lock.cc b/sql/lock.cc index 0a2f91812a7..6d9deb8e4c6 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -445,14 +445,27 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, return sql_lock; } + /***************************************************************************** -** Lock table based on the name. -** This is used when we need total access to a closed, not open table + Lock table based on the name. + This is used when we need total access to a closed, not open table *****************************************************************************/ /* Lock and wait for the named lock. - Returns 0 on ok + + SYNOPSIS + lock_and_wait_for_table_name() + thd Thread handler + table_list Lock first table in this list + + + NOTES + Works together with global read lock. + + RETURN + 0 ok + 1 error */ int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list) @@ -482,27 +495,45 @@ end: /* Put a not open table with an old refresh version in the table cache. - This will force any other threads that uses the table to release it - as soon as possible. - One must have a lock on LOCK_open ! - Return values: - < 0 error - == 0 table locked - > 0 table locked, but someone is using it + + SYNPOSIS + lock_table_name() + thd Thread handler + table_list Lock first table in this list + + WARNING + If you are going to update the table, you should use + lock_and_wait_for_table_name instead of this function as this works + together with 'FLUSH TABLES WITH READ LOCK' + + NOTES + This will force any other threads that uses the table to release it + as soon as possible. + + REQUIREMENTS + One must have a lock on LOCK_open ! + + RETURN: + < 0 error + == 0 table locked + > 0 table locked, but someone is using it */ int lock_table_name(THD *thd, TABLE_LIST *table_list) { TABLE *table; char key[MAX_DBKEY_LENGTH]; - char *db= table_list->db ? table_list->db : (thd->db ? thd->db : (char*) ""); + char *db= table_list->db; uint key_length; DBUG_ENTER("lock_table_name"); + DBUG_PRINT("enter",("db: %s name: %s", db, table_list->real_name)); + safe_mutex_assert_owner(&LOCK_open); key_length=(uint) (strmov(strmov(key,db)+1,table_list->real_name) -key)+ 1; + /* Only insert the table if we haven't insert it already */ for (table=(TABLE*) hash_search(&open_cache,(byte*) key,key_length) ; table ; @@ -534,6 +565,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) DBUG_RETURN(0); } + void unlock_table_name(THD *thd, TABLE_LIST *table_list) { if (table_list->table) @@ -543,6 +575,7 @@ void unlock_table_name(THD *thd, TABLE_LIST *table_list) } } + static bool locked_named_table(THD *thd, TABLE_LIST *table_list) { for (; table_list ; table_list=table_list->next) @@ -583,6 +616,10 @@ bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list) table_list Names of tables to lock NOTES + If you are just locking one table, you should use + lock_and_wait_for_table_name(). + + REQUIREMENTS One must have a lock on LOCK_open when calling this RETURN diff --git a/sql/log.cc b/sql/log.cc index da20bfdb9fa..b18a01b2e56 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -207,7 +207,7 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, open_flags |= O_RDWR; else open_flags |= O_WRONLY; - + db[0]=0; open_count++; if ((file=my_open(log_file_name,open_flags, @@ -220,14 +220,19 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, case LOG_NORMAL: { char *end; + int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s. " #ifdef EMBEDDED_LIBRARY - sprintf(buff, "%s, Version: %s, embedded library\n", my_progname, server_version); + "embedded library\n", my_progname, server_version #elif __NT__ - sprintf(buff, "%s, Version: %s, started with:\nTCP Port: %d, Named Pipe: %s\n", my_progname, server_version, mysqld_port, mysqld_unix_port); + "started with:\nTCP Port: %d, Named Pipe: %s\n", + my_progname, server_version, mysqld_port, mysqld_unix_port #else - sprintf(buff, "%s, Version: %s, started with:\nTcp port: %d Unix socket: %s\n", my_progname,server_version,mysqld_port,mysqld_unix_port); + "started with:\nTcp port: %d Unix socket: %s\n", + my_progname,server_version,mysqld_port,mysqld_unix_port #endif - end=strmov(strend(buff),"Time Id Command Argument\n"); + ); + end=strnmov(buff+len,"Time Id Command Argument\n", + sizeof(buff)-len); if (my_b_write(&log_file, (byte*) buff,(uint) (end-buff)) || flush_io_cache(&log_file)) goto err; @@ -235,21 +240,21 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, } case LOG_NEW: { + uint len; time_t skr=time(NULL); struct tm tm_tmp; + localtime_r(&skr,&tm_tmp); - ulong length; - length= my_sprintf(buff, - (buff, - "# %s, Version: %s at %02d%02d%02d %2d:%02d:%02d\n", - my_progname,server_version, - tm_tmp.tm_year % 100, - tm_tmp.tm_mon+1, - tm_tmp.tm_mday, - tm_tmp.tm_hour, - tm_tmp.tm_min, - tm_tmp.tm_sec)); - if (my_b_write(&log_file, (byte*) buff, length) || + len= my_snprintf(buff,sizeof(buff), + "# %s, Version: %s at %02d%02d%02d %2d:%02d:%02d\n", + my_progname,server_version, + tm_tmp.tm_year % 100, + tm_tmp.tm_mon+1, + tm_tmp.tm_mday, + tm_tmp.tm_hour, + tm_tmp.tm_min, + tm_tmp.tm_sec); + if (my_b_write(&log_file, (byte*) buff, len) || flush_io_cache(&log_file)) goto err; break; @@ -264,7 +269,7 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, index_file_name_arg= name; // Use same basename for index file opt= MY_UNPACK_FILENAME | MY_REPLACE_EXT; } - + if (!my_b_filelength(&log_file)) { /* @@ -547,7 +552,6 @@ int MYSQL_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name, RETURN VALUES 0 ok LOG_INFO_EOF End of log-index-file found - LOG_INFO_SEEK Could not allocate IO cache LOG_INFO_IO Got IO error while reading file */ @@ -1257,8 +1261,7 @@ bool MYSQL_LOG::write(Log_event* event_info) "do the involved tables match (to be implemented) binlog_[wild_]{do|ignore}_table?" (WL#1049)" */ - if ((thd && !(thd->options & OPTION_BIN_LOG) && - (thd->master_access & SUPER_ACL)) || + if ((thd && !(thd->options & OPTION_BIN_LOG)) || (local_db && !db_ok(local_db, binlog_do_db, binlog_ignore_db))) { VOID(pthread_mutex_unlock(&LOCK_log)); diff --git a/sql/log_event.h b/sql/log_event.h index 7f161267add..ec23aad1717 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -751,17 +751,19 @@ public: const char* fname; uint32 skip_lines; sql_ex_info sql_ex; + bool local_fname; /* fname doesn't point to memory inside Log_event::temp_buf */ void set_fname_outside_temp_buf(const char *afname, uint alen) { fname= afname; fname_len= alen; + local_fname= true; } /* fname doesn't point to memory inside Log_event::temp_buf */ int check_fname_outside_temp_buf() { - return fname < temp_buf || fname > temp_buf+ cached_event_len; + return local_fname; } #ifndef MYSQL_CLIENT @@ -1146,6 +1148,7 @@ protected: bool fake_base; public: char* block; + const char *event_buf; uint block_len; uint file_id; bool inited_from_old; @@ -1168,7 +1171,10 @@ public: Create_file_log_event(const char* buf, uint event_len, const Format_description_log_event* description_event); - ~Create_file_log_event() {} + ~Create_file_log_event() + { + my_free((char*) event_buf, MYF(MY_ALLOW_ZERO_PTR)); + } Log_event_type get_type_code() { diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc index b7e2a803e42..71c8d588de7 100644 --- a/sql/mf_iocache.cc +++ b/sql/mf_iocache.cc @@ -30,14 +30,8 @@ flush_io_cache(). */ -#define MAP_TO_USE_RAID #include "mysql_priv.h" #ifdef HAVE_REPLICATION -#ifdef HAVE_AIOWAIT -#include <mysys_err.h> -#include <errno.h> -static void my_aiowait(my_aio_result *result); -#endif extern "C" { diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index e17847ebe24..697b5146b7d 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -73,14 +73,6 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); #define PREV_BITS(type,A) ((type) (((type) 1 << (A)) -1)) #define all_bits_set(A,B) ((A) & (B) != (B)) -#ifndef LL -#ifdef HAVE_LONG_LONG -#define LL(A) A ## LL -#else -#define LL(A) A ## L -#endif -#endif - extern CHARSET_INFO *system_charset_info, *files_charset_info ; extern CHARSET_INFO *national_charset_info, *table_alias_charset; @@ -164,6 +156,9 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; /* Time handling defaults */ #define TIMESTAMP_MAX_YEAR 2038 #define YY_PART_YEAR 70 +#define TIMESTAMP_MIN_YEAR (1900 + YY_PART_YEAR - 1) +#define TIMESTAMP_MAX_VALUE 2145916799 +#define TIMESTAMP_MIN_VALUE 1 #define PRECISION_FOR_DOUBLE 53 #define PRECISION_FOR_FLOAT 24 @@ -205,7 +200,8 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; #define TEST_CORE_ON_SIGNAL 256 /* Give core if signal */ #define TEST_NO_STACKTRACE 512 #define TEST_SIGINT 1024 /* Allow sigint on threads */ - +#define TEST_SYNCHRONIZATION 2048 /* get server to do sleep in some + places */ #endif /* @@ -255,9 +251,6 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; /* The rest of the file is included in the server only */ #ifndef MYSQL_CLIENT -/* options for UNION set by the yacc parser (stored in unit->union_option) */ -#define UNION_ALL 1 - /* Bits for different SQL modes modes (including ANSI mode) */ #define MODE_REAL_AS_FLOAT 1 #define MODE_PIPES_AS_CONCAT 2 @@ -333,6 +326,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout); struct st_table; class THD; +class Statement; /* Struct to handle simple linked lists */ @@ -387,6 +381,11 @@ inline THD *_current_thd(void) #include "sql_udf.h" #include "item.h" typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); +/* sql_parse.cc */ +void free_items(Item *item); +void cleanup_items(Item *item); +class THD; +void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0); #include "sql_class.h" #include "opt_range.h" @@ -447,8 +446,8 @@ int quick_rm_table(enum db_type base,const char *db, bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list); bool mysql_change_db(THD *thd,const char *name); void mysql_parse(THD *thd,char *inBuf,uint length); +bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); bool is_update_query(enum enum_sql_command command); -void free_items(Item *item); bool alloc_query(THD *thd, char *packet, ulong packet_length); void mysql_init_select(LEX *lex); void mysql_init_query(THD *thd, bool lexonly=0); @@ -467,13 +466,8 @@ bool do_command(THD *thd); bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length); bool check_dup(const char *db, const char *name, TABLE_LIST *tables); -#ifndef EMBEDDED_LIBRARY -bool check_stack_overrun(THD *thd,char *dummy); -#else -#define check_stack_overrun(A, B) 0 -#endif -void table_cache_init(void); +bool table_cache_init(void); void table_cache_free(void); uint cached_tables(void); void kill_mysql(void); @@ -577,7 +571,6 @@ int mysql_multi_update(THD *thd, TABLE_LIST *table_list, 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); -void kill_delayed_threads(void); int mysql_delete(THD *thd, TABLE_LIST *table, COND *conds, SQL_LIST *order, ha_rows rows, ulong options); int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok=0); @@ -601,7 +594,8 @@ extern const Field *not_found_field; Field *find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, TABLE_LIST **where, bool report_error); Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, - bool check_grant,bool allow_rowid); + bool check_grant,bool allow_rowid, + uint *cached_field_index_ptr); #ifdef HAVE_OPENSSL #include <openssl/des.h> struct st_des_keyblock @@ -653,14 +647,13 @@ int mysqld_show_column_types(THD *thd); int mysqld_help (THD *thd, const char *text); /* sql_prepare.cc */ -bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length); -void mysql_stmt_execute(THD *thd, char *packet); +void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length); +void mysql_stmt_execute(THD *thd, char *packet, uint packet_length); void mysql_stmt_free(THD *thd, char *packet); void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); int check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, List<Item> &values, ulong counter); -void setup_param_functions(Item_param *param, uchar param_type); /* sql_error.cc */ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, uint code, @@ -682,7 +675,7 @@ 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 *default_value, Item *on_update_value, LEX_STRING *comment, char *change, TYPELIB *interval,CHARSET_INFO *cs, uint uint_geom_type); @@ -705,7 +698,7 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, bool insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, const char *table_name, List_iterator<Item> *it); -bool setup_tables(TABLE_LIST *tables, my_bool reinit); +bool setup_tables(TABLE_LIST *tables); int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, List<Item> *sum_func_list, uint wild_num); int setup_fields(THD *thd, Item** ref_pointer_array, TABLE_LIST *tables, @@ -725,7 +718,6 @@ bool rm_temporary_table(enum db_type base, char *path); void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); bool close_thread_table(THD *thd, TABLE **table_ptr); -void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0); void close_temporary_tables(THD *thd); TABLE_LIST * find_table_in_list(TABLE_LIST *table, const char *db_name, const char *table_name); @@ -794,6 +786,9 @@ bool open_log(MYSQL_LOG *log, const char *hostname, /* mysqld.cc */ extern void yyerror(const char*); +/* item_func.cc */ +extern bool check_reserved_words(LEX_STRING *name); + /* strfunc.cc */ ulonglong find_set(TYPELIB *typelib,const char *x, uint length, char **err_pos, uint *err_len, bool *set_warning); @@ -803,9 +798,6 @@ uint check_word(TYPELIB *lib, const char *val, const char *end, bool is_keyword(const char *name, uint len); -/* sql_parse.cc */ -void free_items(Item *item); -void cleanup_items(Item *item); #define MY_DB_OPT_FILE "db.opt" bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create); @@ -816,7 +808,8 @@ bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create); extern time_t start_time; extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH], - mysql_real_data_home[], *opt_mysql_tmpdir, mysql_charsets_dir[]; + mysql_real_data_home[], *opt_mysql_tmpdir, mysql_charsets_dir[], + opt_ft_boolean_syntax[sizeof(ft_boolean_syntax)]; #define mysql_tmpdir (my_tmpdir(&mysql_tmpdir_list)) extern MY_TMPDIR mysql_tmpdir_list; extern const char *command_name[]; @@ -836,9 +829,11 @@ extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN]; extern char pidfile_name[FN_REFLEN], time_zone[30], *opt_init_file; extern char log_error_file[FN_REFLEN]; extern double log_10[32]; +extern ulonglong log_10_int[20]; extern ulonglong keybuff_size; extern ulong refresh_version,flush_version, thread_id,query_id,opened_tables; -extern ulong created_tmp_tables, created_tmp_disk_tables; +extern ulong created_tmp_tables, created_tmp_disk_tables, bytes_sent; +extern ulong binlog_cache_use, binlog_cache_disk_use; extern ulong aborted_threads,aborted_connects; extern ulong delayed_insert_timeout; extern ulong delayed_insert_limit, delayed_queue_size; @@ -859,7 +854,7 @@ extern ulong ha_read_rnd_count, ha_read_rnd_next_count; extern ulong ha_commit_count, ha_rollback_count,table_cache_size; extern ulong max_connections,max_connect_errors, connect_timeout; extern ulong slave_net_timeout; -extern ulong max_insert_delayed_threads, max_user_connections; +extern ulong max_user_connections; extern ulong long_query_count, what_to_log,flush_time; extern ulong query_buff_size, thread_stack,thread_stack_min; extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit; @@ -871,7 +866,7 @@ extern ulong expire_logs_days; extern my_bool relay_log_purge; extern uint test_flags,select_errors,ha_open_options; extern uint protocol_version, mysqld_port, dropping_tables; -extern uint delay_key_write_options; +extern uint delay_key_write_options, lower_case_table_names; extern bool opt_endinfo, using_udf_functions, locked_in_memory; extern bool opt_using_transactions, mysql_embedded; extern bool using_update_log, opt_large_files; @@ -880,10 +875,10 @@ extern bool opt_disable_networking, opt_skip_show_db; extern bool volatile abort_loop, shutdown_in_progress, grant_option; extern uint volatile thread_count, thread_running, global_read_lock; extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types; -extern my_bool opt_safe_show_db, opt_local_infile, lower_case_table_names; +extern my_bool opt_safe_show_db, opt_local_infile; extern my_bool opt_slave_compressed_protocol, use_temp_pool; -extern my_bool opt_readonly; -extern my_bool opt_enable_named_pipe; +extern my_bool opt_readonly, lower_case_file_system; +extern my_bool opt_enable_named_pipe, opt_sync_frm; extern my_bool opt_secure_auth; extern char *shared_memory_base_name, *mysqld_unix_port; extern bool opt_enable_shared_memory; @@ -893,7 +888,7 @@ extern FILE *bootstrap_file; 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_error_log, LOCK_delayed_insert, LOCK_uuid_generator, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone, LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_system_variables, LOCK_user_conn; @@ -995,6 +990,8 @@ timestamp_type str_to_TIME(const char *str, uint length, TIME *l_time, void localtime_to_TIME(TIME *to, struct tm *from); void calc_time_from_sec(TIME *to, long seconds, long microseconds); +void make_truncated_value_warning(THD *thd, const char *str_val, + uint str_length, timestamp_type time_type); extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type, const char *format_str, uint format_length); @@ -1010,11 +1007,6 @@ void make_datetime(DATE_TIME_FORMAT *format, TIME *l_time, String *str); int test_if_number(char *str,int *res,bool allow_wildcards); void change_byte(byte *,uint,char,char); -#ifndef EMBEDDED_LIBRARY -extern "C" void unireg_abort(int exit_code); -#else -#define unireg_abort(exit_code) DBUG_RETURN(exit_code) -#endif void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form, SQL_SELECT *select, int use_record_cache, bool print_errors); @@ -1116,6 +1108,19 @@ inline void table_case_convert(char * name, uint length) my_casedn(files_charset_info, name, length); } +inline const char *table_case_name(HA_CREATE_INFO *info, const char *name) +{ + return ((lower_case_table_names == 2 && info->alias) ? info->alias : name); +} + +inline ulong sql_rnd_with_mutex() +{ + pthread_mutex_lock(&LOCK_thread_count); + ulong tmp=(ulong) (my_rnd(&sql_rand) * 0xffffffff); /* make all bits random */ + pthread_mutex_unlock(&LOCK_thread_count); + return tmp; +} + Comp_creator *comp_eq_creator(bool invert); Comp_creator *comp_ge_creator(bool invert); Comp_creator *comp_gt_creator(bool invert); @@ -1151,13 +1156,20 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr) table->force_index= table_list->force_index; } -typedef struct st_sym_group { - const char *name; - const char *needed_define; -} SYM_GROUP; -extern SYM_GROUP sym_group_common; -extern SYM_GROUP sym_group_geom; -extern SYM_GROUP sym_group_rtree; +/* + Some functions that are different in the embedded library and the normal + server +*/ + +#ifndef EMBEDDED_LIBRARY +extern "C" void unireg_abort(int exit_code); +void kill_delayed_threads(void); +bool check_stack_overrun(THD *thd,char *dummy); +#else +#define unireg_abort(exit_code) DBUG_RETURN(exit_code) +inline void kill_delayed_threads(void) {} +#define check_stack_overrun(A, B) 0 +#endif #endif /* MYSQL_CLIENT */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 0a05da7491b..d242a54f141 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -152,7 +152,7 @@ int initgroups(const char *,unsigned int); typedef fp_except fp_except_t; #endif - /* We can't handle floating point expections with threads, so disable + /* We can't handle floating point exceptions with threads, so disable this on freebsd */ @@ -186,15 +186,11 @@ inline void reset_floating_point_exceptions() #else #include <my_pthread.h> // For thr_setconcurency() #endif -#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) && !defined(HAVE_mit_thread) -#define SET_RLIMIT_NOFILE -#endif #ifdef SOLARIS extern "C" int gethostname(char *name, int namelen); #endif - /* Set prefix for windows binary */ #ifdef __WIN__ #undef MYSQL_SERVER_SUFFIX @@ -254,6 +250,7 @@ arg_cmp_func Arg_comparator::comparator_matrix[4][2] = bool opt_log, opt_update_log, opt_bin_log, opt_slow_log; bool opt_error_log= IF_WIN(1,0); bool opt_disable_networking=0, opt_skip_show_db=0; +bool lower_case_table_names_used= 0; bool server_id_supplied = 0; bool opt_endinfo,using_udf_functions, locked_in_memory; bool opt_using_transactions, using_update_log; @@ -265,18 +262,20 @@ my_bool opt_reckless_slave = 0; my_bool opt_enable_named_pipe= 0, opt_debugging= 0; my_bool opt_local_infile, opt_external_locking, opt_slave_compressed_protocol; my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; -my_bool lower_case_table_names, opt_old_rpl_compat; 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; 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; my_bool opt_short_log_format= 0; my_bool opt_log_queries_not_using_indexes= 0; +my_bool lower_case_file_system= 0; volatile bool mqh_used = 0; 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 volatile thread_count, thread_running, kill_cached_threads, wake_thread; ulong back_log, connect_timeout, concurrency; @@ -301,7 +300,8 @@ ulong select_range_check_count, select_range_count, select_scan_count; ulong select_full_range_join_count,select_full_join_count; ulong specialflag=0,opened_tables=0,created_tmp_tables=0, created_tmp_disk_tables=0; -ulong max_connections,max_insert_delayed_threads,max_used_connections, +ulong binlog_cache_use= 0, binlog_cache_disk_use= 0; +ulong max_connections,max_used_connections, max_connect_errors, max_user_connections = 0; ulong thread_id=1L,current_pid; ulong slow_launch_threads = 0; @@ -311,6 +311,14 @@ ulong my_bind_addr; /* the address we bind to */ volatile ulong cached_thread_count= 0; double log_10[32]; /* 10 potences */ +ulonglong log_10_int[20]= +{ + 1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL, + ULL(100000000), ULL(1000000000), ULL(10000000000), ULL(100000000000), + ULL(1000000000000), ULL(10000000000000), ULL(100000000000000), + ULL(1000000000000000), ULL(10000000000000000), ULL(100000000000000000), + ULL(1000000000000000000), ULL(10000000000000000000) +}; time_t start_time; @@ -320,7 +328,8 @@ 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], max_sort_char,*mysqld_user,*mysqld_chroot, *opt_init_file, - *opt_init_connect, *opt_init_slave; + *opt_init_connect, *opt_init_slave, + opt_ft_boolean_syntax[sizeof(ft_boolean_syntax)]; const char *opt_date_time_formats[3]; @@ -374,7 +383,7 @@ 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, - LOCK_error_log, + LOCK_error_log, LOCK_uuid_generator, LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received, LOCK_global_system_variables, @@ -434,7 +443,7 @@ static NTService Service; // Service object for WinNT #endif /* __WIN__ */ #ifdef __NT__ -static char szPipeName [ 257 ]; +static char pipe_name[512]; static SECURITY_ATTRIBUTES saPipeSecurity; static SECURITY_DESCRIPTOR sdPipeDescriptor; static HANDLE hPipe = INVALID_HANDLE_VALUE; @@ -494,9 +503,6 @@ extern "C" pthread_handler_decl(handle_connections_namedpipes,arg); static pthread_handler_decl(handle_connections_shared_memory,arg); #endif extern "C" pthread_handler_decl(handle_slave,arg); -#ifdef SET_RLIMIT_NOFILE -static uint set_maximum_open_files(uint max_file_limit); -#endif static ulong find_bit_type(const char *x, TYPELIB *bit_lib); static void clean_up(bool print_message); static void clean_up_mutexes(void); @@ -580,7 +586,7 @@ static void close_connections(void) DBUG_PRINT( "quit", ("Closing named pipes") ); /* Create connection to the handle named pipe handler to break the loop */ - if ((temp = CreateFile(szPipeName, + if ((temp = CreateFile(pipe_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, @@ -588,7 +594,7 @@ static void close_connections(void) 0, NULL )) != INVALID_HANDLE_VALUE) { - WaitNamedPipe(szPipeName, 1000); + WaitNamedPipe(pipe_name, 1000); DWORD dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; SetNamedPipeHandleState(temp, &dwMode, NULL, NULL); CancelIo(temp); @@ -921,6 +927,7 @@ void clean_up(bool print_message) #ifdef USE_RAID end_raid(); #endif + my_free_open_file_info(); my_free((char*) global_system_variables.date_format, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) global_system_variables.time_format, @@ -1056,8 +1063,8 @@ static void set_user(const char *user) { /* Don't give a warning, if real user is same as given with --user */ struct passwd *user_info= getpwnam(user); - - if (!user_info || user_id != user_info->pw_uid) + if ((!user_info || user_id != user_info->pw_uid) && + global_system_variables.log_warnings) fprintf(stderr, "Warning: One can only use the --user switch if running as root\n"); } @@ -1191,11 +1198,14 @@ static void server_init(void) if (Service.IsNT() && mysqld_unix_port[0] && !opt_bootstrap && opt_enable_named_pipe) { - sprintf(szPipeName, "\\\\.\\pipe\\%s", mysqld_unix_port ); - ZeroMemory( &saPipeSecurity, sizeof(saPipeSecurity) ); - ZeroMemory( &sdPipeDescriptor, sizeof(sdPipeDescriptor) ); - if ( !InitializeSecurityDescriptor(&sdPipeDescriptor, - SECURITY_DESCRIPTOR_REVISION) ) + + pipe_name[sizeof(pipe_name)-1]= 0; /* Safety if too long string */ + strxnmov(pipe_name, sizeof(pipe_name)-1, "\\\\.\\pipe\\", + mysqld_unix_port, NullS); + bzero((char*) &saPipeSecurity, sizeof(saPipeSecurity)); + bzero((char*) &sdPipeDescriptor, sizeof(sdPipeDescriptor)); + if (!InitializeSecurityDescriptor(&sdPipeDescriptor, + SECURITY_DESCRIPTOR_REVISION)) { sql_perror("Can't start server : Initialize security descriptor"); unireg_abort(1); @@ -1208,16 +1218,16 @@ static void server_init(void) saPipeSecurity.nLength = sizeof( SECURITY_ATTRIBUTES ); saPipeSecurity.lpSecurityDescriptor = &sdPipeDescriptor; saPipeSecurity.bInheritHandle = FALSE; - if ((hPipe = CreateNamedPipe(szPipeName, - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_BYTE | - PIPE_READMODE_BYTE | - PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - (int) global_system_variables.net_buffer_length, - (int) global_system_variables.net_buffer_length, - NMPWAIT_USE_DEFAULT_WAIT, - &saPipeSecurity )) == INVALID_HANDLE_VALUE) + if ((hPipe= CreateNamedPipe(pipe_name, + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_BYTE | + PIPE_READMODE_BYTE | + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + (int) global_system_variables.net_buffer_length, + (int) global_system_variables.net_buffer_length, + NMPWAIT_USE_DEFAULT_WAIT, + &saPipeSecurity)) == INVALID_HANDLE_VALUE) { LPVOID lpMsgBuf; int error=GetLastError(); @@ -1715,7 +1725,7 @@ static void start_signal_handler(void) (void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); if (!(opt_specialflag & SPECIAL_NO_PRIOR)) my_pthread_attr_setprio(&thr_attr,INTERRUPT_PRIOR); - pthread_attr_setstacksize(&thr_attr, 129*1024); + pthread_attr_setstacksize(&thr_attr,thread_stack); #endif (void) pthread_mutex_lock(&LOCK_thread_count); @@ -1749,7 +1759,8 @@ extern "C" void *signal_hand(void *arg __attribute__((unused))) This should actually be '+ max_number_of_slaves' instead of +10, but the +10 should be quite safe. */ - init_thr_alarm(max_connections+max_insert_delayed_threads+10); + init_thr_alarm(max_connections + + global_system_variables.max_insert_delayed_threads + 10); #if SIGINT != THR_KILL_SIGNAL if (test_flags & TEST_SIGINT) { @@ -2100,10 +2111,11 @@ static int init_common_variables(const char *conf_file_name, int argc, strmov(fn_ext(pidfile_name),".pid"); // Add proper extension #ifndef DBUG_OFF - strxmov(strend(server_version),MYSQL_SERVER_SUFFIX,"-debug",NullS); -#else - strmov(strend(server_version),MYSQL_SERVER_SUFFIX); + if (!*(MYSQL_SERVER_SUFFIX)) + strmov(strend(server_version),"-debug"); + else #endif + strmov(strend(server_version),MYSQL_SERVER_SUFFIX); load_defaults(conf_file_name, groups, &argc, &argv); defaults_argv=argv; @@ -2113,28 +2125,33 @@ static int init_common_variables(const char *conf_file_name, int argc, DBUG_PRINT("info",("%s Ver %s for %s on %s\n",my_progname, server_version, SYSTEM_TYPE,MACHINE_TYPE)); -#if defined( SET_RLIMIT_NOFILE) || defined( OS2) /* connections and databases needs lots of files */ { - uint wanted_files=10+(uint) max(max_connections*5, - max_connections+table_cache_size*2); + uint files, wanted_files; + + wanted_files= 10+(uint) max(max_connections*5, + max_connections+table_cache_size*2); set_if_bigger(wanted_files, open_files_limit); - // Note that some system returns 0 if we succeed here: - uint files=set_maximum_open_files(wanted_files); - if (files && files < wanted_files && ! open_files_limit) + files= my_set_max_open_files(wanted_files); + + if (files < wanted_files) { - max_connections= (ulong) min((files-10),max_connections); - table_cache_size= (ulong) max((files-10-max_connections)/2,64); - DBUG_PRINT("warning", - ("Changed limits: max_connections: %ld table_cache: %ld", - max_connections,table_cache_size)); - sql_print_error("Warning: Changed limits: max_connections: %ld table_cache: %ld",max_connections,table_cache_size); + if (!open_files_limit) + { + max_connections= (ulong) min((files-10),max_connections); + table_cache_size= (ulong) max((files-10-max_connections)/2,64); + DBUG_PRINT("warning", + ("Changed limits: max_open_files: %u max_connections: %ld table_cache: %ld", + files, max_connections, table_cache_size)); + if (global_system_variables.log_warnings) + sql_print_error("Warning: Changed limits: max_open_files: %u max_connections: %ld table_cache: %ld", + files, max_connections, table_cache_size); + } + else if (global_system_variables.log_warnings) + sql_print_error("Warning: Could not increase number of max_open_files to more than %u (request: %u)", files, wanted_files); } open_files_limit= files; } -#else - open_files_limit= 0; /* Can't set or detect limit */ -#endif unireg_init(opt_specialflag); /* Set up extern variabels */ if (init_errmessage()) /* Read error messages from file */ return 1; @@ -2179,10 +2196,14 @@ static int init_common_variables(const char *conf_file_name, int argc, sys_init_connect.value_length= 0; if ((sys_init_connect.value= opt_init_connect)) sys_init_connect.value_length= strlen(opt_init_connect); + else + sys_init_connect.value=my_strdup("",MYF(0)); sys_init_slave.value_length= 0; if ((sys_init_slave.value= opt_init_slave)) sys_init_slave.value_length= strlen(opt_init_slave); + else + sys_init_slave.value=my_strdup("",MYF(0)); if (use_temp_pool && bitmap_init(&temp_pool,0,1024,1)) return 1; @@ -2209,6 +2230,7 @@ static int init_thread_environment() (void) pthread_mutex_init(&LOCK_user_conn, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_active_mi, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_global_system_variables, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_uuid_generator, MY_MUTEX_INIT_FAST); (void) my_rwlock_init(&LOCK_sys_init_connect, NULL); (void) my_rwlock_init(&LOCK_sys_init_slave, NULL); (void) my_rwlock_init(&LOCK_grant, NULL); @@ -2262,8 +2284,9 @@ static void init_ssl() static int init_server_components() { DBUG_ENTER("init_server_components"); - table_cache_init(); - hostname_cache_init(); + if (table_cache_init() || hostname_cache_init()) + unireg_abort(1); + query_cache_result_size_limit(query_cache_limit); query_cache_set_min_res_unit(query_cache_min_res_unit); query_cache_resize(query_cache_size); @@ -2390,10 +2413,10 @@ Now disabling --log-slave-updates."); { if (mlockall(MCL_CURRENT)) { - sql_print_error("Warning: Failed to lock memory. Errno: %d\n",errno); + if (global_system_variables.log_warnings) + sql_print_error("Warning: Failed to lock memory. Errno: %d\n",errno); + locked_in_memory= 0; } - else - locked_in_memory=1; } #else locked_in_memory=0; @@ -2411,7 +2434,7 @@ static void create_maintenance_thread() { if ( #ifdef HAVE_BERKELEY_DB - !berkeley_skip || + (have_berkeley_db == SHOW_OPTION_YES) || #endif (flush_time && flush_time != ~(ulong) 0L)) { @@ -2557,8 +2580,38 @@ int main(int argc, char **argv) } } #endif + thread_stack_min=thread_stack - STACK_MIN_SIZE; + (void) thr_setconcurrency(concurrency); // 10 by default + /* + Ensure that lower_case_table_names is set on system where we have case + insensitive names. If this is not done the users MyISAM tables will + get corrupted if accesses with names of different case. + */ + DBUG_PRINT("info", ("lower_case_table_names: %d", lower_case_table_names)); + if (!lower_case_table_names && + (lower_case_file_system= + (test_if_case_insensitive(mysql_real_data_home) == 1))) + { + if (lower_case_table_names_used) + { + if (global_system_variables.log_warnings) + sql_print_error("\ +Warning: You have forced lower_case_table_names to 0 through a command-line \ +option, even though your file system '%s' is case insensitive. This means \ +that you can corrupt a MyISAM table by accessing it with different cases. \ +You should consider changing lower_case_table_names to 1 or 2", + mysql_real_data_home); + } + else + { + if (global_system_variables.log_warnings) + sql_print_error("Warning: Setting lower_case_table_names=2 because file system for %s is case insensitive", mysql_real_data_home); + lower_case_table_names= 2; + } + } + select_thread=pthread_self(); select_thread_in_use=1; init_ssl(); @@ -2988,8 +3041,6 @@ static void create_new_thread(THD *thd) DBUG_VOID_RETURN; } pthread_mutex_lock(&LOCK_thread_count); - if (thread_count-delayed_insert_threads > max_used_connections) - max_used_connections=thread_count-delayed_insert_threads; thd->thread_id=thread_id++; thd->real_id=pthread_self(); // Keep purify happy @@ -3018,6 +3069,8 @@ static void create_new_thread(THD *thd) thread_count++; thread_created++; threads.append(thd); + if (thread_count-delayed_insert_threads > max_used_connections) + max_used_connections=thread_count-delayed_insert_threads; DBUG_PRINT("info",(("creating thread %d"), thd->thread_id)); thd->connect_time = time(NULL); if ((error=pthread_create(&thd->real_id,&connection_attrib, @@ -3310,7 +3363,7 @@ extern "C" pthread_handler_decl(handle_connections_namedpipes,arg) if (!fConnected) { CloseHandle( hPipe ); - if ((hPipe = CreateNamedPipe(szPipeName, + if ((hPipe = CreateNamedPipe(pipe_name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | @@ -3328,7 +3381,7 @@ extern "C" pthread_handler_decl(handle_connections_namedpipes,arg) } hConnectedPipe = hPipe; /* create new pipe for new connection */ - if ((hPipe = CreateNamedPipe(szPipeName, + if ((hPipe = CreateNamedPipe(pipe_name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | @@ -3593,7 +3646,7 @@ enum options_mysqld OPT_DELAY_KEY_WRITE_ALL, OPT_SLOW_QUERY_LOG, OPT_DELAY_KEY_WRITE, OPT_CHARSETS_DIR, OPT_BDB_HOME, OPT_BDB_LOG, - OPT_BDB_TMP, OPT_BDB_NOSYNC, + OPT_BDB_TMP, OPT_BDB_SYNC, OPT_BDB_LOCK, OPT_BDB, OPT_BDB_NO_RECOVER, OPT_BDB_SHARED, OPT_MASTER_HOST, OPT_MASTER_USER, @@ -3633,7 +3686,7 @@ enum options_mysqld OPT_HAVE_NAMED_PIPE, OPT_DO_PSTACK, OPT_REPORT_HOST, OPT_REPORT_USER, OPT_REPORT_PASSWORD, OPT_REPORT_PORT, - OPT_SHOW_SLAVE_AUTH_INFO, OPT_OLD_RPL_COMPAT, + OPT_SHOW_SLAVE_AUTH_INFO, OPT_SLAVE_LOAD_TMPDIR, OPT_NO_MIX_TYPE, OPT_RPL_RECOVERY_RANK,OPT_INIT_RPL_ROLE, OPT_RELAY_LOG, OPT_RELAY_LOG_INDEX, OPT_RELAY_LOG_INFO_FILE, @@ -3643,7 +3696,7 @@ enum options_mysqld OPT_BACK_LOG, OPT_BINLOG_CACHE_SIZE, OPT_CONNECT_TIMEOUT, OPT_DELAYED_INSERT_TIMEOUT, OPT_DELAYED_INSERT_LIMIT, OPT_DELAYED_QUEUE_SIZE, - OPT_FLUSH_TIME, OPT_FT_MIN_WORD_LEN, + OPT_FLUSH_TIME, OPT_FT_MIN_WORD_LEN, OPT_FT_BOOLEAN_SYNTAX, OPT_FT_MAX_WORD_LEN, OPT_FT_QUERY_EXPANSION_LIMIT, OPT_FT_STOPWORD_FILE, OPT_INTERACTIVE_TIMEOUT, OPT_JOIN_BUFF_SIZE, OPT_KEY_BUFFER_SIZE, OPT_KEY_CACHE_BLOCK_SIZE, @@ -3665,7 +3718,7 @@ enum options_mysqld OPT_OPEN_FILES_LIMIT, OPT_PRELOAD_BUFFER_SIZE, OPT_QUERY_CACHE_LIMIT, OPT_QUERY_CACHE_MIN_RES_UNIT, OPT_QUERY_CACHE_SIZE, - OPT_QUERY_CACHE_TYPE, OPT_RECORD_BUFFER, + OPT_QUERY_CACHE_TYPE, OPT_QUERY_CACHE_WLOCK_INVALIDATE, OPT_RECORD_BUFFER, OPT_RECORD_RND_BUFFER, OPT_RELAY_LOG_SPACE_LIMIT, OPT_RELAY_LOG_PURGE, OPT_SLAVE_NET_TIMEOUT, OPT_SLAVE_COMPRESSED_PROTOCOL, OPT_SLOW_LAUNCH_TIME, OPT_READONLY, OPT_DEBUGGING, @@ -3694,6 +3747,7 @@ 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_BDB_NOSYNC, OPT_ENABLE_SHARED_MEMORY, OPT_SHARED_MEMORY_BASE_NAME, OPT_OLD_PASSWORDS, @@ -3732,8 +3786,14 @@ struct my_option my_long_options[] = {"bdb-no-recover", OPT_BDB_NO_RECOVER, "Don't try to recover Berkeley DB tables on start.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"bdb-no-sync", OPT_BDB_NOSYNC, "Don't synchronously flush logs.", 0, 0, 0, - GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb-no-sync", OPT_BDB_NOSYNC, + "Disable synchronously flushing logs. This option is deprecated, use --skip-sync-bdb-logs or sync-bdb-logs=0 instead", + // (gptr*) &opt_sync_bdb_logs, (gptr*) &opt_sync_bdb_logs, 0, GET_BOOL, + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"sync-bdb-logs", OPT_BDB_SYNC, + "Synchronously flush logs. Enabled by default", + (gptr*) &opt_sync_bdb_logs, (gptr*) &opt_sync_bdb_logs, 0, GET_BOOL, + NO_ARG, 1, 0, 0, 0, 0, 0}, {"bdb-shared-data", OPT_BDB_SHARED, "Start Berkeley DB in multi-process mode.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -3741,6 +3801,9 @@ struct my_option my_long_options[] = (gptr*) &berkeley_tmpdir, (gptr*) &berkeley_tmpdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif /* HAVE_BERKELEY_DB */ + {"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}, {"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, @@ -3957,7 +4020,8 @@ log and this option justs turns on --log-bin instead.", 0, 0, 0, 0}, {"master-password", OPT_MASTER_PASSWORD, "The password the slave thread will authenticate with when connecting to the master. If not set, an empty password is assumed.The value in master.info will take precedence if it can be read.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + (gptr*)&master_password, (gptr*)&master_password, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"master-port", OPT_MASTER_PORT, "The port the master is listening on. If not set, the compiled setting of MYSQL_PORT is assumed. If you have not tinkered with configure options, this should be 3306. The value in master.info will take precedence if it can be read.", (gptr*) &master_port, (gptr*) &master_port, 0, GET_UINT, REQUIRED_ARG, @@ -4042,10 +4106,6 @@ master-ssl", (gptr*) &global_system_variables.old_passwords, (gptr*) &max_system_variables.old_passwords, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"old-rpl-compat", OPT_OLD_RPL_COMPAT, - "Use old LOAD DATA format in the binary log (don't save data in file).", - (gptr*) &opt_old_rpl_compat, (gptr*) &opt_old_rpl_compat, 0, GET_BOOL, - NO_ARG, 0, 0, 0, 0, 0, 0}, #ifdef ONE_THREAD {"one-thread", OPT_ONE_THREAD, "Only use one thread (for debugging under Linux).", 0, 0, 0, GET_NO_ARG, @@ -4214,7 +4274,7 @@ log and this option does nothing anymore.", 0, 0, 0, 0, 0}, {"tmpdir", 't', "Path for temporary files. Several paths may be specified, separated by a " -#if defined( __WIN__) || defined(OS2) +#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__) "semicolon (;)" #else "colon (:)" @@ -4240,11 +4300,11 @@ log and this option does nothing anymore.", NO_ARG, 0, 0, 0, 0, 0, 0}, {"log-warnings", 'W', "Log some not critical warnings to the log file.", (gptr*) &global_system_variables.log_warnings, - (gptr*) &max_system_variables.log_warnings, 0, GET_BOOL, NO_ARG, 0, 0, 0, + (gptr*) &max_system_variables.log_warnings, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"warnings", 'W', "Deprecated ; Use --log-warnings instead.", (gptr*) &global_system_variables.log_warnings, - (gptr*) &max_system_variables.log_warnings, 0, GET_BOOL, NO_ARG, 0, 0, 0, + (gptr*) &max_system_variables.log_warnings, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, { "back_log", OPT_BACK_LOG, "The number of outstanding connection requests MySQL can have. This comes into play when the main MySQL thread gets very many connection requests in a very short time.", @@ -4292,6 +4352,10 @@ log and this option does nothing anymore.", "A dedicated thread is created to flush all tables at the given interval.", (gptr*) &flush_time, (gptr*) &flush_time, 0, GET_ULONG, REQUIRED_ARG, FLUSH_TIME, 0, LONG_TIMEOUT, 0, 1, 0}, + { "ft_boolean_syntax", OPT_FT_BOOLEAN_SYNTAX, + "List of operators for MATCH ... AGAINST ( ... IN BOOLEAN MODE)", + 0, 0, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, { "ft_min_word_len", OPT_FT_MIN_WORD_LEN, "The minimum length of the word to be included in a FULLTEXT index. Note: FULLTEXT indexes must be rebuilt after changing this variable.", (gptr*) &ft_min_word_len, (gptr*) &ft_min_word_len, 0, GET_ULONG, @@ -4402,15 +4466,15 @@ log and this option does nothing anymore.", (gptr*) &max_system_variables.long_query_time, 0, GET_ULONG, REQUIRED_ARG, 10, 1, LONG_TIMEOUT, 0, 1, 0}, {"lower_case_table_names", OPT_LOWER_CASE_TABLE_NAMES, - "If set to 1 table names are stored in lowercase on disk and table names will be case-insensitive.", + "If set to 1 table names are stored in lowercase on disk and table names will be case-insensitive. Should be set to 2 if you are using a case insensitive file system", (gptr*) &lower_case_table_names, - (gptr*) &lower_case_table_names, 0, GET_BOOL, NO_ARG, + (gptr*) &lower_case_table_names, 0, GET_UINT, OPT_ARG, #ifdef FN_NO_CASE_SENCE 1 #else 0 #endif - , 0, 1, 0, 1, 0}, + , 0, 2, 0, 1, 0}, {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET, "Max packetlength to send/receive from to server.", (gptr*) &global_system_variables.max_allowed_packet, @@ -4436,7 +4500,8 @@ The minimum value for this variable is 4096.", REQUIRED_ARG, MAX_CONNECT_ERRORS, 1, ~0L, 0, 1, 0}, {"max_delayed_threads", OPT_MAX_DELAYED_THREADS, "Don't start more than this number of threads to handle INSERT DELAYED statements. If set to zero, which means INSERT DELAYED is not used.", - (gptr*) &max_insert_delayed_threads, (gptr*) &max_insert_delayed_threads, + (gptr*) &global_system_variables.max_insert_delayed_threads, + (gptr*) &max_system_variables.max_insert_delayed_threads, 0, GET_ULONG, REQUIRED_ARG, 20, 0, 16384, 0, 1, 0}, {"max_error_count", OPT_MAX_ERROR_COUNT, "Max number of errors/warnings to store for a statement.", @@ -4541,7 +4606,7 @@ The minimum value for this variable is 4096.", {"open_files_limit", OPT_OPEN_FILES_LIMIT, "If this is not 0, then mysqld will use this value to reserve file descriptors to use with setrlimit(). If this value is 0 then mysqld will reserve max_connections*5 or max_connections + table_cache*2 (whichever is larger) number of files.", (gptr*) &open_files_limit, (gptr*) &open_files_limit, 0, GET_ULONG, - REQUIRED_ARG, 0, 0, 65535, 0, 1, 0}, + REQUIRED_ARG, 0, 0, OS_FILE_LIMIT, 0, 1, 0}, {"preload_buffer_size", OPT_PRELOAD_BUFFER_SIZE, "The size of the buffer that is allocated when preloading indexes", (gptr*) &global_system_variables.preload_buff_size, @@ -4573,12 +4638,17 @@ The minimum value for this variable is 4096.", (gptr*) &global_system_variables.query_cache_type, (gptr*) &max_system_variables.query_cache_type, 0, GET_ULONG, REQUIRED_ARG, 1, 0, 2, 0, 1, 0}, + {"query_cache_wlock_invalidate", OPT_QUERY_CACHE_WLOCK_INVALIDATE, + "Invalidate queries in query cache on LOCK for write", + (gptr*) &global_system_variables.query_cache_wlock_invalidate, + (gptr*) &max_system_variables.query_cache_wlock_invalidate, + 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0}, +#endif /*HAVE_QUERY_CACHE*/ {"query_prealloc_size", OPT_QUERY_PREALLOC_SIZE, "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}, -#endif /*HAVE_QUERY_CACHE*/ {"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, @@ -4622,7 +4692,7 @@ The minimum value for this variable is 4096.", (gptr*) &max_system_variables.range_alloc_block_size, 0, GET_ULONG, REQUIRED_ARG, RANGE_ALLOC_BLOCK_SIZE, 1024, ~0L, 0, 1024, 0}, {"read-only", OPT_READONLY, - "Make all tables readonly, with the expections for replications (slave) threads and users with the SUPER privilege.", + "Make all tables readonly, with the exception for replication (slave) threads and users with the SUPER privilege", (gptr*) &opt_readonly, (gptr*) &opt_readonly, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0}, @@ -4638,8 +4708,8 @@ The minimum value for this variable is 4096.", 1, 0}, {"table_cache", OPT_TABLE_CACHE, "The number of open tables for all threads.", (gptr*) &table_cache_size, - (gptr*) &table_cache_size, 0, GET_ULONG, REQUIRED_ARG, 64, 1, ~0L, 0, 1, - 0}, + (gptr*) &table_cache_size, 0, GET_ULONG, REQUIRED_ARG, 64, 1, 512*1024L, + 0, 1, 0}, {"thread_concurrency", OPT_THREAD_CONCURRENCY, "Permits the application to give the threads system a hint for the desired number of threads that should be run at the same time.", (gptr*) &concurrency, (gptr*) &concurrency, 0, GET_ULONG, REQUIRED_ARG, @@ -4671,7 +4741,8 @@ The minimum value for this variable is 4096.", "The number of seconds the server waits for activity on a connection before closing it.", (gptr*) &global_system_variables.net_wait_timeout, (gptr*) &max_system_variables.net_wait_timeout, 0, GET_ULONG, - REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT), + 0, 1, 0}, {"expire_logs_days", OPT_EXPIRE_LOGS_DAYS, "Binary logs will be rotated after expire-log-days days ", (gptr*) &expire_logs_days, @@ -4704,6 +4775,8 @@ The minimum value for this variable is 4096.", struct show_var_st status_vars[]= { {"Aborted_clients", (char*) &aborted_threads, SHOW_LONG}, {"Aborted_connects", (char*) &aborted_connects, SHOW_LONG}, + {"Binlog_cache_disk_use", (char*) &binlog_cache_disk_use, SHOW_LONG}, + {"Binlog_cache_use", (char*) &binlog_cache_use, SHOW_LONG}, {"Bytes_received", (char*) &bytes_received, SHOW_LONG}, {"Bytes_sent", (char*) &bytes_sent, SHOW_LONG}, {"Com_admin_commands", (char*) &com_other, SHOW_LONG}, @@ -4758,13 +4831,13 @@ struct show_var_st status_vars[]= { {"Com_savepoint", (char*) (com_stat+(uint) SQLCOM_SAVEPOINT),SHOW_LONG}, {"Com_select", (char*) (com_stat+(uint) SQLCOM_SELECT),SHOW_LONG}, {"Com_set_option", (char*) (com_stat+(uint) SQLCOM_SET_OPTION),SHOW_LONG}, - {"Com_show_binlogs", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOGS),SHOW_LONG}, {"Com_show_binlog_events", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOG_EVENTS),SHOW_LONG}, + {"Com_show_binlogs", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOGS),SHOW_LONG}, {"Com_show_charsets", (char*) (com_stat+(uint) SQLCOM_SHOW_CHARSETS),SHOW_LONG}, {"Com_show_collations", (char*) (com_stat+(uint) SQLCOM_SHOW_COLLATIONS),SHOW_LONG}, {"Com_show_column_types", (char*) (com_stat+(uint) SQLCOM_SHOW_COLUMN_TYPES),SHOW_LONG}, - {"Com_show_create_table", (char*) (com_stat+(uint) SQLCOM_SHOW_CREATE),SHOW_LONG}, {"Com_show_create_db", (char*) (com_stat+(uint) SQLCOM_SHOW_CREATE_DB),SHOW_LONG}, + {"Com_show_create_table", (char*) (com_stat+(uint) SQLCOM_SHOW_CREATE),SHOW_LONG}, {"Com_show_databases", (char*) (com_stat+(uint) SQLCOM_SHOW_DATABASES),SHOW_LONG}, {"Com_show_errors", (char*) (com_stat+(uint) SQLCOM_SHOW_ERRORS),SHOW_LONG}, {"Com_show_fields", (char*) (com_stat+(uint) SQLCOM_SHOW_FIELDS),SHOW_LONG}, @@ -4792,11 +4865,11 @@ struct show_var_st status_vars[]= { {"Com_update_multi", (char*) (com_stat+(uint) SQLCOM_UPDATE_MULTI),SHOW_LONG}, {"Connections", (char*) &thread_id, SHOW_LONG_CONST}, {"Created_tmp_disk_tables", (char*) &created_tmp_disk_tables,SHOW_LONG}, - {"Created_tmp_tables", (char*) &created_tmp_tables, SHOW_LONG}, {"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG}, + {"Created_tmp_tables", (char*) &created_tmp_tables, SHOW_LONG}, + {"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG}, {"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG_CONST}, {"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG}, - {"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG}, {"Flush_commands", (char*) &refresh_version, SHOW_LONG_CONST}, {"Handler_commit", (char*) &ha_commit_count, SHOW_LONG}, {"Handler_delete", (char*) &ha_delete_count, SHOW_LONG}, @@ -4823,24 +4896,24 @@ struct show_var_st status_vars[]= { SHOW_KEY_CACHE_LONG}, {"Max_used_connections", (char*) &max_used_connections, SHOW_LONG}, {"Not_flushed_delayed_rows", (char*) &delayed_rows_in_use, SHOW_LONG_CONST}, - {"Open_tables", (char*) 0, SHOW_OPENTABLES}, {"Open_files", (char*) &my_file_opened, SHOW_LONG_CONST}, {"Open_streams", (char*) &my_stream_opened, SHOW_LONG_CONST}, + {"Open_tables", (char*) 0, SHOW_OPENTABLES}, {"Opened_tables", (char*) &opened_tables, SHOW_LONG}, - {"Questions", (char*) 0, SHOW_QUESTION}, #ifdef HAVE_QUERY_CACHE - {"Qcache_queries_in_cache", (char*) &query_cache.queries_in_cache, SHOW_LONG_CONST}, - {"Qcache_inserts", (char*) &query_cache.inserts, SHOW_LONG}, + {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, + SHOW_LONG_CONST}, + {"Qcache_free_memory", (char*) &query_cache.free_memory, + SHOW_LONG_CONST}, {"Qcache_hits", (char*) &query_cache.hits, SHOW_LONG}, + {"Qcache_inserts", (char*) &query_cache.inserts, SHOW_LONG}, {"Qcache_lowmem_prunes", (char*) &query_cache.lowmem_prunes, SHOW_LONG}, {"Qcache_not_cached", (char*) &query_cache.refused, SHOW_LONG}, - {"Qcache_free_memory", (char*) &query_cache.free_memory, - SHOW_LONG_CONST}, - {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, - SHOW_LONG_CONST}, + {"Qcache_queries_in_cache", (char*) &query_cache.queries_in_cache, SHOW_LONG_CONST}, {"Qcache_total_blocks", (char*) &query_cache.total_blocks, SHOW_LONG_CONST}, #endif /*HAVE_QUERY_CACHE*/ + {"Questions", (char*) 0, SHOW_QUESTION}, {"Rpl_status", (char*) 0, SHOW_RPL_STATUS}, {"Select_full_join", (char*) &select_full_join_count, SHOW_LONG}, {"Select_full_range_join", (char*) &select_full_range_join_count, SHOW_LONG}, @@ -4856,35 +4929,35 @@ struct show_var_st status_vars[]= { {"Sort_rows", (char*) &filesort_rows, SHOW_LONG}, {"Sort_scan", (char*) &filesort_scan_count, SHOW_LONG}, #ifdef HAVE_OPENSSL + {"Ssl_accept_renegotiates", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE}, {"Ssl_accepts", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT}, + {"Ssl_callback_cache_hits", (char*) 0, SHOW_SSL_CTX_SESS_CB_HITS}, + {"Ssl_cipher", (char*) 0, SHOW_SSL_GET_CIPHER}, + {"Ssl_cipher_list", (char*) 0, SHOW_SSL_GET_CIPHER_LIST}, + {"Ssl_client_connects", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT}, + {"Ssl_connect_renegotiates", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE}, + {"Ssl_ctx_verify_depth", (char*) 0, SHOW_SSL_CTX_GET_VERIFY_DEPTH}, + {"Ssl_ctx_verify_mode", (char*) 0, SHOW_SSL_CTX_GET_VERIFY_MODE}, + {"Ssl_default_timeout", (char*) 0, SHOW_SSL_GET_DEFAULT_TIMEOUT}, {"Ssl_finished_accepts", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT_GOOD}, {"Ssl_finished_connects", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT_GOOD}, - {"Ssl_accept_renegotiates", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE}, - {"Ssl_connect_renegotiates", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE}, - {"Ssl_callback_cache_hits", (char*) 0, SHOW_SSL_CTX_SESS_CB_HITS}, {"Ssl_session_cache_hits", (char*) 0, SHOW_SSL_CTX_SESS_HITS}, {"Ssl_session_cache_misses", (char*) 0, SHOW_SSL_CTX_SESS_MISSES}, - {"Ssl_session_cache_timeouts", (char*) 0, SHOW_SSL_CTX_SESS_TIMEOUTS}, - {"Ssl_used_session_cache_entries",(char*) 0, SHOW_SSL_CTX_SESS_NUMBER}, - {"Ssl_client_connects", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT}, + {"Ssl_session_cache_mode", (char*) 0, SHOW_SSL_CTX_GET_SESSION_CACHE_MODE}, {"Ssl_session_cache_overflows", (char*) 0, SHOW_SSL_CTX_SESS_CACHE_FULL}, {"Ssl_session_cache_size", (char*) 0, SHOW_SSL_CTX_SESS_GET_CACHE_SIZE}, - {"Ssl_session_cache_mode", (char*) 0, SHOW_SSL_CTX_GET_SESSION_CACHE_MODE}, + {"Ssl_session_cache_timeouts", (char*) 0, SHOW_SSL_CTX_SESS_TIMEOUTS}, {"Ssl_sessions_reused", (char*) 0, SHOW_SSL_SESSION_REUSED}, - {"Ssl_ctx_verify_mode", (char*) 0, SHOW_SSL_CTX_GET_VERIFY_MODE}, - {"Ssl_ctx_verify_depth", (char*) 0, SHOW_SSL_CTX_GET_VERIFY_DEPTH}, - {"Ssl_verify_mode", (char*) 0, SHOW_SSL_GET_VERIFY_MODE}, + {"Ssl_used_session_cache_entries",(char*) 0, SHOW_SSL_CTX_SESS_NUMBER}, {"Ssl_verify_depth", (char*) 0, SHOW_SSL_GET_VERIFY_DEPTH}, + {"Ssl_verify_mode", (char*) 0, SHOW_SSL_GET_VERIFY_MODE}, {"Ssl_version", (char*) 0, SHOW_SSL_GET_VERSION}, - {"Ssl_cipher", (char*) 0, SHOW_SSL_GET_CIPHER}, - {"Ssl_cipher_list", (char*) 0, SHOW_SSL_GET_CIPHER_LIST}, - {"Ssl_default_timeout", (char*) 0, SHOW_SSL_GET_DEFAULT_TIMEOUT}, #endif /* HAVE_OPENSSL */ {"Table_locks_immediate", (char*) &locks_immediate, SHOW_LONG}, {"Table_locks_waited", (char*) &locks_waited, SHOW_LONG}, {"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_CONST}, - {"Threads_created", (char*) &thread_created, SHOW_LONG_CONST}, {"Threads_connected", (char*) &thread_count, SHOW_INT_CONST}, + {"Threads_created", (char*) &thread_created, SHOW_LONG_CONST}, {"Threads_running", (char*) &thread_running, SHOW_INT_CONST}, {"Uptime", (char*) 0, SHOW_STARTTIME}, {NullS, NullS, SHOW_LONG} @@ -4998,6 +5071,7 @@ static void mysql_init_variables(void) filesort_merge_passes= select_range_check_count= select_range_count= 0; select_scan_count= select_full_range_join_count= select_full_join_count= 0; specialflag= opened_tables= created_tmp_tables= created_tmp_disk_tables= 0; + binlog_cache_use= binlog_cache_disk_use= 0; max_used_connections= slow_launch_threads = 0; max_sort_char= 0; mysqld_user= mysqld_chroot= opt_init_file= opt_bin_logname = 0; @@ -5469,8 +5543,8 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; case OPT_STORAGE_ENGINE: { - if ((enum db_type)((global_system_variables.table_type= - ha_resolve_by_name(argument, strlen(argument)))) == DB_TYPE_UNKNOWN) + if ((enum db_type)((global_system_variables.table_type= + ha_resolve_by_name(argument, strlen(argument)))) == DB_TYPE_UNKNOWN) { fprintf(stderr,"Unknown table type: %s\n",argument); exit(1); @@ -5517,7 +5591,14 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), } #ifdef HAVE_BERKELEY_DB case OPT_BDB_NOSYNC: - berkeley_env_flags|=DB_TXN_NOSYNC; + /* Deprecated option */ + opt_sync_bdb_logs= 0; + /* Fall through */ + case OPT_BDB_SYNC: + if (!opt_sync_bdb_logs) + berkeley_env_flags|= DB_TXN_NOSYNC; + else + berkeley_env_flags&= ~DB_TXN_NOSYNC; break; case OPT_BDB_NO_RECOVER: berkeley_init_flags&= ~(DB_RECOVER); @@ -5551,43 +5632,25 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case OPT_BDB: #ifdef HAVE_BERKELEY_DB if (opt_bdb) - { - berkeley_skip=0; have_berkeley_db=SHOW_OPTION_YES; - } else - { - berkeley_skip=1; have_berkeley_db=SHOW_OPTION_DISABLED; - } #endif break; case OPT_ISAM: #ifdef HAVE_ISAM if (opt_isam) - { - isam_skip=0; have_isam= SHOW_OPTION_YES; - } else - { - isam_skip=1; have_isam= SHOW_OPTION_DISABLED; - } #endif break; case OPT_INNODB: #ifdef HAVE_INNOBASE_DB if (opt_innodb) - { - innodb_skip=0; have_innodb=SHOW_OPTION_YES; - } else - { - innodb_skip=1; have_innodb=SHOW_OPTION_DISABLED; - } #endif break; case OPT_INNODB_DATA_FILE_PATH: @@ -5634,15 +5697,25 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), } global_system_variables.sql_mode= fix_sql_mode(global_system_variables. sql_mode); + break; } - case OPT_MASTER_PASSWORD: - master_password=argument; + case OPT_FT_BOOLEAN_SYNTAX: + if (ft_boolean_check_syntax_string((byte*) argument)) + { + fprintf(stderr, "Invalid ft-boolean-syntax string: %s\n", argument); + exit(1); + } + strmake(opt_ft_boolean_syntax, argument, sizeof(ft_boolean_syntax)-1); break; case OPT_SKIP_SAFEMALLOC: #ifdef SAFEMALLOC sf_malloc_quick=1; #endif break; + case OPT_LOWER_CASE_TABLE_NAMES: + lower_case_table_names= argument ? atoi(argument) : 1; + lower_case_table_names_used= 1; + break; } return 0; } @@ -5682,6 +5755,8 @@ static void get_options(int argc,char **argv) int ho_error; my_getopt_register_get_addr(mysql_getopt_value); + strmake(opt_ft_boolean_syntax, ft_boolean_syntax, + sizeof(ft_boolean_syntax)-1); if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option))) exit(ho_error); if (argc > 0) @@ -5737,6 +5812,9 @@ static void get_options(int argc,char **argv) table_alias_charset= (lower_case_table_names ? files_charset_info : &my_charset_bin); + strmake(ft_boolean_syntax, opt_ft_boolean_syntax, + sizeof(ft_boolean_syntax)-1); + if (opt_short_log_format) opt_specialflag|= SPECIAL_SHORT_LOG_FORMAT; if (opt_log_queries_not_using_indexes) @@ -5833,108 +5911,7 @@ static void fix_paths(void) exit(1); } #endif /* HAVE_REPLICATION */ - - /* - Ensure that lower_case_table_names is set on system where we have case - insensitive names. If this is not done the users MyISAM tables will - get corrupted if accesses with names of different case. - */ - if (!lower_case_table_names && - test_if_case_insensitive(mysql_real_data_home) == 1) - { - sql_print_error("Warning: Setting lower_case_table_names=1 becasue file system %s is case insensitive", mysql_real_data_home); - lower_case_table_names= 1; - } -} - - -/* - set how many open files we want to be able to handle - - SYNOPSIS - set_maximum_open_files() - max_file_limit Files to open - - NOTES - The request may not fulfilled becasue of system limitations - - RETURN - Files available to open -*/ - -#ifdef SET_RLIMIT_NOFILE - -#ifndef RLIM_INFINITY -#define RLIM_INFINITY ((uint) 0xffffffff) -#endif - -static uint set_maximum_open_files(uint max_file_limit) -{ - struct rlimit rlimit; - uint old_cur; - DBUG_ENTER("set_maximum_open_files"); - DBUG_PRINT("enter",("files: %u", max_file_limit)); - - if (!getrlimit(RLIMIT_NOFILE,&rlimit)) - { - old_cur= (uint) rlimit.rlim_cur; - DBUG_PRINT("info", ("rlim_cur: %u rlim_max: %u", - (uint) rlimit.rlim_cur, - (uint) rlimit.rlim_max)); - if (rlimit.rlim_cur >= max_file_limit || - rlimit.rlim_cur == RLIM_INFINITY) - DBUG_RETURN(rlimit.rlim_cur); /* purecov: inspected */ - rlimit.rlim_cur= rlimit.rlim_max= max_file_limit; - if (setrlimit(RLIMIT_NOFILE,&rlimit)) - { - if (global_system_variables.log_warnings) - sql_print_error("Warning: setrlimit couldn't increase number of open files to more than %u (request: %u)", - old_cur, max_file_limit); /* purecov: inspected */ - max_file_limit= old_cur; - } - else - { - rlimit.rlim_cur= 0; // Safety if next call fails - (void) getrlimit(RLIMIT_NOFILE,&rlimit); - DBUG_PRINT("info", ("rlim_cur: %u", (uint) rlimit.rlim_cur)); - if ((uint) rlimit.rlim_cur < max_file_limit && - global_system_variables.log_warnings) - sql_print_error("Warning: setrlimit returned ok, but didn't change limits. Max open files is %u (request: %u)", - (uint) rlimit.rlim_cur, - max_file_limit); /* purecov: inspected */ - max_file_limit= (uint) rlimit.rlim_cur; - } - } - DBUG_PRINT("exit",("max_file_limit: %u", max_file_limit)); - DBUG_RETURN(max_file_limit); -} -#endif - - -#ifdef OS2 -static uint set_maximum_open_files(uint max_file_limit) -{ - LONG cbReqCount; - ULONG cbCurMaxFH, cbCurMaxFH0; - APIRET ulrc; - DBUG_ENTER("set_maximum_open_files"); - - // get current limit - cbReqCount = 0; - DosSetRelMaxFH( &cbReqCount, &cbCurMaxFH0); - - // set new limit - cbReqCount = max_file_limit - cbCurMaxFH0; - ulrc = DosSetRelMaxFH( &cbReqCount, &cbCurMaxFH); - if (ulrc) { - sql_print_error("Warning: DosSetRelMaxFH couldn't increase number of open files to more than %d", - cbCurMaxFH0); - cbCurMaxFH = cbCurMaxFH0; - } - - DBUG_RETURN(cbCurMaxFH); } -#endif /* @@ -6017,6 +5994,7 @@ static int test_if_case_insensitive(const char *dir_name) File file; char buff[FN_REFLEN], buff2[FN_REFLEN]; MY_STAT stat_info; + DBUG_ENTER("test_if_case_insensitive"); fn_format(buff, glob_hostname, dir_name, ".lower-test", MY_UNPACK_FILENAME | MY_REPLACE_EXT | MY_REPLACE_DIR); @@ -6026,13 +6004,14 @@ static int test_if_case_insensitive(const char *dir_name) if ((file= my_create(buff, 0666, O_RDWR, MYF(0))) < 0) { sql_print_error("Warning: Can't create test file %s", buff); - return -1; + DBUG_RETURN(-1); } my_close(file, MYF(0)); if (my_stat(buff2, &stat_info, MYF(0))) result= 1; // Can access file (void) my_delete(buff, MYF(MY_WME)); - return result; + DBUG_PRINT("exit", ("result: %d", result)); + DBUG_RETURN(result); } diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc index 3e9d68d5fdb..a04f284a3de 100644 --- a/sql/nt_servc.cc +++ b/sql/nt_servc.cc @@ -498,7 +498,7 @@ BOOL NTService::IsService(LPCSTR ServiceName) if ((scm= OpenSCManager(0, 0,SC_MANAGER_ENUMERATE_SERVICE))) { - if ((service = OpenService(scm,ServiceName, SERVICE_QUERY_STATUS ))) + if ((service = OpenService(scm,ServiceName, SERVICE_QUERY_STATUS))) { ret_value=TRUE; CloseServiceHandle(service); diff --git a/sql/opt_range.cc b/sql/opt_range.cc index be1e764ddba..3907ba866fe 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -296,6 +296,7 @@ typedef struct st_qsel_param { char min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH], max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; bool quick; // Don't calulate possible keys + COND *cond; uint *imerge_cost_buff; /* buffer for index_merge cost estimates */ uint imerge_cost_buff_size; /* size of the buffer */ @@ -807,6 +808,7 @@ SEL_ARG *SEL_ARG::clone(SEL_ARG *new_parent,SEL_ARG **next_arg) return 0; // OOM } increment_use_count(1); + tmp->color= color; return tmp; } @@ -969,7 +971,6 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, param.keys=0; param.mem_root= &alloc; param.imerge_cost_buff_size= 0; - thd->no_errors=1; // Don't warn about NULL init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0); if (!(param.key_parts = (KEY_PART*) alloc_root(&alloc, @@ -1570,6 +1571,8 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) if (cond_func->select_optimize() == Item_func::OPTIMIZE_NONE) DBUG_RETURN(0); // Can't be calculated + param->cond= cond; + if (cond_func->functype() == Item_func::BETWEEN) { if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM) @@ -1794,14 +1797,15 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, max_str[0]= min_str[0]=0; like_error= my_like_range(field->charset(), - res->ptr(),res->length(), - wild_prefix,wild_one,wild_many, - field_length, - min_str+offset, max_str+offset, - &min_length,&max_length); - + res->ptr(), res->length(), + ((Item_func_like*)(param->cond))->escape, + wild_one, wild_many, + field_length, + min_str+offset, max_str+offset, + &min_length, &max_length); if (like_error) // Can't optimize with LIKE DBUG_RETURN(0); + if (offset != maybe_null) // Blob { int2store(min_str+maybe_null,min_length); @@ -1824,7 +1828,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, field->cmp_type() != value->result_type()) DBUG_RETURN(0); - if (value->save_in_field(field, 1) > 0) + if (value->save_in_field(field, 1) < 0) { /* This happens when we try to insert a NULL field in a not null column */ DBUG_RETURN(&null_element); // cmp with NULL is never true @@ -2536,6 +2540,8 @@ key_or(SEL_ARG *key1,SEL_ARG *key2) return 0; // OOM tmp->copy_max_to_min(&key); tmp->increment_use_count(key1->use_count+1); + /* Increment key count as it may be used for next loop */ + key.increment_use_count(1); new_arg->next_key_part=key_or(tmp->next_key_part,key.next_key_part); key1=key1->insert(new_arg); break; @@ -2967,6 +2973,7 @@ static ulong count_key_part_usage(SEL_ARG *root, SEL_ARG *key) void SEL_ARG::test_use_count(SEL_ARG *root) { + uint e_count=0; if (this == root && use_count != 1) { sql_print_error("Note: Use_count: Wrong count %lu for root",use_count); @@ -2974,7 +2981,6 @@ void SEL_ARG::test_use_count(SEL_ARG *root) } if (this->type != SEL_ARG::KEY_RANGE) return; - uint e_count=0; for (SEL_ARG *pos=first(); pos ; pos=pos->next) { e_count++; @@ -2991,8 +2997,8 @@ void SEL_ARG::test_use_count(SEL_ARG *root) } } if (e_count != elements) - sql_print_error("Warning: Wrong use count: %u for tree at %lx", e_count, - (gptr) this); + sql_print_error("Warning: Wrong use count: %u (should be %u) for tree at %lx", + e_count, elements, (gptr) this); } #endif @@ -3260,9 +3266,9 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key, } /* Get range for retrieving rows in QUICK_SELECT::get_next */ - if (!(range= new QUICK_RANGE(param->min_key, + if (!(range= new QUICK_RANGE((const char *) param->min_key, (uint) (tmp_min_key - param->min_key), - param->max_key, + (const char *) param->max_key, (uint) (tmp_max_key - param->max_key), flag))) return 1; // out of memory @@ -3383,8 +3389,8 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, QUICK_RANGE *null_range; *ref->null_ref_key= 1; // Set null byte then create a range - if (!(null_range= new QUICK_RANGE(ref->key_buff, ref->key_length, - ref->key_buff, ref->key_length, + if (!(null_range= new QUICK_RANGE((char*)ref->key_buff, ref->key_length, + (char*)ref->key_buff, ref->key_length, EQ_RANGE))) goto err; *ref->null_ref_key= 0; // Clear null byte @@ -3791,15 +3797,17 @@ int QUICK_SELECT_DESC::get_next() ((range->flag & NEAR_MAX) ? HA_READ_BEFORE_KEY : HA_READ_PREFIX_LAST_OR_PREV)); #else - /* Heikki changed Sept 11, 2002: since InnoDB does not store the cursor - position if READ_KEY_EXACT is used to a primary key with all - key columns specified, we must use below HA_READ_KEY_OR_NEXT, - so that InnoDB stores the cursor position and is able to move - the cursor one step backward after the search. */ - - /* Note: even if max_key is only a prefix, HA_READ_AFTER_KEY will - * do the right thing - go past all keys which match the prefix */ - + /* + Heikki changed Sept 11, 2002: since InnoDB does not store the cursor + position if READ_KEY_EXACT is used to a primary key with all + key columns specified, we must use below HA_READ_KEY_OR_NEXT, + so that InnoDB stores the cursor position and is able to move + the cursor one step backward after the search. + */ + /* + Note: even if max_key is only a prefix, HA_READ_AFTER_KEY will + do the right thing - go past all keys which match the prefix + */ result=file->index_read(record, (byte*) range->max_key, range->max_length, ((range->flag & NEAR_MAX) ? diff --git a/sql/protocol.cc b/sql/protocol.cc index 4be74c74134..dafcabe9e3a 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -175,10 +175,10 @@ net_printf(THD *thd, uint errcode, ...) const char *format; #ifndef EMBEDDED_LIBRARY const char *text_pos; + int head_length= NET_HEADER_SIZE; #else char text_pos[1024]; #endif - int head_length= NET_HEADER_SIZE; NET *net= &thd->net; DBUG_ENTER("net_printf"); @@ -543,7 +543,10 @@ bool Protocol::send_fields(List<Item> *list, uint flag) /* Store fixed length fields */ pos= (char*) local_packet->ptr()+local_packet->length(); *pos++= 12; // Length of packed fields - int2store(pos, field.charsetnr); + if (item->collation.collation == &my_charset_bin || thd_charset == NULL) + int2store(pos, field.charsetnr); + else + int2store(pos, thd_charset->number); int4store(pos+2, field.length); pos[6]= field.type; int2store(pos+7,field.flags); diff --git a/sql/protocol.h b/sql/protocol.h index d2ec08c3cf4..52b78a749b3 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -176,8 +176,3 @@ char *net_store_data(char *to,const char *from, uint length); char *net_store_data(char *to,int32 from); char *net_store_data(char *to,longlong from); -#ifdef EMBEDDED_LIBRARY -bool setup_params_data(struct st_prep_stmt *stmt); -bool setup_params_data_withlog(struct st_prep_stmt *stmt); -#endif - diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index d125c95e839..f254ffb3df3 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -736,6 +736,7 @@ static int fetch_db_tables(THD *thd, MYSQL *mysql, const char *db, table.db= (char*) db; table.real_name= (char*) table_name; table.updating= 1; + if (!tables_ok(thd, &table)) continue; } @@ -768,7 +769,7 @@ int load_master_data(THD* thd) We do not want anyone messing with the slave at all for the entire duration of the data load. */ - LOCK_ACTIVE_MI; + pthread_mutex_lock(&LOCK_active_mi); lock_slave_threads(active_mi); init_thread_mask(&restart_thread_mask,active_mi,0 /*not inverse*/); if (restart_thread_mask && @@ -777,7 +778,7 @@ int load_master_data(THD* thd) { send_error(thd,error); unlock_slave_threads(active_mi); - UNLOCK_ACTIVE_MI; + pthread_mutex_unlock(&LOCK_active_mi); return 1; } @@ -895,7 +896,7 @@ int load_master_data(THD* thd) cleanup_mysql_results(db_res, cur_table_res - 1, table_res); - // adjust position in the master + // adjust replication coordinates from the master if (master_status_res) { MYSQL_ROW row = mysql_fetch_row(master_status_res); @@ -908,10 +909,19 @@ int load_master_data(THD* thd) */ if (row && row[0] && row[1]) { + /* + If the slave's master info is not inited, we init it, then we write + the new coordinates to it. Must call init_master_info() *before* + setting active_mi, because init_master_info() sets active_mi with + defaults. + */ + if (init_master_info(active_mi, master_info_file, relay_log_info_file, + 0)) + send_error(thd, ER_MASTER_INFO); strmake(active_mi->master_log_name, row[0], sizeof(active_mi->master_log_name)); active_mi->master_log_pos = strtoull(row[1], (char**) 0, 10); - // don't hit the magic number + /* at least in recent versions, the condition below should be false */ if (active_mi->master_log_pos < BIN_LOG_HEADER_SIZE) active_mi->master_log_pos = BIN_LOG_HEADER_SIZE; /* @@ -938,7 +948,7 @@ int load_master_data(THD* thd) { send_error(thd, 0, "Failed purging old relay logs"); unlock_slave_threads(active_mi); - UNLOCK_ACTIVE_MI; + pthread_mutex_unlock(&LOCK_active_mi); return 1; } pthread_mutex_lock(&active_mi->rli.data_lock); @@ -969,7 +979,7 @@ int load_master_data(THD* thd) err: unlock_slave_threads(active_mi); - UNLOCK_ACTIVE_MI; + pthread_mutex_unlock(&LOCK_active_mi); thd->proc_info = 0; mysql_close(&mysql); // safe to call since we always do mysql_init() diff --git a/sql/set_var.h b/sql/set_var.h index 85871c90ebb..1cac2953a21 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -35,7 +35,7 @@ enum enum_var_type OPT_DEFAULT, OPT_SESSION, OPT_GLOBAL }; -typedef bool (*sys_check_func)(THD *, set_var *); +typedef int (*sys_check_func)(THD *, set_var *); typedef bool (*sys_update_func)(THD *, set_var *); typedef void (*sys_after_update_func)(THD *,enum_var_type); typedef void (*sys_set_default_func)(THD *, enum_var_type); @@ -139,14 +139,12 @@ public: sys_var_str(const char *name_arg, sys_check_func check_func_arg, sys_update_func update_func_arg, - sys_set_default_func set_default_func_arg) - :sys_var(name_arg), check_func(check_func_arg), + sys_set_default_func set_default_func_arg, + char *value_arg) + :sys_var(name_arg), value(value_arg), check_func(check_func_arg), update_func(update_func_arg),set_default_func(set_default_func_arg) {} - bool check(THD *thd, set_var *var) - { - return check_func ? (*check_func)(thd, var) : 0; - } + bool check(THD *thd, set_var *var); bool update(THD *thd, set_var *var) { return (*update_func)(thd, var); @@ -166,6 +164,34 @@ public: }; +class sys_var_const_str :public sys_var +{ +public: + char *value; // Pointer to const value + sys_var_const_str(const char *name_arg, const char *value_arg) + :sys_var(name_arg), value((char*) value_arg) + {} + bool check(THD *thd, set_var *var) + { + return 1; + } + bool update(THD *thd, set_var *var) + { + return 1; + } + SHOW_TYPE type() { return SHOW_CHAR; } + byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) + { + return (byte*) value; + } + bool check_update_type(Item_result type) + { + return 1; + } + bool check_default(enum_var_type type) { return 1; } +}; + + class sys_var_enum :public sys_var { uint *value; @@ -205,30 +231,23 @@ public: class sys_var_thd_ulong :public sys_var_thd { + sys_check_func check_func; public: ulong SV::*offset; sys_var_thd_ulong(const char *name_arg, ulong SV::*offset_arg) - :sys_var_thd(name_arg), offset(offset_arg) + :sys_var_thd(name_arg), check_func(0), offset(offset_arg) {} sys_var_thd_ulong(const char *name_arg, ulong SV::*offset_arg, - sys_after_update_func func) - :sys_var_thd(name_arg,func), offset(offset_arg) + sys_check_func c_func, sys_after_update_func au_func) + :sys_var_thd(name_arg,au_func), check_func(c_func), offset(offset_arg) {} + 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; } byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; -class sys_var_pseudo_thread_id :public sys_var_thd_ulong -{ -public: - sys_var_pseudo_thread_id(const char *name_arg, ulong SV::*offset_arg) - :sys_var_thd_ulong(name_arg, offset_arg) - {} - bool check(THD *thd, set_var *var); -}; - class sys_var_thd_ha_rows :public sys_var_thd { @@ -376,19 +395,18 @@ public: class sys_var_thd_bit :public sys_var_thd { + sys_check_func check_func; sys_update_func update_func; public: ulong bit_flag; bool reverse; - sys_var_thd_bit(const char *name_arg, sys_update_func func, ulong bit, - bool reverse_arg=0) - :sys_var_thd(name_arg), update_func(func), bit_flag(bit), - reverse(reverse_arg) + sys_var_thd_bit(const char *name_arg, + sys_check_func c_func, sys_update_func u_func, + ulong bit, bool reverse_arg=0) + :sys_var_thd(name_arg), check_func(c_func), update_func(u_func), + bit_flag(bit), reverse(reverse_arg) {} - bool check(THD *thd, set_var *var) - { - return check_enum(thd, var, &bool_typelib); - } + bool check(THD *thd, set_var *var); bool update(THD *thd, set_var *var); bool check_update_type(Item_result type) { return 0; } bool check_type(enum_var_type type) { return type == OPT_GLOBAL; } @@ -487,7 +505,7 @@ public: sys_var_character_set(const char *name_arg) :sys_var_thd(name_arg) { nullable= 0; } bool check(THD *thd, set_var *var); -SHOW_TYPE type() { return SHOW_CHAR; } + SHOW_TYPE type() { return SHOW_CHAR; } bool check_update_type(Item_result type) { return type != STRING_RESULT; /* Only accept strings */ diff --git a/sql/share/charsets/Index.xml b/sql/share/charsets/Index.xml index f21e03de8b2..bb4b6bd24af 100644 --- a/sql/share/charsets/Index.xml +++ b/sql/share/charsets/Index.xml @@ -464,9 +464,6 @@ To make maintaining easier please: <order>Sorbian</order> </collation> <collation name="macce_bin" id="43" order="Binary" flag="binary"/> - <!--collation name="macce_ci_ai" id="44"/--> - <!--collation name="macce_ci" id="45"/--> - <!--collation name="macce_cs" id="46"/--> </charset> <charset name="macroman"> diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index dd3ea82790a..423c3090b8a 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -240,7 +240,7 @@ character-set=latin2 "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -250,6 +250,7 @@ character-set=latin2 "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -273,7 +274,7 @@ character-set=latin2 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -285,8 +286,8 @@ character-set=latin2 "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -298,10 +299,14 @@ character-set=latin2 "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%s' is deprecated, use '%s' instead", +"The target table %-.100s of the %s is not updatable", +"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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index c1471c78c7e..05c34236d03 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -207,7 +207,7 @@ character-set=latin1 "Denne handling kunne ikke udføres med kørende slave, brug først kommandoen STOP SLAVE", "Denne handling kræver en kørende slave. Konfigurer en slave og brug kommandoen START SLAVE", "Denne server er ikke konfigureret som slave. Ret in config-filen eller brug kommandoen CHANGE MASTER TO", -"Kunne ikke initialisere master info-struktur. Check om rettigheder i master.info", +"Could not initialize master info structure, more error messages can be found in the MySQL error log", "Kunne ikke danne en slave-tråd. Check systemressourcerne", "Brugeren %-.64s har allerede mere end 'max_user_connections' aktive forbindelser", "Du må kun bruge konstantudtryk med SET", @@ -234,7 +234,7 @@ character-set=latin1 "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -244,6 +244,7 @@ character-set=latin1 "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -267,6 +268,39 @@ character-set=latin1 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(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 when MySQL slave with SSL will be 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", +"Wrong 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' field", "Row %ld was truncated; It contained more data than there where 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", diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 0f1f9f65e99..03174c83657 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -215,7 +215,7 @@ character-set=latin1 "Deze operatie kan niet worden uitgevoerd met een actieve slave, doe eerst STOP SLAVE", "Deze operatie vereist een actieve slave, configureer slave en doe dan START SLAVE", "De server is niet geconfigureerd als slave, fix in configuratie bestand of met CHANGE MASTER TO", -"Kon master info structuur niet initialiseren, controleer permissies in master.info", +"Could not initialize master info structure, more error messages can be found in the MySQL error log", "Kon slave thread niet aanmaken, controleer systeem resources", "Gebruiker %-.64s heeft reeds meer dan 'max_user_connections' actieve verbindingen", "U mag alleen constante expressies gebruiken bij SET", @@ -242,7 +242,7 @@ character-set=latin1 "Optie '%s' tweemaal gebruikt in opdracht", "Gebruiker '%-.64s' heeft het maximale gebruik van de '%s' faciliteit overschreden (huidige waarde: %ld)", "Toegang geweigerd. U moet het %-.128s privilege hebben voor deze operatie", -"Variabele '%-.64s' is LOCAL en kan niet worden gebruikt met SET GLOBAL", +"Variabele '%-.64s' is SESSION en kan niet worden gebruikt met SET GLOBAL", "Variabele '%-.64s' is GLOBAL en dient te worden gewijzigd met SET GLOBAL", "Variabele '%-.64s' heeft geen standaard waarde", "Variabele '%-.64s' kan niet worden gewijzigd naar de waarde '%-.64s'", @@ -252,6 +252,7 @@ character-set=latin1 "Deze versie van MySQL ondersteunt nog geen '%s'", "Kreeg fatale fout %d: '%-.128s' van master tijdens lezen van data uit binaire log", "Slave SQL thread ignored the query because of replicate-*-table rules", +"Variable '%-.64s' is a %s variable", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -275,6 +276,39 @@ character-set=latin1 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(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 when MySQL slave with SSL will be 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", +"Wrong 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' field", "Row %ld was truncated; It contained more data than there where 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", diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 2d5757977a4..f475d270a54 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -72,9 +72,9 @@ character-set=latin1 "Not unique table/alias: '%-.64s'", "Invalid default value for '%-.64s'", "Multiple primary key defined", -"Too many keys specified. Max %d keys allowed", +"Too many keys specified; max %d keys allowed", "Too many key parts specified. Max %d parts allowed", -"Specified key was too long. Max key length is %d", +"Specified key was too long; max key length is %d bytes", "Key column '%-.64s' doesn't exist in table", "BLOB column '%-.64s' can't be used in key specification with the used table type", "Too big column length for column '%-.64s' (max = %d). Use BLOB instead", @@ -231,7 +231,7 @@ character-set=latin1 "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -241,6 +241,7 @@ character-set=latin1 "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -259,6 +260,44 @@ character-set=latin1 "Slave is already running", "Slave has already been stopped", "Too big size of uncompressed data. 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) was(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 when MySQL slave with SSL will be 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", +"Wrong parameter or combination of parameters for START SLAVE UNTIL", +"It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL. Otherwise you will get problems if you get an 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 updatable", +"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' field", "ZLIB: Not enough memory available for zlib", "ZLIB: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", "ZLIB: Input data was corrupted for zlib", diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index e610121e278..161e2c6863d 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -236,7 +236,7 @@ character-set=latin7 "Määrangut '%s' on lauses kasutatud topelt", "User '%-.64s' has exceeded the '%s' resource (current value: %ld)", "Access denied. You need the %-.128s privilege for this operation", -"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -246,6 +246,7 @@ character-set=latin7 "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -269,6 +270,39 @@ character-set=latin7 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(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 when MySQL slave with SSL will be 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", +"Wrong 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' field", "Row %ld was truncated; It contained more data than there where 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", diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index 04310561c90..2fb86fbf917 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -231,7 +231,7 @@ character-set=latin1 "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -241,6 +241,7 @@ character-set=latin1 "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -264,7 +265,7 @@ character-set=latin1 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -276,8 +277,8 @@ character-set=latin1 "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -289,10 +290,14 @@ character-set=latin1 "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index ccbb0ced3d3..3e4eaa445f8 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -216,7 +216,7 @@ character-set=latin1 "Diese Operation kann nicht bei einem aktiven Slave durchgeführt werden. Bitte zuerst STOP SLAVE ausführen", "Diese Operation benötigt einen aktiven Slave. Bitte Slave konfigurieren und mittels START SLAVE aktivieren", "Der Server ist nicht als Slave konfiguriert. Bitte in der Konfigurationsdatei oder mittels CHANGE MASTER TO beheben", -"Konnte Master-Info-Struktur nicht initialisieren. Bitte Berechtigungen von master.info prüfen", +"Could not initialize master info structure, more error messages can be found in the MySQL error log", "Konnte keinen Slave-Thread starten. Bitte System-Ressourcen überprüfen", "Benutzer '%-.64s' hat mehr als max_user_connections aktive Verbindungen", "Bei SET dürfen nur konstante Ausdrücke verwendet werden", @@ -253,6 +253,7 @@ character-set=latin1 "Diese MySQL-Version unterstützt '%s' nicht", "Schwerer Fehler %d: '%-.128s vom Master beim Lesen des binären Logs aufgetreten", "Slave-SQL-Thread hat die Abfrage aufgrund von replicate-*-table-Regeln ignoriert", +"Variable '%-.64s' is a %s variable", "Falsche Fremdschlüssel-Definition für '%-64s': %s", "Schlüssel- und Tabellenverweis passen nicht zusammen", "Operand solle %d Spalte(n) enthalten", @@ -289,7 +290,7 @@ character-set=latin1 "Variable '%-.64s' ist keine Variablen-Komponenten (kann nicht als XXXX.variablen_name verwendet werden)", "Unbekannte Kollation: '%-.64s'", "SSL-Parameter in CHANGE MASTER werden ignoriert, weil dieser MySQL-Slave ohne SSL-Unterstützung kompiliert wurde. Sie können aber später verwendet werden, wenn der MySQL-Slave mit SSL gestartet wird", -"Server läuft im Modus --secure-auth, aber '%s@%s' hat ein Passwort im alten Format. Bitte Passwort ins neue Format ändern", +"Server läuft im Modus --secure-auth, aber '%s'@'%s' hat ein Passwort im alten Format. Bitte Passwort ins neue Format ändern", "Feld oder Verweis '%-.64s%s%-.64s%s%-.64s' im SELECT-Befehl Nr. %d wurde im SELECT-Befehl Nr. %d aufgelöst", "Falscher Parameter oder falsche Kombination von Parametern für START SLAVE UNTIL", "Es wird empfohlen, mit --skip-slave-start zu starten, wenn mit START SLAVE UNTIL eine Schritt-für-Schritt-Replikation ausgeführt wird. Ansonsten gibt es Probleme, wenn der Slave-Server unerwartet neu startet", @@ -301,10 +302,14 @@ character-set=latin1 "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index f7294fc0697..c287c5fdc9d 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -231,7 +231,7 @@ character-set=greek "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -241,6 +241,7 @@ character-set=greek "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -264,7 +265,7 @@ character-set=greek "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -276,8 +277,8 @@ character-set=greek "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -289,10 +290,14 @@ character-set=greek "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index a127f35584d..80e84c2da72 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -233,7 +233,7 @@ character-set=latin2 "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -243,6 +243,7 @@ character-set=latin2 "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -266,7 +267,7 @@ character-set=latin2 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -278,8 +279,8 @@ character-set=latin2 "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -291,10 +292,14 @@ character-set=latin2 "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index e8225c6ba48..ef41c493622 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -204,7 +204,7 @@ character-set=latin1 "Questa operazione non puo' essere eseguita con un database 'slave' che gira, lanciare prima STOP SLAVE", "Questa operaione richiede un database 'slave', configurarlo ed eseguire START SLAVE", "Il server non e' configurato come 'slave', correggere il file di configurazione cambiando CHANGE MASTER TO", -"Impossibile inizializzare la struttura 'master info', controllare i permessi sul file master.info", +"Could not initialize master info structure, more error messages can be found in the MySQL error log", "Impossibile creare il thread 'slave', controllare le risorse di sistema", "L'utente %-.64s ha gia' piu' di 'max_user_connections' connessioni attive", "Si possono usare solo espressioni costanti con SET", @@ -231,7 +231,7 @@ character-set=latin1 "L'opzione '%s' e' stata usata due volte nel comando", "L'utente '%-.64s' ha ecceduto la risorsa '%s' (valore corrente: %ld)", "Accesso non consentito. Serve il privilegio %-.128s per questa operazione", -"La variabile '%-.64s' e' una variabile locale ( LOCAL ) e non puo' essere cambiata usando SET GLOBAL", +"La variabile '%-.64s' e' una variabile locale ( SESSION ) e non puo' essere cambiata usando SET GLOBAL", "La variabile '%-.64s' e' una variabile globale ( GLOBAL ) e deve essere cambiata usando SET GLOBAL", "La variabile '%-.64s' non ha un valore di default", "Alla variabile '%-.64s' non puo' essere assegato il valore '%-.64s'", @@ -241,6 +241,7 @@ character-set=latin1 "Questa versione di MySQL non supporta ancora '%s'", "Errore fatale %d: '%-.128s' dal master leggendo i dati dal log binario", "Slave SQL thread ignored the query because of replicate-*-table rules", +"Variable '%-.64s' is a %s variable", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -264,7 +265,7 @@ character-set=latin1 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -276,8 +277,8 @@ character-set=latin1 "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -289,10 +290,14 @@ character-set=latin1 "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index af4e626a1fd..60e390b02ff 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -233,7 +233,7 @@ character-set=ujis "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -243,6 +243,7 @@ character-set=ujis "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -266,7 +267,7 @@ character-set=ujis "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -278,8 +279,8 @@ character-set=ujis "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -291,10 +292,14 @@ character-set=ujis "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index dd67927adef..1797a7c8e2a 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -231,7 +231,7 @@ character-set=euckr "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -241,6 +241,7 @@ character-set=euckr "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -264,7 +265,7 @@ character-set=euckr "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -276,8 +277,8 @@ character-set=euckr "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -289,10 +290,14 @@ character-set=euckr "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 06c26a06e99..498b65715b8 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -233,7 +233,7 @@ character-set=latin1 "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -243,6 +243,7 @@ character-set=latin1 "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -266,7 +267,7 @@ character-set=latin1 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -278,8 +279,8 @@ character-set=latin1 "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -291,10 +292,14 @@ character-set=latin1 "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 08358db4b3a..74f4acd43f0 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -233,7 +233,7 @@ character-set=latin1 "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -243,6 +243,7 @@ character-set=latin1 "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -266,7 +267,7 @@ character-set=latin1 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -278,8 +279,8 @@ character-set=latin1 "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -291,10 +292,14 @@ character-set=latin1 "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 9aa6cf0d453..e9f6e6a349e 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -235,7 +235,7 @@ character-set=latin2 "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -245,6 +245,7 @@ character-set=latin2 "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -268,7 +269,7 @@ character-set=latin2 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -280,8 +281,8 @@ character-set=latin2 "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -293,10 +294,14 @@ character-set=latin2 "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 49ccf308634..7056a5bb169 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -205,7 +205,7 @@ character-set=latin1 "Esta operação não pode ser realizada com um 'slave' em execução. Execute STOP SLAVE primeiro", "Esta operação requer um 'slave' em execução. Configure o 'slave' e execute START SLAVE", "O servidor não está configurado como 'slave'. Acerte o arquivo de configuração ou use CHANGE MASTER TO", -"Não pode inicializar a estrutura de informação do 'master'. Verifique as permissões em 'master.info'", +"Could not initialize master info structure, more error messages can be found in the MySQL error log", "Não conseguiu criar 'thread' de 'slave'. Verifique os recursos do sistema", "Usuário '%-.64s' já possui mais que o valor máximo de conexões (max_user_connections) ativas", "Você pode usar apenas expressões constantes com SET", @@ -232,7 +232,7 @@ character-set=latin1 "Opção '%s' usada duas vezes no comando", "Usuário '%-.64s' tem excedido o '%s' recurso (atual valor: %ld)", "Acesso negado. Você precisa o privilégio %-.128s para essa operação", -"Variável '%-.64s' é uma LOCAL variável e não pode ser usada com SET GLOBAL", +"Variável '%-.64s' é uma SESSION variável e não pode ser usada com SET GLOBAL", "Variável '%-.64s' é uma GLOBAL variável e deve ser configurada com SET GLOBAL", "Variável '%-.64s' não tem um valor padrão", "Variável '%-.64s' não pode ser configurada para o valor de '%-.64s'", @@ -242,6 +242,7 @@ character-set=latin1 "Esta versão de MySQL não suporta ainda '%s'", "Obteve fatal erro %d: '%-.128s' do master quando lendo dados do binary log", "Slave SQL thread ignorado a consulta devido às normas de replicação-*-tabela", +"Variable '%-.64s' is a %s variable", "Definição errada da chave estrangeira para '%-.64s': %s", "Referência da chave e referência da tabela não coincidem", "Operand should contain %d column(s)", @@ -278,7 +279,7 @@ character-set=latin1 "Variável '%-.64s' não é uma variável componente (Não pode ser usada como XXXX.variável_nome)", "Collation desconhecida: '%-.64s'", "SSL parâmetros em CHANGE MASTER são ignorados porque este escravo MySQL foi compilado sem o SSL suporte. Os mesmos podem ser usados mais tarde quando o escravo MySQL com SSL seja iniciado.", -"Servidor está rodando em --secure-auth modo, porêm '%s@%s' tem senha no formato antigo; por favor troque a senha para o novo formato", +"Servidor está rodando em --secure-auth modo, porêm '%s'@'%s' tem senha no formato antigo; por favor troque a senha para o novo formato", "Campo ou referência '%-.64s%s%-.64s%s%-.64s' de SELECT #%d foi resolvido em SELECT #%d", "Parâmetro ou combinação de parâmetros errado para START SLAVE UNTIL", "É recomendado para rodar com --skip-slave-start quando fazendo replicação passo-por-passo com START SLAVE UNTIL, de outra forma você não está seguro em caso de inesperada reinicialição do mysqld escravo", @@ -290,10 +291,14 @@ character-set=latin1 "Key cache desconhecida '%-.100s'", "MySQL foi inicializado em modo --skip-name-resolve. Você necesita reincializá-lo sem esta opção para este grant funcionar", "Motor de tabela desconhecido '%s'", -"'%s' é desatualizado. Use '%s' em seu lugar.", -"The target table %-.100s of the %s is not updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%s' é desatualizado. Use '%s' em seu lugar", +"A tabela destino %-.100s do %s não é atualizável", +"O recurso '%s' foi desativado; você necessita MySQL construído com '%s' para ter isto funcionando", +"O servidor MySQL está rodando com a opção %s razão pela qual não pode executar esse commando", +"Coluna '%-.100s' tem valor duplicado '%-.64s' em %s" +"Truncado errado %-.32s valor: '%-.128s'" +"Incorreta definição de tabela; Pode ter somente uma coluna TIMESTAMP com CURRENT_TIMESTAMP em DEFAULT ou ON UPDATE cláusula" +"Inválida cláusula ON UPDATE para campo '%-.64s'", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index b79926cb7e4..c1f7cbf9914 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -235,7 +235,7 @@ character-set=latin2 "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -245,6 +245,7 @@ character-set=latin2 "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -268,7 +269,7 @@ character-set=latin2 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -280,8 +281,8 @@ character-set=latin2 "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -293,10 +294,14 @@ character-set=latin2 "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index be92447d43e..86a4424b8f1 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -76,7 +76,7 @@ character-set=koi8r "õËÁÚÁÎÏ ÎÅÓËÏÌØËÏ ÐÅÒ×ÉÞÎÙÈ ËÌÀÞÅÊ", "õËÁÚÁÎÏ ÓÌÉÛËÏÍ ÍÎÏÇÏ ËÌÀÞÅÊ. òÁÚÒÅÛÁÅÔÓÑ ÕËÁÚÙ×ÁÔØ ÎÅ ÂÏÌÅÅ %d ËÌÀÞÅÊ", "õËÁÚÁÎÏ ÓÌÉÛËÏÍ ÍÎÏÇÏ ÞÁÓÔÅÊ ÓÏÓÔÁ×ÎÏÇÏ ËÌÀÞÁ. òÁÚÒÅÛÁÅÔÓÑ ÕËÁÚÙ×ÁÔØ ÎÅ ÂÏÌÅÅ %d ÞÁÓÔÅÊ", -"õËÁÚÁÎ ÓÌÉÛËÏÍ ÄÌÉÎÎÙÊ ËÌÀÞ. íÁËÓÉÍÁÌØÎÁÑ ÄÌÉÎÁ ËÌÀÞÁ ÓÏÓÔÁ×ÌÑÅÔ %d", +"õËÁÚÁÎ ÓÌÉÛËÏÍ ÄÌÉÎÎÙÊ ËÌÀÞ. íÁËÓÉÍÁÌØÎÁÑ ÄÌÉÎÁ ËÌÀÞÁ ÓÏÓÔÁ×ÌÑÅÔ %d ÂÁÊÔ", "ëÌÀÞÅ×ÏÊ ÓÔÏÌÂÅà '%-.64s' × ÔÁÂÌÉÃÅ ÎÅ ÓÕÝÅÓÔ×ÕÅÔ", "óÔÏÌÂÅà ÔÉÐÁ BLOB '%-.64s' ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÓÐÏÌØÚÏ×ÁÎ ËÁË ÚÎÁÞÅÎÉÅ ËÌÀÞÁ × ÔÁÂÌÉÃÅ ÔÁËÏÇÏ ÔÉÐÁ", "óÌÉÛËÏÍ ÂÏÌØÛÁÑ ÄÌÉÎÁ ÓÔÏÌÂÃÁ '%-.64s' (ÍÁËÓÉÍÕÍ = %d). éÓÐÏÌØÚÕÊÔÅ ÔÉÐ BLOB ×ÍÅÓÔÏ ÔÅËÕÝÅÇÏ", @@ -89,7 +89,7 @@ character-set=koi8r "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ IP-ÓÏËÅÔ", "÷ ÔÁÂÌÉÃÅ '%-.64s' ÎÅÔ ÔÁËÏÇÏ ÉÎÄÅËÓÁ, ËÁË × CREATE INDEX. óÏÚÄÁÊÔÅ ÔÁÂÌÉÃÕ ÚÁÎÏ×Ï", "áÒÇÕÍÅÎÔ ÒÁÚÄÅÌÉÔÅÌÑ ÐÏÌÅÊ - ÎÅ ÔÏÔ, ËÏÔÏÒÙÊ ÏÖÉÄÁÌÓÑ. ïÂÒÁÝÁÊÔÅÓØ Ë ÄÏËÕÍÅÎÔÁÃÉÉ", -"æÉËÓÉÒÏ×ÁÎÎÙÊ ÒÁÚÍÅÒ ÚÁÐÉÓÉ Ó ÐÏÌÑÍÉ ÔÉÐÁ BLOB ÉÓÐÏÌØÚÏ×ÁÔØ ÎÅÌØÚÑ. ðÒÉÍÅÎÑÊÔÅ 'fields terminated by'.", +"æÉËÓÉÒÏ×ÁÎÎÙÊ ÒÁÚÍÅÒ ÚÁÐÉÓÉ Ó ÐÏÌÑÍÉ ÔÉÐÁ BLOB ÉÓÐÏÌØÚÏ×ÁÔØ ÎÅÌØÚÑ, ÐÒÉÍÅÎÑÊÔÅ 'fields terminated by'", "æÁÊÌ '%-.64s' ÄÏÌÖÅÎ ÎÁÈÏÄÉÔØÓÑ × ÔÏÍ ÖÅ ËÁÔÁÌÏÇÅ, ÞÔÏ É ÂÁÚÁ ÄÁÎÎÙÈ, ÉÌÉ ÂÙÔØ ÏÂÝÅÄÏÓÔÕÐÎÙÍ ÄÌÑ ÞÔÅÎÉÑ", "æÁÊÌ '%-.80s' ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ", "úÁÐÉÓÅÊ: %ld õÄÁÌÅÎÏ: %ld ðÒÏÐÕÝÅÎÏ: %ld ðÒÅÄÕÐÒÅÖÄÅÎÉÊ: %ld", @@ -206,7 +206,7 @@ character-set=koi8r "üÔÕ ÏÐÅÒÁÃÉÀ ÎÅ×ÏÚÍÏÖÎÏ ×ÙÐÏÌÎÉÔØ ÐÒÉ ÒÁÂÏÔÁÀÝÅÍ ÐÏÔÏËÅ ÐÏÄÞÉÎÅÎÎÏÇÏ ÓÅÒ×ÅÒÁ. óÎÁÞÁÌÁ ×ÙÐÏÌÎÉÔÅ STOP SLAVE", "äÌÑ ÜÔÏÊ ÏÐÅÒÁÃÉÉ ÔÒÅÂÕÅÔÓÑ ÒÁÂÏÔÁÀÝÉÊ ÐÏÄÞÉÎÅÎÎÙÊ ÓÅÒ×ÅÒ. óÎÁÞÁÌÁ ×ÙÐÏÌÎÉÔÅ START SLAVE", "üÔÏÔ ÓÅÒ×ÅÒ ÎÅ ÎÁÓÔÒÏÅÎ ËÁË ÐÏÄÞÉÎÅÎÎÙÊ. ÷ÎÅÓÉÔÅ ÉÓÐÒÁ×ÌÅÎÉÑ × ËÏÎÆÉÇÕÒÁÃÉÏÎÎÏÍ ÆÁÊÌÅ ÉÌÉ Ó ÐÏÍÏÝØÀ CHANGE MASTER TO", -"îÅ×ÏÚÍÏÖÎÏ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ ÓÔÒÕËÔÕÒÕ ÄÌÑ ÉÎÆÏÒÍÁÃÉÉ Ï ÇÏÌÏ×ÎÏÍ ÓÅÒ×ÅÒÅ. ðÒÏ×ÅÒØÔÅ ÐÒÁ×Á ÎÁ ÆÁÊÌ master.info", +"Could not initialize master info structure, more error messages can be found in the MySQL error log", "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÐÏÔÏË ÐÏÄÞÉÎÅÎÎÏÇÏ ÓÅÒ×ÅÒÁ. ðÒÏ×ÅÒØÔÅ ÓÉÓÔÅÍÎÙÅ ÒÅÓÕÒÓÙ", "õ ÐÏÌØÚÏ×ÁÔÅÌÑ %-.64s ÕÖÅ ÂÏÌØÛÅ ÞÅÍ 'max_user_connections' ÁËÔÉ×ÎÙÈ ÓÏÅÄÉÎÅÎÉÊ", "÷Ù ÍÏÖÅÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ × SET ÔÏÌØËÏ ËÏÎÓÔÁÎÔÎÙÅ ×ÙÒÁÖÅÎÉÑ", @@ -233,7 +233,7 @@ character-set=koi8r "ïÐÃÉÑ '%s' Ä×ÁÖÄÙ ÉÓÐÏÌØÚÏ×ÁÎÁ × ×ÙÒÁÖÅÎÉÉ", "ðÏÌØÚÏ×ÁÔÅÌØ '%-.64s' ÐÒÅ×ÙÓÉÌ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ÒÅÓÕÒÓÁ '%s' (ÔÅËÕÝÅÅ ÚÎÁÞÅÎÉÅ: %ld)", "÷ ÄÏÓÔÕÐÅ ÏÔËÁÚÁÎÏ. ÷ÁÍ ÎÕÖÎÙ ÐÒÉ×ÉÌÅÇÉÉ %-.128s ÄÌÑ ÜÔÏÊ ÏÐÅÒÁÃÉÉ", -"ðÅÒÅÍÅÎÎÁÑ '%-.64s' Ñ×ÌÑÅÔÓÑ ÐÏÔÏËÏ×ÏÊ (LOCAL) ÐÅÒÅÍÅÎÎÏÊ É ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÚÍÅÎÅÎÁ Ó ÐÏÍÏÝØÀ SET GLOBAL", +"ðÅÒÅÍÅÎÎÁÑ '%-.64s' Ñ×ÌÑÅÔÓÑ ÐÏÔÏËÏ×ÏÊ (SESSION) ÐÅÒÅÍÅÎÎÏÊ É ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÚÍÅÎÅÎÁ Ó ÐÏÍÏÝØÀ SET GLOBAL", "ðÅÒÅÍÅÎÎÁÑ '%-.64s' Ñ×ÌÑÅÔÓÑ ÇÌÏÂÁÌØÎÏÊ (GLOBAL) ÐÅÒÅÍÅÎÎÏÊ, É ÅÅ ÓÌÅÄÕÅÔ ÉÚÍÅÎÑÔØ Ó ÐÏÍÏÝØÀ SET GLOBAL", "ðÅÒÅÍÅÎÎÁÑ '%-.64s' ÎÅ ÉÍÅÅÔ ÚÎÁÞÅÎÉÑ ÐÏ ÕÍÏÌÞÁÎÉÀ", "ðÅÒÅÍÅÎÎÁÑ '%-.64s' ÎÅ ÍÏÖÅÔ ÂÙÔØ ÕÓÔÁÎÏ×ÌÅÎÁ × ÚÎÁÞÅÎÉÅ '%-.64s'", @@ -243,6 +243,7 @@ character-set=koi8r "üÔÁ ×ÅÒÓÉÑ MySQL ÐÏËÁ ÅÝÅ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ '%s'", "ðÏÌÕÞÅÎÁ ÎÅÉÓÐÒÁ×ÉÍÁÑ ÏÛÉÂËÁ %d: '%-.128s' ÏÔ ÇÏÌÏ×ÎÏÇÏ ÓÅÒ×ÅÒÁ × ÐÒÏÃÅÓÓÅ ×ÙÂÏÒËÉ ÄÁÎÎÙÈ ÉÚ Ä×ÏÉÞÎÏÇÏ ÖÕÒÎÁÌÁ", "Slave SQL thread ignored the query because of replicate-*-table rules", +"Variable '%-.64s' is a %s variable", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "ïÐÅÒÁÎÄ ÄÏÌÖÅÎ ÓÏÄÅÒÖÁÔØ %d ËÏÌÏÎÏË", @@ -266,7 +267,7 @@ character-set=koi8r "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -278,8 +279,8 @@ character-set=koi8r "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 when MySQL slave with SSL will be started.", -"óÅÒ×ÅÒ ÚÁÐÕÝÅÎ × ÒÅÖÉÍÅ --secure-auth (ÂÅÚÏÐÁÓÎÏÊ Á×ÔÏÒÉÚÁÃÉÉ), ÎÏ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%s@%s' ÐÁÒÏÌØ ÓÏÈÒÁÎ£Î × ÓÔÁÒÏÍ ÆÏÒÍÁÔÅ; ÎÅÏÂÈÏÄÉÍÏ ÏÂÎÏ×ÉÔØ ÆÏÒÍÁÔ ÐÁÒÏÌÑ", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be started", +"óÅÒ×ÅÒ ÚÁÐÕÝÅÎ × ÒÅÖÉÍÅ --secure-auth (ÂÅÚÏÐÁÓÎÏÊ Á×ÔÏÒÉÚÁÃÉÉ), ÎÏ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%s'@'%s' ÐÁÒÏÌØ ÓÏÈÒÁÎ£Î × ÓÔÁÒÏÍ ÆÏÒÍÁÔÅ; ÎÅÏÂÈÏÄÉÍÏ ÏÂÎÏ×ÉÔØ ÆÏÒÍÁÔ ÐÁÒÏÌÑ", "ðÏÌÅ ÉÌÉ ÓÓÙÌËÁ '%-.64s%s%-.64s%s%-.64s' ÉÚ SELECTÁ #%d ÂÙÌÁ ÎÁÊÄÅÎÁ × SELECTÅ #%d", "Wrong 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", @@ -291,10 +292,14 @@ character-set=koi8r "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.", -"ôÁÂÌÉÃÁ %-.100s × %s ÎÅ ÍÏÖÅÔ ÉÚÍÅÎÑÔÓÑ.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%s' is deprecated, use '%s' instead", +"ôÁÂÌÉÃÁ %-.100s × %s ÎÅ ÍÏÖÅÔ ÉÚÍÅÎÑÔÓÑ", +"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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index 92710650f50..957c7650a87 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -259,7 +259,7 @@ character-set=cp1250 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -271,8 +271,8 @@ character-set=cp1250 "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -284,10 +284,14 @@ character-set=cp1250 "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%s' is deprecated, use '%s' instead", +"The target table %-.100s of the %s is not updatable", +"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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index c81d74075ad..feddd50b585 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -239,7 +239,7 @@ character-set=latin2 "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -249,6 +249,7 @@ character-set=latin2 "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "Operand should contain %d column(s)", @@ -272,7 +273,7 @@ character-set=latin2 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -284,8 +285,8 @@ character-set=latin2 "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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", @@ -297,10 +298,14 @@ character-set=latin2 "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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index e79af2de398..f309b8108bb 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -206,7 +206,7 @@ character-set=latin1 "Esta operación no puede ser hecha con el esclavo funcionando, primero use STOP SLAVE", "Esta operación necesita el esclavo funcionando, configure esclavo y haga el START SLAVE", "El servidor no está configurado como esclavo, edite el archivo config file o con CHANGE MASTER TO", -"No puedo inicializar la estructura info del master, verifique permisiones en el master.info", +"Could not initialize master info structure, more error messages can be found in the MySQL error log", "No puedo crear el thread esclavo, verifique recursos del sistema", "Usario %-.64s ya tiene mas que 'max_user_connections' conexiones activas", "Tu solo debes usar expresiones constantes con SET", @@ -233,7 +233,7 @@ character-set=latin1 "Opción '%s' usada dos veces en el comando", "Usuario '%-.64s' ha excedido el recurso '%s' (actual valor: %ld)", "Acceso negado. Usted necesita el privilegio %-.128s para esta operación", -"Variable '%-.64s' es una LOCAL variable y no puede ser usada con SET GLOBAL", +"Variable '%-.64s' es una SESSION variable y no puede ser usada con SET GLOBAL", "Variable '%-.64s' es una GLOBAL variable y no puede ser configurada con SET GLOBAL", "Variable '%-.64s' no tiene un valor patrón", "Variable '%-.64s' no puede ser configurada para el valor de '%-.64s'", @@ -243,58 +243,63 @@ character-set=latin1 "Esta versión de MySQL no soporta todavia '%s'", "Recibió fatal error %d: '%-.128s' del master cuando leyendo datos del binary log", "Slave SQL thread ignorado el query debido a las reglas de replicación-*-tabla", -"Wrong foreign key definition for '%-.64s': %s", -"Key reference and table reference doesn't match", -"Operand should contain %d column(s)", -"Subquery returns more than 1 row", -"Unknown prepared statement handler (%ld) 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 it's own alias", -"Select %u was reduced during optimisation", -"Table '%-.64s' from one of SELECT's can not be used in %-.32s", -"Client does not support authentication protocol requested by server; consider upgrading MySQL client", -"All parts of a SPATIAL KEY must be NOT NULL", -"COLLATION '%s' is not valid for CHARACTER SET '%s'", -"Slave is already running", -"Slave has already been stopped", -"Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)", -"Z_MEM_ERROR: Not enough memory available for zlib", -"Z_BUF_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)", -"Z_DATA_ERROR: Input data was corrupted for zlib", -"%d line(s) was(were) cut by group_concat()", -"Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where 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 when MySQL slave with SSL will be 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", -"Wrong 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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"Variable '%-.64s' es una %s variable", +"Equivocada definición de llave extranjera para '%-.64s': %s", +"Referencia de llave y referencia de tabla no coinciden", +"Operando debe tener %d columna(s)", +"Subconsulta retorna mas que 1 línea", +"Desconocido preparado comando handler (%ld) dado para %s", +"Base de datos Help está corrupto o no existe", +"Cíclica referencia en subconsultas", +"Convirtiendo columna '%s' de %s para %s", +"Referencia '%-.64s' no soportada (%s)", +"Cada tabla derivada debe tener su propio alias", +"Select %u fué reducido durante optimización", +"Tabla '%-.64s' de uno de los SELECT no puede ser usada en %-.32s", +"Cliente no soporta protocolo de autenticación solicitado por el servidor; considere actualizar el cliente MySQL", +"Todas las partes de una SPATIAL KEY deben ser NOT NULL", +"COLLATION '%s' no es válido para CHARACTER SET '%s'", +"Slave ya está funcionando", +"Slave ya fué parado", +"Tamaño demasiado grande para datos descomprimidos. El máximo tamaño es %d. (probablemente, extensión de datos descomprimidos fué corrompida)", +"Z_MEM_ERROR: No suficiente memoria para zlib", +"Z_BUF_ERROR: No suficiente espacio en el búfer de salida para zlib (probablemente, extensión de datos descomprimidos fué corrompida)", +"Z_DATA_ERROR: Dato de entrada fué corrompido para zlib", +"%d línea(s) fue(fueron) cortadas por group_concat()", +"Línea %ld no contiene datos para todas las columnas", +"Línea %ld fué truncada; La misma contine mas datos que las que existen en las columnas de entrada", +"Datos truncado, NULL suministrado para NOT NULL columna '%s' en la línea %ld", +"Datos truncados, fuera de gama para columna '%s' en la línea %ld", +"Datos truncados para columna '%s' en la línea %ld", +"Usando motor de almacenamiento %s para tabla '%s'", +"Ilegal mezcla de collations (%s,%s) y (%s,%s) para operación '%s'", +"No puede remover uno o mas de los usuarios solicitados", +"No puede revocar todos los privilegios, derecho para uno o mas de los usuarios solicitados", +"Ilegal mezcla de collations (%s,%s), (%s,%s), (%s,%s) para operación '%s'", +"Ilegal mezcla de collations para operación '%s'", +"Variable '%-.64s' no es una variable componente (No puede ser usada como XXXX.variable_name)", +"Collation desconocida: '%-.64s'", +"Parametros SSL en CHANGE MASTER son ignorados porque este slave MySQL fue compilado sin soporte SSL; pueden ser usados despues cuando el slave MySQL con SSL sea inicializado", +"Servidor está rodando en modo --secure-auth, pero '%s'@'%s' tiene clave en el antiguo formato; por favor cambie la clave para el nuevo formato", +"Campo o referencia '%-.64s%s%-.64s%s%-.64s' de SELECT #%d fue resolvido en SELECT #%d", +"Parametro equivocado o combinación de parametros para START SLAVE UNTIL", +"Es recomendado rodar con --skip-slave-start cuando haciendo replicación step-by-step con START SLAVE UNTIL, a menos que usted no esté seguro en caso de inesperada reinicialización del mysqld slave", +"SQL thread no es inicializado tal que opciones UNTIL son ignoradas", +"Nombre de índice incorrecto '%-.100s'", +"Nombre de catalog incorrecto '%-.100s'", +"Query cache fallada para configurar tamaño %lu, nuevo tamaño de query cache es %lu", +"Columna '%-.64s' no puede ser parte de FULLTEXT index", +"Desconocida key cache '%-.100s'", +"MySQL esta inicializado en modo --skip-name-resolve. Usted necesita reinicializarlo sin esta opción para este derecho funcionar", +"Desconocido motor de tabla '%s'", +"'%s' está desaprobado, use '%s' en su lugar", +"La tabla destino %-.100s del %s no es actualizable", +"El recurso '%s' fue deshabilitado; usted necesita construir MySQL con '%s' para tener eso funcionando", +"El servidor MySQL está rodando con la opción %s tal que no puede ejecutar este comando", +"Columna '%-.100s' tiene valor doblado '%-.64s' en %s" +"Equivocado truncado %-.32s valor: '%-.128s'" +"Incorrecta definición de tabla; Solamente debe haber una columna TIMESTAMP con CURRENT_TIMESTAMP en DEFAULT o ON UPDATE cláusula" +"Inválido ON UPDATE cláusula para campo '%-.64s'", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 28314a7367a..4761a6c15c1 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -204,7 +204,7 @@ character-set=latin1 "Denna operation kan inte göras under replikering; Gör STOP SLAVE först", "Denna operation kan endast göras under replikering; Konfigurera slaven och gör START SLAVE", "Servern är inte konfigurerade som en replikationsslav. Ändra konfigurationsfilen eller gör CHANGE MASTER TO", -"Kunde inte initialisera replikationsstrukturerna. Kontrollera privilegerna för 'master.info'", +"Kunde inte initialisera replikationsstrukturerna. See MySQL fel fil för mera information", "Kunde inte starta en tråd för replikering", "Användare '%-.64s' har redan 'max_user_connections' aktiva inloggningar", "Man kan endast använda konstantuttryck med SET", @@ -231,7 +231,7 @@ character-set=latin1 "Option '%s' användes två gånger", "Användare '%-.64s' har överskridit '%s' (nuvarande värde: %ld)", "Du har inte privlegiet '%-.128s' som behövs för denna operation", -"Variabel '%-.64s' är en LOCAL variabel och kan inte ändrad med SET GLOBAL", +"Variabel '%-.64s' är en SESSION variabel och kan inte ändrad med SET GLOBAL", "Variabel '%-.64s' är en GLOBAL variabel och bör sättas med SET GLOBAL", "Variabel '%-.64s' har inte ett DEFAULT-värde", "Variabel '%-.64s' kan inte sättas till '%-.64s'", @@ -241,6 +241,7 @@ character-set=latin1 "Denna version av MySQL kan ännu inte utföra '%s'", "Fick fatalt fel %d: '%-.128s' från master vid läsning av binärloggen", "Slav SQL tråden ignorerade frågan pga en replicate-*-table regel", +"Variabel '%-.64s' är av typ %s", "Felaktig FOREIGN KEY-definition för '%-.64s': %s", "Nyckelreferensen och tabellreferensen stämmer inte överens", "Operand should contain %d column(s)", @@ -264,7 +265,7 @@ character-set=latin1 "Z_DATA_ERROR: Input data was corrupted for zlib", "%d rad(er) kapades av group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -276,23 +277,27 @@ character-set=latin1 "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "Wrong 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'", +"Felaktigt index namn '%-.100s'", +"Felaktigt katalog namn '%-.100s'", "Storleken av "Query cache" kunde inte sättas till %lu, ny storlek är %lu", "Kolumn '%-.64s' kan inte vara del av ett FULLTEXT index", -"Unknown key cache '%-.100s'", +"Okänd nyckel 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 updatable.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%s' is deprecated, use '%s' instead", +"Tabel %-.100s använd med '%s' är inte uppdateringsbar", +"'%s' är inte aktiverad; För att aktivera detta måste du bygga om MySQL med '%s' definerad", +"MySQL är startad med --skip-grant-tables. Pga av detta kan du inte använda detta kommando", +"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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index 9263ee89400..2495c2676be 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -79,7 +79,7 @@ character-set=koi8u "ðÅÒ×ÉÎÎÏÇÏ ËÌÀÞÁ ×ÉÚÎÁÞÅÎÏ ÎÅÏÄÎÏÒÁÚÏ×Ï", "úÁÂÁÇÁÔÏ ËÌÀÞ¦× ÚÁÚÎÁÞÅÎÏ. äÏÚ×ÏÌÅÎÏ ÎŠ¦ÌØÛÅ %d ËÌÀÞ¦×", "úÁÂÁÇÁÔÏ ÞÁÓÔÉÎ ËÌÀÞÁ ÚÁÚÎÁÞÅÎÏ. äÏÚ×ÏÌÅÎÏ ÎŠ¦ÌØÛÅ %d ÞÁÓÔÉÎ", -"úÁÚÎÁÞÅÎÉÊ ËÌÀÞ ÚÁÄÏ×ÇÉÊ. îÁʦÌØÛÁ ÄÏ×ÖÉÎÁ ËÌÀÞÁ %d", +"úÁÚÎÁÞÅÎÉÊ ËÌÀÞ ÚÁÄÏ×ÇÉÊ. îÁʦÌØÛÁ ÄÏ×ÖÉÎÁ ËÌÀÞÁ %d ÂÁÊÔ¦×", "ëÌÀÞÏ×ÉÊ ÓÔÏ×ÂÅÃØ '%-.64s' ÎÅ ¦ÓÎÕ¤ Õ ÔÁÂÌÉæ", "BLOB ÓÔÏ×ÂÅÃØ '%-.64s' ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÒÉÓÔÁÎÉÊ Õ ×ÉÚÎÁÞÅÎΦ ËÌÀÞÁ × ÃØÏÍÕ ÔÉЦ ÔÁÂÌÉæ", "úÁÄÏ×ÇÁ ÄÏ×ÖÉÎÁ ÓÔÏ×ÂÃÑ '%-.64s' (max = %d). ÷ÉËÏÒÉÓÔÁÊÔÅ ÔÉÐ BLOB", @@ -152,7 +152,7 @@ character-set=koi8u "ðÏ×ÎÏ×ÁÖÅÎØ ÎÅ ×ÉÚÎÁÞÅÎÏ ÄÌÑ ËÏÒÉÓÔÕ×ÁÞÁ '%-.32s' Ú ÈÏÓÔÕ '%-.64s'", "%-.16s ËÏÍÁÎÄÁ ÚÁÂÏÒÏÎÅÎÁ ËÏÒÉÓÔÕ×ÁÞÕ: '%-.32s'@'%-.64s' Õ ÔÁÂÌÉæ '%-.64s'", "%-.16s ËÏÍÁÎÄÁ ÚÁÂÏÒÏÎÅÎÁ ËÏÒÉÓÔÕ×ÁÞÕ: '%-.32s'@'%-.64s' ÄÌÑ ÓÔÏ×ÂÃÑ '%-.64s' Õ ÔÁÂÌÉæ '%-.64s'", -"èÉÂÎÁ GRANT/REVOKE ËÏÍÁÎÄÁ. ðÒÏÞÉÔÁÊÔÅ ÄÏËÕÍÅÎÔÁæÀ ÓÔÏÓÏ×ÎÏ ÔÏÇÏ, Ñ˦ ÐÒÁ×Á ÍÏÖÎÁ ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ.", +"èÉÂÎÁ GRANT/REVOKE ËÏÍÁÎÄÁ; ÐÒÏÞÉÔÁÊÔÅ ÄÏËÕÍÅÎÔÁæÀ ÓÔÏÓÏ×ÎÏ ÔÏÇÏ, Ñ˦ ÐÒÁ×Á ÍÏÖÎÁ ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ", "áÒÇÕÍÅÎÔ host ÁÂÏ user ÄÌÑ GRANT ÚÁÄÏ×ÇÉÊ", "ôÁÂÌÉÃÑ '%-.64s.%-.64s' ÎÅ ¦ÓÎÕ¤", "ðÏ×ÎÏ×ÁÖÅÎØ ÎÅ ×ÉÚÎÁÞÅÎÏ ÄÌÑ ËÏÒÉÓÔÕ×ÁÞÁ '%-.32s' Ú ÈÏÓÔÕ '%-.64s' ÄÌÑ ÔÁÂÌÉæ '%-.64s'", @@ -209,7 +209,7 @@ character-set=koi8u "ïÐÅÒÁÃ¦Ñ ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÎÁÎÁ Ú ÚÁÐÕÝÅÎÉÍ Ð¦ÄÌÅÇÌÉÍ, ÓÐÏÞÁÔËÕ ×ÉËÏÎÁÊÔÅ STOP SLAVE", "ïÐÅÒÁÃ¦Ñ ×ÉÍÁÇÁ¤ ÚÁÐÕÝÅÎÏÇÏ Ð¦ÄÌÅÇÌÏÇÏ, ÚËÏÎƦÇÕÒÕÊÔŠЦÄÌÅÇÌÏÇÏ ÔÁ ×ÉËÏÎÁÊÔÅ START SLAVE", "óÅÒ×ÅÒ ÎÅ ÚËÏÎƦÇÕÒÏ×ÁÎÏ ÑË Ð¦ÄÌÅÇÌÉÊ, ×ÉÐÒÁ×ÔÅ ÃÅ Õ ÆÁÊ̦ ËÏÎƦÇÕÒÁæ§ ÁÂÏ Ú CHANGE MASTER TO", -"îÅ ÍÏÖÕ ¦Î¦Ã¦Á̦ÚÕ×ÁÔÉ ÓÔÒÕËÔÕÒÕ ÉÎÆÏÒÍÁæ§ ÇÏÌÏ×ÎÏÇÏ, ÐÅÒÅצÒÔÅ ÐÒÁ×Á ÄÏÓÔÕÐÕ ÎÁ master.info", +"Could not initialize master info structure, more error messages can be found in the MySQL error log", "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ Ð¦ÄÌÅÇÌÕ Ç¦ÌËÕ, ÐÅÒÅצÒÔÅ ÓÉÓÔÅÍΦ ÒÅÓÕÒÓÉ", "ëÏÒÉÓÔÕ×ÁÞ %-.64s ×ÖÅ ÍÁ¤ ¦ÌØÛÅ Î¦Ö 'max_user_connections' ÁËÔÉ×ÎÉÈ Ú'¤ÄÎÁÎØ", "íÏÖÎÁ ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ ÌÉÛÅ ×ÉÒÁÚÉ Ú¦ ÓÔÁÌÉÍÉ Õ SET", @@ -236,7 +236,7 @@ character-set=koi8u "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 LOCAL variable and can't be used with SET GLOBAL", +"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'", @@ -246,6 +246,7 @@ character-set=koi8u "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", "Wrong foreign key definition for '%-.64s': %s", "Key reference and table reference doesn't match", "ïÐÅÒÁÎÄ ÍÁ¤ ÓËÌÁÄÁÔÉÓÑ Ú %d ÓÔÏ×Âæ×", @@ -269,7 +270,7 @@ character-set=koi8u "Z_DATA_ERROR: Input data was corrupted for zlib", "%d line(s) was(were) cut by group_concat()", "Row %ld doesn't contain data for all columns", -"Row %ld was truncated; It contained more data than there where input 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", @@ -281,8 +282,8 @@ character-set=koi8u "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 when MySQL slave with SSL will be 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", +"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be 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", "óÔÏ×ÂÅÃØ ÁÂÏ ÐÏÓÉÌÁÎÎÑ '%-.64s%s%-.64s%s%-.64s' ¦Ú SELECTÕ #%d ÂÕÌÏ ÚÎÁÊÄÅÎÅ Õ SELECT¦ #%d", "Wrong 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", @@ -294,10 +295,14 @@ character-set=koi8u "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.", -"ôÁÂÌÉÃÑ %-.100s Õ %s ÎÅ ÍÏÖÅ ÏÎÏ×ÌÀ×ÁÔÉÓØ.", -"The '%s' feature was disabled. You need MySQL built with '%s' define to have it working" -"MySQL is started in --skip-grant-tables mode. You can't use this command" +"'%s' is deprecated, use '%s' instead", +"ôÁÂÌÉÃÑ %-.100s Õ %s ÎÅ ÍÏÖÅ ÏÎÏ×ÌÀ×ÁÔÉÓØ", +"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' field", "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" diff --git a/sql/slave.cc b/sql/slave.cc index e3d9e0dab58..ad4cc820409 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -35,7 +35,6 @@ typedef bool (*CHECK_KILLED_FUNC)(THD*,void*); volatile bool slave_sql_running = 0, slave_io_running = 0; char* slave_load_tmpdir = 0; MASTER_INFO *active_mi; -volatile int active_mi_in_use = 0; HASH replicate_do_table, replicate_ignore_table; DYNAMIC_ARRAY replicate_wild_do_table, replicate_wild_ignore_table; bool do_table_inited = 0, ignore_table_inited = 0; @@ -136,8 +135,12 @@ int init_slave() { DBUG_ENTER("init_slave"); - /* This is called when mysqld starts */ - + /* + This is called when mysqld starts. Before client connections are + accepted. However bootstrap may conflict with us if it does START SLAVE. + So it's safer to take the lock. + */ + pthread_mutex_lock(&LOCK_active_mi); /* TODO: re-write this to interate through the list of files for multi-master @@ -182,9 +185,11 @@ int init_slave() goto err; } } + pthread_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(0); err: + pthread_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(1); } @@ -972,7 +977,14 @@ static int end_slave_on_walk(MASTER_INFO* mi, gptr /*unused*/) void end_slave() { - /* This is called when the server terminates, in close_connections(). */ + /* + This is called when the server terminates, in close_connections(). + It terminates slave threads. However, some CHANGE MASTER etc may still be + running presently. If a START SLAVE was in progress, the mutex lock below + will make us wait until slave threads have started, and START SLAVE + returns, then we terminate them here. + */ + pthread_mutex_lock(&LOCK_active_mi); if (active_mi) { /* @@ -993,6 +1005,7 @@ void end_slave() delete active_mi; active_mi= 0; } + pthread_mutex_unlock(&LOCK_active_mi); } @@ -1369,6 +1382,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, bzero((char*) &tables,sizeof(tables)); tables.db = (char*)db; tables.alias= tables.real_name= (char*)table_name; + /* Drop the table if 'overwrite' is true */ if (overwrite && mysql_rm_table(thd,&tables,1,0)) /* drop if exists */ { @@ -1835,7 +1849,18 @@ int init_master_info(MASTER_INFO* mi, const char* master_info_fname, DBUG_ENTER("init_master_info"); if (mi->inited) + { + /* + We have to reset read position of relay-log-bin as we may have + already been reading from 'hotlog' when the slave was stopped + 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. + */ + my_b_seek(mi->rli.cur_log, (my_off_t) 0); DBUG_RETURN(0); + } + mi->mysql=0; mi->file_id=1; fn_format(fname, master_info_fname, mysql_data_home, "", 4+32); @@ -1997,9 +2022,9 @@ file '%s')", fname); mi->master_log_name, (ulong) mi->master_log_pos)); + mi->rli.mi = mi; if (init_relay_log_info(&mi->rli, slave_info_fname)) goto err; - mi->rli.mi = mi; mi->inited = 1; // now change cache READ -> WRITE - must do this before flush_master_info @@ -2236,10 +2261,29 @@ int show_master_info(THD* thd, MASTER_INFO* mi) protocol->store(mi->ssl_key, &my_charset_bin); if (mi->rli.last_master_timestamp) - protocol->store((ulonglong) - (long)((time_t)time((time_t*) 0) - - mi->rli.last_master_timestamp) - - mi->clock_diff_with_master); + { + long tmp= (long)((time_t)time((time_t*) 0) + - mi->rli.last_master_timestamp) + - mi->clock_diff_with_master; + /* + Apparently on some systems tmp can be <0. Here are possible reasons + related to MySQL: + - the master is itself a slave of another master whose time is ahead. + - somebody used an explicit SET TIMESTAMP on the master. + Possible reason related to granularity-to-second of time functions + (nothing to do with MySQL), which can explain a value of -1: + assume the master's and slave's time are perfectly synchronized, and + that at slave's connection time, when the master's timestamp is read, + it is at the very end of second 1, and (a very short time later) when + the slave's timestamp is read it is at the very beginning of second + 2. Then the recorded value for master is 1 and the recorded value for + 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. + */ + protocol->store((longlong)(max(0, tmp))); + } else protocol->store_null(); @@ -2749,13 +2793,6 @@ int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int expected_error) case ER_NET_ERROR_ON_WRITE: case ER_SERVER_SHUTDOWN: case ER_NEW_ABORTING_CONNECTION: - slave_print_error(rli,expected_error, - "query '%s' partially completed on the master \ -and was aborted. There is a chance that your master is inconsistent at this \ -point. If you are sure that your master is ok, run this query manually on the\ - slave and then restart the slave with SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;\ - SLAVE START; .", thd->query); - thd->query_error= 1; return 1; default: return 0; @@ -2840,7 +2877,7 @@ bool st_relay_log_info::is_until_satisfied() /* Probably error so we aborting */ sql_print_error("Slave SQL thread is stopped because UNTIL " "condition is bad."); - return true; + return TRUE; } } else @@ -3417,7 +3454,7 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, llstr(rli->group_relay_log_pos,llbuff1)); /* execute init_slave variable */ - if (sys_init_slave.value) + if (sys_init_slave.value_length) { execute_init_command(thd, &sys_init_slave, &LOCK_sys_init_slave); if (thd->query_error) @@ -3560,6 +3597,14 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev) if (unlikely(!num_bytes)) /* eof */ { net_write_command(net, 0, "", 0, "", 0);/* 3.23 master wants it */ + /* + If we wrote Create_file_log_event, then we need to write + Execute_load_log_event. If we did not write Create_file_log_event, + then this is an empty file and we can just do as if the LOAD DATA + INFILE had not existed, i.e. write nothing. + */ + if (unlikely(cev_not_written)) + break; Execute_load_log_event xev(thd,0,0); xev.log_pos = cev->log_pos; if (unlikely(mi->rli.relay_log.append(&xev))) @@ -3988,7 +4033,9 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) mi->master_log_pos+= inc_pos; DBUG_PRINT("info", ("master_log_pos: %d, event originating from the same server, ignored", (ulong) mi->master_log_pos)); } - else /* write the event to the relay log */ + else + { + /* write the event to the relay log */ if (likely(!(rli->relay_log.appendv(buf,event_len,0)))) { mi->master_log_pos+= inc_pos; @@ -3997,6 +4044,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) } else error=3; + } err: pthread_mutex_unlock(&mi->data_lock); @@ -4223,6 +4271,7 @@ bool flush_relay_log_info(RELAY_LOG_INFO* rli) error=1; if (flush_io_cache(file)) error=1; + /* Flushing the relay log is done by the slave I/O thread */ return error; } @@ -4478,8 +4527,9 @@ Log_event* next_event(RELAY_LOG_INFO* rli) if (rli->relay_log.is_active(rli->linfo.log_file_name)) { #ifdef EXTRA_DEBUG - sql_print_error("next log '%s' is currently active", - rli->linfo.log_file_name); + if (global_system_variables.log_warnings) + sql_print_error("next log '%s' is currently active", + rli->linfo.log_file_name); #endif rli->cur_log= cur_log= rli->relay_log.get_log_file(); rli->cur_log_old_open_count= rli->relay_log.get_open_count(); @@ -4507,8 +4557,9 @@ Log_event* next_event(RELAY_LOG_INFO* rli) from hot to cold, but not from cold to hot). No need for LOCK_log. */ #ifdef EXTRA_DEBUG - sql_print_error("next log '%s' is not active", - rli->linfo.log_file_name); + if (global_system_variables.log_warnings) + sql_print_error("next log '%s' is not active", + rli->linfo.log_file_name); #endif // open_binlog() will check the magic header if ((rli->cur_log_fd=open_binlog(cur_log,rli->linfo.log_file_name, diff --git a/sql/slave.h b/sql/slave.h index 016944534f7..f4080f3ca8d 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -35,12 +35,19 @@ /* MUTEXES in replication: - LOCK_active_mi: this is meant for multimaster, when we can switch from a - master to another. It protects active_mi. We don't care of it for the moment, - as active_mi never moves (it's created at startup and deleted at shutdown, - and not changed: it always points to the same MASTER_INFO struct), because we - don't have multimaster. So for the moment, mi does not move, and mi->rli does - not either. + LOCK_active_mi: [note: this was originally meant for multimaster, to switch + from a master to another, to protect active_mi] It is used to SERIALIZE ALL + administrative commands of replication: START SLAVE, STOP SLAVE, CHANGE + MASTER, RESET SLAVE, end_slave() (when mysqld stops) [init_slave() does not + need it it's called early]. Any of these commands holds the mutex from the + start till the end. This thus protects us against a handful of deadlocks + (consider start_slave_thread() which, when starting the I/O thread, releases + mi->run_lock, keeps rli->run_lock, and tries to re-acquire mi->run_lock). + + Currently active_mi never moves (it's created at startup and deleted at + shutdown, and not changed: it always points to the same MASTER_INFO struct), + because we don't have multimaster. So for the moment, mi does not move, and + mi->rli does not either. In MASTER_INFO: run_lock, data_lock run_lock protects all information about the run state: slave_running, and the @@ -51,6 +58,9 @@ In RELAY_LOG_INFO: run_lock, data_lock see MASTER_INFO + Order of acquisition: if you want to have LOCK_active_mi and a run_lock, you + must acquire LOCK_active_mi first. + In MYSQL_LOG: LOCK_log, LOCK_index of the binlog and the relay log LOCK_log: when you write to it. LOCK_index: when you create/delete a binlog (so that you have to update the .index file). @@ -67,20 +77,7 @@ extern my_bool opt_log_slave_updates; extern ulonglong relay_log_space_limit; struct st_master_info; -/* - TODO: this needs to be redone, but for now it does not matter since - we do not have multi-master yet. -*/ - -#define LOCK_ACTIVE_MI { pthread_mutex_lock(&LOCK_active_mi); \ - ++active_mi_in_use; \ - pthread_mutex_unlock(&LOCK_active_mi);} - -#define UNLOCK_ACTIVE_MI { pthread_mutex_lock(&LOCK_active_mi); \ - --active_mi_in_use; \ - pthread_mutex_unlock(&LOCK_active_mi); } - -/***************************************************************************** +/**************************************************************************** Replication SQL Thread @@ -203,14 +200,16 @@ typedef struct st_relay_log_info bool ignore_log_space_limit; /* - InnoDB internally stores the master log position it has processed - so far; when the InnoDB code to store this position is called, we have not - updated rli->group_master_log_pos yet. So the position is the event's - log_pos (the position of the end of the event); we save it in the variable - below. It's the *coming* group_master_log_pos (the one which will be - group_master_log_pos in the coming milliseconds). + When it commits, InnoDB internally stores the master log position it has + processed so far; the position to store is the one of the end of the + committing event (the COMMIT query event, or the event if in autocommit + mode). */ +#if MYSQL_VERSION_ID < 40100 + ulonglong future_master_log_pos; +#else ulonglong future_group_master_log_pos; +#endif time_t last_master_timestamp; @@ -574,7 +573,6 @@ extern "C" pthread_handler_decl(handle_slave_io,arg); extern "C" pthread_handler_decl(handle_slave_sql,arg); extern bool volatile abort_loop; extern MASTER_INFO main_mi, *active_mi; /* active_mi for multi-master */ -extern volatile int active_mi_in_use; extern LIST master_list; extern HASH replicate_do_table, replicate_ignore_table; extern DYNAMIC_ARRAY replicate_wild_do_table, replicate_wild_ignore_table; diff --git a/sql/spatial.cc b/sql/spatial.cc index d19429fdd9c..ab415d9af10 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -1,145 +1,183 @@ -#include "mysql_priv.h" +/* Copyright (C) 2004 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 */ +#include "mysql_priv.h" #define MAX_DIGITS_IN_DOUBLE 16 -/***************************** GClassInfo *******************************/ - -#define IMPLEMENT_GEOM(class_name, type_id, name) \ -{ \ - (GF_InitFromText) &class_name::init_from_wkt, \ - (GF_GetDataAsText) &class_name::get_data_as_wkt, \ - (GF_GetDataSize) &class_name::get_data_size, \ - (GF_GetMBR) &class_name::get_mbr, \ - (GF_GetD) &class_name::get_x, \ - (GF_GetD) &class_name::get_y, \ - (GF_GetD) &class_name::length, \ - (GF_GetD) &class_name::area, \ - (GF_GetI) &class_name::is_closed, \ - (GF_GetUI) &class_name::num_interior_ring, \ - (GF_GetUI) &class_name::num_points, \ - (GF_GetUI) &class_name::num_geometries, \ - (GF_GetUI) &class_name::dimension, \ - (GF_GetWS) &class_name::start_point, \ - (GF_GetWS) &class_name::end_point, \ - (GF_GetWS) &class_name::exterior_ring, \ - (GF_GetWS) &class_name::centroid, \ - (GF_GetUIWS) &class_name::point_n, \ - (GF_GetUIWS) &class_name::interior_ring_n, \ - (GF_GetUIWS) &class_name::geometry_n, \ - class_name::type_id, \ - name, \ - NULL \ -}, - - -static Geometry::GClassInfo ci_collection[] = -{ - IMPLEMENT_GEOM(GPoint, wkbPoint, "POINT") - IMPLEMENT_GEOM(GLineString, wkbLineString, "LINESTRING") - IMPLEMENT_GEOM(GPolygon, wkbPolygon, "POLYGON") - IMPLEMENT_GEOM(GMultiPoint, wkbMultiPoint, "MULTIPOINT") - IMPLEMENT_GEOM(GMultiLineString, wkbMultiLineString, "MULTILINESTRING") - IMPLEMENT_GEOM(GMultiPolygon, wkbMultiPolygon, "MULTIPOLYGON") - IMPLEMENT_GEOM(GGeometryCollection, wkbGeometryCollection, "GEOMETRYCOLLECTION") +/***************************** Gis_class_info *******************************/ + +Geometry::Class_info *Geometry::ci_collection[Geometry::wkb_end+1]= +{ + NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -static Geometry::GClassInfo *ci_collection_end = ci_collection + sizeof(ci_collection)/sizeof(ci_collection[0]); +static Geometry::Class_info **ci_collection_end= + Geometry::ci_collection+Geometry::wkb_end + 1; -/***************************** Geometry *******************************/ +Geometry::Class_info::Class_info(const char *name, int type_id, + void(*create_func)(void *)): + m_name(name, strlen(name)), m_type_id(type_id), m_create_func(create_func) +{ + ci_collection[type_id]= this; +} -Geometry::GClassInfo *Geometry::find_class(int type_id) +static void create_point(void *buffer) { - for (GClassInfo *cur_rt = ci_collection; cur_rt < ci_collection_end; ++cur_rt) - { - if (cur_rt->m_type_id == type_id) - { - return cur_rt; - } - } - return NULL; + new(buffer) Gis_point; } - -Geometry::GClassInfo *Geometry::find_class(const char *name, size_t len) + +static void create_linestring(void *buffer) +{ + new(buffer) Gis_line_string; +} + +static void create_polygon(void *buffer) { - for (GClassInfo *cur_rt = ci_collection; - cur_rt < ci_collection_end; ++cur_rt) + new(buffer) Gis_polygon; +} + +static void create_multipoint(void *buffer) +{ + new(buffer) Gis_multi_point; +} + +static void create_multipolygon(void *buffer) +{ + new(buffer) Gis_multi_polygon; +} + +static void create_multilinestring(void *buffer) +{ + new(buffer) Gis_multi_line_string; +} + +static void create_geometrycollection(void *buffer) +{ + new(buffer) Gis_geometry_collection; +} + + + +static Geometry::Class_info point_class("POINT", + Geometry::wkb_point, create_point); + +static Geometry::Class_info linestring_class("LINESTRING", + Geometry::wkb_linestring, + create_linestring); +static Geometry::Class_info polygon_class("POLYGON", + Geometry::wkb_polygon, + create_polygon); +static Geometry::Class_info multipoint_class("MULTIPOINT", + Geometry::wkb_multipoint, + create_multipoint); +static Geometry::Class_info +multilinestring_class("MULTILINESTRING", + Geometry::wkb_multilinestring, create_multilinestring); +static Geometry::Class_info multipolygon_class("MULTIPOLYGON", + Geometry::wkb_multipolygon, + create_multipolygon); +static Geometry::Class_info +geometrycollection_class("GEOMETRYCOLLECTION",Geometry::wkb_geometrycollection, + create_geometrycollection); + +/***************************** Geometry *******************************/ + +Geometry::Class_info *Geometry::find_class(const char *name, uint32 len) +{ + for (Class_info **cur_rt= ci_collection; + cur_rt < ci_collection_end; cur_rt++) { - if ((cur_rt->m_name[len] == 0) && - (my_strnncoll(&my_charset_latin1, (const uchar*)cur_rt->m_name, len, - (const uchar*)name, len) == 0)) - { - return cur_rt; - } + if (*cur_rt && + ((*cur_rt)->m_name.length == len) && + (my_strnncoll(&my_charset_latin1, + (const uchar*) (*cur_rt)->m_name.str, len, + (const uchar*) name, len) == 0)) + return *cur_rt; } - return NULL; + return 0; } -int Geometry::create_from_wkb(const char *data, uint32 data_len) +Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer, + const char *data, uint32 data_len) { uint32 geom_type; + Geometry *result; if (data_len < 1 + 4) - return 1; + return NULL; data++; -//FIXME: check byte ordering + /* + FIXME: check byte ordering + Also check if we could replace this with one byte + */ geom_type= uint4korr(data); data+= 4; - m_vmt= find_class(geom_type); - if (!m_vmt) - return -1; - m_data= data; - m_data_end= data + data_len; - return 0; + if (!(result= create_by_typeid(buffer, (int) geom_type))) + return NULL; + result->m_data= data; + result->m_data_end= data + data_len; + return result; } -int Geometry::create_from_wkt(GTextReadStream *trs, String *wkt, int init_stream) + +Geometry *Geometry::create_from_wkt(Geometry_buffer *buffer, + Gis_read_stream *trs, String *wkt, + bool init_stream) { - int name_len; - const char *name = trs->get_next_word(&name_len); - if (!name) + LEX_STRING name; + Class_info *ci; + + if (trs->get_next_word(&name)) { trs->set_error_msg("Geometry name expected"); - return -1; + return NULL; } - if (!(m_vmt = find_class(name, name_len))) - return -1; - if (wkt->reserve(1 + 4, 512)) - return 1; - wkt->q_append((char)wkbNDR); - wkt->q_append((uint32)get_class_info()->m_type_id); - if (trs->get_next_symbol() != '(') - { - trs->set_error_msg("'(' expected"); - return -1; - } - if (init_from_wkt(trs, wkt)) return 1; - if (trs->get_next_symbol() != ')') - { - trs->set_error_msg("')' expected"); - return -1; - } + if (!(ci= find_class(name.str, name.length)) || + wkt->reserve(1 + 4, 512)) + return NULL; + (*ci->m_create_func)((void *)buffer); + Geometry *result= (Geometry *)buffer; + + wkt->q_append((char) wkb_ndr); + wkt->q_append((uint32) result->get_class_info()->m_type_id); + if (trs->check_next_symbol('(') || + result->init_from_wkt(trs, wkt) || + trs->check_next_symbol(')')) + return NULL; if (init_stream) { - init_from_wkb(wkt->ptr(), wkt->length()); - shift_wkb_header(); + result->init_from_wkb(wkt->ptr(), wkt->length()); + result->shift_wkb_header(); } - return 0; + return result; } -int Geometry::envelope(String *result) const + +bool Geometry::envelope(String *result) const { MBR mbr; + const char *end; - get_mbr(&mbr); - - if (result->reserve(1+4*3+sizeof(double)*10)) + if (get_mbr(&mbr, &end) || result->reserve(1+4*3+SIZEOF_STORED_DOUBLE*10)) return 1; - result->q_append((char)wkbNDR); - result->q_append((uint32)wkbPolygon); - result->q_append((uint32)1); - result->q_append((uint32)5); + result->q_append((char) wkb_ndr); + result->q_append((uint32) wkb_polygon); + result->q_append((uint32) 1); + result->q_append((uint32) 5); result->q_append(mbr.xmin); result->q_append(mbr.ymin); result->q_append(mbr.xmax); @@ -154,29 +192,151 @@ int Geometry::envelope(String *result) const return 0; } + +/* + Create a point from data. + + SYNPOSIS + create_point() + result Put result here + data Data for point is here. + + RETURN + 0 ok + 1 Can't reallocate 'result' +*/ + +bool Geometry::create_point(String *result, const char *data) const +{ + if (no_data(data, SIZEOF_STORED_DOUBLE * 2) || + result->reserve(1 + 4 + SIZEOF_STORED_DOUBLE * 2)) + return 1; + result->q_append((char) wkb_ndr); + result->q_append((uint32) wkb_point); + /* Copy two double in same format */ + result->q_append(data, SIZEOF_STORED_DOUBLE*2); + return 0; +} + +/* + Create a point from coordinates. + + SYNPOSIS + create_point() + result Put result here + x x coordinate for point + y y coordinate for point + + RETURN + 0 ok + 1 Can't reallocate 'result' +*/ + +bool Geometry::create_point(String *result, double x, double y) const +{ + if (result->reserve(1 + 4 + SIZEOF_STORED_DOUBLE * 2)) + return 1; + + result->q_append((char) wkb_ndr); + result->q_append((uint32) wkb_point); + result->q_append(x); + result->q_append(y); + return 0; +} + +/* + Append N points from packed format to text + + SYNOPSIS + append_points() + txt Append points here + n_points Number of points + data Packed data + offset Offset between points + + RETURN + # end of data +*/ + +const char *Geometry::append_points(String *txt, uint32 n_points, + const char *data, uint32 offset) const +{ + while (n_points--) + { + double d; + data+= offset; + float8get(d, data); + txt->qs_append(d); + txt->qs_append(' '); + float8get(d, data + SIZEOF_STORED_DOUBLE); + data+= SIZEOF_STORED_DOUBLE * 2; + txt->qs_append(d); + txt->qs_append(','); + } + return data; +} + + +/* + Get most bounding rectangle (mbr) for X points + + SYNOPSIS + get_mbr_for_points() + mbr MBR (store rectangle here) + points Number of points + data Packed data + offset Offset between points + + RETURN + 0 Wrong data + # end of data +*/ + +const char *Geometry::get_mbr_for_points(MBR *mbr, const char *data, + uint offset) const +{ + uint32 points; + /* read number of points */ + if (no_data(data, 4)) + return 0; + points= uint4korr(data); + data+= 4; + + if (no_data(data, (SIZEOF_STORED_DOUBLE * 2 + offset) * points)) + return 0; + + /* Calculate MBR for points */ + while (points--) + { + data+= offset; + mbr->add_xy(data, data + SIZEOF_STORED_DOUBLE); + data+= SIZEOF_STORED_DOUBLE * 2; + } + return data; +} + + /***************************** Point *******************************/ -size_t GPoint::get_data_size() const +uint32 Gis_point::get_data_size() const { return POINT_DATA_SIZE; } -int GPoint::init_from_wkt(GTextReadStream *trs, String *wkb) + +bool Gis_point::init_from_wkt(Gis_read_stream *trs, String *wkb) { double x, y; - if (wkb->reserve(sizeof(double)*2)) - return 1; - if (trs->get_next_number(&x)) - return 1; - if (trs->get_next_number(&y)) + if (trs->get_next_number(&x) || trs->get_next_number(&y) || + wkb->reserve(SIZEOF_STORED_DOUBLE * 2)) return 1; wkb->q_append(x); wkb->q_append(y); - return 0; } -int GPoint::get_data_as_wkt(String *txt) const + +bool Gis_point::get_data_as_wkt(String *txt, const char **end) const { double x, y; if (get_xy(&x, &y)) @@ -186,324 +346,260 @@ int GPoint::get_data_as_wkt(String *txt) const txt->qs_append(x); txt->qs_append(' '); txt->qs_append(y); + *end= m_data+ POINT_DATA_SIZE; return 0; } -int GPoint::get_mbr(MBR *mbr) const + +bool Gis_point::get_mbr(MBR *mbr, const char **end) const { double x, y; if (get_xy(&x, &y)) return 1; mbr->add_xy(x, y); + *end= m_data+ POINT_DATA_SIZE; return 0; } +const Geometry::Class_info *Gis_point::get_class_info() const +{ + return &point_class; +} + + /***************************** LineString *******************************/ -size_t GLineString::get_data_size() const +uint32 Gis_line_string::get_data_size() const { - uint32 n_points = uint4korr(m_data); - - return 4 + n_points*POINT_DATA_SIZE; + if (no_data(m_data, 4)) + return GET_SIZE_ERROR; + return 4 + uint4korr(m_data) * POINT_DATA_SIZE; } -int GLineString::init_from_wkt(GTextReadStream *trs, String *wkb) + +bool Gis_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb) { - uint32 n_points = 0; - int np_pos = wkb->length(); - GPoint p; + uint32 n_points= 0; + uint32 np_pos= wkb->length(); + Gis_point p; if (wkb->reserve(4, 512)) return 1; - - wkb->q_append((uint32)n_points); + wkb->length(wkb->length()+4); // Reserve space for points for (;;) { if (p.init_from_wkt(trs, wkb)) return 1; - ++n_points; - if (trs->get_next_toc_type() == GTextReadStream::comma) - trs->get_next_symbol(); - else break; + n_points++; + if (trs->skip_char(',')) // Didn't find ',' + break; } - - if (n_points<2) + if (n_points < 2) { trs->set_error_msg("Too few points in LINESTRING"); return 1; } - - wkb->WriteAtPosition(np_pos, n_points); - + wkb->write_at_position(np_pos, n_points); return 0; } -int GLineString::get_data_as_wkt(String *txt) const + +bool Gis_line_string::get_data_as_wkt(String *txt, const char **end) const { uint32 n_points; - const char *data = m_data; + const char *data= m_data; if (no_data(data, 4)) return 1; - - n_points = uint4korr(data); + n_points= uint4korr(data); data += 4; - if (no_data(data, sizeof(double) * 2 * n_points)) + if (n_points < 1 || + no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points) || + txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points)) return 1; - if (txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points)) - return 1; - for (; n_points>0; --n_points) + while (n_points--) { double x, y; float8get(x, data); - data += sizeof(double); - float8get(y, data); - data += sizeof(double); + float8get(y, data + SIZEOF_STORED_DOUBLE); + data+= SIZEOF_STORED_DOUBLE * 2; txt->qs_append(x); txt->qs_append(' '); txt->qs_append(y); txt->qs_append(','); } - txt->length(txt->length() - 1); + txt->length(txt->length() - 1); // Remove end ',' + *end= data; return 0; } -int GLineString::get_mbr(MBR *mbr) const -{ - uint32 n_points; - const char *data = m_data; - - if (no_data(data, 4)) - return 1; - - n_points = uint4korr(data); - data += 4; - if (no_data(data, sizeof(double) * 2 * n_points)) - return 1; - for (; n_points>0; --n_points) - { - mbr->add_xy(data, data + 8); - data += 8+8; - } - - return 0; +bool Gis_line_string::get_mbr(MBR *mbr, const char **end) const +{ + return (*end=get_mbr_for_points(mbr, m_data, 0)) == 0; } -int GLineString::length(double *len) const + +int Gis_line_string::length(double *len) const { uint32 n_points; double prev_x, prev_y; - const char *data = m_data; + const char *data= m_data; - *len=0; + *len= 0; // In case of errors if (no_data(data, 4)) return 1; - n_points = uint4korr(data); - data += 4; - - if (no_data(data, sizeof(double) * 2 * n_points)) + n_points= uint4korr(data); + data+= 4; + if (n_points < 1 || no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points)) return 1; - --n_points; float8get(prev_x, data); - data += 8; - float8get(prev_y, data); - data += 8; + float8get(prev_y, data + SIZEOF_STORED_DOUBLE); + data+= SIZEOF_STORED_DOUBLE*2; - for (; n_points>0; --n_points) + while (--n_points) { double x, y; float8get(x, data); - data += 8; - float8get(y, data); - data += 8; - *len+=sqrt(pow(prev_x-x,2)+pow(prev_y-y,2)); - prev_x=x; - prev_y=y; + float8get(y, data + SIZEOF_STORED_DOUBLE); + data+= SIZEOF_STORED_DOUBLE * 2; + *len+= sqrt(pow(prev_x-x,2)+pow(prev_y-y,2)); + prev_x= x; + prev_y= y; } return 0; } -int GLineString::is_closed(int *closed) const +int Gis_line_string::is_closed(int *closed) const { uint32 n_points; double x1, y1, x2, y2; - - const char *data = m_data; + const char *data= m_data; if (no_data(data, 4)) return 1; - n_points = uint4korr(data); - data += 4; - if (no_data(data, (8+8) * n_points)) + n_points= uint4korr(data); + data+= 4; + if (no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points)) return 1; + + /* Get first point */ float8get(x1, data); - data += 8; - float8get(y1, data); - data += 8 + (n_points-2)*POINT_DATA_SIZE; - float8get(x2, data); - data += 8; - float8get(y2, data); + float8get(y1, data + SIZEOF_STORED_DOUBLE); - *closed=(x1==x2)&&(y1==y2); + /* get last point */ + data+= SIZEOF_STORED_DOUBLE*2 + (n_points-2)*POINT_DATA_SIZE; + float8get(x2, data); + float8get(y2, data + SIZEOF_STORED_DOUBLE); + *closed= (x1==x2) && (y1==y2); return 0; } -int GLineString::num_points(uint32 *n_points) const + +int Gis_line_string::num_points(uint32 *n_points) const { - *n_points = uint4korr(m_data); + *n_points= uint4korr(m_data); return 0; } -int GLineString::start_point(String *result) const -{ - const char *data= m_data + 4; - if (no_data(data, 8 + 8)) - return 1; - - if (result->reserve(1 + 4 + sizeof(double) * 2)) - return 1; - - result->q_append((char) wkbNDR); - result->q_append((uint32) wkbPoint); - double d; - float8get(d, data); - result->q_append(d); - float8get(d, data + 8); - result->q_append(d); - return 0; +int Gis_line_string::start_point(String *result) const +{ + /* +4 is for skipping over number of points */ + return create_point(result, m_data + 4); } -int GLineString::end_point(String *result) const + +int Gis_line_string::end_point(String *result) const { - const char *data= m_data; uint32 n_points; - - if (no_data(data, 4)) + if (no_data(m_data, 4)) return 1; - n_points= uint4korr(data); - - data+= 4 + (n_points - 1) * POINT_DATA_SIZE; - - if (no_data(data, 8 + 8)) - return 1; - - if (result->reserve(1 + 4 + sizeof(double) * 2)) - return 1; - result->q_append((char) wkbNDR); - result->q_append((uint32) wkbPoint); - double d; - float8get(d, data); - result->q_append(d); - float8get(d, data + 8); - result->q_append(d); - - return 0; + n_points= uint4korr(m_data); + return create_point(result, m_data + 4 + (n_points - 1) * POINT_DATA_SIZE); } -int GLineString::point_n(uint32 num, String *result) const +int Gis_line_string::point_n(uint32 num, String *result) const { - const char *data= m_data; uint32 n_points; - - if (no_data(data, 4)) + if (no_data(m_data, 4)) return 1; - n_points= uint4korr(data); - + n_points= uint4korr(m_data); if ((uint32) (num - 1) >= n_points) // means (num > n_points || num < 1) return 1; - data+= 4 + (num - 1) * POINT_DATA_SIZE; - - if (no_data(data, 8 + 8)) - return 1; - if (result->reserve(1 + 4 + sizeof(double) * 2)) - return 1; - - result->q_append((char) wkbNDR); - result->q_append((uint32) wkbPoint); - double d; - float8get(d, data); - result->q_append(d); - float8get(d, data + 8); - result->q_append(d); + return create_point(result, m_data + 4 + (num - 1) * POINT_DATA_SIZE); +} - return 0; +const Geometry::Class_info *Gis_line_string::get_class_info() const +{ + return &linestring_class; } + /***************************** Polygon *******************************/ -size_t GPolygon::get_data_size() const +uint32 Gis_polygon::get_data_size() const { - uint32 n_linear_rings = 0; - const char *data = m_data; + uint32 n_linear_rings; + const char *data= m_data; + if (no_data(data, 4)) - return 1; + return GET_SIZE_ERROR; + n_linear_rings= uint4korr(data); + data+= 4; - n_linear_rings = uint4korr(data); - data += 4; - for (; n_linear_rings>0; --n_linear_rings) + while (n_linear_rings--) { if (no_data(data, 4)) - return 1; - data += 4 + uint4korr(data)*POINT_DATA_SIZE; + return GET_SIZE_ERROR; + data+= 4 + uint4korr(data)*POINT_DATA_SIZE; } - return data - m_data; + return (uint32) (data - m_data); } -int GPolygon::init_from_wkt(GTextReadStream *trs, String *wkb) + +bool Gis_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb) { - uint32 n_linear_rings = 0; - int lr_pos = wkb->length(); + uint32 n_linear_rings= 0; + uint32 lr_pos= wkb->length(); + int closed; if (wkb->reserve(4, 512)) return 1; - - wkb->q_append((uint32)n_linear_rings); - + wkb->length(wkb->length()+4); // Reserve space for points for (;;) { - GLineString ls; - size_t ls_pos=wkb->length(); - if (trs->get_next_symbol() != '(') - { - trs->set_error_msg("'(' expected"); + Gis_line_string ls; + uint32 ls_pos=wkb->length(); + if (trs->check_next_symbol('(') || + ls.init_from_wkt(trs, wkb) || + trs->check_next_symbol(')')) return 1; - } - if (ls.init_from_wkt(trs, wkb)) - return 1; - if (trs->get_next_symbol() != ')') - { - trs->set_error_msg("')' expected"); - return 1; - } + ls.init_from_wkb(wkb->ptr()+ls_pos, wkb->length()-ls_pos); - int closed; - ls.is_closed(&closed); - if (!closed) + if (ls.is_closed(&closed) || !closed) { trs->set_error_msg("POLYGON's linear ring isn't closed"); return 1; } - ++n_linear_rings; - if (trs->get_next_toc_type() == GTextReadStream::comma) - trs->get_next_symbol(); - else + n_linear_rings++; + if (trs->skip_char(',')) // Didn't find ',' break; } - wkb->WriteAtPosition(lr_pos, n_linear_rings); + wkb->write_at_position(lr_pos, n_linear_rings); return 0; } -int GPolygon::get_data_as_wkt(String *txt) const + +bool Gis_polygon::get_data_as_wkt(String *txt, const char **end) const { uint32 n_linear_rings; const char *data= m_data; @@ -514,1059 +610,1045 @@ int GPolygon::get_data_as_wkt(String *txt) const n_linear_rings= uint4korr(data); data+= 4; - for (; n_linear_rings > 0; --n_linear_rings) + while (n_linear_rings--) { + uint32 n_points; if (no_data(data, 4)) return 1; - uint32 n_points= uint4korr(data); + n_points= uint4korr(data); data+= 4; - if (no_data(data, (8 + 8) * n_points)) - return 1; - - if (txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points)) + if (no_data(data, (SIZEOF_STORED_DOUBLE*2) * n_points) || + txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points)) return 1; txt->qs_append('('); - for (; n_points>0; --n_points) - { - double d; - float8get(d, data); - txt->qs_append(d); - txt->qs_append(' '); - float8get(d, data + 8); - txt->qs_append(d); - txt->qs_append(','); - - data+= 8 + 8; - } - (*txt) [txt->length() - 1]= ')'; + data= append_points(txt, n_points, data, 0); + (*txt) [txt->length() - 1]= ')'; // Replace end ',' txt->qs_append(','); } - txt->length(txt->length() - 1); + txt->length(txt->length() - 1); // Remove end ',' + *end= data; return 0; } -int GPolygon::get_mbr(MBR *mbr) const + +bool Gis_polygon::get_mbr(MBR *mbr, const char **end) const { uint32 n_linear_rings; + const char *data= m_data; - const char *data = m_data; if (no_data(data, 4)) return 1; - n_linear_rings = uint4korr(data); - data += 4; - for (; n_linear_rings>0; --n_linear_rings) + n_linear_rings= uint4korr(data); + data+= 4; + + while (n_linear_rings--) { - if (no_data(data, 4)) - return 1; - uint32 n_points = uint4korr(data); - data += 4; - if (no_data(data, (8+8) * n_points)) + if (!(data= get_mbr_for_points(mbr, data, 0))) return 1; - for (; n_points>0; --n_points) - { - mbr->add_xy(data, data + 8); - data += 8+8; - } } + *end= data; return 0; } -int GPolygon::area(double *ar) const + +int Gis_polygon::area(double *ar, const char **end_of_data) const { uint32 n_linear_rings; - double result = -1.0; + double result= -1.0; + const char *data= m_data; - const char *data = m_data; if (no_data(data, 4)) return 1; - n_linear_rings = uint4korr(data); - data += 4; - for (; n_linear_rings>0; --n_linear_rings) + n_linear_rings= uint4korr(data); + data+= 4; + + while (n_linear_rings--) { double prev_x, prev_y; - double lr_area=0; + double lr_area= 0; + uint32 n_points; + if (no_data(data, 4)) return 1; - uint32 n_points = uint4korr(data); - if (no_data(data, (8+8) * n_points)) + n_points= uint4korr(data); + if (no_data(data, (SIZEOF_STORED_DOUBLE*2) * n_points)) return 1; float8get(prev_x, data+4); - float8get(prev_y, data+(4+8)); - data += (4+8+8); + float8get(prev_y, data+(4+SIZEOF_STORED_DOUBLE)); + data+= (4+SIZEOF_STORED_DOUBLE*2); - --n_points; - for (; n_points>0; --n_points) + while (--n_points) // One point is already read { double x, y; float8get(x, data); - float8get(y, data + 8); - lr_area+=(prev_x+x)*(prev_y-y); - prev_x=x; - prev_y=y; - data += (8+8); + float8get(y, data + SIZEOF_STORED_DOUBLE); + data+= (SIZEOF_STORED_DOUBLE*2); + /* QQ: Is the following prev_x+x right ? */ + lr_area+= (prev_x + x)* (prev_y - y); + prev_x= x; + prev_y= y; } - lr_area=fabs(lr_area)/2; - if (result==-1) result=lr_area; - else result-=lr_area; + lr_area= fabs(lr_area)/2; + if (result == -1.0) + result= lr_area; + else + result-= lr_area; } - *ar=fabs(result); + *ar= fabs(result); + *end_of_data= data; return 0; } -int GPolygon::exterior_ring(String *result) const +int Gis_polygon::exterior_ring(String *result) const { - uint32 n_points; - const char *data = m_data + 4; // skip n_linerings + uint32 n_points, length; + const char *data= m_data + 4; // skip n_linerings if (no_data(data, 4)) return 1; - n_points = uint4korr(data); - data += 4; - if (no_data(data, n_points * POINT_DATA_SIZE)) - return 1; - - if (result->reserve(1+4+4+ n_points * POINT_DATA_SIZE)) + n_points= uint4korr(data); + data+= 4; + length= n_points * POINT_DATA_SIZE; + if (no_data(data, length) || result->reserve(1+4+4+ length)) return 1; - result->q_append((char)wkbNDR); - result->q_append((uint32)wkbLineString); + result->q_append((char) wkb_ndr); + result->q_append((uint32) wkb_linestring); result->q_append(n_points); result->q_append(data, n_points * POINT_DATA_SIZE); - return 0; } -int GPolygon::num_interior_ring(uint32 *n_int_rings) const + +int Gis_polygon::num_interior_ring(uint32 *n_int_rings) const { - const char *data = m_data; - if (no_data(data, 4)) + if (no_data(m_data, 4)) return 1; - *n_int_rings = uint4korr(data); - --(*n_int_rings); - + *n_int_rings= uint4korr(m_data)-1; return 0; } -int GPolygon::interior_ring_n(uint32 num, String *result) const + +int Gis_polygon::interior_ring_n(uint32 num, String *result) const { - const char *data = m_data; + const char *data= m_data; uint32 n_linear_rings; uint32 n_points; + uint32 points_size; if (no_data(data, 4)) return 1; + n_linear_rings= uint4korr(data); + data+= 4; - n_linear_rings = uint4korr(data); - data += 4; - if ((num >= n_linear_rings) || (num < 1)) - return -1; + if (num >= n_linear_rings || num < 1) + return 1; - for (; num > 0; --num) + while (num--) { if (no_data(data, 4)) return 1; - data += 4 + uint4korr(data) * POINT_DATA_SIZE; + data+= 4 + uint4korr(data) * POINT_DATA_SIZE; } if (no_data(data, 4)) return 1; - n_points = uint4korr(data); - int points_size = n_points * POINT_DATA_SIZE; - data += 4; - if (no_data(data, points_size)) - return 1; - - if (result->reserve(1+4+4+ points_size)) + n_points= uint4korr(data); + points_size= n_points * POINT_DATA_SIZE; + data+= 4; + if (no_data(data, points_size) || result->reserve(1+4+4+ points_size)) return 1; - result->q_append((char)wkbNDR); - result->q_append((uint32)wkbLineString); + result->q_append((char) wkb_ndr); + result->q_append((uint32) wkb_linestring); result->q_append(n_points); result->q_append(data, points_size); return 0; } -int GPolygon::centroid_xy(double *x, double *y) const + +int Gis_polygon::centroid_xy(double *x, double *y) const { uint32 n_linear_rings; - uint32 i; double res_area, res_cx, res_cy; - const char *data = m_data; + const char *data= m_data; + bool first_loop= 1; LINT_INIT(res_area); LINT_INIT(res_cx); LINT_INIT(res_cy); if (no_data(data, 4)) return 1; - n_linear_rings = uint4korr(data); - data += 4; + n_linear_rings= uint4korr(data); + data+= 4; - for (i = 0; i < n_linear_rings; ++i) + while (n_linear_rings--) { - if (no_data(data, 4)) - return 1; - uint32 n_points = uint4korr(data); + uint32 n_points, org_n_points; double prev_x, prev_y; - double cur_area = 0; - double cur_cx = 0; - double cur_cy = 0; + double cur_area= 0; + double cur_cx= 0; + double cur_cy= 0; - data += 4; - if (no_data(data, (8+8) * n_points)) + if (no_data(data, 4)) + return 1; + org_n_points= n_points= uint4korr(data); + data+= 4; + if (no_data(data, (SIZEOF_STORED_DOUBLE*2) * n_points)) return 1; float8get(prev_x, data); - float8get(prev_y, data+8); - data += (8+8); + float8get(prev_y, data+SIZEOF_STORED_DOUBLE); + data+= (SIZEOF_STORED_DOUBLE*2); - uint32 n = n_points - 1; - for (; n > 0; --n) + while (--n_points) // One point is already read { double x, y; float8get(x, data); - float8get(y, data + 8); - - cur_area += (prev_x + x) * (prev_y - y); - cur_cx += x; - cur_cy += y; - prev_x = x; - prev_y = y; - data += (8+8); + float8get(y, data + SIZEOF_STORED_DOUBLE); + data+= (SIZEOF_STORED_DOUBLE*2); + /* QQ: Is the following prev_x+x right ? */ + cur_area+= (prev_x + x) * (prev_y - y); + cur_cx+= x; + cur_cy+= y; + prev_x= x; + prev_y= y; } - cur_area = fabs(cur_area) / 2; - cur_cx = cur_cx / (n_points - 1); - cur_cy = cur_cy / (n_points - 1); + cur_area= fabs(cur_area) / 2; + cur_cx= cur_cx / (org_n_points - 1); + cur_cy= cur_cy / (org_n_points - 1); - if (i) + if (!first_loop) { - double d_area = res_area - cur_area; + double d_area= res_area - cur_area; if (d_area <= 0) return 1; - res_cx = (res_area * res_cx - cur_area * cur_cx) / d_area; - res_cy = (res_area * res_cy - cur_area * cur_cy) / d_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; } else { - res_area = cur_area; - res_cx = cur_cx; - res_cy = cur_cy; + first_loop= 0; + res_area= cur_area; + res_cx= cur_cx; + res_cy= cur_cy; } } - *x = res_cx; - *y = res_cy; - + *x= res_cx; + *y= res_cy; return 0; } -int GPolygon::centroid(String *result) const + +int Gis_polygon::centroid(String *result) const { double x, y; - - this->centroid_xy(&x, &y); - if (result->reserve(1 + 4 + sizeof(double) * 2)) + if (centroid_xy(&x, &y)) return 1; + return create_point(result, x, y); +} - result->q_append((char)wkbNDR); - result->q_append((uint32)wkbPoint); - result->q_append(x); - result->q_append(y); - - return 0; +const Geometry::Class_info *Gis_polygon::get_class_info() const +{ + return &polygon_class; } /***************************** MultiPoint *******************************/ -size_t GMultiPoint::get_data_size() const +uint32 Gis_multi_point::get_data_size() const { + if (no_data(m_data, 4)) + return GET_SIZE_ERROR; return 4 + uint4korr(m_data)*(POINT_DATA_SIZE + WKB_HEADER_SIZE); } -int GMultiPoint::init_from_wkt(GTextReadStream *trs, String *wkb) + +bool Gis_multi_point::init_from_wkt(Gis_read_stream *trs, String *wkb) { - uint32 n_points = 0; - int np_pos = wkb->length(); - GPoint p; + uint32 n_points= 0; + uint32 np_pos= wkb->length(); + Gis_point p; if (wkb->reserve(4, 512)) return 1; - wkb->q_append((uint32)n_points); + wkb->length(wkb->length()+4); // Reserve space for points for (;;) { if (wkb->reserve(1+4, 512)) return 1; - wkb->q_append((char)wkbNDR); - wkb->q_append((uint32)wkbPoint); + wkb->q_append((char) wkb_ndr); + wkb->q_append((uint32) wkb_point); if (p.init_from_wkt(trs, wkb)) return 1; - ++n_points; - if (trs->get_next_toc_type() == GTextReadStream::comma) - trs->get_next_symbol(); - else + n_points++; + if (trs->skip_char(',')) // Didn't find ',' break; } - wkb->WriteAtPosition(np_pos, n_points); - + wkb->write_at_position(np_pos, n_points); // Store number of found points return 0; } -int GMultiPoint::get_data_as_wkt(String *txt) const + +bool Gis_multi_point::get_data_as_wkt(String *txt, const char **end) const { uint32 n_points; - const char *data= m_data; - if (no_data(data, 4)) - return 1; - - n_points= uint4korr(data); - data+= 4; - if (no_data(data, n_points * (8 + 8 + WKB_HEADER_SIZE))) + if (no_data(m_data, 4)) return 1; - if (txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points)) + n_points= uint4korr(m_data); + if (no_data(m_data+4, + n_points * (SIZEOF_STORED_DOUBLE * 2 + WKB_HEADER_SIZE)) || + txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points)) return 1; - - for (; n_points>0; --n_points) - { - double d; - float8get(d, data + WKB_HEADER_SIZE); - txt->qs_append(d); - txt->qs_append(' '); - float8get(d, data + WKB_HEADER_SIZE + 8); - txt->qs_append(d); - txt->qs_append(','); - data+= WKB_HEADER_SIZE + 8 + 8; - } - txt->length(txt->length()-1); + *end= append_points(txt, n_points, m_data+4, WKB_HEADER_SIZE); + txt->length(txt->length()-1); // Remove end ',' return 0; } -int GMultiPoint::get_mbr(MBR *mbr) const + +bool Gis_multi_point::get_mbr(MBR *mbr, const char **end) const { - uint32 n_points; - const char *data = m_data; - if (no_data(data, 4)) - return 1; - n_points = uint4korr(data); - data += 4; - if (no_data(data, n_points * (8+8+WKB_HEADER_SIZE))) - return 1; - for (; n_points>0; --n_points) - { - mbr->add_xy(data + WKB_HEADER_SIZE, data + 8 + WKB_HEADER_SIZE); - data += (8+8+WKB_HEADER_SIZE); - } - return 0; + return (*end= get_mbr_for_points(mbr, m_data, WKB_HEADER_SIZE)) == 0; } -int GMultiPoint::num_geometries(uint32 *num) const + +int Gis_multi_point::num_geometries(uint32 *num) const { - *num = uint4korr(m_data); + *num= uint4korr(m_data); return 0; } -int GMultiPoint::geometry_n(uint32 num, String *result) const + +int Gis_multi_point::geometry_n(uint32 num, String *result) const { const char *data= m_data; uint32 n_points; + if (no_data(data, 4)) return 1; n_points= uint4korr(data); - data+= 4; - if ((num > n_points) || (num < 1)) - return -1; - data+= (num - 1) * (WKB_HEADER_SIZE + POINT_DATA_SIZE); - if (result->reserve(WKB_HEADER_SIZE + POINT_DATA_SIZE)) + data+= 4+ (num - 1) * (WKB_HEADER_SIZE + POINT_DATA_SIZE); + + if (num > n_points || num < 1 || + no_data(data, WKB_HEADER_SIZE + POINT_DATA_SIZE) || + result->reserve(WKB_HEADER_SIZE + POINT_DATA_SIZE)) return 1; - result->q_append(data, WKB_HEADER_SIZE + POINT_DATA_SIZE); + result->q_append(data, WKB_HEADER_SIZE + POINT_DATA_SIZE); return 0; } +const Geometry::Class_info *Gis_multi_point::get_class_info() const +{ + return &multipoint_class; +} + + /***************************** MultiLineString *******************************/ -size_t GMultiLineString::get_data_size() const +uint32 Gis_multi_line_string::get_data_size() const { - uint32 n_line_strings = 0; - const char *data = m_data; + uint32 n_line_strings; + const char *data= m_data; + if (no_data(data, 4)) - return 1; - n_line_strings = uint4korr(data); - data += 4; + return GET_SIZE_ERROR; + n_line_strings= uint4korr(data); + data+= 4; - for (; n_line_strings>0; --n_line_strings) + while (n_line_strings--) { if (no_data(data, WKB_HEADER_SIZE + 4)) - return 1; - data += WKB_HEADER_SIZE + 4 + uint4korr(data + WKB_HEADER_SIZE) * POINT_DATA_SIZE; + return GET_SIZE_ERROR; + data+= (WKB_HEADER_SIZE + 4 + uint4korr(data + WKB_HEADER_SIZE) * + POINT_DATA_SIZE); } - return data - m_data; + return (uint32) (data - m_data); } -int GMultiLineString::init_from_wkt(GTextReadStream *trs, String *wkb) + +bool Gis_multi_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb) { - uint32 n_line_strings = 0; - int ls_pos = wkb->length(); + uint32 n_line_strings= 0; + uint32 ls_pos= wkb->length(); if (wkb->reserve(4, 512)) return 1; - - wkb->q_append((uint32)n_line_strings); + wkb->length(wkb->length()+4); // Reserve space for points for (;;) { - GLineString ls; + Gis_line_string ls; if (wkb->reserve(1+4, 512)) return 1; - wkb->q_append((char)wkbNDR); - wkb->q_append((uint32)wkbLineString); - - if (trs->get_next_symbol() != '(') - { - trs->set_error_msg("'(' expected"); - return 1; - } - if (ls.init_from_wkt(trs, wkb)) - return 1; + wkb->q_append((char) wkb_ndr); + wkb->q_append((uint32) wkb_linestring); - if (trs->get_next_symbol() != ')') - { - trs->set_error_msg("')' expected"); + if (trs->check_next_symbol('(') || + ls.init_from_wkt(trs, wkb) || + trs->check_next_symbol(')')) return 1; - } - ++n_line_strings; - if (trs->get_next_toc_type() == GTextReadStream::comma) - trs->get_next_symbol(); - else + n_line_strings++; + if (trs->skip_char(',')) // Didn't find ',' break; } - wkb->WriteAtPosition(ls_pos, n_line_strings); - + wkb->write_at_position(ls_pos, n_line_strings); return 0; } -int GMultiLineString::get_data_as_wkt(String *txt) const + +bool Gis_multi_line_string::get_data_as_wkt(String *txt, + const char **end) const { uint32 n_line_strings; const char *data= m_data; + if (no_data(data, 4)) return 1; n_line_strings= uint4korr(data); data+= 4; - for (; n_line_strings > 0; --n_line_strings) + + while (n_line_strings--) { + uint32 n_points; if (no_data(data, (WKB_HEADER_SIZE + 4))) return 1; - uint32 n_points= uint4korr(data + WKB_HEADER_SIZE); + n_points= uint4korr(data + WKB_HEADER_SIZE); data+= WKB_HEADER_SIZE + 4; - if (no_data(data, n_points * (8 + 8))) - return 1; - - if (txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points)) + if (no_data(data, n_points * (SIZEOF_STORED_DOUBLE*2)) || + txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points)) return 1; txt->qs_append('('); - for (; n_points>0; --n_points) - { - double d; - float8get(d, data); - txt->qs_append(d); - txt->qs_append(' '); - float8get(d, data + 8); - txt->qs_append(d); - txt->qs_append(','); - data+= 8 + 8; - } - (*txt) [txt->length() - 1] = ')'; + data= append_points(txt, n_points, data, 0); + (*txt) [txt->length() - 1]= ')'; txt->qs_append(','); } txt->length(txt->length() - 1); + *end= data; return 0; } -int GMultiLineString::get_mbr(MBR *mbr) const + +bool Gis_multi_line_string::get_mbr(MBR *mbr, const char **end) const { uint32 n_line_strings; - const char *data = m_data; + const char *data= m_data; + if (no_data(data, 4)) return 1; - n_line_strings = uint4korr(data); - data += 4; + n_line_strings= uint4korr(data); + data+= 4; - for (; n_line_strings>0; --n_line_strings) + while (n_line_strings--) { - if (no_data(data, WKB_HEADER_SIZE + 4)) - return 1; - uint32 n_points = uint4korr(data + WKB_HEADER_SIZE); - data += 4+WKB_HEADER_SIZE; - if (no_data(data, (8+8)*n_points)) + data+= WKB_HEADER_SIZE; + if (!(data= get_mbr_for_points(mbr, data, 0))) return 1; - - for (; n_points>0; --n_points) - { - mbr->add_xy(data, data + 8); - data += 8+8; - } } + *end= data; return 0; } -int GMultiLineString::num_geometries(uint32 *num) const + +int Gis_multi_line_string::num_geometries(uint32 *num) const { - *num = uint4korr(m_data); + *num= uint4korr(m_data); return 0; } -int GMultiLineString::geometry_n(uint32 num, String *result) const + +int Gis_multi_line_string::geometry_n(uint32 num, String *result) const { - uint32 n_line_strings; + uint32 n_line_strings, n_points, length; const char *data= m_data; + if (no_data(data, 4)) return 1; n_line_strings= uint4korr(data); data+= 4; if ((num > n_line_strings) || (num < 1)) - return -1; + return 1; - for (; num > 0; --num) + for (;;) { if (no_data(data, WKB_HEADER_SIZE + 4)) return 1; - uint32 n_points= uint4korr(data + WKB_HEADER_SIZE); - if (num == 1) - { - if (result->reserve(WKB_HEADER_SIZE + 4 + POINT_DATA_SIZE * n_points)) - return 1; - result->q_append(data, WKB_HEADER_SIZE + 4 + POINT_DATA_SIZE *n_points); + n_points= uint4korr(data + WKB_HEADER_SIZE); + length= WKB_HEADER_SIZE + 4+ POINT_DATA_SIZE * n_points; + if (no_data(data, length)) + return 1; + if (!--num) break; - } - else - { - data+= WKB_HEADER_SIZE + 4 + POINT_DATA_SIZE * n_points; - } + data+= length; } - return 0; + return result->append(data, length, (uint32) 0); } -int GMultiLineString::length(double *len) const + +int Gis_multi_line_string::length(double *len) const { uint32 n_line_strings; - const char *data = m_data; + const char *data= m_data; + if (no_data(data, 4)) return 1; - n_line_strings = uint4korr(data); - data += 4; + n_line_strings= uint4korr(data); + data+= 4; + *len=0; - for (; n_line_strings>0; --n_line_strings) + while (n_line_strings--) { double ls_len; - GLineString ls; - data += WKB_HEADER_SIZE; - ls.init_from_wkb(data, m_data_end - data); + Gis_line_string ls; + data+= WKB_HEADER_SIZE; + ls.init_from_wkb(data, (uint32) (m_data_end - data)); if (ls.length(&ls_len)) return 1; - *len+=ls_len; - data += ls.get_data_size(); + *len+= ls_len; + /* + We know here that ls was ok, so we can call the trivial function + Gis_line_string::get_data_size without error checking + */ + data+= ls.get_data_size(); } return 0; } -int GMultiLineString::is_closed(int *closed) const + +int Gis_multi_line_string::is_closed(int *closed) const { uint32 n_line_strings; - const char *data = m_data; - if (no_data(data, 1)) + const char *data= m_data; + + if (no_data(data, 4 + WKB_HEADER_SIZE)) return 1; - n_line_strings = uint4korr(data); - data += 4 + WKB_HEADER_SIZE; - for (; n_line_strings>0; --n_line_strings) + n_line_strings= uint4korr(data); + data+= 4 + WKB_HEADER_SIZE; + + while (n_line_strings--) { - GLineString ls; - ls.init_from_wkb(data, m_data_end - data); + Gis_line_string ls; + if (no_data(data, 0)) + return 1; + ls.init_from_wkb(data, (uint32) (m_data_end - data)); if (ls.is_closed(closed)) return 1; if (!*closed) return 0; - data += ls.get_data_size() + WKB_HEADER_SIZE; + /* + We know here that ls was ok, so we can call the trivial function + Gis_line_string::get_data_size without error checking + */ + data+= ls.get_data_size() + WKB_HEADER_SIZE; } return 0; } +const Geometry::Class_info *Gis_multi_line_string::get_class_info() const +{ + return &multilinestring_class; +} + + /***************************** MultiPolygon *******************************/ -size_t GMultiPolygon::get_data_size() const +uint32 Gis_multi_polygon::get_data_size() const { uint32 n_polygons; - const char *data = m_data; + const char *data= m_data; + if (no_data(data, 4)) - return 1; - n_polygons = uint4korr(data); - data += 4; + return GET_SIZE_ERROR; + n_polygons= uint4korr(data); + data+= 4; - for (; n_polygons>0; --n_polygons) + while (n_polygons--) { + uint32 n_linear_rings; if (no_data(data, 4 + WKB_HEADER_SIZE)) - return 1; - uint32 n_linear_rings = uint4korr(data + WKB_HEADER_SIZE); - data += 4 + WKB_HEADER_SIZE; + return GET_SIZE_ERROR; + + n_linear_rings= uint4korr(data + WKB_HEADER_SIZE); + data+= 4 + WKB_HEADER_SIZE; - for (; n_linear_rings > 0; --n_linear_rings) + while (n_linear_rings--) { - data += 4 + uint4korr(data) * POINT_DATA_SIZE; + if (no_data(data, 4)) + return GET_SIZE_ERROR; + data+= 4 + uint4korr(data) * POINT_DATA_SIZE; } } - return data - m_data; + return (uint32) (data - m_data); } -int GMultiPolygon::init_from_wkt(GTextReadStream *trs, String *wkb) + +bool Gis_multi_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb) { - uint32 n_polygons = 0; - int np_pos = wkb->length(); - GPolygon p; + uint32 n_polygons= 0; + int np_pos= wkb->length(); + Gis_polygon p; if (wkb->reserve(4, 512)) return 1; - - wkb->q_append((uint32)n_polygons); + wkb->length(wkb->length()+4); // Reserve space for points for (;;) { if (wkb->reserve(1+4, 512)) return 1; - wkb->q_append((char)wkbNDR); - wkb->q_append((uint32)wkbPolygon); + wkb->q_append((char) wkb_ndr); + wkb->q_append((uint32) wkb_polygon); - if (trs->get_next_symbol() != '(') - { - trs->set_error_msg("'(' expected"); - return 1; - } - if (p.init_from_wkt(trs, wkb)) - return 1; - if (trs->get_next_symbol() != ')') - { - trs->set_error_msg("')' expected"); + if (trs->check_next_symbol('(') || + p.init_from_wkt(trs, wkb) || + trs->check_next_symbol(')')) return 1; - } - ++n_polygons; - if (trs->get_next_toc_type() == GTextReadStream::comma) - trs->get_next_symbol(); - else + n_polygons++; + if (trs->skip_char(',')) // Didn't find ',' break; } - wkb->WriteAtPosition(np_pos, n_polygons); + wkb->write_at_position(np_pos, n_polygons); return 0; } -int GMultiPolygon::get_data_as_wkt(String *txt) const + +bool Gis_multi_polygon::get_data_as_wkt(String *txt, const char **end) const { uint32 n_polygons; const char *data= m_data; + if (no_data(data, 4)) return 1; n_polygons= uint4korr(data); data+= 4; - for (; n_polygons>0; --n_polygons) + while (n_polygons--) { - if (no_data(data, 4 + WKB_HEADER_SIZE)) - return 1; - data+= WKB_HEADER_SIZE; - uint32 n_linear_rings= uint4korr(data); - data+= 4; - - if (txt->reserve(1, 512)) + uint32 n_linear_rings; + if (no_data(data, 4 + WKB_HEADER_SIZE) || + txt->reserve(1, 512)) return 1; + n_linear_rings= uint4korr(data+WKB_HEADER_SIZE); + data+= 4 + WKB_HEADER_SIZE; txt->q_append('('); - for (; n_linear_rings>0; --n_linear_rings) + + while (n_linear_rings--) { if (no_data(data, 4)) return 1; uint32 n_points= uint4korr(data); data+= 4; - if (no_data(data, (8 + 8) * n_points)) return 1; - - if (txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points, - 512)) return 1; + if (no_data(data, (SIZEOF_STORED_DOUBLE * 2) * n_points) || + txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points, + 512)) + return 1; txt->qs_append('('); - for (; n_points>0; --n_points) - { - double d; - float8get(d, data); - txt->qs_append(d); - txt->qs_append(' '); - float8get(d, data + 8); - txt->qs_append(d); - txt->qs_append(','); - data+= 8 + 8; - } - (*txt) [txt->length() - 1] = ')'; + data= append_points(txt, n_points, data, 0); + (*txt) [txt->length() - 1]= ')'; txt->qs_append(','); } - (*txt) [txt->length() - 1] = ')'; + (*txt) [txt->length() - 1]= ')'; txt->qs_append(','); } txt->length(txt->length() - 1); + *end= data; return 0; } -int GMultiPolygon::get_mbr(MBR *mbr) const + +bool Gis_multi_polygon::get_mbr(MBR *mbr, const char **end) const { uint32 n_polygons; - const char *data = m_data; + const char *data= m_data; + if (no_data(data, 4)) return 1; - n_polygons = uint4korr(data); - data += 4; + n_polygons= uint4korr(data); + data+= 4; - for (; n_polygons>0; --n_polygons) + while (n_polygons--) { + uint32 n_linear_rings; if (no_data(data, 4+WKB_HEADER_SIZE)) return 1; - uint32 n_linear_rings = uint4korr(data + WKB_HEADER_SIZE); - data += WKB_HEADER_SIZE + 4; + n_linear_rings= uint4korr(data + WKB_HEADER_SIZE); + data+= WKB_HEADER_SIZE + 4; - for (; n_linear_rings>0; --n_linear_rings) + while (n_linear_rings--) { - if (no_data(data, 4)) - return 1; - uint32 n_points = uint4korr(data); - data += 4; - if (no_data(data, (8+8)*n_points)) - return 1; - - for (; n_points>0; --n_points) - { - mbr->add_xy(data, data + 8); - data += 8+8; - } + if (!(data= get_mbr_for_points(mbr, data, 0))) + return 1; } } + *end= data; return 0; } -int GMultiPolygon::num_geometries(uint32 *num) const + +int Gis_multi_polygon::num_geometries(uint32 *num) const { - *num = uint4korr(m_data); + *num= uint4korr(m_data); return 0; } -int GMultiPolygon::geometry_n(uint32 num, String *result) const + +int Gis_multi_polygon::geometry_n(uint32 num, String *result) const { uint32 n_polygons; - const char *data= m_data, *polygon_n; - LINT_INIT(polygon_n); + const char *data= m_data, *start_of_polygon; if (no_data(data, 4)) return 1; n_polygons= uint4korr(data); data+= 4; - if ((num > n_polygons) || (num < 1)) + if (num > n_polygons || num < 1) return -1; - for (; num > 0; --num) + do { + uint32 n_linear_rings; + start_of_polygon= data; + if (no_data(data, WKB_HEADER_SIZE + 4)) return 1; - uint32 n_linear_rings= uint4korr(data + WKB_HEADER_SIZE); - - if (num == 1) - polygon_n= data; + n_linear_rings= uint4korr(data + WKB_HEADER_SIZE); data+= WKB_HEADER_SIZE + 4; - for (; n_linear_rings > 0; --n_linear_rings) + + while (n_linear_rings--) { + uint32 n_points; if (no_data(data, 4)) return 1; - uint32 n_points= uint4korr(data); + n_points= uint4korr(data); data+= 4 + POINT_DATA_SIZE * n_points; } - if (num == 1) - { - if (result->reserve(data - polygon_n)) - return -1; - result->q_append(polygon_n, data - polygon_n); - break; - } - } - return 0; + } while (--num); + if (no_data(data, 0)) // We must check last segment + return 1; + return result->append(start_of_polygon, (uint32) (data - start_of_polygon), + (uint32) 0); } -int GMultiPolygon::area(double *ar) const + +int Gis_multi_polygon::area(double *ar, const char **end_of_data) const { uint32 n_polygons; - const char *data = m_data; - double result = 0; + const char *data= m_data; + double result= 0; + if (no_data(data, 4)) return 1; - n_polygons = uint4korr(data); - data += 4; + n_polygons= uint4korr(data); + data+= 4; - for (; n_polygons>0; --n_polygons) + while (n_polygons--) { double p_area; + Gis_polygon p; - GPolygon p; - data += WKB_HEADER_SIZE; - p.init_from_wkb(data, m_data_end - data); - if (p.area(&p_area)) + data+= WKB_HEADER_SIZE; + p.init_from_wkb(data, (uint32) (m_data_end - data)); + if (p.area(&p_area, &data)) return 1; - result += p_area; - data += p.get_data_size(); + result+= p_area; } - *ar = result; + *ar= result; + *end_of_data= data; return 0; } -int GMultiPolygon::centroid(String *result) const + +int Gis_multi_polygon::centroid(String *result) const { uint32 n_polygons; - uint i; - GPolygon p; + bool first_loop= 1; + Gis_polygon p; double res_area, res_cx, res_cy; double cur_area, cur_cx, cur_cy; + const char *data= m_data; LINT_INIT(res_area); LINT_INIT(res_cx); LINT_INIT(res_cy); - const char *data = m_data; if (no_data(data, 4)) return 1; - n_polygons = uint4korr(data); - data += 4; + n_polygons= uint4korr(data); + data+= 4; - for (i = 0; i < n_polygons; ++i) + while (n_polygons--) { - data += WKB_HEADER_SIZE; - p.init_from_wkb(data, m_data_end - data); - if (p.area(&cur_area)) - return 1; - - if (p.centroid_xy(&cur_cx, &cur_cy)) + data+= WKB_HEADER_SIZE; + p.init_from_wkb(data, (uint32) (m_data_end - data)); + if (p.area(&cur_area, &data) || + p.centroid_xy(&cur_cx, &cur_cy)) return 1; - if (i) + if (!first_loop) { - double sum_area = res_area + cur_area; - res_cx = (res_area * res_cx + cur_area * cur_cx) / sum_area; - res_cy = (res_area * res_cy + cur_area * cur_cy) / sum_area; + double sum_area= res_area + cur_area; + res_cx= (res_area * res_cx + cur_area * cur_cx) / sum_area; + res_cy= (res_area * res_cy + cur_area * cur_cy) / sum_area; } else { - res_area = cur_area; - res_cx = cur_cx; - res_cy = cur_cy; + first_loop= 0; + res_area= cur_area; + res_cx= cur_cx; + res_cy= cur_cy; } - - data += p.get_data_size(); } - if (result->reserve(1 + 4 + sizeof(double) * 2)) - return 1; - result->q_append((char)wkbNDR); - result->q_append((uint32)wkbPoint); - result->q_append(res_cx); - result->q_append(res_cy); + return create_point(result, res_cx, res_cy); +} - return 0; +const Geometry::Class_info *Gis_multi_polygon::get_class_info() const +{ + return &multipolygon_class; } -/***************************** GeometryCollection *******************************/ -size_t GGeometryCollection::get_data_size() const +/************************* GeometryCollection ****************************/ + +uint32 Gis_geometry_collection::get_data_size() const { uint32 n_objects; - const char *data = m_data; + const char *data= m_data; + Geometry_buffer buffer; + Geometry *geom; + if (no_data(data, 4)) - return 1; - n_objects = uint4korr(data); - data += 4; + return GET_SIZE_ERROR; + n_objects= uint4korr(data); + data+= 4; - for (; n_objects>0; --n_objects) + while (n_objects--) { - if (no_data(data, WKB_HEADER_SIZE)) - return 1; - uint32 wkb_type = uint4korr(data + sizeof(char)); - data += WKB_HEADER_SIZE; - - Geometry geom; + uint32 wkb_type,object_size; - if (geom.init(wkb_type)) - return 0; + if (no_data(data, WKB_HEADER_SIZE)) + return GET_SIZE_ERROR; + wkb_type= uint4korr(data + 1); + data+= WKB_HEADER_SIZE; - geom.init_from_wkb(data, m_data_end - data); - size_t object_size=geom.get_data_size(); - data += object_size; + if (!(geom= create_by_typeid(&buffer, wkb_type))) + return GET_SIZE_ERROR; + geom->init_from_wkb(data, (uint) (m_data_end - data)); + if ((object_size= geom->get_data_size()) == GET_SIZE_ERROR) + return GET_SIZE_ERROR; + data+= object_size; } - return data - m_data; + return (uint32) (data - m_data); } -int GGeometryCollection::init_from_wkt(GTextReadStream *trs, String *wkb) + +bool Gis_geometry_collection::init_from_wkt(Gis_read_stream *trs, String *wkb) { - uint32 n_objects = 0; - int no_pos = wkb->length(); - Geometry g; + uint32 n_objects= 0; + uint32 no_pos= wkb->length(); + Geometry_buffer buffer; + Geometry *g; if (wkb->reserve(4, 512)) return 1; - wkb->q_append((uint32)n_objects); + wkb->length(wkb->length()+4); // Reserve space for points for (;;) { - if (g.create_from_wkt(trs, wkb)) + if (!(g= create_from_wkt(&buffer, trs, wkb))) return 1; - if (g.get_class_info()->m_type_id==wkbGeometryCollection) + if (g->get_class_info()->m_type_id == wkb_geometrycollection) { trs->set_error_msg("Unexpected GEOMETRYCOLLECTION"); return 1; } - ++n_objects; - if (trs->get_next_toc_type() == GTextReadStream::comma) - trs->get_next_symbol(); - else break; + n_objects++; + if (trs->skip_char(',')) // Didn't find ',' + break; } - wkb->WriteAtPosition(no_pos, n_objects); + wkb->write_at_position(no_pos, n_objects); return 0; } -int GGeometryCollection::get_data_as_wkt(String *txt) const + +bool Gis_geometry_collection::get_data_as_wkt(String *txt, + const char **end) const { uint32 n_objects; - const char *data = m_data; - Geometry geom; + Geometry_buffer buffer; + Geometry *geom; + const char *data= m_data; + if (no_data(data, 4)) return 1; - n_objects = uint4korr(data); - data += 4; + n_objects= uint4korr(data); + data+= 4; - for (; n_objects>0; --n_objects) + while (n_objects--) { + uint32 wkb_type; + if (no_data(data, WKB_HEADER_SIZE)) return 1; - uint32 wkb_type = uint4korr(data + sizeof(char)); - data += WKB_HEADER_SIZE; + wkb_type= uint4korr(data + 1); + data+= WKB_HEADER_SIZE; - if (geom.init(wkb_type)) + if (!(geom= create_by_typeid(&buffer, wkb_type))) return 1; - geom.init_from_wkb(data, m_data_end - data); - if (geom.as_wkt(txt)) + geom->init_from_wkb(data, (uint) (m_data_end - data)); + if (geom->as_wkt(txt, &data)) + return 1; + if (txt->append(",", 1, 512)) return 1; - data += geom.get_data_size(); - txt->reserve(1, 512); - txt->q_append(','); } txt->length(txt->length() - 1); + *end= data; return 0; } -int GGeometryCollection::get_mbr(MBR *mbr) const + +bool Gis_geometry_collection::get_mbr(MBR *mbr, const char **end) const { uint32 n_objects; - const char *data = m_data; + const char *data= m_data; + Geometry_buffer buffer; + Geometry *geom; + if (no_data(data, 4)) return 1; - n_objects = uint4korr(data); - data += 4; - for (; n_objects>0; --n_objects) + n_objects= uint4korr(data); + data+= 4; + + while (n_objects--) { + uint32 wkb_type; + if (no_data(data, WKB_HEADER_SIZE)) return 1; - uint32 wkb_type = uint4korr(data + sizeof(char)); - data += WKB_HEADER_SIZE; - Geometry geom; + wkb_type= uint4korr(data + 1); + data+= WKB_HEADER_SIZE; - if (geom.init(wkb_type)) + if (!(geom= create_by_typeid(&buffer, wkb_type))) + return 1; + geom->init_from_wkb(data, (uint32) (m_data_end - data)); + if (geom->get_mbr(mbr, &data)) return 1; - geom.init_from_wkb(data, m_data_end - data); - geom.get_mbr(mbr); - data += geom.get_data_size(); } + *end= data; return 0; } -int GGeometryCollection::num_geometries(uint32 *num) const + +int Gis_geometry_collection::num_geometries(uint32 *num) const { - *num = uint4korr(m_data); + if (no_data(m_data, 4)) + return 1; + *num= uint4korr(m_data); return 0; } -int GGeometryCollection::geometry_n(uint32 num, String *result) const + +int Gis_geometry_collection::geometry_n(uint32 num, String *result) const { - const char *data = m_data; - uint32 n_objects; + uint32 n_objects, wkb_type, length; + const char *data= m_data; + Geometry_buffer buffer; + Geometry *geom; + if (no_data(data, 4)) return 1; - n_objects = uint4korr(data); - data += 4; + n_objects= uint4korr(data); + data+= 4; + if (num > n_objects || num < 1) + return 1; - if ((num > n_objects) || (num < 1)) - { - return -1; - } - for (; num > 0; --num) + do { if (no_data(data, WKB_HEADER_SIZE)) return 1; - uint32 wkb_type = uint4korr(data + sizeof(char)); - data += WKB_HEADER_SIZE; + wkb_type= uint4korr(data + 1); + data+= WKB_HEADER_SIZE; - Geometry geom; - if (geom.init(wkb_type)) + if (!(geom= create_by_typeid(&buffer, wkb_type))) return 1; - geom.init_from_wkb(data, m_data_end - data); - if (num == 1) - { - if (result->reserve(1+4+geom.get_data_size())) - return 1; - result->q_append((char)wkbNDR); - result->q_append((uint32)wkb_type); - result->q_append(data, geom.get_data_size()); - break; - } - else - { - data += geom.get_data_size(); - } - } + geom->init_from_wkb(data, (uint) (m_data_end - data)); + if ((length= geom->get_data_size()) == GET_SIZE_ERROR) + return 1; + data+= length; + } while (--num); + + /* Copy found object to result */ + if (result->reserve(1+4+length)) + return 1; + result->q_append((char) wkb_ndr); + result->q_append((uint32) wkb_type); + result->q_append(data-length, length); // data-length = start_of_data return 0; } -int GGeometryCollection::dimension(uint32 *dim) const + +/* + Return dimension for object + + SYNOPSIS + dimension() + res_dim Result dimension + end End of object will be stored here. May be 0 for + simple objects! + RETURN + 0 ok + 1 error +*/ + +bool Gis_geometry_collection::dimension(uint32 *res_dim, const char **end) const { uint32 n_objects; - *dim = 0; - const char *data = m_data; + const char *data= m_data; + Geometry_buffer buffer; + Geometry *geom; + if (no_data(data, 4)) return 1; - n_objects = uint4korr(data); - data += 4; + n_objects= uint4korr(data); + data+= 4; - for (; n_objects > 0; --n_objects) + *res_dim= 0; + while (n_objects--) { + uint32 wkb_type, length, dim; + const char *end_data; + if (no_data(data, WKB_HEADER_SIZE)) return 1; - uint32 wkb_type = uint4korr(data + sizeof(char)); - data += WKB_HEADER_SIZE; - - uint32 d; - - Geometry geom; - if (geom.init(wkb_type)) + wkb_type= uint4korr(data + 1); + data+= WKB_HEADER_SIZE; + if (!(geom= create_by_typeid(&buffer, wkb_type))) return 1; - geom.init_from_wkb(data, m_data_end - data); - if (geom.dimension(&d)) + geom->init_from_wkb(data, (uint32) (m_data_end - data)); + if (geom->dimension(&dim, &end_data)) return 1; - - if (d > *dim) - *dim = d; - data += geom.get_data_size(); + set_if_bigger(*res_dim, dim); + if (end_data) // Complex object + data= end_data; + else if ((length= geom->get_data_size()) == GET_SIZE_ERROR) + return 1; + else + data+= length; } + *end= data; return 0; } -/***************************** /objects *******************************/ +const Geometry::Class_info *Gis_geometry_collection::get_class_info() const +{ + return &geometrycollection_class; +} + diff --git a/sql/spatial.h b/sql/spatial.h index 5fda257f1b1..cf07b364bb3 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -17,118 +17,80 @@ #ifndef _spatial_h #define _spatial_h -const uint POINT_DATA_SIZE = 8+8; -const uint WKB_HEADER_SIZE = 1+4; +const uint SRID_SIZE= 4; +const uint SIZEOF_STORED_DOUBLE= 8; +const uint POINT_DATA_SIZE= SIZEOF_STORED_DOUBLE*2; +const uint WKB_HEADER_SIZE= 1+4; +const uint32 GET_SIZE_ERROR= ((uint32) -1); -struct stPoint2D +struct st_point_2d { double x; double y; }; -struct stLinearRing +struct st_linear_ring { - size_t n_points; - stPoint2D points; + uint32 n_points; + st_point_2d points; }; /***************************** MBR *******************************/ + +/* + It's ok that a lot of the functions are inline as these are only used once + in MySQL +*/ + struct MBR { + double xmin, ymin, xmax, ymax; + MBR() { - xmin=DBL_MAX; - ymin=DBL_MAX; - xmax=-DBL_MAX; - ymax=-DBL_MAX; + xmin= ymin= DBL_MAX; + xmax= ymax= -DBL_MAX; } - MBR(const double &_xmin, const double &_ymin, - const double &_xmax, const double &_ymax) - { - xmin=_xmin; - ymin=_ymin; - xmax=_xmax; - ymax=_ymax; - } + MBR(const double xmin_arg, const double ymin_arg, + const double xmax_arg, const double ymax_arg) + :xmin(xmin_arg), ymin(ymin_arg), xmax(xmax_arg), ymax(ymax_arg) + {} - MBR(const stPoint2D &min, const stPoint2D &max) - { - xmin=min.x; - ymin=min.y; - xmax=max.x; - ymax=max.y; - } - - double xmin; - double ymin; - double xmax; - double ymax; - - void add_xy(double x, double y) + MBR(const st_point_2d &min, const st_point_2d &max) + :xmin(min.x), ymin(min.y), xmax(max.x), ymax(max.y) + {} + + inline void add_xy(double x, double y) { /* Not using "else" for proper one point MBR calculation */ - if (x<xmin) - { - xmin=x; - } - if (x>xmax) - { - xmax=x; - } - if (y<ymin) - { - ymin=y; - } - if (y>ymax) - { - ymax=y; - } + if (x < xmin) + xmin= x; + if (x > xmax) + xmax= x; + if (y < ymin) + ymin= y; + if (y > ymax) + ymax= y; } - void add_xy(const char *px, const char *py) { double x, y; float8get(x, px); float8get(y, py); - /* Not using "else" for proper one point MBR calculation */ - if (x<xmin) - { - xmin=x; - } - if (x>xmax) - { - xmax=x; - } - if (y<ymin) - { - ymin=y; - } - if (y>ymax) - { - ymax=y; - } + add_xy(x,y); } - void add_mbr(const MBR *mbr) { - if (mbr->xmin<xmin) - { - xmin=mbr->xmin; - } - if (mbr->xmax>xmax) - { - xmax=mbr->xmax; - } - if (mbr->ymin<ymin) - { - ymin=mbr->ymin; - } - if (mbr->ymax>ymax) - { - ymax=mbr->ymax; - } + if (mbr->xmin < xmin) + xmin= mbr->xmin; + if (mbr->xmax > xmax) + xmax= mbr->xmax; + if (mbr->ymin < ymin) + ymin= mbr->ymin; + if (mbr->ymax > ymax) + ymax= mbr->ymax; } int equals(const MBR *mbr) @@ -177,12 +139,12 @@ struct MBR int overlaps(const MBR *mbr) { - int lb = mbr->inner_point(xmin, ymin); - int rb = mbr->inner_point(xmax, ymin); - int rt = mbr->inner_point(xmax, ymax); - int lt = mbr->inner_point(xmin, ymax); + int lb= mbr->inner_point(xmin, ymin); + int rb= mbr->inner_point(xmax, ymin); + int rt= mbr->inner_point(xmax, ymax); + int lt= mbr->inner_point(xmin, ymax); - int a = lb+rb+rt+lt; + int a = lb+rb+rt+lt; return (a>0) && (a<4) && (!within(mbr)); } }; @@ -190,188 +152,144 @@ struct MBR /***************************** Geometry *******************************/ -class Geometry; - -typedef int (Geometry::*GF_InitFromText)(GTextReadStream *, String *); -typedef int (Geometry::*GF_GetDataAsText)(String *) const; -typedef size_t (Geometry::*GF_GetDataSize)() const; -typedef int (Geometry::*GF_GetMBR)(MBR *) const; - -typedef int (Geometry::*GF_GetD)(double *) const; -typedef int (Geometry::*GF_GetI)(int *) const; -typedef int (Geometry::*GF_GetUI)(uint32 *) const; -typedef int (Geometry::*GF_GetWS)(String *) const; -typedef int (Geometry::*GF_GetUIWS)(uint32, String *) const; - -#define GEOM_METHOD_PRESENT(geom_obj, method)\ - (geom_obj.m_vmt->method != &Geometry::method) +struct Geometry_buffer; class Geometry { public: + static void *operator new(size_t size, void *buffer) + { + return buffer; + } + enum wkbType { - wkbPoint = 1, - wkbLineString = 2, - wkbPolygon = 3, - wkbMultiPoint = 4, - wkbMultiLineString = 5, - wkbMultiPolygon = 6, - wkbGeometryCollection = 7 + wkb_point= 1, + wkb_linestring= 2, + wkb_polygon= 3, + wkb_multipoint= 4, + wkb_multilinestring= 5, + wkb_multipolygon= 6, + wkb_geometrycollection= 7, + wkb_end=7 }; enum wkbByteOrder { - wkbXDR = 0, /* Big Endian */ - wkbNDR = 1 /* Little Endian */ + wkb_xdr= 0, /* Big Endian */ + wkb_ndr= 1 /* Little Endian */ }; - class GClassInfo + class Class_info { public: - GF_InitFromText init_from_wkt; - GF_GetDataAsText get_data_as_wkt; - GF_GetDataSize get_data_size; - GF_GetMBR get_mbr; - GF_GetD get_x; - GF_GetD get_y; - GF_GetD length; - GF_GetD area; - - GF_GetI is_closed; - - GF_GetUI num_interior_ring; - GF_GetUI num_points; - GF_GetUI num_geometries; - GF_GetUI dimension; - - GF_GetWS start_point; - GF_GetWS end_point; - GF_GetWS exterior_ring; - GF_GetWS centroid; - - GF_GetUIWS point_n; - GF_GetUIWS interior_ring_n; - GF_GetUIWS geometry_n; - + LEX_STRING_WITH_INIT m_name; int m_type_id; - const char *m_name; - GClassInfo *m_next_rt; + void (*m_create_func)(void *); + Class_info(const char *name, int type_id, void(*create_func)(void *)); }; - GClassInfo *m_vmt; - const GClassInfo *get_class_info() const { return m_vmt; } - size_t get_data_size() const { return (this->*m_vmt->get_data_size)(); } - - int init_from_wkt(GTextReadStream *trs, String *wkb) - { return (this->*m_vmt->init_from_wkt)(trs, wkb); } - - int get_data_as_wkt(String *txt) const - { return (this->*m_vmt->get_data_as_wkt)(txt); } - - int get_mbr(MBR *mbr) const { return (this->*m_vmt->get_mbr)(mbr); } - int dimension(uint32 *dim) const - { return (this->*m_vmt->dimension)(dim); } - - int get_x(double *x) const { return (this->*m_vmt->get_x)(x); } - int get_y(double *y) const { return (this->*m_vmt->get_y)(y); } - int length(double *len) const { return (this->*m_vmt->length)(len); } - int area(double *ar) const { return (this->*m_vmt->area)(ar); } - - int is_closed(int *closed) const - { return (this->*m_vmt->is_closed)(closed); } - - int num_interior_ring(uint32 *n_int_rings) const - { return (this->*m_vmt->num_interior_ring)(n_int_rings); } - int num_points(uint32 *n_points) const - { return (this->*m_vmt->num_points)(n_points); } - - int num_geometries(uint32 *num) const - { return (this->*m_vmt->num_geometries)(num); } - - int start_point(String *point) const - { return (this->*m_vmt->start_point)(point); } - int end_point(String *point) const - { return (this->*m_vmt->end_point)(point); } - int exterior_ring(String *ring) const - { return (this->*m_vmt->exterior_ring)(ring); } - int centroid(String *point) const - { return (this->*m_vmt->centroid)(point); } - - int point_n(uint32 num, String *result) const - { return (this->*m_vmt->point_n)(num, result); } - int interior_ring_n(uint32 num, String *result) const - { return (this->*m_vmt->interior_ring_n)(num, result); } - int geometry_n(uint32 num, String *result) const - { return (this->*m_vmt->geometry_n)(num, result); } + virtual const Class_info *get_class_info() const=0; + virtual uint32 get_data_size() const=0; + virtual bool init_from_wkt(Gis_read_stream *trs, String *wkb)=0; + virtual bool get_data_as_wkt(String *txt, const char **end) const=0; + virtual bool get_mbr(MBR *mbr, const char **end) const=0; + virtual bool dimension(uint32 *dim, const char **end) const=0; + virtual int get_x(double *x) const { return -1; } + virtual int get_y(double *y) const { return -1; } + virtual int length(double *len) const { return -1; } + virtual int area(double *ar, const char **end) const { return -1;} + virtual int is_closed(int *closed) const { return -1; } + virtual int num_interior_ring(uint32 *n_int_rings) const { return -1; } + virtual int num_points(uint32 *n_points) const { return -1; } + virtual int num_geometries(uint32 *num) const { return -1; } + virtual int start_point(String *point) const { return -1; } + virtual int end_point(String *point) const { return -1; } + virtual int exterior_ring(String *ring) const { return -1; } + virtual int centroid(String *point) const { return -1; } + virtual int point_n(uint32 num, String *result) const { return -1; } + virtual int interior_ring_n(uint32 num, String *result) const { return -1; } + virtual int geometry_n(uint32 num, String *result) const { return -1; } public: - int create_from_wkb(const char *data, uint32 data_len); - int create_from_wkt(GTextReadStream *trs, String *wkt, int init_stream=1); - int init(int type_id) - { - m_vmt = find_class(type_id); - return !m_vmt; - } - int new_geometry(const char *name, size_t len) + static Geometry *create_by_typeid(Geometry_buffer *buffer, int type_id) { - m_vmt = find_class(name, len); - return !m_vmt; + Class_info *ci; + if (!(ci= find_class((int) type_id))) + return NULL; + (*ci->m_create_func)((void *)buffer); + return (Geometry *)buffer; } - int as_wkt(String *wkt) const + static Geometry *create_from_wkb(Geometry_buffer *buffer, + const char *data, uint32 data_len); + static Geometry *create_from_wkt(Geometry_buffer *buffer, + Gis_read_stream *trs, String *wkt, + bool init_stream=1); + int as_wkt(String *wkt, const char **end) { - if (wkt->reserve(strlen(get_class_info()->m_name) + 2, 512)) + uint32 len= get_class_info()->m_name.length; + if (wkt->reserve(len + 2, 512)) return 1; - wkt->qs_append(get_class_info()->m_name); + wkt->qs_append(get_class_info()->m_name.str, len); wkt->qs_append('('); - if (get_data_as_wkt(wkt)) + if (get_data_as_wkt(wkt, end)) return 1; wkt->qs_append(')'); return 0; } - void init_from_wkb(const char *data, uint32 data_len) + inline void init_from_wkb(const char *data, uint32 data_len) { - m_data = data; - m_data_end = data + data_len; + m_data= data; + m_data_end= data + data_len; } - void shift_wkb_header() + inline void shift_wkb_header() { - m_data += WKB_HEADER_SIZE; + m_data+= WKB_HEADER_SIZE; } - int envelope(String *result) const; + bool envelope(String *result) const; + static Class_info *ci_collection[wkb_end+1]; protected: - static GClassInfo *find_class(int type_id); - static GClassInfo *find_class(const char *name, size_t len); - - bool no_data(const char *cur_data, uint32 data_amount) const + static Class_info *find_class(int type_id) + { + return ((type_id < wkb_point) || (type_id > wkb_end)) ? + NULL : ci_collection[type_id]; + } + static Class_info *find_class(const char *name, uint32 len); + const char *append_points(String *txt, uint32 n_points, + const char *data, uint32 offset) const; + bool create_point(String *result, const char *data) const; + bool create_point(String *result, double x, double y) const; + const char *get_mbr_for_points(MBR *mbr, const char *data, uint offset) + const; + + inline bool no_data(const char *cur_data, uint32 data_amount) const { return (cur_data + data_amount > m_data_end); } - const char *m_data; const char *m_data_end; }; -#define SIZEOF_STORED_DOUBLE 8 /***************************** Point *******************************/ -class GPoint: public Geometry +class Gis_point: public Geometry { public: - size_t get_data_size() const; - int init_from_wkt(GTextReadStream *trs, String *wkb); - int get_data_as_wkt(String *txt) const; - int get_mbr(MBR *mbr) const; + uint32 get_data_size() const; + bool init_from_wkt(Gis_read_stream *trs, String *wkb); + bool get_data_as_wkt(String *txt, const char **end) const; + bool get_mbr(MBR *mbr, const char **end) const; int get_xy(double *x, double *y) const { - const char *data = m_data; - if (no_data(data, SIZEOF_STORED_DOUBLE * 2)) return 1; + const char *data= m_data; + if (no_data(data, SIZEOF_STORED_DOUBLE * 2)) + return 1; float8get(*x, data); float8get(*y, data + SIZEOF_STORED_DOUBLE); return 0; @@ -379,122 +297,166 @@ public: int get_x(double *x) const { - if (no_data(m_data, SIZEOF_STORED_DOUBLE)) return 1; + if (no_data(m_data, SIZEOF_STORED_DOUBLE)) + return 1; float8get(*x, m_data); return 0; } int get_y(double *y) const { - const char *data = m_data; + const char *data= m_data; if (no_data(data, SIZEOF_STORED_DOUBLE * 2)) return 1; float8get(*y, data + SIZEOF_STORED_DOUBLE); return 0; } - int dimension(uint32 *dim) const { *dim = 0; return 0; } + bool dimension(uint32 *dim, const char **end) const + { + *dim= 0; + *end= 0; /* No default end */ + return 0; + } + const Class_info *get_class_info() const; }; + /***************************** LineString *******************************/ -class GLineString: public Geometry +class Gis_line_string: public Geometry { public: - size_t get_data_size() const; - int init_from_wkt(GTextReadStream *trs, String *wkb); - int get_data_as_wkt(String *txt) const; - int get_mbr(MBR *mbr) const; - + uint32 get_data_size() const; + bool init_from_wkt(Gis_read_stream *trs, String *wkb); + bool get_data_as_wkt(String *txt, const char **end) const; + bool get_mbr(MBR *mbr, const char **end) const; int length(double *len) const; int is_closed(int *closed) const; int num_points(uint32 *n_points) const; int start_point(String *point) const; int end_point(String *point) const; int point_n(uint32 n, String *result) const; - int dimension(uint32 *dim) const { *dim = 1; return 0; } + bool dimension(uint32 *dim, const char **end) const + { + *dim= 1; + *end= 0; /* No default end */ + return 0; + } + const Class_info *get_class_info() const; }; + /***************************** Polygon *******************************/ -class GPolygon: public Geometry +class Gis_polygon: public Geometry { public: - size_t get_data_size() const; - int init_from_wkt(GTextReadStream *trs, String *wkb); - int get_data_as_wkt(String *txt) const; - int get_mbr(MBR *mbr) const; - - int area(double *ar) const; + uint32 get_data_size() const; + bool init_from_wkt(Gis_read_stream *trs, String *wkb); + bool get_data_as_wkt(String *txt, const char **end) const; + bool get_mbr(MBR *mbr, const char **end) const; + int area(double *ar, const char **end) const; int exterior_ring(String *result) const; int num_interior_ring(uint32 *n_int_rings) const; int interior_ring_n(uint32 num, String *result) const; int centroid_xy(double *x, double *y) const; int centroid(String *result) const; - int dimension(uint32 *dim) const { *dim = 2; return 0; } + bool dimension(uint32 *dim, const char **end) const + { + *dim= 2; + *end= 0; /* No default end */ + return 0; + } + const Class_info *get_class_info() const; }; + /***************************** MultiPoint *******************************/ -class GMultiPoint: public Geometry +class Gis_multi_point: public Geometry { public: - size_t get_data_size() const; - int init_from_wkt(GTextReadStream *trs, String *wkb); - int get_data_as_wkt(String *txt) const; - int get_mbr(MBR *mbr) const; - + uint32 get_data_size() const; + bool init_from_wkt(Gis_read_stream *trs, String *wkb); + bool get_data_as_wkt(String *txt, const char **end) const; + bool get_mbr(MBR *mbr, const char **end) const; int num_geometries(uint32 *num) const; int geometry_n(uint32 num, String *result) const; - int dimension(uint32 *dim) const { *dim = 0; return 0; } + bool dimension(uint32 *dim, const char **end) const + { + *dim= 0; + *end= 0; /* No default end */ + return 0; + } + const Class_info *get_class_info() const; }; + /***************************** MultiLineString *******************************/ -class GMultiLineString: public Geometry +class Gis_multi_line_string: public Geometry { public: - size_t get_data_size() const; - int init_from_wkt(GTextReadStream *trs, String *wkb); - int get_data_as_wkt(String *txt) const; - int get_mbr(MBR *mbr) const; - + uint32 get_data_size() const; + bool init_from_wkt(Gis_read_stream *trs, String *wkb); + bool get_data_as_wkt(String *txt, const char **end) const; + bool get_mbr(MBR *mbr, const char **end) const; int num_geometries(uint32 *num) const; int geometry_n(uint32 num, String *result) const; int length(double *len) const; int is_closed(int *closed) const; - int dimension(uint32 *dim) const { *dim = 1; return 0; } + bool dimension(uint32 *dim, const char **end) const + { + *dim= 1; + *end= 0; /* No default end */ + return 0; + } + const Class_info *get_class_info() const; }; + /***************************** MultiPolygon *******************************/ -class GMultiPolygon: public Geometry +class Gis_multi_polygon: public Geometry { public: - size_t get_data_size() const; - int init_from_wkt(GTextReadStream *trs, String *wkb); - int get_data_as_wkt(String *txt) const; - int get_mbr(MBR *mbr) const; - + uint32 get_data_size() const; + bool init_from_wkt(Gis_read_stream *trs, String *wkb); + bool get_data_as_wkt(String *txt, const char **end) const; + bool get_mbr(MBR *mbr, const char **end) const; int num_geometries(uint32 *num) const; int geometry_n(uint32 num, String *result) const; - int area(double *ar) const; + int area(double *ar, const char **end) const; int centroid(String *result) const; - int dimension(uint32 *dim) const { *dim = 2; return 0; } + bool dimension(uint32 *dim, const char **end) const + { + *dim= 2; + *end= 0; /* No default end */ + return 0; + } + const Class_info *get_class_info() const; }; -/***************************** GeometryCollection *******************************/ -class GGeometryCollection: public Geometry +/*********************** GeometryCollection *******************************/ + +class Gis_geometry_collection: public Geometry { public: - size_t get_data_size() const; - int init_from_wkt(GTextReadStream *trs, String *wkb); - int get_data_as_wkt(String *txt) const; - int get_mbr(MBR *mbr) const; - + uint32 get_data_size() const; + bool init_from_wkt(Gis_read_stream *trs, String *wkb); + bool get_data_as_wkt(String *txt, const char **end) const; + bool get_mbr(MBR *mbr, const char **end) const; int num_geometries(uint32 *num) const; int geometry_n(uint32 num, String *result) const; - int dimension(uint32 *dim) const; + bool dimension(uint32 *dim, const char **end) const; + const Class_info *get_class_info() const; +}; + +const int geometry_buffer_size= sizeof(Gis_point); +struct Geometry_buffer +{ + void *arr[(geometry_buffer_size - 1)/sizeof(void *) + 1]; }; #endif diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d294055ff8a..ef55eaf8ec6 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -909,8 +909,8 @@ static void acl_update_user(const char *user, const char *host, acl_user->x509_subject= (x509_subject ? strdup_root(&mem,x509_subject) : 0); } - - set_user_salt(acl_user, password, password_len); + if (password) + set_user_salt(acl_user, password, password_len); /* search complete: */ break; } @@ -1195,7 +1195,8 @@ bool check_change_password(THD *thd, const char *host, const char *user) { if (!initialized) { - send_error(thd, ER_SKIP_GRANT_TABLES); /* purecov: inspected */ + net_printf(thd,ER_OPTION_PREVENTS_STATEMENT, + "--skip-grant-tables"); /* purecov: inspected */ return(1); /* purecov: inspected */ } if (!thd->slave_thread && @@ -1371,20 +1372,20 @@ bool hostname_requires_resolving(const char *hostname) { char cur; if (!hostname) - return false; + return FALSE; int namelen= strlen(hostname); int lhlen= strlen(my_localhost); if ((namelen == lhlen) && !my_strnncoll(&my_charset_latin1, (const uchar *)hostname, namelen, (const uchar *)my_localhost, strlen(my_localhost))) - return false; + return FALSE; for (; (cur=*hostname); hostname++) { if ((cur != '%') && (cur != '_') && (cur != '.') && ((cur < '0') || (cur > '9'))) - return true; + return TRUE; } - return false; + return FALSE; } /* @@ -1403,6 +1404,7 @@ static bool update_user_table(THD *thd, const char *host, const char *user, bzero((char*) &tables,sizeof(tables)); tables.alias=tables.real_name=(char*) "user"; tables.db=(char*) "mysql"; + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -1460,6 +1462,7 @@ static bool test_if_create_new_users(THD *thd) bzero((char*) &tl,sizeof(tl)); tl.db= (char*) "mysql"; tl.real_name= (char*) "user"; + db_access=acl_get(thd->host, thd->ip, thd->priv_user, tl.db, 0); if (!(db_access & INSERT_ACL)) @@ -1512,13 +1515,10 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, if (!create_user) { if (what == 'N') - my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT), - MYF(0),combo.user.str,combo.host.str); + my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str); else - my_printf_error(ER_NO_PERMISSION_TO_CREATE_USER, - ER(ER_NO_PERMISSION_TO_CREATE_USER), - MYF(0),thd->user, - thd->host_or_ip); + my_error(ER_NO_PERMISSION_TO_CREATE_USER, MYF(0), + thd->user, thd->host_or_ip); error= -1; goto end; } @@ -1537,6 +1537,11 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, store_record(table,record[1]); // Save copy for update if (combo.password.str) // If password given table->field[2]->store(password, password_len, &my_charset_latin1); + else if (!rights && !revoke_grant && thd->lex->ssl_type == SSL_TYPE_NOT_SPECIFIED && + !thd->lex->mqh.bits) + { + DBUG_RETURN(0); + } } /* Update table columns with new privileges */ @@ -1672,7 +1677,7 @@ static int replace_db_table(TABLE *table, const char *db, if (!initialized) { - my_error(ER_SKIP_GRANT_TABLES, MYF(0)); + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables"); DBUG_RETURN(-1); } @@ -1692,8 +1697,7 @@ static int replace_db_table(TABLE *table, const char *db, { if (what == 'N') { // no row, no revoke - my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT),MYF(0), - combo.user.str,combo.host.str); + my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str); goto abort; } old_row_exists = 0; @@ -1982,9 +1986,8 @@ static int replace_column_table(GRANT_TABLE *g_t, { if (revoke_grant) { - my_printf_error(ER_NONEXISTING_TABLE_GRANT, - ER(ER_NONEXISTING_TABLE_GRANT),MYF(0), - combo.user.str, combo.host.str,table_name); /* purecov: inspected */ + my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0), + combo.user.str, combo.host.str, table_name); /* purecov: inspected */ result= -1; /* purecov: inspected */ continue; /* purecov: inspected */ } @@ -2150,10 +2153,9 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, */ if (revoke_grant) { // no row, no revoke - my_printf_error(ER_NONEXISTING_TABLE_GRANT, - ER(ER_NONEXISTING_TABLE_GRANT),MYF(0), - combo.user.str,combo.host.str, - table_name); /* purecov: deadcode */ + my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0), + combo.user.str, combo.host.str, + table_name); /* purecov: deadcode */ DBUG_RETURN(-1); /* purecov: deadcode */ } old_row_exists = 0; @@ -2253,8 +2255,9 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, if (!initialized) { - send_error(thd, ER_SKIP_GRANT_TABLES); /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), + "--skip-grant-tables"); /* purecov: inspected */ + DBUG_RETURN(-1); /* purecov: inspected */ } if (rights & ~TABLE_ACLS) { @@ -2272,11 +2275,13 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(-1); while ((column = column_iter++)) { + uint unused_field_idx= NO_CACHED_FIELD_INDEX; if (!find_field_in_table(thd,table,column->column.ptr(), - column->column.length(),0,0)) + column->column.length(),0,0, + &unused_field_idx)) { - my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0), - column->column.c_ptr(), table_list->alias); + my_error(ER_BAD_FIELD_ERROR, MYF(0), + column->column.c_ptr(), table_list->alias); DBUG_RETURN(-1); } column_priv|= column->rights; @@ -2291,7 +2296,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, 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); + my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias); DBUG_RETURN(-1); } } @@ -2370,9 +2375,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, { if (revoke_grant) { - my_printf_error(ER_NONEXISTING_TABLE_GRANT, - ER(ER_NONEXISTING_TABLE_GRANT),MYF(0), - Str->user.str, Str->host.str, table_list->real_name); + my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0), + Str->user.str, Str->host.str, table_list->real_name); result= -1; continue; } @@ -2463,7 +2467,8 @@ int mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, DBUG_ENTER("mysql_grant"); if (!initialized) { - my_error(ER_SKIP_GRANT_TABLES, MYF(0)); /* purecov: tested */ + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), + "--skip-grant-tables"); /* purecov: tested */ DBUG_RETURN(-1); /* purecov: tested */ } @@ -2542,8 +2547,7 @@ int mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, } else { - my_printf_error(ER_WRONG_USAGE, ER(ER_WRONG_USAGE), MYF(0), - "DB GRANT","GLOBAL PRIVILEGES"); + my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES"); result= -1; } } @@ -2630,7 +2634,7 @@ my_bool grant_init(THD *org_thd) do { GRANT_TABLE *mem_check; - if (!(mem_check=new GRANT_TABLE(t_table,c_table)) || !mem_check->ok()) + if (!(mem_check=new GRANT_TABLE(t_table,c_table))) { /* This could only happen if we are out memory */ grant_option= FALSE; /* purecov: deadcode */ @@ -2649,7 +2653,7 @@ my_bool grant_init(THD *org_thd) } } - if (my_hash_insert(&column_priv_hash,(byte*) mem_check)) + if (mem_check->ok() && my_hash_insert(&column_priv_hash,(byte*) mem_check)) { grant_option= FALSE; goto end_unlock; @@ -2721,7 +2725,7 @@ void grant_reload(THD *thd) /**************************************************************************** Check grants - All errors are written directly to the client if command name is given ! + All errors are written directly to the client if no_errors is given ! ****************************************************************************/ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, @@ -3069,8 +3073,8 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) LINT_INIT(acl_user); if (!initialized) { - send_error(thd, ER_SKIP_GRANT_TABLES); - DBUG_RETURN(1); + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables"); + DBUG_RETURN(-1); } if (lex_user->host.length > HOSTNAME_LENGTH || lex_user->user.length > USERNAME_LENGTH) @@ -3093,8 +3097,8 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } if (counter == acl_users.elements) { - my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT), - MYF(0),lex_user->user.str,lex_user->host.str); + my_error(ER_NONEXISTING_GRANT, MYF(0), + lex_user->user.str, lex_user->host.str); DBUG_RETURN(-1); } @@ -3249,9 +3253,9 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } } } - db.append (" ON `",5); - db.append(acl_db->db); - db.append ("`.* TO '",8); + db.append (" ON ",4); + append_identifier(thd, &db, acl_db->db, strlen(acl_db->db)); + db.append (".* TO '",7); db.append(lex_user->user.str,lex_user->user.length); db.append ("'@'",3); db.append(lex_user->host.str, lex_user->host.length); @@ -3349,11 +3353,13 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } } } - global.append(" ON `",5); - global.append(grant_table->db); - global.append("`.`",3); - global.append(grant_table->tname); - global.append("` TO '",6); + global.append(" ON ",4); + append_identifier(thd, &global, grant_table->db, + strlen(grant_table->db)); + global.append('.'); + append_identifier(thd, &global, grant_table->tname, + strlen(grant_table->tname)); + global.append(" TO '",5); global.append(lex_user->user.str,lex_user->user.length); global.append("'@'",3); global.append(lex_user->host.str,lex_user->host.length); @@ -3422,7 +3428,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) if (!initialized) { - send_error(thd, ER_SKIP_GRANT_TABLES); + net_printf(thd,ER_OPTION_PREVENTS_STATEMENT, "--skip-grant-tables"); DBUG_RETURN(-1); } diff --git a/sql/sql_acl.h b/sql/sql_acl.h index f17c9781e8c..d2dd5b23f0d 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -36,6 +36,12 @@ #define REPL_SLAVE_ACL (1L << 19) #define REPL_CLIENT_ACL (1L << 20) +/* + don't forget to update + static struct show_privileges_st sys_privileges[] + in sql_show.cc when adding new privileges! +*/ + #define DB_ACLS \ (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 9e73e06d9c6..3c9563165fe 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -225,7 +225,7 @@ bool test_if_number(NUM_INFO *info, const char *str, uint str_len) info->decimals++; if (str == end) { - info->dval = atod(begin); + info->dval = my_atof(begin); return 1; } } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8f319ee645d..8e96ccad586 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -46,12 +46,12 @@ extern "C" byte *table_cache_key(const byte *record,uint *length, return (byte*) entry->table_cache_key; } -void table_cache_init(void) +bool table_cache_init(void) { - VOID(hash_init(&open_cache,&my_charset_bin, - table_cache_size+16,0,0,table_cache_key, - (hash_free_key) free_cache_entry,0)); mysql_rm_tmp_tables(); + return hash_init(&open_cache, &my_charset_bin, table_cache_size+16, + 0, 0,table_cache_key, + (hash_free_key) free_cache_entry, 0) != 0; } void table_cache_free(void) @@ -156,6 +156,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild) table_list.db= (char*) entry->table_cache_key; table_list.real_name= entry->real_name; table_list.grant.privilege=0; + if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list,1)) continue; /* need to check if we haven't already listed it */ @@ -284,8 +285,10 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, if (!found) if_wait_for_refresh=0; // Nothing to wait for } +#ifndef EMBEDDED_LIBRARY if (!tables) kill_delayed_threads(); +#endif if (if_wait_for_refresh) { /* @@ -485,13 +488,19 @@ void close_temporary_tables(THD *thd) return; LINT_INIT(end); - query_buf_size= 50; // Enough for DROP ... TABLE + query_buf_size= 50; // Enough for DROP ... TABLE IF EXISTS for (table=thd->temporary_tables ; table ; table=table->next) + /* + We are going to add 4 ` around the db/table names, so 1 does not look + enough; indeed it is enough, because table->key_length is greater (by 8, + because of server_id and thread_id) than db||table. + */ query_buf_size+= table->key_length+1; if ((query = alloc_root(&thd->mem_root, query_buf_size))) - end=strmov(query, "DROP /*!40005 TEMPORARY */ TABLE "); + // Better add "if exists", in case a RESET MASTER has been done + end=strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "); for (table=thd->temporary_tables ; table ; table=next) { @@ -504,8 +513,8 @@ void close_temporary_tables(THD *thd) Here we assume table_cache_key always starts with \0 terminated db name */ - end = strxmov(end,"`",table->table_cache_key,"`", - ".`",table->real_name,"`,", NullS); + end = strxmov(end,"`",table->table_cache_key,"`.`", + table->real_name,"`,", NullS); } next=table->next; close_temporary(table); @@ -515,6 +524,16 @@ 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); + /* + 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 + "killed", because it's not really a statement updating data, and there + are 99.99% chances it will succeed on slave. + If a real update (one updating a persistent table) was killed on the + master, then this real update will be logged with error_code=killed, + rightfully causing the slave to stop. + */ + qinfo.error_code= 0; mysql_bin_log.write(&qinfo); } thd->temporary_tables=0; @@ -549,7 +568,7 @@ TABLE_LIST * find_table_in_list(TABLE_LIST *table, Find real table in given list. SYNOPSIS - find_table_in_list() + find_real_table_in_list() table - pointer to table list db_name - data base name table_name - table name @@ -808,8 +827,12 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, { if (table->key_length == key_length && !memcmp(table->table_cache_key,key,key_length) && - !my_strcasecmp(system_charset_info,table->table_name,alias)) + !my_strcasecmp(system_charset_info, table->table_name, alias) && + table->query_id != thd->query_id) + { + table->query_id=thd->query_id; goto reset; + } } my_printf_error(ER_TABLE_NOT_LOCKED,ER(ER_TABLE_NOT_LOCKED),MYF(0),alias); DBUG_RETURN(0); @@ -917,6 +940,8 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, table->status=STATUS_NO_RECORD; table->keys_in_use_for_query= table->keys_in_use; table->used_keys= table->keys_for_keyread; + if (table->timestamp_field) + table->timestamp_field->set_timestamp_offsets(); DBUG_ASSERT(table->key_read == 0); DBUG_RETURN(table); } @@ -1308,6 +1333,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, bzero((char*) &table_list, sizeof(table_list)); // just for safe table_list.db=(char*) db; table_list.real_name=(char*) name; + safe_mutex_assert_owner(&LOCK_open); if ((error=lock_table_name(thd,&table_list))) @@ -1342,15 +1368,47 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, error=1; } else - { thd->clear_error(); // Clear error message - } pthread_mutex_lock(&LOCK_open); unlock_table_name(thd,&table_list); if (error) goto err; } + /* + If we are here, there was no fatal error (but error may be still + unitialized). + */ + if (unlikely(entry->file->implicit_emptied)) + { + entry->file->implicit_emptied= 0; + if (mysql_bin_log.is_open()) + { + char *query, *end; + uint query_buf_size= 20 + 2*NAME_LEN + 1; + if ((query= (char*)my_malloc(query_buf_size,MYF(MY_WME)))) + { + end = strxmov(strmov(query, "DELETE FROM `"), + db,"`.`",name,"`", NullS); + Query_log_event qinfo(thd, query, (ulong)(end-query), 0); + mysql_bin_log.write(&qinfo); + my_free(query, MYF(0)); + } + else + { + /* + As replication is maybe going to be corrupted, we need to warn the + DBA on top of warning the client (which will automatically be done + because of MYF(MY_WME) in my_malloc() above). + */ + sql_print_error("Error: when opening HEAP table, could not allocate \ +memory to write 'DELETE FROM `%s`.`%s`' to the binary log",db,name); + if (entry->file) + closefrm(entry); + goto err; + } + } + } DBUG_RETURN(0); err: DBUG_RETURN(1); @@ -1376,10 +1434,10 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter) bool refresh; int result=0; DBUG_ENTER("open_tables"); - *counter= 0; thd->current_tablenr= 0; restart: + *counter= 0; thd->proc_info="Opening tables"; for (tables=start ; tables ; tables=tables->next) { @@ -1721,7 +1779,11 @@ bool rm_temporary_table(enum db_type base, char *path) *fn_ext(path)='\0'; // remove extension handler *file=get_new_handler((TABLE*) 0, base); if (file && file->delete_table(path)) + { error=1; + sql_print_error("Warning: Could not remove tmp table: '%s', error: %d", + path, my_errno); + } delete file; DBUG_RETURN(error); } @@ -1735,33 +1797,42 @@ bool rm_temporary_table(enum db_type base, char *path) #define WRONG_GRANT (Field*) -1 Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, - bool check_grants, bool allow_rowid) + bool check_grants, bool allow_rowid, + uint *cached_field_index_ptr) { - Field *field; - if (table->name_hash.records) + Field **field_ptr, *field; + uint cached_field_index= *cached_field_index_ptr; + + /* We assume here that table->field < NO_CACHED_FIELD_INDEX = UINT_MAX */ + if (cached_field_index < table->fields && + !my_strcasecmp(system_charset_info, + table->field[cached_field_index]->field_name, name)) + field_ptr= table->field + cached_field_index; + else if (table->name_hash.records) + field_ptr= (Field**)hash_search(&table->name_hash,(byte*) name, + length); + else { - if ((field=(Field*) hash_search(&table->name_hash,(byte*) name, - length))) - goto found; + if (!(field_ptr= table->field)) + return (Field *)0; + for (; *field_ptr; ++field_ptr) + if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name)) + break; + } + + if (field_ptr && *field_ptr) + { + *cached_field_index_ptr= field_ptr - table->field; + field= *field_ptr; } else { - Field **ptr; - if (!(ptr=table->field)) - return (Field *)0; - while ((field = *ptr++)) - { - if (!my_strcasecmp(system_charset_info, field->field_name, name)) - goto found; - } + if (!allow_rowid || + my_strcasecmp(system_charset_info, name, "_rowid") || + !(field=table->rowid_field)) + return (Field*) 0; } - if (allow_rowid && - !my_strcasecmp(system_charset_info, name, "_rowid") && - (field=table->rowid_field)) - goto found; - return (Field*) 0; - found: if (thd->set_query_id) { if (field->query_id != thd->query_id) @@ -1788,7 +1859,7 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, find_field_in_tables() thd Pointer to current thread structure item Field item that should be found - tables Tables for scaning + tables Tables for scanning where Table where field found will be returned via this parameter report_error If FALSE then do not report error if item not found @@ -1816,6 +1887,32 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, uint length=(uint) strlen(name); char name_buff[NAME_LEN+1]; + + if (item->cached_table) + { + /* + This shortcut is used by prepared statements. We assuming that + TABLE_LIST *tables is not changed during query execution (which + is true for all queries except RENAME but luckily RENAME doesn't + use fields...) so we can rely on reusing pointer to its member. + With this optimisation we also miss case when addition of one more + field makes some prepared query ambiguous and so erronous, but we + accept this trade off. + */ + found= find_field_in_table(thd, item->cached_table->table, name, length, + test(item->cached_table-> + table->grant.want_privilege), + 1, &(item->cached_field_index)); + + if (found) + { + (*where)= tables; + if (found == WRONG_GRANT) + return (Field*) 0; + return found; + } + } + if (db && lower_case_table_names) { /* @@ -1840,10 +1937,12 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, Field *find=find_field_in_table(thd,tables->table,name,length, test(tables->table->grant. want_privilege), - 1); + 1, &(item->cached_field_index)); if (find) { - (*where)= tables; + (*where)= item->cached_table= tables; + if (!tables->cacheable_table) + item->cached_table= 0; if (find == WRONG_GRANT) return (Field*) 0; if (db || !thd->where) @@ -1897,12 +1996,14 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, Field *field=find_field_in_table(thd,tables->table,name,length, test(tables->table->grant.want_privilege), - allow_rowid); + allow_rowid, &(item->cached_field_index)); if (field) { if (field == WRONG_GRANT) return (Field*) 0; - (*where)= tables; + (*where)= item->cached_table= tables; + if (!tables->cacheable_table) + item->cached_table= 0; if (found) { if (!thd->where) // Returns first found @@ -1997,7 +2098,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, { if (!strcmp(item_field->table_name,table_name) && (!db_name || (db_name && item_field->db_name && - !strcmp(item_field->table_name,table_name)))) + !strcmp(item_field->db_name, db_name)))) { found= li.ref(); *counter= i; @@ -2039,6 +2140,14 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, { if (!wild_num) return 0; + Statement *stmt= thd->current_statement, 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 (stmt) + thd->set_n_backup_item_arena(stmt, &backup); reg2 Item *item; List_iterator<Item> it(fields); while ( wild_num && (item= it++)) @@ -2050,7 +2159,11 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, uint elem= fields.elements; if (insert_fields(thd,tables,((Item_field*) item)->db_name, ((Item_field*) item)->table_name, &it)) + { + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); return (-1); + } if (sum_func_list) { /* @@ -2063,6 +2176,8 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, wild_num--; } } + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); return 0; } @@ -2085,10 +2200,9 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, Item **ref= ref_pointer_array; while ((item= it++)) { - if (item->fix_fields(thd, tables, it.ref()) || - item->check_cols(1)) + if (!item->fixed && item->fix_fields(thd, tables, it.ref()) || + (item= *(it.ref()))->check_cols(1)) DBUG_RETURN(-1); /* purecov: inspected */ - item= *(it.ref()); //Item can be changed in fix fields if (ref) *(ref++)= item; if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM && @@ -2106,8 +2220,6 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, SYNOPSIS setup_tables() tables - tables list - reinit - true if called for table reinitialization before - subquery reexecuting RETURN 0 ok; In this case *map will includes the choosed index @@ -2122,7 +2234,7 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, table->map is not set and all Item_field will be regarded as const items. */ -bool setup_tables(TABLE_LIST *tables, my_bool reinit) +bool setup_tables(TABLE_LIST *tables) { DBUG_ENTER("setup_tables"); uint tablenr=0; @@ -2149,13 +2261,6 @@ bool setup_tables(TABLE_LIST *tables, my_bool reinit) table->keys_in_use_for_query.subtract(map); } table->used_keys.intersect(table->keys_in_use_for_query); - if ((table_list->shared || table->clear_query_id) && !reinit) - { - table->clear_query_id= 0; - /* Clear query_id that may have been set by previous select */ - for (Field **ptr=table->field ; *ptr ; ptr++) - (*ptr)->query_id=0; - } } if (tablenr > MAX_TABLES) { @@ -2231,14 +2336,29 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, DBUG_RETURN(-1); #endif Field **ptr=table->field,*field; + TABLE *natural_join_table= 0; + thd->used_tables|=table->map; + if (!table->outer_join && + tables->natural_join && + !tables->natural_join->table->outer_join) + natural_join_table= tables->natural_join->table; + while ((field = *ptr++)) { - Item_field *item= new Item_field(field); - if (!found++) - (void) it->replace(item); // Replace '*' - else - it->after(item); + uint not_used_field_index= NO_CACHED_FIELD_INDEX; + /* Skip duplicate field names if NATURAL JOIN is used */ + if (!natural_join_table || + !find_field_in_table(thd, natural_join_table, field->field_name, + strlen(field->field_name), 0, 0, + ¬_used_field_index)) + { + Item_field *item= new Item_field(thd, field); + if (!found++) + (void) it->replace(item); // Replace '*' + else + it->after(item); + } /* Mark if field used before in this select. Used by 'insert' to verify if a field name is used twice @@ -2270,6 +2390,8 @@ 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; + Statement *stmt= thd->current_statement, backup; + DBUG_ENTER("setup_conds"); thd->set_query_id=1; @@ -2277,11 +2399,13 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) if (*conds) { thd->where="where clause"; - if ((*conds)->fix_fields(thd, tables, conds) || (*conds)->check_cols(1)) + if (!(*conds)->fixed && (*conds)->fix_fields(thd, tables, conds) || + (*conds)->check_cols(1)) DBUG_RETURN(1); not_null_tables= (*conds)->not_null_tables(); } + /* Check if we are using outer joins */ for (TABLE_LIST *table=tables ; table ; table=table->next) { @@ -2289,7 +2413,9 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) { /* Make a join an a expression */ thd->where="on clause"; - 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) || table->on_expr->check_cols(1)) DBUG_RETURN(1); thd->lex->current_select->cond_count++; @@ -2303,58 +2429,98 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) !(specialflag & SPECIAL_NO_NEW_FUNC))) { table->outer_join= 0; - if (!(*conds=and_conds(*conds, table->on_expr))) - DBUG_RETURN(1); + if (stmt) + thd->set_n_backup_item_arena(stmt, &backup); + *conds= and_conds(*conds, table->on_expr); table->on_expr=0; + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); + if ((*conds) && !(*conds)->fixed && + (*conds)->fix_fields(thd, tables, conds)) + DBUG_RETURN(1); } } if (table->natural_join) { + if (stmt) + thd->set_n_backup_item_arena(stmt, &backup); /* Make a join of all fields with have the same name */ - TABLE *t1=table->table; - TABLE *t2=table->natural_join->table; - Item_cond_and *cond_and=new Item_cond_and(); + TABLE *t1= table->table; + TABLE *t2= table->natural_join->table; + Item_cond_and *cond_and= new Item_cond_and(); if (!cond_and) // If not out of memory - DBUG_RETURN(1); + goto err; cond_and->top_level_item(); - uint i,j; - for (i=0 ; i < t1->fields ; i++) + Field **t1_field, *t2_field; + for (t1_field= t1->field; (*t1_field); t1_field++) { - // TODO: This could be optimized to use hashed names if t2 had a hash - for (j=0 ; j < t2->fields ; j++) - { - if (!my_strcasecmp(system_charset_info, - t1->field[i]->field_name, - t2->field[j]->field_name)) - { - Item_func_eq *tmp=new Item_func_eq(new Item_field(t1->field[i]), - new Item_field(t2->field[j])); - if (!tmp) - DBUG_RETURN(1); - tmp->fix_length_and_dec(); // Update cmp_type - tmp->const_item_cache=0; - /* Mark field used for table cache */ - t1->field[i]->query_id=t2->field[j]->query_id=thd->query_id; - cond_and->list.push_back(tmp); - t1->used_keys.intersect(t1->field[i]->part_of_key); - t2->used_keys.intersect(t2->field[j]->part_of_key); - break; - } - } + const char *t1_field_name= (*t1_field)->field_name; + uint not_used_field_index= NO_CACHED_FIELD_INDEX; + + if ((t2_field= find_field_in_table(thd, t2, t1_field_name, + strlen(t1_field_name), 0, 0, + ¬_used_field_index))) + { + Item_func_eq *tmp=new Item_func_eq(new Item_field(*t1_field), + new Item_field(t2_field)); + if (!tmp) + goto err; + /* Mark field used for table cache */ + (*t1_field)->query_id= t2_field->query_id= thd->query_id; + cond_and->list.push_back(tmp); + t1->used_keys.intersect((*t1_field)->part_of_key); + t2->used_keys.intersect(t2_field->part_of_key); + } } - cond_and->used_tables_cache= t1->map | t2->map; thd->lex->current_select->cond_count+= cond_and->list.elements; + + // to prevent natural join processing during PS re-execution + table->natural_join= 0; + if (!table->outer_join) // Not left join { - if (!(*conds=and_conds(*conds, cond_and))) - DBUG_RETURN(1); + *conds= and_conds(*conds, cond_and); + // fix_fields() should be made with temporary memory pool + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); + if (*conds && !(*conds)->fixed) + { + if ((*conds)->fix_fields(thd, tables, conds)) + DBUG_RETURN(1); + } } else - table->on_expr=and_conds(table->on_expr,cond_and); + { + table->on_expr= and_conds(table->on_expr, cond_and); + // fix_fields() should be made with temporary memory pool + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); + if (table->on_expr && !table->on_expr->fixed) + { + if (table->on_expr->fix_fields(thd, tables, &table->on_expr)) + DBUG_RETURN(1); + } + } } } + + if (stmt) + { + /* + We are in prepared statement preparation code => we should store + WHERE clause changing for next executions. + + We do this ON -> WHERE transformation only once per PS statement. + */ + thd->lex->current_select->where= *conds; + } DBUG_RETURN(test(thd->net.report_error)); + +err: + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); + DBUG_RETURN(1); } @@ -2378,7 +2544,7 @@ fill_record(List<Item> &fields,List<Item> &values, bool ignore_errors) TABLE *table= rfield->table; if (rfield == table->next_number_field) table->auto_increment_field_not_null= true; - if (value->save_in_field(rfield, 0) > 0 && !ignore_errors) + if ((value->save_in_field(rfield, 0) < 0) && !ignore_errors) DBUG_RETURN(1); } DBUG_RETURN(0); @@ -2399,7 +2565,7 @@ fill_record(Field **ptr,List<Item> &values, bool ignore_errors) TABLE *table= field->table; if (field == table->next_number_field) table->auto_increment_field_not_null= true; - if (value->save_in_field(field, 0) == 1 && !ignore_errors) + if ((value->save_in_field(field, 0) < 0) && !ignore_errors) DBUG_RETURN(1); } DBUG_RETURN(0); @@ -2550,7 +2716,8 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, if (table->db_stat) result=1; /* Kill delayed insert threads */ - if (in_use->system_thread && ! in_use->killed) + if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && + ! in_use->killed) { in_use->killed= THD::KILL_CONNECTION; pthread_mutex_lock(&in_use->mysys_var->mutex); diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index fd5c339a6ff..cc3d6fec9ae 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -601,7 +601,6 @@ void query_cache_insert(NET *net, const char *packet, ulong length) if (!query_cache.append_result_data(&result, length, (gptr) packet, query_block)) { - query_cache.refused++; DBUG_PRINT("warning", ("Can't append data")); header->result(result); DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block)); @@ -781,7 +780,9 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) flags.character_set_client_num= thd->variables.character_set_client->number; flags.character_set_results_num= - thd->variables.character_set_results->number; + (thd->variables.character_set_results ? + thd->variables.character_set_results->number : + UINT_MAX); flags.collation_connection_num= thd->variables.collation_connection->number; flags.limit= thd->variables.select_limit; @@ -796,6 +797,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) if (ask_handler_allowance(thd, tables_used)) { + refused++; STRUCT_UNLOCK(&structure_guard_mutex); DBUG_VOID_RETURN; } @@ -884,7 +886,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) DBUG_PRINT("qcache", ("Another thread process same query")); } } - else + else if (thd->lex->sql_command == SQLCOM_SELECT) statistic_increment(refused, &structure_guard_mutex); end: @@ -970,7 +972,9 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) 1 : 0); flags.character_set_client_num= thd->variables.character_set_client->number; flags.character_set_results_num= - thd->variables.character_set_results->number; + (thd->variables.character_set_results ? + thd->variables.character_set_results->number : + UINT_MAX); flags.collation_connection_num= thd->variables.collation_connection->number; flags.limit= thd->variables.select_limit; memcpy((void *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)), @@ -1029,7 +1033,6 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) DBUG_PRINT("qcache", ("probably no SELECT access to %s.%s => return to normal processing", table_list.db, table_list.alias)); - refused++; // This is actually a hit STRUCT_UNLOCK(&structure_guard_mutex); thd->lex->safe_to_cache_query=0; // Don't try to cache this BLOCK_UNLOCK_RD(query_block); @@ -1163,6 +1166,37 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used) DBUG_VOID_RETURN; } + +/* + Invalidate locked for write + + SYNOPSIS + Query_cache::invalidate_locked_for_write() + tables_used - table list + + NOTE + can be used only for opened tables +*/ +void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used) +{ + DBUG_ENTER("Query_cache::invalidate (changed table list)"); + if (query_cache_size > 0 && tables_used) + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + DUMP(this); + for (; tables_used; tables_used= tables_used->next) + { + if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE)) + invalidate_table(tables_used->table); + } + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + /* Remove all cached queries that uses the given table */ @@ -1654,6 +1688,12 @@ void Query_cache::free_query(Query_cache_block *query_block) */ if (result_block != 0) { + if (result_block->type != Query_cache_block::RESULT) + { + // removing unfinished query + refused++; + inserts--; + } Query_cache_block *block = result_block; do { @@ -1662,6 +1702,12 @@ void Query_cache::free_query(Query_cache_block *query_block) free_memory_block(current); } while (block != result_block); } + else + { + // removing unfinished query + refused++; + inserts--; + } query->unlock_n_destroy(); free_memory_block(query_block); @@ -1819,11 +1865,11 @@ my_bool Query_cache::write_result_data(Query_cache_block **result_block, { // It is success (nobody can prevent us write data) STRUCT_UNLOCK(&structure_guard_mutex); - byte *rest = (byte*) data; - Query_cache_block *block = *result_block; uint headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + ALIGN_SIZE(sizeof(Query_cache_result))); #ifndef EMBEDDED_LIBRARY + Query_cache_block *block= *result_block; + byte *rest= (byte*) data; // Now fill list of blocks that created by allocate_data_chain do { @@ -2567,20 +2613,15 @@ TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len, tables_used->db, tables_used->table->db_type)); *tables_type|= tables_used->table->file->table_cache_type(); + /* + table_alias_charset used here because it depends of + lower_case_table_names variable + */ if (tables_used->table->db_type == DB_TYPE_MRG_ISAM || tables_used->table->tmp_table != NO_TMP_TABLE || (tables_used->db_length == 5 && -#ifdef FN_NO_CASE_SENCE - my_strnncoll(system_charset_info, (uchar*)tables_used->db, 6, - (uchar*)"mysql",6) == 0 -#else - tables_used->db[0]=='m' && - tables_used->db[1]=='y' && - tables_used->db[2]=='s' && - tables_used->db[3]=='q' && - tables_used->db[4]=='l' -#endif - )) + my_strnncoll(table_alias_charset, (uchar*)tables_used->db, 6, + (uchar*)"mysql",6) == 0)) { DBUG_PRINT("qcache", ("select not cacheable: used MRG_ISAM, temporary or system table(s)")); diff --git a/sql/sql_cache.h b/sql/sql_cache.h index ac4f465bf79..432c7659aa5 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -375,6 +375,7 @@ protected: void invalidate(THD* thd, TABLE_LIST *tables_used, my_bool using_transactions); void invalidate(CHANGED_TABLE_LIST *tables_used); + void invalidate_locked_for_write(TABLE_LIST *tables_used); void invalidate(THD* thd, TABLE *table, my_bool using_transactions); void invalidate(THD *thd, const char *key, uint32 key_length, my_bool using_transactions); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 635e6807516..f8b2efe911c 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -86,7 +86,7 @@ extern "C" void free_user_var(user_var_entry *entry) ** Thread specific functions ****************************************************************************/ -THD::THD():user_time(0), is_fatal_error(0), +THD::THD():user_time(0), current_statement(0), is_fatal_error(0), last_insert_id_used(0), insert_id_used(0), rand_used(0), in_lock_tables(0), global_read_lock(0), bootstrap(0), spcont(NULL) @@ -150,6 +150,7 @@ THD::THD():user_time(0), is_fatal_error(0), init(); /* Initialize sub structures */ + clear_alloc_root(&transaction.mem_root); init_alloc_root(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE); user_connect=(USER_CONN *)0; hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, @@ -176,7 +177,11 @@ THD::THD():user_time(0), is_fatal_error(0), tablespace_op=FALSE; #ifdef USING_TRANSACTIONS bzero((char*) &transaction,sizeof(transaction)); - if (opt_using_transactions) + /* + Binlog is always open (if needed) before a THD is created (including + bootstrap). + */ + if (opt_using_transactions && mysql_bin_log.is_open()) { if (open_cached_file(&transaction.trans_log, mysql_tmpdir, LOG_PREFIX, binlog_cache_size, @@ -185,14 +190,8 @@ THD::THD():user_time(0), is_fatal_error(0), transaction.trans_log.end_of_file= max_binlog_cache_size; } #endif - /* - We need good random number initialization for new thread - Just coping global one will not work - */ { - pthread_mutex_lock(&LOCK_thread_count); - ulong tmp=(ulong) (my_rnd(&sql_rand) * 0xffffffff); /* make all bits random */ - pthread_mutex_unlock(&LOCK_thread_count); + ulong tmp=sql_rnd_with_mutex(); randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id); } } @@ -351,7 +350,7 @@ THD::~THD() dbug_sentry= THD_SENTRY_GONE; #endif /* Reset stmt_backup.mem_root to not double-free memory from thd.mem_root */ - init_alloc_root(&stmt_backup.mem_root, 0, 0); + clear_alloc_root(&stmt_backup.mem_root); DBUG_VOID_RETURN; } @@ -661,6 +660,8 @@ bool select_send::send_data(List<Item> &items) } } thd->sent_row_count++; + if (!thd->net.vio) + DBUG_RETURN(0); if (!thd->net.report_error) DBUG_RETURN(protocol->write()); DBUG_RETURN(1); @@ -1195,8 +1196,12 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u) else { Item_func_set_user_var *xx = new Item_func_set_user_var(mv->s, item); + /* + Item_func_set_user_var can't substitute something else on its place => + 0 can be passed as last argument (reference on item) + */ xx->fix_fields(thd, (TABLE_LIST*) thd->lex->select_lex.table_list.first, - &item); + 0); xx->fix_length_and_dec(); vars.push_back(xx); } @@ -1211,10 +1216,8 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u) Statement::Statement(THD *thd) :id(++thd->statement_id_counter), - query_id(thd->query_id), set_query_id(1), allow_sum_func(0), - command(thd->command), lex(&main_lex), query(0), query_length(0), @@ -1233,10 +1236,8 @@ Statement::Statement(THD *thd) Statement::Statement() :id(0), - query_id(0), /* initialized later */ set_query_id(1), allow_sum_func(0), /* initialized later */ - command(COM_SLEEP), /* initialized later */ lex(&main_lex), query(0), /* these two are set */ query_length(0), /* in alloc_query() */ @@ -1255,18 +1256,35 @@ Statement::Type Statement::type() const void Statement::set_statement(Statement *stmt) { id= stmt->id; - query_id= stmt->query_id; set_query_id= stmt->set_query_id; allow_sum_func= stmt->allow_sum_func; - command= stmt->command; lex= stmt->lex; query= stmt->query; query_length= stmt->query_length; - free_list= stmt->free_list; - mem_root= stmt->mem_root; } +void Statement::set_n_backup_item_arena(Statement *set, Statement *backup) +{ + backup->set_item_arena(this); + set_item_arena(set); +} + + +void Statement::restore_backup_item_arena(Statement *set, Statement *backup) +{ + set->set_item_arena(this); + set_item_arena(backup); + // reset backup mem_root to avoid its freeing + init_alloc_root(&backup->mem_root, 0, 0); +} + +void Statement::set_item_arena(Statement *set) +{ + mem_root= set->mem_root; + free_list= set->free_list; +} + Statement::~Statement() { free_root(&mem_root, MYF(0)); diff --git a/sql/sql_class.h b/sql/sql_class.h index b7ef30b4e79..fd1ebca9d9e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -217,6 +217,7 @@ public: typedef struct st_copy_info { ha_rows records; ha_rows deleted; + ha_rows updated; ha_rows copied; ha_rows error_count; enum enum_duplicates handle_duplicates; @@ -374,6 +375,7 @@ struct system_variables ulong max_length_for_sort_data; ulong max_sort_length; ulong max_tmp_tables; + ulong max_insert_delayed_threads; ulong myisam_repair_threads; ulong myisam_sort_buff_size; ulong net_buffer_length; @@ -409,6 +411,7 @@ struct system_variables my_bool log_warnings; my_bool low_priority_updates; my_bool new_mode; + my_bool query_cache_wlock_invalidate; my_bool old_passwords; /* Only charset part of these variables is sensible */ @@ -450,7 +453,7 @@ class Statement public: /* FIXME: must be private */ LEX main_lex; -public: + /* Uniquely identifies each statement object in thread scope; change during statement lifetime. FIXME: must be const @@ -458,15 +461,6 @@ public: ulong id; /* - Id of current query. Statement can be reused to execute several queries - query_id is global in context of the whole MySQL server. - ID is automatically generated from mutex-protected counter. - It's used in handler code for various purposes: to check which columns - from table are necessary for this select, to check if it's necessary to - update auto-updatable fields (like auto_increment and timestamp). - */ - ulong query_id; - /* - if set_query_id=1, we set field->query_id for all fields. In that case field list can not contain duplicates. */ @@ -484,11 +478,6 @@ public: See item_sum.cc for details. */ bool allow_sum_func; - /* - Type of current query: COM_PREPARE, COM_QUERY, etc. Set from - first byte of the packet in do_command() - */ - enum enum_server_command command; LEX *lex; // parse tree descriptor /* @@ -499,7 +488,7 @@ public: char *query; uint32 query_length; // current query length /* - List of items created in the parser for this query. Every item puts + List of items created in the parser for this query. Every item puts itself to the list on creation (see Item::Item() for details)) */ Item *free_list; @@ -526,6 +515,32 @@ public: void set_statement(Statement *stmt); /* return class type */ virtual Type type() const; + + 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))) + bzero((char*) ptr,size); + return ptr; + } + inline char *strdup(const char *str) + { return strdup_root(&mem_root,str); } + inline char *strmake(const char *str, uint size) + { return strmake_root(&mem_root,str,size); } + inline char *memdup(const char *str, uint 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))) + memcpy(ptr,str,size); + return ptr; + } + + void set_n_backup_item_arena(Statement *set, Statement *backup); + void restore_backup_item_arena(Statement *set, Statement *backup); + void set_item_arena(Statement *set); }; @@ -676,11 +691,16 @@ public: points to a lock object if the lock is present. See item_func.cc and chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK. */ - ULL *ull; + User_level_lock *ull; #ifndef DBUG_OFF uint dbug_sentry; // watch out for memory corruption #endif struct st_my_thread_var *mysys_var; + /* + Type of current query: COM_PREPARE, COM_QUERY, etc. Set from + first byte of the packet in do_command() + */ + enum enum_server_command command; uint32 server_id; uint32 file_id; // for LOAD DATA INFILE /* @@ -695,7 +715,7 @@ public: delayed_insert *di; my_bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */ struct st_transactions { - IO_CACHE trans_log; + IO_CACHE trans_log; // Inited ONLY if binlog is open ! THD_TRANS all; // Trans since BEGIN WORK THD_TRANS stmt; // Trans for current statement uint bdb_lock_count; @@ -721,6 +741,10 @@ public: Vio* active_vio; #endif /* + Current prepared Statement if there one, or 0 + */ + Statement *current_statement; + /* next_insert_id is set on SET INSERT_ID= #. This is used as the next generated auto_increment value in handler.cc */ @@ -752,6 +776,15 @@ public: List <MYSQL_ERROR> warn_list; uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END]; uint total_warn_count; + /* + Id of current query. Statement can be reused to execute several queries + query_id is global in context of the whole MySQL server. + ID is automatically generated from mutex-protected counter. + It's used in handler code for various purposes: to check which columns + from table are necessary for this select, to check if it's necessary to + update auto-updatable fields (like auto_increment and timestamp). + */ + ulong query_id; ulong warn_id, version, options, thread_id, col_access; /* Statement id is thread-wide. This counter is used to generate ids */ @@ -896,34 +929,14 @@ public: return 0; #endif } - 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))) - bzero((char*) ptr,size); - return ptr; - } - inline char *strdup(const char *str) - { return strdup_root(&mem_root,str); } - inline char *strmake(const char *str, uint size) - { return strmake_root(&mem_root,str,size); } - inline char *memdup(const char *str, uint 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))) - memcpy(ptr,str,size); - return ptr; - } - bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, - const char *from, uint from_length, - CHARSET_INFO *from_cs); inline gptr trans_alloc(unsigned int size) { return alloc_root(&transaction.mem_root,size); } + + bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, + const char *from, uint from_length, + CHARSET_INFO *from_cs); void add_changed_table(TABLE *table); void add_changed_table(const char *key, long key_length); CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length); @@ -946,6 +959,33 @@ public: } inline CHARSET_INFO *charset() { return variables.character_set_client; } void update_charset(); + + inline void allocate_temporary_memory_pool_for_ps_preparing() + { + DBUG_ASSERT(current_statement!=0); + /* + We do not want to have in PS memory all that junk, + which will be created by preparation => substitute memory + from original thread pool. + + We know that PS memory pool is now copied to THD, we move it back + to allow some code use it. + */ + current_statement->set_item_arena(this); + init_sql_alloc(&mem_root, + variables.query_alloc_block_size, + variables.query_prealloc_size); + free_list= 0; + } + inline void free_temporary_memory_pool_for_ps_preparing() + { + DBUG_ASSERT(current_statement!=0); + cleanup_items(current_statement->free_list); + free_items(free_list); + close_thread_tables(this); // to close derived tables + free_root(&mem_root, MYF(0)); + set_item_arena(current_statement); + } }; /* Flags for the THD::system_thread (bitmap) variable */ @@ -1055,8 +1095,9 @@ class select_insert :public select_result { ulonglong last_insert_id; COPY_INFO info; - select_insert(TABLE *table_par,List<Item> *fields_par,enum_duplicates duplic) - :table(table_par),fields(fields_par), last_insert_id(0) + select_insert(TABLE *table_par, List<Item> *fields_par, + enum_duplicates duplic) + :table(table_par), fields(fields_par), last_insert_id(0) { bzero((char*) &info,sizeof(info)); info.handle_duplicates=duplic; @@ -1081,11 +1122,11 @@ class select_create: public select_insert { MYSQL_LOCK *lock; Field **field; public: - select_create (const char *db_name, const char *table_name, - HA_CREATE_INFO *create_info_par, - List<create_field> &fields_par, - List<Key> &keys_par, - List<Item> &select_fields,enum_duplicates duplic) + select_create(const char *db_name, const char *table_name, + 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), name(table_name), extra_fields(&fields_par),keys(&keys_par), create_info(create_info_par), lock(0) diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 54265b58bd4..ce92fa7ff90 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -59,9 +59,9 @@ static bool write_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create) if ((file=my_create(path, CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0) { ulong length; - CHARSET_INFO *cs= (create && create->default_table_charset) ? - create->default_table_charset : - thd->variables.collation_server; + CHARSET_INFO *cs= ((create && create->default_table_charset) ? + create->default_table_charset : + thd->variables.collation_server); length= my_sprintf(buf,(buf, "default-character-set=%s\ndefault-collation=%s\n", cs->csname,cs->name)); @@ -117,10 +117,12 @@ bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create) { if (!strncmp(buf,"default-character-set", (pos-buf))) { - if (!(create->default_table_charset= get_charset_by_csname(pos+1, - MY_CS_PRIMARY, - MYF(0)))) + if (!(create->default_table_charset= + get_charset_by_csname(pos+1, + MY_CS_PRIMARY, + MYF(0)))) { + sql_print_error("Error while loading database options: '%s':",path); sql_print_error(ER(ER_UNKNOWN_CHARACTER_SET),pos+1); } } @@ -129,6 +131,7 @@ bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create) if (!(create->default_table_charset= get_charset_by_name(pos+1, MYF(0)))) { + sql_print_error("Error while loading database options: '%s':",path); sql_print_error(ER(ER_UNKNOWN_COLLATION),pos+1); } } @@ -164,9 +167,9 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, bool silent) { char path[FN_REFLEN+16]; - MY_DIR *dirp; long result=1; int error = 0; + MY_STAT stat_info; uint create_options = create_info ? create_info->options : 0; DBUG_ENTER("mysql_create_db"); @@ -180,12 +183,12 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, } /* Check directory */ - (void)sprintf(path,"%s/%s", mysql_data_home, db); + strxmov(path, mysql_data_home, "/", db, NullS); unpack_dirname(path,path); // Convert if not unix - if ((dirp = my_dir(path,MYF(MY_DONT_SORT)))) + + if (my_stat(path,&stat_info,MYF(MY_WME))) { - my_dirend(dirp); - if (!(create_options & HA_LEX_CREATE_IF_NOT_EXISTS)) + if (!(create_options & HA_LEX_CREATE_IF_NOT_EXISTS)) { my_error(ER_DB_CREATE_EXISTS,MYF(0),db); error = -1; @@ -306,11 +309,19 @@ exit2: /* - Drop all tables in a database. + Drop all tables in a database and the database itself - db-name is already validated when we come here - If thd == 0, do not write any messages; This is useful in replication - when we want to remove a stale database before replacing it with the new one + SYNOPSIS + mysql_rm_db() + thd Thread handle + db Database name in the case given by user + It's already validated when we come here + if_exists Don't give error if database doesn't exists + silent Don't generate errors + + RETURN + 0 ok (Database dropped) + -1 Error generated */ @@ -318,7 +329,7 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) { long deleted=0; int error = 0; - char path[FN_REFLEN+16]; + char path[FN_REFLEN+16], tmp_db[NAME_LEN+1]; MY_DIR *dirp; DBUG_ENTER("mysql_rm_db"); @@ -350,6 +361,14 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) } goto exit; } + if (lower_case_table_names) + { + /* Convert database to lower case */ + strmov(tmp_db, db); + my_casedn_str(system_charset_info, tmp_db); + db= tmp_db; + } + pthread_mutex_lock(&LOCK_open); remove_db_from_cache(db); pthread_mutex_unlock(&LOCK_open); @@ -425,7 +444,7 @@ exit2: /* Removes files with known extensions plus all found subdirectories that - are 2 digits (raid directories). + are 2 hex digits (raid directories). thd MUST be set when calling this function! */ @@ -447,88 +466,83 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, idx++) { FILEINFO *file=dirp->dir_entry+idx; + char *extension; DBUG_PRINT("info",("Examining: %s", file->name)); /* Check if file is a raid directory */ - if (my_isdigit(&my_charset_latin1,file->name[0]) && - my_isdigit(&my_charset_latin1,file->name[1]) && + if ((my_isdigit(&my_charset_latin1, file->name[0]) || + (file->name[0] >= 'a' && file->name[0] <= 'f')) && + (my_isdigit(&my_charset_latin1, file->name[1]) || + (file->name[1] >= 'a' && file->name[1] <= 'f')) && !file->name[2] && !level) { - char newpath[FN_REFLEN]; + char newpath[FN_REFLEN], *copy_of_path; MY_DIR *new_dirp; String *dir; + uint length; strxmov(newpath,org_path,"/",file->name,NullS); - unpack_filename(newpath,newpath); + length= unpack_filename(newpath,newpath); if ((new_dirp = my_dir(newpath,MYF(MY_DONT_SORT)))) { DBUG_PRINT("my",("New subdir found: %s", newpath)); if ((mysql_rm_known_files(thd, new_dirp, NullS, newpath,1)) < 0) - { - my_dirend(dirp); - DBUG_RETURN(-1); - } - raid_dirs.push_back(dir=new String(newpath, &my_charset_latin1)); - dir->copy(); + goto err; + if (!(copy_of_path= thd->memdup(newpath, length+1)) || + !(dir= new (&thd->mem_root) String(copy_of_path, length, + &my_charset_bin)) || + raid_dirs.push_back(dir)) + goto err; continue; } found_other_files++; continue; } - if (find_type(fn_ext(file->name),&deletable_extentions,1+2) <= 0) + extension= fn_ext(file->name); + if (find_type(extension, &deletable_extentions,1+2) <= 0) { - if (find_type(fn_ext(file->name),&known_extentions,1+2) <= 0) + if (find_type(extension, &known_extentions,1+2) <= 0) found_other_files++; continue; } - strxmov(filePath,org_path,"/",file->name,NullS); - if (db && !my_strcasecmp(&my_charset_latin1, - fn_ext(file->name), reg_ext)) + // just for safety we use files_charset_info + if (db && !my_strcasecmp(files_charset_info, + extension, reg_ext)) { /* Drop the table nicely */ - *fn_ext(file->name)=0; // Remove extension + *extension= 0; // Remove extension TABLE_LIST *table_list=(TABLE_LIST*) thd->calloc(sizeof(*table_list)+ strlen(db)+strlen(file->name)+2); if (!table_list) - { - my_dirend(dirp); - DBUG_RETURN(-1); - } + goto err; table_list->db= (char*) (table_list+1); - strmov(table_list->real_name=strmov(table_list->db,db)+1, - file->name); + strmov(table_list->real_name= strmov(table_list->db,db)+1, file->name); + table_list->alias= table_list->real_name; // If lower_case_table_names=2 /* Link into list */ (*tot_list_next)= table_list; tot_list_next= &table_list->next; + deleted++; } else { - + strxmov(filePath, org_path, "/", file->name, NullS); if (my_delete_with_symlink(filePath,MYF(MY_WME))) { - my_dirend(dirp); - DBUG_RETURN(-1); + goto err; } - deleted++; } } - List_iterator<String> it(raid_dirs); - String *dir; - if (thd->killed || (tot_list && mysql_rm_table_part2_with_lock(thd, tot_list, 1, 0, 1))) + goto err; + + /* Remove RAID directories */ { - /* Free memory for allocated raid dirs */ + List_iterator<String> it(raid_dirs); + String *dir; while ((dir= it++)) - delete dir; - my_dirend(dirp); - DBUG_RETURN(-1); - } - while ((dir= it++)) - { - if (rmdir(dir->c_ptr()) < 0) - found_other_files++; - delete dir; + if (rmdir(dir->c_ptr()) < 0) + found_other_files++; } my_dirend(dirp); @@ -539,7 +553,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, if (!found_other_files) { char tmp_path[FN_REFLEN], *pos; - char *path=unpack_filename(tmp_path,org_path); + char *path= tmp_path; + unpack_filename(tmp_path,org_path); #ifdef HAVE_READLINK int error; @@ -576,6 +591,10 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, } } DBUG_RETURN(deleted); + +err: + my_dirend(dirp); + DBUG_RETURN(-1); } @@ -606,13 +625,13 @@ bool mysql_change_db(THD *thd, const char *name) HA_CREATE_INFO create; DBUG_ENTER("mysql_change_db"); - if (!dbname || !(db_length=strip_sp(dbname))) + if (!dbname || !(db_length= strlen(dbname))) { x_free(dbname); /* purecov: inspected */ send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } - if ((db_length > NAME_LEN) || check_db_name(dbname)) + if (check_db_name(dbname)) { net_printf(thd, ER_WRONG_DB_NAME, dbname); x_free(dbname); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 7ebe9cb6002..ad6de901f56 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -194,6 +194,8 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, else table->file->unlock_row(); // Row failed selection, release lock on it } + if (thd->killed && !error) + error= 1; // Aborted thd->proc_info="end"; end_read_record(&info); free_io_cache(table); // Will not do any harm @@ -376,7 +378,7 @@ bool multi_delete::send_data(List<Item> &values) table->status|= STATUS_DELETED; if (!(error=table->file->delete_row(table->record[0]))) deleted++; - else if (!table_being_deleted->next) + else if (!table_being_deleted->next || table_being_deleted->table->file->has_transactions()) { table->file->print_error(error,MYF(0)); DBUG_RETURN(1); @@ -395,6 +397,7 @@ bool multi_delete::send_data(List<Item> &values) DBUG_RETURN(0); } + void multi_delete::send_error(uint errcode,const char *err) { DBUG_ENTER("multi_delete::send_error"); @@ -484,6 +487,8 @@ int multi_delete::do_deletes(bool from_send_error) deleted++; } end_read_record(&info); + if (thd->killed && !local_error) + local_error= 1; if (local_error == -1) // End of file local_error = 0; } diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index d0ed71ecfdd..4a9bf3f1348 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -115,7 +115,6 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, TABLE *table; int res; select_union *derived_result; - TABLE_LIST *tables= (TABLE_LIST *)first_select->table_list.first; bool is_union= first_select->next_select() && first_select->next_select()->linkage == UNION_TYPE; bool is_subsel= first_select->first_inner_unit() ? 1: 0; @@ -138,7 +137,7 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, */ if (!(table= create_tmp_table(thd, &derived_result->tmp_table_param, unit->types, (ORDER*) 0, - is_union && !unit->union_option, 1, + is_union && unit->union_distinct, 1, (first_select->options | thd->options | TMP_TABLE_ALL_COLUMNS), HA_POS_ERROR, @@ -151,7 +150,11 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, if (is_union) - res= mysql_union(thd, lex, derived_result, unit); + { + // execute union without clean up + if (!(res= unit->prepare(thd, derived_result, SELECT_NO_UNLOCK))) + res= unit->exec(); + } else { unit->set_limit(first_select, first_select); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 897aa37ba11..1579ac3b5c8 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -35,7 +35,7 @@ But !!! do_command calls free_root at the end of every query and frees up all the sql_alloc'ed memory. It's harder to work around... - */ +*/ #define HANDLER_TABLES_HACK(thd) { \ TABLE *tmp=thd->open_tables; \ @@ -207,11 +207,13 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, MYF(0),keyinfo->key_parts); goto err; } - List_iterator_fast<Item> it_ke(*key_expr); + List_iterator<Item> it_ke(*key_expr); Item *item; for (key_len=0 ; (item=it_ke++) ; key_part++) { - if (item->fix_fields(thd, tables, &item)) + // 'item' can be changed by fix_fields() call + if (item->fix_fields(thd, tables, it_ke.ref()) || + (item= *it_ke.ref())->check_cols(1)) goto err; if (item->used_tables() & ~RAND_TABLE_BIT) { @@ -287,14 +289,13 @@ static TABLE **find_table_ptr_by_name(THD *thd, const char *db, int dblen; TABLE **ptr; - if (!db || ! *db) - db= thd->db ? thd->db : ""; - dblen=strlen(db)+1; + DBUG_ASSERT(db); + dblen= strlen(db); ptr= &(thd->handler_tables); for (TABLE *table= *ptr; table ; table= *ptr) { - if (!memcmp(table->table_cache_key, db, dblen) && + if ((db == any_db || !memcmp(table->table_cache_key, db, dblen)) && !my_strcasecmp(system_charset_info, (is_alias ? table->table_name : table->real_name), table_name)) diff --git a/sql/sql_help.cc b/sql/sql_help.cc index 3c98b7b0bb4..44293b8214f 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -140,7 +140,7 @@ void memorize_variant_topic(THD *thd, TABLE *topics, int count, { if (count == 1) names->push_back(name); - String *new_name= new 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); } @@ -391,7 +391,7 @@ int search_categories(THD *thd, TABLE *categories, { if (select && !select->cond->val_int()) continue; - String *lname= new String; + 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(); @@ -425,7 +425,7 @@ void get_all_items_for_category(THD *thd, TABLE *items, Field *pfname, { if (!select->cond->val_int()) continue; - String *name= new String(); + String *name= new (&thd->mem_root) String(); get_field(&thd->mem_root,pfname,name); res->push_back(name); } @@ -659,20 +659,17 @@ int mysqld_help(THD *thd, const char *mask) bzero((gptr)tables,sizeof(tables)); tables[0].alias= tables[0].real_name= (char*) "help_topic"; tables[0].lock_type= TL_READ; - tables[0].db= (char*) "mysql"; tables[0].next= &tables[1]; tables[1].alias= tables[1].real_name= (char*) "help_category"; tables[1].lock_type= TL_READ; - tables[1].db= (char*) "mysql"; tables[1].next= &tables[2]; tables[2].alias= tables[2].real_name= (char*) "help_relation"; tables[2].lock_type= TL_READ; - tables[2].db= (char*) "mysql"; tables[2].next= &tables[3]; tables[3].alias= tables[3].real_name= (char*) "help_keyword"; tables[3].lock_type= TL_READ; - tables[3].db= (char*) "mysql"; tables[3].next= 0; + tables[0].db= tables[1].db= tables[2].db= tables[3].db= (char*) "mysql"; List<String> topics_list, categories_list, subcategories_list; String name, description, example; @@ -686,7 +683,7 @@ int mysqld_help(THD *thd, const char *mask) goto end; } /* Init tables and fields to be usable from items */ - setup_tables(tables, 0); + setup_tables(tables); memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields)); if (init_fields(thd, tables, used_fields, array_elements(used_fields))) { @@ -731,27 +728,30 @@ int mysqld_help(THD *thd, const char *mask) &categories_list,&category_id); if (!count_categories) { - if (send_header_2(protocol,false)) + if (send_header_2(protocol,FALSE)) goto end; } else if (count_categories > 1) { - if (send_header_2(protocol,false) || + if (send_header_2(protocol,FALSE) || send_variant_2_list(mem_root,protocol,&categories_list,"Y",0)) goto end; } else { Field *topic_cat_id= used_fields[help_topic_help_category_id].field; - Item *cond_topic_by_cat= new Item_func_equal(new Item_field(topic_cat_id), - new Item_int((int32)category_id)); - Item *cond_cat_by_cat= new Item_func_equal(new Item_field(cat_cat_id), - new Item_int((int32)category_id)); + Item *cond_topic_by_cat= + new Item_func_equal(new Item_field(topic_cat_id), + new Item_int((int32)category_id)); + Item *cond_cat_by_cat= + new Item_func_equal(new Item_field(cat_cat_id), + new Item_int((int32)category_id)); if (!(select_topics_by_cat= prepare_simple_select(thd,cond_topic_by_cat, tables,tables[0].table, &error)) || - !(select_cat_by_cat= prepare_simple_select(thd,cond_cat_by_cat,tables, - tables[1].table,&error))) + !(select_cat_by_cat= + prepare_simple_select(thd,cond_cat_by_cat,tables, + tables[1].table,&error))) { res= -1; goto end; @@ -777,7 +777,7 @@ int mysqld_help(THD *thd, const char *mask) else { /* First send header and functions */ - if (send_header_2(protocol, false) || + if (send_header_2(protocol, FALSE) || send_variant_2_list(mem_root,protocol, &topics_list, "N", 0)) goto end; search_categories(thd, tables[1].table, used_fields, diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 3a7e2a8d285..3e1a41ed049 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -21,12 +21,14 @@ #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, char *query, uint query_length, bool log_on); static void end_delayed_insert(THD *thd); extern "C" pthread_handler_decl(handle_delayed_insert,arg); static void unlink_blobs(register TABLE *table); +#endif /* Define to force use of my_malloc() if the allocated memory block is big */ @@ -39,9 +41,9 @@ static void unlink_blobs(register TABLE *table); #endif /* - Check if insert fields are correct - Updates table->time_stamp to point to timestamp field or 0, depending on - if timestamp should be updated or not. + Check if insert fields are correct. + Sets table->timestamp_default_now/on_update_now to 0 o leaves it to point + to timestamp field, depending on if timestamp should be updated or not. */ int @@ -62,7 +64,7 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, check_grant_all_columns(thd,INSERT_ACL,table)) return -1; #endif - table->time_stamp=0; // This is saved by caller + table->timestamp_default_now= table->timestamp_on_update_now= 0; } else { // Part field list @@ -81,18 +83,18 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, table_list.grant=table->grant; thd->dupp_field=0; - if (setup_tables(&table_list, 0) || + if (setup_tables(&table_list) || setup_fields(thd, 0, &table_list,fields,1,0,0)) return -1; + if (thd->dupp_field) { my_error(ER_FIELD_SPECIFIED_TWICE,MYF(0), thd->dupp_field->field_name); return -1; } - table->time_stamp=0; if (table->timestamp_field && // Don't set timestamp if used - table->timestamp_field->query_id != thd->query_id) - table->time_stamp= table->timestamp_field->offset()+1; + table->timestamp_field->query_id == thd->query_id) + table->timestamp_default_now= table->timestamp_on_update_now= 0; } // For the values we need select_priv #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -109,7 +111,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &update_values, enum_duplicates duplic) { - int error; + int error, res; /* log_on is about delayed inserts only. By default, both logs are enabled (this won't cause problems if the server @@ -124,7 +126,9 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, TABLE *table; List_iterator_fast<List_item> its(values_list); List_item *values; - char *query=thd->query; +#ifndef EMBEDDED_LIBRARY + char *query= thd->query; +#endif thr_lock_type lock_type = table_list->lock_type; TABLE_LIST *insert_table_list= (TABLE_LIST*) thd->lex->select_lex.table_list.first; @@ -135,15 +139,20 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, if we are told to replace duplicates, the insert cannot be concurrent delayed insert changed to regular in slave thread */ +#ifdef EMBEDDED_LIBRARY + if (lock_type == TL_WRITE_DELAYED) + lock_type=TL_WRITE; +#else if ((lock_type == TL_WRITE_DELAYED && ((specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) || - thd->slave_thread || !max_insert_delayed_threads)) || + thd->slave_thread || !thd->variables.max_insert_delayed_threads)) || (lock_type == TL_WRITE_CONCURRENT_INSERT && duplic == DUP_REPLACE) || (duplic == DUP_UPDATE)) lock_type=TL_WRITE; +#endif table_list->lock_type= lock_type; - int res; +#ifndef EMBEDDED_LIBRARY if (lock_type == TL_WRITE_DELAYED) { if (thd->locked_tables) @@ -172,6 +181,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, } } else +#endif /* EMBEDDED_LIBRARY */ res= open_and_lock_tables(thd, table_list); if (res) DBUG_RETURN(-1); @@ -191,7 +201,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, } if (check_insert_fields(thd,table,fields,*values,1) || - setup_tables(insert_table_list, 0) || + setup_tables(insert_table_list) || 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) || @@ -223,7 +233,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, Fill in the given fields and dump it to the table file */ - info.records=info.deleted=info.copied=0; + info.records= info.deleted= info.copied= info.updated= 0; info.handle_duplicates=duplic; info.update_fields=&update_fields; info.update_values=&update_values; @@ -289,12 +299,14 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, break; } } +#ifndef EMBEDDED_LIBRARY if (lock_type == TL_WRITE_DELAYED) { error=write_delayed(thd,table,duplic,query, thd->query_length, log_on); query=0; } else +#endif error=write_record(table,&info); if (error) break; @@ -315,6 +327,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, Now all rows are inserted. Time to update logs and sends response to user */ +#ifndef EMBEDDED_LIBRARY if (lock_type == TL_WRITE_DELAYED) { if (!error) @@ -326,6 +339,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, query_cache_invalidate3(thd, table_list, 1); } else +#endif { if (bulk_insert) { @@ -356,7 +370,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, For the transactional algorithm to work the invalidation must be before binlog writing and ha_autocommit_... */ - if (info.copied || info.deleted) + if (info.copied || info.deleted || info.updated) { query_cache_invalidate3(thd, table_list, 1); } @@ -364,7 +378,8 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, transactional_table= table->file->has_transactions(); log_delayed= (transactional_table || table->tmp_table); - if ((info.copied || info.deleted) && (error <= 0 || !transactional_table)) + if ((info.copied || info.deleted || info.updated) && + (error <= 0 || !transactional_table)) { if (mysql_bin_log.is_open()) { @@ -404,7 +419,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, goto abort; if (values_list.elements == 1 && (!(thd->options & OPTION_WARNINGS) || !thd->cuted_fields)) - send_ok(thd,info.copied+info.deleted,id); + send_ok(thd,info.copied+info.deleted+info.updated,id); else { char buff[160]; @@ -414,16 +429,18 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, (ulong) (info.records - info.copied), (ulong) thd->cuted_fields); else sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records, - (ulong) info.deleted, (ulong) thd->cuted_fields); - ::send_ok(thd,info.copied+info.deleted,(ulonglong)id,buff); + (ulong) info.deleted+info.updated, (ulong) thd->cuted_fields); + ::send_ok(thd,info.copied+info.deleted+info.updated,(ulonglong)id,buff); } free_underlaid_joins(thd, &thd->lex->select_lex); table->insert_values=0; DBUG_RETURN(0); abort: +#ifndef EMBEDDED_LIBRARY if (lock_type == TL_WRITE_DELAYED) end_delayed_insert(thd); +#endif free_underlaid_joins(thd, &thd->lex->select_lex); table->insert_values=0; DBUG_RETURN(-1); @@ -517,12 +534,22 @@ int write_record(TABLE *table,COPY_INFO *info) goto err; if ((error=table->file->update_row(table->record[1],table->record[0]))) goto err; - info->deleted++; + info->updated++; break; } else /* DUP_REPLACE */ { - if (last_uniq_key(table,key_nr)) + /* + The manual defines the REPLACE semantics that it is either + an INSERT or DELETE(s) + INSERT; FOREIGN KEY checks in + InnoDB do not function in the defined way if we allow MySQL + to convert the latter operation internally to an UPDATE. + We also should not perform this conversion if we have + timestamp field with ON UPDATE which is different from DEFAULT. + */ + if (last_uniq_key(table,key_nr) && + !table->file->referenced_by_foreign_key() && + table->timestamp_default_now == table->timestamp_on_update_now) { if ((error=table->file->update_row(table->record[1], table->record[0]))) @@ -588,6 +615,8 @@ static int check_null_fields(THD *thd __attribute__((unused)), A thread is created for each table that one uses with the DELAYED attribute. *****************************************************************************/ +#ifndef EMBEDDED_LIBRARY + class delayed_row :public ilink { public: char *record,*query; @@ -595,7 +624,8 @@ public: time_t start_time; bool query_start_used,last_insert_id_used,insert_id_used, log_query; ulonglong last_insert_id; - ulong time_stamp; + ulong timestamp_default_now; + ulong timestamp_on_update_now; uint query_length; delayed_row(enum_duplicates dup_arg, bool log_query_arg) @@ -729,7 +759,7 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) if (!(tmp=find_handler(thd,table_list))) { /* Don't create more than max_insert_delayed_threads */ - if (delayed_insert_threads >= max_insert_delayed_threads) + if (delayed_insert_threads >= thd->variables.max_insert_delayed_threads) DBUG_RETURN(0); thd->proc_info="Creating delayed handler"; pthread_mutex_lock(&LOCK_delayed_create); @@ -875,6 +905,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) { 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] if (*org_field == found_next_number_field) (*field)->table->found_next_number_field= *field; @@ -885,9 +916,10 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) if (table->timestamp_field) { /* Restore offset as this may have been reset in handle_inserts */ - copy->time_stamp=table->timestamp_field->offset()+1; copy->timestamp_field= (Field_timestamp*) copy->field[table->timestamp_field_offset]; + copy->timestamp_field->unireg_check= table->timestamp_field->unireg_check; + copy->timestamp_field->set_timestamp_offsets(); } /* _rowid is not used with delayed insert */ @@ -938,7 +970,8 @@ static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic, row->last_insert_id_used= thd->last_insert_id_used; row->insert_id_used= thd->insert_id_used; row->last_insert_id= thd->last_insert_id; - row->time_stamp= table->time_stamp; + row->timestamp_default_now= table->timestamp_default_now; + row->timestamp_on_update_now= table->timestamp_on_update_now; di->rows.push_back(row); di->stacked_inserts++; @@ -1277,7 +1310,8 @@ bool delayed_insert::handle_inserts(void) thd.last_insert_id=row->last_insert_id; thd.last_insert_id_used=row->last_insert_id_used; thd.insert_id_used=row->insert_id_used; - table->time_stamp=row->time_stamp; + table->timestamp_default_now= row->timestamp_default_now; + table->timestamp_on_update_now= row->timestamp_on_update_now; info.handle_duplicates= row->dup; if (info.handle_duplicates == DUP_IGNORE || @@ -1310,8 +1344,15 @@ bool delayed_insert::handle_inserts(void) pthread_mutex_lock(&mutex); delete row; - /* Let READ clients do something once in a while */ - if (group_count++ == max_rows) + /* + Let READ clients do something once in a while + We should however not break in the middle of a multi-line insert + if we have binary logging enabled as we don't want other commands + on this table until all entries has been processed + */ + if (group_count++ >= max_rows && (row= rows.head()) && + (!(row->log_query & DELAYED_LOG_BIN && using_bin_log) || + row->query)) { group_count=0; if (stacked_inserts || tables_in_use) // Let these wait a while @@ -1368,8 +1409,7 @@ bool delayed_insert::handle_inserts(void) pthread_mutex_lock(&mutex); DBUG_RETURN(1); } - - +#endif /* EMBEDDED_LIBRARY */ /*************************************************************************** Store records in INSERT ... SELECT * @@ -1454,7 +1494,8 @@ void select_insert::send_error(uint errcode,const char *err) error while inserting into a MyISAM table) we must write to the binlog (and the error code will make the slave stop). */ - if ((info.copied || info.deleted) && !table->file->has_transactions()) + if ((info.copied || info.deleted || info.updated) && + !table->file->has_transactions()) { if (last_insert_id) thd->insert_id(last_insert_id); // For binary log @@ -1467,7 +1508,7 @@ void select_insert::send_error(uint errcode,const char *err) if (!table->tmp_table) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } - if (info.copied || info.deleted) + if (info.copied || info.deleted || info.updated) { query_cache_invalidate3(thd, table, 1); } @@ -1490,7 +1531,7 @@ bool select_insert::send_eof() and ha_autocommit_... */ - if (info.copied || info.deleted) + if (info.copied || info.deleted || info.updated) { query_cache_invalidate3(thd, table, 1); if (!(table->file->has_transactions() || table->tmp_table)) @@ -1523,8 +1564,8 @@ bool select_insert::send_eof() (ulong) (info.records - info.copied), (ulong) thd->cuted_fields); else sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records, - (ulong) info.deleted, (ulong) thd->cuted_fields); - ::send_ok(thd,info.copied+info.deleted,last_insert_id,buff); + (ulong) info.deleted+info.updated, (ulong) thd->cuted_fields); + ::send_ok(thd,info.copied+info.deleted+info.updated,last_insert_id,buff); DBUG_RETURN(0); } @@ -1539,8 +1580,8 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) DBUG_ENTER("select_create::prepare"); unit= u; - table=create_table_from_items(thd, create_info, db, name, - extra_fields, keys, &values, &lock); + table= create_table_from_items(thd, create_info, db, name, + extra_fields, keys, &values, &lock); if (!table) DBUG_RETURN(-1); // abort() deletes table @@ -1555,11 +1596,9 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) /* First field to copy */ field=table->field+table->fields - values.elements; - if (table->timestamp_field) // Don't set timestamp if used - { - table->timestamp_field->set_time(); - table->time_stamp=0; // This should be saved - } + /* Don't set timestamp if used */ + table->timestamp_default_now= table->timestamp_on_update_now= 0; + table->next_number_field=table->found_next_number_field; restore_record(table,default_values); // Get empty record @@ -1645,7 +1684,9 @@ void select_create::abort() #ifdef __GNUC__ template class List_iterator_fast<List_item>; +#ifndef EMBEDDED_LIBRARY template class I_List<delayed_insert>; template class I_List_iterator<delayed_insert>; template class I_List<delayed_row>; -#endif +#endif /* EMBEDDED_LIBRARY */ +#endif /* __GNUC__ */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 1b2457724de..071e41b1247 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -678,16 +678,15 @@ int yylex(void *arg, void *yythd) yylval->lex_str= get_token(lex,yyLength()); return(result_state); - case MY_LEX_USER_VARIABLE_DELIMITER: + case MY_LEX_USER_VARIABLE_DELIMITER: // Found quote char { uint double_quotes= 0; char quote_char= c; // Used char lex->tok_start=lex->ptr; // Skip first ` while ((c=yyGet())) - { -#ifdef USE_MB - if (my_mbcharlen(cs, c) == 1) -#endif + { + int length; + if ((length= my_mbcharlen(cs, c)) == 1) { if (c == (uchar) NAMES_SEP_CHAR) break; /* Old .frm format can't handle this char */ @@ -701,15 +700,9 @@ int yylex(void *arg, void *yythd) } } #ifdef USE_MB - else - { - int l; - if ((l = my_ismbchar(cs, - (const char *)lex->ptr-1, - (const char *)lex->end_of_query)) == 0) - break; - lex->ptr += l-1; - } + else if (length < 1) + break; // Error + lex->ptr+= length-1; #endif } if (double_quotes) @@ -994,13 +987,14 @@ void st_select_lex_unit::init_query() global_parameters= first_select(); select_limit_cnt= HA_POS_ERROR; offset_limit_cnt= 0; - union_option= 0; + union_distinct= 0; prepared= optimized= executed= 0; item= 0; union_result= 0; table= 0; fake_select_lex= 0; cleaned= 0; + item_list.empty(); found_rows_for_union= 0; } @@ -1319,8 +1313,8 @@ bool st_select_lex_unit::create_total_list(THD *thd_arg, st_lex *lex, DESCRIPTION This is used for UNION & subselect to create a new table list of all used tables. - The table_list->table entry in all used tables are set to point - to the entries in this list. + The table_list->table_list in all tables of global list are set to point + to the local SELECT_LEX entries. RETURN 0 - OK @@ -1371,33 +1365,22 @@ create_total_list_n_last_return(THD *thd_arg, { TABLE_LIST *cursor; next_table= aux->next; - for (cursor= **result_arg; cursor; cursor= cursor->next) - if (!strcmp(cursor->db, aux->db) && - !strcmp(cursor->real_name, aux->real_name) && - !strcmp(cursor->alias, aux->alias)) - break; - if (!cursor) + /* Add to the total table list */ + if (!(cursor= (TABLE_LIST *) thd->memdup((char*) aux, + sizeof(*aux)))) { - /* Add not used table to the total table list */ - if (!(cursor= (TABLE_LIST *) thd->memdup((char*) aux, - sizeof(*aux)))) - { - send_error(thd,0); - return 1; - } - *new_table_list= cursor; - cursor->table_list= aux; //to be able mark this table as shared - new_table_list= &cursor->next; - *new_table_list= 0; // end result list + send_error(thd,0); + return 1; } - else - // Mark that it's used twice - cursor->table_list->shared= aux->shared= 1; + *new_table_list= cursor; + cursor->table_list= aux; + new_table_list= &cursor->next; + *new_table_list= 0; // end result list aux->table_list= cursor; } } } -end: + if (slave_list_first) { *new_table_list= slave_list_first; @@ -1509,11 +1492,17 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) { if (ref_pointer_array) return 0; + + /* + We have to create array in prepared statement memory if it is + prepared statement + */ + Statement *stmt= thd->current_statement ? thd->current_statement : thd; return (ref_pointer_array= - (Item **)thd->alloc(sizeof(Item*) * - (item_list.elements + - select_n_having_items + - order_group_num)* 5)) == 0; + (Item **)stmt->alloc(sizeof(Item*) * + (item_list.elements + + select_n_having_items + + order_group_num)* 5)) == 0; } @@ -1575,7 +1564,7 @@ void st_select_lex_unit::print(String *str) if (sl != first_select()) { str->append(" union ", 7); - if (union_option & UNION_ALL) + if (!union_distinct) str->append("all ", 4); } if (sl->braces) @@ -1616,8 +1605,9 @@ void st_select_lex::print_limit(THD *thd, String *str) if (!thd) thd= current_thd; - if (select_limit != thd->variables.select_limit || - select_limit != HA_POS_ERROR || + if ((select_limit != thd->variables.select_limit && + this == &thd->lex->select_lex) || + (select_limit != HA_POS_ERROR && this != &thd->lex->select_lex) || offset_limit != 0L) { str->append(" limit ", 7); @@ -1656,7 +1646,11 @@ void st_select_lex_unit::set_limit(SELECT_LEX *values, /* There are st_select_lex::add_table_to_list & - st_select_lex::set_lock_for_tables in sql_parse.cc + st_select_lex::set_lock_for_tables are in sql_parse.cc st_select_lex::print is in sql_select.h + + st_select_lex_unit::prepare, st_select_lex_unit::exec, + st_select_lex_unit::cleanup, st_select_lex_unit::reinit_exec_mechanism + are in sql_union.cc */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 43d9b013239..7a5a1c13f80 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -347,7 +347,7 @@ public: /* fake SELECT_LEX for union processing */ st_select_lex *fake_select_lex; - uint union_option; + st_select_lex *union_distinct; /* pointer to the last UNION DISTINCT */ void init_query(); bool create_total_list(THD *thd, st_lex *lex, TABLE_LIST **result); @@ -367,6 +367,8 @@ public: int prepare(THD *thd, select_result *result, ulong additional_options); int exec(); int cleanup(); + inline void unclean() { cleaned= 0; } + void reinit_exec_mechanism(); bool check_updateable(char *db, char *table); void print(String *str); @@ -409,6 +411,7 @@ public: SQL_LIST order_list; /* ORDER clause */ List<List_item> expr_list; List<List_item> when_list; /* WHEN clause (expression) */ + SQL_LIST *gorder_list; ha_rows select_limit, offset_limit; /* LIMIT clause parameters */ // Arrays of pointers to top elements of all_fields list Item **ref_pointer_array; @@ -553,13 +556,12 @@ typedef struct st_lex String *wild; sql_exchange *exchange; select_result *result; - Item *default_value; + Item *default_value, *on_update_value; LEX_STRING *comment, name_and_length; LEX_USER *grant_user; gptr yacc_yyss,yacc_yyvs; THD *thd; CHARSET_INFO *charset; - SQL_LIST *gorder_list; List<key_part_spec> col_list; List<key_part_spec> ref_list; @@ -573,7 +575,7 @@ typedef struct st_lex List<Item> *insert_list,field_list,value_list; List<List_item> many_values; List<set_var_base> var_list; - List<Item> param_list; + List<Item_param> param_list; SQL_LIST proc_list, auxilliary_table_list, save_list; TYPELIB *interval; create_field *last_field; @@ -598,7 +600,6 @@ 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 param_count; uint slave_thd_opt; uint8 describe; bool drop_if_exists, drop_temporary, local_file; diff --git a/sql/sql_list.h b/sql/sql_list.h index 8faa7109174..6dee38e9192 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -293,7 +293,7 @@ protected: inline T** ref(void) { return (T**) 0; } public: - List_iterator_fast(List<T> &a) : base_list_iterator(a) {} + inline List_iterator_fast(List<T> &a) : base_list_iterator(a) {} inline T* operator++(int) { return (T*) base_list_iterator::next_fast(); } inline void rewind(void) { base_list_iterator::rewind(); } void sublist(List<T> &list_arg, uint el_arg) @@ -364,6 +364,10 @@ class base_ilist first_link->unlink(); // Unlink from list return first_link; } + inline struct ilink *head() + { + return (first != &last) ? first : 0; + } friend class base_list_iterator; }; @@ -396,6 +400,7 @@ public: inline void append(T* a) { base_ilist::append(a); } inline void push_back(T* a) { base_ilist::push_back(a); } inline T* get() { return (T*) base_ilist::get(); } + inline T* head() { return (T*) base_ilist::head(); } #ifndef _lint friend class I_List_iterator<T>; #endif diff --git a/sql/sql_load.cc b/sql/sql_load.cc index f128601d281..377f41958c5 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -93,8 +93,12 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, LOAD_FILE_INFO lf_info; #endif char *db = table_list->db; // This is never null - /* If no current database, use database where table is located */ - char *tdb= thd->db ? thd->db : db; + /* + If path for file is not defined, we will use the current database. + If this is not set, we will use the directory where the table to be + loaded is located + */ + char *tdb= thd->db ? thd->db : db; // Result is never null bool transactional_table, log_delayed; ulong skip_lines= ex->skip_lines; DBUG_ENTER("mysql_load"); @@ -123,7 +127,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, else { // Part field list thd->dupp_field=0; - if (setup_tables(table_list, 0) || + if (setup_tables(table_list) || setup_fields(thd, 0, table_list, fields, 1, 0, 0)) DBUG_RETURN(-1); if (thd->dupp_field) @@ -237,7 +241,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, } #ifndef EMBEDDED_LIBRARY - if (!opt_old_rpl_compat && mysql_bin_log.is_open()) + if (mysql_bin_log.is_open()) { lf_info.thd = thd; lf_info.ex = ex; @@ -270,13 +274,12 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (!(error=test(read_info.error))) { - uint save_time_stamp=table->time_stamp; if (use_timestamp) - table->time_stamp=0; + table->timestamp_default_now= table->timestamp_on_update_now= 0; + table->next_number_field=table->found_next_number_field; VOID(table->file->extra_opt(HA_EXTRA_WRITE_CACHE, thd->variables.read_buff_size)); - table->bulk_insert= 1; if (handle_duplicates == DUP_IGNORE || handle_duplicates == DUP_REPLACE) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); @@ -293,7 +296,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (table->file->activate_all_index(thd)) error=1; /* purecov: inspected */ table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); - table->time_stamp=save_time_stamp; table->next_number_field=0; } if (file >= 0) @@ -313,7 +315,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (transactional_table) ha_autocommit_or_rollback(thd,error); #ifndef EMBEDDED_LIBRARY - if (!opt_old_rpl_compat && mysql_bin_log.is_open()) + if (mysql_bin_log.is_open()) { /* Make sure last block (the one which caused the error) gets logged. @@ -357,28 +359,16 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, #ifndef EMBEDDED_LIBRARY if (mysql_bin_log.is_open()) { - if (opt_old_rpl_compat) - { - if (!read_file_from_client) - { - Load_log_event qinfo(thd, ex, db, table->table_name, fields, - handle_duplicates, log_delayed); - mysql_bin_log.write(&qinfo); - } - } - else + /* + As already explained above, we need to call end_io_cache() or the last + block will be logged only after Execute_load_log_event (which is wrong), + when read_info is destroyed. + */ + read_info.end_io_cache(); + if (lf_info.wrote_create_file) { - /* - As already explained above, we need to call end_io_cache() or the last - block will be logged only after Execute_load_log_event (which is wrong), - when read_info is destroyed. - */ - read_info.end_io_cache(); - if (lf_info.wrote_create_file) - { - Execute_load_log_event e(thd, db, log_delayed); - mysql_bin_log.write(&e); - } + Execute_load_log_event e(thd, db, log_delayed); + mysql_bin_log.write(&e); } } #endif /*!EMBEDDED_LIBRARY*/ @@ -695,7 +685,7 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs, if (get_it_from_net) cache.read_function = _my_b_net_read; - if (!opt_old_rpl_compat && mysql_bin_log.is_open()) + if (mysql_bin_log.is_open()) cache.pre_read = cache.pre_close = (IO_CACHE_CALLBACK) log_loaded_block; #endif diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc index efc4cf0921d..46f1e6c156e 100644 --- a/sql/sql_olap.cc +++ b/sql/sql_olap.cc @@ -64,7 +64,7 @@ static int make_new_olap_select(LEX *lex, SELECT_LEX *select_lex, List<Item> new while ((item=list_it++)) { - bool not_found=true; + bool not_found= TRUE; if (item->type()==Item::FIELD_ITEM) { Item_field *iif = (Item_field *)item; @@ -152,7 +152,7 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex) List<Item> all_fields(select_lex->item_list); - if (setup_tables((TABLE_LIST *)select_lex->table_list.first, 0) || + if (setup_tables((TABLE_LIST *)select_lex->table_list.first) || setup_fields(lex->thd, 0, (TABLE_LIST *)select_lex->table_list.first, select_lex->item_list, 1, &all_fields,1) || setup_fields(lex->thd, 0, (TABLE_LIST *)select_lex->table_list.first, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 53405eb6fcd..5f52b8d6aac 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -60,16 +60,18 @@ extern "C" int gethostname(char *name, int namelen); #endif +#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_merge_table_access(THD *thd, char *db, TABLE_LIST *tables); -static bool single_table_command_access(THD *thd, ulong privilege, - TABLE_LIST *tables, int *res); static void remove_escape(char *name); static void refresh_status(void); -static bool append_file_to_dir(THD *thd, char **filename_ptr, - char *table_name); +static bool append_file_to_dir(THD *thd, const char **filename_ptr, + const char *table_name); +static int check_one_table_access(THD *thd, ulong privilege, + TABLE_LIST *tables, bool no_errors); const char *any_db="*any*"; // Special symbol for check_access @@ -113,6 +115,7 @@ static void unlock_locked_tables(THD *thd) } } + static bool end_active_trans(THD *thd) { int error=0; @@ -128,6 +131,17 @@ static bool end_active_trans(THD *thd) } +#ifdef HAVE_REPLICATION +inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables) +{ + return (table_rules_on && tables && !tables_ok(thd,tables) && + ((thd->lex->sql_command != SQLCOM_DELETE_MULTI) || + !tables_ok(thd, + (TABLE_LIST *)thd->lex->auxilliary_table_list.first))); +} +#endif + + static HASH hash_user_connections; static int get_or_create_user_conn(THD *thd, const char *user, @@ -218,7 +232,21 @@ int check_user(THD *thd, enum enum_server_command command, #ifdef NO_EMBEDDED_ACCESS_CHECKS thd->master_access= GLOBAL_ACLS; // Full rights - return 0; + /* Change database if necessary: OK or FAIL is sent in mysql_change_db */ + if (db && db[0]) + { + thd->db= 0; + thd->db_length= 0; + if (mysql_change_db(thd, db)) + { + if (thd->user_connect) + decrease_user_connections(thd->user_connect); + DBUG_RETURN(-1); + } + } + else + send_ok(thd); + DBUG_RETURN(0); #else my_bool opt_secure_auth_local; @@ -412,6 +440,8 @@ void init_max_user_conn(void) 1 error */ +#ifndef NO_EMBEDDED_ACCESS_CHECKS + static int check_for_max_user_connections(THD *thd, USER_CONN *uc) { int error=0; @@ -442,7 +472,7 @@ static int check_for_max_user_connections(THD *thd, USER_CONN *uc) (void) pthread_mutex_unlock(&LOCK_user_conn); DBUG_RETURN(error); } - +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ /* Decrease user connection count @@ -531,15 +561,15 @@ bool is_update_query(enum enum_sql_command command) static bool check_mqh(THD *thd, uint check_command) { +#ifdef NO_EMBEDDED_ACCESS_CHECKS + return(0); +#else 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); -#ifdef NO_EMBEDDED_ACCESS_CHECKS - DBUG_RETURN(0); -#else /* If more than a hour since last check, reset resource checking */ if (check_time - uc->intime >= 3600) { @@ -986,7 +1016,7 @@ pthread_handler_decl(handle_one_connection,arg) net->compress=1; // Use compression thd->version= refresh_version; - if (sys_init_connect.value && !(thd->master_access & SUPER_ACL)) + if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL)) { execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); if (thd->query_error) @@ -1161,9 +1191,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT))) DBUG_RETURN(1); - if (check_access(thd, SELECT_ACL, db, &table_list->grant.privilege,0,0)) - goto err; - if (grant_option && check_grant(thd, SELECT_ACL, table_list, 0, 0)) + if (check_one_table_access(thd, SELECT_ACL, table_list, 0)) goto err; thd->free_list = 0; thd->query_length=(uint) strlen(tbl_name); @@ -1183,9 +1211,18 @@ err: } - /* Execute one command from socket (query or simple command) */ - #ifndef EMBEDDED_LIBRARY + +/* + Read one command from socket and execute it (query or simple command). + This function is called in loop from thread function. + SYNOPSIS + do_command() + RETURN VALUE + 0 success + 1 request of thread shutdown (see dispatch_command() description) +*/ + bool do_command(THD *thd) { char *packet; @@ -1238,10 +1275,34 @@ bool do_command(THD *thd) command_name[command])); } net->read_timeout=old_timeout; // restore it + /* + packet_length contains length of data, as it was stored in packet + header. In case of malformed header, packet_length can be zero. + If packet_length is not zero, my_net_read ensures that this number + of bytes was actually read from network. Additionally my_net_read + sets packet[packet_length]= 0 (thus if packet_length == 0, + command == packet[0] == COM_SLEEP). + In dispatch_command packet[packet_length] points beyond the end of packet. + */ DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length)); } #endif /* EMBEDDED_LIBRARY */ +/* + Perform one connection-level (COM_XXXX) command. + SYNOPSIS + dispatch_command() + thd connection handle + command type of command to perform + packet data for the command, packet is always null-terminated + packet_length length of packet + 1 (to show that data is + null-terminated) except for COM_SLEEP, where it + can be zero. + RETURN VALUE + 0 ok + 1 request of thread shutdown, i. e. if command is + COM_QUIT/COM_SHUTDOWN +*/ bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length) @@ -1300,7 +1361,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, send_error(thd); // dump to NET break; } -#ifndef EMBEDDED_LIBRARY case COM_CHANGE_USER: { thd->change_user(); @@ -1319,13 +1379,14 @@ bool dispatch_command(enum enum_server_command command, THD *thd, uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? *passwd++ : strlen(passwd); db+= passwd_len + 1; +#ifndef EMBEDDED_LIBRARY /* Small check for incomming packet */ if ((uint) ((uchar*) db - net->read_pos) > packet_length) { send_error(thd, ER_UNKNOWN_COM_ERROR); break; } - +#endif /* Convert database name to utf8 */ db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1, system_charset_info, db, strlen(db), @@ -1350,7 +1411,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* Clear variables that are allocated */ thd->user_connect= 0; - int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, false); + int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE); if (res) { @@ -1376,10 +1437,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } break; } -#endif /* EMBEDDED_LIBRARY */ case COM_EXECUTE: { - mysql_stmt_execute(thd, packet); + mysql_stmt_execute(thd, packet, packet_length); break; } case COM_LONG_DATA: @@ -1488,9 +1548,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, my_casedn_str(files_charset_info, table_list.real_name); remove_escape(table_list.real_name); // This can't have wildcards - if (check_access(thd,SELECT_ACL,table_list.db,&thd->col_access,0,0)) + if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege, + 0, 0)) break; - table_list.grant.privilege=thd->col_access; if (grant_option && check_grant(thd,SELECT_ACL,&table_list,2,0)) break; mysqld_list_fields(thd,&table_list,fields); @@ -1507,10 +1567,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_CREATE_DB: // QQ: To be removed { + char *db=thd->strdup(packet), *alias; + statistic_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_status); - char *db=thd->strdup(packet); // null test to handle EOM - if (!db || !strip_sp(db) || check_db_name(db)) + if (!db || !(alias= thd->strdup(db)) || check_db_name(db)) { net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL"); break; @@ -1518,15 +1579,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (check_access(thd,CREATE_ACL,db,0,1,0)) break; mysql_log.write(thd,command,packet); - mysql_create_db(thd,db,0,0); + mysql_create_db(thd,(lower_case_table_names == 2 ? alias : db),0,0); break; } case COM_DROP_DB: // QQ: To be removed { statistic_increment(com_stat[SQLCOM_DROP_DB],&LOCK_status); - char *db=thd->strdup(packet); + char *db=thd->strdup(packet), *alias; // null test to handle EOM - if (!db || !strip_sp(db) || check_db_name(db)) + if (!db || !(alias= thd->strdup(db)) || check_db_name(db)) { net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL"); break; @@ -1539,7 +1600,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } mysql_log.write(thd,command,db); - mysql_rm_db(thd,db,0,0); + mysql_rm_db(thd,alias,0,0); break; } #ifndef EMBEDDED_LIBRARY @@ -1810,7 +1871,7 @@ mysql_execute_command(THD *thd) 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 */ - if (table_rules_on && tables && !tables_ok(thd,tables)) + if (all_tables_not_ok(thd,tables)) { /* we warn the slave SQL thread */ my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); @@ -1831,7 +1892,6 @@ mysql_execute_command(THD *thd) } #endif /* !HAVE_REPLICATION */ if (&lex->select_lex != lex->all_selects_list && - lex->sql_command != SQLCOM_CREATE_TABLE && lex->unit.create_total_list(thd, lex, &tables)) DBUG_RETURN(0); @@ -1843,7 +1903,7 @@ mysql_execute_command(THD *thd) !(thd->slave_thread || (thd->master_access & SUPER_ACL)) && (uc_update_queries[lex->sql_command] > 0)) { - send_error(thd, ER_CANT_UPDATE_WITH_READLOCK); + net_printf(thd, ER_OPTION_PREVENTS_STATEMENT, "--read-only"); DBUG_RETURN(-1); } @@ -2038,9 +2098,9 @@ mysql_execute_command(THD *thd) { if (check_global_access(thd, SUPER_ACL)) goto error; - LOCK_ACTIVE_MI; + pthread_mutex_lock(&LOCK_active_mi); res = change_master(thd,active_mi); - UNLOCK_ACTIVE_MI; + pthread_mutex_unlock(&LOCK_active_mi); break; } case SQLCOM_SHOW_SLAVE_STAT: @@ -2048,9 +2108,9 @@ mysql_execute_command(THD *thd) /* Accept one of two privileges */ if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL)) goto error; - LOCK_ACTIVE_MI; + pthread_mutex_lock(&LOCK_active_mi); res = show_master_info(thd,active_mi); - UNLOCK_ACTIVE_MI; + pthread_mutex_unlock(&LOCK_active_mi); break; } case SQLCOM_SHOW_MASTER_STAT: @@ -2102,7 +2162,7 @@ mysql_execute_command(THD *thd) net_printf(thd,ER_WRONG_TABLE_NAME, tables->real_name); break; } - LOCK_ACTIVE_MI; + pthread_mutex_lock(&LOCK_active_mi); /* fetch_master_table will send the error to the client on failure. Give error if the table already exists. @@ -2112,7 +2172,7 @@ mysql_execute_command(THD *thd) { send_ok(thd); } - UNLOCK_ACTIVE_MI; + pthread_mutex_unlock(&LOCK_active_mi); break; } #endif /* HAVE_REPLICATION */ @@ -2121,24 +2181,29 @@ mysql_execute_command(THD *thd) { /* Skip first table, which is the table we are creating */ TABLE_LIST *create_table= tables; + TABLE_LIST *create_table_local= + (TABLE_LIST*)lex->select_lex.table_list.first; + // exclude from global table list tables= tables->next; - lex->select_lex.table_list.first= (byte*) (tables); + // and from local list if it is not the same + if (&lex->select_lex != lex->all_selects_list) + lex->select_lex.table_list.first= (gptr)create_table_local->next; + else + lex->select_lex.table_list.first= (gptr)tables; create_table->next= 0; - if (&lex->select_lex != lex->all_selects_list && - lex->unit.create_total_list(thd, lex, &tables)) - DBUG_RETURN(-1); ulong want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ? CREATE_TMP_ACL : CREATE_ACL); + lex->create_info.alias= create_table->alias; if (check_access(thd, want_priv, create_table->db, &create_table->grant.privilege, 0, 0) || check_merge_table_access(thd, create_table->db, (TABLE_LIST *) lex->create_info.merge_list.first)) - goto error; /* purecov: inspected */ + goto create_eror; /* purecov: inspected */ if (grant_option && want_priv != CREATE_TMP_ACL && check_grant(thd, want_priv, create_table,0,0)) - goto error; + goto create_eror; #ifndef HAVE_READLINK lex->create_info.data_file_name=lex->create_info.index_file_name=0; #else @@ -2149,7 +2214,7 @@ mysql_execute_command(THD *thd) create_table->real_name)) { res=-1; - break; + goto unsent_create_error; } #endif /* @@ -2174,10 +2239,10 @@ mysql_execute_command(THD *thd) create_table->real_name)) { net_printf(thd,ER_UPDATE_TABLE_USED, create_table->real_name); - DBUG_RETURN(-1); + goto create_eror; } if (tables && check_table_access(thd, SELECT_ACL, tables,0)) - goto error; // Error message is given + goto create_eror; // Error message is given select_lex->options|= SELECT_NO_UNLOCK; unit->set_limit(select_lex, select_lex); @@ -2208,15 +2273,40 @@ mysql_execute_command(THD *thd) if (!res) send_ok(thd); } + // put tables back for PS rexecuting + create_table->next= tables; + tables= create_table; + if (&lex->select_lex != lex->all_selects_list) + { + /* + we do not touch local table 'next' field => we need just + put the table in the list + */ + lex->select_lex.table_list.first= (gptr) create_table_local; + } + else + lex->select_lex.table_list.first= (gptr) tables; + break; + +create_eror: + res= 1; //error reported +unsent_create_error: + // put tables back for PS rexecuting + create_table->next= tables; + tables= create_table; + if (&lex->select_lex != lex->all_selects_list) + { + /* + we do not touch local table 'next' field => we need just + put the table in the list + */ + lex->select_lex.table_list.first= (gptr) create_table_local; + } break; } case SQLCOM_CREATE_INDEX: - if (!tables->db) - tables->db=thd->db; - if (check_access(thd,INDEX_ACL,tables->db,&tables->grant.privilege,0,0)) + if (check_one_table_access(thd, INDEX_ACL, tables, 0)) goto error; /* purecov: inspected */ - if (grant_option && check_grant(thd,INDEX_ACL,tables,0,0)) - goto error; thd->slow_command=TRUE; if (end_active_trans(thd)) res= -1; @@ -2227,9 +2317,9 @@ mysql_execute_command(THD *thd) #ifdef HAVE_REPLICATION case SQLCOM_SLAVE_START: { - LOCK_ACTIVE_MI; + pthread_mutex_lock(&LOCK_active_mi); start_slave(thd,active_mi,1 /* net report*/); - UNLOCK_ACTIVE_MI; + pthread_mutex_unlock(&LOCK_active_mi); break; } case SQLCOM_SLAVE_STOP: @@ -2252,9 +2342,9 @@ mysql_execute_command(THD *thd) goto error; } { - LOCK_ACTIVE_MI; + pthread_mutex_lock(&LOCK_active_mi); stop_slave(thd,active_mi,1/* net report*/); - UNLOCK_ACTIVE_MI; + pthread_mutex_unlock(&LOCK_active_mi); break; } #endif /* HAVE_REPLICATION */ @@ -2272,8 +2362,6 @@ mysql_execute_command(THD *thd) res=0; break; } - if (!tables->db) - tables->db=thd->db; if (!select_lex->db) select_lex->db=tables->db; if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege,0,0) || @@ -2282,8 +2370,6 @@ mysql_execute_command(THD *thd) (TABLE_LIST *) lex->create_info.merge_list.first)) goto error; /* purecov: inspected */ - if (!tables->db) - tables->db=thd->db; if (grant_option) { if (check_grant(thd,ALTER_ACL,tables,0,0)) @@ -2295,7 +2381,7 @@ mysql_execute_command(THD *thd) tmp_table.real_name=lex->name; tmp_table.db=select_lex->db; tmp_table.grant.privilege=priv; - if (check_grant(thd,INSERT_ACL | CREATE_ACL,tables,0,0)) + if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0, 0)) goto error; } } @@ -2336,8 +2422,10 @@ mysql_execute_command(THD *thd) if (grant_option) { TABLE_LIST old_list,new_list; - bzero((char*) &old_list, sizeof(old_list)); - bzero((char*) &new_list, sizeof(new_list)); // Safety + /* + we do not need initialize old_list and new_list because we will + come table[0] and table->next[0] there + */ old_list=table[0]; new_list=table->next[0]; old_list.next=new_list.next=0; @@ -2476,16 +2564,15 @@ mysql_execute_command(THD *thd) break; } case SQLCOM_UPDATE: - if (check_db_used(thd,tables)) - goto error; - - if (single_table_command_access(thd, UPDATE_ACL, tables, &res)) - goto error; if (select_lex->item_list.elements != lex->value_list.elements) { send_error(thd,ER_WRONG_VALUE_COUNT); goto error; } + if (check_db_used(thd,tables)) + goto error; + if (check_one_table_access(thd, UPDATE_ACL, tables, 0)) + goto error; res= mysql_update(thd,tables, select_lex->item_list, lex->value_list, @@ -2498,36 +2585,48 @@ mysql_execute_command(THD *thd) res= -1; break; case SQLCOM_UPDATE_MULTI: - if (check_access(thd,UPDATE_ACL,tables->db,&tables->grant.privilege,0,0)) - goto error; - if (grant_option && check_grant(thd,UPDATE_ACL,tables,0,0)) - goto error; + { + const char *msg= 0; + TABLE_LIST *table; + if (select_lex->item_list.elements != lex->value_list.elements) { send_error(thd,ER_WRONG_VALUE_COUNT); 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) { - const char *msg= 0; - if (select_lex->order_list.elements) - msg= "ORDER BY"; - else if (select_lex->select_limit && select_lex->select_limit != - HA_POS_ERROR) - msg= "LIMIT"; - if (msg) - { - net_printf(thd, ER_WRONG_USAGE, "UPDATE", msg); - res= 1; - break; - } - res= mysql_multi_update(thd,tables, - &select_lex->item_list, - &lex->value_list, - select_lex->where, - select_lex->options, - lex->duplicates, unit, select_lex); + TABLE_LIST *save= table->next; + table->next= 0; + if (check_one_table_access(thd, UPDATE_ACL, table, 1) && + check_one_table_access(thd, SELECT_ACL, table, 0)) + goto error; + table->next= save; + } + + if (select_lex->order_list.elements) + msg= "ORDER BY"; + else if (select_lex->select_limit && select_lex->select_limit != + HA_POS_ERROR) + msg= "LIMIT"; + if (msg) + { + net_printf(thd, ER_WRONG_USAGE, "UPDATE", msg); + res= 1; + break; } + res= mysql_multi_update(thd,tables, + &select_lex->item_list, + &lex->value_list, + select_lex->where, + select_lex->options, + lex->duplicates, unit, select_lex); break; + } case SQLCOM_REPLACE: case SQLCOM_INSERT: { @@ -2535,7 +2634,7 @@ mysql_execute_command(THD *thd) ulong privilege= (lex->duplicates == DUP_REPLACE ? INSERT_ACL | DELETE_ACL : INSERT_ACL | update); - if (single_table_command_access(thd, privilege, tables, &res)) + if (check_one_table_access(thd, privilege, tables, 0)) goto error; if (select_lex->item_list.elements != lex->value_list.elements) { @@ -2559,15 +2658,7 @@ mysql_execute_command(THD *thd) { ulong privilege= (lex->duplicates == DUP_REPLACE ? INSERT_ACL | DELETE_ACL : INSERT_ACL); - TABLE_LIST *save_next=tables->next; - tables->next=0; - if (check_access(thd, privilege, - tables->db,&tables->grant.privilege,0,0) || - (grant_option && check_grant(thd, privilege, tables,0,0))) - goto error; - - tables->next=save_next; - if ((res=check_table_access(thd, SELECT_ACL, save_next,0))) + if (check_one_table_access(thd, privilege, tables, 0)) goto error; } @@ -2605,9 +2696,7 @@ mysql_execute_command(THD *thd) break; } case SQLCOM_TRUNCATE: - if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege,0,0)) - goto error; /* purecov: inspected */ - if (grant_option && check_grant(thd,DELETE_ACL,tables,0,0)) + if (check_one_table_access(thd, DELETE_ACL, tables, 0)) goto error; /* Don't allow this within a transaction because we want to use @@ -2622,7 +2711,7 @@ mysql_execute_command(THD *thd) break; case SQLCOM_DELETE: { - if (single_table_command_access(thd, DELETE_ACL, tables, &res)) + if (check_one_table_access(thd, DELETE_ACL, tables, 0)) goto error; // Set privilege for the WHERE clause tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); @@ -2657,7 +2746,7 @@ mysql_execute_command(THD *thd) TABLE_LIST *walk; for (walk= (TABLE_LIST*) tables; walk; walk= walk->next) { - if (!strcmp(auxi->real_name, walk->alias) && + if (!my_strcasecmp(table_alias_charset, auxi->alias, walk->alias) && !strcmp(walk->db, auxi->db)) break; } @@ -2747,8 +2836,8 @@ mysql_execute_command(THD *thd) DROP / * 40005 TEMPORARY * / TABLE that come from parts of binlogs (likely if we use RESET SLAVE or CHANGE MASTER TO), while the temporary table has already been dropped. - To not generate such irrelevant "table does not exist errors", we - silently add IF EXISTS if TEMPORARY was used. + To not generate such irrelevant "table does not exist errors", + we silently add IF EXISTS if TEMPORARY was used. */ if (thd->slave_thread) lex->drop_if_exists= 1; @@ -2757,12 +2846,8 @@ mysql_execute_command(THD *thd) } break; case SQLCOM_DROP_INDEX: - if (!tables->db) - tables->db=thd->db; - if (check_access(thd,INDEX_ACL,tables->db,&tables->grant.privilege,0,0)) + if (check_one_table_access(thd, INDEX_ACL, tables, 0)) goto error; /* purecov: inspected */ - if (grant_option && check_grant(thd,INDEX_ACL,tables,0,0)) - goto error; if (end_active_trans(thd)) res= -1; else @@ -2871,16 +2956,11 @@ mysql_execute_command(THD *thd) #else { char *db=tables->db; - if (!*db) - { - send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */ - goto error; /* purecov: inspected */ - } remove_escape(db); // Fix escaped '_' remove_escape(tables->real_name); - if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,&thd->col_access,0,0)) + if (check_access(thd,SELECT_ACL | EXTRA_ACL,db, + &tables->grant.privilege, 0, 0)) goto error; /* purecov: inspected */ - tables->grant.privilege=thd->col_access; if (grant_option && check_grant(thd,SELECT_ACL,tables,2,0)) goto error; res= mysqld_show_fields(thd,tables, @@ -2896,18 +2976,11 @@ mysql_execute_command(THD *thd) #else { char *db=tables->db; - if (!db) - { - send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */ - goto error; /* purecov: inspected */ - } remove_escape(db); // Fix escaped '_' remove_escape(tables->real_name); - if (!tables->db) - tables->db=thd->db; - if (check_access(thd,SELECT_ACL,db,&thd->col_access,0,0)) - goto error; /* purecov: inspected */ - tables->grant.privilege=thd->col_access; + if (check_access(thd,SELECT_ACL | EXTRA_ACL,db, + &tables->grant.privilege, 0, 0)) + goto error; /* purecov: inspected */ if (grant_option && check_grant(thd,SELECT_ACL,tables,2,0)) goto error; res= mysqld_show_keys(thd,tables); @@ -2936,9 +3009,7 @@ mysql_execute_command(THD *thd) send_error(thd,ER_NOT_ALLOWED_COMMAND); goto error; } - if (check_access(thd,privilege,tables->db,&tables->grant.privilege,0, - 0) || - grant_option && check_grant(thd,privilege,tables,0,0)) + if (check_one_table_access(thd, privilege, tables, 0)) goto error; } res=mysql_load(thd, lex->exchange, tables, lex->field_list, @@ -2975,8 +3046,12 @@ mysql_execute_command(THD *thd) goto error; thd->in_lock_tables=1; thd->options|= OPTION_TABLE_LOCK; - if (!(res=open_and_lock_tables(thd,tables))) + if (!(res= open_and_lock_tables(thd, tables))) { +#ifdef HAVE_QUERY_CACHE + if (thd->variables.query_cache_wlock_invalidate) + query_cache.invalidate_locked_for_write(tables); +#endif /*HAVE_QUERY_CACHE*/ thd->locked_tables=thd->lock; thd->lock=0; send_ok(thd); @@ -2987,7 +3062,8 @@ mysql_execute_command(THD *thd) break; case SQLCOM_CREATE_DB: { - if (!strip_sp(lex->name) || check_db_name(lex->name)) + char *alias; + if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name)) { net_printf(thd,ER_WRONG_DB_NAME, lex->name); break; @@ -3010,12 +3086,14 @@ mysql_execute_command(THD *thd) #endif if (check_access(thd,CREATE_ACL,lex->name,0,1,0)) break; - res=mysql_create_db(thd,lex->name,&lex->create_info,0); + res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name), + &lex->create_info, 0); break; } case SQLCOM_DROP_DB: { - if (!strip_sp(lex->name) || check_db_name(lex->name)) + char *alias; + if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name)) { net_printf(thd, ER_WRONG_DB_NAME, lex->name); break; @@ -3043,7 +3121,7 @@ mysql_execute_command(THD *thd) send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION); goto error; } - res=mysql_rm_db(thd,lex->name,lex->drop_if_exists,0); + res=mysql_rm_db(thd,alias,lex->drop_if_exists,0); break; } case SQLCOM_ALTER_DB: @@ -3053,6 +3131,22 @@ mysql_execute_command(THD *thd) net_printf(thd, ER_WRONG_DB_NAME, lex->name); 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 + 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))) + { + my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); + break; + } +#endif if (check_access(thd,ALTER_ACL,lex->name,0,1,0)) break; if (thd->locked_tables || thd->active_transaction()) @@ -3070,7 +3164,7 @@ mysql_execute_command(THD *thd) net_printf(thd,ER_WRONG_DB_NAME, lex->name); break; } - if (check_access(thd,DROP_ACL,lex->name,0,1,0)) + if (check_access(thd,SELECT_ACL,lex->name,0,1,0)) break; if (thd->locked_tables || thd->active_transaction()) { @@ -3650,19 +3744,19 @@ error: tables belong to subselects. SYNOPSIS - single_table_command_access() - thd - Thread handler - privilege - asked privelage - tables - table list of command - res - pointer on result code variable + check_one_table_access() + thd Thread handler + privilege requested privelage + tables table list of command + no_errors Don't send error to client RETURN 0 - OK - 1 - access denied + 1 - access denied, error is sent to client */ -static bool single_table_command_access(THD *thd, ulong privilege, - TABLE_LIST *tables, int *res) +static int check_one_table_access(THD *thd, ulong privilege, + TABLE_LIST *tables, bool no_errors) { if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0)) @@ -3672,13 +3766,16 @@ static bool single_table_command_access(THD *thd, ulong privilege, TABLE_LIST *subselects_tables= tables->next; tables->next= 0; if (grant_option && check_grant(thd, privilege, tables, 0, 0)) + { + tables->next= subselects_tables; return 1; + } // check rights on tables of subselect (if exists) if (subselects_tables) { tables->next= subselects_tables; - if ((*res= check_table_access(thd, SELECT_ACL, subselects_tables,0))) + if ((check_table_access(thd, SELECT_ACL, subselects_tables,0))) return 1; } return 0; @@ -3711,7 +3808,10 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, DBUG_ENTER("check_access"); DBUG_PRINT("enter",("want_access: %lu master_access: %lu", want_access, thd->master_access)); - ulong db_access,dummy; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + ulong db_access; +#endif + ulong dummy; if (save_priv) *save_priv=0; else @@ -3903,6 +4003,10 @@ static bool check_db_used(THD *thd,TABLE_LIST *tables) #define used_stack(A,B) (long) (B - A) #endif +#ifndef DBUG_OFF +long max_stack_used; +#endif + #ifndef EMBEDDED_LIBRARY bool check_stack_overrun(THD *thd,char *buf __attribute__((unused))) { @@ -3915,6 +4019,9 @@ bool check_stack_overrun(THD *thd,char *buf __attribute__((unused))) thd->fatal_error(); return 1; } +#ifndef DBUG_OFF + max_stack_used= max(max_stack_used, stack_used); +#endif return 0; } #endif /* EMBEDDED_LIBRARY */ @@ -4008,7 +4115,9 @@ mysql_init_select(LEX *lex) { SELECT_LEX *select_lex= lex->current_select; select_lex->init_select(); - select_lex->select_limit= lex->thd->variables.select_limit; + select_lex->select_limit= (&lex->select_lex == select_lex) ? + lex->thd->variables.select_limit : /* Primry UNION */ + HA_POS_ERROR; /* subquery */ if (select_lex == &lex->select_lex) { lex->exchange= 0; @@ -4060,7 +4169,9 @@ mysql_new_select(LEX *lex, bool move_down) fake->select_number= INT_MAX; fake->make_empty_select(); fake->linkage= GLOBAL_OPTIONS_TYPE; - fake->select_limit= lex->thd->variables.select_limit; + fake->select_limit= (&lex->unit == unit) ? + lex->thd->variables.select_limit : /* Primry UNION */ + HA_POS_ERROR; /* subquery */ } } @@ -4114,8 +4225,11 @@ void mysql_init_multi_delete(LEX *lex) } -void -mysql_parse(THD *thd, char *inBuf, uint length) +/* + When you modify mysql_parse(), you may need to mofify + mysql_test_parse_for_slave() in this same file. +*/ +void mysql_parse(THD *thd, char *inBuf, uint length) { DBUG_ENTER("mysql_parse"); @@ -4174,6 +4288,33 @@ mysql_parse(THD *thd, char *inBuf, uint length) } +#ifdef HAVE_REPLICATION +/* + Usable by the replication SQL thread only: just parse a query to know if it + can be ignored because of replicate-*-table rules. + + RETURN VALUES + 0 cannot be ignored + 1 can be ignored +*/ + +bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) +{ + LEX *lex; + bool error= 0; + + mysql_init_query(thd); + lex= lex_start(thd, (uchar*) inBuf, length); + if (!yyparse((void*) thd) && ! thd->is_fatal_error && + all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) + error= 1; /* Ignore question */ + free_items(thd->free_list); /* Free strings used by items */ + lex_end(lex); + + return error; +} +#endif + /***************************************************************************** ** Store field definition for create ** Return 0 if ok @@ -4182,7 +4323,8 @@ mysql_parse(THD *thd, char *inBuf, uint length) bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, char *length, char *decimals, uint type_modifier, - Item *default_value, LEX_STRING *comment, + Item *default_value, Item *on_update_value, + LEX_STRING *comment, char *change, TYPELIB *interval, CHARSET_INFO *cs, uint uint_geom_type) { @@ -4214,7 +4356,16 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, if (default_value) { - if (type == FIELD_TYPE_TIMESTAMP) + /* + Default value should be literal => basic constants => + no need fix_fields() + + We allow only one function as part of default value - + NOW() as default for TIMESTAMP type. + */ + if (default_value->type() == Item::FUNC_ITEM && + !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC && + type == FIELD_TYPE_TIMESTAMP)) { net_printf(thd, ER_INVALID_DEFAULT, field_name); DBUG_RETURN(1); @@ -4235,6 +4386,13 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, DBUG_RETURN(1); } } + + if (on_update_value && type != FIELD_TYPE_TIMESTAMP) + { + net_printf(thd, ER_INVALID_ON_UPDATE, field_name); + DBUG_RETURN(1); + } + if (!(new_field=new create_field())) DBUG_RETURN(1); new_field->field=0; @@ -4398,6 +4556,34 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, new_field->length= min(new_field->length,14); /* purecov: inspected */ } new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG | NOT_NULL_FLAG; + if (default_value) + { + /* Grammar allows only NOW() value for ON UPDATE clause */ + if (default_value->type() == Item::FUNC_ITEM && + ((Item_func*)default_value)->functype() == Item_func::NOW_FUNC) + { + new_field->unireg_check= (on_update_value?Field::TIMESTAMP_DNUN_FIELD: + Field::TIMESTAMP_DN_FIELD); + /* + We don't need default value any longer moreover it is dangerous. + Everything handled by unireg_check further. + */ + new_field->def= 0; + } + else + new_field->unireg_check= (on_update_value?Field::TIMESTAMP_UN_FIELD: + Field::NONE); + } + else + { + /* + We are setting TIMESTAMP_OLD_FIELD here only temporary, we will + replace this value by TIMESTAMP_DNUN_FIELD or NONE later when + information about all TIMESTAMP fields in table will be availiable. + */ + new_field->unireg_check= on_update_value?Field::TIMESTAMP_UN_FIELD: + Field::TIMESTAMP_OLD_FIELD; + } break; case FIELD_TYPE_DATE: // Old date type if (protocol_version != PROTOCOL_VERSION-1) @@ -4567,11 +4753,10 @@ bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc) ORDER *order; Item **item_ptr; DBUG_ENTER("add_to_list"); - if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*)))) + if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)))) DBUG_RETURN(1); - item_ptr = (Item**) (order+1); - *item_ptr=item; - order->item= item_ptr; + order->item_ptr= item; + order->item= &order->item_ptr; order->asc = asc; order->free_me=0; order->used=0; @@ -4661,6 +4846,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX); ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES); ptr->derived= table->sel; + ptr->cacheable_table= 1; if (use_index_arg) ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg, sizeof(*use_index_arg)); @@ -4675,7 +4861,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, tables ; tables=tables->next) { - if (!strcmp(alias_str,tables->alias) && !strcmp(ptr->db, tables->db)) + if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) && + !strcmp(ptr->db, tables->db)) { net_printf(thd,ER_NONUNIQ_TABLE,alias_str); /* purecov: tested */ DBUG_RETURN(0); /* purecov: tested */ @@ -4815,9 +5002,9 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if (purge_time >= 0) mysql_bin_log.purge_logs_before_date(purge_time); } - LOCK_ACTIVE_MI; + pthread_mutex_lock(&LOCK_active_mi); rotate_relay_log(active_mi); - UNLOCK_ACTIVE_MI; + pthread_mutex_unlock(&LOCK_active_mi); #endif if (ha_flush_logs()) result=1; @@ -4878,10 +5065,10 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if (options & REFRESH_SLAVE) { tmp_write_to_binlog= 0; - LOCK_ACTIVE_MI; + pthread_mutex_lock(&LOCK_active_mi); if (reset_slave(thd, active_mi)) result=1; - UNLOCK_ACTIVE_MI; + pthread_mutex_unlock(&LOCK_active_mi); } #endif if (options & REFRESH_USER_RESOURCES) @@ -4964,7 +5151,8 @@ static void refresh_status(void) /* If pointer is not a null pointer, append filename to it */ -static bool append_file_to_dir(THD *thd, char **filename_ptr, char *table_name) +static bool append_file_to_dir(THD *thd, const char **filename_ptr, + const char *table_name) { char buff[FN_REFLEN],*ptr, *end; if (!*filename_ptr) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 43bf3e3651c..af1b85ae8a7 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -39,7 +39,7 @@ Prepare-execute: - Server gets the command 'COM_EXECUTE' to execute the previously prepared query. If there is any param markers; then client - will send the data in the following format: + will send the data in the following format: [COM_EXECUTE:1] [STMT_ID:4] [NULL_BITS:(param_count+7)/8)] @@ -87,16 +87,18 @@ class Prepared_statement: public Statement { public: THD *thd; - Item_param **param; /* array of all placeholders */ + Item_param **param_array; uint param_count; uint last_errno; char last_error[MYSQL_ERRMSG_SIZE]; - bool error_in_prepare, long_data_used; + bool get_longdata_error; + bool long_data_used; bool log_full_query; #ifndef EMBEDDED_LIBRARY - bool (*setup_params)(Prepared_statement *st, uchar *pos, uchar *read_pos); + bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end, + uchar *read_pos); #else - bool (*setup_params_data)(Prepared_statement *st); + bool (*set_params_data)(Prepared_statement *st); #endif public: Prepared_statement(THD *thd_arg); @@ -117,14 +119,7 @@ inline bool is_param_null(const uchar *pos, ulong param_no) enum { STMT_QUERY_LOG_LENGTH= 8192 }; -#ifdef EMBEDDED_LIBRARY -#define SETUP_PARAM_FUNCTION(fn_name) \ -static void fn_name(Item_param *param, uchar **pos, ulong data_len) -#else -#define SETUP_PARAM_FUNCTION(fn_name) \ -static void fn_name(Item_param *param, uchar **pos) -#endif - +enum enum_send_error { DONT_SEND_ERROR= 0, SEND_ERROR }; /* Seek prepared statement in statement map by id: returns zero if statement @@ -132,14 +127,16 @@ static void fn_name(Item_param *param, uchar **pos) */ static Prepared_statement * -find_prepared_statement(THD *thd, ulong id, const char *where) +find_prepared_statement(THD *thd, ulong id, const char *where, + enum enum_send_error se) { Statement *stmt= thd->stmt_map.find(id); if (stmt == 0 || stmt->type() != Statement::PREPARED_STATEMENT) { my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), id, where); - send_error(thd); + if (se == SEND_ERROR) + send_error(thd); return 0; } return (Prepared_statement *) stmt; @@ -155,12 +152,20 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) { NET *net= &stmt->thd->net; char buff[9]; - buff[0]= 0; + buff[0]= 0; /* OK packet indicator */ int4store(buff+1, stmt->id); int2store(buff+5, columns); int2store(buff+7, stmt->param_count); - /* This should be fixed to work with prepared statements */ - return (my_net_write(net, buff, sizeof(buff)) || net_flush(net)); + /* + 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; } #else static bool send_prep_stmt(Prepared_statement *stmt, @@ -178,104 +183,138 @@ static bool send_prep_stmt(Prepared_statement *stmt, /* - Read the length of the parameter data and retun back to - caller by positing the pointer to param data + Read the length of the parameter data and return back to + caller by positing the pointer to param data. */ #ifndef EMBEDDED_LIBRARY -static ulong get_param_length(uchar **packet) +static ulong get_param_length(uchar **packet, ulong len) { reg1 uchar *pos= *packet; + if (len < 1) + return 0; if (*pos < 251) { (*packet)++; return (ulong) *pos; } + if (len < 3) + return 0; if (*pos == 252) { (*packet)+=3; return (ulong) uint2korr(pos+1); } + if (len < 4) + return 0; if (*pos == 253) { (*packet)+=4; return (ulong) uint3korr(pos+1); } + if (len < 5) + return 0; (*packet)+=9; // Must be 254 when here + /* TODO: why uint4korr here? (should be uint8korr) */ return (ulong) uint4korr(pos+1); } #else -#define get_param_length(A) data_len +#define get_param_length(packet, len) len #endif /*!EMBEDDED_LIBRARY*/ /* - Setup param conversion routines - - setup_param_xx() - param Parameter Item - pos Input data buffer + Data conversion routines + SYNOPSIS + set_param_xx() + param parameter item + pos input data buffer + len length of data in the buffer - All these functions reads the data from pos and sets up that data - through 'param' and advances the buffer position to predifined - length position. + All these functions read the data from pos, convert it to requested type + and assign to param; pos is advanced to predefined length. Make a note that the NULL handling is examined at first execution (i.e. when input types altered) and for all subsequent executions we don't read any values for this. - RETURN VALUES - + RETURN VALUE + none */ -SETUP_PARAM_FUNCTION(setup_param_tiny) +void set_param_tiny(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 1) + return; +#endif param->set_int((longlong)(**pos)); *pos+= 1; } -SETUP_PARAM_FUNCTION(setup_param_short) +void set_param_short(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 2) + return; +#endif param->set_int((longlong)sint2korr(*pos)); *pos+= 2; } -SETUP_PARAM_FUNCTION(setup_param_int32) +void set_param_int32(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 4) + return; +#endif param->set_int((longlong)sint4korr(*pos)); *pos+= 4; } -SETUP_PARAM_FUNCTION(setup_param_int64) +void set_param_int64(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 8) + return; +#endif param->set_int((longlong)sint8korr(*pos)); *pos+= 8; } -SETUP_PARAM_FUNCTION(setup_param_float) +void set_param_float(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 4) + return; +#endif float data; float4get(data,*pos); param->set_double((double) data); *pos+= 4; } -SETUP_PARAM_FUNCTION(setup_param_double) +void set_param_double(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 8) + return; +#endif double data; float8get(data,*pos); param->set_double((double) data); *pos+= 8; } -SETUP_PARAM_FUNCTION(setup_param_time) +void set_param_time(Item_param *param, uchar **pos, ulong len) { ulong length; - if ((length= get_param_length(pos))) + if ((length= get_param_length(pos, len)) >= 8) { uchar *to= *pos; - TIME tm; + TIME tm; + /* TODO: why length is compared with 8 here? */ tm.second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0; tm.day= (ulong) sint4korr(to+1); @@ -291,11 +330,11 @@ SETUP_PARAM_FUNCTION(setup_param_time) *pos+= length; } -SETUP_PARAM_FUNCTION(setup_param_datetime) +void set_param_datetime(Item_param *param, uchar **pos, ulong len) { - uint length= get_param_length(pos); + uint length; - if (length) + if ((length= get_param_length(pos, len)) >= 4) { uchar *to= *pos; TIME tm; @@ -321,11 +360,11 @@ SETUP_PARAM_FUNCTION(setup_param_datetime) *pos+= length; } -SETUP_PARAM_FUNCTION(setup_param_date) +void set_param_date(Item_param *param, uchar **pos, ulong len) { ulong length; - if ((length= get_param_length(pos))) + if ((length= get_param_length(pos, len)) >= 4) { uchar *to= *pos; TIME tm; @@ -343,55 +382,55 @@ SETUP_PARAM_FUNCTION(setup_param_date) *pos+= length; } -SETUP_PARAM_FUNCTION(setup_param_str) +void set_param_str(Item_param *param, uchar **pos, ulong len) { - ulong len= get_param_length(pos); - param->set_value((const char *)*pos, len); - *pos+= len; + ulong length= get_param_length(pos, len); + param->set_value((const char *)*pos, length); + *pos+= length; } -void setup_param_functions(Item_param *param, uchar param_type) +static void setup_one_conversion_function(Item_param *param, uchar param_type) { switch (param_type) { case FIELD_TYPE_TINY: - param->setup_param_func= setup_param_tiny; + param->set_param_func= set_param_tiny; param->item_result_type= INT_RESULT; break; case FIELD_TYPE_SHORT: - param->setup_param_func= setup_param_short; + param->set_param_func= set_param_short; param->item_result_type= INT_RESULT; break; case FIELD_TYPE_LONG: - param->setup_param_func= setup_param_int32; + param->set_param_func= set_param_int32; param->item_result_type= INT_RESULT; break; case FIELD_TYPE_LONGLONG: - param->setup_param_func= setup_param_int64; + param->set_param_func= set_param_int64; param->item_result_type= INT_RESULT; break; case FIELD_TYPE_FLOAT: - param->setup_param_func= setup_param_float; + param->set_param_func= set_param_float; param->item_result_type= REAL_RESULT; break; case FIELD_TYPE_DOUBLE: - param->setup_param_func= setup_param_double; + param->set_param_func= set_param_double; param->item_result_type= REAL_RESULT; break; case FIELD_TYPE_TIME: - param->setup_param_func= setup_param_time; + param->set_param_func= set_param_time; param->item_result_type= STRING_RESULT; break; case FIELD_TYPE_DATE: - param->setup_param_func= setup_param_date; + param->set_param_func= set_param_date; param->item_result_type= STRING_RESULT; break; case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP: - param->setup_param_func= setup_param_datetime; + param->set_param_func= set_param_datetime; param->item_result_type= STRING_RESULT; break; default: - param->setup_param_func= setup_param_str; + param->set_param_func= set_param_str; param->item_result_type= STRING_RESULT; } } @@ -402,14 +441,14 @@ void setup_param_functions(Item_param *param, uchar param_type) and if binary/update log is set, generate the valid query. */ -static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, - uchar *read_pos) +static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array, + uchar *read_pos, uchar *data_end) { - THD *thd= stmt->thd; - List<Item> ¶ms= stmt->lex->param_list; - List_iterator<Item> param_iterator(params); - Item_param *param; - + THD *thd= stmt->thd; + Item_param **begin= stmt->param_array; + Item_param **end= begin + stmt->param_count; + uint32 length= 0; + String str, query; const String *res; @@ -418,16 +457,14 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, if (query.copy(stmt->query, stmt->query_length, default_charset_info)) DBUG_RETURN(1); - ulong param_no= 0; - uint32 length= 0; - - while ((param= (Item_param *)param_iterator++)) + for (Item_param **it= begin; it < end; ++it) { + Item_param *param= *it; if (param->long_data_supplied) - res= param->query_val_str(&str); + res= param->query_val_str(&str); else { - if (is_param_null(pos,param_no)) + if (is_param_null(null_array, it - begin)) { param->maybe_null= param->null_value= 1; res= &my_null_string; @@ -435,7 +472,9 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, else { param->maybe_null= param->null_value= 0; - param->setup_param_func(param,&read_pos); + if (read_pos >= data_end) + DBUG_RETURN(1); + param->set_param_func(param, &read_pos, data_end - read_pos); res= param->query_val_str(&str); } } @@ -443,7 +482,6 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, DBUG_RETURN(1); length+= res->length()-1; - param_no++; } if (alloc_query(thd, (char *)query.ptr(), query.length()+1)) DBUG_RETURN(1); @@ -452,76 +490,76 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, } -static bool insert_params(Prepared_statement *stmt, uchar *pos, - uchar *read_pos) +static bool insert_params(Prepared_statement *stmt, uchar *null_array, + uchar *read_pos, uchar *data_end) { - List<Item> ¶ms= stmt->lex->param_list; - List_iterator<Item> param_iterator(params); - Item_param *param; - ulong param_no= 0; + Item_param **begin= stmt->param_array; + Item_param **end= begin + stmt->param_count; DBUG_ENTER("insert_params"); - while ((param= (Item_param *)param_iterator++)) + for (Item_param **it= begin; it < end; ++it) { - if (!param->long_data_supplied) + Item_param *param= *it; + if (!param->long_data_supplied) { - if (is_param_null(pos,param_no)) + if (is_param_null(null_array, it - begin)) param->maybe_null= param->null_value= 1; else { param->maybe_null= param->null_value= 0; - param->setup_param_func(param,&read_pos); + if (read_pos >= data_end) + DBUG_RETURN(1); + param->set_param_func(param, &read_pos, data_end - read_pos); } } - param_no++; } DBUG_RETURN(0); } -static bool setup_params_data(Prepared_statement *stmt) -{ - List<Item> ¶ms= stmt->lex->param_list; - List_iterator<Item> param_iterator(params); - Item_param *param; - uchar *pos= (uchar*) stmt->thd->net.read_pos + 1 + - MYSQL_STMT_HEADER; //skip header - uchar *read_pos= pos+(stmt->param_count+7) / 8; //skip null bits +static bool setup_conversion_functions(Prepared_statement *stmt, + uchar **data, uchar *data_end) +{ + /* skip null bits */ + uchar *read_pos= *data + (stmt->param_count+7) / 8; - DBUG_ENTER("setup_params_data"); + DBUG_ENTER("setup_conversion_functions"); if (*read_pos++) //types supplied / first execute - { + { /* First execute or types altered by the client, setup the conversion routines for all parameters (one time) */ - while ((param= (Item_param *)param_iterator++)) - { - setup_param_functions(param,*read_pos); + Item_param **it= stmt->param_array; + Item_param **end= it + stmt->param_count; + for (; it < end; ++it) + { + if (read_pos >= data_end) + DBUG_RETURN(1); + setup_one_conversion_function(*it, *read_pos); read_pos+= 2; } - param_iterator.rewind(); - } - stmt->setup_params(stmt,pos,read_pos); + } + *data= read_pos; DBUG_RETURN(0); } #else -bool setup_params_data(Prepared_statement *stmt) -{ - List<Item> ¶ms= stmt->lex->param_list; - List_iterator<Item> param_iterator(params); - Item_param *param; +static bool emb_insert_params(Prepared_statement *stmt) +{ + Item_param **it= stmt->param_array; + Item_param **end= it + stmt->param_count; MYSQL_BIND *client_param= stmt->thd->client_params; - DBUG_ENTER("setup_params_data"); + DBUG_ENTER("emb_insert_params"); - for (;(param= (Item_param *)param_iterator++); client_param++) - { - setup_param_functions(param, client_param->buffer_type); + for (; it < end; ++it, ++client_param) + { + Item_param *param= *it; + setup_one_conversion_function(param, client_param->buffer_type); if (!param->long_data_supplied) { if (*client_param->is_null) @@ -530,39 +568,39 @@ bool setup_params_data(Prepared_statement *stmt) { uchar *buff= (uchar*)client_param->buffer; param->maybe_null= param->null_value= 0; - param->setup_param_func(param,&buff, - client_param->length ? - *client_param->length : - client_param->buffer_length); + param->set_param_func(param, &buff, + client_param->length ? + *client_param->length : + client_param->buffer_length); } } } DBUG_RETURN(0); } -bool setup_params_data_withlog(Prepared_statement *stmt) -{ + +static bool emb_insert_params_withlog(Prepared_statement *stmt) +{ THD *thd= stmt->thd; - List<Item> ¶ms= stmt->lex->param_list; - List_iterator<Item> param_iterator(params); - Item_param *param; + Item_param **it= stmt->param_array; + Item_param **end= it + stmt->param_count; MYSQL_BIND *client_param= thd->client_params; String str, query; const String *res; + uint32 length= 0; - DBUG_ENTER("setup_params_data_withlog"); + DBUG_ENTER("emb_insert_params_withlog"); if (query.copy(stmt->query, stmt->query_length, default_charset_info)) DBUG_RETURN(1); - uint32 length= 0; - - for (;(param= (Item_param *)param_iterator++); client_param++) - { - setup_param_functions(param, client_param->buffer_type); + for (; it < end; ++it, ++client_param) + { + Item_param *param= *it; + setup_one_conversion_function(param, client_param->buffer_type); if (param->long_data_supplied) - res= param->query_val_str(&str); + res= param->query_val_str(&str); else { if (*client_param->is_null) @@ -574,16 +612,15 @@ bool setup_params_data_withlog(Prepared_statement *stmt) { uchar *buff= (uchar*)client_param->buffer; param->maybe_null= param->null_value= 0; - param->setup_param_func(param,&buff, - client_param->length ? - *client_param->length : - client_param->buffer_length); + param->set_param_func(param, &buff, + client_param->length ? + *client_param->length : + client_param->buffer_length); res= param->query_val_str(&str); } } if (query.replace(param->pos_in_query+length, 1, *res)) DBUG_RETURN(1); - length+= res->length()-1; } @@ -596,15 +633,21 @@ bool setup_params_data_withlog(Prepared_statement *stmt) #endif /*!EMBEDDED_LIBRARY*/ /* - Validate the following information for INSERT statement: - - field existance - - fields count + Validate the following information for INSERT statement: + - field existence + - fields count + SYNOPSIS + mysql_test_insert_fields() + RETURN VALUE + 0 ok + 1 error, sent to the client + -1 error, not sent to client */ -static bool mysql_test_insert_fields(Prepared_statement *stmt, - TABLE_LIST *table_list, - List<Item> &fields, - List<List_item> &values_list) +static int mysql_test_insert_fields(Prepared_statement *stmt, + TABLE_LIST *table_list, + List<Item> &fields, + List<List_item> &values_list) { THD *thd= stmt->thd; TABLE *table; @@ -622,8 +665,18 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, (grant_option && check_grant(thd,privilege,table_list,0,0))) DBUG_RETURN(1); #endif + + /* + open temporary memory pool for temporary data allocated by derived + tables & preparation procedure + */ + thd->allocate_temporary_memory_pool_for_ps_preparing(); if (open_and_lock_tables(thd, table_list)) - DBUG_RETURN(1); + { + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(-1); + } + table= table_list->table; if ((values= its++)) @@ -632,7 +685,11 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, ulong counter= 0; if (check_insert_fields(thd,table,fields,*values,1)) - DBUG_RETURN(1); + { + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(-1); + } + thd->free_temporary_memory_pool_for_ps_preparing(); value_count= values->elements; its.rewind(); @@ -645,29 +702,34 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW, ER(ER_WRONG_VALUE_COUNT_ON_ROW), MYF(0), counter); - DBUG_RETURN(1); + DBUG_RETURN(-1); } } } - if (send_prep_stmt(stmt, 0)) - DBUG_RETURN(1); + else + { + thd->free_temporary_memory_pool_for_ps_preparing(); + } DBUG_RETURN(0); } /* - Validate the following information - UPDATE - set and where clause DELETE - where clause - - And send update-set clause column list fields info - back to client. For DELETE, just validate where clause - and return no fields information back to client. + Validate the following information: + UPDATE - set and where clause + DELETE - where clause + SYNOPSIS + mysql_test_upd_fields() + RETURN VALUE + 0 success + 1 error, sent to client + -1 error, not sent to client */ -static bool mysql_test_upd_fields(Prepared_statement *stmt, - TABLE_LIST *table_list, - List<Item> &fields, List<Item> &values, - COND *conds) +static int mysql_test_upd_fields(Prepared_statement *stmt, + TABLE_LIST *table_list, + List<Item> &fields, List<Item> &values, + COND *conds) { THD *thd= stmt->thd; @@ -678,48 +740,59 @@ static bool mysql_test_upd_fields(Prepared_statement *stmt, (grant_option && check_grant(thd,UPDATE_ACL,table_list,0,0))) DBUG_RETURN(1); #endif - if (open_and_lock_tables(thd, table_list)) - DBUG_RETURN(1); - if (setup_tables(table_list, 0) || - setup_fields(thd, 0, table_list, fields, 1, 0, 0) || - setup_conds(thd, table_list, &conds) || thd->net.report_error) - DBUG_RETURN(1); /* - Currently return only column list info only, and we are not - sending any info on where clause. + open temporary memory pool for temporary data allocated by derived + tables & preparation procedure */ - if (send_prep_stmt(stmt, 0)) - DBUG_RETURN(1); + thd->allocate_temporary_memory_pool_for_ps_preparing(); + + if (open_and_lock_tables(thd, table_list)) + goto err; + if (setup_tables(table_list) || + setup_fields(thd, 0, table_list, fields, 1, 0, 0) || + setup_conds(thd, table_list, &conds) || thd->net.report_error) + goto err; + + thd->free_temporary_memory_pool_for_ps_preparing(); + + /* TODO: here we should send types of placeholders to the client. */ DBUG_RETURN(0); +err: + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(-1); } /* - Validate the following information: - + Validate the following information: SELECT - column list - where clause - order clause - having clause - group by clause - if no column spec i.e. '*', then setup all fields - - And send column list fields info back to client. + In case of success, if this query is not EXPLAIN, send column list info + back to client. + SYNOPSIS + mysql_test_select_fields() + RETURN VALUE + 0 success + 1 error, sent to client + -1 error, not sent to client */ -static bool mysql_test_select_fields(Prepared_statement *stmt, - TABLE_LIST *tables, - uint wild_num, - List<Item> &fields, COND *conds, - uint og_num, ORDER *order, ORDER *group, - Item *having, ORDER *proc, - ulong select_options, - SELECT_LEX_UNIT *unit, - SELECT_LEX *select_lex) +static int mysql_test_select_fields(Prepared_statement *stmt, + TABLE_LIST *tables, + uint wild_num, + List<Item> &fields, COND *conds, + uint og_num, ORDER *order, ORDER *group, + Item *having, ORDER *proc, + ulong select_options, + SELECT_LEX_UNIT *unit, + SELECT_LEX *select_lex) { THD *thd= stmt->thd; LEX *lex= stmt->lex; - select_result *result= lex->result; DBUG_ENTER("mysql_test_select_fields"); @@ -730,142 +803,166 @@ static bool mysql_test_select_fields(Prepared_statement *stmt, if (check_table_access(thd, privilege, tables,0)) DBUG_RETURN(1); } - else if (check_access(thd, privilege, "*any*",0,0,0)) + else if (check_access(thd, privilege, any_db,0,0,0)) DBUG_RETURN(1); #endif if ((&lex->select_lex != lex->all_selects_list && lex->unit.create_total_list(thd, lex, &tables))) DBUG_RETURN(1); - + + /* + open temporary memory pool for temporary data allocated by derived + tables & preparation procedure + */ + thd->allocate_temporary_memory_pool_for_ps_preparing(); if (open_and_lock_tables(thd, tables)) - DBUG_RETURN(1); + { + send_error(thd); + goto err; + } if (lex->describe) { if (send_prep_stmt(stmt, 0)) - DBUG_RETURN(1); + goto err; } else { + select_result *result= lex->result; if (!result && !(result= new select_send())) { send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_RETURN(1); + goto err; } - JOIN *join= new JOIN(thd, fields, select_options, result); - thd->used_tables= 0; // Updated by setup_fields + thd->used_tables= 0; // Updated by setup_fields + + if (unit->prepare(thd, result, 0)) + { + send_error(thd); + goto err_prep; + } - if (join->prepare(&select_lex->ref_pointer_array, - (TABLE_LIST*)select_lex->get_table_list(), - wild_num, conds, og_num, order, group, having, proc, - select_lex, unit)) - DBUG_RETURN(1); if (send_prep_stmt(stmt, fields.elements) || thd->protocol_simple.send_fields(&fields, 0) #ifndef EMBEDDED_LIBRARY - || net_flush(&thd->net) + || net_flush(&thd->net) #endif ) - DBUG_RETURN(1); - join->cleanup(); + goto err_prep; + + unit->cleanup(); } - DBUG_RETURN(0); + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(0); + +err_prep: + unit->cleanup(); +err: + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(1); } /* - Send the prepare query results back to client + Send the prepare query results back to client + SYNOPSIS + send_prepare_results() + stmt prepared statement + RETURN VALUE + 0 success + 1 error, sent to client */ -static bool send_prepare_results(Prepared_statement *stmt) +static int send_prepare_results(Prepared_statement *stmt) { THD *thd= stmt->thd; LEX *lex= stmt->lex; + SELECT_LEX *select_lex= &lex->select_lex; + TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first; enum enum_sql_command sql_command= lex->sql_command; + int res; DBUG_ENTER("send_prepare_results"); DBUG_PRINT("enter",("command: %d, param_count: %ld", - sql_command, lex->param_count)); - - /* Setup prepared stmt */ - stmt->param_count= lex->param_count; - - SELECT_LEX *select_lex= &lex->select_lex; - TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first; + sql_command, stmt->param_count)); switch (sql_command) { case SQLCOM_INSERT: - if (mysql_test_insert_fields(stmt, tables, lex->field_list, - lex->many_values)) - goto abort; + if ((res= mysql_test_insert_fields(stmt, tables, lex->field_list, + lex->many_values))) + goto error; break; case SQLCOM_UPDATE: - if (mysql_test_upd_fields(stmt, tables, select_lex->item_list, - lex->value_list, select_lex->where)) - goto abort; - break; - + /* XXX: fallthrough */ case SQLCOM_DELETE: - if (mysql_test_upd_fields(stmt, tables, select_lex->item_list, - lex->value_list, select_lex->where)) - goto abort; + if ((res= mysql_test_upd_fields(stmt, tables, select_lex->item_list, + lex->value_list, select_lex->where))) + goto error; break; case SQLCOM_SELECT: - if (mysql_test_select_fields(stmt, tables, select_lex->with_wild, - select_lex->item_list, - select_lex->where, - select_lex->order_list.elements + - select_lex->group_list.elements, - (ORDER*) select_lex->order_list.first, - (ORDER*) select_lex->group_list.first, - select_lex->having, - (ORDER*)lex->proc_list.first, - select_lex->options | thd->options, - &(lex->unit), select_lex)) - goto abort; - break; + if ((res= mysql_test_select_fields(stmt, tables, select_lex->with_wild, + select_lex->item_list, + select_lex->where, + select_lex->order_list.elements + + select_lex->group_list.elements, + (ORDER*) select_lex->order_list.first, + (ORDER*) select_lex->group_list.first, + select_lex->having, + (ORDER*)lex->proc_list.first, + select_lex->options | thd->options, + &(lex->unit), select_lex))) + goto error; + /* Statement and field info has already been sent */ + DBUG_RETURN(0); default: - { - /* - Rest fall through to default category, no parsing - for non-DML statements - */ - if (send_prep_stmt(stmt, 0)) - goto abort; - } + /* + Rest fall through to default category, no parsing + for non-DML statements + */ + break; } - DBUG_RETURN(0); + DBUG_RETURN(send_prep_stmt(stmt, 0)); -abort: - send_error(thd,thd->killed_errno()); +error: + if (res < 0) + send_error(thd,thd->killed_errno()); DBUG_RETURN(1); } /* - Initialize parameter items in statement + Initialize array of parametes in statement from LEX. + (We need to have quick access to items by number in mysql_send_longdata). + This is to avoid using malloc/realloc in the parser. */ -static bool init_param_items(Prepared_statement *stmt) +static bool init_param_array(Prepared_statement *stmt) { - Item_param **to; - - if (!stmt->param_count) - stmt->param= (Item_param **)0; - else - { - if (!(stmt->param= to= (Item_param **) - my_malloc(sizeof(Item_param *)*(stmt->param_count+1), - MYF(MY_WME)))) + LEX *lex= stmt->lex; + if ((stmt->param_count= lex->param_list.elements)) + { + Item_param **to; + 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, + sizeof(Item_param*) * stmt->param_count); + if (!stmt->param_array) + { + send_error(stmt->thd, ER_OUT_OF_RESOURCES); return 1; - - List_iterator<Item> param_iterator(stmt->lex->param_list); - while ((*(to++)= (Item_param *)param_iterator++)); - } + } + for (to= stmt->param_array; + to < stmt->param_array + stmt->param_count; + ++to) + { + *to= param_iterator++; + } + } return 0; } @@ -873,72 +970,71 @@ static bool init_param_items(Prepared_statement *stmt) /* Parse the query and send the total number of parameters and resultset metadata information back to client (if any), - without executing the query i.e. with out any log/disk + without executing the query i.e. without any log/disk writes. This will allow the queries to be re-executed - without re-parsing during execute. - - If parameter markers are found in the query, then store - the information using Item_param along with maintaining a - list in lex->param_list, so that a fast and direct - retrieval can be made without going through all field - items. + without re-parsing during execute. + + If parameter markers are found in the query, then store + the information using Item_param along with maintaining a + list in lex->param_array, so that a fast and direct + retrieval can be made without going through all field + items. */ -bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) +void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) { LEX *lex; Prepared_statement *stmt= new Prepared_statement(thd); - SELECT_LEX *sl; + int error; DBUG_ENTER("mysql_stmt_prepare"); + DBUG_PRINT("prep_query", ("%s", packet)); + if (stmt == 0) - DBUG_RETURN(0); + { + send_error(thd, ER_OUT_OF_RESOURCES); + DBUG_VOID_RETURN; + } if (thd->stmt_map.insert(stmt)) - goto insert_stmt_err; + { + delete stmt; + send_error(thd, ER_OUT_OF_RESOURCES); + DBUG_VOID_RETURN; + } thd->stmt_backup.set_statement(thd); + thd->stmt_backup.set_item_arena(thd); thd->set_statement(stmt); + thd->set_item_arena(stmt); if (alloc_query(thd, packet, packet_length)) - goto alloc_query_err; + { + stmt->set_statement(thd); + stmt->set_item_arena(thd); + thd->set_statement(&thd->stmt_backup); + thd->set_item_arena(&thd->stmt_backup); + /* Statement map deletes statement on erase */ + thd->stmt_map.erase(stmt); + send_error(thd, ER_OUT_OF_RESOURCES); + DBUG_VOID_RETURN; + } - mysql_log.write(thd, COM_PREPARE, "%s", packet); + mysql_log.write(thd, COM_PREPARE, "%s", packet); + thd->current_statement= stmt; lex= lex_start(thd, (uchar *) thd->query, thd->query_length); mysql_init_query(thd); lex->safe_to_cache_query= 0; - lex->param_count= 0; - if (yyparse((void *)thd) || thd->is_fatal_error || send_prepare_results(stmt)) - goto yyparse_err; - - lex_end(lex); + error= yyparse((void *)thd) || thd->is_fatal_error || + init_param_array(stmt) || + send_prepare_results(stmt); + /* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */ if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),WAIT_PRIOR); - - // save WHERE clause pointers to avoid damaging they by optimisation - for (sl= thd->lex->all_selects_list; - sl; - sl= sl->next_select_in_list()) - { - sl->prep_where= sl->where; - } - - cleanup_items(thd->free_list); - stmt->set_statement(thd); - thd->set_statement(&thd->stmt_backup); - - if (init_param_items(stmt)) - goto init_param_err; - - stmt->command= COM_EXECUTE; // set it only once here - - DBUG_RETURN(0); - -yyparse_err: - if (thd->lex->sphead) + if (error && thd->lex->sphead) { if (lex != thd->lex) thd->lex->sphead->restore_lex(thd); @@ -947,171 +1043,221 @@ yyparse_err: } lex_end(lex); stmt->set_statement(thd); + stmt->set_item_arena(thd); thd->set_statement(&thd->stmt_backup); -init_param_err: -alloc_query_err: - /* Statement map deletes statement on erase */ - thd->stmt_map.erase(stmt); - DBUG_RETURN(1); -insert_stmt_err: - delete stmt; - DBUG_RETURN(1); + thd->set_item_arena(&thd->stmt_backup); + thd->current_statement= 0; + + if (error) + { + /* Statement map deletes statement on erase */ + thd->stmt_map.erase(stmt); + /* error is sent inside yyparse/send_prepare_results */ + } + else + { + SELECT_LEX *sl= stmt->lex->all_selects_list; + /* + Save WHERE clause pointers, because they may be changed during query + optimisation. + */ + for (; sl; sl= sl->next_select_in_list()) + { + sl->prep_where= sl->where; + } + } + DBUG_VOID_RETURN; } +/* Reinit statement before execution */ -/* - Executes previously prepared query +static void reset_stmt_for_execute(Prepared_statement *stmt) +{ + THD *thd= stmt->thd; + SELECT_LEX *sl= stmt->lex->all_selects_list; - If there is any parameters (stmt->param_count), then replace - markers with the data supplied from client, and then - execute the query + for (; sl; sl= sl->next_select_in_list()) + { + /* + Copy WHERE clause pointers to avoid damaging they by optimisation + */ + if (sl->prep_where) + sl->where= sl->prep_where->copy_andor_structure(thd); + DBUG_ASSERT(sl->join == 0); + ORDER *order; + /* Fix GROUP list */ + for (order= (ORDER *)sl->group_list.first; order; order= order->next) + order->item= &order->item_ptr; + /* Fix ORDER list */ + for (order= (ORDER *)sl->order_list.first; order; order= order->next) + order->item= &order->item_ptr; + + /* + TODO: When the new table structure is ready, then have a status bit + to indicate the table is altered, and re-do the setup_* + and open the tables back. + */ + for (TABLE_LIST *tables= (TABLE_LIST*) sl->table_list.first; + tables; + tables= tables->next) + { + /* + Reset old pointers to TABLEs: they are not valid since the tables + were closed in the end of previous prepare or execute call. + */ + tables->table= 0; + tables->table_list= 0; + } + + { + SELECT_LEX_UNIT *unit= sl->master_unit(); + unit->unclean(); + unit->types.empty(); + /* for derived tables & PS (which can't be reset by Item_subquery) */ + unit->reinit_exec_mechanism(); + } + } +} + +/* + Executes previously prepared query. + If there is any parameters, then replace markers with the data supplied + from client, and then execute the query. + SYNOPSYS + mysql_stmt_execute() */ -void mysql_stmt_execute(THD *thd, char *packet) + +void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) { ulong stmt_id= uint4korr(packet); +#ifndef EMBEDDED_LIBRARY + uchar *packet_end= (uchar *) packet + packet_length - 1; +#endif Prepared_statement *stmt; - DBUG_ENTER("mysql_stmt_execute"); + + packet+= 9; /* stmt_id + 5 bytes of flags */ - if (!(stmt= find_prepared_statement(thd, stmt_id, "execute"))) + if (!(stmt= find_prepared_statement(thd, stmt_id, "execute", SEND_ERROR))) DBUG_VOID_RETURN; + DBUG_PRINT("exec_query:", ("%s", stmt->query)); + /* Check if we got an error when sending long data */ - if (stmt->error_in_prepare) + if (stmt->get_longdata_error) { send_error(thd, stmt->last_errno, stmt->last_error); DBUG_VOID_RETURN; } - stmt->query_id= thd->query_id; thd->stmt_backup.set_statement(thd); thd->set_statement(stmt); - thd->free_list= 0; - - /* - To make sure that all runtime data is stored in its own memory root and - does not interfere with data possibly present in thd->mem_root. - This root is cleaned up in the end of execution. - FIXME: to be replaced with more efficient approach, and verified why we - can not use thd->mem_root safely. - */ - init_sql_alloc(&thd->mem_root, - thd->variables.query_alloc_block_size, - thd->variables.query_prealloc_size); + reset_stmt_for_execute(stmt); - for (SELECT_LEX *sl= stmt->lex->all_selects_list; - sl; - sl= sl->next_select_in_list()) +#ifndef EMBEDDED_LIBRARY + if (stmt->param_count) { - /* - Copy WHERE clause pointers to avoid damaging they by optimisation - */ - if (sl->prep_where) - sl->where= sl->prep_where->copy_andor_structure(thd); - DBUG_ASSERT(sl->join == 0); - ORDER *order; - /* Fix GROUP list */ - for (order=(ORDER *)sl->group_list.first ; order ; order=order->next) - order->item= (Item **)(order+1); - /* Fix ORDER list */ - for (order=(ORDER *)sl->order_list.first ; order ; order=order->next) - order->item= (Item **)(order+1); + uchar *null_array= (uchar *) packet; + if (setup_conversion_functions(stmt, (uchar **) &packet, packet_end) || + stmt->set_params(stmt, null_array, (uchar *) packet, packet_end)) + goto set_params_data_err; } - +#else /* - TODO: When the new table structure is ready, then have a status bit - to indicate the table is altered, and re-do the setup_* - and open the tables back. + In embedded library we re-install conversion routines each time + we set params, and also we don't need to parse packet. + So we do it in one function. */ - for (TABLE_LIST *tables= (TABLE_LIST*) stmt->lex->select_lex.table_list.first; - tables; - tables= tables->next) - tables->table= 0; // safety - nasty init - -#ifndef EMBEDDED_LIBRARY - if (stmt->param_count && setup_params_data(stmt)) - DBUG_VOID_RETURN; -#else - if (stmt->param_count && (*stmt->setup_params_data)(stmt)) - DBUG_VOID_RETURN; + if (stmt->param_count && stmt->set_params_data(stmt)) + goto set_params_data_err; #endif if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(),QUERY_PRIOR); + my_pthread_setprio(pthread_self(),QUERY_PRIOR); /* TODO: Also, have checks on basic executions such as mysql_insert(), mysql_delete(), mysql_update() and mysql_select() to not to have re-check on setup_* and other things .. - */ - thd->protocol= &thd->protocol_prep; // Switch to binary protocol + */ + thd->protocol= &thd->protocol_prep; // Switch to binary protocol mysql_execute_command(thd); - thd->protocol= &thd->protocol_simple; // Use normal protocol + thd->protocol= &thd->protocol_simple; // Use normal protocol if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); - free_items(thd->free_list); cleanup_items(stmt->free_list); - free_root(&thd->mem_root, MYF(0)); + close_thread_tables(thd); // to close derived tables + thd->set_statement(&thd->stmt_backup); + DBUG_VOID_RETURN; + +set_params_data_err: thd->set_statement(&thd->stmt_backup); + my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute"); + send_error(thd); DBUG_VOID_RETURN; } /* - Reset a prepared statement - + Reset a prepared statement, in case there was an error in send_longdata. + Note: we don't send any reply to that command. SYNOPSIS mysql_stmt_reset() thd Thread handle - packet Packet with stmt handle + packet Packet with stmt id DESCRIPTION This function is useful when one gets an error after calling - mysql_stmt_getlongdata() and one wants to reset the handle + mysql_stmt_getlongdata() and wants to reset the handle so that one can call execute again. + See also bug #1664 */ void mysql_stmt_reset(THD *thd, char *packet) { + /* There is always space for 4 bytes in buffer */ ulong stmt_id= uint4korr(packet); Prepared_statement *stmt; DBUG_ENTER("mysql_stmt_reset"); - if (!(stmt= find_prepared_statement(thd, stmt_id, "reset"))) + if (!(stmt= find_prepared_statement(thd, stmt_id, "reset", DONT_SEND_ERROR))) DBUG_VOID_RETURN; - stmt->error_in_prepare= 0; - Item_param *item= *stmt->param, *end= item + stmt->param_count; + stmt->get_longdata_error= 0; /* Free long data if used */ if (stmt->long_data_used) { + Item_param **item= stmt->param_array; + Item_param **end= item + stmt->param_count; stmt->long_data_used= 0; for (; item < end ; item++) - item->reset(); + (**item).reset(); } DBUG_VOID_RETURN; } /* - Delete a prepared statement from memory + Delete a prepared statement from memory. + Note: we don't send any reply to that command. */ void mysql_stmt_free(THD *thd, char *packet) { + /* There is always space for 4 bytes in packet buffer */ ulong stmt_id= uint4korr(packet); Prepared_statement *stmt; DBUG_ENTER("mysql_stmt_free"); - if (!(stmt= find_prepared_statement(thd, stmt_id, "close"))) + if (!(stmt= find_prepared_statement(thd, stmt_id, "close", DONT_SEND_ERROR))) DBUG_VOID_RETURN; /* Statement map deletes statement on erase */ @@ -1121,7 +1267,7 @@ void mysql_stmt_free(THD *thd, char *packet) /* - Long data in pieces from client + Long data in pieces from client SYNOPSIS mysql_stmt_get_longdata() @@ -1157,14 +1303,15 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) ulong stmt_id= uint4korr(pos); uint param_number= uint2korr(pos+4); - if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata"))) + if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata", + DONT_SEND_ERROR))) DBUG_VOID_RETURN; #ifndef EMBEDDED_LIBRARY if (param_number >= stmt->param_count) { /* Error will be sent in execute call */ - stmt->error_in_prepare= 1; + stmt->get_longdata_error= 1; stmt->last_errno= ER_WRONG_ARGUMENTS; sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS), "get_longdata"); DBUG_VOID_RETURN; @@ -1172,7 +1319,7 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) pos+= MYSQL_LONG_DATA_HEADER; // Point to data #endif - Item_param *param= *(stmt->param+param_number); + Item_param *param= stmt->param_array[param_number]; #ifndef EMBEDDED_LIBRARY param->set_longdata(pos, packet_length-MYSQL_LONG_DATA_HEADER-1); #else @@ -1186,10 +1333,10 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) Prepared_statement::Prepared_statement(THD *thd_arg) :Statement(thd_arg), thd(thd_arg), - param(0), + param_array(0), param_count(0), last_errno(0), - error_in_prepare(0), + get_longdata_error(0), long_data_used(0), log_full_query(0) { @@ -1198,23 +1345,22 @@ Prepared_statement::Prepared_statement(THD *thd_arg) { log_full_query= 1; #ifndef EMBEDDED_LIBRARY - setup_params= insert_params_withlog; + set_params= insert_params_withlog; #else - setup_params_data= setup_params_data_withlog; + set_params_data= emb_insert_params_withlog; #endif } else #ifndef EMBEDDED_LIBRARY - setup_params= insert_params; // not fully qualified query + set_params= insert_params; #else - setup_params_data= ::setup_params_data; + set_params_data= emb_insert_params; #endif } Prepared_statement::~Prepared_statement() { - my_free((char *) param, MYF(MY_ALLOW_ZERO_PTR)); free_items(free_list); } @@ -1224,4 +1370,3 @@ Statement::Type Prepared_statement::type() const return PREPARED_STATEMENT; } - diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 29b4f2f4a01..a1676aed78d 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -24,6 +24,8 @@ static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error); +static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list); + /* Every second entry in the table_list is the original name and every second entry is the new name. @@ -46,6 +48,8 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) DBUG_RETURN(1); } + if (wait_if_global_read_lock(thd,0)) + DBUG_RETURN(1); VOID(pthread_mutex_lock(&LOCK_open)); if (lock_table_names(thd, table_list)) goto err; @@ -54,17 +58,10 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) if ((ren_table=rename_tables(thd,table_list,0))) { /* Rename didn't succeed; rename back the tables in reverse order */ - TABLE_LIST *prev=0,*table; - /* Reverse the table list */ + TABLE_LIST *table; - while (table_list) - { - TABLE_LIST *next=table_list->next; - table_list->next=prev; - prev=table_list; - table_list=next; - } - table_list=prev; + /* Reverse the table list */ + table_list= reverse_table_list(table_list); /* Find the last renamed table */ for (table=table_list ; @@ -73,6 +70,10 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) table=table->next->next; // Skip error table /* Revert to old names */ rename_tables(thd, table, 1); + + /* Revert the table list (for prepared statements) */ + table_list= reverse_table_list(table_list); + error= 1; } @@ -92,11 +93,37 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) err: pthread_mutex_unlock(&LOCK_open); + start_waiting_global_read_lock(thd); DBUG_RETURN(error); } /* + reverse table list + + SYNOPSIS + reverse_table_list() + table_list pointer to table _list + + RETURN + pointer to new (reversed) list +*/ +static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list) +{ + TABLE_LIST *prev= 0; + + while (table_list) + { + TABLE_LIST *next= table_list->next; + table_list->next= prev; + prev= table_list; + table_list= next; + } + return (prev); +} + + +/* Rename all tables in list; Return pointer to wrong entry if something goes wrong. Note that the table_list may be empty! */ @@ -111,19 +138,31 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) { db_type table_type; char name[FN_REFLEN]; - new_table=ren_table->next; + const char *new_alias, *old_alias; + new_table=ren_table->next; + if (lower_case_table_names == 2) + { + old_alias= ren_table->alias; + new_alias= new_table->alias; + } + else + { + old_alias= ren_table->real_name; + new_alias= new_table->real_name; + } sprintf(name,"%s/%s/%s%s",mysql_data_home, - new_table->db,new_table->real_name, - reg_ext); + new_table->db, new_alias, reg_ext); + unpack_filename(name, name); if (!access(name,F_OK)) { - my_error(ER_TABLE_EXISTS_ERROR,MYF(0),name); + my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_alias); DBUG_RETURN(ren_table); // This can't be skipped } sprintf(name,"%s/%s/%s%s",mysql_data_home, - ren_table->db,ren_table->real_name, + ren_table->db, old_alias, reg_ext); + unpack_filename(name, name); if ((table_type=get_table_type(name)) == DB_TYPE_UNKNOWN) { my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); @@ -131,8 +170,8 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) DBUG_RETURN(ren_table); } else if (mysql_rename_table(table_type, - ren_table->db, ren_table->real_name, - new_table->db, new_table->real_name)) + ren_table->db, old_alias, + new_table->db, new_alias)) { if (!skip_error) DBUG_RETURN(ren_table); diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 5ba2e3178ff..98bf4e86aba 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -760,8 +760,6 @@ err: int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) { int slave_errno= 0; - if (!thd) - thd = current_thd; int thread_mask; DBUG_ENTER("start_slave"); diff --git a/sql/sql_repl.h b/sql/sql_repl.h index 2ff38029b7b..c39ef90114d 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -12,7 +12,7 @@ typedef struct st_slave_info THD* thd; } SLAVE_INFO; -extern my_bool opt_show_slave_auth_info, opt_old_rpl_compat; +extern my_bool opt_show_slave_auth_info; extern char *master_host, *master_info_file; extern bool server_id_supplied; extern I_List<i_string> binlog_do_db, binlog_ignore_db; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8ee0601eb79..6004778781d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -71,8 +71,10 @@ static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables, uint select_options, const char *info, Item *having, Procedure *proc, SELECT_LEX_UNIT *unit); -static COND *optimize_cond(COND *conds,Item::cond_result *cond_value); -static COND *remove_eq_conds(COND *cond,Item::cond_result *cond_value); +static COND *optimize_cond(THD *thd, COND *conds, + Item::cond_result *cond_value); +static COND *remove_eq_conds(THD *thd, COND *cond, + Item::cond_result *cond_value); static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item); static bool open_tmp_table(TABLE *table); static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, @@ -292,6 +294,10 @@ JOIN::prepare(Item ***rref_pointer_array, { DBUG_ENTER("JOIN::prepare"); + // to prevent double initialization on EXPLAIN + if (optimized) + DBUG_RETURN(0); + conds= conds_init; order= order_init; group_list= group_init; @@ -304,7 +310,7 @@ JOIN::prepare(Item ***rref_pointer_array, /* Check that all tables, fields, conds and order are ok */ - if (setup_tables(tables_list, 0) || + if (setup_tables(tables_list) || setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) || select_lex->setup_ref_array(thd, og_num) || setup_fields(thd, (*rref_pointer_array), tables_list, fields_list, 1, @@ -321,8 +327,9 @@ JOIN::prepare(Item ***rref_pointer_array, thd->where="having clause"; thd->allow_sum_func=1; select_lex->having_fix_field= 1; - bool having_fix_rc= (having->fix_fields(thd, tables_list, &having) || - having->check_cols(1)); + bool having_fix_rc= (!having->fixed && + (having->fix_fields(thd, tables_list, &having) || + having->check_cols(1))); select_lex->having_fix_field= 0; if (having_fix_rc || thd->net.report_error) DBUG_RETURN(-1); /* purecov: inspected */ @@ -333,8 +340,7 @@ JOIN::prepare(Item ***rref_pointer_array, // Is it subselect { Item_subselect *subselect; - if ((subselect= select_lex->master_unit()->item) && - select_lex->linkage != GLOBAL_OPTIONS_TYPE) + if ((subselect= select_lex->master_unit()->item)) { Item_subselect::trans_res res; if ((res= subselect->select_transformer(this)) != @@ -525,7 +531,7 @@ JOIN::optimize() } #endif - conds= optimize_cond(conds,&cond_value); + conds= optimize_cond(thd, conds, &cond_value); if (thd->net.report_error) { error= 1; @@ -591,7 +597,10 @@ JOIN::optimize() DBUG_RETURN(1); // error == -1 } if (const_table_map != found_const_table_map && - !(select_options & SELECT_DESCRIBE)) + !(select_options & SELECT_DESCRIBE) && + (!conds || + !(conds->used_tables() & RAND_TABLE_BIT) || + select_lex->master_unit() == &thd->lex->unit)) // upper level SELECT { zero_result_cause= "no matching row in const table"; DBUG_PRINT("error",("Error: %s", zero_result_cause)); @@ -849,8 +858,7 @@ JOIN::optimize() for (uint i_h = const_tables; i_h < tables; i_h++) { TABLE* table_h = join_tab[i_h].table; - if (table_h->db_type == DB_TYPE_INNODB) - table_h->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); + table_h->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY); } } #endif @@ -1015,7 +1023,7 @@ JOIN::reinit() /* TODO move to unit reinit */ unit->set_limit(select_lex, select_lex); - if (setup_tables(tables_list, 1)) + if (setup_tables(tables_list)) DBUG_RETURN(1); /* Reset of sum functions */ @@ -1082,7 +1090,7 @@ JOIN::exec() if (!tables_list) { // Only test of functions if (select_options & SELECT_DESCRIBE) - select_describe(this, false, false, false, + select_describe(this, FALSE, FALSE, FALSE, (zero_result_cause?zero_result_cause:"No tables used")); else { @@ -1421,9 +1429,17 @@ JOIN::exec() if (!curr_table->select->cond) curr_table->select->cond= sort_table_cond; else // This should never happen + { if (!(curr_table->select->cond= - new Item_cond_and(curr_table->select->cond, sort_table_cond))) + new Item_cond_and(curr_table->select->cond, + sort_table_cond))) DBUG_VOID_RETURN; + /* + Item_cond_and do not need fix_fields for execution, its parameters + are fixed or do not need fix_fields, too + */ + curr_table->select->cond->quick_fix_field(); + } curr_table->select_cond= curr_table->select->cond; curr_table->select_cond->top_level_item(); DBUG_EXECUTE("where",print_where(curr_table->select->cond, @@ -1520,10 +1536,10 @@ JOIN::cleanup() lock=0; // It's faster to unlock later join_free(1); - if (exec_tmp_table1) - free_tmp_table(thd, exec_tmp_table1); - if (exec_tmp_table2) - free_tmp_table(thd, exec_tmp_table2); + if (exec_tmp_table1) + free_tmp_table(thd, exec_tmp_table1); + if (exec_tmp_table2) + free_tmp_table(thd, exec_tmp_table2); delete select; delete_dynamic(&keyuse); delete procedure; @@ -2145,8 +2161,8 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond, bool optimizable=0; for (uint i=0; i<num_values; i++) { - used_tables|=(*value)->used_tables(); - if (!((*value)->used_tables() & (field->table->map | RAND_TABLE_BIT))) + used_tables|=(value[i])->used_tables(); + if (!((value[i])->used_tables() & (field->table->map | RAND_TABLE_BIT))) optimizable=1; } if (!optimizable) @@ -2638,6 +2654,8 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, ha_rows rec; double tmp; THD *thd= join->thd; + if (thd->killed) // Abort + return; if (!rest_tables) { @@ -2646,7 +2664,8 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, read_time+=record_count/(double) TIME_FOR_COMPARE; if (join->sort_by_table && - join->sort_by_table != join->positions[join->const_tables].table->table) + join->sort_by_table != + join->positions[join->const_tables].table->table) read_time+=record_count; // We have to make a temp table if (read_time < join->best_read) { @@ -2818,7 +2837,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, will match */ if (table->quick_keys.is_set(key) && - table->quick_key_parts[key] <= max_key_part) + table->quick_key_parts[key] == max_key_part) tmp=records= (double) table->quick_rows[key]; else { @@ -2860,7 +2879,15 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, } records=(ulong) tmp; } - if (found_ref_or_null) + /* + If quick_select was used on a part of this key, we know + the maximum number of rows that the key can match. + */ + if (table->quick_keys.is_set(key) && + table->quick_key_parts[key] <= max_key_part && + records > (double) table->quick_rows[key]) + tmp= records= (double) table->quick_rows[key]; + else if (found_ref_or_null) { /* We need to do two key searches to find key */ tmp*= 2.0; @@ -3259,8 +3286,12 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, keyuse,join->const_table_map, &keyinfo->key_part[i], (char*) key_buff,maybe_null); - /* Remmeber if we are going to use REF_OR_NULL */ - if (keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL) + /* + Remeber if we are going to use REF_OR_NULL + But only if field _really_ can be null i.e. we force JT_REF + instead of JT_REF_OR_NULL in case if field can't be null + */ + if ((keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL) && maybe_null) null_ref_key= key_buff; key_buff+=keyinfo->key_part[i].store_length; } @@ -3336,9 +3367,15 @@ store_val_in_field(Field *field,Item *item) bool error; THD *thd=current_thd; ha_rows cuted_fields=thd->cuted_fields; + /* + we should restore old value of count_cuted_fields because + store_val_in_field can be called from mysql_insert + with select_insert, which make count_cuted_fields= 1 + */ + enum_check_fields old_count_cuted_fields= thd->count_cuted_fields; thd->count_cuted_fields= CHECK_FIELD_WARN; error= item->save_in_field(field, 1); - thd->count_cuted_fields= CHECK_FIELD_IGNORE; + thd->count_cuted_fields= old_count_cuted_fields; return error || cuted_fields != thd->cuted_fields; } @@ -3395,7 +3432,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) table_map used_tables; if (join->tables > 1) cond->update_used_tables(); // Tablenr may have changed - if (join->const_tables == join->tables) + if (join->const_tables == join->tables && + join->thd->lex->current_select->master_unit() == + &join->thd->lex->unit) // not upper level SELECT join->const_table_map|=RAND_TABLE_BIT; { // Check const tables COND *const_cond= @@ -3430,6 +3469,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) tab->type=JT_ALL; use_quick_range=1; tab->use_quick=1; + tab->ref.key= -1; tab->ref.key_parts=0; // Don't use ref key. join->best_positions[i].records_read= rows2double(tab->quick->records); } @@ -3497,7 +3537,9 @@ 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); + sel->cond= and_conds(sel->cond, tab->on_expr); + if (sel->cond && !sel->cond->fixed) + sel->cond->fix_fields(join->thd, 0, &sel->cond); if (sel->test_quick_select(join->thd, tab->keys, used_tables & ~ current_map, (join->select_options & @@ -3569,7 +3611,7 @@ static void make_join_readinfo(JOIN *join, uint options) { uint i; - SELECT_LEX *select_lex= &join->thd->lex->select_lex; + bool statistics= test(!(join->select_options & SELECT_DESCRIBE)); DBUG_ENTER("make_join_readinfo"); for (i=join->const_tables ; i < join->tables ; i++) @@ -3663,7 +3705,8 @@ make_join_readinfo(JOIN *join, uint options) { join->thd->server_status|=SERVER_QUERY_NO_GOOD_INDEX_USED; tab->read_first_record= join_init_quick_read_record; - statistic_increment(select_range_check_count, &LOCK_status); + if (statistics) + statistic_increment(select_range_check_count, &LOCK_status); } else { @@ -3672,24 +3715,28 @@ make_join_readinfo(JOIN *join, uint options) { if (tab->select && tab->select->quick) { - statistic_increment(select_range_count, &LOCK_status); + if (statistics) + statistic_increment(select_range_count, &LOCK_status); } else { join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED; - statistic_increment(select_scan_count, &LOCK_status); + if (statistics) + statistic_increment(select_scan_count, &LOCK_status); } } else { if (tab->select && tab->select->quick) { - statistic_increment(select_full_range_join_count, &LOCK_status); + if (statistics) + statistic_increment(select_full_range_join_count, &LOCK_status); } else { join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED; - statistic_increment(select_full_join_count, &LOCK_status); + if (statistics) + statistic_increment(select_full_join_count, &LOCK_status); } } if (!table->no_keyread) @@ -3833,9 +3880,7 @@ JOIN::join_free(bool full) else { for (tab= join_tab, end= tab+tables; tab != end; tab++) - { tab->cleanup(); - } table= 0; } } @@ -4067,7 +4112,7 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables, if (select_options & SELECT_DESCRIBE) { - select_describe(join, false, false, false, info); + select_describe(join, FALSE, FALSE, FALSE, info); DBUG_RETURN(0); } @@ -4311,6 +4356,7 @@ propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_father, SYNPOSIS eliminate_not_funcs() + thd thread handler cond condition tree DESCRIPTION @@ -4327,7 +4373,7 @@ propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_father, New condition tree */ -COND *eliminate_not_funcs(COND *cond) +COND *eliminate_not_funcs(THD *thd, COND *cond) { if (!cond) return cond; @@ -4337,7 +4383,7 @@ COND *eliminate_not_funcs(COND *cond) Item *item; while ((item= li++)) { - Item *new_item= eliminate_not_funcs(item); + Item *new_item= eliminate_not_funcs(thd, item); if (item != new_item) VOID(li.replace(new_item)); /* replace item with a new condition */ } @@ -4345,14 +4391,13 @@ COND *eliminate_not_funcs(COND *cond) else if (cond->type() == Item::FUNC_ITEM && /* 'NOT' operation? */ ((Item_func*) cond)->functype() == Item_func::NOT_FUNC) { - COND *new_cond= ((Item_func*) cond)->arguments()[0]->neg_transformer(); + COND *new_cond= ((Item_func*) cond)->arguments()[0]->neg_transformer(thd); if (new_cond) { /* Here we can delete the NOT function. Something like: delete cond; But we don't need to do it. All items will be deleted later at once. */ - new_cond->fix_fields(current_thd, 0, &new_cond); cond= new_cond; } } @@ -4361,7 +4406,7 @@ COND *eliminate_not_funcs(COND *cond) static COND * -optimize_cond(COND *conds,Item::cond_result *cond_value) +optimize_cond(THD *thd, COND *conds, Item::cond_result *cond_value) { DBUG_ENTER("optimize_cond"); if (!conds) @@ -4371,7 +4416,7 @@ optimize_cond(COND *conds,Item::cond_result *cond_value) } DBUG_EXECUTE("where",print_where(conds,"original");); /* eliminate NOT operators */ - conds= eliminate_not_funcs(conds); + conds= eliminate_not_funcs(thd, conds); DBUG_EXECUTE("where", print_where(conds, "after negation elimination");); /* change field = field to field = const for each found field = const */ propagate_cond_constants((I_List<COND_CMP> *) 0,conds,conds); @@ -4380,7 +4425,7 @@ optimize_cond(COND *conds,Item::cond_result *cond_value) Remove all and-levels where CONST item != CONST item */ DBUG_EXECUTE("where",print_where(conds,"after const change");); - conds=remove_eq_conds(conds,cond_value) ; + conds= remove_eq_conds(thd, conds, cond_value) ; DBUG_EXECUTE("info",print_where(conds,"after remove");); DBUG_RETURN(conds); } @@ -4395,7 +4440,7 @@ optimize_cond(COND *conds,Item::cond_result *cond_value) */ static COND * -remove_eq_conds(COND *cond,Item::cond_result *cond_value) +remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) { if (cond->type() == Item::COND_ITEM) { @@ -4409,7 +4454,7 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) Item *item; while ((item=li++)) { - Item *new_item=remove_eq_conds(item,&tmp_cond_value); + Item *new_item=remove_eq_conds(thd, item, &tmp_cond_value); if (!new_item) li.remove(); else if (item != new_item) @@ -4443,7 +4488,7 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) } } if (should_fix_fields) - cond->fix_fields(current_thd,0, &cond); + cond->update_used_tables(); if (!((Item_cond*) cond)->argument_list()->elements || *cond_value != Item::COND_OK) @@ -4470,7 +4515,6 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) Item_func_isnull *func=(Item_func_isnull*) cond; Item **args= func->arguments(); - THD *thd=current_thd; if (args[0]->type() == Item::FIELD_ITEM) { Field *field=((Item_field*) args[0])->field; @@ -4743,7 +4787,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, else return new Field_double(item_sum->max_length,maybe_null, item->name, table, item_sum->decimals); - case Item_sum::VARIANCE_FUNC: /* Place for sum & count */ + case Item_sum::VARIANCE_FUNC: /* Place for sum & count */ case Item_sum::STD_FUNC: if (group) return new Field_string(sizeof(double)*2+sizeof(longlong), @@ -4771,17 +4815,19 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, default: // This case should never be choosen DBUG_ASSERT(0); + thd->fatal_error(); return 0; } } - thd->fatal_error(); - return 0; // Error + /* We never come here */ } case Item::FIELD_ITEM: case Item::DEFAULT_VALUE_ITEM: - return create_tmp_field_from_field(thd, (*from_field= - ((Item_field*) item)->field), + { + Item_field *field= (Item_field*) item; + return create_tmp_field_from_field(thd, (*from_field= field->field), item, table, modify_item); + } case Item::FUNC_ITEM: case Item::COND_ITEM: case Item::FIELD_AVG_ITEM: @@ -5434,13 +5480,22 @@ free_tmp_table(THD *thd, TABLE *entry) save_proc_info=thd->proc_info; thd->proc_info="removing tmp table"; free_blobs(entry); - if (entry->db_stat && entry->file) + if (entry->file) { - (void) entry->file->close(); + if (entry->db_stat) + { + (void) entry->file->close(); + } + /* + We can't call ha_delete_table here as the table may created in mixed case + here and we have to ensure that delete_table gets the table name in + the original case. + */ + if (!(test_flags & TEST_KEEP_TMP_TABLES) || entry->db_type == DB_TYPE_HEAP) + entry->file->delete_table(entry->real_name); delete entry->file; } - if (!(test_flags & TEST_KEEP_TMP_TABLES) || entry->db_type == DB_TYPE_HEAP) - (void) ha_delete_table(entry->db_type,entry->real_name); + /* free blobs */ for (Field **ptr=entry->field ; *ptr ; ptr++) (*ptr)->free(); @@ -5743,7 +5798,15 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) return 0; } else + { + /* + This row failed selection, release lock on it. + XXX: There is no table handler in MySQL which makes use of this + call. It's kept from Gemini times. A lot of new code was added + recently (i. e. subselects) without having it in mind. + */ info->file->unlock_row(); + } } } while (!(error=info->read_record(info)) && !(*report_error)); } @@ -5905,8 +5968,8 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos) if (tab->on_expr && !table->null_row) { if ((table->null_row= test(tab->on_expr->val_int() == 0))) - empty_record(table); - } + mark_as_null_row(table); + } if (!table->null_row) table->maybe_null=0; DBUG_RETURN(0); @@ -6697,8 +6760,11 @@ static bool test_if_ref(Item_field *left_item,Item *right_item) /* We can remove binary fields and numerical fields except float, as float comparison isn't 100 % secure + We have to keep binary strings to be able to check for end spaces */ if (field->binary() && + field->real_type() != FIELD_TYPE_STRING && + field->real_type() != FIELD_TYPE_VAR_STRING && (field->type() != FIELD_TYPE_FLOAT || field->decimals() == 0)) { return !store_val_in_field(field,right_item); @@ -6711,7 +6777,7 @@ static bool test_if_ref(Item_field *left_item,Item *right_item) static COND * -make_cond_for_table(COND *cond,table_map tables,table_map used_table) +make_cond_for_table(COND *cond, table_map tables, table_map used_table) { if (used_table && !(cond->used_tables() & used_table)) return (COND*) 0; // Already checked @@ -6737,7 +6803,13 @@ make_cond_for_table(COND *cond,table_map tables,table_map used_table) case 1: return new_cond->argument_list()->head(); default: - new_cond->used_tables_cache=((Item_cond*) cond)->used_tables_cache & + /* + Item_cond_and do not need fix_fields for execution, its parameters + are fixed or do not need fix_fields, too + */ + new_cond->quick_fix_field(); + new_cond->used_tables_cache= + ((Item_cond_and*) cond)->used_tables_cache & tables; return new_cond; } @@ -6756,7 +6828,12 @@ make_cond_for_table(COND *cond,table_map tables,table_map used_table) return (COND*) 0; // Always true new_cond->argument_list()->push_back(fix); } - new_cond->used_tables_cache=((Item_cond_or*) cond)->used_tables_cache; + /* + Item_cond_and do not need fix_fields for execution, its parameters + are fixed or do not need fix_fields, too + */ + new_cond->quick_fix_field(); + new_cond->used_tables_cache= ((Item_cond_or*) cond)->used_tables_cache; new_cond->top_level_item(); return new_cond; } @@ -6985,7 +7062,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, ref_key= -1; /* Test if constant range in WHERE */ - if (tab->ref.key >= 0) + if (tab->ref.key >= 0 && tab->ref.key_parts) { ref_key= tab->ref.key; ref_key_parts= tab->ref.key_parts; @@ -7105,13 +7182,20 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, */ if (select_limit >= table->file->records) { - keys=*table->file->keys_to_use_for_scanning(); + keys= *table->file->keys_to_use_for_scanning(); keys.merge(table->used_keys); + + /* + We are adding here also the index speified in FORCE INDEX clause, + if any. + This is to allow users to use index in ORDER BY. + */ + if (table->force_index) + keys.merge(table->keys_in_use_for_query); + keys.intersect(usable_keys); } else - keys.set_all(); - - keys.intersect(usable_keys); + keys= usable_keys; for (nr=0; nr < table->keys ; nr++) { @@ -7273,8 +7357,10 @@ static bool fix_having(JOIN *join, Item **having) if (!table->select->cond) table->select->cond=sort_table_cond; else // This should never happen - if (!(table->select->cond=new Item_cond_and(table->select->cond, - sort_table_cond))) + if (!(table->select->cond= new Item_cond_and(table->select->cond, + sort_table_cond)) || + table->select->cond->fix_fields(join->thd, join->tables_list, + &table->select->cond)) return 1; table->select_cond=table->select->cond; table->select_cond->top_level_item(); @@ -7893,10 +7979,16 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, } order->in_field_list=0; Item *it= *order->item; - if (it->fix_fields(thd, tables, order->item) || - //'it' ressigned because fix_field can change it - (it= *order->item)->check_cols(1) || - thd->is_fatal_error) + /* + We check it->fixed because Item_func_group_concat can put + arguments for which fix_fields already was called. + + 'it' reassigned in if condition because fix_field can change it. + */ + if (!it->fixed && + (it->fix_fields(thd, tables, order->item) || + (it= *order->item)->check_cols(1) || + thd->is_fatal_error)) return 1; // Wrong field uint el= all_fields.elements; all_fields.push_front(it); // Add new field to field list @@ -7924,6 +8016,29 @@ int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, } +/* + Intitialize the GROUP BY list. + + SYNOPSIS + setup_group() + thd Thread handler + ref_pointer_array We store references to all fields that was not in + 'fields' here. + fields All fields in the select part. Any item in 'order' + that is part of these list is replaced by a pointer + to this fields. + all_fields Total list of all unique fields used by the select. + All items in 'order' that was not part of fields will + be added first to this list. + order The fields we should do GROUP BY on. + hidden_group_fields Pointer to flag that is set to 1 if we added any fields + to all_fields. + + RETURN + 0 ok + 1 error (probably out of memory) +*/ + int setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, List<Item> &fields, List<Item> &all_fields, ORDER *order, @@ -8303,12 +8418,11 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, Item *pos; List_iterator_fast<Item> li(all_fields); Copy_field *copy; - DBUG_ENTER("setup_copy_fields"); res_selected_fields.empty(); res_all_fields.empty(); List_iterator_fast<Item> itr(res_all_fields); - uint i, border= all_fields.elements - elements; + DBUG_ENTER("setup_copy_fields"); if (!(copy=param->copy_field= new Copy_field[param->field_count])) goto err2; @@ -8443,6 +8557,23 @@ bool JOIN::alloc_func_list() } +/* + Initialize 'sum_funcs' array with all Item_sum objects + + SYNOPSIS + make_sum_func_list() + field_list All items + send_fields Items in select list + before_group_by Set to 1 if this is called before GROUP BY handling + + NOTES + Calls ::setup() for all item_sum objects in field_list + + RETURN + 0 ok + 1 error +*/ + bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields, bool before_group_by) { @@ -8479,7 +8610,7 @@ bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields, /* - Change all funcs and sum_funcs to fields in tmp table, and create + Change all funcs and sum_funcs to fields in tmp table, and create new list of all items. change_to_use_tmp_fields() @@ -8699,7 +8830,7 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) Field *field=table->field[table->key_info[join_tab->ref.key].key_part[i]. fieldnr-1]; Item *value=join_tab->ref.items[i]; - cond->add(new Item_func_equal(new Item_field(field),value)); + cond->add(new Item_func_equal(new Item_field(field), value)); } if (thd->is_fatal_error) DBUG_RETURN(TRUE); @@ -8979,7 +9110,6 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, List<Item> field_list; List<Item> item_list; THD *thd=join->thd; - SELECT_LEX *select_lex= &join->thd->lex->select_lex; select_result *result=join->result; Item *item_null= new Item_null(); CHARSET_INFO *cs= &my_charset_latin1; @@ -9346,7 +9476,7 @@ void st_select_lex::print(THD *thd, String *str) str->append(table->db); str->append('.'); str->append(table->real_name); - if (strcmp(table->real_name, table->alias)) + if (my_strcasecmp(table_alias_charset, table->real_name, table->alias)) { str->append(' '); str->append(table->alias); diff --git a/sql/sql_select.h b/sql/sql_select.h index e1fc5d1c1fe..07b94b0afed 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -436,4 +436,4 @@ bool cp_buffer_from_ref(TABLE_REF *ref); bool error_if_full_join(JOIN *join); int report_error(TABLE *table, int error); int safe_index_read(JOIN_TAB *tab); -COND *eliminate_not_funcs(COND *cond); +COND *eliminate_not_funcs(THD *thd, COND *cond); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 158d9b1acb0..2e755c419ff 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -31,9 +31,11 @@ static const char *grant_names[]={ "select","insert","update","delete","create","drop","reload","shutdown", "process","file","grant","references","index","alter"}; +#ifndef NO_EMBEDDED_ACCESS_CHECKS static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **), "grant_types", grant_names}; +#endif static int mysql_find_files(THD *thd,List<char> *files, const char *db, const char *path, const char *wild, bool dir); @@ -219,30 +221,32 @@ struct show_privileges_st { const char *comment; }; - -/* - TODO: Update with new privileges -*/ static struct show_privileges_st sys_privileges[]= { - {"Select", "Tables", "To retrieve rows from table"}, - {"Insert", "Tables", "To insert data into tables"}, - {"Update", "Tables", "To update existing rows "}, - {"Delete", "Tables", "To delete existing rows"}, - {"Index", "Tables", "To create or drop indexes"}, - {"Alter", "Tables", "To alter the table"}, + {"Alter", "Tables", "To alter the table"}, + {"Create temporary tables","Databases","To use CREATE TEMPORARY TABLE"}, {"Create", "Databases,Tables,Indexes", "To create new databases and tables"}, - {"Drop", "Databases,Tables", "To drop databases and tables"}, - {"Grant", "Databases,Tables", "To give to other users those privileges you possess"}, + {"Delete", "Tables", "To delete existing rows"}, + {"Drop", "Databases,Tables", "To drop databases and tables"}, + {"File", "File access on server", "To read and write files on the server"}, + {"Grant option", "Databases,Tables", "To give to other users those privileges you possess"}, + {"Index", "Tables", "To create or drop indexes"}, + {"Insert", "Tables", "To insert data into tables"}, + {"Lock tables","Databases","To use LOCK TABLES (together with SELECT privilege)"}, + {"Process", "Server Admin", "To view the plain text of currently executing queries"}, {"References", "Databases,Tables", "To have references on tables"}, - {"Reload", "Server Admin", "To reload or refresh tables, logs and privileges"}, + {"Reload", "Server Admin", "To reload or refresh tables, logs and privileges"}, + {"Replication client","Server Admin","To ask where the slave or master servers are"}, + {"Replication slave","Server Admin","To read binary log events from the master"}, + {"Select", "Tables", "To retrieve rows from table"}, + {"Show databases","Server Admin","To see all databases with SHOW DATABASES"}, {"Shutdown","Server Admin", "To shutdown the server"}, - {"Process", "Server Admin", "To view the plain text of currently executing queries"}, - {"File", "File access on server", "To read and write files on the server"}, + {"Super","Server Admin","To use KILL thread, SET GLOBAL, CHANGE MASTER, etc."}, + {"Update", "Tables", "To update existing rows"}, + {"Usage","Server Admin","No privileges - allow connect only"}, {NullS, NullS, NullS} }; - int mysqld_show_privileges(THD *thd) { List<Item> field_list; @@ -299,11 +303,11 @@ static struct show_column_type_st sys_column_types[]= { {"tinyint", 1, "-128", "127", 0, 0, "YES", "YES", - "NO", "YES", "YES", "NO", "NULL,0", - "A very small integer"}, + "NO", "YES", "YES", "NO", "NULL,0", + "A very small integer"}, {"tinyint unsigned", - 1, "0" , "255", 0, 0, "YES", "YES", - "YES", "YES", "YES", "NO", "NULL,0", + 1, "0" , "255", 0, 0, "YES", "YES", + "YES", "YES", "YES", "NO", "NULL,0", "A very small integer"}, }; @@ -365,7 +369,9 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, char *ext; MY_DIR *dirp; FILEINFO *file; +#ifndef NO_EMBEDDED_ACCESS_CHECKS uint col_access=thd->col_access; +#endif TABLE_LIST table_list; DBUG_ENTER("mysql_find_files"); @@ -692,17 +698,11 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, if (!wild || !wild[0] || !wild_case_compare(system_charset_info, field->field_name,wild)) { -#ifdef NOT_USED - if (thd->col_access & TABLE_ACLS || - ! check_grant_column(thd,table,field->field_name, - (uint) strlen(field->field_name),1)) -#endif { byte *pos; uint flags=field->flags; String type(tmp,sizeof(tmp), system_charset_info); uint col_access; - bool null_default_value=0; protocol->prepare_for_resend(); protocol->store(field->field_name, system_charset_info); @@ -711,6 +711,12 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, if (verbose) protocol->store(field->has_charset() ? field->charset()->name : "NULL", system_charset_info); + /* + Altough TIMESTAMP fields can't contain NULL as its value they + will accept NULL if you will try to insert such value and will + convert it to current TIMESTAMP. So YES here means that NULL + is allowed for assignment but can't be returned. + */ pos=(byte*) ((flags & NOT_NULL_FLAG) && field->type() != FIELD_TYPE_TIMESTAMP ? "" : "YES"); @@ -720,16 +726,24 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, (field->flags & MULTIPLE_KEY_FLAG) ? "MUL":""); protocol->store((char*) pos, system_charset_info); - if (field->type() == FIELD_TYPE_TIMESTAMP || - field->unireg_check == Field::NEXT_NUMBER) - null_default_value=1; - if (!null_default_value && !field->is_null()) + if (table->timestamp_field == field && + field->unireg_check != Field::TIMESTAMP_UN_FIELD) + { + /* + We have NOW() as default value but we use CURRENT_TIMESTAMP form + because it is more SQL standard comatible + */ + protocol->store("CURRENT_TIMESTAMP", system_charset_info); + } + else if (field->unireg_check != Field::NEXT_NUMBER && + !field->is_null()) { // Not null by default type.set(tmp, sizeof(tmp), field->charset()); field->val_str(&type,&type); protocol->store(type.ptr(),type.length(),type.charset()); } - else if (field->maybe_null() || null_default_value) + else if (field->unireg_check == Field::NEXT_NUMBER || + field->maybe_null()) protocol->store_null(); // Null as default else protocol->store("",0, system_charset_info); // empty string @@ -819,7 +833,9 @@ int mysqld_show_create_db(THD *thd, char *dbname, char path[FN_REFLEN]; char buff[2048]; String buffer(buff, sizeof(buff), system_charset_info); +#ifndef NO_EMBEDDED_ACCESS_CHECKS uint db_access; +#endif bool found_libchar; HA_CREATE_INFO create; uint create_options = create_info ? create_info->options : 0; @@ -916,7 +932,7 @@ mysqld_show_logs(THD *thd) DBUG_RETURN(1); #ifdef HAVE_BERKELEY_DB - if (!berkeley_skip && berkeley_show_logs(protocol)) + if ((have_berkeley_db == SHOW_OPTION_YES) && berkeley_show_logs(protocol)) DBUG_RETURN(-1); #endif @@ -995,9 +1011,8 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list) protocol->store_null(); /* Check if we have a key part that only uses part of the field */ - if (!key_part->field || - key_part->length != - table->field[key_part->fieldnr-1]->key_length()) + 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); else protocol->store_null(); @@ -1085,100 +1100,85 @@ mysqld_dump_create_info(THD *thd, TABLE *table, int fd) DBUG_RETURN(0); } -static inline const char *require_quotes(const char *name, uint length) -{ - uint i, d, c; - for (i=0; i<length; i+=d) - { - c=((uchar *)name)[i]; - d=my_mbcharlen(system_charset_info, c); - if (d==1 && !system_charset_info->ident_map[c]) - return name+i; - } - return 0; -} - /* - Looking for char in multibyte string + Go through all character combinations and ensure that sql_lex.cc can + parse it as an identifer. SYNOPSIS - look_for_char() - name string for looking at - length length of name - q '\'' or '\"' for looking for - - RETURN VALUES - # pointer to found char in string - 0 string doesn't contain required char + require_quotes() + name attribute name + name_length length of name + + RETURN + # Pointer to conflicting character + 0 No conflicting character */ -static inline const char *look_for_char(const char *name, - uint length, char q) +static const char *require_quotes(const char *name, uint name_length) { - const char *cur= name; - const char *end= cur+length; - uint symbol_length; - for (; cur<end; cur+= symbol_length) + uint length; + const char *end= name + name_length; + + for ( ; name < end ; name++) { - char c= *cur; - symbol_length= my_mbcharlen(system_charset_info, c); - if (symbol_length==1 && c==q) - return cur; + uchar chr= (uchar) *name; + length= my_mbcharlen(system_charset_info, chr); + if (length == 1 && !system_charset_info->ident_map[chr]) + return name; } return 0; } + +static void append_quoted_simple_identifier(String *packet, char quote_char, + const char *name, uint length) +{ + packet->append("e_char, 1, system_charset_info); + packet->append(name, length, system_charset_info); + packet->append("e_char, 1, system_charset_info); +} + + void append_identifier(THD *thd, String *packet, const char *name, uint length) { - char qtype; - uint part_len; - const char *qplace; + const char *name_end; + char quote_char; + if (thd->variables.sql_mode & MODE_ANSI_QUOTES) - qtype= '\"'; + quote_char= '\"'; else - qtype= '`'; + quote_char= '`'; if (is_keyword(name,length)) { - packet->append(&qtype, 1, system_charset_info); - packet->append(name, length, system_charset_info); - packet->append(&qtype, 1, system_charset_info); + append_quoted_simple_identifier(packet, quote_char, name, length); + return; } - else + + if (!require_quotes(name, length)) { - if (!(qplace= require_quotes(name, length))) - { - if (!(thd->options & OPTION_QUOTE_SHOW_CREATE)) - packet->append(name, length, system_charset_info); - else - { - packet->append(&qtype, 1, system_charset_info); - packet->append(name, length, system_charset_info); - packet->append(&qtype, 1, system_charset_info); - } - } - else - { - packet->shrink(packet->length()+length+2); - packet->append(&qtype, 1, system_charset_info); - if (*qplace != qtype) - qplace= look_for_char(qplace+1,length-(qplace-name)-1,qtype); - while (qplace) - { - if ((part_len= qplace-name)) - { - packet->append(name, part_len, system_charset_info); - length-= part_len; - } - packet->append(qplace, 1, system_charset_info); - name= qplace; - qplace= look_for_char(name+1,length-1,qtype); - } + if (!(thd->options & OPTION_QUOTE_SHOW_CREATE)) packet->append(name, length, system_charset_info); - packet->append(&qtype, 1, system_charset_info); - } + else + append_quoted_simple_identifier(packet, quote_char, name, length); + return; + } + + /* The identifier must be quoted as it includes a quote character */ + + packet->reserve(length*2 + 2); + packet->append("e_char, 1, system_charset_info); + + for (name_end= name+length ; name < name_end ; name+= length) + { + char chr= *name; + length= my_mbcharlen(system_charset_info, chr); + if (length == 1 && chr == quote_char) + packet->append("e_char, 1, system_charset_info); + packet->append(name, length, packet->charset()); } + packet->append("e_char, 1, system_charset_info); } @@ -1206,7 +1206,7 @@ static int store_create_info(THD *thd, TABLE *table, String *packet) { List<Item> field_list; - char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], *end; + char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], *end, *alias; String type(tmp, sizeof(tmp),&my_charset_bin); Field **ptr,*field; uint primary_key; @@ -1232,12 +1232,15 @@ store_create_info(THD *thd, TABLE *table, String *packet) packet->append("CREATE TEMPORARY TABLE ", 23); else packet->append("CREATE TABLE ", 13); - append_identifier(thd,packet, table->real_name, strlen(table->real_name)); + alias= (lower_case_table_names == 2 ? table->table_name : + table->real_name); + append_identifier(thd, packet, alias, strlen(alias)); packet->append(" (\n", 3); for (ptr=table->field ; (field= *ptr); ptr++) { bool has_default; + bool has_now_default; uint flags = field->flags; if (ptr != table->field) @@ -1253,40 +1256,46 @@ store_create_info(THD *thd, TABLE *table, String *packet) field->sql_type(type); packet->append(type.ptr(),type.length()); - if (field->has_charset()) + if (field->has_charset() && !limited_mysql_mode && !foreign_db_mode) { - if (field->charset() == &my_charset_bin) - packet->append(" binary", 7); - else if (!limited_mysql_mode && !foreign_db_mode) + if (field->charset() != table->table_charset) { - if (field->charset() != table->table_charset) - { - packet->append(" character set ", 15); - packet->append(field->charset()->csname); - } - /* - For string types dump collation name only if - collation is not primary for the given charset - */ - if (!(field->charset()->state & MY_CS_PRIMARY)) - { - packet->append(" collate ", 9); - packet->append(field->charset()->name); - } + packet->append(" character set ", 15); + packet->append(field->charset()->csname); + } + /* + For string types dump collation name only if + collation is not primary for the given charset + */ + if (!(field->charset()->state & MY_CS_PRIMARY)) + { + packet->append(" collate ", 9); + packet->append(field->charset()->name); } } if (flags & NOT_NULL_FLAG) packet->append(" NOT NULL", 9); + + /* + Again we are using CURRENT_TIMESTAMP instead of NOW because it is + more standard + */ + has_now_default= table->timestamp_field == field && + field->unireg_check != Field::TIMESTAMP_UN_FIELD; + has_default= (field->type() != FIELD_TYPE_BLOB && - field->type() != FIELD_TYPE_TIMESTAMP && - field->unireg_check != Field::NEXT_NUMBER); + field->unireg_check != Field::NEXT_NUMBER && + !((foreign_db_mode || limited_mysql_mode) && + has_now_default)); if (has_default) { packet->append(" default ", 9); - if (!field->is_null()) + if (has_now_default) + packet->append("CURRENT_TIMESTAMP",17); + else if (!field->is_null()) { // Not null by default type.set(tmp, sizeof(tmp), field->charset()); field->val_str(&type,&type); @@ -1307,6 +1316,11 @@ store_create_info(THD *thd, TABLE *table, String *packet) packet->append(tmp,0); } + if (!foreign_db_mode && !limited_mysql_mode && + table->timestamp_field == field && + field->unireg_check != Field::TIMESTAMP_DN_FIELD) + packet->append(" on update CURRENT_TIMESTAMP",28); + if (field->unireg_check == Field::NEXT_NUMBER && !foreign_db_mode) packet->append(" auto_increment", 15 ); @@ -1814,10 +1828,10 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables, break; case SHOW_SLAVE_RUNNING: { - LOCK_ACTIVE_MI; + pthread_mutex_lock(&LOCK_active_mi); end= strmov(buff, (active_mi->slave_running && active_mi->rli.slave_running) ? "ON" : "OFF"); - UNLOCK_ACTIVE_MI; + pthread_mutex_unlock(&LOCK_active_mi); break; } #endif /* HAVE_REPLICATION */ diff --git a/sql/sql_string.cc b/sql/sql_string.cc index cd25b0c27ec..4c30b14cfe5 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -126,8 +126,8 @@ bool String::set(double num,uint decimals, CHARSET_INFO *cs) str_charset=cs; if (decimals >= NOT_FIXED_DEC) { - sprintf(buff,"%.14g",num); // Enough for a DATETIME - return copy(buff, (uint32) strlen(buff), &my_charset_latin1, cs); + uint32 len= my_sprintf(buff,(buff, "%.14g",num));// Enough for a DATETIME + return copy(buff, len, &my_charset_latin1, cs); } #ifdef HAVE_FCONVERT int decpt,sign; @@ -453,7 +453,7 @@ bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs) if (!arg_length) // Default argument if (!(arg_length= (uint32) strlen(s))) return FALSE; - if (str_charset->mbmaxlen > 1) + if (cs != str_charset && str_charset->mbmaxlen > 1) { uint32 add_length=arg_length * str_charset->mbmaxlen; if (realloc(str_length+ add_length)) @@ -671,9 +671,8 @@ int String::reserve(uint32 space_needed, uint32 grow_by) return FALSE; } -void String::qs_append(const char *str) +void String::qs_append(const char *str, uint32 len) { - int len = strlen(str); memcpy(Ptr + str_length, str, len + 1); str_length += len; } @@ -681,8 +680,7 @@ void String::qs_append(const char *str) void String::qs_append(double d) { char *buff = Ptr + str_length; - sprintf(buff,"%.14g", d); - str_length += strlen(buff); + str_length+= my_sprintf(buff, (buff, "%.14g", d)); } void String::qs_append(double *d) @@ -692,12 +690,6 @@ void String::qs_append(double *d) qs_append(ld); } -void String::qs_append(const char &c) -{ - Ptr[str_length] = c; - str_length += sizeof(c); -} - void String::qs_append(int i) { char *buff = Ptr + str_length; @@ -712,12 +704,56 @@ void String::qs_append(uint i) str_length += strlen(buff); } +/* + Compare strings according to collation, without end space. + + SYNOPSIS + sortcmp() + s First string + t Second string + cs Collation + + NOTE: + Normally this is case sensitive comparison -int sortcmp(const String *x,const String *y, CHARSET_INFO *cs) + RETURN + < 0 s < t + 0 s == t + > 0 s > t +*/ + + +int sortcmp(const String *s,const String *t, CHARSET_INFO *cs) { - return cs->coll->strnncollsp(cs, - (unsigned char *) x->ptr(),x->length(), - (unsigned char *) y->ptr(),y->length()); + return cs->coll->strnncollsp(cs, + (unsigned char *) s->ptr(),s->length(), + (unsigned char *) t->ptr(),t->length()); +} + + +/* + Compare strings byte by byte. End spaces are also compared. + + SYNOPSIS + stringcmp() + s First string + t Second string + + NOTE: + Strings are compared as a stream of unsigned chars + + RETURN + < 0 s < t + 0 s == t + > 0 s > t +*/ + + +int stringcmp(const String *s,const String *t) +{ + uint32 s_len=s->length(),t_len=t->length(),len=min(s_len,t_len); + int cmp= memcmp(s->ptr(), t->ptr(), len); + return (cmp) ? cmp : (int) (s_len - t_len); } @@ -771,10 +807,14 @@ copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, const uchar *from_end= (const uchar*) from+from_length; char *to_start= to; uchar *to_end= (uchar*) to+to_length; + int (*mb_wc)(struct charset_info_st *, my_wc_t *, const uchar *, + 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; while (1) { - if ((cnvres= from_cs->cset->mb_wc(from_cs, &wc, (uchar*) from, + if ((cnvres= (*mb_wc)(from_cs, &wc, (uchar*) from, from_end)) > 0) from+= cnvres; else if (cnvres == MY_CS_ILSEQ) @@ -786,7 +826,7 @@ copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, break; // Impossible char. outp: - if ((cnvres= to_cs->cset->wc_mb(to_cs, wc, (uchar*) to, to_end)) > 0) + if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0) to+= cnvres; else if (cnvres == MY_CS_ILUNI && wc != '?') { @@ -799,6 +839,7 @@ outp: return (uint32) (to - to_start); } + void String::print(String *str) { char *st= (char*)Ptr, *end= st+str_length; diff --git a/sql/sql_string.h b/sql/sql_string.h index d234bf7b507..1c559b597ba 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -69,12 +69,10 @@ public: Alloced_length=str.Alloced_length; alloced=0; str_charset=str.str_charset; } - static void *operator new(size_t size) - { return (void*) sql_alloc((uint) size); } 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_arg,size_t size) /*lint -e715 */ - { sql_element_free(ptr_arg); } + {} ~String() { free(); } inline void set_charset(CHARSET_INFO *charset) { str_charset= charset; } @@ -221,6 +219,7 @@ public: inline void caseup() { my_caseup(str_charset,Ptr,str_length); } inline void casedn() { my_casedn(str_charset,Ptr,str_length); } friend int sortcmp(const String *a,const String *b, CHARSET_INFO *cs); + friend int stringcmp(const String *a,const String *b); friend String *copy_if_not_alloced(String *a,String *b,uint32 arg_length); uint32 numchars(); int charpos(int i,uint32 offset=0); @@ -236,7 +235,7 @@ public: q_*** methods writes values of parameters itself qs_*** methods writes string representation of value */ - void q_append(const char &c) + void q_append(const char c) { Ptr[str_length++] = c; } @@ -261,15 +260,19 @@ public: str_length += data_len; } - void WriteAtPosition(int position, uint32 value) + void write_at_position(int position, uint32 value) { int4store(Ptr + position,value); } - void qs_append(const char *str); + void qs_append(const char *str, uint32 len); void qs_append(double d); void qs_append(double *d); - void qs_append(const char &c); + inline void qs_append(const char c) + { + Ptr[str_length]= c; + str_length++; + } void qs_append(int i); void qs_append(uint i); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 3b593e57d82..4b0ca67a549 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -179,11 +179,10 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool dont_log_query) { TABLE_LIST *table; - char path[FN_REFLEN]; + char path[FN_REFLEN], *alias; String wrong_tables; - db_type table_type; int error; - bool some_tables_deleted=0, tmp_table_deleted=0; + bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0; DBUG_ENTER("mysql_rm_table_part2"); if (lock_table_names(thd, tables)) @@ -212,13 +211,10 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, drop_locked_tables(thd,db,table->real_name); if (thd->killed) DBUG_RETURN(-1); - + alias= (lower_case_table_names == 2) ? table->alias : table->real_name; /* remove form file and isam files */ - strxmov(path, mysql_data_home, "/", db, "/", table->real_name, reg_ext, - NullS); + strxmov(path, mysql_data_home, "/", db, "/", alias, reg_ext, NullS); (void) unpack_filename(path,path); - - table_type=get_table_type(path); } if (drop_temporary || access(path,F_OK)) { @@ -232,10 +228,16 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, else { char *end; - *(end=fn_ext(path))=0; // Remove extension + db_type table_type= get_table_type(path); + *(end=fn_ext(path))=0; // Remove extension for delete error=ha_delete_table(table_type, path); if (error == ENOENT && if_exists) error = 0; + if (error == HA_ERR_ROW_IS_REFERENCED) + { + /* the table is referenced by a foreign key constraint */ + foreign_key_error=1; + } if (!error || error == ENOENT) { /* Delete the table definition file */ @@ -268,7 +270,10 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, error= 0; if (wrong_tables.length()) { - my_error(ER_BAD_TABLE_ERROR,MYF(0),wrong_tables.c_ptr()); + if (!foreign_key_error) + my_error(ER_BAD_TABLE_ERROR,MYF(0),wrong_tables.c_ptr()); + else + my_error(ER_ROW_IS_REFERENCED,MYF(0)); error= 1; } DBUG_RETURN(error); @@ -333,6 +338,47 @@ static int sort_keys(KEY *a, KEY *b) 0); } +/* + Check TYPELIB (set or enum) for duplicates + + SYNOPSIS + check_duplicates_in_interval() + set_or_name "SET" or "ENUM" string for warning message + name name of the checked column + typelib list of values for the column + + DESCRIPTION + This function prints an warning for each value in list + which has some duplicates on its right + + RETURN VALUES + void +*/ + +void check_duplicates_in_interval(const char *set_or_name, + const char *name, TYPELIB *typelib) +{ + unsigned int old_count= typelib->count; + const char **old_type_names= typelib->type_names; + + old_count= typelib->count; + old_type_names= typelib->type_names; + const char **cur_value= typelib->type_names; + for ( ; typelib->count > 1; cur_value++) + { + typelib->type_names++; + typelib->count--; + if (find_type((char*)*cur_value,typelib,1)) + { + push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_DUPLICATED_VALUE_IN_TYPE, + ER(ER_DUPLICATED_VALUE_IN_TYPE), + name,*cur_value,set_or_name); + } + } + typelib->count= old_count; + typelib->type_names= old_type_names; +} /* Create a table @@ -369,7 +415,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, uint select_field_count) { char path[FN_REFLEN]; - const char *key_name; + const char *key_name, *alias; create_field *sql_field,*dup_field; int error= -1; uint db_options,field,null_fields,blob_columns; @@ -377,15 +423,13 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, KEY *key_info,*key_info_buffer; KEY_PART_INFO *key_part_info; int auto_increment=0; + int timestamps= 0, timestamps_with_niladic= 0; handler *file; int field_no,dup_no; enum db_type new_db_type; DBUG_ENTER("mysql_create_table"); - /* - Check for duplicate fields and check type of table to create - */ - + /* Check for duplicate fields and check type of table to create */ if (!fields.elements) { my_error(ER_TABLE_MUST_HAVE_COLUMNS,MYF(0)); @@ -407,6 +451,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, db_options=create_info->table_options; if (create_info->row_type == ROW_TYPE_DYNAMIC) db_options|=HA_OPTION_PACK_RECORD; + alias= table_case_name(create_info, table_name); file=get_new_handler((TABLE*) 0, create_info->db_type); if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) && @@ -422,10 +467,24 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, sql_field->charset= create_info->default_table_charset; /* table_charset is set in ALTER TABLE if we want change character set - for all varchar/char columns + for all varchar/char columns. + But the table charset must not affect the BLOB fields, so don't + allow to change my_charset_bin to somethig else. */ - if (create_info->table_charset) + if (create_info->table_charset && sql_field->charset != &my_charset_bin) sql_field->charset= create_info->table_charset; + + CHARSET_INFO *savecs= sql_field->charset; + if ((sql_field->flags & BINCMP_FLAG) && + !(sql_field->charset= get_charset_by_csname(sql_field->charset->csname, + MY_CS_BINSORT,MYF(0)))) + { + char tmp[64]; + strmake(strmake(tmp, savecs->csname, sizeof(tmp)-4), "_bin", 4); + my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp); + DBUG_RETURN(-1); + } + sql_field->create_length_to_internal_length(); /* Don't pack keys in old tables if the user has requested this */ @@ -539,6 +598,8 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, if (sql_field->charset->state & MY_CS_BINSORT) sql_field->pack_flag|=FIELDFLAG_BINARY; sql_field->unireg_check=Field::INTERVAL_FIELD; + check_duplicates_in_interval("ENUM",sql_field->field_name, + sql_field->interval); break; case FIELD_TYPE_SET: sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) | @@ -546,6 +607,8 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, if (sql_field->charset->state & MY_CS_BINSORT) sql_field->pack_flag|=FIELDFLAG_BINARY; sql_field->unireg_check=Field::BIT_FIELD; + check_duplicates_in_interval("SET",sql_field->field_name, + sql_field->interval); break; case FIELD_TYPE_DATE: // Rest of string types case FIELD_TYPE_NEWDATE: @@ -555,8 +618,22 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, sql_field->pack_flag=f_settype((uint) sql_field->sql_type); break; case FIELD_TYPE_TIMESTAMP: - sql_field->unireg_check=Field::TIMESTAMP_FIELD; - /* fall through */ + /* We should replace old TIMESTAMP fields with their newer analogs */ + if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD) + { + if (!timestamps) + { + sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD; + timestamps_with_niladic++; + } + else + sql_field->unireg_check= Field::NONE; + } + else if (sql_field->unireg_check != Field::NONE) + timestamps_with_niladic++; + + timestamps++; + /* fall-through */ default: sql_field->pack_flag=(FIELDFLAG_NUMBER | (sql_field->flags & UNSIGNED_FLAG ? 0 : @@ -574,6 +651,11 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, auto_increment++; pos+=sql_field->pack_length; } + if (timestamps_with_niladic > 1) + { + my_error(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,MYF(0)); + DBUG_RETURN(-1); + } if (auto_increment > 1) { my_error(ER_WRONG_AUTO_KEY,MYF(0)); @@ -632,6 +714,12 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, DBUG_RETURN(-1); } key_parts+=key->columns.elements; + if (key->name && !tmp_table && + !my_strcasecmp(system_charset_info,key->name,primary_key_name)) + { + my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name); + DBUG_RETURN(-1); + } } tmp=min(file->max_keys(), MAX_KEY); if (key_count > tmp) @@ -846,26 +934,35 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, { if ((length=column->length) > file->max_key_length() || length > file->max_key_part_length()) - { - my_error(ER_WRONG_SUB_KEY,MYF(0)); - DBUG_RETURN(-1); - } - } - /* TODO HF What's this for??? */ - else if (f_is_geom(sql_field->pack_flag)) - { - } - else if (column->length > length || - ((f_is_packed(sql_field->pack_flag) || - ((file->table_flags() & HA_NO_PREFIX_CHAR_KEYS) && - (key_info->flags & HA_NOSAME))) && - column->length != length)) - { - my_error(ER_WRONG_SUB_KEY,MYF(0)); - DBUG_RETURN(-1); + { + length=min(file->max_key_length(), file->max_key_part_length()); + if (key->type == Key::MULTIPLE) + { + /* not a critical problem */ + char warn_buff[MYSQL_ERRMSG_SIZE]; + sprintf(warn_buff,ER(ER_TOO_LONG_KEY),length); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TOO_LONG_KEY, warn_buff); + } + else + { + my_error(ER_TOO_LONG_KEY,MYF(0),length); + DBUG_RETURN(-1); + } + } } - if (!(file->table_flags() & HA_NO_PREFIX_CHAR_KEYS)) - length=column->length; + else if (!f_is_geom(sql_field->pack_flag) && + (column->length > length || + ((f_is_packed(sql_field->pack_flag) || + ((file->table_flags() & HA_NO_PREFIX_CHAR_KEYS) && + (key_info->flags & HA_NOSAME))) && + column->length != length))) + { + my_error(ER_WRONG_SUB_KEY,MYF(0)); + DBUG_RETURN(-1); + } + else if (!(file->table_flags() & HA_NO_PREFIX_CHAR_KEYS)) + length=column->length; } else if (length == 0) { @@ -875,8 +972,20 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, } if (length > file->max_key_part_length()) { - my_error(ER_WRONG_SUB_KEY,MYF(0)); - DBUG_RETURN(-1); + length=file->max_key_part_length(); + if (key->type == Key::MULTIPLE) + { + /* not a critical problem */ + char warn_buff[MYSQL_ERRMSG_SIZE]; + sprintf(warn_buff,ER(ER_TOO_LONG_KEY),length); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TOO_LONG_KEY, warn_buff); + } + else + { + my_error(ER_TOO_LONG_KEY,MYF(0),length); + DBUG_RETURN(-1); + } } key_part_info->length=(uint16) length; /* Use packed keys for long strings on the first column */ @@ -956,7 +1065,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE; } else - (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,db,table_name,reg_ext); + (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,db,alias,reg_ext); unpack_filename(path,path); /* Check if table already exists */ if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) @@ -967,7 +1076,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, create_info->table_existed= 1; // Mark that table existed DBUG_RETURN(0); } - my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name); + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias); DBUG_RETURN(-1); } if (wait_if_global_read_lock(thd, 0)) @@ -1046,7 +1155,8 @@ make_unique_key_name(const char *field_name,KEY *start,KEY *end) { char buff[MAX_FIELD_NAME],*buff_end; - if (!check_if_keyname_exists(field_name,start,end)) + if (!check_if_keyname_exists(field_name,start,end) && + my_strcasecmp(system_charset_info,field_name,primary_key_name)) return (char*) field_name; // Use fieldname buff_end=strmake(buff,field_name,MAX_FIELD_NAME-4); for (uint i=2 ; ; i++) @@ -1107,7 +1217,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(0); if (!(table=open_table(thd,db,name,name,(bool*) 0))) { - quick_rm_table(create_info->db_type,db,name); + quick_rm_table(create_info->db_type,db,table_case_name(create_info,name)); DBUG_RETURN(0); } table->reginfo.lock_type=TL_WRITE; @@ -1116,7 +1226,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, VOID(pthread_mutex_lock(&LOCK_open)); hash_delete(&open_cache,(byte*) table); VOID(pthread_mutex_unlock(&LOCK_open)); - quick_rm_table(create_info->db_type,db,name); + quick_rm_table(create_info->db_type,db,table_case_name(create_info, name)); DBUG_RETURN(0); } table->file->extra(HA_EXTRA_WRITE_CACHE); @@ -1131,18 +1241,32 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, bool mysql_rename_table(enum db_type base, const char *old_db, - const char * old_name, + const char *old_name, const char *new_db, - const char * new_name) + const char *new_name) { - char from[FN_REFLEN],to[FN_REFLEN]; + char from[FN_REFLEN], to[FN_REFLEN]; + char tmp_from[NAME_LEN+1], tmp_to[NAME_LEN+1]; handler *file=get_new_handler((TABLE*) 0, base); int error=0; DBUG_ENTER("mysql_rename_table"); + + if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED)) + { + /* Table handler expects to get all file names as lower case */ + strmov(tmp_from, old_name); + my_casedn_str(system_charset_info, tmp_from); + old_name= tmp_from; + + strmov(tmp_to, new_name); + my_casedn_str(system_charset_info, tmp_to); + new_name= tmp_to; + } (void) sprintf(from,"%s/%s/%s",mysql_data_home,old_db,old_name); (void) sprintf(to,"%s/%s/%s",mysql_data_home,new_db,new_name); fn_format(from,from,"","",4); fn_format(to,to, "","",4); + if (!(error=file->rename_table((const char*) from,(const char *) to))) { if (rename_file_ext(from,to,reg_ext)) @@ -1158,6 +1282,7 @@ mysql_rename_table(enum db_type base, DBUG_RETURN(error != 0); } + /* Force all other threads to stop using the table @@ -1202,7 +1327,7 @@ static void wait_while_table_is_used(THD *thd,TABLE *table, Close a cached table SYNOPSIS - clsoe_cached_table() + close_cached_table() thd Thread handler table Table to remove from cache @@ -1552,6 +1677,12 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, protocol->store("Operation failed",16, system_charset_info); break; + case HA_ADMIN_REJECT: + protocol->store("status", 6, system_charset_info); + protocol->store("Operation need committed state",30, system_charset_info); + open_for_modify= FALSE; + break; + case HA_ADMIN_ALREADY_DONE: protocol->store("status", 6, system_charset_info); protocol->store("Table is already up to date", 27, system_charset_info); @@ -1762,7 +1893,8 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table, char *table_name= table->real_name; char *src_db= thd->db; char *src_table= table_ident->table.str; - int err; + int err, res= -1; + TABLE_LIST src_tables_list; DBUG_ENTER("mysql_create_like_table"); /* @@ -1776,6 +1908,13 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table, my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table); DBUG_RETURN(-1); } + + src_tables_list.db= table_ident->db.str ? table_ident->db.str : thd->db; + src_tables_list.real_name= table_ident->table.str; + src_tables_list.next= 0; + + if (lock_and_wait_for_table_name(thd, &src_tables_list)) + goto err; if ((tmp_table= find_temporary_table(thd, src_db, src_table))) strxmov(src_path, (*tmp_table)->path, reg_ext, NullS); @@ -1786,7 +1925,7 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table, if (access(src_path, F_OK)) { my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table); - DBUG_RETURN(-1); + goto err; } } @@ -1814,9 +1953,9 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table, /* Create a new table by copying from source table - */ - if (my_copy(src_path, dst_path, MYF(MY_WME))) - DBUG_RETURN(-1); + */ + if (my_copy(src_path, dst_path, MYF(MY_WME|MY_DONT_OVERWRITE_FILE))) + goto err; /* As mysql_truncate don't work on a new table at this stage of @@ -1832,16 +1971,28 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table, { (void) rm_temporary_table(create_info->db_type, dst_path); /* purecov: inspected */ - DBUG_RETURN(-1); /* purecov: inspected */ + goto err; /* purecov: inspected */ } } else if (err) { (void) quick_rm_table(create_info->db_type, db, table_name); /* purecov: inspected */ - DBUG_RETURN(-1); /* purecov: inspected */ + goto err; /* purecov: inspected */ } - DBUG_RETURN(0); + + // Must be written before unlock + mysql_update_log.write(thd,thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + thd->clear_error(); + Query_log_event qinfo(thd, thd->query, thd->query_length, + test(create_info->options & + HA_LEX_CREATE_TMP_TABLE)); + mysql_bin_log.write(&qinfo); + } + res= 0; + goto err; table_exists: if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) @@ -1850,10 +2001,16 @@ table_exists: sprintf(warn_buff,ER(ER_TABLE_EXISTS_ERROR),table_name); push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TABLE_EXISTS_ERROR,warn_buff); - DBUG_RETURN(0); + res= 0; } - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); - DBUG_RETURN(-1); + else + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); + +err: + pthread_mutex_lock(&LOCK_open); + unlock_table_name(thd, &src_tables_list); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(res); } @@ -1943,9 +2100,10 @@ int mysql_discard_or_import_tablespace(THD *thd, err: close_thread_tables(thd); thd->tablespace_op=FALSE; - if (error == 0) { + if (error == 0) + { send_ok(thd); - DBUG_RETURN(0); + DBUG_RETURN(0); } DBUG_RETURN(error); } @@ -1964,21 +2122,24 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, { TABLE *table,*new_table; int error; - char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN], - *table_name,*db; + char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN]; + char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; char index_file[FN_REFLEN], data_file[FN_REFLEN]; - bool use_timestamp=0; ha_rows copied,deleted; ulonglong next_insert_id; - uint save_time_stamp,db_create_options, used_fields; + uint db_create_options, used_fields; enum db_type old_db_type,new_db_type; DBUG_ENTER("mysql_alter_table"); thd->proc_info="init"; table_name=table_list->real_name; + alias= (lower_case_table_names == 2) ? table_list->alias : table_name; + db=table_list->db; - if (!new_db || !strcmp(new_db,db)) - new_db=db; + if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db)) + { + new_db= db; + } used_fields=create_info->used_fields; mysql_ha_closeall(thd, table_list); @@ -1986,7 +2147,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */ if (tablespace_op != NO_TABLESPACE_OP) DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list, - tablespace_op)); + tablespace_op)); if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ))) DBUG_RETURN(-1); @@ -1994,18 +2155,32 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, if (new_name) { strmov(new_name_buff,new_name); - fn_same(new_name_buff,table_name,3); + strmov(new_alias= new_alias_buff, new_name); if (lower_case_table_names) - my_casedn_str(system_charset_info,new_name); - if (!my_strcasecmp(table_alias_charset, new_name_buff, table_name)) - new_name=table_name; // No. Make later check easier + { + if (lower_case_table_names != 2) + { + my_casedn_str(system_charset_info, new_name_buff); + new_alias= new_name; // Create lower case table name + } + my_casedn_str(system_charset_info, new_name); + } + if (new_db == db && + !my_strcasecmp(table_alias_charset, new_name_buff, table_name)) + { + /* + Source and destination table names are equal: make later check + easier. + */ + new_alias= new_name= table_name; + } else { if (table->tmp_table) { if (find_temporary_table(thd,new_db,new_name_buff)) { - my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name); + my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name_buff); DBUG_RETURN(-1); } } @@ -2015,14 +2190,14 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, F_OK)) { /* Table will be closed in do_command() */ - my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name); + my_error(ER_TABLE_EXISTS_ERROR,MYF(0), new_alias); DBUG_RETURN(-1); } } } } else - new_name=table_name; + new_alias= new_name= table_name; old_db_type=table->db_type; if (create_info->db_type == DB_TYPE_DEFAULT) @@ -2059,7 +2234,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, { *fn_ext(new_name)=0; close_cached_table(thd, table); - if (mysql_rename_table(old_db_type,db,table_name,new_db,new_name)) + if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias)) error= -1; } VOID(pthread_mutex_unlock(&LOCK_open)); @@ -2084,14 +2259,12 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); VOID(pthread_mutex_unlock(&LOCK_open)); table->file->deactivate_non_unique_index(HA_POS_ERROR); + /* COND_refresh will be signaled in close_thread_tables() */ } else push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), table->table_name); - break; - - /* COND_refresh will be signaled in close_thread_tables() */ break; } } @@ -2122,7 +2295,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET)) create_info->default_table_charset= table->table_charset; - restore_record(table,default_values); // Empty record for DEFAULT + restore_record(table,default_values); // Empty record for DEFAULT List_iterator<Alter_drop> drop_it(drop_list); List_iterator<create_field> def_it(fields); List_iterator<Alter_column> alter_it(alter_list); @@ -2171,8 +2344,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, if (def) { // Field is changed def->field=field; - if (def->sql_type == FIELD_TYPE_TIMESTAMP) - use_timestamp=1; if (!def->after) { create_list.push_back(def); @@ -2182,9 +2353,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, else { // Use old field value create_list.push_back(def=new create_field(field,field)); - if (def->sql_type == FIELD_TYPE_TIMESTAMP) - use_timestamp=1; - alter_it.rewind(); // Change default if ALTER Alter_column *alter; while ((alter=alter_it++)) @@ -2326,6 +2494,12 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, { if (key->type != Key::FOREIGN_KEY) key_list.push_back(key); + if (key->name && + !my_strcasecmp(system_charset_info,key->name,primary_key_name)) + { + my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name); + DBUG_RETURN(-1); + } } } @@ -2431,9 +2605,13 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, goto err; } - save_time_stamp=new_table->time_stamp; - if (use_timestamp) - new_table->time_stamp=0; + + /* + We don't want update TIMESTAMP fields during ALTER TABLE + and copy_data_between_tables uses only write_row() for new_table so + don't need to set up timestamp_on_update_now member. + */ + new_table->timestamp_default_now= 0; new_table->next_number_field=new_table->found_next_number_field; thd->count_cuted_fields= CHECK_FIELD_WARN; // calc cuted fields thd->cuted_fields=0L; @@ -2446,7 +2624,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, order_num, order, &copied, &deleted); thd->last_insert_id=next_insert_id; // Needed for correct log thd->count_cuted_fields= CHECK_FIELD_IGNORE; - new_table->time_stamp=save_time_stamp; if (table->tmp_table) { @@ -2468,7 +2645,7 @@ 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_name)) + if (rename_temporary_table(thd, new_table, new_db, new_alias)) { // Fatal error close_temporary_table(thd,new_db,tmp_name); my_free((gptr) new_table,MYF(0)); @@ -2492,6 +2669,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, VOID(pthread_mutex_unlock(&LOCK_open)); goto err; } + /* Data is copied. Now we rename the old table to a temp name, rename the new one to the old name, remove all entries from the old table @@ -2543,12 +2721,12 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, VOID(quick_rm_table(new_db_type,new_db,tmp_name)); } else if (mysql_rename_table(new_db_type,new_db,tmp_name,new_db, - new_name)) + new_alias)) { // Try to get everything back error=1; - VOID(quick_rm_table(new_db_type,new_db,new_name)); + VOID(quick_rm_table(new_db_type,new_db,new_alias)); VOID(quick_rm_table(new_db_type,new_db,tmp_name)); - VOID(mysql_rename_table(old_db_type,db,old_name,db,table_name)); + VOID(mysql_rename_table(old_db_type,db,old_name,db,alias)); } if (error) { @@ -2714,13 +2892,14 @@ 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_recovery_logging(thd,FALSE); + /* + 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_recovery_logging(thd,FALSE); if (error) { - error = 1; + error= 1; goto err; } @@ -2796,7 +2975,7 @@ int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt) List<Item> field_list; Item *item; Protocol *protocol= thd->protocol; - DBUG_ENTER("mysql_admin_table"); + DBUG_ENTER("mysql_checksum_table"); field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2)); item->maybe_null= 1; @@ -2839,12 +3018,10 @@ int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt) /* calculating table's checksum */ ha_checksum crc= 0; - if (t->db_type == DB_TYPE_INNODB) { - /* InnoDB 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 */ - t->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); - } + /* InnoDB 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 */ + t->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); if (t->file->rnd_init(1)) protocol->store_null(); diff --git a/sql/sql_test.cc b/sql/sql_test.cc index cd493fcac30..f2909b93a2a 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -306,10 +306,14 @@ static void display_table_locks(void) THR_LOCK *lock=(THR_LOCK*) list->data; VOID(pthread_mutex_lock(&lock->mutex)); - push_locks_into_array(&saved_table_locks, lock->write.data, false, "Locked - write"); - push_locks_into_array(&saved_table_locks, lock->write_wait.data, true, "Waiting - write"); - push_locks_into_array(&saved_table_locks, lock->read.data, false, "Locked - read"); - push_locks_into_array(&saved_table_locks, lock->read_wait.data, true, "Waiting - read"); + push_locks_into_array(&saved_table_locks, lock->write.data, FALSE, + "Locked - write"); + push_locks_into_array(&saved_table_locks, lock->write_wait.data, TRUE, + "Waiting - write"); + push_locks_into_array(&saved_table_locks, lock->read.data, FALSE, + "Locked - read"); + push_locks_into_array(&saved_table_locks, lock->read_wait.data, TRUE, + "Waiting - read"); VOID(pthread_mutex_unlock(&lock->mutex)); } VOID(pthread_mutex_unlock(&THR_LOCK_lock)); diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 6e8aae54b23..561f79f9de1 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -120,6 +120,7 @@ void udf_init() udf_func *tmp; TABLE_LIST tables; READ_RECORD read_record_info; + TABLE *table; int error; DBUG_ENTER("ufd_init"); @@ -152,13 +153,11 @@ void udf_init() if (simple_open_n_lock_tables(new_thd, &tables)) { DBUG_PRINT("error",("Can't open udf table")); - sql_print_error("Can't open mysql/func table"); - close_thread_tables(new_thd); - delete new_thd; - DBUG_VOID_RETURN; + sql_print_error("Can't open the mysql/func table. Please run the mysql_install_db script to create it."); + goto end; } - TABLE *table = tables.table; + table= tables.table; init_read_record(&read_record_info, new_thd, table, NULL,1,0); while (!(error = read_record_info.read_record(&read_record_info))) { @@ -206,6 +205,8 @@ void udf_init() sql_print_error(ER(ER_GET_ERRNO), my_errno); end_read_record(&read_record_info); new_thd->version--; // Force close to free memory + +end: close_thread_tables(new_thd); delete new_thd; /* Remember that we don't have a THD */ @@ -297,7 +298,11 @@ udf_func *find_udf(const char *name,uint length,bool mark_used) DBUG_ENTER("find_udf"); /* TODO: This should be changed to reader locks someday! */ - rw_rdlock(&THR_LOCK_udf); + if (mark_used) + rw_wrlock(&THR_LOCK_udf); /* Called during fix_fields */ + else + rw_rdlock(&THR_LOCK_udf); /* Called during parsing */ + if ((udf=(udf_func*) hash_search(&udf_hash,(byte*) name, length ? length : (uint) strlen(name)))) { @@ -473,7 +478,7 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) if (!(udf=(udf_func*) hash_search(&udf_hash,(byte*) udf_name->str, (uint) udf_name->length))) { - net_printf(thd, ER_FUNCTION_NOT_DEFINED, udf_name); + net_printf(thd, ER_FUNCTION_NOT_DEFINED, udf_name->str); goto err; } del_udf(udf); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 303b0990cf7..7ac5c23a54d 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -112,6 +112,7 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, SELECT_LEX *lex_select_save= thd_arg->lex->current_select; SELECT_LEX *sl, *first_select; select_result *tmp_result; + ORDER *tmp_order; DBUG_ENTER("st_select_lex_unit::prepare"); /* @@ -150,6 +151,9 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, JOIN *join= new JOIN(thd_arg, sl->item_list, sl->options | thd_arg->options | additional_options, tmp_result); + if (!join) + goto err; + thd_arg->lex->current_select= sl; set_limit(sl, sl); if (sl->braces) @@ -175,6 +179,7 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, 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)); } @@ -200,12 +205,14 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, } } + item_list.empty(); + // it is not single select if (first_select->next_select()) { union_result->tmp_table_param.field_count= types.elements; if (!(table= create_tmp_table(thd_arg, &union_result->tmp_table_param, types, - (ORDER*) 0, !union_option, 1, + (ORDER*) 0, union_distinct, 1, (first_select_in_union()->options | thd_arg->options | TMP_TABLE_ALL_COLUMNS), @@ -219,15 +226,25 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, result_table_list.table= table; union_result->set_table(table); - item_list.empty(); thd_arg->lex->current_select= lex_select_save; { + Statement *stmt= thd->current_statement; + Statement backup; + if (stmt) + thd->set_n_backup_item_arena(stmt, &backup); Field **field; for (field= table->field; *field; field++) { - if (item_list.push_back(new Item_field(*field))) + Item_field *item= new Item_field(*field); + if (!item || item_list.push_back(item)) + { + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); DBUG_RETURN(-1); + } } + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); } } else @@ -262,6 +279,8 @@ int st_select_lex_unit::exec() item->reset(); table->file->delete_all_rows(); } + if (union_distinct) // for subselects + table->file->extra(HA_EXTRA_CHANGE_KEY_TO_UNIQUE); for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) { ha_rows records_at_start= 0; @@ -306,33 +325,14 @@ int st_select_lex_unit::exec() sl->options|= found_rows_for_union; } sl->join->select_options=sl->options; - /* - As far as union share table space we should reassign table map, - which can be spoiled by 'prepare' of JOIN of other UNION parts - if it use same tables - */ - uint tablenr=0; - for (TABLE_LIST *table_list= (TABLE_LIST*) sl->table_list.first; - table_list; - table_list= table_list->next, tablenr++) - { - if (table_list->shared) - { - /* - review notes: Check it carefully. I still can't understand - why I should not touch table->used_keys. For my point of - view we should do here same procedura as it was done by - setup_table - */ - setup_table_map(table_list->table, table_list, tablenr); - } - } res= sl->join->optimize(); } if (!res) { records_at_start= table->file->records; sl->join->exec(); + if (sl == union_distinct) + table->file->extra(HA_EXTRA_CHANGE_KEY_TO_DUP); res= sl->join->error; offset_limit_cnt= sl->offset_limit; if (!res && union_result->flush()) @@ -391,8 +391,10 @@ int st_select_lex_unit::exec() allocate JOIN for fake select only once (privent mysql_select automatic allocation) */ - fake_select_lex->join= new JOIN(thd, item_list, - fake_select_lex->options, result); + if (!(fake_select_lex->join= new JOIN(thd, item_list, + fake_select_lex->options, result))) + DBUG_RETURN(-1); + /* Fake st_select_lex should have item list for correctref_array allocation. @@ -439,7 +441,7 @@ int st_select_lex_unit::cleanup() { DBUG_RETURN(0); } - cleaned= 0; + cleaned= 1; if (union_result) { @@ -467,3 +469,25 @@ int st_select_lex_unit::cleanup() } DBUG_RETURN(error); } + + +void st_select_lex_unit::reinit_exec_mechanism() +{ + prepared= optimized= executed= 0; +#ifndef DBUG_OFF + if (first_select()->next_select()) + { + List_iterator_fast<Item> it(item_list); + Item *field; + while ((field= it++)) + { + /* + we can't cleanup here, because it broke link to temporary table field, + but have to drop fixed flag to allow next fix_field of this field + during re-executing + */ + field->fixed= 0; + } + } +#endif +} diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 24199df9b84..277a6b3bf37 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -15,8 +15,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* Update of records - Multi-table updates were introduced by Monty and Sinisa <sinisa@mysql.com> +/* + Single table and multi table updates of tables. + Multi-table updates were introduced by Sinisa & Monty */ #include "mysql_priv.h" @@ -94,7 +95,7 @@ int mysql_update(THD *thd, tables.table= table; tables.alias= table_list->alias; - if (setup_tables(update_table_list, 0) || + if (setup_tables(update_table_list) || setup_conds(thd,update_table_list,&conds) || thd->lex->select_lex.setup_ref_array(thd, order_num) || setup_order(thd, thd->lex->select_lex.ref_pointer_array, @@ -119,7 +120,6 @@ int mysql_update(THD *thd, { timestamp_query_id=table->timestamp_field->query_id; table->timestamp_field->query_id=thd->query_id-1; - table->time_stamp= table->timestamp_field->offset() +1; } /* Check the fields we are going to modify */ @@ -132,7 +132,7 @@ int mysql_update(THD *thd, { // Don't set timestamp column if this is modified if (table->timestamp_field->query_id == thd->query_id) - table->time_stamp=0; + table->timestamp_on_update_now= 0; else table->timestamp_field->query_id=timestamp_query_id; } @@ -272,6 +272,8 @@ int mysql_update(THD *thd, } } } + if (thd->killed && !error) + error= 1; // Aborted limit= tmp_limit; end_read_record(&info); /* Change select to use tempfile */ @@ -344,6 +346,8 @@ int mysql_update(THD *thd, table->file->unlock_row(); thd->row_count++; } + if (thd->killed && !error) + error= 1; // Aborted end_read_record(&info); free_io_cache(table); // If ORDER BY thd->proc_info="end"; @@ -436,17 +440,31 @@ int mysql_multi_update(THD *thd, int res; multi_update *result; TABLE_LIST *tl; + table_map item_tables= 0, derived_tables= 0; DBUG_ENTER("mysql_multi_update"); -#ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege=(SELECT_ACL & ~table_list->grant.privilege); -#endif - if ((res=open_and_lock_tables(thd,table_list))) + if ((res=open_and_lock_tables(thd,table_list))) DBUG_RETURN(res); select_lex->select_limit= HA_POS_ERROR; - table_map item_tables= 0, derived_tables= 0; + /* + Ensure that we have update privilege for all tables and columns in the + SET part + */ + for (tl= table_list ; tl ; tl=tl->next) + { + TABLE *table= tl->table; + /* + Update of derived tables is checked later + We don't check privileges here, becasue then we would get error + "UPDATE command denided .. for column N" instead of + "Target table ... is not updatable" + */ + if (!tl->derived) + table->grant.want_privilege= (UPDATE_ACL & ~table->grant.privilege); + } + if (thd->lex->derived_tables) { // Assign table map values to check updatability of derived tables @@ -477,13 +495,14 @@ int mysql_multi_update(THD *thd, for (tl= select_lex->get_table_list() ; tl ; tl= tl->next) { TABLE *table= tl->table; - if (table->timestamp_field) - { - table->time_stamp=0; - // Only set timestamp column if this is not modified - if (table->timestamp_field->query_id != thd->query_id) - table->time_stamp= table->timestamp_field->offset() +1; - } + + /* We only need SELECT privilege for columns in the values list */ + table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); + // Only set timestamp column if this is not modified + if (table->timestamp_field && + table->timestamp_field->query_id == thd->query_id) + table->timestamp_on_update_now= 0; + if (tl->derived) derived_tables|= table->map; } @@ -680,7 +699,7 @@ multi_update::initialize_tables(JOIN *join) { TABLE *table=table_ref->table; uint cnt= table_ref->shared; - Item_field *If; + Item_field *ifield; List<Item> temp_fields= *fields_for_table[cnt]; ORDER group; @@ -704,10 +723,10 @@ multi_update::initialize_tables(JOIN *join) /* ok to be on stack as this is not referenced outside of this func */ Field_string offset(table->file->ref_length, 0, "offset", table, &my_charset_bin); - if (!(If=new Item_field(((Field *) &offset)))) + if (!(ifield= new Item_field(((Field *) &offset)))) DBUG_RETURN(1); - If->maybe_null=0; - if (temp_fields.push_front(If)) + ifield->maybe_null= 0; + if (temp_fields.push_front(ifield)) DBUG_RETURN(1); /* Make an unique key over the first field to avoid duplicated updates */ @@ -950,14 +969,16 @@ int multi_update::do_updates(bool from_send_error) DBUG_RETURN(0); for (cur_table= update_tables; cur_table ; cur_table= cur_table->next) { + byte *ref_pos; + TABLE *tmp_table; + table = cur_table->table; if (table == table_to_update) continue; // Already updated - org_updated= updated; - byte *ref_pos; - TABLE *tmp_table= tmp_tables[cur_table->shared]; + tmp_table= tmp_tables[cur_table->shared]; tmp_table->file->extra(HA_EXTRA_CACHE); // Change to read cache + (void) table->file->rnd_init(0); table->file->extra(HA_EXTRA_NO_CACHE); /* @@ -1024,6 +1045,7 @@ int multi_update::do_updates(bool from_send_error) else trans_safe= 0; // Can't do safe rollback } + (void) table->file->rnd_end(); } DBUG_RETURN(0); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 79ede4dbfa7..17236941ceb 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -68,6 +68,7 @@ inline Item *or_or_concat(THD *thd, Item* A, Item* B) Table_ident *table; char *simple_string; Item *item; + Item_num *item_num; List<Item> *item_list; List<String> *string_list; String *string; @@ -224,6 +225,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token CONSTRAINT %token CONTINUE_SYM %token CONVERT_SYM +%token CURRENT_USER %token DATABASES %token DATA_SYM %token DECLARE_SYM @@ -614,7 +616,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token BEFORE_SYM %left SET_VAR -%left OR_OR_CONCAT OR +%left OR_OR_CONCAT OR XOR %left AND %left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE %left EQ EQUAL_SYM GE GT_SYM LE LT NE IS LIKE REGEXP IN_SYM @@ -623,7 +625,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %left SHIFT_LEFT SHIFT_RIGHT %left '-' '+' %left '*' '/' '%' DIV_SYM MOD_SYM -%left XOR %left '^' %left NEG '~' %right NOT @@ -640,7 +641,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); opt_table_alias %type <table> - table_ident references + table_ident table_ident_ref references %type <simple_string> remember_name remember_end opt_ident opt_db text_or_password @@ -654,7 +655,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); udf_type if_exists opt_local opt_table_options table_options 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 + opt_ignore_leaves fulltext_options spatial_type union_option %type <ulong_num> ULONG_NUM raid_types merge_insert_types @@ -672,10 +673,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); using_list expr_or_default set_expr_or_default interval_expr param_marker singlerow_subselect singlerow_subselect_init exists_subselect exists_subselect_init geometry_function - signed_literal NUM_literal + signed_literal now_or_signed_literal sp_opt_default simple_ident_nospvar simple_ident_q +%type <item_num> + NUM_literal + %type <item_list> expr_list udf_expr_list udf_expr_list2 when_list ident_list ident_list_arg @@ -759,7 +763,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); table_to_table_list table_to_table opt_table_list opt_as handler_rkey_function handler_read_or_scan single_multi table_wild_list table_wild_one opt_wild - union_clause union_list union_option + union_clause union_list precision subselect_start opt_and charset subselect_end select_var_list select_var_list_init help opt_len opt_extended_describe @@ -2169,26 +2173,16 @@ create_table_option: table_list->next=0; lex->create_info.used_fields|= HA_CREATE_USED_UNION; } - | DEFAULT charset opt_equal charset_name_or_default + | opt_default charset opt_equal charset_name_or_default { Lex->create_info.default_table_charset= $4; Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; } - | charset opt_equal charset_name_or_default - { - Lex->create_info.table_charset= $3; - Lex->create_info.used_fields|= HA_CREATE_USED_CHARSET; - } - | DEFAULT COLLATE_SYM opt_equal collation_name_or_default + | opt_default COLLATE_SYM opt_equal collation_name_or_default { - Lex->create_info.table_charset= $4; + Lex->create_info.default_table_charset= $4; Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; } - | COLLATE_SYM opt_equal collation_name_or_default - { - Lex->create_info.table_charset= $3; - Lex->create_info.used_fields|= HA_CREATE_USED_CHARSET; - } | INSERT_METHOD opt_equal merge_insert_types { Lex->create_info.merge_insert_method= $3; Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD;} | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.data_file_name= $4.str; } @@ -2297,7 +2291,7 @@ field_spec: { LEX *lex=Lex; lex->length=lex->dec=0; lex->type=0; lex->interval=0; - lex->default_value=0; + lex->default_value= lex->on_update_value= 0; lex->comment=0; lex->charset=NULL; } @@ -2307,7 +2301,8 @@ field_spec: if (add_field_to_list(lex->thd, $1.str, (enum enum_field_types) $3, lex->length,lex->dec,lex->type, - lex->default_value, lex->comment, + lex->default_value, lex->on_update_value, + lex->comment, lex->change,lex->interval,lex->charset, lex->uint_geom_type)) YYABORT; @@ -2503,7 +2498,9 @@ opt_attribute_list: attribute: NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; } | NOT NULL_SYM { Lex->type|= NOT_NULL_FLAG; } - | DEFAULT signed_literal { Lex->default_value=$2; } + | DEFAULT now_or_signed_literal { Lex->default_value=$2; } + | ON UPDATE_SYM NOW_SYM optional_braces + { Lex->on_update_value= new Item_func_now_local(); } | AUTO_INC { Lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; } | SERIAL_SYM DEFAULT VALUE_SYM { Lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG; } @@ -2511,6 +2508,7 @@ attribute: | UNIQUE_SYM { Lex->type|= UNIQUE_FLAG; } | UNIQUE_SYM KEY_SYM { Lex->type|= UNIQUE_KEY_FLAG; } | COMMENT_SYM TEXT_STRING_sys { Lex->comment= &$2; } + | BINARY { Lex->type|= BINCMP_FLAG; } | COLLATE_SYM collation_name { if (Lex->charset && !my_charset_same(Lex->charset,$2)) @@ -2526,6 +2524,11 @@ attribute: } ; +now_or_signed_literal: + NOW_SYM optional_braces { $$= new Item_func_now_local(); } + | signed_literal { $$=$1; } + ; + charset: CHAR_SYM SET {} | CHARSET {} @@ -2592,7 +2595,6 @@ opt_binary: /* empty */ { Lex->charset=NULL; } | ASCII_SYM { Lex->charset=&my_charset_latin1; } | BYTE_SYM { Lex->charset=&my_charset_bin; } - | BINARY { Lex->charset=&my_charset_bin; } | UNICODE_SYM { if (!(Lex->charset=get_charset_by_csname("ucs2",MY_CS_PRIMARY,MYF(0)))) @@ -2826,7 +2828,7 @@ alter_list_item: { LEX *lex=Lex; lex->length=lex->dec=0; lex->type=0; lex->interval=0; - lex->default_value=0; + lex->default_value= lex->on_update_value= 0; lex->comment=0; lex->charset= NULL; lex->simple_alter=0; @@ -2837,7 +2839,8 @@ alter_list_item: if (add_field_to_list(lex->thd,$3.str, (enum enum_field_types) $5, lex->length,lex->dec,lex->type, - lex->default_value, lex->comment, + lex->default_value, lex->on_update_value, + lex->comment, $3.str, lex->interval, lex->charset, lex->uint_geom_type)) YYABORT; @@ -2884,6 +2887,27 @@ alter_list_item: lex->select_lex.db=$3->db.str; lex->name= $3->table.str; } + | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate + { + if (!$4) + { + THD *thd= YYTHD; + $4= thd->variables.collation_database; + } + $5= $5 ? $5 : $4; + if (!my_charset_same($4,$5)) + { + net_printf(YYTHD,ER_COLLATION_CHARSET_MISMATCH, + $5->name,$4->csname); + YYABORT; + } + LEX *lex= Lex; + lex->create_info.table_charset= + lex->create_info.default_table_charset= $5; + lex->create_info.used_fields|= (HA_CREATE_USED_CHARSET | + HA_CREATE_USED_DEFAULT_CHARSET); + lex->simple_alter= 0; + } | create_table_options_space_separated { Lex->simple_alter=0; } | order_clause { Lex->simple_alter=0; }; @@ -3246,13 +3270,13 @@ select_init: SELECT_LEX * sel= lex->current_select; if (sel->set_braces(1)) { - send_error(lex->thd, ER_SYNTAX_ERROR); + yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } if (sel->linkage == UNION_TYPE && !sel->master_unit()->first_select()->braces) { - send_error(lex->thd, ER_SYNTAX_ERROR); + yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } /* select in braces, can't contain global parameters */ @@ -3268,13 +3292,13 @@ select_init2: SELECT_LEX * sel= lex->current_select; if (lex->current_select->set_braces(0)) { - send_error(lex->thd, ER_SYNTAX_ERROR); + yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } if (sel->linkage == UNION_TYPE && sel->master_unit()->first_select()->braces) { - send_error(lex->thd, ER_SYNTAX_ERROR); + yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } } @@ -3603,7 +3627,7 @@ simple_expr: $$= new Item_func_set_collation($1, new Item_string($3.str, $3.length, - YYTHD->charset())); + YYTHD->charset())); } | literal | param_marker @@ -3619,6 +3643,12 @@ simple_expr: } | '@' '@' opt_var_ident_type ident_or_text opt_component { + + if ($4.str && $5.str && check_reserved_words(&$4)) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } if (!($$= get_system_var(YYTHD, (enum_var_type) $3, $4, $5))) YYABORT; } @@ -3751,6 +3781,8 @@ simple_expr: $$= new Item_func_curtime_local($3); Lex->safe_to_cache_query=0; } + | CURRENT_USER optional_braces + { $$= create_func_current_user(); } | DATE_ADD_INTERVAL '(' expr ',' interval_expr interval ')' { $$= new Item_date_add_interval($3,$5,$6,0); } | DATE_SUB_INTERVAL '(' expr ',' interval_expr interval ')' @@ -3830,20 +3862,19 @@ simple_expr: { if ($1->type() != Item::ROW_ITEM) { - send_error(Lex->thd, ER_SYNTAX_ERROR); + yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } $$= new Item_func_interval((Item_row *)$1); } | LAST_INSERT_ID '(' ')' { - $$= get_system_var(YYTHD, OPT_SESSION, "last_insert_id", 14, - "last_insert_id()"); + $$= new Item_func_last_insert_id(); Lex->safe_to_cache_query= 0; } | LAST_INSERT_ID '(' expr ')' { - $$= new Item_func_set_last_insert_id($3); + $$= new Item_func_last_insert_id($3); Lex->safe_to_cache_query= 0; } | LEFT '(' expr ',' expr ')' @@ -4086,14 +4117,14 @@ geometry_function: { $$= GEOM_NEW(Item_func_geometry_from_wkb($3, $5)); } | GEOMETRYCOLLECTION '(' expr_list ')' { $$= GEOM_NEW(Item_func_spatial_collection(* $3, - Geometry::wkbGeometryCollection, - Geometry::wkbPoint)); } + Geometry::wkb_geometrycollection, + Geometry::wkb_point)); } | LINESTRING '(' expr_list ')' { $$= GEOM_NEW(Item_func_spatial_collection(* $3, - Geometry::wkbLineString, Geometry::wkbPoint)); } + Geometry::wkb_linestring, Geometry::wkb_point)); } | MULTILINESTRING '(' expr_list ')' { $$= GEOM_NEW( Item_func_spatial_collection(* $3, - Geometry::wkbMultiLineString, Geometry::wkbLineString)); } + Geometry::wkb_multilinestring, Geometry::wkb_linestring)); } | MLINEFROMTEXT '(' expr ')' { $$= GEOM_NEW(Item_func_geometry_from_text($3)); } | MLINEFROMTEXT '(' expr ',' expr ')' @@ -4108,10 +4139,10 @@ geometry_function: { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); } | MULTIPOINT '(' expr_list ')' { $$= GEOM_NEW(Item_func_spatial_collection(* $3, - Geometry::wkbMultiPoint, Geometry::wkbPoint)); } + Geometry::wkb_multipoint, Geometry::wkb_point)); } | MULTIPOLYGON '(' expr_list ')' { $$= GEOM_NEW(Item_func_spatial_collection(* $3, - Geometry::wkbMultiPolygon, Geometry::wkbPolygon)); } + Geometry::wkb_multipolygon, Geometry::wkb_polygon)); } | POINT_SYM '(' expr ',' expr ')' { $$= GEOM_NEW(Item_func_point($3,$5)); } | POINTFROMTEXT '(' expr ')' @@ -4124,7 +4155,7 @@ geometry_function: { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); } | POLYGON '(' expr_list ')' { $$= GEOM_NEW(Item_func_spatial_collection(* $3, - Geometry::wkbPolygon, Geometry::wkbLineString)); } + Geometry::wkb_polygon, Geometry::wkb_linestring)); } | GEOMCOLLFROMTEXT '(' expr ')' { $$= GEOM_NEW(Item_func_geometry_from_text($3)); } | GEOMCOLLFROMTEXT '(' expr ',' expr ')' @@ -4216,11 +4247,15 @@ sum_expr: { $$=new Item_sum_sum($3); } | SUM_SYM '(' DISTINCT in_sum_expr ')' { $$=new Item_sum_sum_distinct($4); } - | GROUP_CONCAT_SYM '(' opt_distinct expr_list opt_gorder_clause - opt_gconcat_separator ')' + | GROUP_CONCAT_SYM '(' opt_distinct + { Select->in_sum_expr++; } + expr_list opt_gorder_clause + opt_gconcat_separator + ')' { - $$=new Item_func_group_concat($3,$4,Lex->gorder_list,$6); - $4->empty(); + Select->in_sum_expr--; + $$=new Item_func_group_concat($3,$5,Select->gorder_list,$7); + $5->empty(); }; opt_distinct: @@ -4228,23 +4263,22 @@ opt_distinct: |DISTINCT { $$ = 1; }; opt_gconcat_separator: - /* empty */ { $$ = new String(",",1,default_charset_info); } + /* empty */ { $$ = new (&YYTHD->mem_root) String(",",1,default_charset_info); } |SEPARATOR_SYM text_string { $$ = $2; }; opt_gorder_clause: /* empty */ { - LEX *lex=Lex; - lex->gorder_list = NULL; + Select->gorder_list = NULL; } | order_clause { - LEX *lex=Lex; - lex->gorder_list= - (SQL_LIST*) sql_memdup((char*) &lex->current_select->order_list, + SELECT_LEX *select= Select; + select->gorder_list= + (SQL_LIST*) sql_memdup((char*) &select->order_list, sizeof(st_sql_list)); - lex->current_select->order_list.empty(); + select->order_list.empty(); }; @@ -4254,7 +4288,7 @@ in_sum_expr: LEX *lex= Lex; if (lex->current_select->inc_in_sum_expr()) { - send_error(lex->thd, ER_SYNTAX_ERROR); + yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } } @@ -4265,7 +4299,7 @@ in_sum_expr: }; cast_type: - BINARY { $$=ITEM_CAST_BINARY; Lex->charset= NULL; Lex->length= (char*)0; } + BINARY opt_len { $$=ITEM_CAST_CHAR; Lex->charset= &my_charset_bin; } | CHAR_SYM opt_len opt_binary { $$=ITEM_CAST_CHAR; } | NCHAR_SYM opt_len { $$=ITEM_CAST_CHAR; Lex->charset= national_charset_info; } | SIGNED_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->length= (char*)0; } @@ -4425,8 +4459,8 @@ select_derived: if (((int)lex->sql_command >= (int)SQLCOM_HA_OPEN && lex->sql_command <= (int)SQLCOM_HA_READ) || lex->sql_command == (int)SQLCOM_KILL) - { - send_error(lex->thd, ER_SYNTAX_ERROR); + { + yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || @@ -4434,8 +4468,13 @@ select_derived: YYABORT; mysql_init_select(lex); lex->current_select->linkage= DERIVED_TABLE_TYPE; + lex->current_select->parsing_place= SELECT_LEX_NODE::SELECT_LIST; + } + select_options select_item_list + { + Select->parsing_place= SELECT_LEX_NODE::NO_MATTER; } - select_options select_item_list opt_select_from union_opt + opt_select_from union_opt ; opt_outer: @@ -4478,15 +4517,15 @@ key_list_or_empty: key_usage_list2: key_usage_list2 ',' ident { Select-> - interval_list.push_back(new 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 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 String("PRIMARY", 7, + interval_list.push_back(new (&YYTHD->mem_root) String("PRIMARY", 7, system_charset_info)); }; using_list: @@ -4663,9 +4702,12 @@ order_dir: opt_limit_clause_init: /* empty */ { - SELECT_LEX *sel= Select; + LEX *lex= Lex; + SELECT_LEX *sel= lex->current_select; sel->offset_limit= 0L; - sel->select_limit= Lex->thd->variables.select_limit; + sel->select_limit= (&lex->select_lex == sel) ? + Lex->thd->variables.select_limit : /* primary SELECT */ + HA_POS_ERROR; /* subquery */ } | limit_clause {} ; @@ -5094,7 +5136,7 @@ opt_insert_update: for a moment */ if (Lex->sql_command != SQLCOM_INSERT) { - send_error(Lex->thd, ER_SYNTAX_ERROR); + yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } } @@ -5351,6 +5393,29 @@ show_param: { Lex->sql_command= SQLCOM_SHOW_LOGS; WARN_DEPRECATED("SHOW BDB LOGS", "SHOW ENGINE BDB LOGS"); } | LOGS_SYM { Lex->sql_command= SQLCOM_SHOW_LOGS; WARN_DEPRECATED("SHOW LOGS", "SHOW ENGINE BDB LOGS"); } + | GRANTS + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_GRANTS; + THD *thd= lex->thd; + LEX_USER *curr_user; + if (!(curr_user= (LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + YYABORT; + curr_user->user.str= thd->priv_user; + curr_user->user.length= strlen(thd->priv_user); + if (*thd->priv_host != 0) + { + curr_user->host.str= thd->priv_host; + curr_user->host.length= strlen(thd->priv_host); + } + else + { + curr_user->host.str= (char *) "%"; + curr_user->host.length= 1; + } + curr_user->password.str=NullS; + lex->grant_user= curr_user; + } | GRANTS FOR_SYM user { LEX *lex=Lex; @@ -5492,7 +5557,7 @@ opt_describe_column: /* empty */ {} | text_string { Lex->wild= $1; } | ident - { Lex->wild= new 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 */ @@ -5574,6 +5639,11 @@ purge_option: YYABORT; } Item *tmp= new Item_func_unix_timestamp($2); + /* + it is OK only emulate fix_fieds, because we need only + value of constant + */ + tmp->quick_fix_field(); Lex->sql_command = SQLCOM_PURGE_BEFORE; Lex->purge_time= (ulong) tmp->val_int(); } @@ -5709,26 +5779,38 @@ text_literal: text_string: TEXT_STRING_literal - { $$= new 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); - $$= tmp ? tmp->val_str((String*) 0) : (String*) 0; + /* + it is OK only emulate fix_fieds, because we need only + value of constant + */ + $$= tmp ? + tmp->quick_fix_field(), tmp->val_str((String*) 0) : + (String*) 0; } ; param_marker: '?' { - LEX *lex=Lex; - if (YYTHD->command == COM_PREPARE) + THD *thd=YYTHD; + LEX *lex= thd->lex; + if (thd->command == COM_PREPARE) { - lex->param_list.push_back($$=new Item_param((uint)(lex->tok_start-(uchar *)YYTHD->query))); - lex->param_count++; + Item_param *item= new Item_param((uint) (lex->tok_start - + (uchar *) thd->query)); + if (!($$= item) || lex->param_list.push_back(item)) + { + send_error(thd, ER_OUT_OF_RESOURCES); + YYABORT; + } } else { - yyerror("You have an error in your SQL syntax"); + yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } } @@ -5737,7 +5819,11 @@ param_marker: signed_literal: literal { $$ = $1; } | '+' NUM_literal { $$ = $2; } - | '-' NUM_literal { $$ = new Item_func_neg($2); } + | '-' NUM_literal + { + $2->max_length++; + $$= $2->neg(); + } ; @@ -5750,7 +5836,13 @@ literal: | UNDERSCORE_CHARSET HEX_NUM { Item *tmp= new Item_varbinary($2.str,$2.length); - String *str= tmp ? tmp->val_str((String*) 0) : (String*) 0; + /* + it is OK only emulate fix_fieds, because we need only + value of constant + */ + String *str= tmp ? + tmp->quick_fix_field(), tmp->val_str((String*) 0) : + (String*) 0; $$= new Item_string(str ? str->ptr() : "", str ? str->length() : 0, Lex->charset); @@ -5897,8 +5989,13 @@ field_ident: table_ident: ident { $$=new Table_ident($1); } | ident '.' ident { $$=new Table_ident(YYTHD, $1,$3,0);} - | '.' ident { $$=new Table_ident($2);} - /* For Delphi */; + | '.' ident { $$=new Table_ident($2);} /* For Delphi */ + ; + +table_ident_ref: + ident { LEX_STRING db={(char*) any_db,3}; $$=new Table_ident(YYTHD, db,$1,0); } + | ident '.' ident { $$=new Table_ident(YYTHD, $1,$3,0);} + ; IDENT_sys: IDENT { $$= $1; } @@ -5969,7 +6066,25 @@ user: if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) YYABORT; $$->user = $1; $$->host=$3; - }; + } + | CURRENT_USER optional_braces + { + THD *thd= YYTHD; + if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + YYABORT; + $$->user.str= thd->priv_user; + $$->user.length= strlen(thd->priv_user); + if (*thd->priv_host != 0) + { + $$->host.str= thd->priv_host; + $$->host.length= strlen(thd->priv_host); + } + else + { + $$->host.str= (char *) "%"; + $$->host.length= 1; + } + }; /* Keyword that we allow for identifiers */ @@ -6074,6 +6189,7 @@ keyword: | MASTER_LOG_POS_SYM {} | MASTER_USER_SYM {} | MASTER_PASSWORD_SYM {} + | MASTER_SERVER_ID_SYM {} | MASTER_CONNECT_RETRY_SYM {} | MASTER_SSL_SYM {} | MASTER_SSL_CA_SYM {} @@ -6195,7 +6311,7 @@ set: { LEX *lex=Lex; lex->sql_command= SQLCOM_SET_OPTION; - lex->option_type=OPT_DEFAULT; + lex->option_type=OPT_SESSION; lex->var_list.empty(); } option_value_list @@ -6348,6 +6464,11 @@ internal_variable_name: } | ident '.' ident { + if (check_reserved_words(&$1)) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } sys_var *tmp=find_sys_var($3.str, $3.length); if (!tmp) YYABORT; @@ -6453,14 +6574,14 @@ handler: if (!lex->current_select->add_table_to_list(lex->thd, $2, $4, 0)) YYABORT; } - | HANDLER_SYM table_ident CLOSE_SYM + | HANDLER_SYM table_ident_ref CLOSE_SYM { LEX *lex= Lex; lex->sql_command = SQLCOM_HA_CLOSE; if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0)) YYABORT; } - | HANDLER_SYM table_ident READ_SYM + | HANDLER_SYM table_ident_ref READ_SYM { LEX *lex=Lex; lex->sql_command = SQLCOM_HA_READ; @@ -6529,7 +6650,7 @@ revoke_command: grant_privileges ON opt_table FROM user_list {} | - ALL PRIVILEGES ',' GRANT FROM user_list + ALL PRIVILEGES ',' GRANT OPTION FROM user_list { Lex->sql_command = SQLCOM_REVOKE_ALL; } @@ -6738,7 +6859,7 @@ column_list: column_list_id: ident { - String *new_str = new 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; @@ -6844,7 +6965,7 @@ union_clause: ; union_list: - UNION_SYM union_option + UNION_SYM union_option { LEX *lex=Lex; if (lex->exchange) @@ -6855,13 +6976,16 @@ union_list: } if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) { - send_error(lex->thd, ER_SYNTAX_ERROR); + yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } if (mysql_new_select(lex, 0)) YYABORT; mysql_init_select(lex); lex->current_select->linkage=UNION_TYPE; + if ($2) /* UNION DISTINCT - remember position */ + lex->current_select->master_unit()->union_distinct= + lex->current_select; } select_init {} ; @@ -6903,9 +7027,10 @@ order_or_limit: ; union_option: - /* empty */ {} - | DISTINCT {} - | ALL {Select->master_unit()->union_option|= UNION_ALL;}; + /* empty */ { $$=1; } + | DISTINCT { $$=1; } + | ALL { $$=0; } + ; singlerow_subselect: subselect_start singlerow_subselect_init @@ -6955,8 +7080,8 @@ subselect_start: if (((int)lex->sql_command >= (int)SQLCOM_HA_OPEN && lex->sql_command <= (int)SQLCOM_HA_READ) || lex->sql_command == (int)SQLCOM_KILL) - { - send_error(lex->thd, ER_SYNTAX_ERROR); + { + yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } if (mysql_new_select(Lex, 1)) diff --git a/sql/structs.h b/sql/structs.h index 37208e63400..86d754f00d6 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -20,11 +20,21 @@ struct st_table; class Field; -typedef struct lex_string { +typedef struct st_lex_string +{ char *str; uint length; } LEX_STRING; +typedef struct st_lex_string_with_init :public st_lex_string +{ + st_lex_string_with_init(const char *str_arg, uint length_arg) + { + str= (char*) str_arg; + length= length_arg; + } +} LEX_STRING_WITH_INIT; + typedef struct st_date_time_format { uchar positions[8]; @@ -210,7 +220,7 @@ typedef struct user_conn { #define REG_NEW_RECORD 2 /* Write a new record if not found */ #define REG_UPDATE 4 /* Uppdate record */ #define REG_DELETE 8 /* Delete found record */ -#define REG_PROG 16 /* User is updateing database */ +#define REG_PROG 16 /* User is updating database */ #define REG_CLEAR_AFTER_WRITE 32 #define REG_MAY_BE_UPDATED 64 #define REG_AUTO_UPDATE 64 /* Used in D-forms for scroll-tables */ diff --git a/sql/table.cc b/sql/table.cc index 8fe061af530..23d99466a83 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -30,11 +30,11 @@ static void fix_type_pointers(const char ***array, TYPELIB *point_to_type, static uint find_field(TABLE *form,uint start,uint length); -static byte* get_field_name(Field *buff,uint *length, +static byte* get_field_name(Field **buff,uint *length, my_bool not_used __attribute__((unused))) { - *length= (uint) strlen(buff->field_name); - return (byte*) buff->field_name; + *length= (uint) strlen((*buff)->field_name); + return (byte*) (*buff)->field_name; } /* @@ -101,7 +101,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, if (!outparam->real_name || !outparam->table_name) goto err_end; - if ((file=my_open(fn_format(index_file,name,"",reg_ext,4), + if ((file=my_open(fn_format(index_file,name,"",reg_ext,MY_UNPACK_FILENAME), O_RDONLY | O_SHARE, MYF(0))) < 0) @@ -465,7 +465,6 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, goto err_not_open; /* purecov: inspected */ } reg_field->comment=comment; - reg_field->set_charset(charset); if (!(reg_field->flags & NOT_NULL_FLAG)) { if ((null_bit<<=1) == 256) @@ -479,7 +478,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, if (outparam->timestamp_field == reg_field) outparam->timestamp_field_offset=i; if (use_hash) - (void) my_hash_insert(&outparam->name_hash,(byte*) *field_ptr); // Will never fail + (void) my_hash_insert(&outparam->name_hash,(byte*) field_ptr); // Will never fail } *field_ptr=0; // End marker @@ -581,7 +580,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, { if ((index_flags & HA_KEY_READ_ONLY) && (field->key_type() != HA_KEYTYPE_TEXT || - (!(ha_option & HA_KEY_READ_WRONG_STR) && + (!((ha_option & HA_KEY_READ_WRONG_STR) || + (field->flags & BINARY_FLAG)) && !(keyinfo->flags & HA_FULLTEXT)))) field->part_of_key.set_bit(key); if ((field->key_type() != HA_KEYTYPE_TEXT || @@ -700,8 +700,9 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, if (db_stat) { int err; + unpack_filename(index_file,index_file); if ((err=(outparam->file-> - ha_open(unpack_filename(index_file,index_file), + ha_open(index_file, (db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR), (db_stat & HA_OPEN_TEMPORARY ? HA_OPEN_TMP_TABLE : ((db_stat & HA_WAIT_IF_LOCKED) || @@ -1282,14 +1283,17 @@ char *get_field(MEM_ROOT *mem, Field *field) bool check_db_name(char *name) { - char *start=name; + char *start=name; + /* Used to catch empty names and names with end space */ + bool last_char_is_space= TRUE; - if (lower_case_table_names) + if (lower_case_table_names && name != any_db) my_casedn_str(files_charset_info, name); while (*name) { #if defined(USE_MB) && defined(USE_MB_IDENT) + last_char_is_space= my_isspace(default_charset_info, *name); if (use_mb(system_charset_info)) { int len=my_ismbchar(system_charset_info, name, @@ -1300,19 +1304,22 @@ bool check_db_name(char *name) continue; } } +#else + last_char_is_space= *name==' '; #endif if (*name == '/' || *name == '\\' || *name == FN_LIBCHAR || *name == FN_EXTCHAR) return 1; name++; } - return (uint) (name - start) > NAME_LEN || name == start; + return last_char_is_space || (uint) (name - start) > NAME_LEN; } /* Allow anything as a table name, as long as it doesn't contain an a '/', or a '.' character + or ' ' at the end returns 1 on error */ @@ -1322,10 +1329,17 @@ bool check_table_name(const char *name, uint length) const char *end= name+length; if (!length || length > NAME_LEN) return 1; +#if defined(USE_MB) && defined(USE_MB_IDENT) + bool last_char_is_space= FALSE; +#else + if (name[length-1]==' ') + return 1; +#endif while (name != end) { #if defined(USE_MB) && defined(USE_MB_IDENT) + last_char_is_space= my_isspace(default_charset_info, *name); if (use_mb(system_charset_info)) { int len=my_ismbchar(system_charset_info, name, end); @@ -1340,16 +1354,23 @@ bool check_table_name(const char *name, uint length) return 1; name++; } +#if defined(USE_MB) && defined(USE_MB_IDENT) + return last_char_is_space; +#else return 0; +#endif } + 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) + last_char_is_space= my_isspace(default_charset_info, *name); if (use_mb(system_charset_info)) { int len=my_ismbchar(system_charset_info, name, @@ -1360,13 +1381,15 @@ bool check_column_name(const char *name) continue; } } +#else + last_char_is_space= *name==' '; #endif if (*name == NAMES_SEP_CHAR) return 1; name++; } /* Error if empty or too long column name */ - return (name == start || (uint) (name - start) > NAME_LEN); + return last_char_is_space || (uint) (name - start) > NAME_LEN; } /* diff --git a/sql/table.h b/sql/table.h index ba7349d33fc..18155e1568d 100644 --- a/sql/table.h +++ b/sql/table.h @@ -26,6 +26,7 @@ class st_select_lex_unit; typedef struct st_order { struct st_order *next; Item **item; /* Point at item in select fields */ + Item *item_ptr; /* Storage for initial item */ Item **item_copy; /* For SPs; the original item ptr */ bool asc; /* true if ascending */ bool free_me; /* true if item isn't shared */ @@ -66,7 +67,8 @@ struct st_table { handler *file; Field **field; /* Pointer to fields */ Field_blob **blob_field; /* Pointer to blob fields */ - HASH name_hash; /* hash of field names */ + /* hash of field names (contains pointers to elements of field array) */ + HASH name_hash; byte *record[2]; /* Pointer to records */ byte *default_values; /* Default values for INSERT */ byte *insert_values; /* used by INSERT ... UPDATE */ @@ -97,8 +99,20 @@ struct st_table { uint raid_type,raid_chunks; uint status; /* Used by postfix.. */ uint system; /* Set if system record */ - ulong time_stamp; /* Set to offset+1 of record */ + + /* + These two members hold offset in record + 1 for TIMESTAMP field + with NOW() as default value or/and with ON UPDATE NOW() option. + If 0 then such field is absent in this table or auto-set for default + or/and on update should be temporaly disabled for some reason. + These values is setup to offset value for each statement in open_table() + and turned off in statement processing code (see mysql_update as example). + */ + ulong timestamp_default_now; + ulong timestamp_on_update_now; + /* Index of auto-updated TIMESTAMP field in field array */ uint timestamp_field_offset; + uint next_number_index; uint blob_ptr_size; /* 4 or 8 */ uint next_number_key_offset; @@ -109,7 +123,7 @@ struct st_table { my_bool maybe_null,outer_join; /* Used with OUTER JOIN */ my_bool force_index; my_bool distinct,const_table,no_rows; - my_bool key_read, bulk_insert; + my_bool key_read; my_bool crypted; my_bool db_low_byte_first; /* Portable row format */ my_bool locked_by_flush; @@ -119,7 +133,7 @@ struct st_table { my_bool is_view; my_bool no_keyread, no_cache; my_bool clear_query_id; /* To reset query_id for tables and cols */ - my_bool auto_increment_field_not_null; + my_bool auto_increment_field_not_null; Field *next_number_field, /* Set if next_number is activated */ *found_next_number_field, /* Set on open */ *rowid_field; @@ -174,12 +188,13 @@ typedef struct st_table_list GRANT_INFO grant; thr_lock_type lock_type; uint outer_join; /* Which join type */ - uint shared; /* Used in union or in multi-upd */ + uint shared; /* Used in multi-upd */ uint32 db_length, real_name_length; bool straight; /* optimize with prev table */ bool updating; /* for replicate-do/ignore table */ bool force_index; /* Prefer index over table scan */ bool ignore_leaves; /* Preload only non-leaf nodes */ + bool cacheable_table; /* stop PS caching */ } TABLE_LIST; typedef struct st_changed_table_list diff --git a/sql/time.cc b/sql/time.cc index 9a18a150c50..7fb466f6b97 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -62,6 +62,9 @@ long my_gmt_sec(TIME *t, long *my_timezone) struct tm *l_time,tm_tmp; long diff, current_timezone; + if (t->year > TIMESTAMP_MAX_YEAR || t->year < TIMESTAMP_MIN_YEAR) + return 0; + if (t->hour >= 24) { /* Fix for time-loop */ t->day+=t->hour/24; @@ -124,8 +127,10 @@ long my_gmt_sec(TIME *t, long *my_timezone) tmp-=t->minute*60 + t->second; // Move to previous hour } *my_timezone= current_timezone; - if (tmp < 0 && t->year <= 1900+YY_PART_YEAR) + + if (tmp < TIMESTAMP_MIN_VALUE || tmp > TIMESTAMP_MAX_VALUE) tmp= 0; + return (long) tmp; } /* my_gmt_sec */ @@ -386,9 +391,11 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) ulong not_zero_date, allow_space; bool is_internal_format; const char *pos, *last_field_pos; + const char *str_begin= str; const char *end=str+length; const uchar *format_position; bool found_delimitier= 0, found_space= 0; + uint frac_pos, frac_len; DBUG_ENTER("str_to_TIME"); DBUG_PRINT("ENTER",("str: %.*s",length,str)); @@ -477,7 +484,7 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) tmp_value=tmp_value*10 + (ulong) (uchar) (*str - '0'); str++; } - date_len[i]+= (uint) (str - start); + date_len[i]= (uint) (str - start); if (tmp_value > 999999) // Impossible date part DBUG_RETURN(TIMESTAMP_NONE); date[i]=tmp_value; @@ -530,9 +537,9 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) { if (str+2 <= end && (str[1] == 'M' || str[1] == 'm')) { - if (str[1] == 'p' || str[1] == 'P') + if (str[0] == 'p' || str[0] == 'P') add_hours= 12; - else if (str[1] != 'a' || str[1] != 'A') + else if (str[0] != 'a' || str[0] != 'A') continue; // Not AM/PM str+= 2; // Skip AM/PM /* Skip space after AM/PM */ @@ -550,7 +557,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) number_of_fields= i - start_loop; while (i < MAX_DATE_PARTS) - date[i++]=0; + { + date_len[i]= 0; + date[i++]= 0; + } if (!is_internal_format) { @@ -564,7 +574,13 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) l_time->hour= date[(uint) format_position[3]]; l_time->minute= date[(uint) format_position[4]]; l_time->second= date[(uint) format_position[5]]; - l_time->second_part= date[(uint) format_position[6]]; + + frac_pos= (uint) format_position[6]; + frac_len= date_len[frac_pos]; + if (frac_len < 6) + date[frac_pos]*= (uint) log_10_int[6 - frac_len]; + l_time->second_part= date[frac_pos]; + if (format_position[7] != (uchar) 255) { if (l_time->hour > 12) @@ -580,6 +596,8 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) l_time->hour= date[3]; l_time->minute= date[4]; l_time->second= date[5]; + if (date_len[6] < 6) + date[6]*= (uint) log_10_int[6 - date_len[6]]; l_time->second_part=date[6]; } l_time->neg= 0; @@ -609,15 +627,17 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) current_thd->cuted_fields++; goto err; } - if (str != end && current_thd->count_cuted_fields) + + l_time->time_type= (number_of_fields <= 3 ? + TIMESTAMP_DATE : TIMESTAMP_DATETIME); + + for (; str != end ; str++) { - for (; str != end ; str++) + if (!my_isspace(&my_charset_latin1,*str)) { - if (!my_isspace(&my_charset_latin1,*str)) - { - current_thd->cuted_fields++; - break; - } + make_truncated_value_warning(current_thd, str_begin, length, + l_time->time_type); + break; } } @@ -634,15 +654,12 @@ time_t str_to_timestamp(const char *str,uint length) { TIME l_time; long not_used; + time_t timestamp= 0; - if (str_to_TIME(str,length,&l_time,0) <= TIMESTAMP_DATETIME_ERROR) - return(0); - if (l_time.year >= TIMESTAMP_MAX_YEAR || l_time.year < 1900+YY_PART_YEAR-1) - { + if (str_to_TIME(str,length,&l_time,0) > TIMESTAMP_DATETIME_ERROR && + !(timestamp= my_gmt_sec(&l_time, ¬_used))) current_thd->cuted_fields++; - return(0); - } - return(my_gmt_sec(&l_time, ¬_used)); + return timestamp; } @@ -684,6 +701,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) { long date[5],value; const char *end=str+length, *end_of_days; + const char *str_begin= str; bool found_days,found_hours; uint state; @@ -704,7 +722,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) { // Probably full timestamp enum timestamp_type res= str_to_TIME(str,length,l_time, (TIME_FUZZY_DATE | - TIME_DATETIME_ONLY)); + TIME_DATETIME_ONLY)); if ((int) res >= (int) TIMESTAMP_DATETIME_ERROR) return res == TIMESTAMP_DATETIME_ERROR; } @@ -782,6 +800,8 @@ fractional: my_isdigit(&my_charset_latin1,str[0]) && field_length--) value=value*10 + (uint) (uchar) (*str - '0'); + if (field_length) + value*= (long) log_10_int[field_length]; date[4]=value; } else @@ -794,12 +814,12 @@ fractional: str++; if (str+2 <= end && (str[1] == 'M' || str[1] == 'm')) { - if (str[1] == 'p' || str[1] == 'P') + if (str[0] == 'p' || str[0] == 'P') { str+= 2; date[1]= date[1]%12 + 12; } - else if (str[1] == 'a' || str[1] == 'A') + else if (str[0] == 'a' || str[0] == 'A') str+=2; } } @@ -820,13 +840,14 @@ fractional: l_time->time_type= TIMESTAMP_TIME; /* Check if there is garbage at end of the TIME specification */ - if (str != end && current_thd->count_cuted_fields) + if (str != end) { do { if (!my_isspace(&my_charset_latin1,*str)) { - current_thd->cuted_fields++; + make_truncated_value_warning(current_thd, str_begin, length, + TIMESTAMP_TIME); break; } } while (++str != end); @@ -1263,3 +1284,35 @@ void make_datetime(DATE_TIME_FORMAT *format, TIME *l_time, String *str) str->length(length); str->set_charset(&my_charset_bin); } + +void make_truncated_value_warning(THD *thd, const char *str_val, + uint str_length, timestamp_type time_type) +{ + char warn_buff[MYSQL_ERRMSG_SIZE]; + const char *type_str; + + char buff[128]; + String str(buff,(uint32) sizeof(buff), system_charset_info); + str.length(0); + str.append(str_val, str_length); + str.append('\0'); + + switch (time_type) { + case TIMESTAMP_DATE: + type_str= "date"; + break; + case TIMESTAMP_DATETIME: + type_str= "datetime"; + break; + case TIMESTAMP_TIME: + type_str= "time"; + break; + default: + type_str= "string"; + break; + } + sprintf(warn_buff, ER(ER_TRUNCATED_WRONG_VALUE), + type_str, str.ptr()); + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_TRUNCATED_WRONG_VALUE, warn_buff); +} diff --git a/sql/unireg.cc b/sql/unireg.cc index 6ebba313442..0e4b449c6a3 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -29,6 +29,7 @@ #include <m_ctype.h> #define FCOMP 17 /* Bytes for a packed field */ +#define FCOMP 17 /* Bytes for a packed field */ static uchar * pack_screens(List<create_field> &create_fields, uint *info_length, uint *screens, bool small_file); @@ -150,7 +151,9 @@ int rea_create_table(THD *thd, my_string file_name, my_free((gptr) screen_buff,MYF(0)); my_free((gptr) keybuff, MYF(0)); - if (my_sync(file, MYF(MY_WME))) + + if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE) && + my_sync(file, MYF(MY_WME))) goto err2; if (my_close(file,MYF(MY_WME)) || ha_create_table(file_name,create_info,0)) @@ -351,8 +354,12 @@ static bool pack_header(uchar *forminfo, enum db_type table_type, MTYP_NOEMPTY_BIT); no_empty++; } - if ((MTYP_TYPENR(field->unireg_check) == Field::TIMESTAMP_FIELD || - f_packtype(field->pack_flag) == (int) FIELD_TYPE_TIMESTAMP) && + /* + We mark first TIMESTAMP field with NOW() in DEFAULT or ON UPDATE + as auto-update field. + */ + if (field->sql_type == FIELD_TYPE_TIMESTAMP && + MTYP_TYPENR(field->unireg_check) != Field::NONE && !time_stamp_pos) time_stamp_pos=(int) field->offset+1; length=field->pack_length; |