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;
INSERT INTO t1 VALUES(_ujis 0xA4A2);
CREATE PROCEDURE sp1()
BEGIN
DECLARE a CHAR(1);
DECLARE a CHAR(2) CHARSET ujis;
DECLARE cur1 CURSOR FOR SELECT c1 FROM t1;
OPEN cur1;
FETCH cur1 INTO a;
......
drop database if exists mysqltest1;
create schema foo;
show create schema foo;
Database Create Database
......
drop table if exists t1,t2;
drop table if exists t1aa,t2aa;
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.db where user='mysqltest_1' || user='mysqltest_2' || user='mysqltest_3';
flush privileges;
......
......@@ -10,5 +10,5 @@ user()
#
show processlist;
Id User Host db Command Time State Info
# root # test Sleep # NULL
# root # test Query # NULL show processlist
<id> root <host> test <command> <time> <state> <info>
<id> root <host> test <command> <time> <state> <info>
......@@ -25,6 +25,7 @@ count(*)
select count(*) from t2;
count(*)
0
drop procedure if exists p1;
create procedure p1()
begin
declare done integer default 0;
......
drop procedure if exists p1|
drop procedure if exists p2|
create procedure p1()
begin
prepare stmt from "select 1";
......
This diff is collapsed.
......@@ -248,13 +248,13 @@ return i+1|
call sub1("sub1a", (select 7))|
call sub1("sub1b", (select max(i) from t2))|
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 sub2("sub2")|
select * from t1|
id data
sub1a 7
sub1b 3
sub1c 1
sub1d 1
sub2 6
select sub3((select max(i) from t2))|
......@@ -2686,7 +2686,7 @@ call bug8937()|
s x y z
16 3 1 6
a
3.2000
3.2
drop procedure bug8937|
delete from t1|
drop procedure if exists bug6900|
......@@ -2890,21 +2890,30 @@ create function bug9775(v1 char(1)) returns enum('a','b') return v1|
select bug9775('a'),bug9775('b'),bug9775('c')|
bug9775('a') bug9775('b') bug9775('c')
a b
Warnings:
Warning 1265 Data truncated for column 'bug9775('c')' at row 1
drop function bug9775|
create function bug9775(v1 int) returns enum('a','b') return v1|
select bug9775(1),bug9775(2),bug9775(3)|
bug9775(1) bug9775(2) bug9775(3)
a b
Warnings:
Warning 1265 Data truncated for column 'bug9775(3)' at row 1
drop function bug9775|
create function bug9775(v1 char(1)) returns set('a','b') return v1|
select 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|
create function bug9775(v1 int) returns set('a','b') return v1|
select bug9775(1),bug9775(2),bug9775(3),bug9775(4)|
bug9775(1) bug9775(2) bug9775(3) bug9775(4)
a b a,b
Warnings:
Warning 1265 Data truncated for column 'bug9775(4)' at row 1
drop function bug9775|
drop function if exists bug8861|
create function bug8861(v1 int) returns year return v1|
......@@ -2927,12 +2936,10 @@ create procedure bug9004_2(x char(16))
call bug9004_1(x)|
call bug9004_1('12345678901234567')|
Warnings:
Warning 1265 Data truncated for column 'id' at row 1
Warning 1265 Data truncated for column 'id' at row 2
Warning 1265 Data truncated for column 'x' at row 1
call bug9004_2('12345678901234567890')|
Warnings:
Warning 1265 Data truncated for column 'id' at row 1
Warning 1265 Data truncated for column 'id' at row 2
Warning 1265 Data truncated for column 'x' at row 1
delete from t1|
drop procedure bug9004_1|
drop procedure bug9004_2|
......@@ -3527,14 +3534,15 @@ end|
call bug12589_1()|
Table Create Table
tm1 CREATE TEMPORARY TABLE `tm1` (
`spv1` decimal(1,0) unsigned default NULL
`spv1` decimal(3,3) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
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()|
Table Create Table
tm1 CREATE TEMPORARY TABLE `tm1` (
`spv1` decimal(6,3) unsigned default NULL
`spv1` decimal(6,3) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
call bug12589_3()|
Table Create Table
......@@ -4016,34 +4024,37 @@ create procedure bug14643_1()
begin
declare continue handler for sqlexception select 'boo' as 'Handler';
begin
declare v int default x;
declare v int default undefined_var;
if v = 1 then
select 1;
else
select 2;
select v, isnull(v);
end if;
end;
end|
create procedure bug14643_2()
begin
declare continue handler for sqlexception select 'boo' as 'Handler';
case x
case undefined_var
when 1 then
select 1;
else
select 2;
end case;
select undefined_var;
end|
call bug14643_1()|
Handler
boo
2
2
v isnull(v)
NULL 1
call bug14643_2()|
Handler
boo
2
2
Handler
boo
drop procedure bug14643_1|
drop procedure bug14643_2|
drop procedure if exists bug14304|
......
using_big_test
0
DROP TABLE IF EXISTS t1, t2;
CREATE TABLE t1 (id INTEGER);
CREATE TABLE t2 (id INTEGER);
INSERT INTO t1 (id) VALUES (1), (1), (1),(1);
......@@ -40,19 +39,19 @@ AVG(DISTINCT id)
512.5000
SELECT SUM(DISTINCT id)/COUNT(DISTINCT id) FROM t1 GROUP BY id % 13;
SUM(DISTINCT id)/COUNT(DISTINCT id)
513.50000
508.00000
509.00000
510.00000
511.00000
512.00000
513.00000
514.00000
515.00000
516.00000
517.00000
511.50000
512.50000
513.5000
508.0000
509.0000
510.0000
511.0000
512.0000
513.0000
514.0000
515.0000
516.0000
517.0000
511.5000
512.5000
INSERT INTO t1 SELECT id+1024 FROM t1;
INSERT INTO t1 SELECT id+2048 FROM t1;
INSERT INTO t1 SELECT id+4096 FROM t1;
......
drop procedure if exists sp1;
create procedure sp1 () begin
declare v1, v2, v3, v4 decimal(16,12); declare v5 int;
set v1 = 1; set v2 = 2; set v3 = 1000000000000; set v4 = 2000000000000; set v5 = 0;
while v5 < 100000 do
set v1 = v1 + 0.000000000001; set v2 = v2 - 0.000000000001; set v3 = v3 + 1; set v4 = v4 - 1; set v5 = v5 + 1;
end while; select v1, v2, v3 * 0.000000000001, v4 * 0.000000000001; end;//
CREATE PROCEDURE sp1()
BEGIN
DECLARE v1, v2, v3, v4 DECIMAL(28,12);
DECLARE v3_2, v4_2 DECIMAL(28, 12);
DECLARE counter INT;
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()//
v1 v2 v3 * 0.000000000001 v4 * 0.000000000001
1.000000100000 1.999999900000 1.000000100000 1.999999900000
v1 v2 v3 v3_2 v4 v4_2
1.000000100000 1.999999900000 1000000100000.000000000000 1.000000100000 1999999900000.000000000000 1.999999900000
drop procedure sp1;
This diff is collapsed.
......@@ -1170,7 +1170,7 @@ INSERT INTO t1 VALUES(_ujis 0xA4A2);
DELIMITER |;
CREATE PROCEDURE sp1()
BEGIN
DECLARE a CHAR(1);
DECLARE a CHAR(2) CHARSET ujis;
DECLARE cur1 CURSOR FOR SELECT c1 FROM t1;
OPEN cur1;
FETCH cur1 INTO a;
......
#
# 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;
show create schema foo;
......
......@@ -10,6 +10,7 @@
drop table if exists t1,t2;
drop table if exists t1aa,t2aa;
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.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';
connect (con1, 127.0.0.1, root, , test, $MASTER_MYPORT, );
--replace_column 1 #
select user();
--replace_column 1 # 6 # 3 #
--replace_column 1 <id> 3 <host> 5 <command> 6 <time> 7 <state> 8 <info>
show processlist;
connection default;
......@@ -52,6 +52,9 @@ while ($1)
--enable_query_log
select count(*) from t1;
select count(*) from t2;
--disable_warnings
drop procedure if exists p1;
--enable_warnings
delimiter |;
create procedure p1()
begin
......
delimiter |;
--disable_warnings
drop procedure if exists p1|
drop procedure if exists p2|
--enable_warnings
######################################################################
# Test Dynamic SQL in stored procedures. #############################
######################################################################
......
......@@ -367,6 +367,7 @@ create function sub3(i int) returns int
call sub1("sub1a", (select 7))|
call sub1("sub1b", (select max(i) from t2))|
--error ER_OPERAND_COLUMNS
call sub1("sub1c", (select i,d from t2 limit 1))|
call sub1("sub1d", (select 1 from (select 1) a))|
call sub2("sub2")|
......@@ -4797,12 +4798,12 @@ begin
declare continue handler for sqlexception select 'boo' as 'Handler';
begin
declare v int default x;
declare v int default undefined_var;
if v = 1 then
select 1;
else
select 2;
select v, isnull(v);
end if;
end;
end|
......@@ -4811,12 +4812,14 @@ create procedure bug14643_2()
begin
declare continue handler for sqlexception select 'boo' as 'Handler';
case x
case undefined_var
when 1 then
select 1;
else
select 2;
end case;
select undefined_var;
end|
call bug14643_1()|
......
......@@ -12,12 +12,31 @@ drop procedure if exists sp1;
delimiter //;
#
create procedure sp1 () begin
declare v1, v2, v3, v4 decimal(16,12); declare v5 int;
set v1 = 1; set v2 = 2; set v3 = 1000000000000; set v4 = 2000000000000; set v5 = 0;
while v5 < 100000 do
set v1 = v1 + 0.000000000001; set v2 = v2 - 0.000000000001; set v3 = v3 + 1; set v4 = v4 - 1; set v5 = v5 + 1;
end while; select v1, v2, v3 * 0.000000000001, v4 * 0.000000000001; end;//
CREATE PROCEDURE sp1()
BEGIN
DECLARE v1, v2, v3, v4 DECIMAL(28,12);
DECLARE v3_2, v4_2 DECIMAL(28, 12);
DECLARE counter INT;
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()//
#-- should return
......
This diff is collapsed.
......@@ -130,7 +130,19 @@ public:
null_bit == field->null_bit);
}
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; }
/*
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 sort_length() const { return pack_length(); }
virtual void reset(void) { bzero(ptr,pack_length()); }
......@@ -1395,6 +1407,12 @@ public:
void init_for_tmp_table(enum_field_types sql_type_arg,
uint32 max_length, uint32 decimals,
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()
}
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():
rsize(0), name(0), orig_name(0), name_length(0), fixed(0),
is_autogenerated_name(TRUE),
......@@ -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);
Item *it= this_item();
......@@ -814,7 +829,7 @@ double Item_splocal::val_real()
}
longlong Item_splocal::val_int()
longlong Item_sp_variable::val_int()
{
DBUG_ASSERT(fixed);
Item *it= this_item();
......@@ -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);
Item *it= this_item();
String *res= it->val_str(sp);
null_value= it->null_value;
if (!res)
return NULL;
......@@ -854,11 +870,12 @@ String *Item_splocal::val_str(String *sp)
str_value.set(res->ptr(), res->length(), res->charset());
else
res->mark_as_const();
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);
Item *it= this_item();
......@@ -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 it->is_null();
return this_item()->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_splocal::this_item()
{
DBUG_ASSERT(owner == thd->spcont->owner);
return thd->spcont->get_item(m_offset);
DBUG_ASSERT(m_sp == m_thd->spcont->sp);
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_splocal::this_item_addr(THD *thd, Item **addr)
Item_splocal::this_item_addr(THD *thd, Item **)
{
DBUG_ASSERT(owner == thd->spcont->owner);
return thd->spcont->get_item_addr(m_offset);
DBUG_ASSERT(m_sp == thd->spcont->sp);
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);
return thd->spcont->get_item(m_offset);
str->reserve(m_name.length+8);
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;
thd= thd_arg; // Must be set before this_item()
it= this_item();
DBUG_ASSERT(it->fixed);
max_length= it->max_length;
decimals= it->decimals;
unsigned_flag= it->unsigned_flag;
fixed= 1;
return FALSE;
DBUG_ASSERT(m_sp == m_thd->spcont->sp);
return m_thd->spcont->get_case_expr(m_case_expr_id);
}
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);
str->append(m_name.str, m_name.length);
str->append('@');
str->qs_append(m_offset);
DBUG_ASSERT(m_sp == thd->spcont->sp);
return thd->spcont->get_case_expr_addr(m_case_expr_id);
}
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)
}
void Item_name_const::cleanup()
{
fixed= 0;
}
void Item_name_const::print(String *str)
{
str->append(STRING_WITH_LEN("NAME_CONST("));
......@@ -3911,6 +3957,9 @@ int Item::save_in_field(Field *field, bool no_conversions)
str_value.set_quick(0, 0, cs);
return set_field_to_null_with_conversions(field, no_conversions);
}
/* NOTE: If null_value == FALSE, "result" must be not NULL. */
field->set_notnull();
error=field->store(result->ptr(),result->length(),cs);
str_value.set_quick(0, 0, cs);
......
......@@ -341,8 +341,6 @@ public:
{ return (void*) sql_alloc((uint) size); }
static void *operator new(size_t size, MEM_ROOT *mem_root)
{ 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, MEM_ROOT *mem_root) {}
......@@ -671,13 +669,13 @@ public:
current value and pointer to current Item otherwise.
*/
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
current value and pointer passed via parameter otherwise.
*/
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
virtual uint cols() { return 1; }
......@@ -706,21 +704,32 @@ public:
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:
#ifndef DBUG_OFF
......@@ -728,11 +737,74 @@ public:
Routine to which this Item_splocal belongs. Used for checking if correct
runtime context is used for variable handling.
*/
sp_head *owner;
sp_head *m_sp;
#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
statement itself is in sp_instr_stmt::m_query).
......@@ -745,78 +817,94 @@ public:
*/
uint pos_in_query;
Item_splocal(LEX_STRING name, uint offset, uint pos_in_q=0)
: m_offset(offset), m_name(name), thd(0), pos_in_query(pos_in_q)
{
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;
}
Item_splocal(const LEX_STRING &sp_var_name, uint sp_var_idx,
enum_field_types sp_var_type, uint pos_in_q= 0);
bool is_splocal() { return 1; } /* Needed for error checking */
Item *this_item();
const Item *this_item() const;
Item **this_item_addr(THD *thd, Item **);
Item *this_const_item() const;
bool fix_fields(THD *, Item **);
void cleanup();
void print(String *str);
inline uint get_offset()
{
return m_offset;
}
public:
inline const LEX_STRING *my_name() const;
// Abstract methods inherited from Item. Just defer the call to
// the item in the frame
enum Type type() const;
inline uint get_var_idx() const;
double val_real();
longlong val_int();
String *val_str(String *sp);
my_decimal *val_decimal(my_decimal *);
bool is_null();
void print(String *str);
inline enum Type type() const;
inline Item_result result_type() const;
};
void make_field(Send_field *field)
{
Item *it= this_item();
/*****************************************************************************
Item_splocal inline implementation.
*****************************************************************************/
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 const LEX_STRING *Item_splocal::my_name() const
{
return &m_name;
}
Item_result result_type() const
{
return this_const_item()->result_type();
}
inline uint Item_splocal::get_var_idx() const
{
return m_var_idx;
}
bool const_item() const
{
return TRUE;
}
inline enum Item::Type Item_splocal::type() const
{
return m_type;
}
int save_in_field(Field *field, bool no_conversions)
{
return this_item()->save_in_field(field, no_conversions);
}
inline Item_result Item_splocal::result_type() const
{
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).
......@@ -843,7 +931,6 @@ public:
}
bool fix_fields(THD *, Item **);
void cleanup();
enum Type type() const;
double val_real();
......
......@@ -4716,7 +4716,7 @@ Item_func_sp::sp_result_field(void) const
share->table_cache_key = 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);
}
......@@ -4729,17 +4729,17 @@ Item_func_sp::sp_result_field(void) const
1 value = NULL or error
*/
int
bool
Item_func_sp::execute(Field **flp)
{
Item *it;
THD *thd= current_thd;
Field *f;
if (execute(&it))
{
null_value= 1;
context->process_error(current_thd);
return 1;
}
/*
Get field in virtual tmp table to store result. Create the field if
invoked first time.
*/
if (!(f= *flp))
{
*flp= f= sp_result_field();
......@@ -4748,20 +4748,33 @@ Item_func_sp::execute(Field **flp)
f->null_ptr= (uchar *)&null_value;
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
Item_func_sp::execute(Item **itp)
bool
Item_func_sp::execute_impl(THD *thd, Field *return_value_fld)
{
DBUG_ENTER("Item_func_sp::execute");
THD *thd= current_thd;
int res= -1;
bool err_status= TRUE;
Sub_statement_state statement_state;
Security_context *save_security_ctx= thd->security_ctx, *save_ctx_func;
DBUG_ENTER("Item_func_sp::execute_impl");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (context->security_ctx)
{
......@@ -4778,7 +4791,7 @@ Item_func_sp::execute(Item **itp)
function call into binlog.
*/
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);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
......@@ -4788,7 +4801,7 @@ error:
#else
error:
#endif
DBUG_RETURN(res);
DBUG_RETURN(err_status);
}
......@@ -4884,7 +4897,7 @@ Item_func_sp::tmp_table_field(TABLE *t_arg)
DBUG_ENTER("Item_func_sp::tmp_table_field");
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)
res= Item_func::tmp_table_field(t_arg);
......
......@@ -1374,8 +1374,8 @@ private:
Field *result_field;
char result_buf[64];
int execute(Item **itp);
int execute(Field **flp);
bool execute(Field **flp);
bool execute_impl(THD *thd, Field *return_value_fld);
Field *sp_result_field(void) const;
public:
......
......@@ -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);
int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache,
KEY_CACHE *dst_cache);
TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list);
bool mysql_xa_recover(THD *thd);
......@@ -1101,8 +1102,8 @@ void unhex_type2(TYPELIB *lib);
uint check_word(TYPELIB *lib, const char *val, const char *end,
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"
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)
bzero(&table, sizeof(table));
table.in_use= thd;
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);
delete field;
}
......
This diff is collapsed.
......@@ -33,6 +33,9 @@
Item_result
sp_map_result_type(enum enum_field_types type);
Item::Type
sp_map_item_type(enum enum_field_types type);
uint
sp_get_flags_for_command(LEX *lex);
......@@ -123,12 +126,9 @@ public:
/* TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */
int m_type;
uint m_flags; // Boolean attributes of a stored routine
enum enum_field_types m_returns; // For FUNCTIONs only
Field::geometry_type m_geom_returns;
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
create_field m_return_field_def; /* This is used for FUNCTIONs only. */
uchar *m_tmp_query; // Temporary pointer to sub query string
uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value
st_sp_chistics *m_chistics;
......@@ -202,9 +202,6 @@ public:
void
init_strings(THD *thd, LEX *lex, sp_name *name);
TYPELIB *
create_typelib(List<String> *src);
int
create(THD *thd);
......@@ -214,10 +211,10 @@ public:
void
destroy();
int
execute_function(THD *thd, Item **args, uint argcount, Item **resp);
bool
execute_function(THD *thd, Item **args, uint argcount, Field *return_fld);
int
bool
execute_procedure(THD *thd, List<Item> *args);
int
......@@ -278,7 +275,12 @@ public:
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,
st_sp_chistics *chistics, ulong sql_mode);
......@@ -363,7 +365,7 @@ private:
*/
HASH m_sptabs;
int
bool
execute(THD *thd);
/*
......@@ -1074,6 +1076,31 @@ private:
}; // 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
bool
sp_change_security_context(THD *thd, sp_head *sp,
......@@ -1086,8 +1113,10 @@ TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex,
const char *db, const char *name,
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,
Item *reuse, bool use_callers_arena);
bool
sp_eval_expr(THD *thd, Field *result_field, Item *expr_item);
#endif /* _SP_HEAD_H_ */
......@@ -51,21 +51,26 @@ sp_cond_check(LEX_STRING *sqlstate)
}
sp_pcontext::sp_pcontext(sp_pcontext *prev)
: Sql_alloc(), m_psubsize(0), m_csubsize(0), m_hsubsize(0),
m_handlers(0), m_parent(prev), m_pboundary(0)
:Sql_alloc(), m_total_pvars(0), m_csubsize(0), m_hsubsize(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_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_cursor, sizeof(LEX_STRING), 16, 8));
VOID(my_init_dynamic_array(&m_handler, sizeof(sp_cond_type_t *), 16, 8));
m_label.empty();
m_children.empty();
if (!prev)
{
m_poffset= m_coffset= 0;
m_num_case_exprs= 0;
}
else
{
m_poffset= prev->current_pvars();
m_poffset= prev->m_poffset + prev->m_total_pvars;
m_coffset= prev->current_cursors();
m_num_case_exprs= prev->get_num_case_exprs();
}
}
......@@ -81,6 +86,7 @@ sp_pcontext::destroy()
m_children.empty();
m_label.empty();
delete_dynamic(&m_pvar);
delete_dynamic(&m_case_expr_id_lst);
delete_dynamic(&m_cond);
delete_dynamic(&m_cursor);
delete_dynamic(&m_handler);
......@@ -99,16 +105,19 @@ sp_pcontext::push_context()
sp_pcontext *
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)
m_parent->m_psubsize= submax;
submax= max_handlers();
uint submax= max_handlers();
if (submax > m_parent->m_hsubsize)
m_parent->m_hsubsize= submax;
submax= max_cursors();
if (submax > m_parent->m_csubsize)
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;
}
......@@ -191,26 +200,29 @@ sp_pcontext::find_pvar(uint offset)
return NULL; // index out of bounds
}
void
sp_pvar_t *
sp_pcontext::push_pvar(LEX_STRING *name, enum enum_field_types type,
sp_param_mode_t mode)
{
sp_pvar_t *p= (sp_pvar_t *)sql_alloc(sizeof(sp_pvar_t));
if (p)
{
if (m_pvar.elements == m_psubsize)
m_psubsize+= 1;
p->name.str= name->str;
p->name.length= name->length;
p->type= type;
p->mode= mode;
p->offset= current_pvars();
p->dflt= NULL;
insert_dynamic(&m_pvar, (gptr)&p);
}
if (!p)
return NULL;
++m_total_pvars;
p->name.str= name->str;
p->name.length= name->length;
p->type= type;
p->mode= mode;
p->offset= current_pvars();
p->dflt= NULL;
insert_dynamic(&m_pvar, (gptr)&p);
return p;
}
sp_label_t *
sp_pcontext::push_label(char *name, uint ip)
{
......@@ -354,6 +366,29 @@ sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped)
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.
This is only used for debugging.
......
......@@ -34,8 +34,16 @@ typedef struct sp_pvar
LEX_STRING name;
enum enum_field_types type;
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;
create_field field_def;
} sp_pvar_t;
......@@ -114,9 +122,9 @@ class sp_pcontext : public Sql_alloc
//
inline uint
max_pvars()
total_pvars()
{
return m_psubsize + m_pvar.elements;
return m_total_pvars;
}
inline uint
......@@ -155,16 +163,15 @@ class sp_pcontext : public Sql_alloc
p->dflt= it;
}
void
sp_pvar_t *
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
pop_pvar(uint num = 1)
{
while (num--)
pop_dynamic(&m_pvar);
}
/*
Retrieve definitions of fields from the current context and its
children.
*/
void
retrieve_field_definitions(List<create_field> *field_def_lst);
// Find by name
sp_pvar_t *
......@@ -175,7 +182,7 @@ class sp_pcontext : public Sql_alloc
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.
*/
inline void
......@@ -184,6 +191,45 @@ class sp_pcontext : public Sql_alloc
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
//
......@@ -280,8 +326,18 @@ class sp_pcontext : public Sql_alloc
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
uint m_psubsize;
uint m_csubsize;
uint m_hsubsize;
uint m_handlers; // No. of handlers in this context
......@@ -290,8 +346,19 @@ private:
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
/*
Boundary for finding variables in this context. This is the number
of variables currently "invisible" to default clauses.
......@@ -300,7 +367,10 @@ private:
*/
uint m_pboundary;
int m_num_case_exprs;
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_cursor; // Cursors
DYNAMIC_ARRAY m_handler; // Handlers, for checking of duplicates
......
......@@ -29,41 +29,137 @@
#include "sp_rcontext.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),
m_ihsp(0), m_hfound(-1), m_ccount(0), m_prev_ctx(prev)
sp_rcontext::sp_rcontext(sp_pcontext *root_parsing_ctx,
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::set_item_eval(THD *thd, uint idx, Item **item_addr,
enum_field_types type)
sp_rcontext::~sp_rcontext()
{
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;
Item *reuse_it;
/* sp_eval_func_item will use callers_arena */
int res;
reuse_it= get_item(idx);
it= sp_eval_func_item(thd, item_addr, type, reuse_it, TRUE);
if (! it)
res= -1;
else
List<create_field> field_def_lst;
if (!m_root_parsing_ctx->total_pvars())
return FALSE;
m_root_parsing_ctx->retrieve_field_definitions(&field_def_lst);
DBUG_ASSERT(field_def_lst.elements == m_root_parsing_ctx->total_pvars());
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;
set_item(idx, it);
if (!(m_var_items[idx]= new Item_field(m_var_table->field[idx])))
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
sp_rcontext::find_handler(uint sql_errno,
MYSQL_ERROR::enum_warning_level level)
......@@ -117,32 +213,14 @@ sp_rcontext::find_handler(uint sql_errno,
}
if (found < 0)
{
if (m_prev_ctx)
return m_prev_ctx->find_handler(sql_errno, level);
if (m_prev_runtime_ctx)
return m_prev_runtime_ctx->find_handler(sql_errno, level);
return FALSE;
}
m_hfound= found;
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
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);
}
void
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
......@@ -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
****************************************************************************/
......@@ -294,11 +503,8 @@ bool Select_fetch_into_spvars::send_data(List<Item> &items)
*/
for (; pv= pv_iter++, item= item_iter++; )
{
Item *reuse= thd->spcont->get_item(pv->offset);
/* Evaluate a new item on the arena of the calling instruction */
Item *it= sp_eval_func_item(thd, &item, pv->type, reuse, TRUE);
thd->spcont->set_item(pv->offset, it);
if (thd->spcont->set_variable(thd, pv->offset, item))
return TRUE;
}
return FALSE;
}
......@@ -43,12 +43,22 @@ typedef struct
/*
This is a run context? of one SP ?
THis is
- a stack of cursors?
- a stack of handlers?
- a stack of Items ?
- a stack of instruction locations in SP?
This class is a runtime context of a Stored Routine. It is used in an
execution and is intended to contain all dynamic objects (i.e. objects, which
can be changed during execution), such as:
- stored routine variables;
- cursors;
- 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
......@@ -68,62 +78,34 @@ class sp_rcontext : public Sql_alloc
#ifndef DBUG_OFF
/*
Routine to which this Item_splocal belongs. Used for checking if correct
runtime context is used for variable handling.
The routine for which this runtime context is created. Used for checking
if correct runtime context is used for variable handling.
*/
sp_head *owner;
sp_head *sp;
#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()
{
// 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;
}
~sp_rcontext();
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
set_item_eval(THD *thd, uint idx, Item **i, enum_field_types type);
inline Item *
get_item(uint idx)
{
return m_frame[idx];
}
set_variable(THD *thd, uint var_idx, Item *value);
inline Item **
get_item_addr(uint idx)
{
return m_frame + idx;
}
Item *
get_item(uint var_idx);
Item **
get_item_addr(uint var_idx);
inline void
set_result(Item *it)
{
m_result= it;
}
bool
set_return_value(THD *thd, Item *return_value_item);
inline Item *
get_result()
inline bool
is_return_value_set() const
{
return m_result;
return m_return_value_set;
}
inline void
......@@ -195,14 +177,6 @@ class sp_rcontext : public Sql_alloc
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
push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
......@@ -221,13 +195,42 @@ class sp_rcontext : public Sql_alloc
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:
sp_pcontext *m_root_parsing_ctx;
uint m_count;
uint m_fsize;
Item **m_frame;
/* Virtual table for storing variables. */
TABLE *m_var_table;
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
uint m_hcount; // Stack pointer for m_handler
......@@ -236,13 +239,22 @@ private:
uint *m_in_handler; // Active handler, for recursion check
uint m_ihsp; // Stack pointer for m_in_handler
int m_hfound; // Set by find_handler; -1 if not found
List<Item> m_saved; // Saved variables during handler exec.
sp_cursor **m_cstack;
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
......
......@@ -1503,10 +1503,10 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
my_var *mv= gl++;
if (mv->local)
{
Item_splocal *var;
(void)local_vars.push_back(var= new Item_splocal(mv->s, mv->offset));
Item_splocal *var= new Item_splocal(mv->s, mv->offset, mv->type);
(void)local_vars.push_back(var);
#ifndef DBUG_OFF
var->owner= mv->owner;
var->m_sp= mv->sp;
#endif
}
else
......@@ -1779,8 +1779,8 @@ bool select_dumpvar::send_data(List<Item> &items)
{
if ((yy=var_li++))
{
if (thd->spcont->set_item_eval(current_thd,
yy->get_offset(), it.ref(), zz->type))
if (thd->spcont->set_variable(current_thd, yy->get_var_idx(),
*it.ref()))
DBUG_RETURN(1);
}
}
......
......@@ -2100,7 +2100,7 @@ public:
Routine to which this Item_splocal belongs. Used for checking if correct
runtime context is used for variable handling.
*/
sp_head *owner;
sp_head *sp;
#endif
bool local;
uint offset;
......
This diff is collapsed.
......@@ -8911,6 +8911,7 @@ err:
TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list)
{
uint field_count= field_list.elements;
uint blob_count= 0;
Field **field;
create_field *cdef; /* column definition */
uint record_length= 0;
......@@ -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;
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 */
List_iterator_fast<create_field> it(field_list);
while ((cdef= it++))
......@@ -8942,9 +8949,15 @@ TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list)
record_length+= (**field).pack_length();
if (! ((**field).flags & NOT_NULL_FLAG))
++null_count;
if ((*field)->flags & BLOB_FLAG)
s->blob_field[blob_count++]= (uint) (field - table->field);
++field;
}
*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;
s->reclength= record_length + null_pack_length;
......
......@@ -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,
ulonglong select_options, ha_rows rows_limit,
char* alias);
TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list);
void free_tmp_table(THD *thd, TABLE *entry);
void count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields,
bool reset_with_sum_func);
......
......@@ -1123,7 +1123,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
trg_action_time_type time_type,
bool old_row_is_record1)
{
int res= 0;
bool err_status= FALSE;
sp_head *sp_trigger= bodies[event][time_type];
if (sp_trigger)
......@@ -1183,7 +1183,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
#endif // NO_EMBEDDED_ACCESS_CHECKS
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);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
......@@ -1191,7 +1191,7 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
#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