--source include/have_sequence.inc

--echo #
--echo # MDEV-20034 Add support for the pre-defined weak SYS_REFCURSOR
--echo #


--echo #
--echo # Sending a cursor via an SP IN parameter using EXECUTE IMMEDIATE
--echo #

DELIMITER /;
CREATE PROCEDURE p1(IN c0 SYS_REFCURSOR)
BEGIN
  DECLARE v INT;
  FETCH c0 INTO v;
  SELECT 'p1' AS stage, c0, v;
END;
/
CREATE PROCEDURE p2()
BEGIN
  DECLARE c0 SYS_REFCURSOR;
  DECLARE c1 SYS_REFCURSOR;
  DECLARE v INT;
  OPEN c0 FOR SELECT 1;                    -- c0 takes m_statement_cursors.at(0)
  OPEN c1 FOR VALUES (10),(20),(30);       -- c1 takes m_statement_cursors.at(1)
  EXECUTE IMMEDIATE 'CALL p1(?)' USING c1; -- should fetch 10 from c1
  FETCH c1 INTO v;                         -- should fetch 20 from c1
  SELECT 'p2-c1' AS stage, c1, v;
  FETCH c0 INTO v;                         -- should fetch 1 from c0
  SELECT 'p2-c0' AS stage, c0, v;
END;
/
DELIMITER ;/
CALL p2;
CALL p2; /* make sure it starts from m_statement_cursors(0) on the second execution */
DROP PROCEDURE p2;
DROP PROCEDURE p1;

--echo #
--echo # SYS_REFCURSOR dynamic parameters with PREPARE..EXECUTE
--echo #

DELIMITER /;
CREATE PROCEDURE p1()
BEGIN
  DECLARE c0 SYS_REFCURSOR;
  DECLARE c1 SYS_REFCURSOR;
  OPEN c0 FOR SELECT 1;
  OPEN c1 FOR SELECT 1;
  PREPARE stmt FROM 'SELECT ?,?';
  EXECUTE stmt USING c0,c1;
  EXECUTE stmt USING c0,c1;
END;
/
DELIMITER ;/
CALL p1;
DROP PROCEDURE p1;


DELIMITER /;
CREATE PROCEDURE p1()
BEGIN
  DECLARE c0 SYS_REFCURSOR;
  DECLARE c1 SYS_REFCURSOR;
  OPEN c0 FOR SELECT 1;
  OPEN c1 FOR SELECT 1;
  PREPARE stmt FROM 'SELECT ?,?';
  EXECUTE stmt USING COALESCE(c0), COALESCE(c1);
  EXECUTE stmt USING COALESCE(c0), COALESCE(c1);
END;
/
DELIMITER ;/
CALL p1;
DROP PROCEDURE p1;


--echo # 
--echo # Make sure various combinations of
--echo # Item_param, Item_func_coalesce, Item_func_sp 
--echo # do not leak m_statement_cursors elements:
--echo # c0 is expected to be 0 in all SELECT statements below.
--echo #

DELIMITER /;
CREATE FUNCTION ff0() RETURNS SYS_REFCURSOR
BEGIN
  DECLARE ff0c0 SYS_REFCURSOR;
  OPEN ff0c0 FOR SELECT 10;
  RETURN ff0c0;
END;
/
CREATE PROCEDURE p1()
BEGIN
  DECLARE c0 SYS_REFCURSOR;
  DECLARE v INT;
  OPEN c0 FOR SELECT 1;
  SELECT 'p1-0', c0;

  EXECUTE IMMEDIATE 'SELECT COALESCE(?) FROM seq_1_to_5' USING c0;
  SELECT 'p1-1', c0;

  EXECUTE IMMEDIATE 'SELECT COALESCE(?) FROM seq_1_to_5' USING c0;
  SELECT 'p1-2', c0;

  EXECUTE IMMEDIATE 'SELECT LAST_VALUE(NULL,COALESCE(?)) FROM seq_1_to_5' USING c0;
  SELECT 'p1-3', c0;

  EXECUTE IMMEDIATE 'SELECT LAST_VALUE(COALESCE(?), COALESCE(?)) FROM seq_1_to_5' USING c0, c0;
  SELECT 'p1-4', c0;

  EXECUTE IMMEDIATE 'SELECT LAST_VALUE(COALESCE(?),NULL) FROM seq_1_to_5' USING c0;
  SELECT 'p1-5', c0;

END;
/
DELIMITER ;/
CALL p1;
DROP PROCEDURE p1;
DROP FUNCTION ff0;


