Reputation: 8768
I have a stored procedure, which should process rows in a table using a cursor. The procedure works most of the time, but sometimes it just does not execute fully. I know this because I have a simple debugging facility, embedded into the code, which logs specific lines and variables into dedicated debug table. Most interesting that the problem does always occur when running from PHP. If I use mysql client I never got this problem.
The procedure (presented here in somewhat shortened way) is the following:
CREATE PROCEDURE findnextedge(IN lastid BIGINT)
findnext_context:BEGIN
DECLARE stop BOOLEAN DEFAULT FALSE;
DECLARE count INT DEFAULT 0;
DECLARE cur_fid BIGINT DEFAULT 0;
DECLARE cur_pid1 BIGINT DEFAULT 0;
DECLARE cur_pid2 BIGINT DEFAULT 0;
DECLARE cur CURSOR FOR SELECT fid, pid1, pid2 FROM edges WHERE pid1 = lastid;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET stop = TRUE;
CALL debuglog(0, 'findnextedge', 'lastid', lastid, NULL, NULL, NULL, NULL);
SELECT SQL_CALC_FOUND_ROWS fid FROM edges WHERE pid1 = lastid;
SET count = FOUND_ROWS();
CALL debuglog(1, 'findnextedge', 'count', count, NULL, NULL, NULL, NULL);
IF count = 0 THEN
DELETE FROM paths WHERE pid1 = lastid AND pid2 = 0;
SELECT COUNT(*) INTO count FROM paths WHERE pid2 = 0;
CALL debuglog(2, 'findnextedge', 'count', count, NULL, NULL, NULL, NULL);
IF count = 0 THEN
SET @count = 1;
END IF;
LEAVE findnext_context;
END IF;
DELETE FROM paths WHERE pid1 = lastid AND pid2 = 0 ORDER BY pid1 LIMIT 1;
OPEN cur;
CALL debuglog(6, 'findnextedge', 'open', TRUE, NULL, NULL, NULL, NULL);
REPEAT
FETCH cur INTO cur_fid, cur_pid1, cur_pid2;
CALL debuglog(7, 'findnextedge', 'stop', stop, NULL, NULL, NULL, NULL);
IF stop = FALSE THEN
CALL debuglog(3, 'findnextedge', 'cur_fid', cur_fid, 'cur_pid1', cur_pid1, 'cur_pid2', cur_pid2);
// DO MAIN JOB
// ...
CALL debuglog(5, 'findnextedge', NULL, NULL, NULL, NULL, NULL, NULL);
END IF;
CALL debuglog(8, 'findnextedge', 'stop', stop, NULL, NULL, NULL, NULL);
UNTIL stop = TRUE
END REPEAT;
CLOSE cur;
END;
The whole output produced if the problem occurs:
point context name1 value1 name2 value2 name3 value3 counter time
0 findnext lastid 0 NULL NULL NULL NULL 0 2012-11-27 18:29:56
1 findnext count 1 NULL NULL NULL NULL 1 2012-11-27 18:29:56
6 findnext open 1 NULL NULL NULL NULL 2 2012-11-27 18:29:56
7 findnext stop 0 NULL NULL NULL NULL 3 2012-11-27 18:29:56
According to the log, in the point 7, just after fetching the cursor stop
value is false, yet execution does not reach neither point 3, nor 8.
It looks like some internal error occurs, but i'm not sure how can I trap it. Strangely, that this happens on the very same data, from time to time, and works otherwise.
P.S. MySQL version 5.0.51b, PHP 5.2.6.
P.S.S. I've managed to find a related question - Calling a Stored Procedure Within a Cursor Loop, Without Tripping the Continue Handler. As the name of my procedure implies, it gets invoked from inside a loop in an external procedure (that has a loop through the "paths" table, by the way, and another continue handler), so it resembles those situation, and may be somehow important. I've tried the solution from the linked question, but it didn't help either.
A solution is found, the answer is posted below.
Upvotes: 4
Views: 254
Reputation: 8768
I've solved the problem, though the way how it's done seems very strange to me.
The lines:
SELECT SQL_CALC_FOUND_ROWS fid FROM edges WHERE pid1 = lastid;
SET count = FOUND_ROWS();
was replaced with simplified:
SELECT COUNT(*) INTO count FROM edges WHERE pid1 = lastid;
And this works without a flaw.
The reason why the same thing was initially coded in 2 lines is that I did not have debugging table at the beginning and I controlled execution flow via SELECT
's.
It seems stored procedures do not like when something is SELECT
ed into (unavailable) console. In my case this led to inconsistent behaviour: procedure dropped execution in some arbitrary test runs on the same data. If someone could shed a light on such behaviour, I'd be glad to accept that answer, instead of mine.
Upvotes: 0
Reputation: 8395
The handler to stop the loop, as in:
DECLARE CONTINUE HANDLER FOR NOT FOUND SET stop = TRUE;
is global to the entire procedure.
As a result, it may well catch other not found events, not coming from the cursor fetch, and cause the loop to exit early.
Try using a begin end block just around the cursor fetch, and declare a handler there.
Upvotes: 1