Bug #25271 Caching JOIN in stored procedure
Submitted: 25 Dec 2006 15:18 Modified: 26 Dec 2006 12:32
Reporter: Develar Email Updates:
Status: Can't repeat Impact on me:
None 
Category:MySQL Server: Stored Routines Severity:S2 (Serious)
Version:5.0.27 OS:Windows (Windows XP SP2)
Assigned to: CPU Architecture:Any
Tags: join, stored procedure

[25 Dec 2006 15:18] Develar
Description:
LEFT JOIN is caching in stored procedure

CALL admin_get_parent_privileges(1);

return valid result. 

+-------+------+------+------+------+--------+-------+------+-------+------+------+--------+-------+------------------+
| 2 * 2 | id   | user | read | edit | delete | grant | id   | group | read | edit | delete | grant | RAND()           |
+-------+------+------+------+------+--------+-------+------+-------+------+------+--------+-------+------------------+
|     4 | NULL | NULL | NULL | NULL |   NULL |  NULL |    1 |     1 |    1 |    1 |      1 |     1 | 0.87516949686703 |
+-------+------+------+------+------+--------+-------+------+-------+------+------+--------+-------+------------------+

but if you execute it after 

CALL admin_get_parent_privileges(6);

you get invalid result

+-------+------+------+------+------+--------+-------+------+-------+------+------+--------+-------+------------------+
| 2 * 2 | id   | user | read | edit | delete | grant | id   | group | read | edit | delete | grant | RAND()           |
+-------+------+------+------+------+--------+-------+------+-------+------+------+--------+-------+------------------+
|     4 | NULL | NULL | NULL | NULL |   NULL |  NULL | NULL |  NULL | NULL | NULL |   NULL |  NULL | 0.56265535723665 |
+-------+------+------+------+------+--------+-------+------+-------+------+------+--------+-------+------------------+
You can get valid result only after restart server or alter stored procedure.

How to repeat:
# SQL Manager 2005 for MySQL 3.7.6.2
# ---------------------------------------
# Host     : localhost
# Port     : 3306
# Database : help-centr

#
# Structure for the `tree` table : 
#

CREATE TABLE `tree` (
  `id` mediumint(8) unsigned NOT NULL auto_increment,
  `parent` mediumint(8) unsigned NOT NULL default '0',
  `type` tinyint(3) unsigned NOT NULL default '0',
  `uri` char(40) NOT NULL,
  `time_create` int(10) unsigned NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `parent` (`parent`,`uri`(25))
) ENGINE=MyISAM AUTO_INCREMENT=61 DEFAULT CHARSET=utf8 PACK_KEYS=1;

CREATE TRIGGER `tree_before_ins_tr` BEFORE INSERT ON `tree`
  FOR EACH ROW
BEGIN
     SET NEW.time_create = UNIX_TIMESTAMP();
END;

#
# Structure for the `users_privileges` table : 
#