--echo #
--echo # Similar tests using PREPARE..EXECUTE
--echo #

DELIMITER /;
CREATE FUNCTION ff0() RETURNS SYS_REFCURSOR
BEGIN
  DECLARE ff0c0 SYS_REFCURSOR;
  OPEN ff0c0 FOR SELECT 10;
  RETURN ff0c0;
END;
/
CREATE PROCEDURE p1()
BEGIN
  DECLARE c0 SYS_REFCURSOR;
  DECLARE v INT;
  OPEN c0 FOR SELECT 1;
  SELECT 'p1-0', c0;

  PREPARE stmt FROM 'SELECT COALESCE(?) FROM seq_1_to_5';
  EXECUTE stmt USING c0;
  DEALLOCATE PREPARE stmt;
  SELECT 'p1-1', c0;

  PREPARE stmt FROM 'SELECT COALESCE(?) FROM seq_1_to_5';
  EXECUTE stmt USING c0;
  DEALLOCATE PREPARE stmt;
  SELECT 'p1-2', c0;

  PREPARE stmt FROM 'SELECT LAST_VALUE(NULL,COALESCE(?)) FROM seq_1_to_5';
  EXECUTE stmt USING c0;
  DEALLOCATE PREPARE stmt;
  SELECT 'p1-3', c0;

  PREPARE stmt FROM 'SELECT LAST_VALUE(COALESCE(?), COALESCE(?)) FROM seq_1_to_5';
  EXECUTE stmt USING c0, c0;
  DEALLOCATE PREPARE stmt;
  SELECT 'p1-4', c0;

  PREPARE stmt FROM 'SELECT LAST_VALUE(COALESCE(?),NULL) FROM seq_1_to_5';
  EXECUTE stmt USING c0;
  DEALLOCATE PREPARE stmt;
  SELECT 'p1-5', c0;

END;
/
DELIMITER ;/
CALL p1;
DROP PROCEDURE p1;
DROP FUNCTION ff0;


--echo #
--echo # Getting a cursor via an SP OUT parameter using EXECUTE IMMEDIATE
--echo #

DELIMITER /;
CREATE PROCEDURE p1(OUT c1 SYS_REFCURSOR)
BEGIN
  DECLARE c0 SYS_REFCURSOR;
  OPEN c0 FOR SELECT 10; -- will be closed on return
  OPEN c1 FOR SELECT 11;
END;
/
CREATE PROCEDURE p2()
BEGIN
  DECLARE c0 SYS_REFCURSOR;
  DECLARE v INT;
  EXECUTE IMMEDIATE 'CALL p1(?)' USING c0;
  FETCH c0 INTO v;
  SELECT c0, v;
END;
/
DELIMITER ;/
CALL p2;
CALL p2; /* make sure it starts from m_statement_cursors(0) on the second execution */
DROP PROCEDURE p2;
DROP PROCEDURE p1;


--echo #
--echo # Getting a cursor via an SP OUT parameter using EXECUTE IMMEDIATE
--echo # This script demonstrates that two open cursors co-exist.
--echo #

# - On the first loop iteration p1 opens m_statement_cursors.at(0).
# - On the second iteration p1 opens m_statement_cursors.at(1),
#   because p2c0 still refers to m_statement_cursors.at(0).
#   In the end of 'CALL p1(?)' f1c0 gets copied to p2c0,
#   p2c0 detaches from m_statement_cursors.at(0)
#   p2c0 now refers to m_statement_cursors.at(1)
# - On the third iteration p1 opens m_statement_cursors.at(0)
# And so on:
# - on the odd iteration p1 opens m_statement_cursors.at(0)
# - on the even iteration p1 opens m_statement_cursors.at(1)


DELIMITER /;
CREATE PROCEDURE p1(OUT f1c0 SYS_REFCURSOR)
BEGIN
  OPEN f1c0 FOR SELECT 10;
END;
/
CREATE PROCEDURE p2(count INT)
BEGIN
  DECLARE p2c0 SYS_REFCURSOR;
  DECLARE v INT;
  FOR i IN 1..count DO
    EXECUTE IMMEDIATE 'CALL p1(?)' USING p2c0;
    SELECT CONCAT('p2c0=', CAST(p2c0 AS CHAR)) AS p2c0;
  END FOR;
  FETCH p2c0 INTO v;
  SELECT CONCAT('v=', v) AS v;
