summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BitKeeper/etc/logging_ok2
-rw-r--r--Makefile.am5
-rw-r--r--client/mysqladmin.cc2
-rw-r--r--config/ac-macros/misc.m44
-rw-r--r--configure.in3
-rw-r--r--include/config-win.h17
-rw-r--r--include/my_global.h2
-rw-r--r--mysql-test/r/bigint.result17
-rw-r--r--mysql-test/r/cast.result73
-rw-r--r--mysql-test/r/innodb-replace.result1
-rw-r--r--mysql-test/r/user_var.result41
-rw-r--r--mysql-test/t/bigint.test10
-rw-r--r--mysql-test/t/cast.test26
-rw-r--r--mysql-test/t/innodb-replace.test4
-rw-r--r--mysql-test/t/user_var.test26
-rw-r--r--mysys/default.c135
-rw-r--r--mysys/mf_keycache.c11
-rw-r--r--sql/field.h2
-rw-r--r--sql/handler.cc7
-rw-r--r--sql/hostname.cc9
-rw-r--r--sql/item.h7
-rw-r--r--sql/item_func.cc88
-rw-r--r--sql/item_func.h10
-rw-r--r--sql/protocol.cc2
-rw-r--r--sql/sql_insert.cc4
-rw-r--r--sql/sql_prepare.cc2
-rw-r--r--sql/sql_select.cc746
-rw-r--r--sql/sql_select.h18
-rw-r--r--sql/unireg.cc7
-rwxr-xr-xtests/index_corrupt.pl212
-rw-r--r--tests/mysql_client_test.c61
31 files changed, 1100 insertions, 454 deletions
diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok
index e706a3358d5..346dcae3cfd 100644
--- a/BitKeeper/etc/logging_ok
+++ b/BitKeeper/etc/logging_ok
@@ -50,6 +50,8 @@ dlenev@build.mysql.com
dlenev@jabberwock.localdomain
dlenev@mysql.com
ejonore@mc03.ndb.mysql.com
+evgen@moonbone.(none)
+evgen@moonbone.local
gbichot@production.mysql.com
gbichot@quadita2.mysql.com
gbichot@quadxeon.mysql.com
diff --git a/Makefile.am b/Makefile.am
index 29a52ff9bfc..eec27484710 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -102,5 +102,10 @@ tags:
test:
cd mysql-test; ./mysql-test-run && ./mysql-test-run --ps-protocol
+test-force:
+ cd mysql-test; \
+ mysql-test-run --force ;\
+ mysql-test-run --ps-protocol --force
+
# Don't update the files from bitkeeper
%::SCCS/s.%
diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc
index 1eec9bc2555..32e0dfdb837 100644
--- a/client/mysqladmin.cc
+++ b/client/mysqladmin.cc
@@ -727,7 +727,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv)
void (*func) (MYSQL_RES*, MYSQL_ROW, uint);
new_line = 1;
- if (mysql_query(mysql, "show status") ||
+ if (mysql_query(mysql, "show /*!50002 GLOBAL */ status") ||
!(res = mysql_store_result(mysql)))
{
my_printf_error(0, "unable to show status; error: '%s'", MYF(ME_BELL),
diff --git a/config/ac-macros/misc.m4 b/config/ac-macros/misc.m4
index 201702e5379..d75dedafa2a 100644
--- a/config/ac-macros/misc.m4
+++ b/config/ac-macros/misc.m4
@@ -646,8 +646,8 @@ m4_define([_AC_PROG_CXX_EXIT_DECLARATION],
'void exit (int);' \
'#include <stdlib.h>'
do
- _AC_COMPILE_IFELSE([AC_LANG_PROGRAM([@%:@include <stdlib.h>
-$ac_declaration],
+ _AC_COMPILE_IFELSE([AC_LANG_PROGRAM([$ac_declaration
+@%:@include <stdlib.h>],
[exit (42);])],
[],
[continue])
diff --git a/configure.in b/configure.in
index 57f99c29410..8b0a0ddeb56 100644
--- a/configure.in
+++ b/configure.in
@@ -1805,6 +1805,9 @@ If you are using gcc 2.8.# you should upgrade to egcs 1.0.3 or newer and try
again]);
fi
fi
+AC_CHECK_TYPES([sigset_t, off_t], [], [], [#include <sys/types.h>])
+AC_CHECK_TYPES([size_t], [], [], [#include <stdio.h>])
+
MYSQL_PTHREAD_YIELD
######################################################################
diff --git a/include/config-win.h b/include/config-win.h
index bc392ce73d8..a4f81a0ec6a 100644
--- a/include/config-win.h
+++ b/include/config-win.h
@@ -106,20 +106,33 @@ functions */
/* Type information */
+#if defined(__EMX__) || !defined(HAVE_UINT)
+#undef HAVE_UINT
+#define HAVE_UINT
typedef unsigned short ushort;
typedef unsigned int uint;
+#endif /* defined(__EMX__) || !defined(HAVE_UINT) */
+
typedef unsigned __int64 ulonglong; /* Microsofts 64 bit types */
typedef __int64 longlong;
+#ifndef HAVE_SIGSET_T
typedef int sigset_t;
+#endif
#define longlong_defined
-/* off_t should not be __int64 because of conflicts in header files;
- Use my_off_t or os_off_t instead */
+/*
+ off_t should not be __int64 because of conflicts in header files;
+ Use my_off_t or os_off_t instead
+*/
+#ifndef HAVE_OFF_T
typedef long off_t;
+#endif
typedef __int64 os_off_t;
#ifdef _WIN64
typedef UINT_PTR rf_SetTimer;
#else
+#ifndef HAVE_SIZE_T
typedef unsigned int size_t;
+#endif
typedef uint rf_SetTimer;
#endif
diff --git a/include/my_global.h b/include/my_global.h
index 0353dc91fcf..55ec427e3e1 100644
--- a/include/my_global.h
+++ b/include/my_global.h
@@ -414,6 +414,8 @@ int __void__;
#endif
#if defined(__EMX__) || !defined(HAVE_UINT)
+#undef HAVE_UINT
+#define HAVE_UINT
typedef unsigned int uint;
typedef unsigned short ushort;
#endif
diff --git a/mysql-test/r/bigint.result b/mysql-test/r/bigint.result
index e372c307ba7..a0c8f317db2 100644
--- a/mysql-test/r/bigint.result
+++ b/mysql-test/r/bigint.result
@@ -128,3 +128,20 @@ t2.value64=t1.value64;
value64 value32 value64 value32
9223372036854775807 2 9223372036854775807 4
drop table t1, t2;
+create table t1 select 1 as 'a';
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` bigint(1) NOT NULL default '0'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+drop table t1;
+create table t1 select 9223372036854775809 as 'a';
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` bigint(19) unsigned NOT NULL default '0'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+select * from t1;
+a
+9223372036854775809
+drop table t1;
diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result
index feefd47c611..6dc608a9289 100644
--- a/mysql-test/r/cast.result
+++ b/mysql-test/r/cast.result
@@ -4,12 +4,11 @@ CAST(1-2 AS UNSIGNED)
select CAST(CAST(1-2 AS UNSIGNED) AS SIGNED INTEGER);
CAST(CAST(1-2 AS UNSIGNED) AS SIGNED INTEGER)
-1
-select CONVERT('-1',UNSIGNED);
-CONVERT('-1',UNSIGNED)
-18446744073709551615
select CAST('10 ' as unsigned integer);
CAST('10 ' as unsigned integer)
10
+Warnings:
+Warning 1292 Truncated incorrect INTEGER value: '10 '
select cast(-5 as unsigned) | 1, cast(-5 as unsigned) & -1;
cast(-5 as unsigned) | 1 cast(-5 as unsigned) & -1
18446744073709551611 18446744073709551611
@@ -100,6 +99,41 @@ select 10E+0+'a';
10
Warnings:
Warning 1292 Truncated incorrect DOUBLE value: 'a'
+select cast('18446744073709551616' as unsigned);
+cast('18446744073709551616' as unsigned)
+18446744073709551615
+Warnings:
+Warning 1292 Truncated incorrect INTEGER value: '18446744073709551616'
+select cast('18446744073709551616' as signed);
+cast('18446744073709551616' as signed)
+-1
+Warnings:
+Warning 1292 Truncated incorrect INTEGER value: '18446744073709551616'
+select cast('9223372036854775809' as signed);
+cast('9223372036854775809' as signed)
+-9223372036854775807
+Warnings:
+Warning 1105 Cast to signed converted positive out-of-range integer to it's negative complement
+select cast('-1' as unsigned);
+cast('-1' as unsigned)
+18446744073709551615
+Warnings:
+Warning 1105 Cast to unsigned converted negative integer to it's positive complement
+select cast('abc' as signed);
+cast('abc' as signed)
+0
+Warnings:
+Warning 1292 Truncated incorrect INTEGER value: 'abc'
+select cast('1a' as signed);
+cast('1a' as signed)
+1
+Warnings:
+Warning 1292 Truncated incorrect INTEGER value: '1a'
+select cast('' as signed);
+cast('' as signed)
+0
+Warnings:
+Warning 1292 Truncated incorrect INTEGER value: ''
set names binary;
select cast(_latin1'test' as char character set latin2);
cast(_latin1'test' as char character set latin2)
@@ -255,6 +289,39 @@ timediff(cast('2004-12-30 12:00:00' as time), '12:00:00')
select timediff(cast('1 12:00:00' as time), '12:00:00');
timediff(cast('1 12:00:00' as time), '12:00:00')
24:00:00
+select cast(18446744073709551615 as unsigned);
+cast(18446744073709551615 as unsigned)
+18446744073709551615
+select cast(18446744073709551615 as signed);
+cast(18446744073709551615 as signed)
+-1
+select cast('18446744073709551615' as unsigned);
+cast('18446744073709551615' as unsigned)
+18446744073709551615
+select cast('18446744073709551615' as signed);
+cast('18446744073709551615' as signed)
+-1
+Warnings:
+Warning 1105 Cast to signed converted positive out-of-range integer to it's negative complement
+select cast('9223372036854775807' as signed);
+cast('9223372036854775807' as signed)
+9223372036854775807
+select cast(concat('184467440','73709551615') as unsigned);
+cast(concat('184467440','73709551615') as unsigned)
+18446744073709551615
+select cast(concat('184467440','73709551615') as signed);
+cast(concat('184467440','73709551615') as signed)
+-1
+Warnings:
+Warning 1105 Cast to signed converted positive out-of-range integer to it's negative complement
+select cast(repeat('1',20) as unsigned);
+cast(repeat('1',20) as unsigned)
+11111111111111111111
+select cast(repeat('1',20) as signed);
+cast(repeat('1',20) as signed)
+-7335632962598440505
+Warnings:
+Warning 1105 Cast to signed converted positive out-of-range integer to it's negative complement
select cast('1.2' as decimal(3,2));
cast('1.2' as decimal(3,2))
1.20
diff --git a/mysql-test/r/innodb-replace.result b/mysql-test/r/innodb-replace.result
index a27806640ad..b7edcc49e56 100644
--- a/mysql-test/r/innodb-replace.result
+++ b/mysql-test/r/innodb-replace.result
@@ -1,3 +1,4 @@
+drop table if exists t1;
create table t1 (c1 char(5) unique not null, c2 int, stamp timestamp) engine=innodb;
select * from t1;
c1 c2 stamp
diff --git a/mysql-test/r/user_var.result b/mysql-test/r/user_var.result
index 68eae111111..d9a647ce2c3 100644
--- a/mysql-test/r/user_var.result
+++ b/mysql-test/r/user_var.result
@@ -182,3 +182,44 @@ coercibility(@v1) coercibility(@v2) coercibility(@v3) coercibility(@v4)
set session @honk=99;
set one_shot @honk=99;
ERROR HY000: The 'SET ONE_SHOT' syntax is reserved for purposes internal to the MySQL server
+set @first_var= NULL;
+create table t1 select @first_var;
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `@first_var` longblob
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+drop table t1;
+set @first_var= cast(NULL as signed integer);
+create table t1 select @first_var;
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `@first_var` bigint(20) default NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+drop table t1;
+set @first_var= NULL;
+create table t1 select @first_var;
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `@first_var` bigint(20) default NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+drop table t1;
+set @first_var= concat(NULL);
+create table t1 select @first_var;
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `@first_var` longblob
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+drop table t1;
+set @first_var=1;
+set @first_var= cast(NULL as CHAR);
+create table t1 select @first_var;
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `@first_var` longtext
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+drop table t1;
diff --git a/mysql-test/t/bigint.test b/mysql-test/t/bigint.test
index a26b78254e7..99c8a13d0b5 100644
--- a/mysql-test/t/bigint.test
+++ b/mysql-test/t/bigint.test
@@ -104,3 +104,13 @@ t2.value64=t1.value64;
drop table t1, t2;
+#
+# Test of CREATE ... SELECT and unsigned integers
+#
+create table t1 select 1 as 'a';
+show create table t1;
+drop table t1;
+create table t1 select 9223372036854775809 as 'a';
+show create table t1;
+select * from t1;
+drop table t1;
diff --git a/mysql-test/t/cast.test b/mysql-test/t/cast.test
index e7dd49394ee..cafecd6000d 100644
--- a/mysql-test/t/cast.test
+++ b/mysql-test/t/cast.test
@@ -4,7 +4,6 @@
select CAST(1-2 AS UNSIGNED);
select CAST(CAST(1-2 AS UNSIGNED) AS SIGNED INTEGER);
-select CONVERT('-1',UNSIGNED);
select CAST('10 ' as unsigned integer);
select cast(-5 as unsigned) | 1, cast(-5 as unsigned) & -1;
select cast(-5 as unsigned) -1, cast(-5 as unsigned) + 1;
@@ -34,6 +33,15 @@ select 10+'a';
select 10.0+cast('a' as decimal);
select 10E+0+'a';
+# out-of-range cases
+select cast('18446744073709551616' as unsigned);
+select cast('18446744073709551616' as signed);
+select cast('9223372036854775809' as signed);
+select cast('-1' as unsigned);
+select cast('abc' as signed);
+select cast('1a' as signed);
+select cast('' as signed);
+
#
# Character set convertion
#
@@ -132,6 +140,22 @@ select timediff(cast('2004-12-30 12:00:00' as time), '12:00:00');
# Still we should not throw away "days" part of time value
select timediff(cast('1 12:00:00' as time), '12:00:00');
+#
+# Bug #7036: Casting from string to unsigned would cap value of result at
+# maximum signed value instead of maximum unsigned value
+#
+select cast(18446744073709551615 as unsigned);
+select cast(18446744073709551615 as signed);
+select cast('18446744073709551615' as unsigned);
+select cast('18446744073709551615' as signed);
+select cast('9223372036854775807' as signed);
+
+select cast(concat('184467440','73709551615') as unsigned);
+select cast(concat('184467440','73709551615') as signed);
+
+select cast(repeat('1',20) as unsigned);
+select cast(repeat('1',20) as signed);
+
#decimal-related additions
select cast('1.2' as decimal(3,2));
select 1e18 * cast('1.2' as decimal(3,2));
diff --git a/mysql-test/t/innodb-replace.test b/mysql-test/t/innodb-replace.test
index e7e96da1443..516f058a68e 100644
--- a/mysql-test/t/innodb-replace.test
+++ b/mysql-test/t/innodb-replace.test
@@ -2,6 +2,10 @@
# embedded server ignores 'delayed', so skip this
-- source include/not_embedded.inc
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
#
# Bug #1078
#
diff --git a/mysql-test/t/user_var.test b/mysql-test/t/user_var.test
index ef360f2231d..b9d06558f34 100644
--- a/mysql-test/t/user_var.test
+++ b/mysql-test/t/user_var.test
@@ -119,3 +119,29 @@ select coercibility(@v1),coercibility(@v2),coercibility(@v3),coercibility(@v4);
set session @honk=99;
--error 1382
set one_shot @honk=99;
+
+#
+# Bug #6598: problem with cast(NULL as signed integer);
+#
+
+set @first_var= NULL;
+create table t1 select @first_var;
+show create table t1;
+drop table t1;
+set @first_var= cast(NULL as signed integer);
+create table t1 select @first_var;
+show create table t1;
+drop table t1;
+set @first_var= NULL;
+create table t1 select @first_var;
+show create table t1;
+drop table t1;
+set @first_var= concat(NULL);
+create table t1 select @first_var;
+show create table t1;
+drop table t1;
+set @first_var=1;
+set @first_var= cast(NULL as CHAR);
+create table t1 select @first_var;
+show create table t1;
+drop table t1;
diff --git a/mysys/default.c b/mysys/default.c
index 0f33c94d17e..aa2293ac0af 100644
--- a/mysys/default.c
+++ b/mysys/default.c
@@ -410,6 +410,56 @@ static int search_default_file(Process_option_func opt_handler,
/*
+ Skip over keyword and get argument after keyword
+
+ SYNOPSIS
+ get_argument()
+ keyword Include directive keyword
+ kwlen Length of keyword
+ ptr Pointer to the keword in the line under process
+ line line number
+
+ RETURN
+ 0 error
+ # Returns pointer to the argument after the keyword.
+*/
+
+static char *get_argument(const char *keyword, uint kwlen,
+ char *ptr, char *name, uint line)
+{
+ char *end;
+
+ /* Skip over "include / includedir keyword" and following whitespace */
+
+ for (ptr+= kwlen - 1;
+ my_isspace(&my_charset_latin1, ptr[0]);
+ ptr++)
+ {}
+
+ /*
+ Trim trailing whitespace from directory name
+ The -1 below is for the newline added by fgets()
+ Note that my_isspace() is true for \r and \n
+ */
+ for (end= ptr + strlen(ptr) - 1;
+ my_isspace(&my_charset_latin1, *(end - 1));
+ end--)
+ {}
+ end[0]= 0; /* Cut off end space */
+
+ /* Print error msg if there is nothing after !include* directive */
+ if (end <= ptr)
+ {
+ fprintf(stderr,
+ "error: Wrong '!%s' directive in config file: %s at line %d\n",
+ keyword, name, line);
+ return 0;
+ }
+ return ptr;
+}
+
+
+/*
Open a configuration file (if exists) and read given options from it
SYNOPSIS
@@ -497,40 +547,34 @@ static int search_default_file_with_ext(Process_option_func opt_handler,
continue;
/* Configuration File Directives */
- if ((*ptr == '!') && (recursion_level < max_recursion_level))
+ if ((*ptr == '!'))
{
+ if (recursion_level >= max_recursion_level)
+ {
+ for (end= ptr + strlen(ptr) - 1;
+ my_isspace(&my_charset_latin1, *(end - 1));
+ end--)
+ {}
+ end[0]= 0;
+ fprintf(stderr,
+ "Warning: skipping '%s' directive as maximum include"
+ "recursion level was reached in file %s at line %d\n",
+ ptr, name, line);
+ continue;
+ }
+
/* skip over `!' and following whitespace */
for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++)
{}
- if ((!strncmp(ptr, includedir_keyword, sizeof(includedir_keyword) - 1))
- && my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1]))
+ if ((!strncmp(ptr, includedir_keyword,
+ sizeof(includedir_keyword) - 1)) &&
+ my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1]))
{
- /* skip over "includedir" and following whitespace */
- for (ptr+= sizeof(includedir_keyword) - 1;
- my_isspace(&my_charset_latin1, ptr[0]); ptr++)
- {}
-
- /* trim trailing whitespace from directory name */
- end= ptr + strlen(ptr) - 1;
- /* fgets() stores the newline character in the buffer */
- if ((end[0] == '\n') || (end[0] == '\r') ||
- my_isspace(&my_charset_latin1, end[0]))
- {
- for (; my_isspace(&my_charset_latin1, *(end - 1)); end--)
- {}
- end[0]= 0;
- }
-
- /* print error msg if there is nothing after !includedir directive */
- if (end == ptr)
- {
- fprintf(stderr,
- "error: Wrong !includedir directive in config "
- "file: %s at line %d\n",
- name,line);
- goto err;
- }
+ if (!(ptr= get_argument(includedir_keyword,
+ sizeof(includedir_keyword),
+ ptr, name, line)))
+ goto err;
if (!(search_dir= my_dir(ptr, MYF(MY_WME))))
goto err;
@@ -559,28 +603,13 @@ static int search_default_file_with_ext(Process_option_func opt_handler,
my_dirend(search_dir);
}
- else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1))
- && my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword) - 1]))
+ else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) &&
+ my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1]))
{
- /* skip over `include' and following whitespace */
- for (ptr+= sizeof(include_keyword) - 1;
- my_isspace(&my_charset_latin1, ptr[0]); ptr++)
- {}
-
- /* trim trailing whitespace from filename */
- end= ptr + strlen(ptr) - 1;
- for (; my_isspace(&my_charset_latin1, *(end - 1)) ; end--)
- {}
- end[0]= 0;
-
- if (end == ptr)
- {
- fprintf(stderr,
- "error: Wrong !include directive in config "
- "file: %s at line %d\n",
- name,line);
- goto err;
- }
+ if (!(ptr= get_argument(include_keyword,
+ sizeof(include_keyword), ptr,
+ name, line)))
+ goto err;
search_default_file_with_ext(opt_handler, handler_ctx, "", "", ptr,
recursion_level + 1);
@@ -588,14 +617,6 @@ static int search_default_file_with_ext(Process_option_func opt_handler,
continue;
}
- else
- if (recursion_level >= max_recursion_level)
- {
- fprintf(stderr,
- "warning: skipping !include directive as maximum include"
- "recursion level was reached in file %s at line %d\n",
- name, line);
- }
if (*ptr == '[') /* Group name */
{
diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c
index 4967b60cd68..2308536cd37 100644
--- a/mysys/mf_keycache.c
+++ b/mysys/mf_keycache.c
@@ -1025,8 +1025,8 @@ static void reg_requests(KEY_CACHE *keycache, BLOCK_LINK *block, int count)
for a too long time (this time is determined by parameter age_threshold).
*/
-static inline void unreg_request(KEY_CACHE *keycache,
- BLOCK_LINK *block, int at_end)
+static void unreg_request(KEY_CACHE *keycache,
+ BLOCK_LINK *block, int at_end)
{
if (! --block->requests)
{
@@ -1045,10 +1045,13 @@ static inline void unreg_request(KEY_CACHE *keycache,
}
link_block(keycache, block, hot, (my_bool)at_end);
block->last_hit_time= keycache->keycache_time;
- if (++keycache->keycache_time - keycache->used_ins->last_hit_time >
+ keycache->keycache_time++;
+
+ block= keycache->used_ins;
+ /* Check if we should link a hot block to the warm block */
+ if (block && keycache->keycache_time - block->last_hit_time >
keycache->age_threshold)
{
- block= keycache->used_ins;
unlink_block(keycache, block);
link_block(keycache, block, 0, 0);
if (block->temperature != BLOCK_WARM)
diff --git a/sql/field.h b/sql/field.h
index 0f1cc0b95aa..ac9c2f351b3 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -118,6 +118,7 @@ public:
String *val_int_as_str(String *val_buffer, my_bool unsigned_flag);
virtual Item_result result_type () const=0;
virtual Item_result cmp_type () const { return result_type(); }
+ virtual Item_result cast_to_int_type () const { return result_type(); }
static enum_field_types field_type_merge(enum_field_types, enum_field_types);
static Item_result result_merge_type(enum_field_types);
bool eq(Field *field)
@@ -1216,6 +1217,7 @@ public:
}
enum_field_types type() const { return FIELD_TYPE_STRING; }
enum Item_result cmp_type () const { return INT_RESULT; }
+ enum Item_result cast_to_int_type () const { return INT_RESULT; }
enum ha_base_keytype key_type() const;
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr);
diff --git a/sql/handler.cc b/sql/handler.cc
index d2501afe97a..7318de1c503 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -185,9 +185,10 @@ enum db_type ha_checktype(enum db_type database_type)
thd= current_thd;
return ((enum db_type) thd->variables.table_type != DB_TYPE_UNKNOWN ?
(enum db_type) thd->variables.table_type :
- (enum db_type) global_system_variables.table_type !=
- DB_TYPE_UNKNOWN ?
- (enum db_type) global_system_variables.table_type : DB_TYPE_MYISAM);
+ ((enum db_type) global_system_variables.table_type !=
+ DB_TYPE_UNKNOWN ?
+ (enum db_type) global_system_variables.table_type : DB_TYPE_MYISAM)
+ );
} /* ha_checktype */
diff --git a/sql/hostname.cc b/sql/hostname.cc
index c74d230bbcb..fe2fad6f3b2 100644
--- a/sql/hostname.cc
+++ b/sql/hostname.cc
@@ -177,7 +177,14 @@ my_string ip_to_hostname(struct in_addr *in, uint *errors)
&tmp_errno)))
{
DBUG_PRINT("error",("gethostbyname_r returned %d",tmp_errno));
- add_wrong_ip(in);
+ /*
+ Don't cache responses when the DSN server is down, as otherwise
+ transient DNS failure may leave any number of clients (those
+ that attempted to connect during the outage) unable to connect
+ indefinitely.
+ */
+ if (tmp_errno == HOST_NOT_FOUND || tmp_error == NO_DATA)
+ add_wrong_ip(in);
my_gethostbyname_r_free();
DBUG_RETURN(0);
}
diff --git a/sql/item.h b/sql/item.h
index e86c66ca6f3..850a2d6636d 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -302,7 +302,8 @@ public:
{ return save_in_field(field, 1); }
virtual bool send(Protocol *protocol, String *str);
virtual bool eq(const Item *, bool binary_cmp) const;
- virtual Item_result result_type () const { return REAL_RESULT; }
+ virtual Item_result result_type() const { return REAL_RESULT; }
+ virtual Item_result cast_to_int_type() const { return result_type(); }
virtual enum_field_types field_type() const;
virtual enum Type type() const =0;
/* valXXX methods must return NULL or 0 or 0.0 if null_value is set. */
@@ -738,6 +739,10 @@ public:
{
return field->result_type();
}
+ Item_result cast_to_int_type() const
+ {
+ return field->cast_to_int_type();
+ }
enum_field_types field_type() const
{
return field->type();
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 9825de66477..4e3011ac4bb 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -904,6 +904,58 @@ void Item_func_signed::print(String *str)
}
+longlong Item_func_signed::val_int_from_str(int *error)
+{
+ char buff[MAX_FIELD_WIDTH], *end;
+ String tmp(buff,sizeof(buff), &my_charset_bin), *res;
+ longlong value;
+
+ /*
+ For a string result, we must first get the string and then convert it
+ to a longlong
+ */
+
+ if (!(res= args[0]->val_str(&tmp)))
+ {
+ null_value= 1;
+ *error= 0;
+ return 0;
+ }
+ null_value= 0;
+ end= (char*) res->ptr()+ res->length();
+ value= my_strtoll10(res->ptr(), &end, error);
+ if (*error > 0 || end != res->ptr()+ res->length())
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TRUNCATED_WRONG_VALUE,
+ ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER",
+ res->c_ptr());
+ return value;
+}
+
+
+longlong Item_func_signed::val_int()
+{
+ longlong value;
+ int error;
+
+ if (args[0]->cast_to_int_type() != STRING_RESULT)
+ {
+ value= args[0]->val_int();
+ null_value= args[0]->null_value;
+ return value;
+ }
+
+ value= val_int_from_str(&error);
+ if (value < 0 && error == 0)
+ {
+ push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ "Cast to signed converted positive out-of-range integer to "
+ "it's negative complement");
+ }
+ return value;
+}
+
+
void Item_func_unsigned::print(String *str)
{
str->append("cast(", 5);
@@ -913,6 +965,27 @@ void Item_func_unsigned::print(String *str)
}
+longlong Item_func_unsigned::val_int()
+{
+ longlong value;
+ int error;
+
+ if (args[0]->cast_to_int_type() != STRING_RESULT)
+ {
+ value= args[0]->val_int();
+ null_value= args[0]->null_value;
+ return value;
+ }
+
+ value= val_int_from_str(&error);
+ if (error < 0)
+ push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ "Cast to unsigned converted negative integer to it's "
+ "positive complement");
+ return value;
+}
+
+
String *Item_decimal_typecast::val_str(String *str)
{
my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf);
@@ -3271,7 +3344,8 @@ bool Item_func_set_user_var::fix_fields(THD *thd, TABLE_LIST *tables,
from the argument if the argument is NULL
and the variable has previously been initialized.
*/
- if (!entry->collation.collation || !args[0]->null_value)
+ null_item= (args[0]->type() == NULL_ITEM);
+ if (!entry->collation.collation || !null_item)
entry->collation.set(args[0]->collation.collation, DERIVATION_IMPLICIT);
collation.set(entry->collation.collation, DERIVATION_IMPLICIT);
cached_result_type= args[0]->result_type();
@@ -3315,8 +3389,8 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length,
char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
if (entry->value && entry->value != pos)
my_free(entry->value,MYF(0));
- entry->value=0;
- entry->length=0;
+ entry->value= 0;
+ entry->length= 0;
}
else
{
@@ -3355,9 +3429,9 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length,
if (type == DECIMAL_RESULT)
((my_decimal*)entry->value)->fix_buffer_pointer();
entry->length= length;
- entry->type=type;
entry->collation.set(cs, dv);
}
+ entry->type=type;
return 0;
}
@@ -3366,6 +3440,12 @@ bool
Item_func_set_user_var::update_hash(void *ptr, uint length, Item_result type,
CHARSET_INFO *cs, Derivation dv)
{
+ /*
+ If we set a variable explicitely to NULL then keep the old
+ result type of the variable
+ */
+ if ((null_value= args[0]->null_value) && null_item)
+ type= entry->type; // Don't change type of item
if (::update_hash(entry, (null_value= args[0]->null_value),
ptr, length, type, cs, dv))
{
diff --git a/sql/item_func.h b/sql/item_func.h
index cb0b7dc02a4..76d1151f3bf 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -262,12 +262,8 @@ public:
null_value= args[0]->null_value;
return tmp;
}
- longlong val_int()
- {
- longlong tmp= args[0]->val_int();
- null_value= args[0]->null_value;
- return tmp;
- }
+ longlong val_int();
+ longlong val_int_from_str(int *error);
void fix_length_and_dec()
{ max_length=args[0]->max_length; unsigned_flag=0; }
void print(String *str);
@@ -281,6 +277,7 @@ public:
const char *func_name() const { return "cast_as_unsigned"; }
void fix_length_and_dec()
{ max_length=args[0]->max_length; unsigned_flag=1; }
+ longlong val_int();
void print(String *str);
};
@@ -1071,6 +1068,7 @@ class Item_func_set_user_var :public Item_func
char buffer[MAX_FIELD_WIDTH];
String value;
my_decimal decimal_buff;
+ bool null_item;
union
{
longlong vint;
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 2ea435d84ef..9a96da004c5 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -820,7 +820,7 @@ bool Protocol_simple::store_long(longlong from)
#endif
char buff[20];
return net_store_data((char*) buff,
- (uint) (int10_to_str((int)from,buff, (from <0)?-10:10)-buff));
+ (uint) (int10_to_str((long int)from,buff, (from <0)?-10:10)-buff));
}
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index ac8f5a06745..729f9751bba 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -89,7 +89,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
}
if (values.elements != table->s->fields)
{
- my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1);
+ my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L);
return -1;
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -112,7 +112,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
int res;
if (fields.elements != values.elements)
{
- my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1);
+ my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L);
return -1;
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 56d09d7c563..17c5f51f1e1 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2223,7 +2223,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
my_pthread_setprio(pthread_self(), QUERY_PRIOR);
thd->protocol= &thd->protocol_prep; // Switch to binary protocol
- (void) stmt->cursor->fetch(num_rows);
+ stmt->cursor->fetch(num_rows);
thd->protocol= &thd->protocol_simple; // Use normal protocol
if (!(specialflag & SPECIAL_NO_PRIOR))
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 81f062c06ec..18172eb1f03 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -114,17 +114,31 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
static Next_select_func setup_end_select_func(JOIN *join);
static int do_select(JOIN *join,List<Item> *fields,TABLE *tmp_table,
Procedure *proc);
-static int sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records);
-static int sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records);
-static int flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last);
-static int end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
-static int end_send_group(JOIN *join, JOIN_TAB *join_tab,bool end_of_records);
-static int end_write(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
-static int end_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
-static int end_unique_update(JOIN *join,JOIN_TAB *join_tab,
- bool end_of_records);
-static int end_write_group(JOIN *join, JOIN_TAB *join_tab,
- bool end_of_records);
+
+static enum_nested_loop_state
+sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
+static enum_nested_loop_state
+evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
+ int error, my_bool *report_error);
+static enum_nested_loop_state
+evaluate_null_complemented_join_record(JOIN *join, JOIN_TAB *join_tab);
+static enum_nested_loop_state
+sub_select(JOIN *join,JOIN_TAB *join_tab, bool end_of_records);
+static enum_nested_loop_state
+flush_cached_records(JOIN *join, JOIN_TAB *join_tab, bool skip_last);
+static enum_nested_loop_state
+end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
+static enum_nested_loop_state
+end_send_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
+static enum_nested_loop_state
+end_write(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
+static enum_nested_loop_state
+end_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
+static enum_nested_loop_state
+end_unique_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
+static enum_nested_loop_state
+end_write_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
+
static int test_if_group_changed(List<Item_buff> &list);
static int join_read_const_table(JOIN_TAB *tab, POSITION *pos);
static int join_read_system(JOIN_TAB *tab);
@@ -1800,13 +1814,7 @@ Cursor::open(JOIN *join_arg)
happen for the first table in join_tab list
*/
DBUG_ASSERT(join_tab->table->null_row == 0);
-
- /*
- There is always at least one record in the table, as otherwise we
- wouldn't have opened the cursor. Therefore a failure is the only
- reason read_first_record can return not 0.
- */
- DBUG_RETURN(join_tab->read_first_record(join_tab));
+ DBUG_RETURN(0);
}
@@ -1816,97 +1824,36 @@ Cursor::open(JOIN *join_arg)
PRECONDITION:
Cursor is open
RETURN VALUES:
- -4 there are more rows, send_eof sent to the client
- 0 no more rows, send_eof was sent to the client, cursor is closed
- other fatal fetch error, cursor is closed (error is not reported)
+ none, this function will send error or OK to network if necessary.
*/
-int
+void
Cursor::fetch(ulong num_rows)
{
THD *thd= join->thd;
JOIN_TAB *join_tab= join->join_tab + join->const_tables;
- COND *on_expr= *join_tab->on_expr_ref;
- COND *select_cond= join_tab->select_cond;
- READ_RECORD *info= &join_tab->read_record;
- int error= 0;
+ enum_nested_loop_state error= NESTED_LOOP_OK;
/* save references to memory, allocated during fetch */
thd->set_n_backup_item_arena(this, &thd->stmt_backup);
join->fetch_limit+= num_rows;
- /*
- Run while there are new rows in the first table;
- For each row, satisfying ON and WHERE clauses (those parts of them which
- can be evaluated early), call next_select.
- */
- do
- {
- int no_more_rows;
-
- join->examined_rows++;
-
- if (thd->killed) /* Aborted by user */
- {
- my_message(ER_SERVER_SHUTDOWN, ER(ER_SERVER_SHUTDOWN), MYF(0));
- return -1;
- }
-
- if (on_expr == 0 || on_expr->val_int())
- {
- if (select_cond == 0 || select_cond->val_int())
- {
- /*
- TODO: call table->unlock_row() to unlock row failed selection,
- when this feature will be used.
- */
- error= join_tab->next_select(join, join_tab + 1, 0);
- DBUG_ASSERT(error <= 0);
- if (error)
- {
- /* real error or LIMIT/FETCH LIMIT worked */
- if (error == -4)
- {
- /*
- FETCH LIMIT, read ahead one row, and close cursor
- if there is no more rows XXX: to be fixed to support
- non-equi-joins!
- */
- if ((no_more_rows= info->read_record(info)))
- error= no_more_rows > 0 ? -1: 0;
- }
- break;
- }
- }
- }
- /* read next row; break loop if there was an error */
- if ((no_more_rows= info->read_record(info)))
- {
- if (no_more_rows > 0)
- error= -1;
- else
- {
- enum { END_OF_RECORDS= 1 };
- error= join_tab->next_select(join, join_tab+1, (int) END_OF_RECORDS);
- }
- break;
- }
- }
- while (thd->net.report_error == 0);
-
- if (thd->net.report_error)
- error= -1;
- if (error == -3) /* LIMIT clause worked */
- error= 0;
+ error= sub_select(join, join_tab, 0);
+ if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS)
+ error= sub_select(join,join_tab,1);
+ if (error == NESTED_LOOP_QUERY_LIMIT)
+ error= NESTED_LOOP_OK; /* select_limit used */
+ if (error == NESTED_LOOP_CURSOR_LIMIT)
+ join->resume_nested_loop= TRUE;
#ifdef USING_TRANSACTIONS
ha_release_temporary_latches(thd);
#endif
thd->restore_backup_item_arena(this, &thd->stmt_backup);
- if (error == -4)
+ if (error == NESTED_LOOP_CURSOR_LIMIT)
{
/* Fetch limit worked, possibly more rows are there */
thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
@@ -1916,20 +1863,19 @@ Cursor::fetch(ulong num_rows)
else
{
close();
- if (error == 0)
+ if (error == NESTED_LOOP_OK)
{
thd->server_status|= SERVER_STATUS_LAST_ROW_SENT;
::send_eof(thd);
thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT;
}
- else
+ else if (error != NESTED_LOOP_KILLED)
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
/* free cursor memory */
free_items(free_list);
free_list= 0;
free_root(&main_mem_root, MYF(0));
}
- return error;
}
@@ -2546,8 +2492,8 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
KEY_OPTIMIZE_EXISTS) |
((old->optimize | new_fields->optimize) &
KEY_OPTIMIZE_REF_OR_NULL));
- old->null_rejecting= old->null_rejecting &&
- new_fields->null_rejecting;
+ old->null_rejecting= (old->null_rejecting &&
+ new_fields->null_rejecting);
}
}
else if (old->eq_func && new_fields->eq_func &&
@@ -2559,8 +2505,8 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
KEY_OPTIMIZE_EXISTS) |
((old->optimize | new_fields->optimize) &
KEY_OPTIMIZE_REF_OR_NULL));
- old->null_rejecting= old->null_rejecting &&
- new_fields->null_rejecting;
+ old->null_rejecting= (old->null_rejecting &&
+ new_fields->null_rejecting);
}
else if (old->eq_func && new_fields->eq_func &&
(old->val->is_null() || new_fields->val->is_null()))
@@ -2572,7 +2518,7 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
if (old->val->is_null())
old->val= new_fields->val;
/* The referred expression can be NULL: */
- old->null_rejecting= false;
+ old->null_rejecting= 0;
}
else
{
@@ -2734,6 +2680,8 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
If the condition has form "tbl.keypart = othertbl.field" and
othertbl.field can be NULL, there will be no matches if othertbl.field
has NULL value.
+ We use null_rejecting in add_not_null_conds() to add
+ 'othertbl.field IS NOT NULL' to tab->select_cond.
*/
(*key_fields)->null_rejecting= (cond->functype() == Item_func::EQ_FUNC) &&
((*value)->type() == Item::FIELD_ITEM) &&
@@ -8949,7 +8897,8 @@ static Next_select_func setup_end_select_func(JOIN *join)
static int
do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
{
- int error= 0;
+ int rc= 0;
+ enum_nested_loop_state error= NESTED_LOOP_OK;
JOIN_TAB *join_tab;
DBUG_ENTER("do_select");
@@ -8979,24 +8928,30 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
if (!join->conds || join->conds->val_int())
{
Next_select_func end_select= join->join_tab[join->tables-1].next_select;
- if (!(error=(*end_select)(join,join_tab,0)) || error == -3)
- error=(*end_select)(join,join_tab,1);
+ error= (*end_select)(join,join_tab,0);
+ if (error == NESTED_LOOP_OK || error == NESTED_LOOP_QUERY_LIMIT)
+ error= (*end_select)(join,join_tab,1);
}
else if (join->send_row_on_empty_set())
- error= join->result->send_data(*join->fields);
+ rc= join->result->send_data(*join->fields);
}
else
{
error= sub_select(join,join_tab,0);
- if (error >= 0)
+ if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS)
error= sub_select(join,join_tab,1);
- if (error == -3)
- error= 0; /* select_limit used */
+ if (error == NESTED_LOOP_QUERY_LIMIT)
+ error= NESTED_LOOP_OK; /* select_limit used */
}
+ if (error == NESTED_LOOP_NO_MORE_ROWS)
+ error= NESTED_LOOP_OK;
- if (error >= 0)
+ if (error == NESTED_LOOP_OK)
{
- error=0;
+ /*
+ Sic: this branch works even if rc != 0, e.g. when
+ send_data above returns an error.
+ */
if (!table) // If sending data to client
{
/*
@@ -9005,10 +8960,12 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
*/
join->join_free(0); // Unlock all cursors
if (join->result->send_eof())
- error= 1; // Don't send error
+ rc= 1; // Don't send error
}
DBUG_PRINT("info",("%ld records output",join->send_records));
}
+ else
+ rc= -1;
if (table)
{
int tmp, new_errno= 0;
@@ -9026,40 +8983,42 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
table->file->print_error(new_errno,MYF(0));
}
#ifndef DBUG_OFF
- if (error)
+ if (rc)
{
DBUG_PRINT("error",("Error: do_select() failed"));
}
#endif
- DBUG_RETURN(join->thd->net.report_error ? -1 : error);
+ DBUG_RETURN(join->thd->net.report_error ? -1 : rc);
}
-static int
+static enum_nested_loop_state
sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
- int error;
+ enum_nested_loop_state rc;
if (end_of_records)
{
- if ((error=flush_cached_records(join,join_tab,FALSE)) < 0)
- return error; /* purecov: inspected */
- return sub_select(join,join_tab,end_of_records);
+ rc= flush_cached_records(join,join_tab,FALSE);
+ if (rc == NESTED_LOOP_OK || rc == NESTED_LOOP_NO_MORE_ROWS)
+ rc= sub_select(join,join_tab,end_of_records);
+ return rc;
}
if (join->thd->killed) // If aborted by user
{
join->thd->send_kill_message();
- return -2; /* purecov: inspected */
+ return NESTED_LOOP_KILLED; /* purecov: inspected */
}
if (join_tab->use_quick != 2 || test_if_quick_select(join_tab) <= 0)
{
if (!store_record_in_cache(&join_tab->cache))
- return 0; // There is more room in cache
+ return NESTED_LOOP_OK; // There is more room in cache
return flush_cached_records(join,join_tab,FALSE);
}
- if ((error=flush_cached_records(join,join_tab,TRUE)) < 0)
- return error; /* purecov: inspected */
- return sub_select(join,join_tab,end_of_records); /* Use ordinary select */
+ rc= flush_cached_records(join, join_tab, TRUE);
+ if (rc == NESTED_LOOP_OK || rc == NESTED_LOOP_NO_MORE_ROWS)
+ rc= sub_select(join, join_tab, end_of_records);
+ return rc;
}
/*
@@ -9171,11 +9130,10 @@ sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
table of the embedding nested join, if any.
RETURN
- 0, if success
- # of the error, otherwise
+ return one of enum_nested_loop_state, except NESTED_LOOP_NO_MORE_ROWS.
*/
-static int
+static enum_nested_loop_state
sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
join_tab->table->null_row=0;
@@ -9183,206 +9141,258 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
return (*join_tab->next_select)(join,join_tab+1,end_of_records);
int error;
- JOIN_TAB *first_unmatched;
- JOIN_TAB *tab;
- /* Cache variables for faster loop */
- COND *select_cond= join_tab->select_cond;
+ enum_nested_loop_state rc;
my_bool *report_error= &(join->thd->net.report_error);
+ READ_RECORD *info= &join_tab->read_record;
- join->return_tab= join_tab;
-
- if (join_tab->last_inner)
+ if (join->resume_nested_loop)
+ {
+ /* If not the last table, plunge down the nested loop */
+ if (join_tab < join->join_tab + join->tables - 1)
+ rc= (*join_tab->next_select)(join, join_tab + 1, 0);
+ else
+ {
+ join->resume_nested_loop= FALSE;
+ rc= NESTED_LOOP_OK;
+ }
+ }
+ else
{
- /* join_tab is the first inner table for an outer join operation. */
+ join->return_tab= join_tab;
- /* Set initial state of guard variables for this table.*/
- join_tab->found=0;
- join_tab->not_null_compl= 1;
+ if (join_tab->last_inner)
+ {
+ /* join_tab is the first inner table for an outer join operation. */
- /* Set first_unmatched for the last inner table of this group */
- join_tab->last_inner->first_unmatched= join_tab;
+ /* Set initial state of guard variables for this table.*/
+ join_tab->found=0;
+ join_tab->not_null_compl= 1;
+
+ /* Set first_unmatched for the last inner table of this group */
+ join_tab->last_inner->first_unmatched= join_tab;
+ }
+ join->thd->row_count= 0;
+
+ error= (*join_tab->read_first_record)(join_tab);
+ rc= evaluate_join_record(join, join_tab, error, report_error);
}
- if (!(error=(*join_tab->read_first_record)(join_tab)))
+ while (rc == NESTED_LOOP_OK)
{
- bool not_exists_optimize= join_tab->table->reginfo.not_exists_optimize;
- bool not_used_in_distinct=join_tab->not_used_in_distinct;
- ha_rows found_records=join->found_records;
- READ_RECORD *info= &join_tab->read_record;
+ error= info->read_record(info);
+ rc= evaluate_join_record(join, join_tab, error, report_error);
+ }
- join->thd->row_count= 0;
- do
+ if (rc == NESTED_LOOP_NO_MORE_ROWS &&
+ join_tab->last_inner && !join_tab->found)
+ rc= evaluate_null_complemented_join_record(join, join_tab);
+
+ if (rc == NESTED_LOOP_NO_MORE_ROWS)
+ rc= NESTED_LOOP_OK;
+ return rc;
+}
+
+
+/*
+ Process one record of the nested loop join.
+
+ DESCRIPTION
+ This function will evaluate parts of WHERE/ON clauses that are
+ applicable to the partial record on hand and in case of success
+ submit this record to the next level of the nested loop.
+*/
+
+static enum_nested_loop_state
+evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
+ int error, my_bool *report_error)
+{
+ bool not_exists_optimize= join_tab->table->reginfo.not_exists_optimize;
+ bool not_used_in_distinct=join_tab->not_used_in_distinct;
+ ha_rows found_records=join->found_records;
+ COND *select_cond= join_tab->select_cond;
+
+ if (error > 0 || (*report_error)) // Fatal error
+ return NESTED_LOOP_ERROR;
+ if (error < 0)
+ return NESTED_LOOP_NO_MORE_ROWS;
+ if (join->thd->killed) // Aborted by user
+ {
+ join->thd->send_kill_message();
+ return NESTED_LOOP_KILLED; /* purecov: inspected */
+ }
+ DBUG_PRINT("info", ("select cond 0x%lx", (ulong)select_cond));
+ if (!select_cond || select_cond->val_int())
+ {
+ /*
+ There is no select condition or the attached pushed down
+ condition is true => a match is found.
+ */
+ bool found= 1;
+ while (join_tab->first_unmatched && found)
{
- if (join->thd->killed) // Aborted by user
- {
- join->thd->send_kill_message();
- return -2; /* purecov: inspected */
- }
- DBUG_PRINT("info", ("select cond 0x%lx", (ulong)select_cond));
- if (!select_cond || select_cond->val_int())
+ /*
+ The while condition is always false if join_tab is not
+ the last inner join table of an outer join operation.
+ */
+ JOIN_TAB *first_unmatched= join_tab->first_unmatched;
+ /*
+ Mark that a match for current outer table is found.
+ This activates push down conditional predicates attached
+ to the all inner tables of the outer join.
+ */
+ first_unmatched->found= 1;
+ for (JOIN_TAB *tab= first_unmatched; tab <= join_tab; tab++)
{
- /*
- There is no select condition or the attached pushed down
- condition is true => a match is found.
- */
- bool found= 1;
- while (join_tab->first_unmatched && found)
+ /* Check all predicates that has just been activated. */
+ /*
+ Actually all predicates non-guarded by first_unmatched->found
+ will be re-evaluated again. It could be fixed, but, probably,
+ it's not worth doing now.
+ */
+ if (tab->select_cond && !tab->select_cond->val_int())
{
- /*
- The while condition is always false if join_tab is not
- the last inner join table of an outer join operation.
- */
- first_unmatched= join_tab->first_unmatched;
- /*
- Mark that a match for current outer table is found.
- This activates push down conditional predicates attached
- to the all inner tables of the outer join.
- */
- first_unmatched->found= 1;
- for (tab= first_unmatched; tab <= join_tab; tab++)
- {
- /* Check all predicates that has just been activated. */
+ /* The condition attached to table tab is false */
+ if (tab == join_tab)
+ found= 0;
+ else
+ {
/*
- Actually all predicates non-guarded by first_unmatched->found
- will be re-evaluated again. It could be fixed, but, probably,
- it's not worth doing now.
- */
- if (tab->select_cond && !tab->select_cond->val_int())
- {
- /* The condition attached to table tab is false */
- if (tab == join_tab)
- found= 0;
- else
- {
- /*
- Set a return point if rejected predicate is attached
- not to the last table of the current nest level.
- */
- join->return_tab= tab;
- return 0;
- }
- }
+ Set a return point if rejected predicate is attached
+ not to the last table of the current nest level.
+ */
+ join->return_tab= tab;
+ return NESTED_LOOP_OK;
}
- /*
- Check whether join_tab is not the last inner table
- for another embedding outer join.
- */
- if ((first_unmatched= first_unmatched->first_upper) &&
- first_unmatched->last_inner != join_tab)
- first_unmatched= 0;
- join_tab->first_unmatched= first_unmatched;
}
-
- /*
- It was not just a return to lower loop level when one
- of the newly activated predicates is evaluated as false
- (See above join->return_tab= tab).
- */
- join->examined_rows++;
- join->thd->row_count++;
-
- if (found)
- {
- if (not_exists_optimize)
- break;
- /* A match from join_tab is found for the current partial join. */
- if ((error=(*join_tab->next_select)(join, join_tab+1, 0)) < 0)
- return error;
- if (join->return_tab < join_tab)
- return 0;
- /*
- Test if this was a SELECT DISTINCT query on a table that
- was not in the field list; In this case we can abort if
- we found a row, as no new rows can be added to the result.
- */
- if (not_used_in_distinct && found_records != join->found_records)
- return 0;
- }
- else
- info->file->unlock_row();
}
- else
- {
- /*
- The condition pushed down to the table join_tab rejects all rows
- with the beginning coinciding with the current partial join.
- */
- join->examined_rows++;
- join->thd->row_count++;
- }
-
- } while (!(error=info->read_record(info)) && !(*report_error));
- }
- if (error > 0 || (*report_error)) // Fatal error
- return -1;
-
- if (join_tab->last_inner && !join_tab->found)
- {
- /*
- The table join_tab is the first inner table of a outer join operation
- and no matches has been found for the current outer row.
- */
- JOIN_TAB *last_inner_tab= join_tab->last_inner;
- for ( ; join_tab <= last_inner_tab ; join_tab++)
- {
- /* Change the the values of guard predicate variables. */
- join_tab->found= 1;
- join_tab->not_null_compl= 0;
- /* The outer row is complemented by nulls for each inner tables */
- restore_record(join_tab->table,s->default_values); // Make empty record
- mark_as_null_row(join_tab->table); // For group by without error
- select_cond= join_tab->select_cond;
- /* Check all attached conditions for inner table rows. */
- if (select_cond && !select_cond->val_int())
- return 0;
- }
- join_tab--;
- /*
- The row complemented by nulls might be the first row
- of embedding outer joins.
- If so, perform the same actions as in the code
- for the first regular outer join row above.
- */
- for ( ; ; )
- {
- first_unmatched= join_tab->first_unmatched;
+ /*
+ Check whether join_tab is not the last inner table
+ for another embedding outer join.
+ */
if ((first_unmatched= first_unmatched->first_upper) &&
first_unmatched->last_inner != join_tab)
first_unmatched= 0;
join_tab->first_unmatched= first_unmatched;
- if (!first_unmatched)
- break;
- first_unmatched->found= 1;
- for (JOIN_TAB *tab= first_unmatched; tab <= join_tab; tab++)
- {
- if (tab->select_cond && !tab->select_cond->val_int())
- {
- join->return_tab= tab;
- return 0;
- }
- }
}
+
/*
- The row complemented by nulls satisfies all conditions
- attached to inner tables.
- Send the row complemented by nulls to be joined with the
- remaining tables.
- */
- if ((error=(*join_tab->next_select)(join, join_tab+1 ,0)) < 0)
- return error;
+ It was not just a return to lower loop level when one
+ of the newly activated predicates is evaluated as false
+ (See above join->return_tab= tab).
+ */
+ join->examined_rows++;
+ join->thd->row_count++;
+
+ if (found)
+ {
+ enum enum_nested_loop_state rc;
+ if (not_exists_optimize)
+ return NESTED_LOOP_NO_MORE_ROWS;
+ /* A match from join_tab is found for the current partial join. */
+ rc= (*join_tab->next_select)(join, join_tab+1, 0);
+ if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS)
+ return rc;
+ if (join->return_tab < join_tab)
+ return NESTED_LOOP_OK;
+ /*
+ Test if this was a SELECT DISTINCT query on a table that
+ was not in the field list; In this case we can abort if
+ we found a row, as no new rows can be added to the result.
+ */
+ if (not_used_in_distinct && found_records != join->found_records)
+ return NESTED_LOOP_OK;
+ }
+ else
+ join_tab->read_record.file->unlock_row();
}
- return 0;
+ else
+ {
+ /*
+ The condition pushed down to the table join_tab rejects all rows
+ with the beginning coinciding with the current partial join.
+ */
+ join->examined_rows++;
+ join->thd->row_count++;
+ }
+ return NESTED_LOOP_OK;
}
-static int
+/*
+ DESCRIPTION
+ Construct a NULL complimented partial join record and feed it to the next
+ level of the nested loop. This function is used in case we have
+ an OUTER join and no matching record was found.
+*/
+
+static enum_nested_loop_state
+evaluate_null_complemented_join_record(JOIN *join, JOIN_TAB *join_tab)
+{
+ /*
+ The table join_tab is the first inner table of a outer join operation
+ and no matches has been found for the current outer row.
+ */
+ JOIN_TAB *last_inner_tab= join_tab->last_inner;
+ /* Cache variables for faster loop */
+ COND *select_cond;
+ for ( ; join_tab <= last_inner_tab ; join_tab++)
+ {
+ /* Change the the values of guard predicate variables. */
+ join_tab->found= 1;
+ join_tab->not_null_compl= 0;
+ /* The outer row is complemented by nulls for each inner tables */
+ restore_record(join_tab->table,s->default_values); // Make empty record
+ mark_as_null_row(join_tab->table); // For group by without error
+ select_cond= join_tab->select_cond;
+ /* Check all attached conditions for inner table rows. */
+ if (select_cond && !select_cond->val_int())
+ return NESTED_LOOP_OK;
+ }
+ join_tab--;
+ /*
+ The row complemented by nulls might be the first row
+ of embedding outer joins.
+ If so, perform the same actions as in the code
+ for the first regular outer join row above.
+ */
+ for ( ; ; )
+ {
+ JOIN_TAB *first_unmatched= join_tab->first_unmatched;
+ if ((first_unmatched= first_unmatched->first_upper) &&
+ first_unmatched->last_inner != join_tab)
+ first_unmatched= 0;
+ join_tab->first_unmatched= first_unmatched;
+ if (!first_unmatched)
+ break;
+ first_unmatched->found= 1;
+ for (JOIN_TAB *tab= first_unmatched; tab <= join_tab; tab++)
+ {
+ if (tab->select_cond && !tab->select_cond->val_int())
+ {
+ join->return_tab= tab;
+ return NESTED_LOOP_OK;
+ }
+ }
+ }
+ /*
+ The row complemented by nulls satisfies all conditions
+ attached to inner tables.
+ Send the row complemented by nulls to be joined with the
+ remaining tables.
+ */
+ return (*join_tab->next_select)(join, join_tab+1, 0);
+}
+
+
+static enum_nested_loop_state
flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last)
{
+ enum_nested_loop_state rc= NESTED_LOOP_OK;
int error;
READ_RECORD *info;
if (!join_tab->cache.records)
- return 0; /* Nothing to do */
+ return NESTED_LOOP_OK; /* Nothing to do */
if (skip_last)
(void) store_record_in_cache(&join_tab->cache); // Must save this for later
if (join_tab->use_quick == 2)
@@ -9397,7 +9407,7 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last)
if ((error=join_init_read_record(join_tab)))
{
reset_cache_write(&join_tab->cache);
- return -error; /* No records or error */
+ return error < 0 ? NESTED_LOOP_NO_MORE_ROWS: NESTED_LOOP_ERROR;
}
for (JOIN_TAB *tmp=join->join_tab; tmp != join_tab ; tmp++)
@@ -9412,11 +9422,11 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last)
if (join->thd->killed)
{
join->thd->send_kill_message();
- return -2; // Aborted by user /* purecov: inspected */
+ return NESTED_LOOP_KILLED; // Aborted by user /* purecov: inspected */
}
SQL_SELECT *select=join_tab->select;
- if (!error && (!join_tab->cache.select ||
- !join_tab->cache.select->skip_record()))
+ if (rc == NESTED_LOOP_OK &&
+ (!join_tab->cache.select || !join_tab->cache.select->skip_record()))
{
uint i;
reset_cache_read(&join_tab->cache);
@@ -9424,11 +9434,14 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last)
{
read_cached_record(join_tab);
if (!select || !select->skip_record())
- if ((error=(join_tab->next_select)(join,join_tab+1,0)) < 0)
+ {
+ rc= (join_tab->next_select)(join,join_tab+1,0);
+ if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS)
{
reset_cache_write(&join_tab->cache);
- return error; /* purecov: inspected */
+ return rc;
}
+ }
}
}
} while (!(error=info->read_record(info)));
@@ -9437,10 +9450,10 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last)
read_cached_record(join_tab); // Restore current record
reset_cache_write(&join_tab->cache);
if (error > 0) // Fatal error
- return -1; /* purecov: inspected */
+ return NESTED_LOOP_ERROR; /* purecov: inspected */
for (JOIN_TAB *tmp2=join->join_tab; tmp2 != join_tab ; tmp2++)
tmp2->table->status=tmp2->status;
- return 0;
+ return NESTED_LOOP_OK;
}
@@ -9906,13 +9919,32 @@ join_read_next_same_or_null(READ_RECORD *info)
/*****************************************************************************
- The different end of select functions
- These functions returns < 0 when end is reached, 0 on ok and > 0 if a
- fatal error (like table corruption) was detected
+ DESCRIPTION
+ Functions that end one nested loop iteration. Different functions
+ are used to support GROUP BY clause and to redirect records
+ to a table (e.g. in case of SELECT into a temporary table) or to the
+ network client.
+
+ RETURN VALUES
+ NESTED_LOOP_OK - the record has been successfully handled
+ NESTED_LOOP_ERROR - a fatal error (like table corruption)
+ was detected
+ NESTED_LOOP_KILLED - thread shutdown was requested while processing
+ the record
+ NESTED_LOOP_QUERY_LIMIT - the record has been successfully handled;
+ additionally, the nested loop produced the
+ number of rows specified in the LIMIT clause
+ for the query
+ NESTED_LOOP_CURSOR_LIMIT - the record has been successfully handled;
+ additionally, there is a cursor and the nested
+ loop algorithm produced the number of rows
+ that is specified for current cursor fetch
+ operation.
+ All return values except NESTED_LOOP_OK abort the nested loop.
*****************************************************************************/
/* ARGSUSED */
-static int
+static enum_nested_loop_state
end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
bool end_of_records)
{
@@ -9921,14 +9953,14 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
int error;
if (join->having && join->having->val_int() == 0)
- DBUG_RETURN(0); // Didn't match having
+ DBUG_RETURN(NESTED_LOOP_OK); // Didn't match having
error=0;
if (join->procedure)
error=join->procedure->send_row(*join->fields);
else if (join->do_send_rows)
error=join->result->send_data(*join->fields);
if (error)
- DBUG_RETURN(-1); /* purecov: inspected */
+ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
if (++join->send_records >= join->unit->select_limit_cnt &&
join->do_send_rows)
{
@@ -9962,10 +9994,10 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
join->do_send_rows= 0;
if (join->unit->fake_select_lex)
join->unit->fake_select_lex->select_limit= HA_POS_ERROR;
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
}
}
- DBUG_RETURN(-3); // Abort nicely
+ DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT); // Abort nicely
}
else if (join->send_records >= join->fetch_limit)
{
@@ -9973,20 +10005,20 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
There is a server side cursor and all rows for
this fetch request are sent.
*/
- DBUG_RETURN(-4);
+ DBUG_RETURN(NESTED_LOOP_CURSOR_LIMIT);
}
}
else
{
if (join->procedure && join->procedure->end_of_records())
- DBUG_RETURN(-1);
+ DBUG_RETURN(NESTED_LOOP_ERROR);
}
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
}
/* ARGSUSED */
-static int
+static enum_nested_loop_state
end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
bool end_of_records)
{
@@ -10038,14 +10070,14 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
}
if (error > 0)
- DBUG_RETURN(-1); /* purecov: inspected */
+ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
if (end_of_records)
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
if (join->send_records >= join->unit->select_limit_cnt &&
join->do_send_rows)
{
if (!(join->select_options & OPTION_FOUND_ROWS))
- DBUG_RETURN(-3); // Abort nicely
+ DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT); // Abort nicely
join->do_send_rows=0;
join->unit->select_limit_cnt = HA_POS_ERROR;
}
@@ -10055,14 +10087,14 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
There is a server side cursor and all rows
for this fetch request are sent.
*/
- DBUG_RETURN(-4);
+ DBUG_RETURN(NESTED_LOOP_CURSOR_LIMIT);
}
}
}
else
{
if (end_of_records)
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
join->first_record=1;
VOID(test_if_group_changed(join->group_fields));
}
@@ -10070,33 +10102,32 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
copy_fields(&join->tmp_table_param);
if (init_sum_functions(join->sum_funcs, join->sum_funcs_end[idx+1]))
- DBUG_RETURN(-1);
+ DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->procedure)
join->procedure->add();
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
}
}
if (update_sum_func(join->sum_funcs))
- DBUG_RETURN(-1);
+ DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->procedure)
join->procedure->add();
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
}
/* ARGSUSED */
-static int
+static enum_nested_loop_state
end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
bool end_of_records)
{
TABLE *table=join->tmp_table;
- int error;
DBUG_ENTER("end_write");
if (join->thd->killed) // Aborted by user
{
join->thd->send_kill_message();
- DBUG_RETURN(-2); /* purecov: inspected */
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
}
if (!end_of_records)
{
@@ -10121,6 +10152,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
#endif
if (!join->having || join->having->val_int())
{
+ int error;
join->found_records++;
if ((error=table->file->write_row(table->record[0])))
{
@@ -10129,28 +10161,28 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
goto end;
if (create_myisam_from_heap(join->thd, table, &join->tmp_table_param,
error,1))
- DBUG_RETURN(-1); // Not a table_is_full error
+ DBUG_RETURN(NESTED_LOOP_ERROR); // Not a table_is_full error
table->s->uniques=0; // To ensure rows are the same
}
if (++join->send_records >= join->tmp_table_param.end_write_records &&
join->do_send_rows)
{
if (!(join->select_options & OPTION_FOUND_ROWS))
- DBUG_RETURN(-3);
+ DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
join->do_send_rows=0;
join->unit->select_limit_cnt = HA_POS_ERROR;
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
}
}
}
end:
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
}
/* Group by searching after group record and updating it if possible */
/* ARGSUSED */
-static int
+static enum_nested_loop_state
end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
bool end_of_records)
{
@@ -10160,11 +10192,11 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_ENTER("end_update");
if (end_of_records)
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
if (join->thd->killed) // Aborted by user
{
join->thd->send_kill_message();
- DBUG_RETURN(-2); /* purecov: inspected */
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
}
join->found_records++;
@@ -10188,9 +10220,9 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-1); /* purecov: inspected */
+ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
}
/*
@@ -10212,19 +10244,19 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
if (create_myisam_from_heap(join->thd, table, &join->tmp_table_param,
error, 0))
- DBUG_RETURN(-1); // Not a table_is_full error
+ DBUG_RETURN(NESTED_LOOP_ERROR); // Not a table_is_full error
/* Change method to update rows */
table->file->ha_index_init(0);
join->join_tab[join->tables-1].next_select=end_unique_update;
}
join->send_records++;
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
}
/* Like end_update, but this is done with unique constraints instead of keys */
-static int
+static enum_nested_loop_state
end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
bool end_of_records)
{
@@ -10233,11 +10265,11 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_ENTER("end_unique_update");
if (end_of_records)
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
if (join->thd->killed) // Aborted by user
{
join->thd->send_kill_message();
- DBUG_RETURN(-2); /* purecov: inspected */
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
}
init_tmptable_sum_functions(join->sum_funcs);
@@ -10251,12 +10283,12 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if ((int) table->file->get_dup_key(error) < 0)
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-1); /* purecov: inspected */
+ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
if (table->file->rnd_pos(table->record[1],table->file->dupp_ref))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-1); /* purecov: inspected */
+ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
restore_record(table,record[1]);
update_tmptable_sum_func(join->sum_funcs,table);
@@ -10264,27 +10296,26 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-1); /* purecov: inspected */
+ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
}
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
}
/* ARGSUSED */
-static int
+static enum_nested_loop_state
end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
bool end_of_records)
{
TABLE *table=join->tmp_table;
- int error;
int idx= -1;
DBUG_ENTER("end_write_group");
if (join->thd->killed)
{ // Aborted by user
join->thd->send_kill_message();
- DBUG_RETURN(-2); /* purecov: inspected */
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
}
if (!join->first_record || end_of_records ||
(idx=test_if_group_changed(join->group_fields)) >= 0)
@@ -10303,28 +10334,27 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
copy_sum_funcs(join->sum_funcs,
join->sum_funcs_end[send_group_parts]);
- if (join->having && join->having->val_int() == 0)
- error= -1;
- else if ((error= table->file->write_row(table->record[0])))
+ if (!join->having || join->having->val_int())
{
- if (create_myisam_from_heap(join->thd, table,
- &join->tmp_table_param,
- error, 0))
- DBUG_RETURN(-1);
+ int error= table->file->write_row(table->record[0]);
+ if (error && create_myisam_from_heap(join->thd, table,
+ &join->tmp_table_param,
+ error, 0))
+ DBUG_RETURN(NESTED_LOOP_ERROR);
}
if (join->rollup.state != ROLLUP::STATE_NONE)
{
if (join->rollup_write_data((uint) (idx+1), table))
- DBUG_RETURN(-1);
+ DBUG_RETURN(NESTED_LOOP_ERROR);
}
if (end_of_records)
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
}
}
else
{
if (end_of_records)
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
join->first_record=1;
VOID(test_if_group_changed(join->group_fields));
}
@@ -10333,17 +10363,17 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
copy_fields(&join->tmp_table_param);
copy_funcs(join->tmp_table_param.items_to_copy);
if (init_sum_functions(join->sum_funcs, join->sum_funcs_end[idx+1]))
- DBUG_RETURN(-1);
+ DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->procedure)
join->procedure->add();
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
}
}
if (update_sum_func(join->sum_funcs))
- DBUG_RETURN(-1);
+ DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->procedure)
join->procedure->add();
- DBUG_RETURN(0);
+ DBUG_RETURN(NESTED_LOOP_OK);
}
diff --git a/sql/sql_select.h b/sql/sql_select.h
index c9a9b26d1b4..82efb62f7a3 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -91,7 +91,15 @@ enum join_type { JT_UNKNOWN,JT_SYSTEM,JT_CONST,JT_EQ_REF,JT_REF,JT_MAYBE_REF,
class JOIN;
-typedef int (*Next_select_func)(JOIN *,struct st_join_table *,bool);
+enum enum_nested_loop_state
+{
+ NESTED_LOOP_KILLED= -2, NESTED_LOOP_ERROR= -1,
+ NESTED_LOOP_OK= 0, NESTED_LOOP_NO_MORE_ROWS= 1,
+ NESTED_LOOP_QUERY_LIMIT= 3, NESTED_LOOP_CURSOR_LIMIT= 4
+};
+
+typedef enum_nested_loop_state
+(*Next_select_func)(JOIN *, struct st_join_table *, bool);
typedef int (*Read_record_func)(struct st_join_table *tab);
@@ -162,6 +170,11 @@ class JOIN :public Sql_alloc
uint send_group_parts;
bool sort_and_group,first_record,full_join,group, no_field_update;
bool do_send_rows;
+ /*
+ TRUE when we want to resume nested loop iterations when
+ fetching data from a cursor
+ */
+ bool resume_nested_loop;
table_map const_table_map,found_const_table_map,outer_join;
ha_rows send_records,found_records,examined_rows,row_limit, select_limit;
/*
@@ -263,6 +276,7 @@ class JOIN :public Sql_alloc
sort_and_group= 0;
first_record= 0;
do_send_rows= 1;
+ resume_nested_loop= FALSE;
send_records= 0;
found_records= 0;
fetch_limit= HA_POS_ERROR;
@@ -374,7 +388,7 @@ public:
void reset_thd(THD *thd);
int open(JOIN *join);
- int fetch(ulong num_rows);
+ void fetch(ulong num_rows);
void reset() { join= 0; }
bool is_open() const { return join != 0; }
void close();
diff --git a/sql/unireg.cc b/sql/unireg.cc
index 929ca5c672e..95a383e0f01 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -685,6 +685,9 @@ static bool make_empty_rec(THD *thd, File file,enum db_type table_type,
thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values
while ((field=it++))
{
+ /*
+ regfield don't have to be deleted as it's allocated with sql_alloc()
+ */
Field *regfield=make_field((char*) buff+field->offset,field->length,
null_pos,
null_count & 7,
@@ -696,7 +699,8 @@ static bool make_empty_rec(THD *thd, File file,enum db_type table_type,
field->interval,
field->field_name,
&table);
- DBUG_ASSERT(regfield);
+ if (!regfield)
+ goto err; // End of memory
if (!(field->flags & NOT_NULL_FLAG))
null_count++;
@@ -730,7 +734,6 @@ static bool make_empty_rec(THD *thd, File file,enum db_type table_type,
regfield->store(ER(ER_NO), (uint) strlen(ER(ER_NO)),system_charset_info);
else
regfield->reset();
- delete regfield;
}
/* Fill not used startpos */
diff --git a/tests/index_corrupt.pl b/tests/index_corrupt.pl
new file mode 100755
index 00000000000..19bf54f5d11
--- /dev/null
+++ b/tests/index_corrupt.pl
@@ -0,0 +1,212 @@
+#!/usr/bin/perl -w
+#
+# This is a test for a key cache bug (bug #10167)
+# To expose the bug mysqld should be started with --key-buffer-size=64K
+#
+
+$opt_loop_count=100000; # Change this to make test harder/easier
+
+##################### Standard benchmark inits ##############################
+
+use DBI;
+use Getopt::Long;
+use Benchmark;
+
+package main;
+
+$opt_skip_create=$opt_skip_in=$opt_verbose=$opt_fast_insert=
+ $opt_lock_tables=$opt_debug=$opt_skip_delete=$opt_fast=$opt_force=0;
+$opt_host=$opt_user=$opt_password=""; $opt_db="test";
+
+GetOptions("host=s","db=s","loop-count=i","skip-create","skip-in",
+ "skip-delete","verbose","fast-insert","lock-tables","debug","fast",
+ "force","user=s","password=s") || die "Aborted";
+$opt_verbose=$opt_debug=$opt_lock_tables=$opt_fast_insert=$opt_fast=$opt_skip_in=$opt_force=undef; # Ignore warnings from these
+
+$firsttable = "bench_f1";
+$secondtable = "bench_f2";
+$kill_file= "/tmp/mysqltest_index_corrupt.$$";
+
+####
+#### Start timeing and start test
+####
+
+$start_time=new Benchmark;
+if (!$opt_skip_create)
+{
+ $dbh = DBI->connect("DBI:mysql:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $dbh->do("drop table if exists $firsttable, $secondtable");
+
+ print "Creating tables in $opt_db\n";
+ $dbh->do("create table $firsttable (
+c_pollid INTEGER NOT NULL,
+c_time BIGINT NOT NULL,
+c_data DOUBLE NOT NULL,
+c_error INTEGER NOT NULL,
+c_warning INTEGER NOT NULL,
+c_okay INTEGER NOT NULL,
+c_unknown INTEGER NOT NULL,
+c_rolled_up BIT NOT NULL,
+INDEX t_mgmt_hist_r_i1 (c_pollid),
+INDEX t_mgmt_hist_r_i2 (c_time),
+INDEX t_mgmt_hist_r_i3 (c_rolled_up))") or die $DBI::errstr;
+
+ $dbh->do("create table $secondtable (
+c_pollid INTEGER NOT NULL,
+c_min_time BIGINT NOT NULL,
+c_max_time BIGINT NOT NULL,
+c_min_data DOUBLE NOT NULL,
+c_max_data DOUBLE NOT NULL,
+c_avg_data DOUBLE NOT NULL,
+c_error INTEGER NOT NULL,
+c_warning INTEGER NOT NULL,
+c_okay INTEGER NOT NULL,
+c_unknown INTEGER NOT NULL,
+c_rolled_up BIT NOT NULL,
+INDEX t_mgmt_hist_d_i1 (c_pollid),
+INDEX t_mgmt_hist_d_i2 (c_min_time),
+INDEX t_mgmt_hist_d_i3 (c_max_time),
+INDEX t_mgmt_hist_d_i4 (c_rolled_up))") or die $DBI::errstr;
+
+
+ $dbh->disconnect; $dbh=0; # Close handler
+}
+$|= 1; # Autoflush
+
+####
+#### Start the tests
+####
+
+print "Running tests\n";
+insert_in_bench() if (($pid=fork()) == 0); $work{$pid}="insert";
+select_from_bench() if (($pid=fork()) == 0); $work{$pid}="insert-select;
+delete_from_bench() if (($pid=fork()) == 0); $work{$pid}="delete";
+
+$errors=0;
+while (($pid=wait()) != -1)
+{
+ $ret=$?/256;
+ print "thread '" . $work{$pid} . "' finished with exit code $ret\n";
+ $errors++ if ($ret != 0);
+}
+
+if (!$opt_skip_delete && !$errors)
+{
+ $dbh = DBI->connect("DBI:mysql:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $dbh->do("drop table $firsttable, $secondtable");
+}
+print ($errors ? "Test failed\n" :"Test ok\n");
+
+$end_time=new Benchmark;
+print "Total time: " .
+ timestr(timediff($end_time, $start_time),"noc") . "\n";
+
+unlink $kill_file;
+
+exit(0);
+
+#
+# Insert records in the two tables
+#
+
+sub insert_in_bench
+{
+ my ($dbh,$rows,$found,$i);
+
+ $dbh = DBI->connect("DBI:mysql:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ for ($rows= 1; $rows <= $opt_loop_count ; $rows++)
+ {
+ $c_pollid = sprintf("%d",rand 1000);
+ $c_time = sprintf("%d",rand 100000);
+ $c_data = rand 1000000;
+ $test = rand 1;
+ $c_error=0;
+ $c_warning=0;
+ $c_okay=0;
+ $c_unknown=0;
+ if ($test < .8) {
+ $c_okay=1;
+ } elsif ($test <.9) {
+ $c_error=1;
+ } elsif ($test <.95) {
+ $c_warning=1;
+ } else {
+ $c_unknown=1;
+ }
+ $statement = "INSERT INTO $firsttable (c_pollid, c_time, c_data, c_error
+, c_warning, c_okay, c_unknown, c_rolled_up) ".
+ "VALUES ($c_pollid,$c_time,$c_data,$c_error,$c_warning,$c_okay,$c_unknown,0)";
+ $cursor = $dbh->prepare($statement);
+ $cursor->execute();
+ $cursor->finish();
+ }
+
+ $dbh->disconnect; $dbh=0;
+ print "insert_in_bench: Inserted $rows rows\n";
+
+ # Kill other threads
+ open(KILLFILE, "> $kill_file");
+ close(KILLFILE);
+
+ exit(0);
+}
+
+
+sub select_from_bench
+{
+ my ($dbh,$rows,$cursor);
+
+ $dbh = DBI->connect("DBI:mysql:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ for ($rows= 1; $rows < $opt_loop_count ; $rows++)
+ {
+ $t_value = rand 100000;
+ $t_value2 = $t_value+10000;
+ $statement = "INSERT INTO $secondtable (c_pollid, c_min_time, c_max_time
+, c_min_data, c_max_data, c_avg_data, c_error, c_warning, c_okay, c_unknown, c_rolled_up) SELECT c_pollid, MIN(c_time), MAX(c_time), MIN(c_data), MAX(c_data), AVG(c_data), SUM(c_error), SUM(c_warning), SUM(c_okay), SUM(c_unknown), 0 FROM $firsttable WHERE (c_time>=$t_value) AND (c_time<$t_value2) AND (c_rolled_up=0) GROUP BY c_pollid";
+ $cursor = $dbh->prepare($statement);
+ $cursor->execute();
+ $cursor->finish();
+ sleep 1;
+ if (-e $kill_file)
+ {
+ last;
+ }
+ }
+ print "select_from_bench: insert-select executed $rows times\n";
+ exit(0);
+}
+
+
+sub delete_from_bench
+{
+ my ($dbh,$row, $t_value, $t2_value, $statement, $cursor);
+
+ $dbh = DBI->connect("DBI:mysql:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ for ($rows= 1; $rows < $opt_loop_count ; $rows++)
+ {
+ $t_value = rand 50000;
+ $t2_value = $t_value + 50001;
+ $statement = "DELETE FROM $firsttable WHERE (c_time>$t_value) AND (c_time<$t2_value)";
+ $cursor = $dbh->prepare($statement);
+ $cursor->execute();
+ $cursor->finish();
+ sleep 10;
+ if (-e $kill_file)
+ {
+ last;
+ }
+ }
+ print "delete: delete executed $rows times\n";
+ exit(0);
+}
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index 9b7c8281043..d484c9af7ee 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -7046,6 +7046,7 @@ static void test_set_option()
bug #89 (reported by mark@mysql.com)
*/
+#ifndef EMBEDDED_LIBRARY
static void test_prepare_grant()
{
int rc;
@@ -7138,7 +7139,7 @@ static void test_prepare_grant()
}
}
-
+#endif
/*
Test a crash when invalid/corrupted .frm is used in the
@@ -12597,7 +12598,7 @@ static void test_bug8330()
const char *stmt_text;
MYSQL_STMT *stmt[2];
int i, rc;
- char *query= "select a,b from t1 where a=?";
+ const char *query= "select a,b from t1 where a=?";
MYSQL_BIND bind[2];
long lval[2];
@@ -12788,7 +12789,7 @@ static void test_bug8722()
}
-MYSQL_STMT *open_cursor(char *query)
+MYSQL_STMT *open_cursor(const char *query)
{
int rc;
const ulong type= (ulong)CURSOR_TYPE_READ_ONLY;
@@ -12854,6 +12855,59 @@ static void test_bug9159()
myquery(rc);
}
+
+/* Crash when opening a cursor to a query with DISTICNT and no key */
+
+static void test_bug9520()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[1];
+ char a[6];
+ ulong a_len;
+ int rc, row_count= 0;
+
+ myheader("test_bug9520");
+
+ mysql_query(mysql, "drop table if exists t1");
+ mysql_query(mysql, "create table t1 (a char(5), b char(5), c char(5),"
+ " primary key (a, b, c))");
+ rc= mysql_query(mysql, "insert into t1 values ('x', 'y', 'z'), "
+ " ('a', 'b', 'c'), ('k', 'l', 'm')");
+ myquery(rc);
+
+ stmt= open_cursor("select distinct b from t1");
+
+ /*
+ Not crashes with:
+ stmt= open_cursor("select distinct a from t1");
+ */
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero(bind, sizeof(bind));
+ bind[0].buffer_type= MYSQL_TYPE_STRING;
+ bind[0].buffer= (char*) a;
+ bind[0].buffer_length= sizeof(a);
+ bind[0].length= &a_len;
+
+ mysql_stmt_bind_result(stmt, bind);
+
+ while (!(rc= mysql_stmt_fetch(stmt)))
+ row_count++;
+
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ printf("Fetched %d rows\n", row_count);
+ DBUG_ASSERT(row_count == 3);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+
/*
Read and parse arguments and MySQL options from my.cnf
*/
@@ -13079,6 +13133,7 @@ static struct my_tests_st my_tests[]= {
{ "test_bug8722", test_bug8722 },
{ "test_bug8880", test_bug8880 },
{ "test_bug9159", test_bug9159 },
+ { "test_bug9520", test_bug9520 },
{ 0, 0 }
};