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 }
};