List:Commits« Previous MessageNext Message »
From:Kristofer Pettersson Date:November 9 2009 10:35pm
Subject:bzr commit into mysql-5.1-bugteam branch (kristofer.pettersson:3190)
View as plain text  
#At file:///Users/thek/Development/mysql-5.1-bugteam/ based on revid:kristofer.pettersson@stripped

 3190 Kristofer Pettersson	2009-11-09 [merge]
      automerge

    modified:
      config/ac-macros/misc.m4
      configure.in
      extra/yassl/taocrypt/src/random.cpp
      libmysql/libmysql.c
      mysql-test/include/mtr_warnings.sql
      mysql-test/lib/My/SafeProcess/safe_process_win.cc
      mysql-test/r/innodb_lock_wait_timeout_1.result
      mysql-test/r/innodb_mysql.result
      mysql-test/r/olap.result
      mysql-test/t/disabled.def
      mysql-test/t/innodb_lock_wait_timeout_1.test
      mysql-test/t/innodb_mysql.test
      mysql-test/t/olap.test
      scripts/mysql_secure_installation.pl.in
      scripts/mysql_secure_installation.sh
      sql/item.cc
      sql/item.h
      sql/item_cmpfunc.cc
      sql/item_cmpfunc.h
      sql/item_subselect.cc
      sql/item_xmlfunc.cc
      sql/mysqld.cc
      sql/records.cc
      sql/rpl_rli.cc
      sql/sp_rcontext.cc
      sql/sql_select.cc
      sql/sql_select.h
      sql/structs.h
=== modified file 'config/ac-macros/misc.m4'
--- a/config/ac-macros/misc.m4	2009-07-30 15:24:09 +0000
+++ b/config/ac-macros/misc.m4	2009-11-04 23:08:21 +0000
@@ -601,15 +601,15 @@ dnl ------------------------------------
 
 dnl MYSQL_NEEDS_MYSYS_NEW
 AC_DEFUN([MYSQL_NEEDS_MYSYS_NEW],
-[AC_CACHE_CHECK([needs mysys_new helpers], mysql_use_mysys_new,
+[AC_CACHE_CHECK([needs mysys_new helpers], mysql_cv_use_mysys_new,
 [
 AC_LANG_PUSH(C++)
 AC_TRY_LINK([], [
 class A { public: int b; }; A *a=new A; a->b=10; delete a;
-], mysql_use_mysys_new=no, mysql_use_mysys_new=yes)
+], mysql_cv_use_mysys_new=no, mysql_cv_use_mysys_new=yes)
 AC_LANG_POP(C++)
 ])
-if test "$mysql_use_mysys_new" = "yes"
+if test "$mysql_cv_use_mysys_new" = "yes"
 then
   AC_DEFINE([USE_MYSYS_NEW], [1], [Needs to use mysys_new helpers])
 fi

=== modified file 'configure.in'
--- a/configure.in	2009-10-27 13:20:34 +0000
+++ b/configure.in	2009-11-05 09:48:50 +0000
@@ -13,6 +13,12 @@ AC_CANONICAL_SYSTEM
 AM_INIT_AUTOMAKE(mysql, 5.1.41)
 AM_CONFIG_HEADER([include/config.h:config.h.in])
 
+# Request support for automake silent-rules if available.
+# Default to verbose output. One can use the configure-time
+# option --enable-silent-rules or make V=1 to activate
+# silent rules.
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([no])])
+
 PROTOCOL_VERSION=10
 DOT_FRM_VERSION=6
 # See the libtool docs for information on how to do shared lib versions.

=== modified file 'extra/yassl/taocrypt/src/random.cpp'
--- a/extra/yassl/taocrypt/src/random.cpp	2007-01-29 15:54:40 +0000
+++ b/extra/yassl/taocrypt/src/random.cpp	2009-10-06 16:10:34 +0000
@@ -27,7 +27,6 @@
 #include <time.h>
 
 #if defined(_WIN32)
-    #define _WIN32_WINNT 0x0400
     #include <windows.h>
     #include <wincrypt.h>
 #else

