Commit 6b2f1309 authored by unknown's avatar unknown

Patch for WL#2894: Make stored routine variables work

according to the standard.

The idea is to use Field-classes to implement stored routines
variables. Also, we should provide facade to Item-hierarchy
by Item_field class (it is necessary, since SRVs take part
in expressions).

The patch fixes the following bugs:
  - BUG#8702: Stored Procedures: No Error/Warning shown for inappropriate data 
    type matching; 
 
  - BUG#8768: Functions: For any unsigned data type, -ve values can be passed 
    and returned; 
 
  - BUG#8769: Functions: For Int datatypes, out of range values can be passed 
    and returned; 
 
  - BUG#9078: STORED PROCDURE: Decimal digits are not displayed when we use 
    DECIMAL datatype; 
 
  - BUG#9572: Stored procedures: variable type declarations ignored; 
 
  - BUG#12903: upper function does not work inside a function; 
 
  - BUG#13705: parameters to stored procedures are not verified; 
 
  - BUG#13808: ENUM type stored procedure parameter accepts non-enumerated
    data; 
 
  - BUG#13909: Varchar Stored Procedure Parameter always BINARY string (ignores 
    CHARACTER SET); 
 
  - BUG#14161: Stored procedure cannot retrieve bigint unsigned;

  - BUG#14188: BINARY variables have no 0x00 padding;

  - BUG#15148: Stored procedure variables accept non-scalar values;


mysql-test/r/ctype_ujis.result:
  Explicitly specify correct charset.
mysql-test/r/schema.result:
  Drop our test database to not affect this test if some test
  left it cause of failure.
mysql-test/r/show_check.result:
  Drop our test database to not affect this test if some test
  left it cause of failure.
mysql-test/r/skip_name_resolve.result:
  Ignore columns with unpredictable values.
mysql-test/r/sp-big.result:
  Add cleanup statement.
mysql-test/r/sp-dynamic.result:
  Add cleanup statements.
mysql-test/r/sp.result:
  Update result file.
mysql-test/r/sum_distinct-big.result:
  Update result file.
mysql-test/r/type_newdecimal-big.result:
  Update result file.
mysql-test/t/ctype_ujis.test:
  Explicitly specify correct charset.
mysql-test/t/schema.test:
  Drop our test database to not affect this test if some test
  left it cause of failure.
mysql-test/t/show_check.test:
  Drop our test database to not affect this test if some test
  left it cause of failure.
mysql-test/t/skip_name_resolve.test:
  Ignore columns with unpredictable values.
mysql-test/t/sp-big.test:
  Add cleanup statement.
mysql-test/t/sp-dynamic.test:
  Add cleanup statements.
mysql-test/t/sp.test:
  Non-scalar values prohibited for assignment to SP-vars;
  polishing.
mysql-test/t/type_newdecimal-big.test:
  Update type specification so that the variables
  can contain the large values used in the test.
sql/field.cc:
  Extract create_field::init() to initialize an existing
  instance of create_field from new_create_field().
sql/field.h:
  Extract create_field::init() to initialize an existing
  instance of create_field from new_create_field().
sql/item.cc:
  - Introduce a new class: Item_sp_variable -- a base class
    of stored-routine-variables classes;
  - Introduce Item_case_expr -- an Item, which is used to access
    to the expression of CASE statement;
sql/item.h:
  - Introduce a new class: Item_sp_variable -- a base class
    of stored-routine-variables classes;
  - Introduce Item_case_expr -- an Item, which is used to access
    to the expression of CASE statement;
sql/item_func.cc:
  Pass the Field (instead of Item) for the return value of
  a function to the function execution routine.
sql/item_func.h:
  Pass the Field (instead of Item) for the return value of
  a function to the function execution routine.
sql/mysql_priv.h:
  Move create_virtual_tmp_table() out of sql_select.h.
sql/sp.cc:
  Use create_result_field() instead of make_field().
sql/sp_head.cc:
  - Add a function to map enum_field_types to Item::Type;
  - Add sp_instr_push_case_expr instruction -- an instruction
    to push CASE expression into the active running context;
  - Add sp_instr_pop_case_expr instruction -- an instruction
    to pop CASE expression from the active running context;
  - Adapt the SP-execution code to using Fields instead of Items
    for SP-vars;
  - Use create_field structure for field description instead of
    a set of members.
sql/sp_head.h:
  - Add a function to map enum_field_types to Item::Type;
  - Add sp_instr_push_case_expr instruction -- an instruction
    to push CASE expression into the active running context;
  - Add sp_instr_pop_case_expr instruction -- an instruction
    to pop CASE expression from the active running context;
  - Adapt the SP-execution code to using Fields instead of Items
    for SP-vars;
  - Use create_field structure for field description instead of
    a set of members.
sql/sp_pcontext.cc:
  - Change rules to assign an index of SP-variable: use
    transparent index;
  - Add an operation to retrieve a list of defined SP-vars
    from the processing context recursively.
sql/sp_pcontext.h:
  - Change rules to assign an index of SP-variable: use
    transparent index;
  - Add an operation to retrieve a list of defined SP-vars
    from the processing context recursively.
sql/sp_rcontext.cc:
  - Change rules to assign an index of SP-variable: use
    transparent index;
  - Use a tmp virtual table to store SP-vars instead of Items;
  - Provide operations to work with CASE expresion.
sql/sp_rcontext.h:
  - Change rules to assign an index of SP-variable: use
    transparent index;
  - Use a tmp virtual table to store SP-vars instead of Items;
  - Provide operations to work with CASE expresion.
sql/sql_class.cc:
  - Reflect Item_splocal ctor changes;
  - Item_splocal::get_offset() has been renamed to get_var_idx().
sql/sql_class.h:
  Polishing.
sql/sql_parse.cc:
  Extract create_field::init() to initialize an existing
  instance of create_field from new_create_field().
sql/sql_select.cc:
  Take care of BLOB columns in create_virtual_tmp_table().
sql/sql_select.h:
  Move create_virtual_tmp_table() out of sql_select.h.
sql/sql_trigger.cc:
  Use boolean constants for boolean type instead of numerical ones.
sql/sql_yacc.yy:
  Provide an instance of create_field for each SP-var.
mysql-test/include/sp-vars.inc:
  The definitions of common-procedures, which are created
  under different circumstances.
mysql-test/r/sp-vars.result:
  Result file for the SP-vars test.
mysql-test/sp-vars.test:
  A new test for checking SP-vars functionality.