CREATE TABLE `users_privileges` (
  `id` mediumint(8) unsigned NOT NULL,
  `user` smallint(5) unsigned NOT NULL,
  `read` tinyint(1) NOT NULL,
  `edit` tinyint(1) NOT NULL,
  `delete` tinyint(1) NOT NULL,
  `grant` tinyint(1) NOT NULL,
  PRIMARY KEY  (`id`,`user`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 PACK_KEYS=1;

#
# Structure for the `groups_privileges` table : 
#

CREATE TABLE `groups_privileges` (
  `id` mediumint(8) unsigned NOT NULL,
  `group` smallint(5) unsigned NOT NULL,
  `read` tinyint(1) NOT NULL,
  `edit` tinyint(1) NOT NULL,
  `delete` tinyint(1) NOT NULL,
  `grant` tinyint(1) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `group` (`group`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 PACK_KEYS=1;

#
# Data for the `tree` table  (LIMIT 0,500)
#

INSERT INTO `tree` (`id`, `parent`, `type`, `uri`, `time_create`) VALUES 
  (1,0,1,'http://www.help-centr.ru',0),
  (4,1,2,'services',0),
  (6,4,1,'for-sng-citizens',0);

#
# Data for the `groups_privileges` table  (LIMIT 0,500)
#

INSERT INTO `groups_privileges` (`id`, `group`, `read`, `edit`, `delete`, `grant`) VALUES 
  (1,1,1,1,1,1),
  (4,1,1,1,1,1);

CREATE PROCEDURE `admin_get_parent_privileges`(IN $parent MEDIUMINT UNSIGNED)
    NOT DETERMINISTIC
    CONTAINS SQL
    SQL SECURITY DEFINER
    COMMENT ''
BEGIN
	SELECT SQL_NO_CACHE
			2 * 2,
			users_privileges.*,
			groups_privileges.*,
			RAND()
		FROM tree
			LEFT JOIN users_privileges ON (tree.id = users_privileges.id AND users_privileges.`user` = 1)
			LEFT JOIN groups_privileges ON (tree.id = groups_privileges.id AND groups_privileges.`group` = 1)
		WHERE tree.id = $parent;
		
END;

Suggested fix:
I have solved a problem addition of RAND()

DROP PROCEDURE IF EXISTS admin_get_parent_privileges;
CREATE PROCEDURE `admin_get_parent_privileges`(IN $parent MEDIUMINT UNSIGNED)
    NOT DETERMINISTIC
    CONTAINS SQL
    SQL SECURITY DEFINER
    COMMENT ''
BEGIN
	SELECT SQL_NO_CACHE
			2 * 2,
			users_privileges.*,
			groups_privileges.*,
			RAND()
		FROM tree
			LEFT JOIN users_privileges ON (tree.id = users_privileges.id AND users_privileges.`user` = 1 AND RAND())
			LEFT JOIN groups_privileges ON (tree.id = groups_privileges.id AND groups_privileges.`group` = 1 AND RAND())
		WHERE tree.id = $parent;
		
END;
[26 Dec 2006 12:16] Valeriy Kravchuk
Thank you for a problem report. Please, send the results of:

SHOW VARIABLES LIKE 'query%';
[26 Dec 2006 12:19] Develar
+------------------------------+----------+
| Variable_name                | Value    |
+------------------------------+----------+
| query_alloc_block_size       | 8192     |
| query_cache_limit            | 1048576  |
| query_cache_min_res_unit     | 4096     |
| query_cache_size             | 16777216 |
| query_cache_type             | ON       |
| query_cache_wlock_invalidate | OFF      |
| query_prealloc_size          | 8192     |
+------------------------------+----------+
[26 Dec 2006 12:32] Valeriy Kravchuk
Sorry, but I was not able to repeat the behaviour described with latest 5.0.34-BK on Linux:

mysql> CREATE PROCEDURE `admin_get_parent_privileges`(IN $parent MEDIUMINT UNSIGNED)
    -> NOT DETERMINISTIC
    -> CONTAINS SQL
    -> SQL SECURITY DEFINER
    -> COMMENT ''
    -> BEGIN
    -> SELECT SQL_NO_CACHE
    -> 2 * 2,
    -> users_privileges.*,
    -> groups_privileges.*,
    -> RAND()
    -> FROM tree
    -> LEFT JOIN users_privileges ON (tree.id = users_privileges.id AND
    -> users_privileges.`user` = 1)
    -> LEFT JOIN groups_privileges ON (tree.id = groups_privileges.id AND
    -> groups_privileges.`group` = 1)
    -> WHERE tree.id = $parent;
    -> END;
    -> //
Query OK, 0 rows affected (0.07 sec)

mysql> CALL admin_get_parent_privileges(1)//
+-------+------+------+------+------+--------+-------+----+-------+------+------
+--------+-------+------------------+
| 2 * 2 | id   | user | read | edit | delete | grant | id | group | read | edit
| delete | grant | RAND()           |
+-------+------+------+------+------+--------+-------+----+-------+------+------
+--------+-------+------------------+
|     4 | NULL | NULL | NULL | NULL |   NULL |  NULL |  1 |     1 |    1 |    1
|      1 |     1 | 0.93895839428432 |
+-------+------+------+------+------+--------+-------+----+-------+------+------
+--------+-------+------------------+
1 row in set (0.01 sec)

Query OK, 0 rows affected (0.01 sec)

mysql> CALL admin_get_parent_privileges(6)//
+-------+------+------+------+------+--------+-------+------+-------+------+----
--+--------+-------+------------------+
| 2 * 2 | id   | user | read | edit | delete | grant | id   | group | read | edi
t | delete | grant | RAND()           |
+-------+------+------+------+------+--------+-------+------+-------+------+----
--+--------+-------+------------------+
|     4 | NULL | NULL | NULL | NULL |   NULL |  NULL | NULL |  NULL | NULL | NUL
L |   NULL |  NULL | 0.76751278505429 |
+-------+------+------+------+------+--------+-------+------+-------+------+----
--+--------+-------+------------------+
1 row in set (0.01 sec)

Query OK, 0 rows affected (0.01 sec)

mysql> CALL admin_get_parent_privileges(1)//
+-------+------+------+------+------+--------+-------+----+-------+------+------
+--------+-------+-------------------+
| 2 * 2 | id   | user | read | edit | delete | grant | id | group | read | edit
| delete | grant | RAND()            |
+-------+------+------+------+------+--------+-------+----+-------+------+------
+--------+-------+-------------------+
|     4 | NULL | NULL | NULL | NULL |   NULL |  NULL |  1 |     1 |    1 |    1
|      1 |     1 | 0.020688773152129 |
+-------+------+------+------+------+--------+-------+----+-------+------+------
+--------+-------+-------------------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.01 sec)

mysql> show variables like 'query%'//
+------------------------------+----------+
| Variable_name                | Value    |
+------------------------------+----------+
| query_alloc_block_size       | 8192     |
| query_cache_limit            | 1048576  |
| query_cache_min_res_unit     | 4096     |
| query_cache_size             | 16777216 |
| query_cache_type             | ON       |
| query_cache_wlock_invalidate | OFF      |
| query_prealloc_size          | 8192     |
+------------------------------+----------+
7 rows in set (0.01 sec)

mysql> select version()//
+--------------+
| version()    |
+--------------+
| 5.0.34-debug |
+--------------+
1 row in set (0.00 sec)

So, looks like problem is fixed in current sources.