=== modified file 'libmysql/libmysql.c'
--- a/libmysql/libmysql.c	2009-11-02 11:22:36 +0000
+++ b/libmysql/libmysql.c	2009-11-03 18:18:44 +0000
@@ -1631,12 +1631,14 @@ mysql_real_escape_string(MYSQL *mysql, c
 
 
 char * STDCALL
-mysql_odbc_escape_string(MYSQL *mysql,
-			 char *to, ulong to_length,
-			 const char *from, ulong from_length,
-			 void *param,
-			 char * (*extend_buffer)
-			 (void *, char *, ulong *))
+mysql_odbc_escape_string(MYSQL *mysql __attribute__((unused)),
+                         char *to __attribute__((unused)),
+                         ulong to_length __attribute__((unused)),
+                         const char *from __attribute__((unused)),
+                         ulong from_length __attribute__((unused)),
+                         void *param __attribute__((unused)),
+                         char * (*extend_buffer)(void *, char *, ulong *)
+                         __attribute__((unused)))
 {
   return NULL;
 }

=== modified file 'mysql-test/include/mtr_warnings.sql'
--- a/mysql-test/include/mtr_warnings.sql	2009-10-16 21:25:05 +0000
+++ b/mysql-test/include/mtr_warnings.sql	2009-11-06 17:08:06 +0000
@@ -175,6 +175,15 @@ INSERT INTO global_suppressions VALUES
  ("Can't find file: '.\\\\test\\\\\\?{8}.frm'"),
  ("Slave: Unknown table 't1' Error_code: 1051"),
 
+ /*
+   Transient network failures that cause warnings on reconnect.
+   BUG#47743 and BUG#47983.
+ */
+ ("Slave I/O: Get master SERVER_ID failed with error:.*"),
+ ("Slave I/O: Get master clock failed with error:.*"),
+ ("Slave I/O: Get master COLLATION_SERVER failed with error:.*"),
+ ("Slave I/O: Get master TIME_ZONE failed with error:.*"),
+
  ("THE_LAST_SUPPRESSION")||
 
 

=== modified file 'mysql-test/lib/My/SafeProcess/safe_process_win.cc'
--- a/mysql-test/lib/My/SafeProcess/safe_process_win.cc	2009-07-23 17:01:24 +0000
+++ b/mysql-test/lib/My/SafeProcess/safe_process_win.cc	2009-10-06 16:10:34 +0000
@@ -50,9 +50,6 @@
            is killed.
 */
 
-/* Requires Windows 2000 or higher */
-#define _WIN32_WINNT 0x0500
-
 #include <windows.h>
 #include <stdio.h>
 #include <tlhelp32.h>

=== modified file 'mysql-test/r/innodb_lock_wait_timeout_1.result'
--- a/mysql-test/r/innodb_lock_wait_timeout_1.result	2009-10-28 14:49:56 +0000
+++ b/mysql-test/r/innodb_lock_wait_timeout_1.result	2009-11-03 17:45:52 +0000
@@ -48,5 +48,310 @@ commit;
 set autocommit=default;
 drop table t1;
 #
+# Bug#41756 Strange error messages about locks from InnoDB
+#
+drop table if exists t1;
+# In the default transaction isolation mode, and/or with
+# innodb_locks_unsafe_for_binlog=OFF, handler::unlock_row()
+# in InnoDB does nothing.
+# Thus in order to reproduce the condition that led to the
+# warning, one needs to relax isolation by either
+# setting a weaker tx_isolation value, or by turning on
+# the unsafe replication switch.
+# For testing purposes, choose to tweak the isolation level,
+# since it's settable at runtime, unlike
+# innodb_locks_unsafe_for_binlog, which is
+# only a command-line switch.
+#
+set @@session.tx_isolation="read-committed";
+# Prepare data. We need a table with a unique index,
+# for join_read_key to be used. The other column
+# allows to control what passes WHERE clause filter.
+create table t1 (a int primary key, b int) engine=innodb;
+# Let's make sure t1 has sufficient amount of rows
+# to exclude JT_ALL access method when reading it,
+# i.e. make sure that JT_EQ_REF(a) is always preferred.
+insert into t1 values (1,1), (2,null), (3,1), (4,1),
+(5,1), (6,1), (7,1), (8,1), (9,1), (10,1),
+(11,1), (12,1), (13,1), (14,1), (15,1),
+(16,1), (17,1), (18,1), (19,1), (20,1);
+#
+# Demonstrate that for the SELECT statement
+# used later in the test JT_EQ_REF access method is used.
+#
+explain
+select 1 from t1 natural join (select 2 as a, 1 as b union all
+select 2 as a, 2 as b) as t2 for update;
+id	1
+select_type	PRIMARY
+table	<derived2>
+type	ALL
+possible_keys	NULL
+key	NULL
+key_len	NULL
+ref	NULL
+rows	2
+Extra	
+id	1
+select_type	PRIMARY
+table	t1
+type	eq_ref
+possible_keys	PRIMARY
+key	PRIMARY
+key_len	4
+ref	t2.a
+rows	1
+Extra	Using where
+id	2
+select_type	DERIVED
+table	NULL
+type	NULL
+possible_keys	NULL
+key	NULL
+key_len	NULL
+ref	NULL
+rows	NULL
+Extra	No tables used
+id	3
+select_type	UNION
+table	NULL
+type	NULL
+possible_keys	NULL
+key	NULL
+key_len	NULL
+ref	NULL
+rows	NULL
+Extra	No tables used
+id	NULL
+select_type	UNION RESULT
+table	<union2,3>
+type	ALL
+possible_keys	NULL
+key	NULL
+key_len	NULL
+ref	NULL
+rows	NULL
+Extra	
+#
+# Demonstrate that the reported SELECT statement
+# no longer produces warnings.
+#
+select 1 from t1 natural join (select 2 as a, 1 as b union all
+select 2 as a, 2 as b) as t2 for update;
+1
+commit;
+# 
+# Demonstrate that due to lack of inter-sweep "reset" function,
+# we keep some non-matching records locked, even though we know
+# we could unlock them.
+# To do that, show that if there is only one distinct value
+# for a in t2 (a=2), we will keep record (2,null) in t1 locked.
+# But if we add another value for "a" to t2, say 6,
+# join_read_key cache will be pruned at least once, 
+# and thus record (2, null) in t1 will get unlocked.
+#
+begin;
+select 1 from t1 natural join (select 2 as a, 1 as b union all
+select 2 as a, 2 as b) as t2 for update;
+1
+#
+# Switching to connection con1
+# We should be able to delete all records from t1 except (2, null),
+# since they were not locked.
+begin;
+# Delete in series of 3 records so that full scan
+# is not used and we're not blocked on record (2,null)
+delete from t1 where a in (1,3,4);
+delete from t1 where a in (5,6,7);
+delete from t1 where a in (8,9,10);
+delete from t1 where a in (11,12,13);
+delete from t1 where a in (14,15,16);
+delete from t1 where a in (17,18);
+delete from t1 where a in (19,20);
+# 
+# Record (2, null) is locked. This is actually unnecessary, 
+# because the previous select returned no rows. 
+# Just demonstrate the effect.
+#
+delete from t1;
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+rollback;
+#
+# Switching to connection default
+#
+# Show that the original contents of t1 is intact:
+select * from t1;
+a	b
+1	1
+2	NULL
+3	1
+4	1
+5	1
+6	1
+7	1
+8	1
+9	1
+10	1
+11	1
+12	1
+13	1
+14	1
+15	1
+16	1
+17	1
+18	1
+19	1
+20	1
+commit;
+#
+# Have a one more record in t2 to show that 
+# if join_read_key cache is purned, the current
+# row under the cursor is unlocked (provided, this row didn't 
+# match the partial WHERE clause, of course).
+# Sic: the result of this test dependent on the order of retrieval
+# of records --echo # from the derived table, if !
+# We use DELETE to disable the JOIN CACHE. This DELETE modifies no
+# records. It also should leave no InnoDB row locks.
+#
+begin;
+delete t1.* from t1 natural join (select 2 as a, 2 as b union all
+select 0 as a, 0 as b) as t2;
+# Demonstrate that nothing was deleted form t1
+select * from t1;
+a	b
+1	1
+2	NULL
+3	1
+4	1
+5	1
+6	1
+7	1
+8	1
+9	1
+10	1
+11	1
+12	1
+13	1
+14	1
+15	1
+16	1
+17	1
+18	1
+19	1
+20	1
+#
+# Switching to connection con1
+begin;
+# Since there is another distinct record in the derived table
+# the previous matching record in t1 -- (2,null) -- was unlocked.
+delete from t1;
+# We will need the contents of the table again.
+rollback;
+select * from t1;
+a	b
+1	1
+2	NULL
+3	1
+4	1
+5	1
+6	1
+7	1
+8	1
+9	1
+10	1
+11	1
+12	1
+13	1
+14	1
+15	1
+16	1
+17	1
+18	1
+19	1
+20	1
+commit;
+#
+# Switching to connection default
+rollback;
+begin;
+#
+# Before this patch, we could wrongly unlock a record
+# that was cached and later used in a join. Demonstrate that
+# this is no longer the case.
+# Sic: this test is also order-dependent (i.e. the
+# the bug would show up only if the first record in the union
+# is retreived and processed first.
+#
+# Verify that JT_EQ_REF is used.
+explain
+select 1 from t1 natural join (select 3 as a, 2 as b union all
+select 3 as a, 1 as b) as t2 for update;
+id	1
+select_type	PRIMARY
+table	<derived2>
+type	ALL
+possible_keys	NULL
+key	NULL
+key_len	NULL
+ref	NULL
+rows	2
+Extra	
+id	1
+select_type	PRIMARY
+table	t1
+type	eq_ref
+possible_keys	PRIMARY
+key	PRIMARY
+key_len	4
+ref	t2.a
+rows	1
+Extra	Using where
+id	2
+select_type	DERIVED
+table	NULL
+type	NULL
+possible_keys	NULL
+key	NULL
+key_len	NULL
+ref	NULL
+rows	NULL
+Extra	No tables used
+id	3
+select_type	UNION
+table	NULL
+type	NULL
+possible_keys	NULL
+key	NULL
+key_len	NULL
+ref	NULL
+rows	NULL
+Extra	No tables used
+id	NULL
+select_type	UNION RESULT
+table	<union2,3>
+type	ALL
+possible_keys	NULL
+key	NULL
+key_len	NULL
+ref	NULL
+rows	NULL
+Extra	
+# Lock the record.
+select 1 from t1 natural join (select 3 as a, 2 as b union all
+select 3 as a, 1 as b) as t2 for update;
+1
+1
+# Switching to connection con1
+#
+# We should not be able to delete record (3,1) from t1,
+# (previously it was possible).
+#
+delete from t1 where a=3;
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+# Switching to connection default
+commit;
+set @@session.tx_isolation=default;
+drop table t1;
+#
 # End of 5.1 tests
 #

=== modified file 'mysql-test/r/innodb_mysql.result'
--- a/mysql-test/r/innodb_mysql.result	2009-10-13 04:43:27 +0000
+++ b/mysql-test/r/innodb_mysql.result	2009-11-04 11:54:28 +0000
@@ -2251,4 +2251,26 @@ c >= '2009-10-09 00:00:00.001' AND c <= 
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
 DROP TABLE t1;
+#
+# Bug #46175: NULL read_view and consistent read assertion
+#
+CREATE TABLE t1(a CHAR(13),KEY(a)) ENGINE=innodb;
+CREATE TABLE t2(b DATETIME,KEY(b)) ENGINE=innodb;
+INSERT INTO t1 VALUES (),();
+INSERT INTO t2 VALUES (),();
+CREATE OR REPLACE VIEW v1 AS SELECT 1 FROM t2 
+WHERE b =(SELECT a FROM t1 LIMIT 1);
+CREATE PROCEDURE p1(num INT)
+BEGIN
+DECLARE i INT DEFAULT 0;
+REPEAT
+SHOW CREATE VIEW v1;
+SET i:=i+1;
+UNTIL i>num END REPEAT;
+END|
+# Should not crash
+# Should not crash
+DROP PROCEDURE p1;
+DROP VIEW v1;
+DROP TABLE t1,t2;
 End of 5.1 tests

=== modified file 'mysql-test/r/olap.result'
--- a/mysql-test/r/olap.result	2009-10-30 15:59:06 +0000
+++ b/mysql-test/r/olap.result	2009-11-06 14:54:19 +0000
@@ -753,4 +753,16 @@ b
 100
 NULL
 DROP TABLE t1, t2;
+#
+# Bug #48475: DISTINCT is ignored with GROUP BY WITH ROLLUP
+#             and only const tables
+CREATE TABLE t1 (a INT);
+CREATE TABLE t2 (b INT);
+INSERT INTO t1 VALUES (1);
+INSERT INTO t2 VALUES (1);
+SELECT DISTINCT b FROM t1, t2 GROUP BY a, b WITH ROLLUP;
+b
+1
+NULL
+DROP TABLE t1, t2;
 End of 5.0 tests

=== modified file 'mysql-test/t/disabled.def'
--- a/mysql-test/t/disabled.def	2009-11-03 08:46:04 +0000
+++ b/mysql-test/t/disabled.def	2009-11-04 09:18:34 +0000
@@ -14,3 +14,4 @@ query_cache_28249        : Bug#43861 200
 partition_innodb_builtin : Bug#32430 2009-09-25 mattiasj Waiting for push of Innodb changes
 partition_innodb_plugin  : Bug#32430 2009-09-25 mattiasj Waiting for push of Innodb changes
 innodb-autoinc           : Bug#48482 2009-11-02 svoj innodb-autoinc.test fails with results difference
+rpl_killed_ddl           : Bug#45520: rpl_killed_ddl fails sporadically in pb2

=== modified file 'mysql-test/t/innodb_lock_wait_timeout_1.test'
--- a/mysql-test/t/innodb_lock_wait_timeout_1.test	2009-10-28 14:49:56 +0000
+++ b/mysql-test/t/innodb_lock_wait_timeout_1.test	2009-11-03 17:45:52 +0000
@@ -71,5 +71,160 @@ set autocommit=default;
 drop table t1;
 
 --echo #
+--echo # Bug#41756 Strange error messages about locks from InnoDB
+--echo #
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+--echo # In the default transaction isolation mode, and/or with
+--echo # innodb_locks_unsafe_for_binlog=OFF, handler::unlock_row()
+--echo # in InnoDB does nothing.
+--echo # Thus in order to reproduce the condition that led to the
+--echo # warning, one needs to relax isolation by either
+--echo # setting a weaker tx_isolation value, or by turning on
+--echo # the unsafe replication switch.
+--echo # For testing purposes, choose to tweak the isolation level,
+--echo # since it's settable at runtime, unlike
+--echo # innodb_locks_unsafe_for_binlog, which is
+--echo # only a command-line switch.
+--echo #
+set @@session.tx_isolation="read-committed";
+
+--echo # Prepare data. We need a table with a unique index,
+--echo # for join_read_key to be used. The other column
+--echo # allows to control what passes WHERE clause filter.
+create table t1 (a int primary key, b int) engine=innodb;
+--echo # Let's make sure t1 has sufficient amount of rows
+--echo # to exclude JT_ALL access method when reading it,
+--echo # i.e. make sure that JT_EQ_REF(a) is always preferred.
+insert into t1 values (1,1), (2,null), (3,1), (4,1),
+                      (5,1), (6,1), (7,1), (8,1), (9,1), (10,1),
+                      (11,1), (12,1), (13,1), (14,1), (15,1),
+                      (16,1), (17,1), (18,1), (19,1), (20,1);
+--echo #
+--echo # Demonstrate that for the SELECT statement
+--echo # used later in the test JT_EQ_REF access method is used.
+--echo #
+--vertical_results
+explain
+select 1 from t1 natural join (select 2 as a, 1 as b union all
+                               select 2 as a, 2 as b) as t2 for update;
+--horizontal_results
+--echo #
+--echo # Demonstrate that the reported SELECT statement
+--echo # no longer produces warnings.
+--echo #
+select 1 from t1 natural join (select 2 as a, 1 as b union all
+                               select 2 as a, 2 as b) as t2 for update;
+commit;
+--echo # 
+--echo # Demonstrate that due to lack of inter-sweep "reset" function,
+--echo # we keep some non-matching records locked, even though we know
+--echo # we could unlock them.
+--echo # To do that, show that if there is only one distinct value
+--echo # for a in t2 (a=2), we will keep record (2,null) in t1 locked.
+--echo # But if we add another value for "a" to t2, say 6,
+--echo # join_read_key cache will be pruned at least once, 
+--echo # and thus record (2, null) in t1 will get unlocked.
+--echo #
+begin;
+select 1 from t1 natural join (select 2 as a, 1 as b union all
+                               select 2 as a, 2 as b) as t2 for update;
+connect (con1,localhost,root,,);
+--echo #
+--echo # Switching to connection con1
+connection con1;
+--echo # We should be able to delete all records from t1 except (2, null),
+--echo # since they were not locked.
+begin;
+--echo # Delete in series of 3 records so that full scan
+--echo # is not used and we're not blocked on record (2,null)
+delete from t1 where a in (1,3,4);
+delete from t1 where a in (5,6,7);
+delete from t1 where a in (8,9,10);
+delete from t1 where a in (11,12,13);
+delete from t1 where a in (14,15,16);
+delete from t1 where a in (17,18);
+delete from t1 where a in (19,20);
+--echo # 
+--echo # Record (2, null) is locked. This is actually unnecessary, 
+--echo # because the previous select returned no rows. 
+--echo # Just demonstrate the effect.
+--echo #
+--error ER_LOCK_WAIT_TIMEOUT
+delete from t1;
+rollback;
+--echo #
+--echo # Switching to connection default
+connection default;
+--echo #
+--echo # Show that the original contents of t1 is intact:
+select * from t1;
+commit;
+--echo #
+--echo # Have a one more record in t2 to show that 
+--echo # if join_read_key cache is purned, the current
+--echo # row under the cursor is unlocked (provided, this row didn't 
+--echo # match the partial WHERE clause, of course).
+--echo # Sic: the result of this test dependent on the order of retrieval
+--echo # of records --echo # from the derived table, if !
+--echo # We use DELETE to disable the JOIN CACHE. This DELETE modifies no
+--echo # records. It also should leave no InnoDB row locks.
+--echo #
+begin;
+delete t1.* from t1 natural join (select 2 as a, 2 as b union all
+                                  select 0 as a, 0 as b) as t2;
+--echo # Demonstrate that nothing was deleted form t1
+select * from t1;
+--echo #
+--echo # Switching to connection con1
+connection con1;
+begin;
+--echo # Since there is another distinct record in the derived table
+--echo # the previous matching record in t1 -- (2,null) -- was unlocked.
+delete from t1;
+--echo # We will need the contents of the table again.
+rollback;
+select * from t1;
+commit;
+--echo #
+--echo # Switching to connection default
+connection default;
+rollback;
+begin;
+--echo #
+--echo # Before this patch, we could wrongly unlock a record
+--echo # that was cached and later used in a join. Demonstrate that
+--echo # this is no longer the case.
+--echo # Sic: this test is also order-dependent (i.e. the
+--echo # the bug would show up only if the first record in the union
+--echo # is retreived and processed first.
+--echo #
+--echo # Verify that JT_EQ_REF is used.
+--vertical_results
+explain
+select 1 from t1 natural join (select 3 as a, 2 as b union all
+                               select 3 as a, 1 as b) as t2 for update;
+--horizontal_results
+--echo # Lock the record.
+select 1 from t1 natural join (select 3 as a, 2 as b union all
+                               select 3 as a, 1 as b) as t2 for update;
+--echo # Switching to connection con1
+connection con1;
+--echo #
+--echo # We should not be able to delete record (3,1) from t1,
+--echo # (previously it was possible).
+--echo #
+--error ER_LOCK_WAIT_TIMEOUT
+delete from t1 where a=3;
+--echo # Switching to connection default
+connection default;
+commit;
+
+disconnect con1;
+set @@session.tx_isolation=default;
+drop table t1;
+
+--echo #
 --echo # End of 5.1 tests
 --echo #

=== modified file 'mysql-test/t/innodb_mysql.test'
--- a/mysql-test/t/innodb_mysql.test	2009-10-13 04:43:27 +0000
+++ b/mysql-test/t/innodb_mysql.test	2009-11-04 11:54:28 +0000
@@ -489,5 +489,51 @@ EXPLAIN SELECT * FROM t1 WHERE a = 'TEST
   c >= '2009-10-09 00:00:00.001' AND c <= '2009-10-09 00:00:00.00';
 DROP TABLE t1;
 
+--echo #
+--echo # Bug #46175: NULL read_view and consistent read assertion
+--echo #
+
+CREATE TABLE t1(a CHAR(13),KEY(a)) ENGINE=innodb;
+CREATE TABLE t2(b DATETIME,KEY(b)) ENGINE=innodb;
+INSERT INTO t1 VALUES (),();
+INSERT INTO t2 VALUES (),();
+CREATE OR REPLACE VIEW v1 AS SELECT 1 FROM t2 
+  WHERE b =(SELECT a FROM t1 LIMIT 1);
+
+--disable_query_log
+--disable_result_log
+CONNECT (con1, localhost, root,,);
+--enable_query_log
+--enable_result_log
+CONNECTION default;
+
+DELIMITER |;
+CREATE PROCEDURE p1(num INT)
+BEGIN
+  DECLARE i INT DEFAULT 0;
+  REPEAT
+    SHOW CREATE VIEW v1;
+    SET i:=i+1;
+  UNTIL i>num END REPEAT;
+END|
+DELIMITER ;|
+
+--echo # Should not crash
+--disable_query_log
+--disable_result_log
+--send CALL p1(1000)
+CONNECTION con1;
+--echo # Should not crash
+CALL p1(1000);
+
+CONNECTION default;
+--reap
+--enable_query_log
+--enable_result_log
+
+DISCONNECT con1;
+DROP PROCEDURE p1;
+DROP VIEW v1;
+DROP TABLE t1,t2;
 
 --echo End of 5.1 tests

=== modified file 'mysql-test/t/olap.test'
--- a/mysql-test/t/olap.test	2009-10-30 15:54:53 +0000
+++ b/mysql-test/t/olap.test	2009-11-06 06:44:01 +0000
@@ -390,4 +390,17 @@ SELECT DISTINCT b FROM t1, t2 GROUP BY a
 
 DROP TABLE t1, t2;
 
+--echo #
+--echo # Bug #48475: DISTINCT is ignored with GROUP BY WITH ROLLUP
+--echo #             and only const tables
+
+CREATE TABLE t1 (a INT);
+CREATE TABLE t2 (b INT);
+INSERT INTO t1 VALUES (1);
+INSERT INTO t2 VALUES (1);
+
+SELECT DISTINCT b FROM t1, t2 GROUP BY a, b WITH ROLLUP;
+
+DROP TABLE t1, t2;
+
 --echo End of 5.0 tests

=== modified file 'scripts/mysql_secure_installation.pl.in'
--- a/scripts/mysql_secure_installation.pl.in	2007-12-28 21:58:54 +0000
+++ b/scripts/mysql_secure_installation.pl.in	2009-11-03 21:34:01 +0000
@@ -17,16 +17,41 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 use Fcntl;
+use File::Spec;
+use if $^O eq 'MSWin32', 'Term::ReadKey' => qw/ReadMode/;
 use strict;
 
 my $config  = ".my.cnf.$$";
 my $command = ".mysql.$$";
 my $hadpass = 0;
+my $mysql;  # How to call the mysql client
+my $rootpass = "";
 
-# FIXME
-# trap "interrupt" 2
 
-my $rootpass = "";
+$SIG{QUIT} = $SIG{INT} = sub {
+  print "\nAborting!\n\n";
+  echo_on();
+  cleanup();
+  exit 1;
+};
+
+
+END {
+  # Remove temporary files, even if exiting via die(), etc.
+  cleanup();
+}
+
+
+sub read_without_echo {
+  my ($prompt) = @_;
+  print $prompt;
+  echo_off();
+  my $answer = <STDIN>;
+  echo_on();
+  print "\n";
+  chomp($answer);
+  return $answer;
+}
 
 sub echo_on {
   if ($^O eq 'MSWin32') {
@@ -55,6 +80,25 @@ sub write_file {
 }
 
 sub prepare {
+  # Locate the mysql client; look in current directory first, then
+  # in path
+  our $SAVEERR;   # Suppress Perl warning message
+  open SAVEERR, ">& STDERR";
+  close STDERR;
+  for my $m (File::Spec->catfile('bin', 'mysql'), 'mysql') {
+    # mysql --version should always work
+    qx($m --no-defaults --version);
+    next unless $? == 0;
+
+    $mysql = $m;
+    last;
+  }
+  open STDERR, ">& SAVEERR";
+
+  die "Can't find a 'mysql' client in PATH or ./bin\n"
+    unless $mysql;
+
+  # Create safe files to avoid leaking info to other users
   foreach my $file ( $config, $command ) {
     next if -f $file;                   # Already exists
     local *FILE;
@@ -64,30 +108,50 @@ sub prepare {
   }
 }
 
+# Simple escape mechanism (\-escape any ' and \), suitable for two contexts:
+# - single-quoted SQL strings
+# - single-quoted option values on the right hand side of = in my.cnf
+#
+# These two contexts don't handle escapes identically.  SQL strings allow
+# quoting any character (\C => C, for any C), but my.cnf parsing allows
+# quoting only \, ' or ".  For example, password='a\b' quotes a 3-character
+# string in my.cnf, but a 2-character string in SQL.
+#
+# This simple escape works correctly in both places.
+sub basic_single_escape {
+  my ($str) = @_;
+  # Inside a character class, \ is not special; this escapes both \ and '
+  $str =~ s/([\'])/\\$1/g;
+  return $str;
+}
+
 sub do_query {
   my $query   = shift;
   write_file($command, $query);
-  system("mysql --defaults-file=$config < $command");
-  return $?;
+  my $rv = system("$mysql --defaults-file=$config < $command");
+  # system() returns -1 if exec fails (e.g., command not found, etc.); die
+  # in this case because nothing is going to work
+  die "Failed to execute mysql client '$mysql'\n" if $rv == -1;
+  # Return true if query executed OK, or false if there was some problem
+  # (for example, SQL error or wrong password)
+  return ($rv == 0 ? 1 : undef);
 }
 
 sub make_config {
   my $password = shift;
 
+  my $esc_pass = basic_single_escape($rootpass);
   write_file($config,
              "# mysql_secure_installation config file",
              "[mysql]",
              "user=root",
-             "password=$rootpass");
+             "password='$esc_pass'");
 }
 
 sub get_root_password {
-  my $status = 1;
-  while ( $status == 1 ) {
-    echo_off();
-    print "Enter current password for root (enter for none): ";
-    my $password = <STDIN>;
-    echo_on();
+  my $attempts = 3;
+  for (;;) {
+    my $password = read_without_echo("Enter current password for root (enter for none): ");
     if ( $password ) {
       $hadpass = 1;
     } else {
@@ -95,64 +159,56 @@ sub get_root_password {
     }
     $rootpass = $password;
     make_config($rootpass);
-    do_query("");
-    $status = $?;
+    last if do_query("");
+
+    die "Unable to connect to the server as root user, giving up.\n"
+      if --$attempts == 0;
   }
   print "OK, successfully used password, moving on...\n\n";
 }
 
 sub set_root_password {
-  echo_off();
-  print "New password: ";
-  my $password1 = <STDIN>;
-  print "\nRe-enter new password: ";
-  my $password2 = <STDIN>;
-  print "\n";
-  echo_on();
-
-  if ( $password1 eq $password2 ) {
-    print "Sorry, passwords do not match.\n\n";
-    return 1;
-  }
+  my $password1;
+  for (;;) {
+    $password1 = read_without_echo("New password: ");
+
+    if ( !$password1 ) {
+      print "Sorry, you can't use an empty password here.\n\n";
+      next;
+    }
 
-  if ( !$password1 ) {
-    print "Sorry, you can't use an empty password here.\n\n";
-    return 1;
-  }
+    my $password2 = read_without_echo("Re-enter new password: ");
 
-  do_query("UPDATE mysql.user SET Password=PASSWORD('$password1') WHERE User='root';");
-  if ( $? == 0 ) {
-    print "Password updated successfully!\n";
-    print "Reloading privilege tables..\n";
-    if ( !reload_privilege_tables() ) {
-      exit 1;
+    if ( $password1 ne $password2 ) {
+      print "Sorry, passwords do not match.\n\n";
+      next;
     }
-    print "\n";
-    $rootpass = $password1;
-    make_config($rootpass);
-  } else {
-    print "Password update failed!\n";
-    exit 1;
+
+    last;
   }
 
-  return 0;
+  my $esc_pass = basic_single_escape($password1);
+  do_query("UPDATE mysql.user SET Password=PASSWORD('$esc_pass') WHERE User='root';")
+    or die "Password update failed!\n";
+
+  print "Password updated successfully!\n";
+  print "Reloading privilege tables..\n";
+  reload_privilege_tables()
+    or die "Can not continue.\n";
+
+  print "\n";
+  $rootpass = $password1;
+  make_config($rootpass);
 }
 
 sub remove_anonymous_users {
-  do_query("DELETE FROM mysql.user WHERE User='';");
-  if ( $? == 0 ) {
-    print " ... Success!\n";
-  } else {
-    print " ... Failed!\n";
-    exit 1;
-  }
-
-  return 0;
+  do_query("DELETE FROM mysql.user WHERE User='';")
+    or die print " ... Failed!\n";
+  print " ... Success!\n";
 }
 
 sub remove_remote_root {
-  do_query("DELETE FROM mysql.user WHERE User='root' AND Host!='localhost';");
-  if ( $? == 0 ) {
+  if (do_query("DELETE FROM mysql.user WHERE User='root' AND Host!='localhost';")) {
     print " ... Success!\n";
   } else {
     print " ... Failed!\n";
@@ -161,44 +217,31 @@ sub remove_remote_root {
 
 sub remove_test_database {
   print " - Dropping test database...\n";
-  do_query("DROP DATABASE test;");
-  if ( $? == 0 ) {
+  if (do_query("DROP DATABASE test;")) {
     print " ... Success!\n";
   } else {
     print " ... Failed!  Not critical, keep moving...\n";
   }
 
   print " - Removing privileges on test database...\n";
-  do_query("DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'");
-  if ( $? == 0 ) {
+  if (do_query("DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'")) {
     print " ... Success!\n";
   } else {
     print " ... Failed!  Not critical, keep moving...\n";
   }
-
-  return 0;
 }
 
 sub reload_privilege_tables {
-  do_query("FLUSH PRIVILEGES;");
-  if ( $? == 0 ) {
+  if (do_query("FLUSH PRIVILEGES;")) {
     print " ... Success!\n";
-    return 0;
+    return 1;
   } else {
     print " ... Failed!\n";
-    return 1;
+    return undef;
   }
 }
 
-sub interrupt {
-  print "\nAborting!\n\n";
-  cleanup();
-  echo_on();
-  exit 1;
-}
-
 sub cleanup {
-  print "Cleaning up...\n";
   unlink($config,$command);
 }
 
@@ -242,11 +285,7 @@ my $reply = <STDIN>;
 if ( $reply =~ /n/i ) {
   print " ... skipping.\n";
 } else {
-  my $status = 1;
-  while ( $status == 1 ) {
-    set_root_password();
-    $status = $?;
-  }
+  set_root_password();
 }
 print "\n";
 
@@ -334,8 +373,6 @@ if ( $reply =~ /n/i ) {
 }
 print "\n";
 
-cleanup();
-
 print <<HERE;
 
 

=== modified file 'scripts/mysql_secure_installation.sh'
--- a/scripts/mysql_secure_installation.sh	2007-01-01 04:31:23 +0000
+++ b/scripts/mysql_secure_installation.sh	2009-11-03 20:50:28 +0000
@@ -38,16 +38,39 @@ prepare() {
 }
 
 do_query() {
-    echo $1 >$command
+    echo "$1" >$command
+    #sed 's,^,> ,' < $command  # Debugging
     mysql --defaults-file=$config <$command
     return $?
 }
 
+# Simple escape mechanism (\-escape any ' and \), suitable for two contexts:
+# - single-quoted SQL strings
+# - single-quoted option values on the right hand side of = in my.cnf
+#
+# These two contexts don't handle escapes identically.  SQL strings allow
+# quoting any character (\C => C, for any C), but my.cnf parsing allows
+# quoting only \, ' or ".  For example, password='a\b' quotes a 3-character
+# string in my.cnf, but a 2-character string in SQL.
+#
+# This simple escape works correctly in both places.
+basic_single_escape () {
+    # The quoting on this sed command is a bit complex.  Single-quoted strings
+    # don't allow *any* escape mechanism, so they cannot contain a single
+    # quote.  The string sed gets (as argv[1]) is:  s/\(['\]\)/\\\1/g
+    #
+    # Inside a character class, \ and ' are not special, so the ['\] character
+    # class is balanced and contains two characters.
+    echo "$1" | sed 's/\(['"'"'\]\)/\\\1/g'
+}
+
 make_config() {
     echo "# mysql_secure_installation config file" >$config
     echo "[mysql]" >>$config
     echo "user=root" >>$config
-    echo "password=$rootpass" >>$config
+    esc_pass=`basic_single_escape "$rootpass"`
+    echo "password='$esc_pass'" >>$config
+    #sed 's,^,> ,' < $config  # Debugging
 }
 
 get_root_password() {
@@ -94,13 +117,12 @@ set_root_password() {
 	return 1
     fi
 
-    do_query "UPDATE mysql.user SET Password=PASSWORD('$password1') WHERE User='root';"
+    esc_pass=`basic_single_escape "$password1"`
+    do_query "UPDATE mysql.user SET Password=PASSWORD('$esc_pass') WHERE User='root';"
     if [ $? -eq 0 ]; then
 	echo "Password updated successfully!"
 	echo "Reloading privilege tables.."
-	if ! reload_privilege_tables; then
-	    exit 1
-	fi
+	reload_privilege_tables || exit 1
 	echo
 	rootpass=$password1
 	make_config

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2009-11-02 11:21:39 +0000
+++ b/sql/item.cc	2009-11-06 19:42:24 +0000
@@ -6940,7 +6940,22 @@ int stored_field_cmp_to_item(THD *thd, F
 
 Item_cache* Item_cache::get_cache(const Item *item)
 {
-  switch (item->result_type()) {
+  return get_cache(item, item->result_type());
+}
+
+
+/**
+  Get a cache item of given type.
+
+  @param item         value to be cached
+  @param type         required type of cache
+
+  @return cache item
+*/
+
+Item_cache* Item_cache::get_cache(const Item *item, const Item_result type)
+{
+  switch (type) {
   case INT_RESULT:
     return new Item_cache_int();
   case REAL_RESULT:
@@ -6958,6 +6973,12 @@ Item_cache* Item_cache::get_cache(const 
   }
 }
 
+void Item_cache::store(Item *item)
+{
+  if (item)
+    example= item;
+  value_cached= FALSE;
+}
 
 void Item_cache::print(String *str, enum_query_type query_type)
 {
@@ -6969,17 +6990,19 @@ void Item_cache::print(String *str, enum
   str->append(')');
 }
 
-
-void Item_cache_int::store(Item *item)
+void  Item_cache_int::cache_value()
 {
-  value= item->val_int_result();
-  null_value= item->null_value;
-  unsigned_flag= item->unsigned_flag;
+  value_cached= TRUE;
+  value= example->val_int_result();
+  null_value= example->null_value;
+  unsigned_flag= example->unsigned_flag;
 }
 
 
 void Item_cache_int::store(Item *item, longlong val_arg)
 {
+  /* An explicit values is given, save it. */
+  value_cached= TRUE;
   value= val_arg;
   null_value= item->null_value;
   unsigned_flag= item->unsigned_flag;
@@ -6989,6 +7012,8 @@ void Item_cache_int::store(Item *item, l
 String *Item_cache_int::val_str(String *str)
 {
   DBUG_ASSERT(fixed == 1);
+  if (!value_cached)
+    cache_value();
   str->set(value, default_charset());
   return str;
 }
@@ -6997,21 +7022,49 @@ String *Item_cache_int::val_str(String *
 my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val)
 {
   DBUG_ASSERT(fixed == 1);
+  if (!value_cached)
+    cache_value();
   int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_val);
   return decimal_val;
 }
 
+double Item_cache_int::val_real()
+{
+  DBUG_ASSERT(fixed == 1);
+  if (!value_cached)
+    cache_value();
+  return (double) value;
+}
 
-void Item_cache_real::store(Item *item)
+longlong Item_cache_int::val_int()
 {
-  value= item->val_result();
-  null_value= item->null_value;
+  DBUG_ASSERT(fixed == 1);
+  if (!value_cached)
+    cache_value();
+  return value;
 }
 
+void Item_cache_real::cache_value()
+{
+  value_cached= TRUE;
+  value= example->val_result();
+  null_value= example->null_value;
+}
+
+
+double Item_cache_real::val_real()
+{
+  DBUG_ASSERT(fixed == 1);
+  if (!value_cached)
+    cache_value();
+  return value;
+}
 
 longlong Item_cache_real::val_int()
 {
   DBUG_ASSERT(fixed == 1);
+  if (!value_cached)
+    cache_value();
   return (longlong) rint(value);
 }
 
@@ -7019,6 +7072,8 @@ longlong Item_cache_real::val_int()
 String* Item_cache_real::val_str(String *str)
 {
   DBUG_ASSERT(fixed == 1);
+  if (!value_cached)
+    cache_value();
   str->set_real(value, decimals, default_charset());
   return str;
 }
@@ -7027,15 +7082,18 @@ String* Item_cache_real::val_str(String 
 my_decimal *Item_cache_real::val_decimal(my_decimal *decimal_val)
 {
   DBUG_ASSERT(fixed == 1);
+  if (!value_cached)
+    cache_value();
   double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val);
   return decimal_val;
 }
 
 
-void Item_cache_decimal::store(Item *item)
+void Item_cache_decimal::cache_value()
 {
-  my_decimal *val= item->val_decimal_result(&decimal_value);
-  if (!(null_value= item->null_value) && val != &decimal_value)
+  value_cached= TRUE;
+  my_decimal *val= example->val_decimal_result(&decimal_value);
+  if (!(null_value= example->null_value) && val != &decimal_value)
     my_decimal2decimal(val, &decimal_value);
 }
 
@@ -7043,6 +7101,8 @@ double Item_cache_decimal::val_real()
 {
   DBUG_ASSERT(fixed);
   double res;
+  if (!value_cached)
+    cache_value();
   my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &res);
   return res;
 }
@@ -7051,6 +7111,8 @@ longlong Item_cache_decimal::val_int()
 {
   DBUG_ASSERT(fixed);
   longlong res;
+  if (!value_cached)
+    cache_value();
   my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &res);
   return res;
 }
@@ -7058,6 +7120,8 @@ longlong Item_cache_decimal::val_int()
 String* Item_cache_decimal::val_str(String *str)
 {
   DBUG_ASSERT(fixed);
+  if (!value_cached)
+    cache_value();
   my_decimal_round(E_DEC_FATAL_ERROR, &decimal_value, decimals, FALSE,
                    &decimal_value);
   my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, str);
@@ -7067,15 +7131,18 @@ String* Item_cache_decimal::val_str(Stri
 my_decimal *Item_cache_decimal::val_decimal(my_decimal *val)
 {
   DBUG_ASSERT(fixed);
+  if (!value_cached)
+    cache_value();
   return &decimal_value;
 }
 
 
-void Item_cache_str::store(Item *item)
+void Item_cache_str::cache_value()
 {
-  value_buff.set(buffer, sizeof(buffer), item->collation.collation);
-  value= item->str_result(&value_buff);
-  if ((null_value= item->null_value))
+  value_cached= TRUE;
+  value_buff.set(buffer, sizeof(buffer), example->collation.collation);
+  value= example->str_result(&value_buff);
+  if ((null_value= example->null_value))
     value= 0;
   else if (value != &value_buff)
   {
@@ -7097,6 +7164,8 @@ double Item_cache_str::val_real()
   DBUG_ASSERT(fixed == 1);
   int err_not_used;
   char *end_not_used;
+  if (!value_cached)
+    cache_value();
   if (value)
     return my_strntod(value->charset(), (char*) value->ptr(),
 		      value->length(), &end_not_used, &err_not_used);
@@ -7108,6 +7177,8 @@ longlong Item_cache_str::val_int()
 {
   DBUG_ASSERT(fixed == 1);
   int err;
+  if (!value_cached)
+    cache_value();
   if (value)
     return my_strntoll(value->charset(), value->ptr(),
 		       value->length(), 10, (char**) 0, &err);
@@ -7115,9 +7186,21 @@ longlong Item_cache_str::val_int()
     return (longlong)0;
 }
 
+
+String* Item_cache_str::val_str(String *str)
+{
+  DBUG_ASSERT(fixed == 1);
+  if (!value_cached)
+    cache_value();
+  return value;
+}
+
+
 my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val)
 {
   DBUG_ASSERT(fixed == 1);
+  if (!value_cached)
+    cache_value();
   if (value)
     string2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val);
   else
@@ -7128,6 +7211,8 @@ my_decimal *Item_cache_str::val_decimal(
 
 int Item_cache_str::save_in_field(Field *field, bool no_conversions)
 {
+  if (!value_cached)
+    cache_value();
   int res= Item_cache::save_in_field(field, no_conversions);
   return (is_varbinary && field->type() == MYSQL_TYPE_STRING &&
           value->length() < field->field_length) ? 1 : res;
@@ -7162,11 +7247,19 @@ bool Item_cache_row::setup(Item * item)
 
 void Item_cache_row::store(Item * item)
 {
+  for (uint i= 0; i < item_count; i++)
+    values[i]->store(item->element_index(i));
+}
+
+
+void Item_cache_row::cache_value()
+{
+  value_cached= TRUE;
   null_value= 0;
-  item->bring_value();
+  example->bring_value();
   for (uint i= 0; i < item_count; i++)
   {
-    values[i]->store(item->element_index(i));
+    values[i]->cache_value();
     null_value|= values[i]->null_value;
   }
 }

=== modified file 'sql/item.h'
--- a/sql/item.h	2009-11-02 11:21:39 +0000
+++ b/sql/item.h	2009-11-06 19:42:24 +0000
@@ -1024,7 +1024,11 @@ class sp_head;
 
 class Item_basic_constant :public Item
 {
+  table_map used_table_map;
 public:
+  Item_basic_constant(): Item(), used_table_map(0) {};
+  void set_used_tables(table_map map) { used_table_map= map; }
+  table_map used_tables() const { return used_table_map; }
   /* to prevent drop fixed flag (no need parent cleanup call) */
   void cleanup()
   {
@@ -2889,15 +2893,25 @@ protected:
   */  
   Field *cached_field;
   enum enum_field_types cached_field_type;
-public:
-  Item_cache(): 
-    example(0), used_table_map(0), cached_field(0), cached_field_type(MYSQL_TYPE_STRING) 
+  /*
+    TRUE <=> cache holds value of the last stored item (i.e actual value).
+    store() stores item to be cached and sets this flag to FALSE.
+    On the first call of val_xxx function if this flag is set to FALSE the 
+    cache_value() will be called to actually cache value of saved item.
+    cache_value() will set this flag to TRUE.
+  */
+  bool value_cached;
+public:
+  Item_cache():
+    example(0), used_table_map(0), cached_field(0), cached_field_type(MYSQL_TYPE_STRING),
+    value_cached(0)
   {
     fixed= 1; 
     null_value= 1;
   }
   Item_cache(enum_field_types field_type_arg):
-    example(0), used_table_map(0), cached_field(0), cached_field_type(field_type_arg)
+    example(0), used_table_map(0), cached_field(0), cached_field_type(field_type_arg),
+    value_cached(0)
   {
     fixed= 1;
     null_value= 1;
@@ -2917,10 +2931,10 @@ public:
       cached_field= ((Item_field *)item)->field;
     return 0;
   };
-  virtual void store(Item *)= 0;
   enum Type type() const { return CACHE_ITEM; }
   enum_field_types field_type() const { return cached_field_type; }
   static Item_cache* get_cache(const Item *item);
+  static Item_cache* get_cache(const Item* item, const Item_result type);
   table_map used_tables() const { return used_table_map; }
   virtual void keep_array() {}
   virtual void print(String *str, enum_query_type query_type);
@@ -2932,6 +2946,8 @@ public:
   {
     return this == item;
   }
+  virtual void store(Item *item);
+  virtual void cache_value()= 0;
 };
 
 
@@ -2940,18 +2956,19 @@ class Item_cache_int: public Item_cache
 protected:
   longlong value;
 public:
-  Item_cache_int(): Item_cache(), value(0) {}
+  Item_cache_int(): Item_cache(),
+    value(0) {}
   Item_cache_int(enum_field_types field_type_arg):
     Item_cache(field_type_arg), value(0) {}
 
-  void store(Item *item);
   void store(Item *item, longlong val_arg);
-  double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; }
-  longlong val_int() { DBUG_ASSERT(fixed == 1); return value; }
+  double val_real();
+  longlong val_int();
   String* val_str(String *str);
   my_decimal *val_decimal(my_decimal *);
   enum Item_result result_type() const { return INT_RESULT; }
   bool result_as_longlong() { return TRUE; }
+  void cache_value();
 };
 
 
@@ -2959,14 +2976,15 @@ class Item_cache_real: public Item_cache
 {
   double value;
 public:
-  Item_cache_real(): Item_cache(), value(0) {}
+  Item_cache_real(): Item_cache(),
+    value(0) {}
 
-  void store(Item *item);
-  double val_real() { DBUG_ASSERT(fixed == 1); return value; }
+  double val_real();
   longlong val_int();
   String* val_str(String *str);
   my_decimal *val_decimal(my_decimal *);
   enum Item_result result_type() const { return REAL_RESULT; }
+  void cache_value();
 };
 
 
@@ -2977,12 +2995,12 @@ protected:
 public:
   Item_cache_decimal(): Item_cache() {}
 
-  void store(Item *item);
   double val_real();
   longlong val_int();
   String* val_str(String *str);
   my_decimal *val_decimal(my_decimal *);
   enum Item_result result_type() const { return DECIMAL_RESULT; }
+  void cache_value();
 };
 
 
@@ -3000,14 +3018,14 @@ public:
                    MYSQL_TYPE_VARCHAR &&
                  !((const Item_field *) item)->field->has_charset())
   {}
-  void store(Item *item);
   double val_real();
   longlong val_int();
-  String* val_str(String *) { DBUG_ASSERT(fixed == 1); return value; }
+  String* val_str(String *);
   my_decimal *val_decimal(my_decimal *);
   enum Item_result result_type() const { return STRING_RESULT; }
   CHARSET_INFO *charset() const { return value->charset(); };
   int save_in_field(Field *field, bool no_conversions);
+  void cache_value();
 };
 
 class Item_cache_row: public Item_cache
@@ -3017,7 +3035,8 @@ class Item_cache_row: public Item_cache
   bool save_array;
 public:
   Item_cache_row()
-    :Item_cache(), values(0), item_count(2), save_array(0) {}
+    :Item_cache(), values(0), item_count(2),
+    save_array(0) {}
   
   /*
     'allocate' used only in row transformer, to preallocate space for row 
@@ -3075,6 +3094,7 @@ public:
       values= 0;
     DBUG_VOID_RETURN;
   }
+  void cache_value();
 };
 
 

=== modified file 'sql/item_cmpfunc.cc'
--- a/sql/item_cmpfunc.cc	2009-11-03 12:48:59 +0000
+++ b/sql/item_cmpfunc.cc	2009-11-06 19:42:24 +0000
@@ -785,15 +785,21 @@ Arg_comparator::can_compare_as_dates(Ite
 
   if (cmp_type != CMP_DATE_DFLT)
   {
+    THD *thd= current_thd;
     /*
       Do not cache GET_USER_VAR() function as its const_item() may return TRUE
       for the current thread but it still may change during the execution.
+      Don't use cache while in the context analysis mode only (i.e. for 
+      EXPLAIN/CREATE VIEW and similar queries). Cache is useless in such 
+      cases and can cause problems. For example evaluating subqueries can 
+      confuse storage engines since in context analysis mode tables 
+      aren't locked.
     */
-    if (cmp_type != CMP_DATE_WITH_DATE && str_arg->const_item() &&
+    if (!thd->is_context_analysis_only() &&
+        cmp_type != CMP_DATE_WITH_DATE && str_arg->const_item() &&
         (str_arg->type() != Item::FUNC_ITEM ||
         ((Item_func*)str_arg)->functype() != Item_func::GUSERVAR_FUNC))
     {
-      THD *thd= current_thd;
       ulonglong value;
       bool error;
       String tmp, *str_val= 0;
@@ -881,13 +887,13 @@ int Arg_comparator::set_cmp_func(Item_bo
 {
   enum enum_date_cmp_type cmp_type;
   ulonglong const_value= (ulonglong)-1;
+  thd= current_thd;
+  owner= owner_arg;
   a= a1;
   b= a2;
 
   if ((cmp_type= can_compare_as_dates(*a, *b, &const_value)))
   {
-    thd= current_thd;
-    owner= owner_arg;
     a_type= (*a)->field_type();
     b_type= (*b)->field_type();
     a_cache= 0;
@@ -895,6 +901,10 @@ int Arg_comparator::set_cmp_func(Item_bo
 
     if (const_value != (ulonglong)-1)
     {
+      /*
+        cache_converted_constant can't be used here because it can't
+        correctly convert a DATETIME value from string to int representation.
+      */
       Item_cache_int *cache= new Item_cache_int();
       /* Mark the cache as non-const to prevent re-caching. */
       cache->set_used_tables(1);
@@ -920,8 +930,6 @@ int Arg_comparator::set_cmp_func(Item_bo
            (*b)->field_type() == MYSQL_TYPE_TIME)
   {
     /* Compare TIME values as integers. */
-    thd= current_thd;
-    owner= owner_arg;
     a_cache= 0;
     b_cache= 0;
     is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC);
@@ -940,10 +948,46 @@ int Arg_comparator::set_cmp_func(Item_bo
       return 1;
   }
 
+  a= cache_converted_constant(thd, a, &a_cache, type);
+  b= cache_converted_constant(thd, b, &b_cache, type);
   return set_compare_func(owner_arg, type);
 }
 
 
+/**
+  Convert and cache a constant.
+
+  @param value      [in]  An item to cache
+  @param cache_item [out] Placeholder for the cache item
+  @param type       [in]  Comparison type
+
+  @details
+    When given item is a constant and its type differs from comparison type
+    then cache its value to avoid type conversion of this constant on each
+    evaluation. In this case the value is cached and the reference to the cache
+    is returned.
+    Original value is returned otherwise.
+
+  @return cache item or original value.
+*/
+
+Item** Arg_comparator::cache_converted_constant(THD *thd, Item **value,
+                                                Item **cache_item,
+                                                Item_result type)
+{
+  /* Don't need cache if doing context analysis only. */
+  if (!thd->is_context_analysis_only() &&
+      (*value)->const_item() && type != (*value)->result_type())
+  {
+    Item_cache *cache= Item_cache::get_cache(*value, type);
+    cache->store(*value);
+    *cache_item= cache;
+    return cache_item;
+  }
+  return value;
+}
+
+
 void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1)
 {
   thd= current_thd;
@@ -1582,6 +1626,7 @@ longlong Item_in_optimizer::val_int()
   bool tmp;
   DBUG_ASSERT(fixed == 1);
   cache->store(args[0]);
+  cache->cache_value();
   
   if (cache->null_value)
   {

=== modified file 'sql/item_cmpfunc.h'
--- a/sql/item_cmpfunc.h	2009-11-02 12:24:07 +0000
+++ b/sql/item_cmpfunc.h	2009-11-06 19:42:24 +0000
@@ -94,6 +94,8 @@ public:
                                                       ulonglong *const_val_arg);
 
   void set_datetime_cmp_func(Item **a1, Item **b1);
+  Item** cache_converted_constant(THD *thd, Item **value, Item **cache,
+                                  Item_result type);
   static arg_cmp_func comparator_matrix [5][2];
 
   friend class Item_func;

=== modified file 'sql/item_subselect.cc'
--- a/sql/item_subselect.cc	2009-10-29 23:01:54 +0000
+++ b/sql/item_subselect.cc	2009-11-06 19:42:24 +0000
@@ -480,6 +480,7 @@ Item_singlerow_subselect::select_transfo
 void Item_singlerow_subselect::store(uint i, Item *item)
 {
   row[i]->store(item);
+  row[i]->cache_value();
 }
 
 enum Item_result Item_singlerow_subselect::result_type() const
@@ -1831,6 +1832,7 @@ void subselect_engine::set_row(List<Item
     if (!(row[i]= Item_cache::get_cache(sel_item)))
       return;
     row[i]->setup(sel_item);
+    row[i]->store(sel_item);
   }
   if (item_list.elements > 1)
     res_type= ROW_RESULT;
@@ -1954,6 +1956,7 @@ int subselect_single_select_engine::exec
               tab->read_record.record= tab->table->record[0];
               tab->read_record.thd= join->thd;
               tab->read_record.ref_length= tab->table->file->ref_length;
+              tab->read_record.unlock_row= rr_unlock_row;
               *(last_changed_tab++)= tab;
               break;
             }

=== modified file 'sql/item_xmlfunc.cc'
--- a/sql/item_xmlfunc.cc	2009-09-23 13:21:29 +0000
+++ b/sql/item_xmlfunc.cc	2009-11-06 19:34:25 +0000
@@ -941,14 +941,16 @@ static Item *create_comparator(MY_XPATH 
      in a loop through all of the nodes in the node set.
     */
 
-    Item *fake= new Item_string("", 0, xpath->cs);
+    Item_string *fake= new Item_string("", 0, xpath->cs);
+    /* Don't cache fake because its value will be changed during comparison.*/
+    fake->set_used_tables(RAND_TABLE_BIT);
     Item_nodeset_func *nodeset;
     Item *scalar, *comp;
     if (a->type() == Item::XPATH_NODESET)
     {
       nodeset= (Item_nodeset_func*) a;
       scalar= b;
-      comp= eq_func(oper, fake, scalar);
+      comp= eq_func(oper, (Item*)fake, scalar);
     }
     else
     {

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2009-11-03 00:52:57 +0000
+++ b/sql/mysqld.cc	2009-11-05 06:07:31 +0000
@@ -3888,6 +3888,27 @@ server.");
 
   if (opt_bin_log)
   {
+    /* Reports an error and aborts, if the --log-bin's path 
+       is a directory.*/
+    if (opt_bin_logname && 
+        opt_bin_logname[strlen(opt_bin_logname) - 1] == FN_LIBCHAR)
+    {
+      sql_print_error("Path '%s' is a directory name, please specify \
+a file name for --log-bin option", opt_bin_logname);
+      unireg_abort(1);
+    }
+
+    /* Reports an error and aborts, if the --log-bin-index's path 
+       is a directory.*/
+    if (opt_binlog_index_name && 
+        opt_binlog_index_name[strlen(opt_binlog_index_name) - 1] 
+        == FN_LIBCHAR)
+    {
+      sql_print_error("Path '%s' is a directory name, please specify \
+a file name for --log-bin-index option", opt_binlog_index_name);
+      unireg_abort(1);
+    }
+
     char buf[FN_REFLEN];
     const char *ln;
     ln= mysql_bin_log.generate_name(opt_bin_logname, "-bin", 1, buf);

=== modified file 'sql/records.cc'
--- a/sql/records.cc	2009-10-20 18:00:07 +0000
+++ b/sql/records.cc	2009-11-03 17:45:52 +0000
@@ -62,6 +62,7 @@ void init_read_record_idx(READ_RECORD *i
   info->file=  table->file;
   info->record= table->record[0];
   info->print_error= print_error;
+  info->unlock_row= rr_unlock_row;
 
   table->status=0;			/* And it's always found */
   if (!table->file->inited)
@@ -187,6 +188,7 @@ void init_read_record(READ_RECORD *info,
   }
   info->select=select;
   info->print_error=print_error;
+  info->unlock_row= rr_unlock_row;
   info->ignore_not_found_rows= 0;
   table->status=0;			/* And it's always found */
 

=== modified file 'sql/rpl_rli.cc'
--- a/sql/rpl_rli.cc	2009-09-23 13:21:29 +0000
+++ b/sql/rpl_rli.cc	2009-11-05 06:07:31 +0000
@@ -132,6 +132,29 @@ int init_relay_log_info(Relay_log_info* 
     rli->relay_log.max_size (and mysql_bin_log.max_size).
   */
   {
+    /* Reports an error and returns, if the --relay-log's path 
+       is a directory.*/
+    if (opt_relay_logname && 
+        opt_relay_logname[strlen(opt_relay_logname) - 1] == FN_LIBCHAR)
+    {
+      pthread_mutex_unlock(&rli->data_lock);
+      sql_print_error("Path '%s' is a directory name, please specify \
+a file name for --relay-log option", opt_relay_logname);
+      DBUG_RETURN(1);
+    }
+
+    /* Reports an error and returns, if the --relay-log-index's path 
+       is a directory.*/
+    if (opt_relaylog_index_name && 
+        opt_relaylog_index_name[strlen(opt_relaylog_index_name) - 1] 
+        == FN_LIBCHAR)
+    {
+      pthread_mutex_unlock(&rli->data_lock);
+      sql_print_error("Path '%s' is a directory name, please specify \
+a file name for --relay-log-index option", opt_relaylog_index_name);
+      DBUG_RETURN(1);
+    }
+
     char buf[FN_REFLEN];
     const char *ln;
     static bool name_warning_sent= 0;

=== modified file 'sql/sp_rcontext.cc'
--- a/sql/sp_rcontext.cc	2008-01-23 22:36:57 +0000
+++ b/sql/sp_rcontext.cc	2009-11-06 19:34:25 +0000
@@ -617,7 +617,7 @@ sp_rcontext::set_case_expr(THD *thd, int
   }
 
   m_case_expr_holders[case_expr_id]->store(case_expr_item);
-
+  m_case_expr_holders[case_expr_id]->cache_value();
   return FALSE;
 }
 

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2009-11-02 11:21:39 +0000
+++ b/sql/sql_select.cc	2009-11-06 14:54:19 +0000
@@ -149,6 +149,7 @@ static int join_read_const_table(JOIN_TA
 static int join_read_system(JOIN_TAB *tab);
 static int join_read_const(JOIN_TAB *tab);
 static int join_read_key(JOIN_TAB *tab);
+static void join_read_key_unlock_row(st_join_table *tab);
 static int join_read_always_key(JOIN_TAB *tab);
 static int join_read_last_key(JOIN_TAB *tab);
 static int join_no_more_records(READ_RECORD *info);
@@ -981,14 +982,20 @@ JOIN::optimize()
     DBUG_RETURN(1);
   }
 
-  if (select_lex->olap == ROLLUP_TYPE && rollup_process_const_fields())
+  if (rollup.state != ROLLUP::STATE_NONE)
   {
-    DBUG_PRINT("error", ("Error: rollup_process_fields() failed"));
-    DBUG_RETURN(1);
+    if (rollup_process_const_fields())
+    {
+      DBUG_PRINT("error", ("Error: rollup_process_fields() failed"));
+      DBUG_RETURN(1);
+    }
+  }
+  else
+  {
+    /* Remove distinct if only const tables */
+    select_distinct= select_distinct && (const_tables != tables);
   }
 
-  /* Remove distinct if only const tables */
-  select_distinct= select_distinct && (const_tables != tables);
   thd_proc_info(thd, "preparing");
   if (result->initialize_tables(this))
   {
@@ -1287,11 +1294,14 @@ JOIN::optimize()
     - We are using an ORDER BY or GROUP BY on fields not in the first table
     - We are using different ORDER BY and GROUP BY orders
     - The user wants us to buffer the result.
+    When the WITH ROLLUP modifier is present, we cannot skip temporary table
+    creation for the DISTINCT clause just because there are only const tables.
   */
-  need_tmp= (const_tables != tables &&
+  need_tmp= ((const_tables != tables &&
 	     ((select_distinct || !simple_order || !simple_group) ||
 	      (group_list && order) ||
-	      test(select_options & OPTION_BUFFER_RESULT)));
+	      test(select_options & OPTION_BUFFER_RESULT))) ||
+             rollup.state != ROLLUP::STATE_NONE && select_distinct);
 
   // No cache for MATCH
   make_join_readinfo(this,
@@ -5628,7 +5638,9 @@ static bool create_ref_for_key(JOIN *joi
   }
   j->ref.key_buff2=j->ref.key_buff+ALIGN_SIZE(length);
   j->ref.key_err=1;
+  j->ref.has_record= FALSE;
   j->ref.null_rejecting= 0;
+  j->ref.use_count= 0;
   keyuse=org_keyuse;
 
   store_key **ref_key= j->ref.key_copy;
@@ -6461,6 +6473,20 @@ make_join_select(JOIN *join,SQL_SELECT *
   DBUG_RETURN(0);
 }
 
+
+/**
+  The default implementation of unlock-row method of READ_RECORD,
+  used in all access methods.
+*/
+
+void rr_unlock_row(st_join_table *tab)
+{
+  READ_RECORD *info= &tab->read_record;
+  info->file->unlock_row();
+}
+
+
+
 static void
 make_join_readinfo(JOIN *join, ulonglong options)
 {
@@ -6476,6 +6502,7 @@ make_join_readinfo(JOIN *join, ulonglong
     TABLE *table=tab->table;
     tab->read_record.table= table;
     tab->read_record.file=table->file;
+    tab->read_record.unlock_row= rr_unlock_row;
     tab->next_select=sub_select;		/* normal select */
 
     /*
@@ -6521,6 +6548,7 @@ make_join_readinfo(JOIN *join, ulonglong
       delete tab->quick;
       tab->quick=0;
       tab->read_first_record= join_read_key;
+      tab->read_record.unlock_row= join_read_key_unlock_row;
       tab->read_record.read_record= join_no_more_records;
       if (table->covering_keys.is_set(tab->ref.key) &&
 	  !table->no_keyread)
@@ -11357,7 +11385,7 @@ evaluate_join_record(JOIN *join, JOIN_TA
         return NESTED_LOOP_NO_MORE_ROWS;
     }
     else
-      join_tab->read_record.file->unlock_row();
+      join_tab->read_record.unlock_row(join_tab);
   }
   else
   {
@@ -11367,7 +11395,7 @@ evaluate_join_record(JOIN *join, JOIN_TA
     */
     join->examined_rows++;
     join->thd->row_count++;
-    join_tab->read_record.file->unlock_row();
+    join_tab->read_record.unlock_row(join_tab);
   }
   return NESTED_LOOP_OK;
 }
@@ -11727,18 +11755,55 @@ join_read_key(JOIN_TAB *tab)
       table->status=STATUS_NOT_FOUND;
       return -1;
     }
+    /*
+      Moving away from the current record. Unlock the row
+      in the handler if it did not match the partial WHERE.
+    */
+    if (tab->ref.has_record && tab->ref.use_count == 0)
+    {
+      tab->read_record.file->unlock_row();
+      tab->ref.has_record= FALSE;
+    }
     error=table->file->index_read_map(table->record[0],
                                       tab->ref.key_buff,
                                       make_prev_keypart_map(tab->ref.key_parts),
                                       HA_READ_KEY_EXACT);
     if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
       return report_error(table, error);
+
+    if (! error)
+    {
+      tab->ref.has_record= TRUE;
+      tab->ref.use_count= 1;
+    }
+  }
+  else if (table->status == 0)
+  {
+    DBUG_ASSERT(tab->ref.has_record);
+    tab->ref.use_count++;
   }
   table->null_row=0;
   return table->status ? -1 : 0;
 }
 
 
+/**
+  Since join_read_key may buffer a record, do not unlock
+  it if it was not used in this invocation of join_read_key().
+  Only count locks, thus remembering if the record was left unused,
+  and unlock already when pruning the current value of
+  TABLE_REF buffer.
+  @sa join_read_key()
+*/
+
+static void
+join_read_key_unlock_row(st_join_table *tab)
+{
+  DBUG_ASSERT(tab->ref.use_count);
+  if (tab->ref.use_count)
+    tab->ref.use_count--;
+}
+
 /*
   ref access method implementation: "read_first" function
 

=== modified file 'sql/sql_select.h'
--- a/sql/sql_select.h	2009-10-30 15:59:06 +0000
+++ b/sql/sql_select.h	2009-11-03 17:45:52 +0000
@@ -58,6 +58,8 @@ class store_key;
 typedef struct st_table_ref
 {
   bool		key_err;
+  /** True if something was read into buffer in join_read_key.  */
+  bool          has_record;
   uint          key_parts;                ///< num of ...
   uint          key_length;               ///< length of key_buff
   int           key;                      ///< key no
@@ -85,6 +87,11 @@ typedef struct st_table_ref
   table_map	depend_map;		  ///< Table depends on these tables.
   /* null byte position in the key_buf. Used for REF_OR_NULL optimization */
   uchar          *null_ref_key;
+  /*
+    The number of times the record associated with this key was used
+    in the join.
+  */
+  ha_rows       use_count;
 } TABLE_REF;
 
 

=== modified file 'sql/structs.h'
--- a/sql/structs.h	2009-06-26 19:57:42 +0000
+++ b/sql/structs.h	2009-11-03 17:45:52 +0000
@@ -115,16 +115,22 @@ typedef struct st_reginfo {		/* Extra in
 } REGINFO;
 
 
-struct st_read_record;				/* For referense later */
 class SQL_SELECT;
 class THD;
 class handler;
+struct st_join_table;
 
-typedef struct st_read_record {			/* Parameter to read_record */
+void rr_unlock_row(st_join_table *tab);
+
+struct READ_RECORD {			/* Parameter to read_record */
+  typedef int (*Read_func)(READ_RECORD*);
+  typedef void (*Unlock_row_func)(st_join_table *);
   struct st_table *table;			/* Head-form */
   handler *file;
   struct st_table **forms;			/* head and ref forms */
-  int (*read_record)(struct st_read_record *);
+
+  Read_func read_record;
+  Unlock_row_func unlock_row;
   THD *thd;
   SQL_SELECT *select;
   uint cache_records;
@@ -136,7 +142,7 @@ typedef struct st_read_record {			/* Par
   uchar	*cache,*cache_pos,*cache_end,*read_positions;
   IO_CACHE *io_cache;
   bool print_error, ignore_not_found_rows;
-} READ_RECORD;
+};
 
 
 /*


Attachment: [text/bzr-bundle]
Thread
bzr commit into mysql-5.1-bugteam branch (kristofer.pettersson:3190)Kristofer Pettersson9 Nov