END;
/
DELIMITER ;/
SET @@max_open_cursors=2;
--disable_column_names
CALL p2(10);
SET @@max_open_cursors=1;
--error ER_TOO_MANY_OPEN_CURSORS
CALL p2(10);
--enable_column_names
SET @@max_open_cursors=DEFAULT;
DROP PROCEDURE p2;
DROP PROCEDURE p1;


--echo #
--echo # Getting a cursor via an SP INOUT parameter using EXECUTE IMMEDIATE
--echo #

DELIMITER /;
CREATE PROCEDURE p1(INOUT c1 SYS_REFCURSOR)
BEGIN
  DECLARE c0 SYS_REFCURSOR;
  OPEN c0 FOR SELECT 10; -- will be closed on return
  OPEN c1 FOR SELECT 11;
END;
/
CREATE PROCEDURE p2()
BEGIN
  DECLARE c0 SYS_REFCURSOR;
  DECLARE v INT;
  EXECUTE IMMEDIATE 'CALL p1(?)' USING c0;
  FETCH c0 INTO v;
  SELECT c0, v;
END;
/
DELIMITER ;/
CALL p2;
CALL p2; /* make sure it starts from m_statement_cursors(0) on the second execution */
DROP PROCEDURE p2;
DROP PROCEDURE p1;


--echo #
--echo # Getting a cursor via an SP INOUT parameter using EXECUTE IMMEDIATE
--echo # This script demonstates that only one cursor is needed.
--echo #

DELIMITER /;
CREATE PROCEDURE p1(INOUT f1c0 SYS_REFCURSOR)
BEGIN
  OPEN f1c0 FOR SELECT 10;
END;
/
CREATE PROCEDURE p2(count INT)
BEGIN
  DECLARE p2c0 SYS_REFCURSOR;
  DECLARE v INT;
  FOR i IN 1..count DO
    EXECUTE IMMEDIATE 'CALL p1(?)' USING p2c0;
    SELECT CONCAT('p2c0=', CAST(p2c0 AS CHAR)) AS p2c0;
  END FOR;
  FETCH p2c0 INTO v;
  SELECT CONCAT('v=', v) AS v;
END;
/
DELIMITER ;/
SET @@max_open_cursors=1;
--disable_column_names
CALL p2(10);
--enable_column_names
SET @@max_open_cursors=DEFAULT;
DROP PROCEDURE p2;
DROP PROCEDURE p1;


--echo #
--echo # Setting Item_param from not-NULL to NULL via EXECUTE IMMEDIATE
--echo # OUT param
--echo #

DELIMITER /;
CREATE PROCEDURE p1(OUT p1c0 SYS_REFCURSOR)
BEGIN
  SELECT 'p1-0' AS stage, p1c0;
  SET p1c0= NULL;
  SELECT 'p1-1' AS stage, p1c0;
END;
/
CREATE PROCEDURE p2()
BEGIN
  DECLARE p2c0 SYS_REFCURSOR;
  OPEN p2c0 FOR SELECT 10;
  EXECUTE IMMEDIATE 'CALL p1(?)' USING p2c0;
  SELECT 'p2-1' AS stage, p2c0;
  OPEN p2c0 FOR SELECT 20;
  SELECT 'p2-2' AS stage, p2c0; -- Should return 0 if cursor array elements do not leak.
END;
/
DELIMITER ;/
--disable_column_names
CALL p2;
--enable_column_names
DROP PROCEDURE p2;
DROP PROCEDURE p1;


--echo #
--echo # Setting Item_param from not-NULL to NULL via EXECUTE IMMEDIATE
--echo # INOUT param
--echo #

DELIMITER /;
CREATE PROCEDURE p1(INOUT p1c0 SYS_REFCURSOR)
BEGIN
  SELECT 'p1-0' AS stage, p1c0;
  SET p1c0= NULL;
  SELECT 'p1-1' AS stage, p1c0;
END;
/
CREATE PROCEDURE p2()
BEGIN
  DECLARE p2c0 SYS_REFCURSOR;
  OPEN p2c0 FOR SELECT 10;
  EXECUTE IMMEDIATE 'CALL p1(?)' USING p2c0;
  SELECT 'p2-1' AS stage, p2c0;
  OPEN p2c0 FOR SELECT 20;
  SELECT 'pc-2' AS stage, p2c0; -- Should return 0 if cursor array elements do not leak.
END;
/
DELIMITER ;/
--disable_column_names
CALL p2;
--enable_column_names
DROP PROCEDURE p2;
DROP PROCEDURE p1;
