Bug #9486 can't perform multi-update in stored procedure
Submitted: 30 Mar 2005 13:58 Modified: 14 Jun 2005 7:18
Reporter: Dmitry L Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server Severity:S3 (Non-critical)
Version:5.0.3 OS:Microsoft Windows (Win2k)
Assigned to: Dmitry Lenev CPU Architecture:Any

[30 Mar 2005 13:58] Dmitry L
Description:
Can't perform update inside stored procedure:
Error is: "Table 'op' was locked with a READ lock and can't be updated"

How to repeat:
here's procedure:
DELIMITER $$
DROP PROCEDURE IF EXISTS `parallax_kernel`.`updateTitles`$$
CREATE PROCEDURE `parallax_kernel`.`updateTitles`(OUT affected INT)
BEGIN
  UPDATE objects_path op,
         objects_hier oh
     SET op.title = oh.title
   WHERE op.pid = oh.id
     AND op.map = oh.map;
  SELECT ROW_COUNT() INTO affected;
END$$
DELIMITER ;

Tables:
CREATE TABLE `objects_path` (
  `id` int(10) unsigned NOT NULL default '0',
  `pid` int(10) unsigned NOT NULL default '0',
  `map` smallint(6) unsigned NOT NULL default '0',
  `level` tinyint(4) unsigned NOT NULL default '0',
  `title` varchar(255) default NULL,
  PRIMARY KEY  (`id`,`map`,`level`),
  KEY `pid` (`pid`),
  KEY `level` (`level`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `objects_hier` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `pid` int(10) unsigned NOT NULL default '0',
  `flags_0` int(10) unsigned NOT NULL default '0',
  `flags_1` int(10) unsigned NOT NULL default '0',
  `flags_2` int(10) unsigned NOT NULL default '0',
  `proto` smallint(6) unsigned NOT NULL default '0',
  `sort` smallint(6) NOT NULL default '0',
  `template` int(10) unsigned default NULL,
  `map` smallint(6) unsigned NOT NULL default '0',
  `mapped_from` smallint(6) unsigned default NULL,
  `lnk4` int(10) unsigned default NULL,
  `crt` double(15,4) unsigned NOT NULL default '0.0000',
  `mdf` double(15,4) unsigned NOT NULL default '0.0000',
  `path` bigint(20) unsigned default NULL,
  `owner` int(10) unsigned default NULL,
  `size` int(10) unsigned NOT NULL default '100',
  `title` varchar(255) default NULL,
  `remark` varchar(255) default NULL,
  `xml` mediumtext,
  PRIMARY KEY  (`id`,`map`),
  KEY `proto` (`proto`),
  KEY `title` (`title`),
  KEY `pid` (`pid`),
  KEY `id` (`id`,`mapped_from`),
  KEY `lnk4` (`lnk4`,`map`),
  KEY `sort` (`sort`),
  KEY `path` (`path`),
  KEY `owner` (`owner`),
  KEY `size` (`size`),
  KEY `template` (`template`,`map`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Call is:
call updateTitles(@a);

There are no active locks and opened transactions on the server and call is done by @root
[9 Apr 2005 12:05] Dmitry Lenev
Please also note that second invocation of updateTitles() will block current connection...
[14 Apr 2005 4:51] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/internals/24000
[15 Apr 2005 16:30] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/internals/24057
[22 Apr 2005 8:32] Dmitry Lenev
Fixed in 5.0.5
[26 Apr 2005 3:10] Paul Dubois
Noted in 5.0.5 changelog.
[14 Jun 2005 0:12] Peter Brawley
This bug persists in 5.0.6, as the following script shows:

CREATE TABLE airports (
code char(3) NOT NULL,
city char(100) default NULL,
latitude float NOT NULL,
longitude float NOT NULL,
PRIMARY KEY (code)
) ENGINE=MyISAM;

INSERT INTO airports VALUES ('JFK', 'New York, NY', 40.75, -73.97);
INSERT INTO airports VALUES ('LAX', 'Los Angeles, CA', 34.05, -118.22);
INSERT INTO airports VALUES ('LHR', 'London, England', 51.5, -0.45);
INSERT INTO airports VALUES ('HEL', 'Helsinki, Finland', 60.17, 24.97);
INSERT INTO airports VALUES ('CDG', 'Paris, France', 48.86, 2.33);
INSERT INTO airports VALUES ('STL', 'St Louis, MO', 38.63, -90.2);
INSERT INTO airports VALUES ('ARN', 'Stockholm, Sweden', 59.33, 18.05); 

-- Workaround for another bug
SET GLOBAL log_bin_trust_routine_creators=TRUE; 

DROP FUNCTION IF EXISTS GeoDistKM; 
DELIMITER |
CREATE FUNCTION GeoDistKM( lat1 FLOAT, lon1 FLOAT, lat2 FLOAT, lon2 FLOAT ) RETURNS float
BEGIN
  DECLARE pi, q1, q2, q3 FLOAT;
  DECLARE rads FLOAT DEFAULT 0;
  SET pi = PI();
  SET lat1 = lat1 * pi / 180;
  SET lon1 = lon1 * pi / 180;
  SET lat2 = lat2 * pi / 180;
  SET lon2 = lon2 * pi / 180;
  SET q1 = COS(lon1-lon2);
  SET q2 = COS(lat1-lat2);
  SET q3 = COS(lat1+lat2);
  SET rads = ACOS( 0.5*((1.0+q1)*q2 - (1.0-q1)*q3) ); 
  RETURN 6378.388 * rads;
END;
|
DELIMITER ; 

CREATE TABLE flights (
  id INT PRIMARY KEY AUTO_INCREMENT,
  depart CHAR(3),
  arrive CHAR(3),
  distance DECIMAL(10,2),
  direction DECIMAL(10,2)
) ENGINE=MYISAM;

INSERT INTO flights
  SELECT
  NULL,
  depart.code,
  arrive.code,
 ROUND(GeoDistKM(depart.latitude,depart.longitude,arrive.latitude,arrive.longitude),2) AS Km,
  ROUND(DEGREES(ATAN(arrive.latitude-depart.latitude,arrive.longitude-depart.longitude)),2)
  FROM airports AS depart
  INNER JOIN airports AS arrive ON depart.code <> arrive.code
  HAVING Km <= 6000;
  
CREATE TABLE routes (
  id INT PRIMARY KEY AUTO_INCREMENT,
  depart CHAR(3),
  arrive CHAR(3),
  hops SMALLINT,
  route CHAR(50),
  distance DECIMAL(10,2),
  direction DECIMAL(10,2)
) ENGINE=MYISAM; 

CREATE OR REPLACE VIEW nexthops AS
  SELECT 
    routes.depart,
    flights.arrive,
    routes.hops+1 AS hops,
    CONCAT(routes.route, ',', flights.arrive) AS route,
    MIN(routes.distance + flights.distance) AS distance,
    routes.direction
  FROM routes INNER JOIN flights 
    ON routes.arrive = flights.depart
    AND LOCATE(flights.arrive,routes.route) = 0
  WHERE flights.direction+360>routes.direction+270 
    AND flights.direction+360<routes.direction+450
  GROUP BY depart,arrive;
  
SET GLOBAL log_bin_trust_routine_creators=TRUE; 
DROP PROCEDURE IF EXISTS BuildRoutes;
DELIMITER |
CREATE PROCEDURE BuildRoutes()
BEGIN
  DECLARE rows INT DEFAULT 0;
  TRUNCATE routes;

  -- STEP 1, LISTING 5: SEED ROUTES WITH 1-HOP FLIGHTS
  INSERT INTO routes
    SELECT 
      NULL,
      depart,
      arrive,
      1,
      CONCAT(depart,',',arrive),
      distance,
      direction
  FROM flights;
  SET rows = ROW_COUNT();

  WHILE (rows > 0) DO

    -- STEP 2, LISTING 8: ADD NEXT SET OF ROUTES
    INSERT INTO routes
      SELECT 
        NULL,
        nexthops.depart,
        nexthops.arrive,
        nexthops.hops,
        nexthops.route,
        nexthops.distance,
        nexthops.direction 
      FROM nexthops
      LEFT JOIN routes ON nexthops.depart = routes.depart 
            AND nexthops.arrive = routes.arrive
      WHERE routes.depart IS NULL AND routes.arrive IS NULL;
    SET rows = ROW_COUNT();

  END WHILE;

END;
|
DELIMITER ;

ERROR 1099 (HY000): Table 'routes' was locked with a READ lock and can't be updated
[14 Jun 2005 7:18] Dmitry Lenev
Hi, Peter!

Original problem mentioned in this bug report was impossibility to perform multi-update in  stored procedure due to problem with table locking (I have updated synopsis to clarify this). 

After investigating your issue I think that your problem has nothing to do with original one so it should be reported and handled separately. And as I've noticed you already have done that! :)

Thank you!