diff --git a/VC++Files/mysql.sln b/VC++Files/mysql.sln index f7c612ff390..c5bf8291888 100644 --- a/VC++Files/mysql.sln +++ b/VC++Files/mysql.sln @@ -257,6 +257,7 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mysql_client_test", "tests\mysql_client_test.vcproj", "{DA224DAB-5006-42BE-BB77-16E8BE5326D5}" ProjectSection(ProjectDependencies) = postProject {26383276-4843-494B-8BE0-8936ED3EBAAB} = {26383276-4843-494B-8BE0-8936ED3EBAAB} + {44D9C7DC-6636-4B82-BD01-6876C64017DF} = {44D9C7DC-6636-4B82-BD01-6876C64017DF} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mysqlmanager", "server-tools\instance-manager\mysqlmanager.vcproj", "{6D524B3E-210A-4FCD-8D41-FEC0D21E83AC}" diff --git a/VC++Files/mysys/mysys.vcproj b/VC++Files/mysys/mysys.vcproj index befb4ad58b0..cf367e5666b 100644 --- a/VC++Files/mysys/mysys.vcproj +++ b/VC++Files/mysys/mysys.vcproj @@ -3232,6 +3232,49 @@ PreprocessorDefinitions=""/> + + + + + + + + + + + + + + + + + ','count(ns:element)'); +extractValue('','count(ns:element)') +1 +select extractValue('a','/ns:element'); +extractValue('a','/ns:element') +a +select extractValue('a','/ns:element/@xmlns:ns'); +extractValue('a','/ns:element/@xmlns:ns') +myns diff --git a/mysql-test/t/cast.test b/mysql-test/t/cast.test index 533da542855..b733a23f398 100644 --- a/mysql-test/t/cast.test +++ b/mysql-test/t/cast.test @@ -47,7 +47,7 @@ select cast('1a' as signed); select cast('' as signed); # -# Character set convertion +# Character set conversion # set names binary; select cast(_latin1'test' as char character set latin2); diff --git a/mysql-test/t/information_schema_inno.test b/mysql-test/t/information_schema_inno.test index 9cd64a54ad9..195bf57a880 100644 --- a/mysql-test/t/information_schema_inno.test +++ b/mysql-test/t/information_schema_inno.test @@ -21,3 +21,35 @@ select * from information_schema.KEY_COLUMN_USAGE where TABLE_SCHEMA= "test"; drop table t3, t2, t1; + +# +# Test for REFERENTIAL_CONSTRAINTS table +# +CREATE TABLE t1(a1 INT NOT NULL, a2 INT NOT NULL, + PRIMARY KEY(a1, a2)) ENGINE=INNODB; +CREATE TABLE t2(b1 INT, b2 INT, INDEX (b1, b2), + CONSTRAINT A1 + FOREIGN KEY (b1, b2) REFERENCES t1(a1, a2) + ON UPDATE CASCADE ON DELETE NO ACTION) ENGINE=INNODB; +CREATE TABLE t3(b1 INT, b2 INT, INDEX (b1, b2), + CONSTRAINT A2 + FOREIGN KEY (b1, b2) REFERENCES t2(b1, b2) + ON UPDATE SET NULL ON DELETE RESTRICT) ENGINE=INNODB; +CREATE TABLE t4(b1 INT, b2 INT, INDEX (b1, b2), + CONSTRAINT A3 + FOREIGN KEY (b1, b2) REFERENCES t3(b1, b2) + ON UPDATE NO ACTION ON DELETE SET NULL) ENGINE=INNODB; +CREATE TABLE t5(b1 INT, b2 INT, INDEX (b1, b2), + CONSTRAINT A4 + FOREIGN KEY (b1, b2) REFERENCES t4(b1, b2) + ON UPDATE RESTRICT ON DELETE CASCADE) ENGINE=INNODB; + + +select a.CONSTRAINT_SCHEMA, b.TABLE_NAME, CONSTRAINT_TYPE, + b.CONSTRAINT_NAME, UNIQUE_CONSTRAINT_SCHEMA, UNIQUE_CONSTRAINT_NAME, + MATCH_OPTION, UPDATE_RULE, DELETE_RULE +from information_schema.TABLE_CONSTRAINTS a, + information_schema.REFERENTIAL_CONSTRAINTS b +where a.CONSTRAINT_SCHEMA = 'test' and a.CONSTRAINT_SCHEMA = b.CONSTRAINT_SCHEMA and +a.CONSTRAINT_NAME = b.CONSTRAINT_NAME; +drop tables t5, t4, t3, t2, t1; diff --git a/mysql-test/t/mysql_client_test-master.opt b/mysql-test/t/mysql_client_test-master.opt new file mode 100644 index 00000000000..3711946168d --- /dev/null +++ b/mysql-test/t/mysql_client_test-master.opt @@ -0,0 +1 @@ +--log --log-output=FILE diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 9ffdb72ca22..639a576fb35 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -1110,4 +1110,40 @@ select * from t1 order by 1+1; drop table t1; +# +# Bug#19308 "REPAIR/OPTIMIZE/ANALYZE supported in SP but not in PS". +# Add test coverage for the added commands. +# +create table t1 (a int); +create table t2 like t1; +create table t3 like t2; +prepare stmt from "repair table t1"; +execute stmt; +execute stmt; +prepare stmt from "optimize table t1"; +execute stmt; +execute stmt; +prepare stmt from "analyze table t1"; +execute stmt; +execute stmt; +prepare stmt from "repair table t1, t2, t3"; +execute stmt; +execute stmt; +prepare stmt from "optimize table t1, t2, t3"; +execute stmt; +execute stmt; +prepare stmt from "analyze table t1, t2, t3"; +execute stmt; +execute stmt; +prepare stmt from "repair table t1, t4, t3"; +execute stmt; +execute stmt; +prepare stmt from "optimize table t1, t3, t4"; +execute stmt; +execute stmt; +prepare stmt from "analyze table t4, t1"; +execute stmt; +execute stmt; +deallocate prepare stmt; + # End of 5.0 tests diff --git a/mysql-test/t/ps_1general.test b/mysql-test/t/ps_1general.test index 6b168711de8..f61a7820afe 100644 --- a/mysql-test/t/ps_1general.test +++ b/mysql-test/t/ps_1general.test @@ -456,13 +456,10 @@ into table t1 fields terminated by ''\t'' '; prepare stmt1 from ' select * into outfile ''data.txt'' from t1 '; execute stmt1 ; ## ---error 1295 prepare stmt1 from ' optimize table t1 ' ; ---error 1295 prepare stmt1 from ' analyze table t1 ' ; --error 1295 prepare stmt1 from ' checksum table t1 ' ; ---error 1295 prepare stmt1 from ' repair table t1 ' ; --error 1295 prepare stmt1 from ' restore table t1 from ''data.txt'' ' ; diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test index 7596a7ba708..e2ff20e6ecc 100644 --- a/mysql-test/t/query_cache.test +++ b/mysql-test/t/query_cache.test @@ -207,7 +207,7 @@ show status like "Qcache_queries_in_cache"; drop table t1; # -# Charset convertion (cp1251_koi8 always present) +# Charset conversion (cp1251_koi8 always present) # create table t1 (a char(1) not null collate koi8r_general_ci); insert into t1 values(_koi8r"á"); diff --git a/mysql-test/t/timezone2.test b/mysql-test/t/timezone2.test index 069c19341e4..bfc909d6995 100644 --- a/mysql-test/t/timezone2.test +++ b/mysql-test/t/timezone2.test @@ -3,6 +3,7 @@ # Preparing playground --disable_warnings drop table if exists t1, t2; +drop function if exists f1; --enable_warnings @@ -222,3 +223,22 @@ select * from t1; drop table t1; # End of 4.1 tests + +# +# Test for bug #11081 "Using a CONVERT_TZ function in a stored function +# or trigger fails". +# +create table t1 (ldt datetime, udt datetime); +create function f1(i datetime) returns datetime + return convert_tz(i, 'UTC', 'Europe/Moscow'); +create trigger t1_bi before insert on t1 for each row + set new.udt:= convert_tz(new.ldt, 'Europe/Moscow', 'UTC'); +# This should work without errors +insert into t1 (ldt) values ('2006-04-19 16:30:00'); +select * from t1; +# This should work without errors as well +select ldt, f1(udt) as ldt2 from t1; +drop table t1; +drop function f1; + +# End of 5.0 tests diff --git a/mysql-test/t/xml.test b/mysql-test/t/xml.test index 8a12dbca51d..65d0a40291f 100644 --- a/mysql-test/t/xml.test +++ b/mysql-test/t/xml.test @@ -335,3 +335,11 @@ END// DELIMITER ;// CALL p2(); DROP PROCEDURE p2; + +# +# Bug#18170: XML: ExtractValue(): +# XPath expression can't use QNames (colon in names) +# +select extractValue('','count(ns:element)'); +select extractValue('a','/ns:element'); +select extractValue('a','/ns:element/@xmlns:ns'); diff --git a/mysys/Makefile.am b/mysys/Makefile.am index 7a66164b775..abc5cc142f5 100644 --- a/mysys/Makefile.am +++ b/mysys/Makefile.am @@ -53,6 +53,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \ charset.c charset-def.c my_bitmap.c my_bit.c md5.c \ my_gethostbyname.c rijndael.c my_aes.c sha1.c \ my_handler.c my_netware.c my_largepage.c \ + my_memmem.c \ my_windac.c my_access.c base64.c EXTRA_DIST = thr_alarm.c thr_lock.c my_pthread.c my_thr_init.c \ thr_mutex.c thr_rwlock.c \ diff --git a/mysys/cmakelists.txt b/mysys/cmakelists.txt index 5a3b8f1657e..4aa99a70121 100644 --- a/mysys/cmakelists.txt +++ b/mysys/cmakelists.txt @@ -26,4 +26,4 @@ ADD_LIBRARY(mysys array.c charset-def.c charset.c checksum.c default.c default_m my_static.c my_symlink.c my_symlink2.c my_sync.c my_thr_init.c my_wincond.c my_windac.c my_winsem.c my_winthread.c my_write.c ptr_cmp.c queues.c rijndael.c safemalloc.c sha1.c string.c thr_alarm.c thr_lock.c thr_mutex.c - thr_rwlock.c tree.c typelib.c my_vle.c base64.c) + thr_rwlock.c tree.c typelib.c my_vle.c base64.c my_memmem.c) diff --git a/mysys/mf_iocache2.c b/mysys/mf_iocache2.c index e181ccfb88d..f1ea21c2a47 100644 --- a/mysys/mf_iocache2.c +++ b/mysys/mf_iocache2.c @@ -252,37 +252,89 @@ uint my_b_printf(IO_CACHE *info, const char* fmt, ...) uint my_b_vprintf(IO_CACHE *info, const char* fmt, va_list args) { uint out_length=0; + uint minimum_width; /* as yet unimplemented */ + uint minimum_width_sign; + uint precision; /* as yet unimplemented for anything but %b */ - for (; *fmt ; fmt++) + /* + Store the location of the beginning of a format directive, for the + case where we learn we shouldn't have been parsing a format string + at all, and we don't want to lose the flag/precision/width/size + information. + */ + const char* backtrack; + + for (; *fmt != '\0'; fmt++) { - if (*fmt++ != '%') + /* Copy everything until '%' or end of string */ + const char *start=fmt; + uint length; + + for (; (*fmt != '\0') && (*fmt != '%'); fmt++) ; + + length= (uint) (fmt - start); + out_length+=length; + if (my_b_write(info, start, length)) + goto err; + + if (*fmt == '\0') /* End of format */ { - /* Copy everything until '%' or end of string */ - const char *start=fmt-1; - uint length; - for (; *fmt && *fmt != '%' ; fmt++ ) ; - length= (uint) (fmt - start); - out_length+=length; - if (my_b_write(info, start, length)) - goto err; - if (!*fmt) /* End of format */ - { - return out_length; - } - fmt++; - /* Found one '%' */ + return out_length; } + + /* + By this point, *fmt must be a percent; Keep track of this location and + skip over the percent character. + */ + DBUG_ASSERT(*fmt == '%'); + backtrack= fmt; + fmt++; + + minimum_width= 0; + precision= 0; + minimum_width_sign= 1; /* Skip if max size is used (to be compatible with printf) */ - while (my_isdigit(&my_charset_latin1, *fmt) || *fmt == '.' || *fmt == '-') + while (*fmt == '-') { fmt++; minimum_width_sign= -1; } + if (*fmt == '*') { + precision= (int) va_arg(args, int); fmt++; + } else { + while (my_isdigit(&my_charset_latin1, *fmt)) { + minimum_width=(minimum_width * 10) + (*fmt - '0'); + fmt++; + } + } + minimum_width*= minimum_width_sign; + + if (*fmt == '.') { + fmt++; + if (*fmt == '*') { + precision= (int) va_arg(args, int); + fmt++; + } else { + while (my_isdigit(&my_charset_latin1, *fmt)) { + precision=(precision * 10) + (*fmt - '0'); + fmt++; + } + } + } + if (*fmt == 's') /* String parameter */ { reg2 char *par = va_arg(args, char *); uint length = (uint) strlen(par); + /* TODO: implement minimum width and precision */ out_length+=length; if (my_b_write(info, par, length)) goto err; } + else if (*fmt == 'b') /* Sized buffer parameter, only precision makes sense */ + { + char *par = va_arg(args, char *); + out_length+= precision; + if (my_b_write(info, par, precision)) + goto err; + } else if (*fmt == 'd' || *fmt == 'u') /* Integer parameter */ { register int iarg; @@ -317,9 +369,9 @@ uint my_b_vprintf(IO_CACHE *info, const char* fmt, va_list args) else { /* %% or unknown code */ - if (my_b_write(info, "%", 1)) - goto err; - out_length++; + if (my_b_write(info, backtrack, fmt-backtrack)) + goto err; + out_length+= fmt-backtrack; } } return out_length; diff --git a/mysys/my_memmem.c b/mysys/my_memmem.c new file mode 100644 index 00000000000..682a1314f09 --- /dev/null +++ b/mysys/my_memmem.c @@ -0,0 +1,66 @@ +#include "my_base.h" + +/* + my_memmem, port of a GNU extension. + + Returns a pointer to the beginning of the substring, needle, or NULL if the + substring is not found in haystack. +*/ +void *my_memmem(const void *haystack, size_t haystacklen, + const void *needle, size_t needlelen) +{ + const unsigned char *cursor; + const unsigned char *last_possible_needle_location = + (unsigned char *)haystack + haystacklen - needlelen; + + /* Easy answers */ + if (needlelen > haystacklen) return(NULL); + if (needle == NULL) return(NULL); + if (haystack == NULL) return(NULL); + if (needlelen == 0) return(NULL); + if (haystacklen == 0) return(NULL); + + for (cursor = haystack; cursor <= last_possible_needle_location; cursor++) { + if (memcmp(needle, cursor, needlelen) == 0) { + return((void *) cursor); + } + } + return(NULL); +} + + + +#ifdef MAIN +#include + +int main(int argc, char *argv[]) { + char haystack[10], needle[3]; + + memmove(haystack, "0123456789", 10); + + memmove(needle, "no", 2); + assert(my_memmem(haystack, 10, needle, 2) == NULL); + + memmove(needle, "345", 3); + assert(my_memmem(haystack, 10, needle, 3) != NULL); + + memmove(needle, "789", 3); + assert(my_memmem(haystack, 10, needle, 3) != NULL); + assert(my_memmem(haystack, 9, needle, 3) == NULL); + + memmove(needle, "012", 3); + assert(my_memmem(haystack, 10, needle, 3) != NULL); + assert(my_memmem(NULL, 10, needle, 3) == NULL); + + assert(my_memmem(NULL, 10, needle, 3) == NULL); + assert(my_memmem(haystack, 0, needle, 3) == NULL); + assert(my_memmem(haystack, 10, NULL, 3) == NULL); + assert(my_memmem(haystack, 10, needle, 0) == NULL); + + assert(my_memmem(haystack, 1, needle, 3) == NULL); + + printf("success\n"); + return(0); +} + +#endif diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index ad7c5fd2c0e..3f606cd0b15 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -5832,34 +5832,55 @@ ha_innobase::get_foreign_key_list(THD *thd, List *f_key_list) break; } - ulong length= 0; - if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE) { - length=17; - tmp_buff= "ON DELETE CASCADE"; - } - else if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL) { - length=18; - tmp_buff= "ON DELETE SET NULL"; - } - else if (foreign->type == DICT_FOREIGN_ON_DELETE_NO_ACTION) { - length=19; - tmp_buff= "ON DELETE NO ACTION"; - } - else if (foreign->type == DICT_FOREIGN_ON_UPDATE_CASCADE) { - length=17; - tmp_buff= "ON UPDATE CASCADE"; - } - else if (foreign->type == DICT_FOREIGN_ON_UPDATE_SET_NULL) { - length=18; - tmp_buff= "ON UPDATE SET NULL"; - } - else if (foreign->type == DICT_FOREIGN_ON_UPDATE_NO_ACTION) { - length=19; - tmp_buff= "ON UPDATE NO ACTION"; - } - f_key_info.constraint_method= make_lex_string(thd, - f_key_info.constraint_method, - tmp_buff, length, 1); + ulong length; + if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) + { + length=7; + tmp_buff= "CASCADE"; + } + else if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) + { + length=8; + tmp_buff= "SET NULL"; + } + else if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) + { + length=9; + tmp_buff= "NO ACTION"; + } + else + { + length=8; + tmp_buff= "RESTRICT"; + } + f_key_info.delete_method= make_lex_string(thd, f_key_info.delete_method, + tmp_buff, length, 1); + + + if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) + { + length=7; + tmp_buff= "CASCADE"; + } + else if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) + { + length=8; + tmp_buff= "SET NULL"; + } + else if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) + { + length=9; + tmp_buff= "NO ACTION"; + } + else + { + length=8; + tmp_buff= "RESTRICT"; + } + f_key_info.update_method= make_lex_string(thd, f_key_info.update_method, + tmp_buff, length, 1); + + FOREIGN_KEY_INFO *pf_key_info= ((FOREIGN_KEY_INFO *) thd->memdup((gptr) &f_key_info, diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index a245e3b1b33..17e8db90dc7 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -2280,6 +2280,30 @@ static int my_xpath_parse_Number(MY_XPATH *xpath) } +/* + QName grammar can be found in a separate document + http://www.w3.org/TR/REC-xml-names/#NT-QName + + [6] QName ::= (Prefix ':')? LocalPart + [7] Prefix ::= NCName + [8] LocalPart ::= NCName +*/ +static int +my_xpath_parse_QName(MY_XPATH *xpath) +{ + const char *beg; + if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT)) + return 0; + beg= xpath->prevtok.beg; + if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_COLON)) + return 1; /* Non qualified name */ + if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT)) + return 0; + xpath->prevtok.beg= beg; + return 1; +} + + /* Scan Variable reference @@ -2313,7 +2337,7 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath) static int my_xpath_parse_NodeTest_QName(MY_XPATH *xpath) { - if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT)) + if (!my_xpath_parse_QName(xpath)) return 0; DBUG_ASSERT(xpath->context); uint len= xpath->prevtok.end - xpath->prevtok.beg; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index bc398b3d20a..749a968a5af 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -177,6 +177,8 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; /* Characters shown for the command in 'show processlist' */ #define PROCESS_LIST_WIDTH 100 +/* Characters shown for the command in 'information_schema.processlist' */ +#define PROCESS_LIST_INFO_WIDTH 65535 /* Time handling defaults */ #define TIMESTAMP_MAX_YEAR 2038 diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ebf4b3fed66..b1b498b356d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1753,7 +1753,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (alloc_query(thd, packet, packet_length)) break; // fatal error is set char *packet_end= thd->query + thd->query_length; - general_log_print(thd, command, "%s", thd->query); + general_log_print(thd, command, "%.*b", thd->query_length, thd->query); DBUG_PRINT("query",("%-.4096s",thd->query)); if (!(specialflag & SPECIAL_NO_PRIOR)) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index e0cf9095a22..6d0a0f4799c 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1755,6 +1755,9 @@ static bool check_prepared_statement(Prepared_statement *stmt, case SQLCOM_CALL: case SQLCOM_CREATE_VIEW: case SQLCOM_DROP_VIEW: + case SQLCOM_REPAIR: + case SQLCOM_ANALYZE: + case SQLCOM_OPTIMIZE: break; default: diff --git a/sql/sql_show.cc b/sql/sql_show.cc index da5ed3e46f8..1c55a7dd26c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1553,15 +1553,11 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) TABLE *table= tables->table; CHARSET_INFO *cs= system_charset_info; char *user; - bool verbose; - ulong max_query_length; time_t now= time(0); DBUG_ENTER("fill_process_list"); user= thd->security_ctx->master_access & PROCESS_ACL ? NullS : thd->security_ctx->priv_user; - verbose= thd->lex->verbose; - max_query_length= PROCESS_LIST_WIDTH; VOID(pthread_mutex_lock(&LOCK_thread_count)); @@ -1645,7 +1641,8 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) if (tmp->query) { table->field[7]->store(tmp->query, - min(max_query_length, tmp->query_length), cs); + min(PROCESS_LIST_INFO_WIDTH, + tmp->query_length), cs); table->field[7]->set_notnull(); } @@ -4234,6 +4231,75 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond) } +/* + Fill and store records into I_S.referential_constraints table + + SYNOPSIS + get_referential_constraints_record() + thd thread handle + tables table list struct(processed table) + table I_S table + res 1 means the error during opening of the processed table + 0 means processed table is opened without error + base_name db name + file_name table name + + RETURN + 0 ok + # error +*/ + +static int +get_referential_constraints_record(THD *thd, struct st_table_list *tables, + TABLE *table, bool res, + const char *base_name, const char *file_name) +{ + CHARSET_INFO *cs= system_charset_info; + DBUG_ENTER("get_referential_constraints_record"); + + if (res) + { + if (!tables->view) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + thd->net.last_errno, thd->net.last_error); + thd->clear_error(); + DBUG_RETURN(0); + } + if (!tables->view) + { + List f_key_list; + TABLE *show_table= tables->table; + show_table->file->info(HA_STATUS_VARIABLE | + HA_STATUS_NO_LOCK | + HA_STATUS_TIME); + + show_table->file->get_foreign_key_list(thd, &f_key_list); + FOREIGN_KEY_INFO *f_key_info; + List_iterator_fast it(f_key_list); + while ((f_key_info= it++)) + { + restore_record(table, s->default_values); + table->field[1]->store(base_name, strlen(base_name), cs); + table->field[9]->store(file_name, strlen(file_name), cs); + table->field[2]->store(f_key_info->forein_id->str, + f_key_info->forein_id->length, cs); + table->field[4]->store(f_key_info->referenced_db->str, + f_key_info->referenced_db->length, cs); + table->field[5]->store(f_key_info->referenced_table->str, + f_key_info->referenced_table->length, cs); + table->field[6]->store(STRING_WITH_LEN("NONE"), cs); + table->field[7]->store(f_key_info->update_method->str, + f_key_info->update_method->length, cs); + table->field[8]->store(f_key_info->delete_method->str, + f_key_info->delete_method->length, cs); + if (schema_table_store_record(thd, table)) + DBUG_RETURN(1); + } + } + DBUG_RETURN(0); +} + + /* Find schema_tables elment by name @@ -5099,9 +5165,9 @@ ST_FIELD_INFO processlist_fields_info[]= {"HOST", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Host"}, {"DB", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, "Db"}, {"COMMAND", 16, MYSQL_TYPE_STRING, 0, 0, "Command"}, - {"TIME", 4, MYSQL_TYPE_LONG, 0, 0, "Time"}, + {"TIME", 7, MYSQL_TYPE_LONG, 0, 0, "Time"}, {"STATE", 30, MYSQL_TYPE_STRING, 0, 1, "State"}, - {"INFO", PROCESS_LIST_WIDTH, MYSQL_TYPE_STRING, 0, 1, "Info"}, + {"INFO", PROCESS_LIST_INFO_WIDTH, MYSQL_TYPE_STRING, 0, 1, "Info"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -5163,6 +5229,22 @@ ST_FIELD_INFO files_fields_info[]= {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; +ST_FIELD_INFO referential_constraints_fields_info[]= +{ + {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, + {"CONSTRAINT_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"CONSTRAINT_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"UNIQUE_CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, + {"UNIQUE_CONSTRAINT_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"UNIQUE_CONSTRAINT_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"MATCH_OPTION", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"UPDATE_RULE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"DELETE_RULE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} +}; + + /* Description of ST_FIELD_INFO in table.h @@ -5198,6 +5280,9 @@ ST_SCHEMA_TABLE schema_tables[]= fill_plugins, make_old_format, 0, -1, -1, 0}, {"PROCESSLIST", processlist_fields_info, create_schema_table, fill_schema_processlist, make_old_format, 0, -1, -1, 0}, + {"REFERENTIAL_CONSTRAINTS", referential_constraints_fields_info, + create_schema_table, get_all_tables, 0, get_referential_constraints_record, + 1, 9, 0}, {"ROUTINES", proc_fields_info, create_schema_table, fill_schema_proc, make_proc_old_format, 0, -1, -1, 0}, {"SCHEMATA", schema_fields_info, create_schema_table, diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 79228be8a76..19ee9f259dc 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -331,7 +331,7 @@ bool String::set_or_copy_aligned(const char *str,uint32 arg_length, return copy_aligned(str, arg_length, offset, cs); } - /* Copy with charset convertion */ + /* Copy with charset conversion */ bool String::copy(const char *str, uint32 arg_length, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs, uint *errors) diff --git a/sql/table.h b/sql/table.h index aec9a7115e6..85d49444b29 100644 --- a/sql/table.h +++ b/sql/table.h @@ -335,7 +335,8 @@ typedef struct st_foreign_key_info LEX_STRING *forein_id; LEX_STRING *referenced_db; LEX_STRING *referenced_table; - LEX_STRING *constraint_method; + LEX_STRING *update_method; + LEX_STRING *delete_method; List foreign_fields; List referenced_fields; } FOREIGN_KEY_INFO; @@ -359,6 +360,7 @@ enum enum_schema_tables SCH_PARTITIONS, SCH_PLUGINS, SCH_PROCESSLIST, + SCH_REFERENTIAL_CONSTRAINTS, SCH_PROCEDURES, SCH_SCHEMATA, SCH_SCHEMA_PRIVILEGES, diff --git a/sql/tztime.cc b/sql/tztime.cc index 1be2321200b..228a8cd9b92 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1382,11 +1382,30 @@ static LS_INFO *tz_lsis= 0; static bool time_zone_tables_exist= 1; -typedef struct st_tz_names_entry: public Sql_alloc +/* + Names of tables (with their lengths) that are needed + for dynamical loading of time zone descriptions. +*/ + +static const LEX_STRING tz_tables_names[MY_TZ_TABLES_COUNT]= { + {(char *) STRING_WITH_LEN("time_zone_name")}, + {(char *) STRING_WITH_LEN("time_zone")}, + {(char *) STRING_WITH_LEN("time_zone_transition_type")}, + {(char *) STRING_WITH_LEN("time_zone_transition")} +}; + +/* Name of database to which those tables belong. */ + +static const LEX_STRING tz_tables_db_name= {(char *) STRING_WITH_LEN("mysql")}; + + +class Tz_names_entry: public Sql_alloc +{ +public: String name; Time_zone *tz; -} TZ_NAMES_ENTRY; +}; /* @@ -1394,7 +1413,7 @@ typedef struct st_tz_names_entry: public Sql_alloc they should obey C calling conventions. */ -extern "C" byte* my_tz_names_get_key(TZ_NAMES_ENTRY *entry, uint *length, +extern "C" byte* my_tz_names_get_key(Tz_names_entry *entry, uint *length, my_bool not_used __attribute__((unused))) { *length= entry->name.length(); @@ -1415,7 +1434,8 @@ extern "C" byte* my_offset_tzs_get_key(Time_zone_offset *entry, uint *length, SYNOPSIS tz_init_table_list() - tz_tabs - pointer to preallocated array of 4 TABLE_LIST objects + tz_tabs - pointer to preallocated array of MY_TZ_TABLES_COUNT + TABLE_LIST objects global_next_ptr - pointer to variable which points to global_next member of last element of global table list (or list root then list is empty) (in/out). @@ -1430,27 +1450,27 @@ extern "C" byte* my_offset_tzs_get_key(Time_zone_offset *entry, uint *length, static void tz_init_table_list(TABLE_LIST *tz_tabs, TABLE_LIST ***global_next_ptr) { - bzero(tz_tabs, sizeof(TABLE_LIST) * 4); - tz_tabs[0].alias= tz_tabs[0].table_name= (char*)"time_zone_name"; - tz_tabs[1].alias= tz_tabs[1].table_name= (char*)"time_zone"; - tz_tabs[2].alias= tz_tabs[2].table_name= (char*)"time_zone_transition_type"; - tz_tabs[3].alias= tz_tabs[3].table_name= (char*)"time_zone_transition"; - tz_tabs[0].next_global= tz_tabs[0].next_local= tz_tabs+1; - tz_tabs[1].next_global= tz_tabs[1].next_local= tz_tabs+2; - tz_tabs[2].next_global= tz_tabs[2].next_local= tz_tabs+3; - tz_tabs[0].lock_type= tz_tabs[1].lock_type= tz_tabs[2].lock_type= - tz_tabs[3].lock_type= TL_READ; - tz_tabs[0].db= tz_tabs[1].db= tz_tabs[2].db= tz_tabs[3].db= (char *)"mysql"; + bzero(tz_tabs, sizeof(TABLE_LIST) * MY_TZ_TABLES_COUNT); + + for (int i= 0; i < MY_TZ_TABLES_COUNT; i++) + { + tz_tabs[i].alias= tz_tabs[i].table_name= tz_tables_names[i].str; + tz_tabs[i].table_name_length= tz_tables_names[i].length; + tz_tabs[i].db= tz_tables_db_name.str; + tz_tabs[i].db_length= tz_tables_db_name.length; + tz_tabs[i].lock_type= TL_READ; + + if (i != MY_TZ_TABLES_COUNT - 1) + tz_tabs[i].next_global= tz_tabs[i].next_local= &tz_tabs[i+1]; + if (i != 0) + tz_tabs[i].prev_global= &tz_tabs[i-1].next_global; + } /* Link into global list */ tz_tabs[0].prev_global= *global_next_ptr; - tz_tabs[1].prev_global= &tz_tabs[0].next_global; - tz_tabs[2].prev_global= &tz_tabs[1].next_global; - tz_tabs[3].prev_global= &tz_tabs[2].next_global; - **global_next_ptr= tz_tabs; /* Update last-global-pointer to point to pointer in last table */ - *global_next_ptr= &tz_tabs[3].next_global; + *global_next_ptr= &tz_tabs[MY_TZ_TABLES_COUNT-1].next_global; } @@ -1479,7 +1499,8 @@ TABLE_LIST fake_time_zone_tables_list; NOTE my_tz_check_n_skip_implicit_tables() function depends on fact that - elements of list created are allocated as TABLE_LIST[4] array. + elements of list created are allocated as TABLE_LIST[MY_TZ_TABLES_COUNT] + array. RETURN VALUES Returns pointer to first TABLE_LIST object, (could be 0 if time zone @@ -1495,7 +1516,8 @@ my_tz_get_table_list(THD *thd, TABLE_LIST ***global_next_ptr) if (!time_zone_tables_exist) DBUG_RETURN(0); - if (!(tz_tabs= (TABLE_LIST *)thd->alloc(sizeof(TABLE_LIST) * 4))) + if (!(tz_tabs= (TABLE_LIST *)thd->alloc(sizeof(TABLE_LIST) * + MY_TZ_TABLES_COUNT))) DBUG_RETURN(&fake_time_zone_tables_list); tz_init_table_list(tz_tabs, global_next_ptr); @@ -1534,9 +1556,9 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) { THD *thd; TABLE_LIST *tables= 0; - TABLE_LIST tables_buff[5], **last_global_next_ptr; + TABLE_LIST tables_buff[1+MY_TZ_TABLES_COUNT], **last_global_next_ptr; TABLE *table; - TZ_NAMES_ENTRY *tmp_tzname; + Tz_names_entry *tmp_tzname; my_bool return_val= 1; int res; DBUG_ENTER("my_tz_init"); @@ -1568,7 +1590,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) tz_inited= 1; /* Add 'SYSTEM' time zone to tz_names hash */ - if (!(tmp_tzname= new (&tz_storage) TZ_NAMES_ENTRY())) + if (!(tmp_tzname= new (&tz_storage) Tz_names_entry())) { sql_print_error("Fatal error: OOM while initializing time zones"); goto end_with_cleanup; @@ -1764,7 +1786,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) { TABLE *table= 0; TIME_ZONE_INFO *tz_info; - TZ_NAMES_ENTRY *tmp_tzname; + Tz_names_entry *tmp_tzname; Time_zone *return_val= 0; int res; uint tzid, ttid; @@ -2039,7 +2061,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) } - if (!(tmp_tzname= new (&tz_storage) TZ_NAMES_ENTRY()) || + if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()) || !(tmp_tzname->tz= new (&tz_storage) Time_zone_db(tz_info, &(tmp_tzname->name))) || (tmp_tzname->name.set(tz_name_buff, tz_name->length(), @@ -2186,7 +2208,7 @@ str_to_offset(const char *str, uint length, long *offset) Time_zone * my_tz_find(const String * name, TABLE_LIST *tz_tables) { - TZ_NAMES_ENTRY *tmp_tzname; + Tz_names_entry *tmp_tzname; Time_zone *result_tz= 0; long offset; DBUG_ENTER("my_tz_find"); @@ -2221,7 +2243,7 @@ my_tz_find(const String * name, TABLE_LIST *tz_tables) else { result_tz= 0; - if ((tmp_tzname= (TZ_NAMES_ENTRY *)hash_search(&tz_names, + if ((tmp_tzname= (Tz_names_entry *)hash_search(&tz_names, (const byte *)name->ptr(), name->length()))) result_tz= tmp_tzname->tz; @@ -2273,7 +2295,7 @@ Time_zone *my_tz_find_with_opening_tz_tables(THD *thd, const String *name) our time zone tables. Note that if we don't have tz tables on this slave, we don't even try. */ - TABLE_LIST tables[4]; + TABLE_LIST tables[MY_TZ_TABLES_COUNT]; TABLE_LIST *dummy; TABLE_LIST **dummyp= &dummy; tz_init_table_list(tables, &dummyp); diff --git a/sql/tztime.h b/sql/tztime.h index 42e50988e52..95184c9b3d1 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -69,6 +69,15 @@ extern my_time_t sec_since_epoch_TIME(TIME *t); extern TABLE_LIST fake_time_zone_tables_list; +/* + Number of elements in table list produced by my_tz_get_table_list() + (this table list contains tables which are needed for dynamical loading + of time zone descriptions). Actually it is imlementation detail that + should not be used anywhere outside of tztime.h and tztime.cc. +*/ + +static const int MY_TZ_TABLES_COUNT= 4; + /* Check if we have pointer to the begining of list of implicitly used time zone tables, set SELECT_ACL for them and fast-forward to its end. @@ -91,9 +100,9 @@ inline bool my_tz_check_n_skip_implicit_tables(TABLE_LIST **table, { if (*table == tz_tables) { - for (int i= 0; i < 4; i++) + for (int i= 0; i < MY_TZ_TABLES_COUNT; i++) (*table)[i].grant.privilege= SELECT_ACL; - (*table)+= 3; + (*table)+= MY_TZ_TABLES_COUNT - 1; return TRUE; } return FALSE; diff --git a/strings/CHARSET_INFO.txt b/strings/CHARSET_INFO.txt index f7a10f95880..40f171440a4 100644 --- a/strings/CHARSET_INFO.txt +++ b/strings/CHARSET_INFO.txt @@ -172,7 +172,7 @@ mb_wc - converts the left multibyte sequence into it Unicode code. mc_mb - converts the given Unicode code into multibyte sequence. -Case and sort convertion +Case and sort conversion ------------------------ caseup_str - converts the given 0-terminated string into the upper case casedn_str - converts the given 0-terminated string into the lower case @@ -227,4 +227,4 @@ hash_sort() - calculates hash value taking in account the collation rules, e.g. case-insensitivity, accent sensitivity, etc. - \ No newline at end of file + diff --git a/strings/my_vsnprintf.c b/strings/my_vsnprintf.c index 0e036c2bbcd..e4302f50c58 100644 --- a/strings/my_vsnprintf.c +++ b/strings/my_vsnprintf.c @@ -27,6 +27,7 @@ %#[l]d %#[l]u %#[l]x + %#.#b Local format; note first # is ignored and second is REQUIRED %#.#s Note first # is ignored RETURN @@ -40,7 +41,7 @@ int my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap) for (; *fmt ; fmt++) { - if (fmt[0] != '%') + if (*fmt != '%') { if (to == end) /* End of buffer */ break; @@ -95,6 +96,16 @@ int my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap) to=strnmov(to,par,plen); continue; } + else if (*fmt == 'b') /* Buffer parameter */ + { + char *par = va_arg(ap, char *); + DBUG_ASSERT(to <= end); + if (to + abs(width) + 1 > end) + width= end - to - 1; /* sign doesn't matter */ + memmove(to, par, abs(width)); + to+= width; + continue; + } else if (*fmt == 'd' || *fmt == 'u'|| *fmt== 'x') /* Integer parameter */ { register long larg; diff --git a/tests/Makefile.am b/tests/Makefile.am index ba0a7ad73d0..4cad3c30bdd 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -43,7 +43,7 @@ INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ LIBS = @CLIENT_LIBS@ LDADD = @CLIENT_EXTRA_LDFLAGS@ \ $(top_builddir)/libmysql/libmysqlclient.la -mysql_client_test_LDADD= $(LDADD) $(CXXLDFLAGS) +mysql_client_test_LDADD= $(LDADD) $(CXXLDFLAGS) -L../mysys -lmysys mysql_client_test_SOURCES= mysql_client_test.c $(yassl_dummy_link_fix) insert_test_SOURCES= insert_test.c $(yassl_dummy_link_fix) select_test_SOURCES= select_test.c $(yassl_dummy_link_fix) diff --git a/tests/cmakelists.txt b/tests/cmakelists.txt index c9b0b8735a2..46c42d461f3 100644 --- a/tests/cmakelists.txt +++ b/tests/cmakelists.txt @@ -6,4 +6,4 @@ ADD_DEFINITIONS("-DMYSQL_CLIENT") INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) ADD_EXECUTABLE(mysql_client_test mysql_client_test.c) -TARGET_LINK_LIBRARIES(mysql_client_test dbug mysqlclient yassl taocrypt zlib wsock32) +TARGET_LINK_LIBRARIES(mysql_client_test dbug mysys mysqlclient yassl taocrypt zlib wsock32) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 5730efced35..d927b27ab37 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -14839,6 +14839,73 @@ static void test_bug15613() myquery(rc); mysql_stmt_close(stmt); } + +/* + Bug#17667: An attacker has the opportunity to bypass query logging. +*/ +static void test_bug17667() +{ + int rc; + struct buffer_and_length { + const char *buffer; + const uint length; + } statements[]= { + { "drop table if exists bug17667", 29 }, + { "create table bug17667 (c varchar(20))", 37 }, + { "insert into bug17667 (c) values ('regular') /* NUL=\0 with comment */", 68 }, + { "insert into bug17667 (c) values ('NUL=\0 in value')", 50 }, + { "insert into bug17667 (c) values ('5 NULs=\0\0\0\0\0')", 48 }, + { "/* NUL=\0 with comment */ insert into bug17667 (c) values ('encore')", 67 }, + { "drop table bug17667", 19 }, + { NULL, 0 } }; + + struct buffer_and_length *statement_cursor; + FILE *log_file; + + myheader("test_bug17667"); + + for (statement_cursor= statements; statement_cursor->buffer != NULL; + statement_cursor++) { + rc= mysql_real_query(mysql, statement_cursor->buffer, + statement_cursor->length); + myquery(rc); + } + + sleep(1); /* The server may need time to flush the data to the log. */ + log_file= fopen("var/log/master.log", "r"); + if (log_file != NULL) { + + for (statement_cursor= statements; statement_cursor->buffer != NULL; + statement_cursor++) { + char line_buffer[MAX_TEST_QUERY_LENGTH*2]; + /* more than enough room for the query and some marginalia. */ + + do { + memset(line_buffer, '/', MAX_TEST_QUERY_LENGTH*2); + + DIE_UNLESS(fgets(line_buffer, MAX_TEST_QUERY_LENGTH*2, log_file) != + NULL); + /* If we reach EOF before finishing the statement list, then we failed. */ + + } while (my_memmem(line_buffer, MAX_TEST_QUERY_LENGTH*2, + statement_cursor->buffer, statement_cursor->length) == NULL); + } + + printf("success. All queries found intact in the log.\n"); + + } else { + fprintf(stderr, "Could not find the log file, var/log/master.log, so " + "test_bug17667 is \ninconclusive. Run test from the " + "mysql-test/mysql-test-run* program \nto set up the correct " + "environment for this test.\n\n"); + } + + if (log_file != NULL) + fclose(log_file); + +} + + /* Bug#14169: type of group_concat() result changed to blob if tmp_table was used */ @@ -15139,6 +15206,7 @@ static struct my_tests_st my_tests[]= { { "test_bug16144", test_bug16144 }, { "test_bug15613", test_bug15613 }, { "test_bug14169", test_bug14169 }, + { "test_bug17667", test_bug17667 }, { 0, 0 } };