parent 29eac312
delimiter |;
---------------------------------------------------------------------------
CREATE PROCEDURE sp_vars_check_dflt()
BEGIN
DECLARE v1 TINYINT DEFAULT 1e200;
DECLARE v1u TINYINT UNSIGNED DEFAULT 1e200;
DECLARE v2 TINYINT DEFAULT -1e200;
DECLARE v2u TINYINT UNSIGNED DEFAULT -1e200;
DECLARE v3 TINYINT DEFAULT 300;
DECLARE v3u TINYINT UNSIGNED DEFAULT 300;
DECLARE v4 TINYINT DEFAULT -300;
DECLARE v4u TINYINT UNSIGNED DEFAULT -300;
DECLARE v5 TINYINT DEFAULT 10 * 10 * 10;
DECLARE v5u TINYINT UNSIGNED DEFAULT 10 * 10 * 10;
DECLARE v6 TINYINT DEFAULT -10 * 10 * 10;
DECLARE v6u TINYINT UNSIGNED DEFAULT -10 * 10 * 10;
DECLARE v7 TINYINT DEFAULT '10';
DECLARE v8 TINYINT DEFAULT '10 ';
DECLARE v9 TINYINT DEFAULT ' 10 ';
DECLARE v10 TINYINT DEFAULT 'String 10 ';
DECLARE v11 TINYINT DEFAULT 'String10';
DECLARE v12 TINYINT DEFAULT '10 String';
DECLARE v13 TINYINT DEFAULT '10String';
DECLARE v14 TINYINT DEFAULT concat('10', ' ');
DECLARE v15 TINYINT DEFAULT concat(' ', '10');
DECLARE v16 TINYINT DEFAULT concat('Hello, ', 'world');
DECLARE v17 DECIMAL(64, 2) DEFAULT 12;
DECLARE v18 DECIMAL(64, 2) DEFAULT 12.123;
DECLARE v19 DECIMAL(64, 2) DEFAULT 11 + 1;
DECLARE v20 DECIMAL(64, 2) DEFAULT 12 + 0.123;
SELECT v1, v1u, v2, v2u, v3, v3u, v4, v4u;
SELECT v5, v5u, v6, v6u;
SELECT v7, v8, v9, v10, v11, v12, v13, v14, v15, v16;
SELECT v17, v18, v19, v20;
END|
---------------------------------------------------------------------------
CREATE PROCEDURE sp_vars_check_assignment()
BEGIN
DECLARE i1, i2, i3, i4 TINYINT;
DECLARE u1, u2, u3, u4 TINYINT UNSIGNED;
DECLARE d1, d2, d3 DECIMAL(64, 2);
SET i1 = 1e200;
SET i2 = -1e200;
SET i3 = 300;
SET i4 = -300;
SELECT i1, i2, i3, i4;
SET i1 = 10 * 10 * 10;
SET i2 = -10 * 10 * 10;
SET i3 = sign(10 * 10) * 10 * 20;
SET i4 = sign(-10 * 10) * -10 * 20;
SELECT i1, i2, i3, i4;
SET u1 = 1e200;
SET u2 = -1e200;
SET u3 = 300;
SET u4 = -300;
SELECT u1, u2, u3, u4;
SET u1 = 10 * 10 * 10;
SET u2 = -10 * 10 * 10;
SET u3 = sign(10 * 10) * 10 * 20;
SET u4 = sign(-10 * 10) * -10 * 20;
SELECT u1, u2, u3, u4;
SET d1 = 1234;
SET d2 = 1234.12;
SET d3 = 1234.1234;
SELECT d1, d2, d3;
SET d1 = 12 * 100 + 34;
SET d2 = 12 * 100 + 34 + 0.12;
SET d3 = 12 * 100 + 34 + 0.1234;
SELECT d1, d2, d3;
END|
---------------------------------------------------------------------------
CREATE FUNCTION sp_vars_check_ret1() RETURNS TINYINT
BEGIN
RETURN 1e200;
END|
---------------------------------------------------------------------------
CREATE FUNCTION sp_vars_check_ret2() RETURNS TINYINT
BEGIN
RETURN 10 * 10 * 10;
END|
---------------------------------------------------------------------------
CREATE FUNCTION sp_vars_check_ret3() RETURNS TINYINT
BEGIN
RETURN 'Hello, world';
END|
---------------------------------------------------------------------------
CREATE FUNCTION sp_vars_check_ret4() RETURNS DECIMAL(64, 2)
BEGIN
RETURN 12 * 10 + 34 + 0.1234;
END|
---------------------------------------------------------------------------
delimiter ;|
...@@ -2317,7 +2317,7 @@ CREATE TABLE t2(c2 char(2)) default charset = ujis; ...@@ -2317,7 +2317,7 @@ CREATE TABLE t2(c2 char(2)) default charset = ujis;
INSERT INTO t1 VALUES(_ujis 0xA4A2); INSERT INTO t1 VALUES(_ujis 0xA4A2);
CREATE PROCEDURE sp1() CREATE PROCEDURE sp1()
BEGIN BEGIN
DECLARE a CHAR(1); DECLARE a CHAR(2) CHARSET ujis;
DECLARE cur1 CURSOR FOR SELECT c1 FROM t1; DECLARE cur1 CURSOR FOR SELECT c1 FROM t1;
OPEN cur1; OPEN cur1;
FETCH cur1 INTO a; FETCH cur1 INTO a;
......
drop database if exists mysqltest1;
create schema foo; create schema foo;
show create schema foo; show create schema foo;
Database Create Database Database Create Database
......
drop table if exists t1,t2; drop table if exists t1,t2;
drop table if exists t1aa,t2aa; drop table if exists t1aa,t2aa;
drop database if exists mysqltest; drop database if exists mysqltest;
drop database if exists mysqltest1;
delete from mysql.user where user='mysqltest_1' || user='mysqltest_2' || user='mysqltest_3'; delete from mysql.user where user='mysqltest_1' || user='mysqltest_2' || user='mysqltest_3';
delete from mysql.db where user='mysqltest_1' || user='mysqltest_2' || user='mysqltest_3'; delete from mysql.db where user='mysqltest_1' || user='mysqltest_2' || user='mysqltest_3';
flush privileges; flush privileges;
......
...@@ -10,5 +10,5 @@ user() ...@@ -10,5 +10,5 @@ user()
# #
show processlist; show processlist;
Id User Host db Command Time State Info Id User Host db Command Time State Info
# root # test Sleep # NULL <id> root <host> test <command> <time> <state> <info>
# root # test Query # NULL show processlist <id> root <host> test <command> <time> <state> <info>
...@@ -25,6 +25,7 @@ count(*) ...@@ -25,6 +25,7 @@ count(*)
select count(*) from t2; select count(*) from t2;
count(*) count(*)
0 0
drop procedure if exists p1;
create procedure p1() create procedure p1()
begin begin
declare done integer default 0; declare done integer default 0;
......
drop procedure if exists p1|
drop procedure if exists p2|
create procedure p1() create procedure p1()
begin begin
prepare stmt from "select 1"; prepare stmt from "select 1";
......
This diff is collapsed.
...@@ -248,13 +248,13 @@ return i+1| ...@@ -248,13 +248,13 @@ return i+1|
call sub1("sub1a", (select 7))| call sub1("sub1a", (select 7))|
call sub1("sub1b", (select max(i) from t2))| call sub1("sub1b", (select max(i) from t2))|
call sub1("sub1c", (select i,d from t2 limit 1))| call sub1("sub1c", (select i,d from t2 limit 1))|
ERROR 21000: Operand should contain 1 column(s)
call sub1("sub1d", (select 1 from (select 1) a))| call sub1("sub1d", (select 1 from (select 1) a))|
call sub2("sub2")| call sub2("sub2")|
select * from t1| select * from t1|
id data id data
sub1a 7 sub1a 7
sub1b 3 sub1b 3
sub1c 1
sub1d 1 sub1d 1
sub2 6 sub2 6
select sub3((select max(i) from t2))| select sub3((select max(i) from t2))|
...@@ -2686,7 +2686,7 @@ call bug8937()| ...@@ -2686,7 +2686,7 @@ call bug8937()|
s x y z s x y z
16 3 1 6 16 3 1 6
a a
3.2000 3.2
drop procedure bug8937| drop procedure bug8937|
delete from t1| delete from t1|
drop procedure if exists bug6900| drop procedure if exists bug6900|
...@@ -2890,21 +2890,30 @@ create function bug9775(v1 char(1)) returns enum('a','b') return v1| ...@@ -2890,21 +2890,30 @@ create function bug9775(v1 char(1)) returns enum('a','b') return v1|
select bug9775('a'),bug9775('b'),bug9775('c')| select bug9775('a'),bug9775('b'),bug9775('c')|
bug9775('a') bug9775('b') bug9775('c') bug9775('a') bug9775('b') bug9775('c')
a b a b
Warnings:
Warning 1265 Data truncated for column 'bug9775('c')' at row 1
drop function bug9775| drop function bug9775|
create function bug9775(v1 int) returns enum('a','b') return v1| create function bug9775(v1 int) returns enum('a','b') return v1|
select bug9775(1),bug9775(2),bug9775(3)| select bug9775(1),bug9775(2),bug9775(3)|
bug9775(1) bug9775(2) bug9775(3) bug9775(1) bug9775(2) bug9775(3)
a b a b
Warnings:
Warning 1265 Data truncated for column 'bug9775(3)' at row 1
drop function bug9775| drop function bug9775|
create function bug9775(v1 char(1)) returns set('a','b') return v1| create function bug9775(v1 char(1)) returns set('a','b') return v1|
select bug9775('a'),bug9775('b'),bug9775('a,b'),bug9775('c')| select bug9775('a'),bug9775('b'),bug9775('a,b'),bug9775('c')|
bug9775('a') bug9775('b') bug9775('a,b') bug9775('c') bug9775('a') bug9775('b') bug9775('a,b') bug9775('c')
a b a,b a b a
Warnings:
Warning 1265 Data truncated for column 'v1' at row 1
Warning 1265 Data truncated for column 'bug9775('c')' at row 1
drop function bug9775| drop function bug9775|
create function bug9775(v1 int) returns set('a','b') return v1| create function bug9775(v1 int) returns set('a','b') return v1|
select bug9775(1),bug9775(2),bug9775(3),bug9775(4)| select bug9775(1),bug9775(2),bug9775(3),bug9775(4)|
bug9775(1) bug9775(2) bug9775(3) bug9775(4) bug9775(1) bug9775(2) bug9775(3) bug9775(4)
a b a,b a b a,b
Warnings:
Warning 1265 Data truncated for column 'bug9775(4)' at row 1
drop function bug9775| drop function bug9775|
drop function if exists bug8861| drop function if exists bug8861|
create function bug8861(v1 int) returns year return v1| create function bug8861(v1 int) returns year return v1|
...@@ -2927,12 +2936,10 @@ create procedure bug9004_2(x char(16)) ...@@ -2927,12 +2936,10 @@ create procedure bug9004_2(x char(16))
call bug9004_1(x)| call bug9004_1(x)|
call bug9004_1('12345678901234567')| call bug9004_1('12345678901234567')|
Warnings: Warnings:
Warning 1265 Data truncated for column 'id' at row 1 Warning 1265 Data truncated for column 'x' at row 1
Warning 1265 Data truncated for column 'id' at row 2
call bug9004_2('12345678901234567890')| call bug9004_2('12345678901234567890')|
Warnings: Warnings:
Warning 1265 Data truncated for column 'id' at row 1 Warning 1265 Data truncated for column 'x' at row 1
Warning 1265 Data truncated for column 'id' at row 2
delete from t1| delete from t1|
drop procedure bug9004_1| drop procedure bug9004_1|
drop procedure bug9004_2| drop procedure bug9004_2|
...@@ -3527,14 +3534,15 @@ end| ...@@ -3527,14 +3534,15 @@ end|
call bug12589_1()| call bug12589_1()|
Table Create Table Table Create Table
tm1 CREATE TEMPORARY TABLE `tm1` ( tm1 CREATE TEMPORARY TABLE `tm1` (
`spv1` decimal(1,0) unsigned default NULL `spv1` decimal(3,3) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
Warnings: Warnings:
Warning 1292 Truncated incorrect DECIMAL value: 'test' Warning 1264 Out of range value adjusted for column 'spv1' at row 1
Warning 1366 Incorrect decimal value: 'test' for column 'spv1' at row 1
call bug12589_2()| call bug12589_2()|
Table Create Table Table Create Table
tm1 CREATE TEMPORARY TABLE `tm1` ( tm1 CREATE TEMPORARY TABLE `tm1` (
`spv1` decimal(6,3) unsigned default NULL `spv1` decimal(6,3) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
call bug12589_3()| call bug12589_3()|
Table Create Table Table Create Table
...@@ -4016,34 +4024,37 @@ create procedure bug14643_1() ...@@ -4016,34 +4024,37 @@ create procedure bug14643_1()
begin begin
declare continue handler for sqlexception select 'boo' as 'Handler'; declare continue handler for sqlexception select 'boo' as 'Handler';
begin begin
declare v int default x; declare v int default undefined_var;
if v = 1 then if v = 1 then
select 1; select 1;
else else
select 2; select v, isnull(v);
end if; end if;
end; end;
end| end|
create procedure bug14643_2() create procedure bug14643_2()
begin begin
declare continue handler for sqlexception select 'boo' as 'Handler'; declare continue handler for sqlexception select 'boo' as 'Handler';
case x case undefined_var
when 1 then when 1 then
select 1; select 1;
else else
select 2; select 2;
end case; end case;
select undefined_var;
end| end|
call bug14643_1()| call bug14643_1()|
Handler Handler
boo boo
2 v isnull(v)
2 NULL 1
call bug14643_2()| call bug14643_2()|
Handler Handler
boo boo
2 2
2 2
Handler
boo
drop procedure bug14643_1| drop procedure bug14643_1|
drop procedure bug14643_2| drop procedure bug14643_2|
drop procedure if exists bug14304| drop procedure if exists bug14304|
......
using_big_test DROP TABLE IF EXISTS t1, t2;
0
CREATE TABLE t1 (id INTEGER); CREATE TABLE t1 (id INTEGER);
CREATE TABLE t2 (id INTEGER); CREATE TABLE t2 (id INTEGER);
INSERT INTO t1 (id) VALUES (1), (1), (1),(1); INSERT INTO t1 (id) VALUES (1), (1), (1),(1);
...@@ -40,19 +39,19 @@ AVG(DISTINCT id) ...@@ -40,19 +39,19 @@ AVG(DISTINCT id)
512.5000 512.5000
SELECT SUM(DISTINCT id)/COUNT(DISTINCT id) FROM t1 GROUP BY id % 13; SELECT SUM(DISTINCT id)/COUNT(DISTINCT id) FROM t1 GROUP BY id % 13;
SUM(DISTINCT id)/COUNT(DISTINCT id) SUM(DISTINCT id)/COUNT(DISTINCT id)
513.50000 513.5000
508.00000 508.0000
509.00000 509.0000
510.00000 510.0000
511.00000 511.0000
512.00000 512.0000
513.00000 513.0000
514.00000 514.0000
515.00000 515.0000
516.00000 516.0000
517.00000 517.0000
511.50000 511.5000
512.50000 512.5000
INSERT INTO t1 SELECT id+1024 FROM t1; INSERT INTO t1 SELECT id+1024 FROM t1;
INSERT INTO t1 SELECT id+2048 FROM t1; INSERT INTO t1 SELECT id+2048 FROM t1;
INSERT INTO t1 SELECT id+4096 FROM t1; INSERT INTO t1 SELECT id+4096 FROM t1;
......
drop procedure if exists sp1; drop procedure if exists sp1;
create procedure sp1 () begin CREATE PROCEDURE sp1()
declare v1, v2, v3, v4 decimal(16,12); declare v5 int; BEGIN
set v1 = 1; set v2 = 2; set v3 = 1000000000000; set v4 = 2000000000000; set v5 = 0; DECLARE v1, v2, v3, v4 DECIMAL(28,12);
while v5 < 100000 do DECLARE v3_2, v4_2 DECIMAL(28, 12);
set v1 = v1 + 0.000000000001; set v2 = v2 - 0.000000000001; set v3 = v3 + 1; set v4 = v4 - 1; set v5 = v5 + 1; DECLARE counter INT;
end while; select v1, v2, v3 * 0.000000000001, v4 * 0.000000000001; end;// SET v1 = 1;
SET v2 = 2;
SET v3 = 1000000000000;
SET v4 = 2000000000000;
SET counter = 0;
WHILE counter < 100000 DO
SET v1 = v1 + 0.000000000001;
SET v2 = v2 - 0.000000000001;
SET v3 = v3 + 1;
SET v4 = v4 - 1;
SET counter = counter + 1;
END WHILE;
SET v3_2 = v3 * 0.000000000001;
SET v4_2 = v4 * 0.000000000001;
SELECT v1, v2, v3, v3_2, v4, v4_2;
END//
call sp1()// call sp1()//
v1 v2 v3 * 0.000000000001 v4 * 0.000000000001 v1 v2 v3 v3_2 v4 v4_2
1.000000100000 1.999999900000 1.000000100000 1.999999900000 1.000000100000 1.999999900000 1000000100000.000000000000 1.000000100000 1999999900000.000000000000 1.999999900000
drop procedure sp1; drop procedure sp1;
This diff is collapsed.
...@@ -1170,7 +1170,7 @@ INSERT INTO t1 VALUES(_ujis 0xA4A2); ...@@ -1170,7 +1170,7 @@ INSERT INTO t1 VALUES(_ujis 0xA4A2);
DELIMITER |; DELIMITER |;
CREATE PROCEDURE sp1() CREATE PROCEDURE sp1()
BEGIN BEGIN
DECLARE a CHAR(1); DECLARE a CHAR(2) CHARSET ujis;
DECLARE cur1 CURSOR FOR SELECT c1 FROM t1; DECLARE cur1 CURSOR FOR SELECT c1 FROM t1;
OPEN cur1; OPEN cur1;
FETCH cur1 INTO a; FETCH cur1 INTO a;
......
# #
# Just a couple of tests to make sure that schema works. # Just a couple of tests to make sure that schema works.
# #
# Drop mysqltest1 database, as it can left from the previous tests.
#
--disable_warnings
drop database if exists mysqltest1;
--enable_warnings
create schema foo; create schema foo;
show create schema foo; show create schema foo;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
drop table if exists t1,t2; drop table if exists t1,t2;
drop table if exists t1aa,t2aa; drop table if exists t1aa,t2aa;
drop database if exists mysqltest; drop database if exists mysqltest;
drop database if exists mysqltest1;
delete from mysql.user where user='mysqltest_1' || user='mysqltest_2' || user='mysqltest_3'; delete from mysql.user where user='mysqltest_1' || user='mysqltest_2' || user='mysqltest_3';
delete from mysql.db where user='mysqltest_1' || user='mysqltest_2' || user='mysqltest_3'; delete from mysql.db where user='mysqltest_1' || user='mysqltest_2' || user='mysqltest_3';
......
...@@ -15,6 +15,6 @@ DROP USER mysqltest_1@'127.0.0.1/255.255.255.255'; ...@@ -15,6 +15,6 @@ DROP USER mysqltest_1@'127.0.0.1/255.255.255.255';
connect (con1, 127.0.0.1, root, , test, $MASTER_MYPORT, ); connect (con1, 127.0.0.1, root, , test, $MASTER_MYPORT, );
--replace_column 1 # --replace_column 1 #
select user(); select user();
--replace_column 1 # 6 # 3 # --replace_column 1 <id> 3 <host> 5 <command> 6 <time> 7 <state> 8 <info>
show processlist; show processlist;
connection default; connection default;
...@@ -52,6 +52,9 @@ while ($1) ...@@ -52,6 +52,9 @@ while ($1)
--enable_query_log --enable_query_log
select count(*) from t1; select count(*) from t1;
select count(*) from t2; select count(*) from t2;
--disable_warnings
drop procedure if exists p1;
--enable_warnings
delimiter |; delimiter |;
create procedure p1() create procedure p1()
begin begin
......
delimiter |; delimiter |;
--disable_warnings
drop procedure if exists p1|
drop procedure if exists p2|
--enable_warnings
###################################################################### ######################################################################
# Test Dynamic SQL in stored procedures. ############################# # Test Dynamic SQL in stored procedures. #############################
###################################################################### ######################################################################
......
...@@ -367,6 +367,7 @@ create function sub3(i int) returns int ...@@ -367,6 +367,7 @@ create function sub3(i int) returns int
call sub1("sub1a", (select 7))| call sub1("sub1a", (select 7))|
call sub1("sub1b", (select max(i) from t2))| call sub1("sub1b", (select max(i) from t2))|
--error ER_OPERAND_COLUMNS
call sub1("sub1c", (select i,d from t2 limit 1))| call sub1("sub1c", (select i,d from t2 limit 1))|
call sub1("sub1d", (select 1 from (select 1) a))| call sub1("sub1d", (select 1 from (select 1) a))|
call sub2("sub2")| call sub2("sub2")|
...@@ -4797,12 +4798,12 @@ begin ...@@ -4797,12 +4798,12 @@ begin
declare continue handler for sqlexception select 'boo' as 'Handler'; declare continue handler for sqlexception select 'boo' as 'Handler';
begin begin
declare v int default x; declare v int default undefined_var;
if v = 1 then if v = 1 then
select 1; select 1;
else else
select 2; select v, isnull(v);
end if; end if;
end; end;
end| end|
...@@ -4811,12 +4812,14 @@ create procedure bug14643_2() ...@@ -4811,12 +4812,14 @@ create procedure bug14643_2()
begin begin
declare continue handler for sqlexception select 'boo' as 'Handler'; declare continue handler for sqlexception select 'boo' as 'Handler';
case x case undefined_var
when 1 then when 1 then
select 1; select 1;
else else
select 2; select 2;
end case; end case;
select undefined_var;
end| end|
call bug14643_1()| call bug14643_1()|
......
...@@ -12,12 +12,31 @@ drop procedure if exists sp1; ...@@ -12,12 +12,31 @@ drop procedure if exists sp1;
delimiter //; delimiter //;
# #
create procedure sp1 () begin CREATE PROCEDURE sp1()
declare v1, v2, v3, v4 decimal(16,12); declare v5 int; BEGIN
set v1 = 1; set v2 = 2; set v3 = 1000000000000; set v4 = 2000000000000; set v5 = 0; DECLARE v1, v2, v3, v4 DECIMAL(28,12);
while v5 < 100000 do DECLARE v3_2, v4_2 DECIMAL(28, 12);
set v1 = v1 + 0.000000000001; set v2 = v2 - 0.000000000001; set v3 = v3 + 1; set v4 = v4 - 1; set v5 = v5 + 1; DECLARE counter INT;
end while; select v1, v2, v3 * 0.000000000001, v4 * 0.000000000001; end;//
SET v1 = 1;
SET v2 = 2;
SET v3 = 1000000000000;
SET v4 = 2000000000000;
SET counter = 0;
WHILE counter < 100000 DO
SET v1 = v1 + 0.000000000001;
SET v2 = v2 - 0.000000000001;
SET v3 = v3 + 1;
SET v4 = v4 - 1;
SET counter = counter + 1;
END WHILE;
SET v3_2 = v3 * 0.000000000001;
SET v4_2 = v4 * 0.000000000001;
SELECT v1, v2, v3, v3_2, v4, v4_2;
END//
# #
call sp1()// call sp1()//
#-- should return #-- should return
......
This diff is collapsed.
...@@ -130,7 +130,19 @@ public: ...@@ -130,7 +130,19 @@ public:
null_bit == field->null_bit); null_bit == field->null_bit);
} }
virtual bool eq_def(Field *field); virtual bool eq_def(Field *field);
/*
pack_length() returns size (in bytes) used to store field data in memory
(i.e. it returns the maximum size of the field in a row of the table,
which is located in RAM).
*/
virtual uint32 pack_length() const { return (uint32) field_length; } virtual uint32 pack_length() const { return (uint32) field_length; }
/*
pack_length_in_rec() returns size (in bytes) used to store field data on
storage (i.e. it returns the maximal size of the field in a row of the
table, which is located on disk).
*/
virtual uint32 pack_length_in_rec() const { return pack_length(); } virtual uint32 pack_length_in_rec() const { return pack_length(); }
virtual uint32 sort_length() const { return pack_length(); } virtual uint32 sort_length() const { return pack_length(); }
virtual void reset(void) { bzero(ptr,pack_length()); } virtual void reset(void) { bzero(ptr,pack_length()); }
...@@ -1395,6 +1407,12 @@ public: ...@@ -1395,6 +1407,12 @@ public:
void init_for_tmp_table(enum_field_types sql_type_arg, void init_for_tmp_table(enum_field_types sql_type_arg,
uint32 max_length, uint32 decimals, uint32 max_length, uint32 decimals,
bool maybe_null, bool is_unsigned); bool maybe_null, bool is_unsigned);
bool init(THD *thd, char *field_name, enum_field_types type, char *length,
char *decimals, uint type_modifier, Item *default_value,
Item *on_update_value, LEX_STRING *comment, char *change,
List<String> *interval_list, CHARSET_INFO *cs,
uint uint_geom_type);
}; };
......
...@@ -296,23 +296,6 @@ longlong Item::val_int_from_decimal() ...@@ -296,23 +296,6 @@ longlong Item::val_int_from_decimal()
} }
void *Item::operator new(size_t size, Item *reuse, uint *rsize)
{
if (reuse && size <= reuse->rsize)
{
if (rsize)
(*rsize)= reuse->rsize;
reuse->cleanup();
delete reuse;
TRASH((void *)reuse, size);
return (void *)reuse;
}
if (rsize)
(*rsize)= (uint) size;
return (void *)sql_alloc((uint)size);
}
Item::Item(): Item::Item():
rsize(0), name(0), orig_name(0), name_length(0), fixed(0), rsize(0), name(0), orig_name(0), name_length(0), fixed(0),
is_autogenerated_name(TRUE), is_autogenerated_name(TRUE),
...@@ -802,9 +785,41 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions) ...@@ -802,9 +785,41 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions)
/***************************************************************************** /*****************************************************************************
Item_splocal methods Item_sp_variable methods
*****************************************************************************/ *****************************************************************************/
double Item_splocal::val_real()
Item_sp_variable::Item_sp_variable(char *sp_var_name_str,
uint sp_var_name_length)
:m_thd(0)
#ifndef DBUG_OFF
, m_sp(0)
#endif
{
m_name.str= sp_var_name_str;
m_name.length= sp_var_name_length;
}
bool Item_sp_variable::fix_fields(THD *thd, Item **)
{
Item *it;
m_thd= thd; /* NOTE: this must be set before any this_xxx() */
it= this_item();
DBUG_ASSERT(it->fixed);
max_length= it->max_length;
decimals= it->decimals;
unsigned_flag= it->unsigned_flag;
fixed= 1;
collation.set(it->collation.collation, it->collation.derivation);
return FALSE;
}
double Item_sp_variable::val_real()
{ {
DBUG_ASSERT(fixed); DBUG_ASSERT(fixed);
Item *it= this_item(); Item *it= this_item();
...@@ -814,7 +829,7 @@ double Item_splocal::val_real() ...@@ -814,7 +829,7 @@ double Item_splocal::val_real()
} }
longlong Item_splocal::val_int() longlong Item_sp_variable::val_int()
{ {
DBUG_ASSERT(fixed); DBUG_ASSERT(fixed);
Item *it= this_item(); Item *it= this_item();
...@@ -824,13 +839,14 @@ longlong Item_splocal::val_int() ...@@ -824,13 +839,14 @@ longlong Item_splocal::val_int()
} }
String *Item_splocal::val_str(String *sp) String *Item_sp_variable::val_str(String *sp)
{ {
DBUG_ASSERT(fixed); DBUG_ASSERT(fixed);
Item *it= this_item(); Item *it= this_item();
String *res= it->val_str(sp); String *res= it->val_str(sp);
null_value= it->null_value; null_value= it->null_value;
if (!res) if (!res)
return NULL; return NULL;
...@@ -854,11 +870,12 @@ String *Item_splocal::val_str(String *sp) ...@@ -854,11 +870,12 @@ String *Item_splocal::val_str(String *sp)
str_value.set(res->ptr(), res->length(), res->charset()); str_value.set(res->ptr(), res->length(), res->charset());
else else
res->mark_as_const(); res->mark_as_const();
return &str_value; return &str_value;
} }
my_decimal *Item_splocal::val_decimal(my_decimal *decimal_value) my_decimal *Item_sp_variable::val_decimal(my_decimal *decimal_value)
{ {
DBUG_ASSERT(fixed); DBUG_ASSERT(fixed);
Item *it= this_item(); Item *it= this_item();
...@@ -868,73 +885,108 @@ my_decimal *Item_splocal::val_decimal(my_decimal *decimal_value) ...@@ -868,73 +885,108 @@ my_decimal *Item_splocal::val_decimal(my_decimal *decimal_value)
} }
bool Item_splocal::is_null() bool Item_sp_variable::is_null()
{ {
Item *it= this_item(); return this_item()->is_null();
return it->is_null(); }
/*****************************************************************************
Item_splocal methods
*****************************************************************************/
Item_splocal::Item_splocal(const LEX_STRING &sp_var_name,
uint sp_var_idx,
enum_field_types sp_var_type,
uint pos_in_q)
:Item_sp_variable(sp_var_name.str, sp_var_name.length),
m_var_idx(sp_var_idx), pos_in_query(pos_in_q)
{
maybe_null= TRUE;
m_type= sp_map_item_type(sp_var_type);
m_result_type= sp_map_result_type(sp_var_type);
} }
Item * Item *
Item_splocal::this_item() Item_splocal::this_item()
{ {
DBUG_ASSERT(owner == thd->spcont->owner); DBUG_ASSERT(m_sp == m_thd->spcont->sp);
return thd->spcont->get_item(m_offset);
return m_thd->spcont->get_item(m_var_idx);
}
const Item *
Item_splocal::this_item() const
{
DBUG_ASSERT(m_sp == m_thd->spcont->sp);
return m_thd->spcont->get_item(m_var_idx);
} }
Item ** Item **
Item_splocal::this_item_addr(THD *thd, Item **addr) Item_splocal::this_item_addr(THD *thd, Item **)
{ {
DBUG_ASSERT(owner == thd->spcont->owner); DBUG_ASSERT(m_sp == thd->spcont->sp);
return thd->spcont->get_item_addr(m_offset);
return thd->spcont->get_item_addr(m_var_idx);
} }
Item *
Item_splocal::this_const_item() const void Item_splocal::print(String *str)
{ {
DBUG_ASSERT(owner == thd->spcont->owner); str->reserve(m_name.length+8);
return thd->spcont->get_item(m_offset); str->append(m_name.str, m_name.length);
str->append('@');
str->qs_append(m_var_idx);
} }
Item::Type
Item_splocal::type() const /*****************************************************************************
Item_case_expr methods
*****************************************************************************/
Item_case_expr::Item_case_expr(int case_expr_id)
:Item_sp_variable(STRING_WITH_LEN("case_expr")),
m_case_expr_id(case_expr_id)
{ {
if (thd && thd->spcont)
{
DBUG_ASSERT(owner == thd->spcont->owner);
return thd->spcont->get_item(m_offset)->type();
}
return NULL_ITEM; // Anything but SUBSELECT_ITEM
} }
bool Item_splocal::fix_fields(THD *thd_arg, Item **ref) Item *
Item_case_expr::this_item()
{ {
Item *it; DBUG_ASSERT(m_sp == m_thd->spcont->sp);
thd= thd_arg; // Must be set before this_item()
it= this_item(); return m_thd->spcont->get_case_expr(m_case_expr_id);
DBUG_ASSERT(it->fixed);
max_length= it->max_length;
decimals= it->decimals;
unsigned_flag= it->unsigned_flag;
fixed= 1;
return FALSE;
} }
void Item_splocal::cleanup()
const Item *
Item_case_expr::this_item() const
{ {
fixed= 0; DBUG_ASSERT(m_sp == m_thd->spcont->sp);
return m_thd->spcont->get_case_expr(m_case_expr_id);
} }
void Item_splocal::print(String *str) Item **
Item_case_expr::this_item_addr(THD *thd, Item **)
{ {
str->reserve(m_name.length+8); DBUG_ASSERT(m_sp == thd->spcont->sp);
str->append(m_name.str, m_name.length);
str->append('@'); return thd->spcont->get_case_expr_addr(m_case_expr_id);
str->qs_append(m_offset); }
void Item_case_expr::print(String *str)
{
str->append(STRING_WITH_LEN("case_expr@"));
str->qs_append(m_case_expr_id);
} }
...@@ -1013,12 +1065,6 @@ bool Item_name_const::fix_fields(THD *thd, Item **ref) ...@@ -1013,12 +1065,6 @@ bool Item_name_const::fix_fields(THD *thd, Item **ref)
} }
void Item_name_const::cleanup()
{
fixed= 0;
}
void Item_name_const::print(String *str) void Item_name_const::print(String *str)
{ {
str->append(STRING_WITH_LEN("NAME_CONST(")); str->append(STRING_WITH_LEN("NAME_CONST("));
...@@ -3911,6 +3957,9 @@ int Item::save_in_field(Field *field, bool no_conversions) ...@@ -3911,6 +3957,9 @@ int Item::save_in_field(Field *field, bool no_conversions)
str_value.set_quick(0, 0, cs); str_value.set_quick(0, 0, cs);
return set_field_to_null_with_conversions(field, no_conversions); return set_field_to_null_with_conversions(field, no_conversions);
} }
/* NOTE: If null_value == FALSE, "result" must be not NULL. */
field->set_notnull(); field->set_notnull();
error=field->store(result->ptr(),result->length(),cs); error=field->store(result->ptr(),result->length(),cs);
str_value.set_quick(0, 0, cs); str_value.set_quick(0, 0, cs);
......
...@@ -341,8 +341,6 @@ public: ...@@ -341,8 +341,6 @@ public:
{ return (void*) sql_alloc((uint) size); } { return (void*) sql_alloc((uint) size); }
static void *operator new(size_t size, MEM_ROOT *mem_root) static void *operator new(size_t size, MEM_ROOT *mem_root)
{ return (void*) alloc_root(mem_root, (uint) size); } { return (void*) alloc_root(mem_root, (uint) size); }
/* Special for SP local variable assignment - reusing slots */
static void *operator new(size_t size, Item *reuse, uint *rsize);
static void operator delete(void *ptr,size_t size) { TRASH(ptr, size); } static void operator delete(void *ptr,size_t size) { TRASH(ptr, size); }
static void operator delete(void *ptr, MEM_ROOT *mem_root) {} static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
...@@ -671,13 +669,13 @@ public: ...@@ -671,13 +669,13 @@ public:
current value and pointer to current Item otherwise. current value and pointer to current Item otherwise.
*/ */
virtual Item *this_item() { return this; } virtual Item *this_item() { return this; }
virtual const Item *this_item() const { return this; }
/* /*
For SP local variable returns address of pointer to Item representing its For SP local variable returns address of pointer to Item representing its
current value and pointer passed via parameter otherwise. current value and pointer passed via parameter otherwise.
*/ */
virtual Item **this_item_addr(THD *thd, Item **addr) { return addr; } virtual Item **this_item_addr(THD *thd, Item **addr) { return addr; }
/* For SPs mostly. */
virtual Item *this_const_item() const { return const_cast<Item*>(this); }
// Row emulation // Row emulation
virtual uint cols() { return 1; } virtual uint cols() { return 1; }
...@@ -706,21 +704,32 @@ public: ...@@ -706,21 +704,32 @@ public:
class sp_head; class sp_head;
/*
A reference to local SP variable (incl. reference to SP parameter), used in
runtime.
NOTE
This item has a "value" item, defined as
this_item() = thd->spcont->get_item(m_offset)
and it delegates everything to that item (if !this_item() then this item
poses as Item_null) except for name, which is the name of SP local
variable.
*/
class Item_splocal : public Item /*****************************************************************************
The class is a base class for representation of stored routine variables in
the Item-hierarchy. There are the following kinds of SP-vars:
- local variables (Item_splocal);
- CASE expression (Item_case_expr);
*****************************************************************************/
class Item_sp_variable :public Item
{ {
uint m_offset; protected:
/*
THD, which is stored in fix_fields() and is used in this_item() to avoid
current_thd use.
*/
THD *m_thd;
public:
LEX_STRING m_name;
/*
Buffer, pointing to the string value of the item. We need it to
protect internal buffer from changes. See comment to analogous
member in Item_param for more details.
*/
String str_value_ptr;
public: public:
#ifndef DBUG_OFF #ifndef DBUG_OFF
...@@ -728,11 +737,74 @@ public: ...@@ -728,11 +737,74 @@ public:
Routine to which this Item_splocal belongs. Used for checking if correct Routine to which this Item_splocal belongs. Used for checking if correct
runtime context is used for variable handling. runtime context is used for variable handling.
*/ */
sp_head *owner; sp_head *m_sp;
#endif #endif
LEX_STRING m_name;
THD *thd;
public:
Item_sp_variable(char *sp_var_name_str, uint sp_var_name_length);
public:
bool fix_fields(THD *thd, Item **);
double val_real();
longlong val_int();
String *val_str(String *sp);
my_decimal *val_decimal(my_decimal *decimal_value);
bool is_null();
public:
inline void make_field(Send_field *field);
inline bool const_item() const;
inline int save_in_field(Field *field, bool no_conversions);
inline bool send(Protocol *protocol, String *str);
};
/*****************************************************************************
Item_sp_variable inline implementation.
*****************************************************************************/
inline void Item_sp_variable::make_field(Send_field *field)
{
Item *it= this_item();
if (name)
it->set_name(name, (uint) strlen(name), system_charset_info);
else
it->set_name(m_name.str, m_name.length, system_charset_info);
it->make_field(field);
}
inline bool Item_sp_variable::const_item() const
{
return TRUE;
}
inline int Item_sp_variable::save_in_field(Field *field, bool no_conversions)
{
return this_item()->save_in_field(field, no_conversions);
}
inline bool Item_sp_variable::send(Protocol *protocol, String *str)
{
return this_item()->send(protocol, str);
}
/*****************************************************************************
A reference to local SP variable (incl. reference to SP parameter), used in
runtime.
*****************************************************************************/
class Item_splocal :public Item_sp_variable
{
uint m_var_idx;
Type m_type;
Item_result m_result_type;
public:
/* /*
Position of this reference to SP variable in the statement (the Position of this reference to SP variable in the statement (the
statement itself is in sp_instr_stmt::m_query). statement itself is in sp_instr_stmt::m_query).
...@@ -745,78 +817,94 @@ public: ...@@ -745,78 +817,94 @@ public:
*/ */
uint pos_in_query; uint pos_in_query;
Item_splocal(LEX_STRING name, uint offset, uint pos_in_q=0) Item_splocal(const LEX_STRING &sp_var_name, uint sp_var_idx,
: m_offset(offset), m_name(name), thd(0), pos_in_query(pos_in_q) enum_field_types sp_var_type, uint pos_in_q= 0);
{
maybe_null= TRUE;
}
/* For error printing */
inline LEX_STRING *my_name(LEX_STRING *get_name)
{
if (!get_name)
return &m_name;
(*get_name)= m_name;
return get_name;
}
bool is_splocal() { return 1; } /* Needed for error checking */ bool is_splocal() { return 1; } /* Needed for error checking */
Item *this_item(); Item *this_item();
const Item *this_item() const;
Item **this_item_addr(THD *thd, Item **); Item **this_item_addr(THD *thd, Item **);
Item *this_const_item() const;
bool fix_fields(THD *, Item **); void print(String *str);
void cleanup();
inline uint get_offset() public:
{ inline const LEX_STRING *my_name() const;
return m_offset;
}
// Abstract methods inherited from Item. Just defer the call to inline uint get_var_idx() const;
// the item in the frame
enum Type type() const;
double val_real(); inline enum Type type() const;
longlong val_int(); inline Item_result result_type() const;
String *val_str(String *sp); };
my_decimal *val_decimal(my_decimal *);
bool is_null();
void print(String *str);
void make_field(Send_field *field) /*****************************************************************************
{ Item_splocal inline implementation.
Item *it= this_item(); *****************************************************************************/
if (name) inline const LEX_STRING *Item_splocal::my_name() const
it->set_name(name, (uint) strlen(name), system_charset_info); {
else return &m_name;
it->set_name(m_name.str, m_name.length, system_charset_info); }
it->make_field(field);
}
Item_result result_type() const inline uint Item_splocal::get_var_idx() const
{ {
return this_const_item()->result_type(); return m_var_idx;
} }
bool const_item() const inline enum Item::Type Item_splocal::type() const
{ {
return TRUE; return m_type;
} }
int save_in_field(Field *field, bool no_conversions) inline Item_result Item_splocal::result_type() const
{ {
return this_item()->save_in_field(field, no_conversions); return m_result_type;
} }
bool send(Protocol *protocol, String *str)
{ /*****************************************************************************
return this_item()->send(protocol, str); A reference to case expression in SP, used in runtime.
} *****************************************************************************/
class Item_case_expr :public Item_sp_variable
{
public:
Item_case_expr(int case_expr_id);
public:
Item *this_item();
const Item *this_item() const;
Item **this_item_addr(THD *thd, Item **);
inline enum Type type() const;
inline Item_result result_type() const;
public:
/*
NOTE: print() is intended to be used from views and for debug.
Item_case_expr can not occur in views, so here it is only for debug
purposes.
*/
void print(String *str);
private:
int m_case_expr_id;
}; };
/*****************************************************************************
Item_case_expr inline implementation.
*****************************************************************************/
inline enum Item::Type Item_case_expr::type() const
{
return this_item()->type();
}
inline Item_result Item_case_expr::result_type() const
{
return this_item()->result_type();
}
/* /*
NAME_CONST(given_name, const_value). NAME_CONST(given_name, const_value).
...@@ -843,7 +931,6 @@ public: ...@@ -843,7 +931,6 @@ public:
} }
bool fix_fields(THD *, Item **); bool fix_fields(THD *, Item **);
void cleanup();
enum Type type() const; enum Type type() const;
double val_real(); double val_real();
......
...@@ -4716,7 +4716,7 @@ Item_func_sp::sp_result_field(void) const ...@@ -4716,7 +4716,7 @@ Item_func_sp::sp_result_field(void) const
share->table_cache_key = empty_name; share->table_cache_key = empty_name;
share->table_name = empty_name; share->table_name = empty_name;
} }
field= m_sp->make_field(max_length, name, dummy_table); field= m_sp->create_result_field(max_length, name, dummy_table);
DBUG_RETURN(field); DBUG_RETURN(field);
} }
...@@ -4729,17 +4729,17 @@ Item_func_sp::sp_result_field(void) const ...@@ -4729,17 +4729,17 @@ Item_func_sp::sp_result_field(void) const
1 value = NULL or error 1 value = NULL or error
*/ */
int bool
Item_func_sp::execute(Field **flp) Item_func_sp::execute(Field **flp)
{ {
Item *it; THD *thd= current_thd;
Field *f; Field *f;
if (execute(&it))
{ /*
null_value= 1; Get field in virtual tmp table to store result. Create the field if
context->process_error(current_thd); invoked first time.
return 1; */
}
if (!(f= *flp)) if (!(f= *flp))
{ {
*flp= f= sp_result_field(); *flp= f= sp_result_field();
...@@ -4748,20 +4748,33 @@ Item_func_sp::execute(Field **flp) ...@@ -4748,20 +4748,33 @@ Item_func_sp::execute(Field **flp)
f->null_ptr= (uchar *)&null_value; f->null_ptr= (uchar *)&null_value;
f->null_bit= 1; f->null_bit= 1;
} }
it->save_in_field(f, 1);
return null_value= f->is_null(); /* Execute function and store the return value in the field. */
if (execute_impl(thd, f))
{
null_value= 1;
context->process_error(thd);
return TRUE;
}
/* Check that the field (the value) is not NULL. */
null_value= f->is_null();
return null_value;
} }
int bool
Item_func_sp::execute(Item **itp) Item_func_sp::execute_impl(THD *thd, Field *return_value_fld)
{ {
DBUG_ENTER("Item_func_sp::execute"); bool err_status= TRUE;
THD *thd= current_thd;
int res= -1;
Sub_statement_state statement_state; Sub_statement_state statement_state;
Security_context *save_security_ctx= thd->security_ctx, *save_ctx_func; Security_context *save_security_ctx= thd->security_ctx, *save_ctx_func;
DBUG_ENTER("Item_func_sp::execute_impl");
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
if (context->security_ctx) if (context->security_ctx)
{ {
...@@ -4778,7 +4791,7 @@ Item_func_sp::execute(Item **itp) ...@@ -4778,7 +4791,7 @@ Item_func_sp::execute(Item **itp)
function call into binlog. function call into binlog.
*/ */
thd->reset_sub_statement_state(&statement_state, SUB_STMT_FUNCTION); thd->reset_sub_statement_state(&statement_state, SUB_STMT_FUNCTION);
res= m_sp->execute_function(thd, args, arg_count, itp); err_status= m_sp->execute_function(thd, args, arg_count, return_value_fld);
thd->restore_sub_statement_state(&statement_state); thd->restore_sub_statement_state(&statement_state);
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
...@@ -4788,7 +4801,7 @@ error: ...@@ -4788,7 +4801,7 @@ error:
#else #else
error: error:
#endif #endif
DBUG_RETURN(res); DBUG_RETURN(err_status);
} }
...@@ -4884,7 +4897,7 @@ Item_func_sp::tmp_table_field(TABLE *t_arg) ...@@ -4884,7 +4897,7 @@ Item_func_sp::tmp_table_field(TABLE *t_arg)
DBUG_ENTER("Item_func_sp::tmp_table_field"); DBUG_ENTER("Item_func_sp::tmp_table_field");
if (m_sp) if (m_sp)
res= m_sp->make_field(max_length, (const char *)name, t_arg); res= m_sp->create_result_field(max_length, (const char*) name, t_arg);
if (!res) if (!res)
res= Item_func::tmp_table_field(t_arg); res= Item_func::tmp_table_field(t_arg);
......
...@@ -1374,8 +1374,8 @@ private: ...@@ -1374,8 +1374,8 @@ private:
Field *result_field; Field *result_field;
char result_buf[64]; char result_buf[64];
int execute(Item **itp); bool execute(Field **flp);
int execute(Field **flp); bool execute_impl(THD *thd, Field *return_value_fld);
Field *sp_result_field(void) const; Field *sp_result_field(void) const;
public: public:
......
...@@ -660,6 +660,7 @@ bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list, ...@@ -660,6 +660,7 @@ bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list,
bool mysql_preload_keys(THD* thd, TABLE_LIST* table_list); bool mysql_preload_keys(THD* thd, TABLE_LIST* table_list);
int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache, int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache,
KEY_CACHE *dst_cache); KEY_CACHE *dst_cache);
TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list);
bool mysql_xa_recover(THD *thd); bool mysql_xa_recover(THD *thd);
...@@ -1101,8 +1102,8 @@ void unhex_type2(TYPELIB *lib); ...@@ -1101,8 +1102,8 @@ void unhex_type2(TYPELIB *lib);
uint check_word(TYPELIB *lib, const char *val, const char *end, uint check_word(TYPELIB *lib, const char *val, const char *end,
const char **end_of_word); const char **end_of_word);
bool is_keyword(const char *name, uint len);
bool is_keyword(const char *name, uint len);
#define MY_DB_OPT_FILE "db.opt" #define MY_DB_OPT_FILE "db.opt"
bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create); bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
......
...@@ -467,7 +467,7 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) ...@@ -467,7 +467,7 @@ sp_returns_type(THD *thd, String &result, sp_head *sp)
bzero(&table, sizeof(table)); bzero(&table, sizeof(table));
table.in_use= thd; table.in_use= thd;
table.s = &table.share_not_to_be_used; table.s = &table.share_not_to_be_used;
field= sp->make_field(0, 0, &table); field= sp->create_result_field(0, 0, &table);
field->sql_type(result); field->sql_type(result);
delete field; delete field;
} }
......
This diff is collapsed.
...@@ -33,6 +33,9 @@ ...@@ -33,6 +33,9 @@
Item_result Item_result
sp_map_result_type(enum enum_field_types type); sp_map_result_type(enum enum_field_types type);
Item::Type
sp_map_item_type(enum enum_field_types type);
uint uint
sp_get_flags_for_command(LEX *lex); sp_get_flags_for_command(LEX *lex);
...@@ -123,12 +126,9 @@ public: ...@@ -123,12 +126,9 @@ public:
/* TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */ /* TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */
int m_type; int m_type;
uint m_flags; // Boolean attributes of a stored routine uint m_flags; // Boolean attributes of a stored routine
enum enum_field_types m_returns; // For FUNCTIONs only
Field::geometry_type m_geom_returns; create_field m_return_field_def; /* This is used for FUNCTIONs only. */
CHARSET_INFO *m_returns_cs; // For FUNCTIONs only
TYPELIB *m_returns_typelib; // For FUNCTIONs only
uint m_returns_len; // For FUNCTIONs only
uint m_returns_pack; // For FUNCTIONs only
uchar *m_tmp_query; // Temporary pointer to sub query string uchar *m_tmp_query; // Temporary pointer to sub query string
uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value
st_sp_chistics *m_chistics; st_sp_chistics *m_chistics;
...@@ -202,9 +202,6 @@ public: ...@@ -202,9 +202,6 @@ public:
void void
init_strings(THD *thd, LEX *lex, sp_name *name); init_strings(THD *thd, LEX *lex, sp_name *name);
TYPELIB *
create_typelib(List<String> *src);
int int
create(THD *thd); create(THD *thd);
...@@ -214,10 +211,10 @@ public: ...@@ -214,10 +211,10 @@ public:
void void
destroy(); destroy();
int bool
execute_function(THD *thd, Item **args, uint argcount, Item **resp); execute_function(THD *thd, Item **args, uint argcount, Field *return_fld);
int bool
execute_procedure(THD *thd, List<Item> *args); execute_procedure(THD *thd, List<Item> *args);
int int
...@@ -278,7 +275,12 @@ public: ...@@ -278,7 +275,12 @@ public:
char *create_string(THD *thd, ulong *lenp); char *create_string(THD *thd, ulong *lenp);
Field *make_field(uint max_length, const char *name, TABLE *dummy); Field *create_result_field(uint field_max_length, const char *field_name,
TABLE *table);
bool fill_field_definition(THD *thd, LEX *lex,
enum enum_field_types field_type,
create_field *field_def);
void set_info(longlong created, longlong modified, void set_info(longlong created, longlong modified,
st_sp_chistics *chistics, ulong sql_mode); st_sp_chistics *chistics, ulong sql_mode);
...@@ -363,7 +365,7 @@ private: ...@@ -363,7 +365,7 @@ private:
*/ */
HASH m_sptabs; HASH m_sptabs;
int bool
execute(THD *thd); execute(THD *thd);
/* /*
...@@ -1074,6 +1076,31 @@ private: ...@@ -1074,6 +1076,31 @@ private:
}; // class sp_instr_error : public sp_instr }; // class sp_instr_error : public sp_instr
class sp_instr_set_case_expr :public sp_instr
{
public:
sp_instr_set_case_expr(uint ip, sp_pcontext *ctx, uint case_expr_id,
Item *case_expr, LEX *lex)
:sp_instr(ip, ctx), m_case_expr_id(case_expr_id), m_case_expr(case_expr),
m_lex_keeper(lex, TRUE)
{}
virtual int execute(THD *thd, uint *nextp);
virtual int exec_core(THD *thd, uint *nextp);
virtual void print(String *str);
private:
uint m_case_expr_id;
Item *m_case_expr;
sp_lex_keeper m_lex_keeper;
}; // class sp_instr_set_case_expr : public sp_instr
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
bool bool
sp_change_security_context(THD *thd, sp_head *sp, sp_change_security_context(THD *thd, sp_head *sp,
...@@ -1086,8 +1113,10 @@ TABLE_LIST * ...@@ -1086,8 +1113,10 @@ TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex, sp_add_to_query_tables(THD *thd, LEX *lex,
const char *db, const char *name, const char *db, const char *name,
thr_lock_type locktype); thr_lock_type locktype);
Item *
sp_prepare_func_item(THD* thd, Item **it_addr);
Item *sp_eval_func_item(THD *thd, Item **it, enum_field_types type, bool
Item *reuse, bool use_callers_arena); sp_eval_expr(THD *thd, Field *result_field, Item *expr_item);
#endif /* _SP_HEAD_H_ */ #endif /* _SP_HEAD_H_ */
...@@ -51,21 +51,26 @@ sp_cond_check(LEX_STRING *sqlstate) ...@@ -51,21 +51,26 @@ sp_cond_check(LEX_STRING *sqlstate)
} }
sp_pcontext::sp_pcontext(sp_pcontext *prev) sp_pcontext::sp_pcontext(sp_pcontext *prev)
: Sql_alloc(), m_psubsize(0), m_csubsize(0), m_hsubsize(0), :Sql_alloc(), m_total_pvars(0), m_csubsize(0), m_hsubsize(0),
m_handlers(0), m_parent(prev), m_pboundary(0) m_handlers(0), m_parent(prev), m_pboundary(0)
{ {
VOID(my_init_dynamic_array(&m_pvar, sizeof(sp_pvar_t *), 16, 8)); VOID(my_init_dynamic_array(&m_pvar, sizeof(sp_pvar_t *), 16, 8));
VOID(my_init_dynamic_array(&m_case_expr_id_lst, sizeof(int), 16, 8));
VOID(my_init_dynamic_array(&m_cond, sizeof(sp_cond_type_t *), 16, 8)); VOID(my_init_dynamic_array(&m_cond, sizeof(sp_cond_type_t *), 16, 8));
VOID(my_init_dynamic_array(&m_cursor, sizeof(LEX_STRING), 16, 8)); VOID(my_init_dynamic_array(&m_cursor, sizeof(LEX_STRING), 16, 8));
VOID(my_init_dynamic_array(&m_handler, sizeof(sp_cond_type_t *), 16, 8)); VOID(my_init_dynamic_array(&m_handler, sizeof(sp_cond_type_t *), 16, 8));
m_label.empty(); m_label.empty();
m_children.empty(); m_children.empty();
if (!prev) if (!prev)
{
m_poffset= m_coffset= 0; m_poffset= m_coffset= 0;
m_num_case_exprs= 0;
}
else else
{ {
m_poffset= prev->current_pvars(); m_poffset= prev->m_poffset + prev->m_total_pvars;
m_coffset= prev->current_cursors(); m_coffset= prev->current_cursors();
m_num_case_exprs= prev->get_num_case_exprs();
} }
} }
...@@ -81,6 +86,7 @@ sp_pcontext::destroy() ...@@ -81,6 +86,7 @@ sp_pcontext::destroy()
m_children.empty(); m_children.empty();
m_label.empty(); m_label.empty();
delete_dynamic(&m_pvar); delete_dynamic(&m_pvar);
delete_dynamic(&m_case_expr_id_lst);
delete_dynamic(&m_cond); delete_dynamic(&m_cond);
delete_dynamic(&m_cursor); delete_dynamic(&m_cursor);
delete_dynamic(&m_handler); delete_dynamic(&m_handler);
...@@ -99,16 +105,19 @@ sp_pcontext::push_context() ...@@ -99,16 +105,19 @@ sp_pcontext::push_context()
sp_pcontext * sp_pcontext *
sp_pcontext::pop_context() sp_pcontext::pop_context()
{ {
uint submax= max_pvars(); m_parent->m_total_pvars= m_parent->m_total_pvars + m_total_pvars;
if (submax > m_parent->m_psubsize) uint submax= max_handlers();
m_parent->m_psubsize= submax;
submax= max_handlers();
if (submax > m_parent->m_hsubsize) if (submax > m_parent->m_hsubsize)
m_parent->m_hsubsize= submax; m_parent->m_hsubsize= submax;
submax= max_cursors(); submax= max_cursors();
if (submax > m_parent->m_csubsize) if (submax > m_parent->m_csubsize)
m_parent->m_csubsize= submax; m_parent->m_csubsize= submax;
if (m_num_case_exprs > m_parent->m_num_case_exprs)
m_parent->m_num_case_exprs= m_num_case_exprs;
return m_parent; return m_parent;
} }
...@@ -191,26 +200,29 @@ sp_pcontext::find_pvar(uint offset) ...@@ -191,26 +200,29 @@ sp_pcontext::find_pvar(uint offset)
return NULL; // index out of bounds return NULL; // index out of bounds
} }
void sp_pvar_t *
sp_pcontext::push_pvar(LEX_STRING *name, enum enum_field_types type, sp_pcontext::push_pvar(LEX_STRING *name, enum enum_field_types type,
sp_param_mode_t mode) sp_param_mode_t mode)
{ {
sp_pvar_t *p= (sp_pvar_t *)sql_alloc(sizeof(sp_pvar_t)); sp_pvar_t *p= (sp_pvar_t *)sql_alloc(sizeof(sp_pvar_t));
if (p) if (!p)
{ return NULL;
if (m_pvar.elements == m_psubsize)
m_psubsize+= 1; ++m_total_pvars;
p->name.str= name->str;
p->name.length= name->length; p->name.str= name->str;
p->type= type; p->name.length= name->length;
p->mode= mode; p->type= type;
p->offset= current_pvars(); p->mode= mode;
p->dflt= NULL; p->offset= current_pvars();
insert_dynamic(&m_pvar, (gptr)&p); p->dflt= NULL;
} insert_dynamic(&m_pvar, (gptr)&p);
return p;
} }
sp_label_t * sp_label_t *
sp_pcontext::push_label(char *name, uint ip) sp_pcontext::push_label(char *name, uint ip)
{ {
...@@ -354,6 +366,29 @@ sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped) ...@@ -354,6 +366,29 @@ sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped)
return FALSE; return FALSE;
} }
void
sp_pcontext::retrieve_field_definitions(List<create_field> *field_def_lst)
{
/* Put local/context fields in the result list. */
for (uint i = 0; i < m_pvar.elements; ++i)
{
sp_pvar_t *var_def;
get_dynamic(&m_pvar, (gptr) &var_def, i);
field_def_lst->push_back(&var_def->field_def);
}
/* Put the fields of the enclosed contexts in the result list. */
List_iterator_fast<sp_pcontext> li(m_children);
sp_pcontext *ctx;
while ((ctx = li++))
ctx->retrieve_field_definitions(field_def_lst);
}
/* /*
Find a cursor by offset from the top. Find a cursor by offset from the top.
This is only used for debugging. This is only used for debugging.
......
...@@ -34,8 +34,16 @@ typedef struct sp_pvar ...@@ -34,8 +34,16 @@ typedef struct sp_pvar
LEX_STRING name; LEX_STRING name;
enum enum_field_types type; enum enum_field_types type;
sp_param_mode_t mode; sp_param_mode_t mode;
uint offset; // Offset in current frame
/*
offset -- basically, this is an index of variable in the scope of root
parsing context. This means, that all variables in a stored routine
have distinct indexes/offsets.
*/
uint offset;
Item *dflt; Item *dflt;
create_field field_def;
} sp_pvar_t; } sp_pvar_t;
...@@ -114,9 +122,9 @@ class sp_pcontext : public Sql_alloc ...@@ -114,9 +122,9 @@ class sp_pcontext : public Sql_alloc
// //
inline uint inline uint
max_pvars() total_pvars()
{ {
return m_psubsize + m_pvar.elements; return m_total_pvars;
} }
inline uint inline uint
...@@ -155,16 +163,15 @@ class sp_pcontext : public Sql_alloc ...@@ -155,16 +163,15 @@ class sp_pcontext : public Sql_alloc
p->dflt= it; p->dflt= it;
} }
void sp_pvar_t *
push_pvar(LEX_STRING *name, enum enum_field_types type, sp_param_mode_t mode); push_pvar(LEX_STRING *name, enum enum_field_types type, sp_param_mode_t mode);
// Pop the last 'num' slots of the frame /*
inline void Retrieve definitions of fields from the current context and its
pop_pvar(uint num = 1) children.
{ */
while (num--) void
pop_dynamic(&m_pvar); retrieve_field_definitions(List<create_field> *field_def_lst);
}
// Find by name // Find by name
sp_pvar_t * sp_pvar_t *
...@@ -175,7 +182,7 @@ class sp_pcontext : public Sql_alloc ...@@ -175,7 +182,7 @@ class sp_pcontext : public Sql_alloc
find_pvar(uint offset); find_pvar(uint offset);
/* /*
Set the current scope boundary (for default values) Set the current scope boundary (for default values).
The argument is the number of variables to skip. The argument is the number of variables to skip.
*/ */
inline void inline void
...@@ -184,6 +191,45 @@ class sp_pcontext : public Sql_alloc ...@@ -184,6 +191,45 @@ class sp_pcontext : public Sql_alloc
m_pboundary= n; m_pboundary= n;
} }
/*
CASE expressions support.
*/
inline int
register_case_expr()
{
return m_num_case_exprs++;
}
inline int
get_num_case_exprs() const
{
return m_num_case_exprs;
}
inline bool
push_case_expr_id(int case_expr_id)
{
return insert_dynamic(&m_case_expr_id_lst, (gptr) &case_expr_id);
}
inline void
pop_case_expr_id()
{
pop_dynamic(&m_case_expr_id_lst);
}
inline int
get_current_case_expr_id() const
{
int case_expr_id;
get_dynamic((DYNAMIC_ARRAY*)&m_case_expr_id_lst, (gptr) &case_expr_id,
m_case_expr_id_lst.elements - 1);
return case_expr_id;
}
// //
// Labels // Labels
// //
...@@ -280,8 +326,18 @@ class sp_pcontext : public Sql_alloc ...@@ -280,8 +326,18 @@ class sp_pcontext : public Sql_alloc
protected: protected:
/*
m_total_pvars -- number of variables (including all types of arguments)
in this context including all children contexts.
m_total_pvars >= m_pvar.elements.
m_total_pvars of the root parsing context contains number of all
variables (including arguments) in all enclosed contexts.
*/
uint m_total_pvars;
// The maximum sub context's framesizes // The maximum sub context's framesizes
uint m_psubsize;
uint m_csubsize; uint m_csubsize;
uint m_hsubsize; uint m_hsubsize;
uint m_handlers; // No. of handlers in this context uint m_handlers; // No. of handlers in this context
...@@ -290,8 +346,19 @@ private: ...@@ -290,8 +346,19 @@ private:
sp_pcontext *m_parent; // Parent context sp_pcontext *m_parent; // Parent context
uint m_poffset; // Variable offset for this context /*
m_poffset -- basically, this is an index of the first variable in this
parsing context.
m_poffset is 0 for root context.
Since now each variable is stored in separate place, no reuse is done,
so m_poffset is different for all enclosed contexts.
*/
uint m_poffset;
uint m_coffset; // Cursor offset for this context uint m_coffset; // Cursor offset for this context
/* /*
Boundary for finding variables in this context. This is the number Boundary for finding variables in this context. This is the number
of variables currently "invisible" to default clauses. of variables currently "invisible" to default clauses.
...@@ -300,7 +367,10 @@ private: ...@@ -300,7 +367,10 @@ private:
*/ */
uint m_pboundary; uint m_pboundary;
int m_num_case_exprs;
DYNAMIC_ARRAY m_pvar; // Parameters/variables DYNAMIC_ARRAY m_pvar; // Parameters/variables
DYNAMIC_ARRAY m_case_expr_id_lst; /* Stack of CASE expression ids. */
DYNAMIC_ARRAY m_cond; // Conditions DYNAMIC_ARRAY m_cond; // Conditions
DYNAMIC_ARRAY m_cursor; // Cursors DYNAMIC_ARRAY m_cursor; // Cursors
DYNAMIC_ARRAY m_handler; // Handlers, for checking of duplicates DYNAMIC_ARRAY m_handler; // Handlers, for checking of duplicates
......
...@@ -29,41 +29,137 @@ ...@@ -29,41 +29,137 @@
#include "sp_rcontext.h" #include "sp_rcontext.h"
#include "sp_pcontext.h" #include "sp_pcontext.h"
sp_rcontext::sp_rcontext(sp_rcontext *prev, uint fsize, uint hmax, uint cmax)
: m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0), sp_rcontext::sp_rcontext(sp_pcontext *root_parsing_ctx,
m_ihsp(0), m_hfound(-1), m_ccount(0), m_prev_ctx(prev) Field *return_value_fld,
sp_rcontext *prev_runtime_ctx)
:m_root_parsing_ctx(root_parsing_ctx),
m_var_table(0),
m_var_items(0),
m_return_value_fld(return_value_fld),
m_return_value_set(FALSE),
m_hcount(0),
m_hsp(0),
m_ihsp(0),
m_hfound(-1),
m_ccount(0),
m_case_expr_holders(0),
m_prev_runtime_ctx(prev_runtime_ctx)
{ {
m_frame= (Item **)sql_alloc(fsize * sizeof(Item*));
m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t));
m_hstack= (uint *)sql_alloc(hmax * sizeof(uint));
m_in_handler= (uint *)sql_alloc(hmax * sizeof(uint));
m_cstack= (sp_cursor **)sql_alloc(cmax * sizeof(sp_cursor *));
m_saved.empty();
} }
int sp_rcontext::~sp_rcontext()
sp_rcontext::set_item_eval(THD *thd, uint idx, Item **item_addr, {
enum_field_types type) if (m_var_table)
free_blobs(m_var_table);
}
/*
Initialize sp_rcontext instance.
SYNOPSIS
thd Thread handle
RETURN
FALSE on success
TRUE on error
*/
bool sp_rcontext::init(THD *thd)
{
if (init_var_table(thd) || init_var_items())
return TRUE;
return
!(m_handler=
(sp_handler_t*)thd->alloc(m_root_parsing_ctx->max_handlers() *
sizeof(sp_handler_t))) ||
!(m_hstack=
(uint*)thd->alloc(m_root_parsing_ctx->max_handlers() *
sizeof(uint))) ||
!(m_in_handler=
(uint*)thd->alloc(m_root_parsing_ctx->max_handlers() *
sizeof(uint))) ||
!(m_cstack=
(sp_cursor**)thd->alloc(m_root_parsing_ctx->max_cursors() *
sizeof(sp_cursor*))) ||
!(m_case_expr_holders=
(Item_cache**)thd->calloc(m_root_parsing_ctx->get_num_case_exprs() *
sizeof (Item_cache*)));
}
/*
Create and initialize a table to store SP-vars.
SYNOPSIS
thd Thread handler.
RETURN
FALSE on success
TRUE on error
*/
bool
sp_rcontext::init_var_table(THD *thd)
{ {
Item *it; List<create_field> field_def_lst;
Item *reuse_it;
/* sp_eval_func_item will use callers_arena */ if (!m_root_parsing_ctx->total_pvars())
int res; return FALSE;
reuse_it= get_item(idx); m_root_parsing_ctx->retrieve_field_definitions(&field_def_lst);
it= sp_eval_func_item(thd, item_addr, type, reuse_it, TRUE);
if (! it) DBUG_ASSERT(field_def_lst.elements == m_root_parsing_ctx->total_pvars());
res= -1;
else if (!(m_var_table= create_virtual_tmp_table(thd, field_def_lst)))
return TRUE;
m_var_table->copy_blobs= TRUE;
m_var_table->alias= "";
return FALSE;
}
/*
Create and initialize an Item-adapter (Item_field) for each SP-var field.
RETURN
FALSE on success
TRUE on error
*/
bool
sp_rcontext::init_var_items()
{
uint idx;
uint num_vars= m_root_parsing_ctx->total_pvars();
if (!(m_var_items= (Item**) sql_alloc(num_vars * sizeof (Item *))))
return TRUE;
for (idx = 0; idx < num_vars; ++idx)
{ {
res= 0; if (!(m_var_items[idx]= new Item_field(m_var_table->field[idx])))
set_item(idx, it); return TRUE;
} }
return res; return FALSE;
} }
bool
sp_rcontext::set_return_value(THD *thd, Item *return_value_item)
{
DBUG_ASSERT(m_return_value_fld);
m_return_value_set = TRUE;
return sp_eval_expr(thd, m_return_value_fld, return_value_item);
}
bool bool
sp_rcontext::find_handler(uint sql_errno, sp_rcontext::find_handler(uint sql_errno,
MYSQL_ERROR::enum_warning_level level) MYSQL_ERROR::enum_warning_level level)
...@@ -117,32 +213,14 @@ sp_rcontext::find_handler(uint sql_errno, ...@@ -117,32 +213,14 @@ sp_rcontext::find_handler(uint sql_errno,
} }
if (found < 0) if (found < 0)
{ {
if (m_prev_ctx) if (m_prev_runtime_ctx)
return m_prev_ctx->find_handler(sql_errno, level); return m_prev_runtime_ctx->find_handler(sql_errno, level);
return FALSE; return FALSE;
} }
m_hfound= found; m_hfound= found;
return TRUE; return TRUE;
} }
void
sp_rcontext::save_variables(uint fp)
{
while (fp < m_count)
{
m_saved.push_front(m_frame[fp]);
m_frame[fp++]= NULL; // Prevent reuse
}
}
void
sp_rcontext::restore_variables(uint fp)
{
uint i= m_count;
while (i-- > fp)
m_frame[i]= m_saved.pop();
}
void void
sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i) sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
...@@ -150,6 +228,7 @@ sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i) ...@@ -150,6 +228,7 @@ sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
m_cstack[m_ccount++]= new sp_cursor(lex_keeper, i); m_cstack[m_ccount++]= new sp_cursor(lex_keeper, i);
} }
void void
sp_rcontext::pop_cursors(uint count) sp_rcontext::pop_cursors(uint count)
{ {
...@@ -160,6 +239,40 @@ sp_rcontext::pop_cursors(uint count) ...@@ -160,6 +239,40 @@ sp_rcontext::pop_cursors(uint count)
} }
int
sp_rcontext::set_variable(THD *thd, uint var_idx, Item *value)
{
return set_variable(thd, m_var_table->field[var_idx], value);
}
int
sp_rcontext::set_variable(THD *thd, Field *field, Item *value)
{
if (!value)
{
field->set_null();
return 0;
}
return sp_eval_expr(thd, field, value);
}
Item *
sp_rcontext::get_item(uint var_idx)
{
return m_var_items[var_idx];
}
Item **
sp_rcontext::get_item_addr(uint var_idx)
{
return m_var_items + var_idx;
}
/* /*
* *
* sp_cursor * sp_cursor
...@@ -263,6 +376,102 @@ sp_cursor::fetch(THD *thd, List<struct sp_pvar> *vars) ...@@ -263,6 +376,102 @@ sp_cursor::fetch(THD *thd, List<struct sp_pvar> *vars)
} }
/*
Create an instance of appropriate Item_cache class depending on the
specified type in the callers arena.
SYNOPSIS
thd thread handler
result_type type of the expression
RETURN
Pointer to valid object on success
NULL on error
NOTE
We should create cache items in the callers arena, as they are used
between in several instructions.
*/
Item_cache *
sp_rcontext::create_case_expr_holder(THD *thd, Item_result result_type)
{
Item_cache *holder;
Query_arena current_arena;
thd->set_n_backup_active_arena(thd->spcont->callers_arena, &current_arena);
holder= Item_cache::get_cache(result_type);
thd->restore_active_arena(thd->spcont->callers_arena, &current_arena);
return holder;
}
/*
Set CASE expression to the specified value.
SYNOPSIS
thd thread handler
case_expr_id identifier of the CASE expression
case_expr_item a value of the CASE expression
RETURN
FALSE on success
TRUE on error
NOTE
The idea is to reuse Item_cache for the expression of the one CASE
statement. This optimization takes place when there is CASE statement
inside of a loop. So, in other words, we will use the same object on each
iteration instead of creating a new one for each iteration.
TODO
Hypothetically, a type of CASE expression can be different for each
iteration. For instance, this can happen if the expression contains a
session variable (something like @@VAR) and its type is changed from one
iteration to another.
In order to cope with this problem, we check type each time, when we use
already created object. If the type does not match, we re-create Item.
This also can (should?) be optimized.
*/
int
sp_rcontext::set_case_expr(THD *thd, int case_expr_id, Item *case_expr_item)
{
if (!(case_expr_item= sp_prepare_func_item(thd, &case_expr_item)))
return TRUE;
if (!m_case_expr_holders[case_expr_id] ||
m_case_expr_holders[case_expr_id]->result_type() !=
case_expr_item->result_type())
{
m_case_expr_holders[case_expr_id]=
create_case_expr_holder(thd, case_expr_item->result_type());
}
m_case_expr_holders[case_expr_id]->store(case_expr_item);
return FALSE;
}
Item *
sp_rcontext::get_case_expr(int case_expr_id)
{
return m_case_expr_holders[case_expr_id];
}
Item **
sp_rcontext::get_case_expr_addr(int case_expr_id)
{
return (Item**) m_case_expr_holders + case_expr_id;
}
/*************************************************************************** /***************************************************************************
Select_fetch_into_spvars Select_fetch_into_spvars
****************************************************************************/ ****************************************************************************/
...@@ -294,11 +503,8 @@ bool Select_fetch_into_spvars::send_data(List<Item> &items) ...@@ -294,11 +503,8 @@ bool Select_fetch_into_spvars::send_data(List<Item> &items)
*/ */
for (; pv= pv_iter++, item= item_iter++; ) for (; pv= pv_iter++, item= item_iter++; )
{ {
Item *reuse= thd->spcont->get_item(pv->offset); if (thd->spcont->set_variable(thd, pv->offset, item))
/* Evaluate a new item on the arena of the calling instruction */ return TRUE;
Item *it= sp_eval_func_item(thd, &item, pv->type, reuse, TRUE);
thd->spcont->set_item(pv->offset, it);
} }
return FALSE; return FALSE;
} }
...@@ -43,12 +43,22 @@ typedef struct ...@@ -43,12 +43,22 @@ typedef struct
/* /*
This is a run context? of one SP ? This class is a runtime context of a Stored Routine. It is used in an
THis is execution and is intended to contain all dynamic objects (i.e. objects, which
- a stack of cursors? can be changed during execution), such as:
- a stack of handlers? - stored routine variables;
- a stack of Items ? - cursors;
- a stack of instruction locations in SP? - handlers;
Runtime context is used with sp_head class. sp_head class is intended to
contain all static things, related to the stored routines (code, for example).
sp_head instance creates runtime context for the execution of a stored
routine.
There is a parsing context (an instance of sp_pcontext class), which is used
on parsing stage. However, now it contains some necessary for an execution
things, such as definition of used stored routine variables. That's why
runtime context needs a reference to the parsing context.
*/ */
class sp_rcontext : public Sql_alloc class sp_rcontext : public Sql_alloc
...@@ -68,62 +78,34 @@ class sp_rcontext : public Sql_alloc ...@@ -68,62 +78,34 @@ class sp_rcontext : public Sql_alloc
#ifndef DBUG_OFF #ifndef DBUG_OFF
/* /*
Routine to which this Item_splocal belongs. Used for checking if correct The routine for which this runtime context is created. Used for checking
runtime context is used for variable handling. if correct runtime context is used for variable handling.
*/ */
sp_head *owner; sp_head *sp;
#endif #endif
sp_rcontext(sp_rcontext *prev, uint fsize, uint hmax, uint cmax); sp_rcontext(sp_pcontext *root_parsing_ctx, Field *return_value_fld,
sp_rcontext *prev_runtime_ctx);
bool init(THD *thd);
~sp_rcontext() ~sp_rcontext();
{
// Not needed?
//sql_element_free(m_frame);
//m_saved.empty();
}
inline void
push_item(Item *i)
{
if (m_count < m_fsize)
m_frame[m_count++]= i;
}
inline void
set_item(uint idx, Item *i)
{
if (idx < m_count)
m_frame[idx]= i;
}
/* Returns 0 on success, -1 on (eval) failure */
int int
set_item_eval(THD *thd, uint idx, Item **i, enum_field_types type); set_variable(THD *thd, uint var_idx, Item *value);
inline Item *
get_item(uint idx)
{
return m_frame[idx];
}
inline Item ** Item *
get_item_addr(uint idx) get_item(uint var_idx);
{
return m_frame + idx;
}
Item **
get_item_addr(uint var_idx);
inline void bool
set_result(Item *it) set_return_value(THD *thd, Item *return_value_item);
{
m_result= it;
}
inline Item * inline bool
get_result() is_return_value_set() const
{ {
return m_result; return m_return_value_set;
} }
inline void inline void
...@@ -195,14 +177,6 @@ class sp_rcontext : public Sql_alloc ...@@ -195,14 +177,6 @@ class sp_rcontext : public Sql_alloc
m_ihsp-= 1; m_ihsp-= 1;
} }
// Save variables starting at fp and up
void
save_variables(uint fp);
// Restore variables down to fp
void
restore_variables(uint fp);
void void
push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i); push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
...@@ -221,13 +195,42 @@ class sp_rcontext : public Sql_alloc ...@@ -221,13 +195,42 @@ class sp_rcontext : public Sql_alloc
return m_cstack[i]; return m_cstack[i];
} }
/*
CASE expressions support.
*/
int
set_case_expr(THD *thd, int case_expr_id, Item *case_expr_item);
Item *
get_case_expr(int case_expr_id);
Item **
get_case_expr_addr(int case_expr_id);
private: private:
sp_pcontext *m_root_parsing_ctx;
uint m_count; /* Virtual table for storing variables. */
uint m_fsize; TABLE *m_var_table;
Item **m_frame;
Item *m_result; // For FUNCTIONs /*
Collection of Item_field proxies, each of them points to the corresponding
field in m_var_table.
*/
Item **m_var_items;
/*
This is a pointer to a field, which should contain return value for stored
functions (only). For stored procedures, this pointer is NULL.
*/
Field *m_return_value_fld;
/*
Indicates whether the return value (in m_return_value_fld) has been set
during execution.
*/
bool m_return_value_set;
sp_handler_t *m_handler; // Visible handlers sp_handler_t *m_handler; // Visible handlers
uint m_hcount; // Stack pointer for m_handler uint m_hcount; // Stack pointer for m_handler
...@@ -236,13 +239,22 @@ private: ...@@ -236,13 +239,22 @@ private:
uint *m_in_handler; // Active handler, for recursion check uint *m_in_handler; // Active handler, for recursion check
uint m_ihsp; // Stack pointer for m_in_handler uint m_ihsp; // Stack pointer for m_in_handler
int m_hfound; // Set by find_handler; -1 if not found int m_hfound; // Set by find_handler; -1 if not found
List<Item> m_saved; // Saved variables during handler exec.
sp_cursor **m_cstack; sp_cursor **m_cstack;
uint m_ccount; uint m_ccount;
sp_rcontext *m_prev_ctx; // Previous context (NULL if none) Item_cache **m_case_expr_holders;
/* Previous runtime context (NULL if none) */
sp_rcontext *m_prev_runtime_ctx;
private:
bool init_var_table(THD *thd);
bool init_var_items();
Item_cache *create_case_expr_holder(THD *thd, Item_result result_type);
int set_variable(THD *thd, Field *field, Item *value);
}; // class sp_rcontext : public Sql_alloc }; // class sp_rcontext : public Sql_alloc
......
...@@ -1503,10 +1503,10 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u) ...@@ -1503,10 +1503,10 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
my_var *mv= gl++; my_var *mv= gl++;
if (mv->local) if (mv->local)
{ {
Item_splocal *var; Item_splocal *var= new Item_splocal(mv->s, mv->offset, mv->type);
(void)local_vars.push_back(var= new Item_splocal(mv->s, mv->offset)); (void)local_vars.push_back(var);
#ifndef DBUG_OFF #ifndef DBUG_OFF
var->owner= mv->owner; var->m_sp= mv->sp;
#endif #endif
} }
else else
...@@ -1779,8 +1779,8 @@ bool select_dumpvar::send_data(List<Item> &items) ...@@ -1779,8 +1779,8 @@ bool select_dumpvar::send_data(List<Item> &items)
{ {
if ((yy=var_li++)) if ((yy=var_li++))
{ {
if (thd->spcont->set_item_eval(current_thd, if (thd->spcont->set_variable(current_thd, yy->get_var_idx(),
yy->get_offset(), it.ref(), zz->type)) *it.ref()))
DBUG_RETURN(1); DBUG_RETURN(1);
} }
} }
......
...@@ -2100,7 +2100,7 @@ public: ...@@ -2100,7 +2100,7 @@ public:
Routine to which this Item_splocal belongs. Used for checking if correct Routine to which this Item_splocal belongs. Used for checking if correct
runtime context is used for variable handling. runtime context is used for variable handling.
*/ */
sp_head *owner; sp_head *sp;
#endif #endif
bool local; bool local;
uint offset; uint offset;
......
This diff is collapsed.
...@@ -8911,6 +8911,7 @@ err: ...@@ -8911,6 +8911,7 @@ err:
TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list) TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list)
{ {
uint field_count= field_list.elements; uint field_count= field_list.elements;
uint blob_count= 0;
Field **field; Field **field;
create_field *cdef; /* column definition */ create_field *cdef; /* column definition */
uint record_length= 0; uint record_length= 0;
...@@ -8927,6 +8928,12 @@ TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list) ...@@ -8927,6 +8928,12 @@ TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list)
table->s= s= &table->share_not_to_be_used; table->s= s= &table->share_not_to_be_used;
s->fields= field_count; s->fields= field_count;
if (!(s->blob_field= (uint*)thd->alloc((field_list.elements + 1) *
sizeof(uint))))
return 0;
s->blob_ptr_size= mi_portable_sizeof_char_ptr;
/* Create all fields and calculate the total length of record */ /* Create all fields and calculate the total length of record */
List_iterator_fast<create_field> it(field_list); List_iterator_fast<create_field> it(field_list);
while ((cdef= it++)) while ((cdef= it++))
...@@ -8942,9 +8949,15 @@ TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list) ...@@ -8942,9 +8949,15 @@ TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list)
record_length+= (**field).pack_length(); record_length+= (**field).pack_length();
if (! ((**field).flags & NOT_NULL_FLAG)) if (! ((**field).flags & NOT_NULL_FLAG))
++null_count; ++null_count;
if ((*field)->flags & BLOB_FLAG)
s->blob_field[blob_count++]= (uint) (field - table->field);
++field; ++field;
} }
*field= NULL; /* mark the end of the list */ *field= NULL; /* mark the end of the list */
s->blob_field[blob_count]= 0; /* mark the end of the list */
s->blob_fields= blob_count;
null_pack_length= (null_count + 7)/8; null_pack_length= (null_count + 7)/8;
s->reclength= record_length + null_pack_length; s->reclength= record_length + null_pack_length;
......
...@@ -406,7 +406,6 @@ TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ...@@ -406,7 +406,6 @@ TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
ORDER *group, bool distinct, bool save_sum_fields, ORDER *group, bool distinct, bool save_sum_fields,
ulonglong select_options, ha_rows rows_limit, ulonglong select_options, ha_rows rows_limit,
char* alias); char* alias);
TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list);
void free_tmp_table(THD *thd, TABLE *entry); void free_tmp_table(THD *thd, TABLE *entry);
void count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields, void count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields,
bool reset_with_sum_func); bool reset_with_sum_func);
......
...@@ -1123,7 +1123,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, ...@@ -1123,7 +1123,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
trg_action_time_type time_type, trg_action_time_type time_type,
bool old_row_is_record1) bool old_row_is_record1)
{ {
int res= 0; bool err_status= FALSE;
sp_head *sp_trigger= bodies[event][time_type]; sp_head *sp_trigger= bodies[event][time_type];
if (sp_trigger) if (sp_trigger)
...@@ -1183,7 +1183,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, ...@@ -1183,7 +1183,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
#endif // NO_EMBEDDED_ACCESS_CHECKS #endif // NO_EMBEDDED_ACCESS_CHECKS
thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER); thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
res= sp_trigger->execute_function(thd, 0, 0, 0); err_status= sp_trigger->execute_function(thd, 0, 0, 0);
thd->restore_sub_statement_state(&statement_state); thd->restore_sub_statement_state(&statement_state);
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
...@@ -1191,7 +1191,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, ...@@ -1191,7 +1191,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
#endif // NO_EMBEDDED_ACCESS_CHECKS #endif // NO_EMBEDDED_ACCESS_CHECKS
} }
return res; return err_status;
} }
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment