diff options
-rw-r--r-- | mysql-test/r/sp.result | 106 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/r/sp-cursor.result | 29 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/t/sp-cursor.test | 27 | ||||
-rw-r--r-- | mysql-test/t/sp.test | 114 | ||||
-rw-r--r-- | sql/field.h | 6 | ||||
-rw-r--r-- | sql/sp_rcontext.cc | 36 | ||||
-rw-r--r-- | sql/sp_rcontext.h | 2 |
7 files changed, 316 insertions, 4 deletions
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 1adb979a35a..4b571e526a2 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -8127,3 +8127,109 @@ v_name v_total c 1 DROP PROCEDURE p1; DROP TABLE t1; +# +# Start of 10.3 tests +# +# +# MDEV-12007 Allow ROW variables as a cursor FETCH target +# +# The cursor and the ROW variable in FETCH must have the same number of fields +CREATE PROCEDURE p1() +BEGIN +DECLARE done INT DEFAULT FALSE; +DECLARE rec ROW(aa INT, bb VARCHAR(32), cc INT); +DECLARE cur CURSOR FOR SELECT 10 AS a,'b10' AS b; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; +OPEN cur; +read_loop: +LOOP +FETCH cur INTO rec; +IF done THEN +LEAVE read_loop; +END IF; +END LOOP; +CLOSE cur; +END; +$$ +CALL p1(); +ERROR HY000: Incorrect number of FETCH variables +DROP PROCEDURE p1; +# Multiple ROW variables in FETCH +CREATE PROCEDURE p1() +BEGIN +DECLARE done INT DEFAULT FALSE; +DECLARE rec1 ROW(aa INT); +DECLARE rec2 ROW(aa INT); +DECLARE cur CURSOR FOR SELECT 10 AS a, 20 AS b; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; +OPEN cur; +read_loop: +LOOP +FETCH cur INTO rec1, rec2; +IF done THEN +LEAVE read_loop; +END IF; +END LOOP; +CLOSE cur; +END; +$$ +CALL p1(); +ERROR 21000: Operand should contain 1 column(s) +DROP PROCEDURE p1; +# A complete working example +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +INSERT INTO t1 VALUES (20,'b20'); +INSERT INTO t1 VALUES (30,'b30'); +CREATE PROCEDURE p1() +BEGIN +DECLARE done INT DEFAULT FALSE; +DECLARE rec ROW(aa INT, bb VARCHAR(32)); +DECLARE cur CURSOR FOR SELECT a,b FROM t1; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; +OPEN cur; +read_loop: +LOOP +FETCH cur INTO rec; +IF done THEN +LEAVE read_loop; +END IF; +SELECT CONCAT('rec=(',rec.aa,',',rec.bb,')') AS c; +END LOOP; +CLOSE cur; +END; +$$ +CALL p1(); +c +rec=(10,b10) +c +rec=(20,b20) +c +rec=(30,b30) +DROP PROCEDURE p1; +DROP TABLE t1; +# A ROW variable with a single field +CREATE PROCEDURE p1() +BEGIN +DECLARE done INT DEFAULT FALSE; +DECLARE rec ROW(aa INT); +DECLARE cur CURSOR FOR SELECT 10 AS a UNION SELECT 20; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; +OPEN cur; +read_loop: +LOOP +FETCH cur INTO rec; +IF done THEN +LEAVE read_loop; +END IF; +SELECT CONCAT('rec=(',rec.aa,')') AS c; +END LOOP; +CLOSE cur; +END; +$$ +CALL p1(); +c +rec=(10) +c +rec=(20) +DROP PROCEDURE p1; diff --git a/mysql-test/suite/compat/oracle/r/sp-cursor.result b/mysql-test/suite/compat/oracle/r/sp-cursor.result index 758663f3063..025eca9fe06 100644 --- a/mysql-test/suite/compat/oracle/r/sp-cursor.result +++ b/mysql-test/suite/compat/oracle/r/sp-cursor.result @@ -783,3 +783,32 @@ a b c DROP TABLE t2; DROP PROCEDURE p1; DROP TABLE t1; +# +# MDEV-12007 Allow ROW variables as a cursor FETCH target +# +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +INSERT INTO t1 VALUES (20,'b20'); +INSERT INTO t1 VALUES (30,'b30'); +CREATE PROCEDURE p1 AS +rec ROW(a INT, b VARCHAR(32)); +CURSOR c IS SELECT a,b FROM t1; +BEGIN +OPEN c; +LOOP +FETCH c INTO rec; +EXIT WHEN c%NOTFOUND; +SELECT ('rec=(' || rec.a ||','|| rec.b||')') AS c; +END LOOP; +CLOSE c; +END; +$$ +CALL p1(); +c +rec=(10,b10) +c +rec=(20,b20) +c +rec=(30,b30) +DROP PROCEDURE p1; +DROP TABLE t1; diff --git a/mysql-test/suite/compat/oracle/t/sp-cursor.test b/mysql-test/suite/compat/oracle/t/sp-cursor.test index e968ded847d..f73d76d4fe1 100644 --- a/mysql-test/suite/compat/oracle/t/sp-cursor.test +++ b/mysql-test/suite/compat/oracle/t/sp-cursor.test @@ -783,3 +783,30 @@ SELECT * FROM t2; DROP TABLE t2; DROP PROCEDURE p1; DROP TABLE t1; + +--echo # +--echo # MDEV-12007 Allow ROW variables as a cursor FETCH target +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +INSERT INTO t1 VALUES (20,'b20'); +INSERT INTO t1 VALUES (30,'b30'); +DELIMITER $$; +CREATE PROCEDURE p1 AS + rec ROW(a INT, b VARCHAR(32)); + CURSOR c IS SELECT a,b FROM t1; +BEGIN + OPEN c; + LOOP + FETCH c INTO rec; + EXIT WHEN c%NOTFOUND; + SELECT ('rec=(' || rec.a ||','|| rec.b||')') AS c; + END LOOP; + CLOSE c; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 92f5acffb8e..7eedc67acd9 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -9588,3 +9588,117 @@ DELIMITER ;| CALL p1(); DROP PROCEDURE p1; DROP TABLE t1; + +--echo # +--echo # Start of 10.3 tests +--echo # + +--echo # +--echo # MDEV-12007 Allow ROW variables as a cursor FETCH target +--echo # + + +--echo # The cursor and the ROW variable in FETCH must have the same number of fields +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE done INT DEFAULT FALSE; + DECLARE rec ROW(aa INT, bb VARCHAR(32), cc INT); + DECLARE cur CURSOR FOR SELECT 10 AS a,'b10' AS b; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + OPEN cur; +read_loop: + LOOP + FETCH cur INTO rec; + IF done THEN + LEAVE read_loop; + END IF; + END LOOP; + CLOSE cur; +END; +$$ +DELIMITER ;$$ +--error ER_SP_WRONG_NO_OF_FETCH_ARGS +CALL p1(); +DROP PROCEDURE p1; + + +--echo # Multiple ROW variables in FETCH +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE done INT DEFAULT FALSE; + DECLARE rec1 ROW(aa INT); + DECLARE rec2 ROW(aa INT); + DECLARE cur CURSOR FOR SELECT 10 AS a, 20 AS b; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + OPEN cur; +read_loop: + LOOP + FETCH cur INTO rec1, rec2; + IF done THEN + LEAVE read_loop; + END IF; + END LOOP; + CLOSE cur; +END; +$$ +DELIMITER ;$$ +--error ER_OPERAND_COLUMNS +CALL p1(); +DROP PROCEDURE p1; + + +--echo # A complete working example +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +INSERT INTO t1 VALUES (20,'b20'); +INSERT INTO t1 VALUES (30,'b30'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE done INT DEFAULT FALSE; + DECLARE rec ROW(aa INT, bb VARCHAR(32)); + DECLARE cur CURSOR FOR SELECT a,b FROM t1; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + OPEN cur; +read_loop: + LOOP + FETCH cur INTO rec; + IF done THEN + LEAVE read_loop; + END IF; + SELECT CONCAT('rec=(',rec.aa,',',rec.bb,')') AS c; + END LOOP; + CLOSE cur; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # A ROW variable with a single field +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE done INT DEFAULT FALSE; + DECLARE rec ROW(aa INT); + DECLARE cur CURSOR FOR SELECT 10 AS a UNION SELECT 20; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + OPEN cur; +read_loop: + LOOP + FETCH cur INTO rec; + IF done THEN + LEAVE read_loop; + END IF; + SELECT CONCAT('rec=(',rec.aa,')') AS c; + END LOOP; + CLOSE cur; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; diff --git a/sql/field.h b/sql/field.h index 68327159a22..b3b8dcd703e 100644 --- a/sql/field.h +++ b/sql/field.h @@ -4037,6 +4037,12 @@ public: { return m_row_field_definitions != NULL; } + // Check if "this" defines a ROW variable with n elements + uint is_row(uint n) const + { + return m_row_field_definitions != NULL && + m_row_field_definitions->elements == n; + } Row_definition_list *row_field_definitions() const { return m_row_field_definitions; diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 17c91b09727..3ae455e00c9 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -572,6 +572,22 @@ int sp_rcontext::set_variable_row_field(THD *thd, uint var_idx, uint field_idx, } +int sp_rcontext::set_variable_row(THD *thd, uint var_idx, List<Item> &items) +{ + DBUG_ENTER("sp_rcontext::set_variable_row"); + DBUG_ASSERT(thd->spcont->get_item(var_idx)->cols() == items.elements); + List_iterator<Item> it(items); + Item *item; + for (uint i= 0 ; (item= it++) ; i++) + { + int rc; + if ((rc= thd->spcont->set_variable_row_field(thd, var_idx, i, &item))) + DBUG_RETURN(rc); + } + DBUG_RETURN(0); +} + + Item_cache *sp_rcontext::create_case_expr_holder(THD *thd, const Item *item) const { @@ -688,7 +704,9 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars) MYF(0)); return -1; } - if (vars->elements != result.get_field_count()) + if (vars->elements != result.get_field_count() && + (vars->elements != 1 || + !vars->head()->field_def.is_row(result.get_field_count()))) { my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS, ER_THD(thd, ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0)); @@ -743,15 +761,16 @@ int sp_cursor::Select_fetch_into_spvars::prepare(List<Item> &fields, } -int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items) +bool sp_cursor::Select_fetch_into_spvars:: + send_data_to_variable_list(List<sp_variable> &vars, List<Item> &items) { - List_iterator_fast<sp_variable> spvar_iter(*spvar_list); + List_iterator_fast<sp_variable> spvar_iter(vars); List_iterator_fast<Item> item_iter(items); sp_variable *spvar; Item *item; /* Must be ensured by the caller */ - DBUG_ASSERT(spvar_list->elements == items.elements); + DBUG_ASSERT(vars.elements == items.elements); /* Assign the row fetched from a server side cursor to stored @@ -764,3 +783,12 @@ int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items) } return false; } + + +int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items) +{ + return (spvar_list->elements == 1 && + (spvar_list->head())->field_def.is_row(items.elements)) ? + thd->spcont->set_variable_row(thd, spvar_list->head()->offset, items) : + send_data_to_variable_list(*spvar_list, items); +} diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 78cbfd9ec26..79d571faca7 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -190,6 +190,7 @@ public: void set_variable_row_field_to_null(THD *thd, uint var_idx, uint field_idx); int set_variable_row_field(THD *thd, uint var_idx, uint field_idx, Item **value); + int set_variable_row(THD *thd, uint var_idx, List<Item> &items); Item *get_item(uint var_idx) const { return m_var_items[var_idx]; } @@ -419,6 +420,7 @@ private: { List<sp_variable> *spvar_list; uint field_count; + bool send_data_to_variable_list(List<sp_variable> &vars, List<Item> &items); public: Select_fetch_into_spvars(THD *thd_arg): select_result_interceptor(thd_arg) {} uint get_field_count() { return field_count; } |