Commit e01d33d7 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-14467 Item_param: replace {INT|DECIMAL|REAL|STRING|TIME}_VALUE with Type_handler

1. Removing data type specific constants from enum_item_param_state,
   adding SHORT_DATA_VALUE instead.
2. Replacing tests for Item_param::state for the removed constants to
   tests for Type_handler::cmp_type() against {INT|REAL|TIME|DECIAML}_RESULT.
   Deriving Item_param::PValue from Type_handler_hybrid_field_type,
   to store the data type handler of the current value of the parameter.

3. Moving Item_param::decimal_value and Item_param::str_value_ptr
   to Item_param::PValue. Adding Item_param::PValue::m_string
   and changing Item_param to use it to store string values,
   instead of Item::str_value. The intent is to replace Item_param::value
   to a st_value based implementation in the future, to avoid duplicate code.
   Adding a sub-class Item::PValue_simple, to implement
   Item_param::PValue::swap() easier.
   Remaming Item_basic_value::fix_charset_and_length_from_str_value()
   to fix_charset_and_length() and adding the "CHARSET_INFO" pointer
   parameter, instead of getting it directly from item->str_value.charset().
   Changing Item_param to pass value.m_string.charset() instead
   of str_value.charset().
   Adding a String argument to the overloaded
   fix_charset_and_length_from_str_value() and changing Item_param
   to pass value.m_string instead of str_value.

4. Replacing the case in Item_param::save_in_field() to a call
   for Type_handler::Item_save_in_field().

5. Adding new methods into Item_param::PValue:
   val_real(), val_int(), val_decimal(), val_str().
   Changing the corresponding Item_param methods
   to use these new Item_param::PValue methods
   internally. Adding a helper method
   Item_param::can_return_value() and removing
   duplicate code in Item_param::val_xxx().

6. Removing value.set_handler() from Item_param::set_conversion()
   and Type_handler_xxx::Item_param_set_from_value().
   It's now done inside Item_param::set_param_func(),
   Item_param::set_value() and Item_param::set_limit_clause_param().

7. Changing Type_handler_int_result::Item_param_set_from_value()
   to set max_length using attr->max_length instead of
   MY_INT64_NUM_DECIMAL_DIGITS, to preserve the data type
   of the assigned expression more precisely.

8. Adding Type_handler_hybrid_field_type::swap(),
   using it in Item_param::PValue::swap().

9. Moving the data-type specific code from
   Item_param::query_val_str(), Item_param::eq(),
   Item_param::clone_item() to
   Item_param::value_query_type_str(),
   Item_param::value_eq(), Item_param::value_clone_item(),
   to split the "state" dependent code and
   the data type dependent code.
   Later we'll split the data type related code further
   and add new methods in Type_handler. This will be done
   after we replace Item_param::PValue to st_value.

10. Adding asserts into set_int(), set_double(), set_decimal(),
   set_time(), set_str(), set_longdata() to make sure that
   the value set to Item_param corresponds to the previously
   set data type handler.

11. Adding tests into t/ps.test and suite/binlog/t/binlog_stm_ps.test,
   to cover Item_param::print() and Item_param::append_for_log()
   for LIMIT clause parameters.
   Note, the patch does not change the behavior covered by the new
   tests. Adding for better code coverage.

12. Adding tests for more precise integer data type in queries like this:
    EXECUTE IMMEDIATE
     'CREATE OR REPLACE TABLE t1 AS SELECT 999999999 AS a,? AS b'
      USING 999999999;
    The explicit integer literal and the same integer literal
    passed as a PS parameter now produce columns of the same data type.
    Re-recording old results in ps.result, gis.result, func_hybrid_type.result
    accordingly.
parent 590400f7
...@@ -3443,8 +3443,8 @@ EXECUTE stmt USING @a,@a; ...@@ -3443,8 +3443,8 @@ EXECUTE stmt USING @a,@a;
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
Table Create Table Table Create Table
t1 CREATE TABLE `t1` ( t1 CREATE TABLE `t1` (
`a` varchar(21) DEFAULT NULL, `a` varchar(20) DEFAULT NULL,
`b` varchar(21) DEFAULT NULL `b` varchar(20) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1; DROP TABLE t1;
# #
......
...@@ -4390,7 +4390,7 @@ SELECT ST_BUFFER(Point(1,1), Point(1,1)); ...@@ -4390,7 +4390,7 @@ SELECT ST_BUFFER(Point(1,1), Point(1,1));
ERROR HY000: Illegal parameter data type geometry for operation 'st_buffer' ERROR HY000: Illegal parameter data type geometry for operation 'st_buffer'
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ST_ENVELOPE(?) AS g'; PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ST_ENVELOPE(?) AS g';
EXECUTE stmt USING 1; EXECUTE stmt USING 1;
ERROR HY000: Illegal parameter data type bigint for operation 'st_envelope' ERROR HY000: Illegal parameter data type int for operation 'st_envelope'
EXECUTE stmt USING POINT(1,1); EXECUTE stmt USING POINT(1,1);
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
Table Create Table Table Create Table
...@@ -4404,7 +4404,7 @@ DROP TABLE t1; ...@@ -4404,7 +4404,7 @@ DROP TABLE t1;
DEALLOCATE PREPARE stmt; DEALLOCATE PREPARE stmt;
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ST_BUFFER(?,?) AS g'; PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ST_BUFFER(?,?) AS g';
EXECUTE stmt USING 1,1; EXECUTE stmt USING 1,1;
ERROR HY000: Illegal parameter data type bigint for operation 'st_buffer' ERROR HY000: Illegal parameter data type int for operation 'st_buffer'
EXECUTE stmt USING POINT(1,1),POINT(1,1); EXECUTE stmt USING POINT(1,1),POINT(1,1);
ERROR HY000: Illegal parameter data type geometry for operation 'st_buffer' ERROR HY000: Illegal parameter data type geometry for operation 'st_buffer'
EXECUTE stmt USING POINT(1,1),0; EXECUTE stmt USING POINT(1,1),0;
...@@ -4455,7 +4455,7 @@ SELECT POINT(1,POINT(1,1)); ...@@ -4455,7 +4455,7 @@ SELECT POINT(1,POINT(1,1));
ERROR HY000: Illegal parameter data type geometry for operation 'point' ERROR HY000: Illegal parameter data type geometry for operation 'point'
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ST_GEOMFROMTEXT(?,?) AS g'; PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ST_GEOMFROMTEXT(?,?) AS g';
EXECUTE stmt USING 1,1; EXECUTE stmt USING 1,1;
ERROR HY000: Illegal parameter data type bigint for operation 'st_geometryfromtext' ERROR HY000: Illegal parameter data type int for operation 'st_geometryfromtext'
EXECUTE stmt USING POINT(1,1),POINT(1,1); EXECUTE stmt USING POINT(1,1),POINT(1,1);
ERROR HY000: Illegal parameter data type geometry for operation 'st_geometryfromtext' ERROR HY000: Illegal parameter data type geometry for operation 'st_geometryfromtext'
EXECUTE stmt USING 'POINT(1 1)',1; EXECUTE stmt USING 'POINT(1 1)',1;
......
...@@ -4407,7 +4407,7 @@ EXECUTE stmt USING 10; ...@@ -4407,7 +4407,7 @@ EXECUTE stmt USING 10;
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
Table Create Table Table Create Table
t1 CREATE TABLE `t1` ( t1 CREATE TABLE `t1` (
`c1` bigint(21) NOT NULL `c1` int(2) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1; DROP TABLE t1;
EXECUTE stmt USING 10.123; EXECUTE stmt USING 10.123;
...@@ -4614,10 +4614,10 @@ EXECUTE IMMEDIATE ...@@ -4614,10 +4614,10 @@ EXECUTE IMMEDIATE
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
Table Create Table Table Create Table
t1 CREATE TABLE `t1` ( t1 CREATE TABLE `t1` (
`a` bigint(21) NOT NULL, `a` bigint(20) NOT NULL,
`b` decimal(3,1) DEFAULT NULL, `b` decimal(3,1) DEFAULT NULL,
`c` double NOT NULL, `c` double NOT NULL,
`d` varchar(3) NOT NULL `d` tinytext NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1; DROP TABLE t1;
EXECUTE IMMEDIATE EXECUTE IMMEDIATE
...@@ -4626,7 +4626,7 @@ EXECUTE IMMEDIATE ...@@ -4626,7 +4626,7 @@ EXECUTE IMMEDIATE
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
Table Create Table Table Create Table
t1 CREATE TABLE `t1` ( t1 CREATE TABLE `t1` (
`a` bigint(21) NOT NULL, `a` int(2) NOT NULL,
`b` decimal(3,1) DEFAULT NULL, `b` decimal(3,1) DEFAULT NULL,
`c` double NOT NULL, `c` double NOT NULL,
`d` varchar(3) NOT NULL `d` varchar(3) NOT NULL
...@@ -5088,3 +5088,63 @@ t1 CREATE TABLE `t1` ( ...@@ -5088,3 +5088,63 @@ t1 CREATE TABLE `t1` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1; DROP TABLE t1;
DROP PROCEDURE p1; DROP PROCEDURE p1;
#
# MDEV-14467 Item_param: replace {INT|DECIMAL|REAL|STRING|TIME}_VALUE with Type_handler
#
EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT 1 FROM DUAL LIMIT ?' USING 10;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select 1 AS `1` limit 10
EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT 1 FROM DUAL LIMIT ?' USING 10.1;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select 1 AS `1` limit 10
EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT 1 FROM DUAL LIMIT ?' USING 10.1e0;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select 1 AS `1` limit 10
EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT 1 FROM DUAL LIMIT ?' USING '10';
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select 1 AS `1` limit 10
EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT 1 FROM DUAL LIMIT ?' USING TIME'10:10:10';
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select 1 AS `1` limit 101010
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 AS SELECT 1 AS a,? AS b' USING 1;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(1) NOT NULL,
`b` int(1) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1;
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 AS SELECT 10 AS a,? AS b' USING 10;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(2) NOT NULL,
`b` int(2) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1;
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 AS SELECT 999999999 AS a,? AS b' USING 999999999;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(9) NOT NULL,
`b` int(9) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1;
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 AS SELECT 2147483647 AS a,? AS b' USING 2147483647;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` bigint(10) NOT NULL,
`b` bigint(10) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1;
...@@ -185,3 +185,46 @@ master-bin.000004 # Gtid # # GTID #-#-# ...@@ -185,3 +185,46 @@ master-bin.000004 # Gtid # # GTID #-#-#
master-bin.000004 # Query # # use `test`; DROP PROCEDURE p1 master-bin.000004 # Query # # use `test`; DROP PROCEDURE p1
master-bin.000004 # Gtid # # GTID #-#-# master-bin.000004 # Gtid # # GTID #-#-#
master-bin.000004 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ master-bin.000004 # Query # # use `test`; DROP TABLE `t1` /* generated by server */
#
#MDEV-14467 Item_param: replace {INT|DECIMAL|REAL|STRING|TIME}_VALUE with Type_handler
#
FLUSH LOGS;
CREATE TABLE t1 (a INT);
EXECUTE IMMEDIATE 'INSERT INTO t1 SELECT 1 LIMIT ?' USING 10;
Warnings:
Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted
EXECUTE IMMEDIATE 'INSERT INTO t1 SELECT 1 LIMIT ?' USING 10.1;
Warnings:
Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted
EXECUTE IMMEDIATE 'INSERT INTO t1 SELECT 1 LIMIT ?' USING 10.1e0;
Warnings:
Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted
EXECUTE IMMEDIATE 'INSERT INTO t1 SELECT 1 LIMIT ?' USING '10';
Warnings:
Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted
EXECUTE IMMEDIATE 'INSERT INTO t1 SELECT 1 LIMIT ?' USING TIME'10:10:10';
Warnings:
Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted
DROP TABLE t1;
include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000005 # Binlog_checkpoint # # master-bin.000005
master-bin.000005 # Gtid # # GTID #-#-#
master-bin.000005 # Query # # use `test`; CREATE TABLE t1 (a INT)
master-bin.000005 # Gtid # # BEGIN GTID #-#-#
master-bin.000005 # Query # # use `test`; INSERT INTO t1 SELECT 1 LIMIT 10
master-bin.000005 # Query # # COMMIT
master-bin.000005 # Gtid # # BEGIN GTID #-#-#
master-bin.000005 # Query # # use `test`; INSERT INTO t1 SELECT 1 LIMIT 10
master-bin.000005 # Query # # COMMIT
master-bin.000005 # Gtid # # BEGIN GTID #-#-#
master-bin.000005 # Query # # use `test`; INSERT INTO t1 SELECT 1 LIMIT 10
master-bin.000005 # Query # # COMMIT
master-bin.000005 # Gtid # # BEGIN GTID #-#-#
master-bin.000005 # Query # # use `test`; INSERT INTO t1 SELECT 1 LIMIT 10
master-bin.000005 # Query # # COMMIT
master-bin.000005 # Gtid # # BEGIN GTID #-#-#
master-bin.000005 # Query # # use `test`; INSERT INTO t1 SELECT 1 LIMIT 101010
master-bin.000005 # Query # # COMMIT
master-bin.000005 # Gtid # # GTID #-#-#
master-bin.000005 # Query # # use `test`; DROP TABLE `t1` /* generated by server */
...@@ -99,3 +99,19 @@ DROP TABLE t1; ...@@ -99,3 +99,19 @@ DROP TABLE t1;
--let $binlog_file = LAST --let $binlog_file = LAST
source include/show_binlog_events.inc; source include/show_binlog_events.inc;
--echo #
--echo #MDEV-14467 Item_param: replace {INT|DECIMAL|REAL|STRING|TIME}_VALUE with Type_handler
--echo #
FLUSH LOGS;
CREATE TABLE t1 (a INT);
EXECUTE IMMEDIATE 'INSERT INTO t1 SELECT 1 LIMIT ?' USING 10;
EXECUTE IMMEDIATE 'INSERT INTO t1 SELECT 1 LIMIT ?' USING 10.1;
EXECUTE IMMEDIATE 'INSERT INTO t1 SELECT 1 LIMIT ?' USING 10.1e0;
EXECUTE IMMEDIATE 'INSERT INTO t1 SELECT 1 LIMIT ?' USING '10';
EXECUTE IMMEDIATE 'INSERT INTO t1 SELECT 1 LIMIT ?' USING TIME'10:10:10';
DROP TABLE t1;
--let $binlog_file = LAST
source include/show_binlog_events.inc;
...@@ -4541,3 +4541,29 @@ CREATE TABLE t1 AS SELECT @a AS c1; ...@@ -4541,3 +4541,29 @@ CREATE TABLE t1 AS SELECT @a AS c1;
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
DROP TABLE t1; DROP TABLE t1;
DROP PROCEDURE p1; DROP PROCEDURE p1;
--echo #
--echo # MDEV-14467 Item_param: replace {INT|DECIMAL|REAL|STRING|TIME}_VALUE with Type_handler
--echo #
EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT 1 FROM DUAL LIMIT ?' USING 10;
EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT 1 FROM DUAL LIMIT ?' USING 10.1;
EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT 1 FROM DUAL LIMIT ?' USING 10.1e0;
EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT 1 FROM DUAL LIMIT ?' USING '10';
EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT 1 FROM DUAL LIMIT ?' USING TIME'10:10:10';
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 AS SELECT 1 AS a,? AS b' USING 1;
SHOW CREATE TABLE t1;
DROP TABLE t1;
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 AS SELECT 10 AS a,? AS b' USING 10;
SHOW CREATE TABLE t1;
DROP TABLE t1;
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 AS SELECT 999999999 AS a,? AS b' USING 999999999;
SHOW CREATE TABLE t1;
DROP TABLE t1;
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 AS SELECT 2147483647 AS a,? AS b' USING 2147483647;
SHOW CREATE TABLE t1;
DROP TABLE t1;
...@@ -3765,8 +3765,9 @@ void Item_param::set_null() ...@@ -3765,8 +3765,9 @@ void Item_param::set_null()
void Item_param::set_int(longlong i, uint32 max_length_arg) void Item_param::set_int(longlong i, uint32 max_length_arg)
{ {
DBUG_ENTER("Item_param::set_int"); DBUG_ENTER("Item_param::set_int");
DBUG_ASSERT(value.type_handler()->cmp_type() == INT_RESULT);
value.integer= (longlong) i; value.integer= (longlong) i;
state= INT_VALUE; state= SHORT_DATA_VALUE;
collation.set_numeric(); collation.set_numeric();
max_length= max_length_arg; max_length= max_length_arg;
decimals= 0; decimals= 0;
...@@ -3778,8 +3779,9 @@ void Item_param::set_int(longlong i, uint32 max_length_arg) ...@@ -3778,8 +3779,9 @@ void Item_param::set_int(longlong i, uint32 max_length_arg)
void Item_param::set_double(double d) void Item_param::set_double(double d)
{ {
DBUG_ENTER("Item_param::set_double"); DBUG_ENTER("Item_param::set_double");
DBUG_ASSERT(value.type_handler()->cmp_type() == REAL_RESULT);
value.real= d; value.real= d;
state= REAL_VALUE; state= SHORT_DATA_VALUE;
collation.set_numeric(); collation.set_numeric();
max_length= DBL_DIG + 8; max_length= DBL_DIG + 8;
decimals= NOT_FIXED_DEC; decimals= NOT_FIXED_DEC;
...@@ -3805,14 +3807,15 @@ void Item_param::set_decimal(const char *str, ulong length) ...@@ -3805,14 +3807,15 @@ void Item_param::set_decimal(const char *str, ulong length)
{ {
char *end; char *end;
DBUG_ENTER("Item_param::set_decimal"); DBUG_ENTER("Item_param::set_decimal");
DBUG_ASSERT(value.type_handler()->cmp_type() == DECIMAL_RESULT);
end= (char*) str+length; end= (char*) str+length;
str2my_decimal(E_DEC_FATAL_ERROR, str, &decimal_value, &end); str2my_decimal(E_DEC_FATAL_ERROR, str, &value.m_decimal, &end);
state= DECIMAL_VALUE; state= SHORT_DATA_VALUE;
decimals= decimal_value.frac; decimals= value.m_decimal.frac;
collation.set_numeric(); collation.set_numeric();
max_length= max_length=
my_decimal_precision_to_length_no_truncation(decimal_value.precision(), my_decimal_precision_to_length_no_truncation(value.m_decimal.precision(),
decimals, unsigned_flag); decimals, unsigned_flag);
maybe_null= 0; maybe_null= 0;
fix_type(Item::DECIMAL_ITEM); fix_type(Item::DECIMAL_ITEM);
...@@ -3821,14 +3824,15 @@ void Item_param::set_decimal(const char *str, ulong length) ...@@ -3821,14 +3824,15 @@ void Item_param::set_decimal(const char *str, ulong length)
void Item_param::set_decimal(const my_decimal *dv, bool unsigned_arg) void Item_param::set_decimal(const my_decimal *dv, bool unsigned_arg)
{ {
state= DECIMAL_VALUE; DBUG_ASSERT(value.type_handler()->cmp_type() == DECIMAL_RESULT);
state= SHORT_DATA_VALUE;
my_decimal2decimal(dv, &decimal_value); my_decimal2decimal(dv, &value.m_decimal);
decimals= (uint8) decimal_value.frac; decimals= (uint8) value.m_decimal.frac;
collation.set_numeric(); collation.set_numeric();
unsigned_flag= unsigned_arg; unsigned_flag= unsigned_arg;
max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, max_length= my_decimal_precision_to_length(value.m_decimal.intg + decimals,
decimals, unsigned_flag); decimals, unsigned_flag);
fix_type(Item::DECIMAL_ITEM); fix_type(Item::DECIMAL_ITEM);
} }
...@@ -3836,7 +3840,7 @@ void Item_param::set_decimal(const my_decimal *dv, bool unsigned_arg) ...@@ -3836,7 +3840,7 @@ void Item_param::set_decimal(const my_decimal *dv, bool unsigned_arg)
void Item_param::fix_temporal(uint32 max_length_arg, uint decimals_arg) void Item_param::fix_temporal(uint32 max_length_arg, uint decimals_arg)
{ {
state= TIME_VALUE; state= SHORT_DATA_VALUE;
collation.set_numeric(); collation.set_numeric();
max_length= max_length_arg; max_length= max_length_arg;
decimals= decimals_arg; decimals= decimals_arg;
...@@ -3847,6 +3851,7 @@ void Item_param::fix_temporal(uint32 max_length_arg, uint decimals_arg) ...@@ -3847,6 +3851,7 @@ void Item_param::fix_temporal(uint32 max_length_arg, uint decimals_arg)
void Item_param::set_time(const MYSQL_TIME *tm, void Item_param::set_time(const MYSQL_TIME *tm,
uint32 max_length_arg, uint decimals_arg) uint32 max_length_arg, uint decimals_arg)
{ {
DBUG_ASSERT(value.type_handler()->cmp_type() == TIME_RESULT);
value.time= *tm; value.time= *tm;
fix_temporal(max_length_arg, decimals_arg); fix_temporal(max_length_arg, decimals_arg);
} }
...@@ -3869,6 +3874,7 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type, ...@@ -3869,6 +3874,7 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type,
uint32 max_length_arg) uint32 max_length_arg)
{ {
DBUG_ENTER("Item_param::set_time"); DBUG_ENTER("Item_param::set_time");
DBUG_ASSERT(value.type_handler()->cmp_type() == TIME_RESULT);
value.time= *tm; value.time= *tm;
value.time.time_type= time_type; value.time.time_type= time_type;
...@@ -3891,12 +3897,13 @@ bool Item_param::set_str(const char *str, ulong length, ...@@ -3891,12 +3897,13 @@ bool Item_param::set_str(const char *str, ulong length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs) CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{ {
DBUG_ENTER("Item_param::set_str"); DBUG_ENTER("Item_param::set_str");
DBUG_ASSERT(value.type_handler()->cmp_type() == STRING_RESULT);
/* /*
Assign string with no conversion: data is converted only after it's Assign string with no conversion: data is converted only after it's
been written to the binary log. been written to the binary log.
*/ */
uint dummy_errors; uint dummy_errors;
if (str_value.copy(str, length, fromcs, tocs, &dummy_errors)) if (value.m_string.copy(str, length, fromcs, tocs, &dummy_errors))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
/* /*
Set str_value_ptr to make sure it's in sync with str_value. Set str_value_ptr to make sure it's in sync with str_value.
...@@ -3909,10 +3916,10 @@ bool Item_param::set_str(const char *str, ulong length, ...@@ -3909,10 +3916,10 @@ bool Item_param::set_str(const char *str, ulong length,
later, which will convert the value from the client character set to the later, which will convert the value from the client character set to the
connection character set, and will reset both str_value and str_value_ptr. connection character set, and will reset both str_value and str_value_ptr.
*/ */
str_value_ptr.set(str_value.ptr(), value.m_string_ptr.set(value.m_string.ptr(),
str_value.length(), value.m_string.length(),
str_value.charset()); value.m_string.charset());
state= STRING_VALUE; state= SHORT_DATA_VALUE;
collation.set(tocs, DERIVATION_COERCIBLE); collation.set(tocs, DERIVATION_COERCIBLE);
max_length= length; max_length= length;
maybe_null= 0; maybe_null= 0;
...@@ -3926,6 +3933,7 @@ bool Item_param::set_str(const char *str, ulong length, ...@@ -3926,6 +3933,7 @@ bool Item_param::set_str(const char *str, ulong length,
bool Item_param::set_longdata(const char *str, ulong length) bool Item_param::set_longdata(const char *str, ulong length)
{ {
DBUG_ENTER("Item_param::set_longdata"); DBUG_ENTER("Item_param::set_longdata");
DBUG_ASSERT(value.type_handler()->cmp_type() == STRING_RESULT);
/* /*
If client character set is multibyte, end of long data packet If client character set is multibyte, end of long data packet
...@@ -3936,7 +3944,7 @@ bool Item_param::set_longdata(const char *str, ulong length) ...@@ -3936,7 +3944,7 @@ bool Item_param::set_longdata(const char *str, ulong length)
(here), and first have to concatenate all pieces together, (here), and first have to concatenate all pieces together,
write query to the binary log and only then perform conversion. write query to the binary log and only then perform conversion.
*/ */
if (str_value.length() + length > max_long_data_size) if (value.m_string.length() + length > max_long_data_size)
{ {
my_message(ER_UNKNOWN_ERROR, my_message(ER_UNKNOWN_ERROR,
"Parameter of prepared statement which is set through " "Parameter of prepared statement which is set through "
...@@ -3946,7 +3954,7 @@ bool Item_param::set_longdata(const char *str, ulong length) ...@@ -3946,7 +3954,7 @@ bool Item_param::set_longdata(const char *str, ulong length)
DBUG_RETURN(true); DBUG_RETURN(true);
} }
if (str_value.append(str, length, &my_charset_bin)) if (value.m_string.append(str, length, &my_charset_bin))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
state= LONG_DATA_VALUE; state= LONG_DATA_VALUE;
maybe_null= 0; maybe_null= 0;
...@@ -4009,15 +4017,16 @@ bool Item_param::set_from_item(THD *thd, Item *item) ...@@ -4009,15 +4017,16 @@ bool Item_param::set_from_item(THD *thd, Item *item)
else else
{ {
unsigned_flag= item->unsigned_flag; unsigned_flag= item->unsigned_flag;
set_handler_by_result_type(item->result_type()); set_handler(item->type_handler());
DBUG_RETURN(set_limit_clause_param(val)); DBUG_RETURN(set_limit_clause_param(val));
} }
} }
struct st_value tmp; struct st_value tmp;
if (!item->save_in_value(&tmp)) if (!item->save_in_value(&tmp))
{ {
if (item->type_handler()->Item_param_set_from_value(thd, this, item, &tmp)) const Type_handler *h= item->type_handler();
DBUG_RETURN(true); set_handler(h);
DBUG_RETURN(set_value(thd, item, &tmp, h));
} }
else else
set_null(); set_null();
...@@ -4037,16 +4046,16 @@ void Item_param::reset() ...@@ -4037,16 +4046,16 @@ void Item_param::reset()
{ {
DBUG_ENTER("Item_param::reset"); DBUG_ENTER("Item_param::reset");
/* Shrink string buffer if it's bigger than max possible CHAR column */ /* Shrink string buffer if it's bigger than max possible CHAR column */
if (str_value.alloced_length() > MAX_CHAR_WIDTH) if (value.m_string.alloced_length() > MAX_CHAR_WIDTH)
str_value.free(); value.m_string.free();
else else
str_value.length(0); value.m_string.length(0);
str_value_ptr.length(0); value.m_string_ptr.length(0);
/* /*
We must prevent all charset conversions until data has been written We must prevent all charset conversions until data has been written
to the binary log. to the binary log.
*/ */
str_value.set_charset(&my_charset_bin); value.m_string.set_charset(&my_charset_bin);
collation.set(&my_charset_bin, DERIVATION_COERCIBLE); collation.set(&my_charset_bin, DERIVATION_COERCIBLE);
state= NO_VALUE; state= NO_VALUE;
maybe_null= 1; maybe_null= 1;
...@@ -4075,19 +4084,9 @@ int Item_param::save_in_field(Field *field, bool no_conversions) ...@@ -4075,19 +4084,9 @@ int Item_param::save_in_field(Field *field, bool no_conversions)
Garbage (e.g. in case of a memory overrun) is handled after the switch. Garbage (e.g. in case of a memory overrun) is handled after the switch.
*/ */
switch (state) { switch (state) {
case INT_VALUE: case SHORT_DATA_VALUE:
return field->store(value.integer, unsigned_flag);
case REAL_VALUE:
return field->store(value.real);
case DECIMAL_VALUE:
return field->store_decimal(&decimal_value);
case TIME_VALUE:
field->store_time_dec(&value.time, decimals);
return 0;
case STRING_VALUE:
case LONG_DATA_VALUE: case LONG_DATA_VALUE:
return field->store(str_value.ptr(), str_value.length(), return value.type_handler()->Item_save_in_field(this, field, no_conversions);
str_value.charset());
case NULL_VALUE: case NULL_VALUE:
return set_field_to_null_with_conversions(field, no_conversions); return set_field_to_null_with_conversions(field, no_conversions);
case DEFAULT_VALUE: case DEFAULT_VALUE:
...@@ -4107,6 +4106,28 @@ int Item_param::save_in_field(Field *field, bool no_conversions) ...@@ -4107,6 +4106,28 @@ int Item_param::save_in_field(Field *field, bool no_conversions)
} }
bool Item_param::can_return_value() const
{
// There's no "default". See comments in Item_param::save_in_field().
switch (state) {
case SHORT_DATA_VALUE:
case LONG_DATA_VALUE:
return true;
case IGNORE_VALUE:
case DEFAULT_VALUE:
invalid_default_param();
// fall through
case NULL_VALUE:
return false;
case NO_VALUE:
DBUG_ASSERT(0); // Should not be possible
return false;
}
DBUG_ASSERT(0); // Garbage
return false;
}
void Item_param::invalid_default_param() const void Item_param::invalid_default_param() const
{ {
my_message(ER_INVALID_DEFAULT_PARAM, my_message(ER_INVALID_DEFAULT_PARAM,
...@@ -4116,7 +4137,14 @@ void Item_param::invalid_default_param() const ...@@ -4116,7 +4137,14 @@ void Item_param::invalid_default_param() const
bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate) bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate)
{ {
if (state == TIME_VALUE) /*
LIMIT clause parameter should not call get_date()
For non-LIMIT parameters, handlers must be the same.
*/
DBUG_ASSERT(type_handler()->result_type() ==
value.type_handler()->result_type());
if (state == SHORT_DATA_VALUE &&
value.type_handler()->cmp_type() == TIME_RESULT)
{ {
*res= value.time; *res= value.time;
return 0; return 0;
...@@ -4125,157 +4153,117 @@ bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate) ...@@ -4125,157 +4153,117 @@ bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate)
} }
double Item_param::val_real() double Item_param::PValue::val_real() const
{ {
// There's no "default". See comments in Item_param::save_in_field(). switch (type_handler()->cmp_type()) {
switch (state) { case REAL_RESULT:
case REAL_VALUE: return real;
return value.real; case INT_RESULT:
case INT_VALUE: return (double) integer;
return (double) value.integer; case DECIMAL_RESULT:
case DECIMAL_VALUE:
{ {
double result; double result;
my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &result); my_decimal2double(E_DEC_FATAL_ERROR, &m_decimal, &result);
return result; return result;
} }
case STRING_VALUE: case STRING_RESULT:
case LONG_DATA_VALUE: return double_from_string_with_check(&m_string);
{ case TIME_RESULT:
return double_from_string_with_check(&str_value);
}
case TIME_VALUE:
/* /*
This works for example when user says SELECT ?+0.0 and supplies This works for example when user says SELECT ?+0.0 and supplies
time value for the placeholder. time value for the placeholder.
*/ */
return TIME_to_double(&value.time); return TIME_to_double(&time);
case IGNORE_VALUE: case ROW_RESULT:
case DEFAULT_VALUE: DBUG_ASSERT(0);
invalid_default_param(); break;
// fall through
case NULL_VALUE:
return 0.0;
case NO_VALUE:
DBUG_ASSERT(0); // Should not be possible
return 0.0;
} }
DBUG_ASSERT(0); // Garbage
return 0.0; return 0.0;
} }
longlong Item_param::val_int() longlong Item_param::PValue::val_int(const Type_std_attributes *attr) const
{ {
// There's no "default". See comments in Item_param::save_in_field(). switch (type_handler()->cmp_type()) {
switch (state) { case REAL_RESULT:
case REAL_VALUE: return (longlong) rint(real);
return (longlong) rint(value.real); case INT_RESULT:
case INT_VALUE: return integer;
return value.integer; case DECIMAL_RESULT:
case DECIMAL_VALUE:
{ {
longlong i; longlong i;
my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &i); my_decimal2int(E_DEC_FATAL_ERROR, &m_decimal, attr->unsigned_flag, &i);
return i; return i;
} }
case STRING_VALUE: case STRING_RESULT:
case LONG_DATA_VALUE: return longlong_from_string_with_check(&m_string);
{ case TIME_RESULT:
return longlong_from_string_with_check(&str_value); return (longlong) TIME_to_ulonglong(&time);
} case ROW_RESULT:
case TIME_VALUE: DBUG_ASSERT(0);
return (longlong) TIME_to_ulonglong(&value.time); break;
case IGNORE_VALUE:
case DEFAULT_VALUE:
invalid_default_param();
// fall through
case NULL_VALUE:
return 0;
case NO_VALUE:
DBUG_ASSERT(0); // Should not be possible
return 0;
} }
DBUG_ASSERT(0); // Garbage
return 0; return 0;
} }
my_decimal *Item_param::val_decimal(my_decimal *dec) my_decimal *Item_param::PValue::val_decimal(my_decimal *dec,
const Type_std_attributes *attr)
{ {
// There's no "default". See comments in Item_param::save_in_field(). switch (type_handler()->cmp_type()) {
switch (state) { case DECIMAL_RESULT:
case DECIMAL_VALUE: return &m_decimal;
return &decimal_value; case REAL_RESULT:
case REAL_VALUE: double2my_decimal(E_DEC_FATAL_ERROR, real, dec);
double2my_decimal(E_DEC_FATAL_ERROR, value.real, dec);
return dec; return dec;
case INT_VALUE: case INT_RESULT:
int2my_decimal(E_DEC_FATAL_ERROR, value.integer, unsigned_flag, dec); int2my_decimal(E_DEC_FATAL_ERROR, integer, attr->unsigned_flag, dec);
return dec; return dec;
case STRING_VALUE: case STRING_RESULT:
case LONG_DATA_VALUE: return decimal_from_string_with_check(dec, &m_string);
return decimal_from_string_with_check(dec, &str_value); case TIME_RESULT:
case TIME_VALUE: return TIME_to_my_decimal(&time, dec);
{ case ROW_RESULT:
return TIME_to_my_decimal(&value.time, dec); DBUG_ASSERT(0);
} break;
case IGNORE_VALUE:
case DEFAULT_VALUE:
invalid_default_param();
// fall through
case NULL_VALUE:
return 0;
case NO_VALUE:
DBUG_ASSERT(0); // Should not be possible
return 0;
} }
DBUG_ASSERT(0); // Gabrage
return 0; return 0;
} }
String *Item_param::val_str(String* str) String *Item_param::PValue::val_str(String *str,
{ const Type_std_attributes *attr)
// There's no "default". See comments in Item_param::save_in_field(). {
switch (state) { switch (type_handler()->cmp_type()) {
case STRING_VALUE: case STRING_RESULT:
case LONG_DATA_VALUE: return &m_string_ptr;
return &str_value_ptr; case REAL_RESULT:
case REAL_VALUE: str->set_real(real, NOT_FIXED_DEC, &my_charset_bin);
str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin);
return str; return str;
case INT_VALUE: case INT_RESULT:
str->set(value.integer, &my_charset_bin); str->set(integer, &my_charset_bin);
return str; return str;
case DECIMAL_VALUE: case DECIMAL_RESULT:
if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, if (my_decimal2string(E_DEC_FATAL_ERROR, &m_decimal, 0, 0, 0, str) <= 1)
0, 0, 0, str) <= 1)
return str; return str;
return NULL; return NULL;
case TIME_VALUE: case TIME_RESULT:
{ {
if (str->reserve(MAX_DATE_STRING_REP_LENGTH)) if (str->reserve(MAX_DATE_STRING_REP_LENGTH))
break; return NULL;
str->length((uint) my_TIME_to_str(&value.time, (char*) str->ptr(), str->length((uint) my_TIME_to_str(&time, (char*) str->ptr(),
decimals)); attr->decimals));
str->set_charset(&my_charset_bin); str->set_charset(&my_charset_bin);
return str; return str;
} }
case IGNORE_VALUE: case ROW_RESULT:
case DEFAULT_VALUE: DBUG_ASSERT(0);
invalid_default_param(); break;
// fall through
case NULL_VALUE:
return NULL;
case NO_VALUE:
DBUG_ASSERT(0); // Should not be possible
return NULL;
} }
DBUG_ASSERT(0); // Garbage
return NULL; return NULL;
} }
/** /**
Return Param item values in string format, for generating the dynamic Return Param item values in string format, for generating the dynamic
query used in update/binary logs. query used in update/binary logs.
...@@ -4287,32 +4275,31 @@ String *Item_param::val_str(String* str) ...@@ -4287,32 +4275,31 @@ String *Item_param::val_str(String* str)
that binary log contains wrong statement that binary log contains wrong statement
*/ */
const String *Item_param::query_val_str(THD *thd, String* str) const const String *Item_param::value_query_val_str(THD *thd, String *str) const
{ {
// There's no "default". See comments in Item_param::save_in_field(). switch (value.type_handler()->cmp_type()) {
switch (state) { case INT_RESULT:
case INT_VALUE:
str->set_int(value.integer, unsigned_flag, &my_charset_bin); str->set_int(value.integer, unsigned_flag, &my_charset_bin);
return str; return str;
case REAL_VALUE: case REAL_RESULT:
str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin); str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin);
return str; return str;
case DECIMAL_VALUE: case DECIMAL_RESULT:
if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, if (my_decimal2string(E_DEC_FATAL_ERROR, &value.m_decimal,
0, 0, 0, str) > 1) 0, 0, 0, str) > 1)
return &my_null_string; return &my_null_string;
return str; return str;
case TIME_VALUE: case TIME_RESULT:
{ {
static const uint32 typelen= 9; // "TIMESTAMP" is the longest type name static const uint32 typelen= 9; // "TIMESTAMP" is the longest type name
char *buf, *ptr; char *buf, *ptr;
str->length(0); str->length(0);
/* /*
TODO: in case of error we need to notify replication TODO: in case of error we need to notify replication
that binary log contains wrong statement that binary log contains wrong statement
*/ */
if (str->reserve(MAX_DATE_STRING_REP_LENGTH + 3 + typelen)) if (str->reserve(MAX_DATE_STRING_REP_LENGTH + 3 + typelen))
break; return NULL;
/* Create date string inplace */ /* Create date string inplace */
switch (value.time.time_type) { switch (value.time.time_type) {
...@@ -4338,15 +4325,29 @@ const String *Item_param::query_val_str(THD *thd, String* str) const ...@@ -4338,15 +4325,29 @@ const String *Item_param::query_val_str(THD *thd, String* str) const
str->length((uint32) (ptr - buf)); str->length((uint32) (ptr - buf));
return str; return str;
} }
case STRING_VALUE: case STRING_RESULT:
case LONG_DATA_VALUE:
{ {
str->length(0); str->length(0);
append_query_string(value.cs_info.character_set_client, str, append_query_string(value.cs_info.character_set_client, str,
str_value.ptr(), str_value.length(), value.m_string.ptr(), value.m_string.length(),
thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES); thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
return str; return str;
} }
case ROW_RESULT:
DBUG_ASSERT(0);
break;
}
return NULL;
}
const String *Item_param::query_val_str(THD *thd, String* str) const
{
// There's no "default". See comments in Item_param::save_in_field().
switch (state) {
case SHORT_DATA_VALUE:
case LONG_DATA_VALUE:
return value_query_val_str(thd, str);
case IGNORE_VALUE: case IGNORE_VALUE:
case DEFAULT_VALUE: case DEFAULT_VALUE:
return &my_default_string; return &my_default_string;
...@@ -4369,19 +4370,20 @@ const String *Item_param::query_val_str(THD *thd, String* str) const ...@@ -4369,19 +4370,20 @@ const String *Item_param::query_val_str(THD *thd, String* str) const
bool Item_param::convert_str_value(THD *thd) bool Item_param::convert_str_value(THD *thd)
{ {
bool rc= FALSE; bool rc= FALSE;
if (state == STRING_VALUE || state == LONG_DATA_VALUE) if ((state == SHORT_DATA_VALUE || state == LONG_DATA_VALUE) &&
value.type_handler()->cmp_type() == STRING_RESULT)
{ {
rc= value.cs_info.convert_if_needed(thd, &str_value); rc= value.cs_info.convert_if_needed(thd, &value.m_string);
/* Here str_value is guaranteed to be in final_character_set_of_str_value */ /* Here str_value is guaranteed to be in final_character_set_of_str_value */
/* /*
str_value_ptr is returned from val_str(). It must be not alloced str_value_ptr is returned from val_str(). It must be not alloced
to prevent it's modification by val_str() invoker. to prevent it's modification by val_str() invoker.
*/ */
str_value_ptr.set(str_value.ptr(), str_value.length(), value.m_string_ptr.set(value.m_string.ptr(), value.m_string.length(),
str_value.charset()); value.m_string.charset());
/* Synchronize item charset and length with value charset */ /* Synchronize item charset and length with value charset */
fix_charset_and_length_from_str_value(DERIVATION_COERCIBLE); fix_charset_and_length_from_str_value(value.m_string, DERIVATION_COERCIBLE);
} }
return rc; return rc;
} }
...@@ -4390,18 +4392,48 @@ bool Item_param::convert_str_value(THD *thd) ...@@ -4390,18 +4392,48 @@ bool Item_param::convert_str_value(THD *thd)
bool Item_param::basic_const_item() const bool Item_param::basic_const_item() const
{ {
DBUG_ASSERT(fixed || state == NO_VALUE); DBUG_ASSERT(fixed || state == NO_VALUE);
if (state == NO_VALUE || state == TIME_VALUE) if (state == NO_VALUE ||
(state == SHORT_DATA_VALUE && type_handler()->cmp_type() == TIME_RESULT))
return FALSE; return FALSE;
return TRUE; return TRUE;
} }
Item *Item_param::value_clone_item(THD *thd)
{
MEM_ROOT *mem_root= thd->mem_root;
switch (value.type_handler()->cmp_type()) {
case INT_RESULT:
return (unsigned_flag ?
new (mem_root) Item_uint(thd, name.str, value.integer, max_length) :
new (mem_root) Item_int(thd, name.str, value.integer, max_length));
case REAL_RESULT:
return new (mem_root) Item_float(thd, name.str, value.real, decimals,
max_length);
case DECIMAL_RESULT:
return 0; // Should create Item_decimal. See MDEV-11361.
case STRING_RESULT:
return new (mem_root) Item_string(thd, name.str,
value.m_string.c_ptr_quick(),
value.m_string.length(),
value.m_string.charset(),
collation.derivation,
collation.repertoire);
case TIME_RESULT:
break;
case ROW_RESULT:
DBUG_ASSERT(0);
break;
}
return 0;
}
/* see comments in the header file */ /* see comments in the header file */
Item * Item *
Item_param::clone_item(THD *thd) Item_param::clone_item(THD *thd)
{ {
MEM_ROOT *mem_root= thd->mem_root;
// There's no "default". See comments in Item_param::save_in_field(). // There's no "default". See comments in Item_param::save_in_field().
switch (state) { switch (state) {
case IGNORE_VALUE: case IGNORE_VALUE:
...@@ -4409,24 +4441,13 @@ Item_param::clone_item(THD *thd) ...@@ -4409,24 +4441,13 @@ Item_param::clone_item(THD *thd)
invalid_default_param(); invalid_default_param();
// fall through // fall through
case NULL_VALUE: case NULL_VALUE:
return new (mem_root) Item_null(thd, name.str); return new (thd->mem_root) Item_null(thd, name.str);
case INT_VALUE: case SHORT_DATA_VALUE:
return (unsigned_flag ?
new (mem_root) Item_uint(thd, name.str, value.integer, max_length) :
new (mem_root) Item_int(thd, name.str, value.integer, max_length));
case REAL_VALUE:
return new (mem_root) Item_float(thd, name.str, value.real, decimals,
max_length);
case DECIMAL_VALUE:
return 0; // Should create Item_decimal. See MDEV-11361.
case STRING_VALUE:
case LONG_DATA_VALUE: case LONG_DATA_VALUE:
return new (mem_root) Item_string(thd, name.str, str_value.c_ptr_quick(), {
str_value.length(), str_value.charset(), DBUG_ASSERT(type_handler()->cmp_type() == value.type_handler()->cmp_type());
collation.derivation, return value_clone_item(thd);
collation.repertoire); }
case TIME_VALUE:
return 0;
case NO_VALUE: case NO_VALUE:
return 0; return 0;
} }
...@@ -4435,6 +4456,24 @@ Item_param::clone_item(THD *thd) ...@@ -4435,6 +4456,24 @@ Item_param::clone_item(THD *thd)
} }
bool Item_param::value_eq(const Item *item, bool binary_cmp) const
{
switch (value.type_handler()->cmp_type()) {
case INT_RESULT:
return int_eq(value.integer, item);
case REAL_RESULT:
return real_eq(value.real, item);
case STRING_RESULT:
return str_eq(&value.m_string, item, binary_cmp);
case DECIMAL_RESULT:
case TIME_RESULT:
case ROW_RESULT:
break;
}
return false;
}
bool bool
Item_param::eq(const Item *item, bool binary_cmp) const Item_param::eq(const Item *item, bool binary_cmp) const
{ {
...@@ -4449,15 +4488,9 @@ Item_param::eq(const Item *item, bool binary_cmp) const ...@@ -4449,15 +4488,9 @@ Item_param::eq(const Item *item, bool binary_cmp) const
return false; return false;
case NULL_VALUE: case NULL_VALUE:
return null_eq(item); return null_eq(item);
case INT_VALUE: case SHORT_DATA_VALUE:
return int_eq(value.integer, item);
case REAL_VALUE:
return real_eq(value.real, item);
case STRING_VALUE:
case LONG_DATA_VALUE: case LONG_DATA_VALUE:
return str_eq(&str_value, item, binary_cmp); return value_eq(item, binary_cmp);
case DECIMAL_VALUE:
case TIME_VALUE:
case NO_VALUE: case NO_VALUE:
return false; return false;
} }
...@@ -4524,12 +4557,8 @@ Item_param::set_param_type_and_swap_value(Item_param *src) ...@@ -4524,12 +4557,8 @@ Item_param::set_param_type_and_swap_value(Item_param *src)
null_value= src->null_value; null_value= src->null_value;
state= src->state; state= src->state;
fixed= src->fixed; fixed= src->fixed;
value= src->value;
value.set_handler(src->value.type_handler()); value.swap(src->value);
decimal_value.swap(src->decimal_value);
str_value.swap(src->str_value);
str_value_ptr.swap(src->str_value_ptr);
} }
...@@ -4575,8 +4604,15 @@ Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it) ...@@ -4575,8 +4604,15 @@ Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it)
{ {
Item *arg= *it; Item *arg= *it;
struct st_value tmp; struct st_value tmp;
/*
The OUT parameter is bound to some data type.
It's important not to touch m_type_handler,
to make sure the next mysql_stmt_execute()
correctly fetches the value from the client-server protocol,
using set_param_func().
*/
if (arg->save_in_value(&tmp) || if (arg->save_in_value(&tmp) ||
arg->type_handler()->Item_param_set_from_value(thd, this, arg, &tmp)) set_value(thd, arg, &tmp, arg->type_handler()))
{ {
set_null(); set_null();
return false; return false;
......
...@@ -2184,7 +2184,8 @@ class Item_basic_value :public Item ...@@ -2184,7 +2184,8 @@ class Item_basic_value :public Item
uint repertoire() const { return MY_STRING_METADATA::repertoire; } uint repertoire() const { return MY_STRING_METADATA::repertoire; }
size_t char_length() const { return MY_STRING_METADATA::char_length; } size_t char_length() const { return MY_STRING_METADATA::char_length; }
}; };
void fix_charset_and_length_from_str_value(Derivation dv, Metadata metadata) void fix_charset_and_length(CHARSET_INFO *cs,
Derivation dv, Metadata metadata)
{ {
/* /*
We have to have a different max_length than 'length' here to We have to have a different max_length than 'length' here to
...@@ -2193,13 +2194,13 @@ class Item_basic_value :public Item ...@@ -2193,13 +2194,13 @@ class Item_basic_value :public Item
number of chars for a string of this type because we in Create_field:: number of chars for a string of this type because we in Create_field::
divide the max_length with mbmaxlen). divide the max_length with mbmaxlen).
*/ */
collation.set(str_value.charset(), dv, metadata.repertoire()); collation.set(cs, dv, metadata.repertoire());
fix_char_length(metadata.char_length()); fix_char_length(metadata.char_length());
decimals= NOT_FIXED_DEC; decimals= NOT_FIXED_DEC;
} }
void fix_charset_and_length_from_str_value(Derivation dv) void fix_charset_and_length_from_str_value(const String &str, Derivation dv)
{ {
fix_charset_and_length_from_str_value(dv, Metadata(&str_value)); fix_charset_and_length(str.charset(), dv, Metadata(&str));
} }
Item_basic_value(THD *thd): Item(thd) {} Item_basic_value(THD *thd): Item(thd) {}
/* /*
...@@ -3075,12 +3076,27 @@ class Item_null_result :public Item_null ...@@ -3075,12 +3076,27 @@ class Item_null_result :public Item_null
For example in case of 'SELECT ?' you'll get MYSQL_TYPE_STRING both For example in case of 'SELECT ?' you'll get MYSQL_TYPE_STRING both
in result set and placeholders metadata, no matter what type you will in result set and placeholders metadata, no matter what type you will
supply for this placeholder in mysql_stmt_execute. supply for this placeholder in mysql_stmt_execute.
Item_param has two Type_handler pointers,
which can point to different handlers:
1. In the Type_handler_hybrid_field_type member
It's initialized in:
- Item_param::setup_conversion(), for client-server PS protocol,
according to the bind type.
- Item_param::set_from_item(), for EXECUTE and EXECUTE IMMEDIATE,
according to the actual parameter data type.
2. In the "value" member.
It's initialized in:
- Item_param::set_param_func(), for client-server PS protocol.
- Item_param::set_from_item(), for EXECUTE and EXECUTE IMMEDIATE.
*/ */
class Item_param :public Item_basic_value, class Item_param :public Item_basic_value,
private Settable_routine_parameter, private Settable_routine_parameter,
public Rewritable_query_parameter, public Rewritable_query_parameter,
public Type_handler_hybrid_field_type, private Type_handler_hybrid_field_type,
public Type_geometry_attributes public Type_geometry_attributes
{ {
/* /*
...@@ -3126,9 +3142,8 @@ class Item_param :public Item_basic_value, ...@@ -3126,9 +3142,8 @@ class Item_param :public Item_basic_value,
*/ */
enum enum_item_param_state enum enum_item_param_state
{ {
NO_VALUE, NULL_VALUE, INT_VALUE, REAL_VALUE, NO_VALUE, NULL_VALUE, SHORT_DATA_VALUE, LONG_DATA_VALUE,
STRING_VALUE, TIME_VALUE, LONG_DATA_VALUE, DEFAULT_VALUE, IGNORE_VALUE
DECIMAL_VALUE, DEFAULT_VALUE, IGNORE_VALUE
} state; } state;
enum Type item_type; enum Type item_type;
...@@ -3182,24 +3197,11 @@ class Item_param :public Item_basic_value, ...@@ -3182,24 +3197,11 @@ class Item_param :public Item_basic_value,
} }
}; };
/*
A buffer for string and long data values. Historically all allocated
values returned from val_str() were treated as eligible to
modification. I. e. in some cases Item_func_concat can append it's
second argument to return value of the first one. Because of that we
can't return the original buffer holding string data from val_str(),
and have to have one buffer for data and another just pointing to
the data. This is the latter one and it's returned from val_str().
Can not be declared inside the union as it's not a POD type.
*/
String str_value_ptr;
bool m_empty_string_is_null; bool m_empty_string_is_null;
class PValue: public Type_handler_hybrid_field_type class PValue_simple
{ {
public: public:
PValue(): Type_handler_hybrid_field_type(&type_handler_null) {}
union union
{ {
longlong integer; longlong integer;
...@@ -3207,11 +3209,52 @@ class Item_param :public Item_basic_value, ...@@ -3207,11 +3209,52 @@ class Item_param :public Item_basic_value,
CONVERSION_INFO cs_info; CONVERSION_INFO cs_info;
MYSQL_TIME time; MYSQL_TIME time;
}; };
void swap(PValue_simple &other)
{
swap_variables(PValue_simple, *this, other);
}
};
class PValue: public Type_handler_hybrid_field_type,
public PValue_simple,
public Value_source
{
public:
PValue(): Type_handler_hybrid_field_type(&type_handler_null) {}
my_decimal m_decimal;
String m_string;
/*
A buffer for string and long data values. Historically all allocated
values returned from val_str() were treated as eligible to
modification. I. e. in some cases Item_func_concat can append it's
second argument to return value of the first one. Because of that we
can't return the original buffer holding string data from val_str(),
and have to have one buffer for data and another just pointing to
the data. This is the latter one and it's returned from val_str().
Can not be declared inside the union as it's not a POD type.
*/
String m_string_ptr;
void swap(PValue &other)
{
Type_handler_hybrid_field_type::swap(other);
PValue_simple::swap(other);
m_decimal.swap(other.m_decimal);
m_string.swap(other.m_string);
m_string_ptr.swap(other.m_string_ptr);
}
double val_real() const;
longlong val_int(const Type_std_attributes *attr) const;
my_decimal *val_decimal(my_decimal *dec, const Type_std_attributes *attr);
String *val_str(String *str, const Type_std_attributes *attr);
}; };
PValue value; PValue value;
my_decimal decimal_value; const String *value_query_val_str(THD *thd, String* str) const;
bool value_eq(const Item *item, bool binary_cmp) const;
Item *value_clone_item(THD *thd);
bool can_return_value() const;
public: public:
/* /*
...@@ -3237,10 +3280,22 @@ class Item_param :public Item_basic_value, ...@@ -3237,10 +3280,22 @@ class Item_param :public Item_basic_value,
return item_type; return item_type;
} }
double val_real(); double val_real()
longlong val_int(); {
my_decimal *val_decimal(my_decimal*); return can_return_value() ? value.val_real() : 0e0;
String *val_str(String*); }
longlong val_int()
{
return can_return_value() ? value.val_int(this) : 0;
}
my_decimal *val_decimal(my_decimal *dec)
{
return can_return_value() ? value.val_decimal(dec, this) : NULL;
}
String *val_str(String *str)
{
return can_return_value() ? value.val_str(str, this) : NULL;
}
bool get_date(MYSQL_TIME *tm, ulonglong fuzzydate); bool get_date(MYSQL_TIME *tm, ulonglong fuzzydate);
int save_in_field(Field *field, bool no_conversions); int save_in_field(Field *field, bool no_conversions);
...@@ -3283,11 +3338,29 @@ class Item_param :public Item_basic_value, ...@@ -3283,11 +3338,29 @@ class Item_param :public Item_basic_value,
*/ */
void set_param_func(uchar **pos, ulong len) void set_param_func(uchar **pos, ulong len)
{ {
value.type_handler()->Item_param_set_param_func(this, pos, len); /*
To avoid Item_param::set_xxx() asserting on data type mismatch,
we set the value type handler here:
- It can not be initialized yet after Item_param::setup_conversion().
- Also, for LIMIT clause parameters, the value type handler might have
changed from the real type handler to type_handler_longlong.
So here we'll restore it.
*/
const Type_handler *h= Item_param::type_handler();
value.set_handler(h);
h->Item_param_set_param_func(this, pos, len);
}
bool set_value(THD *thd, const Type_all_attributes *attr,
const st_value *val, const Type_handler *h)
{
value.set_handler(h); // See comments in set_param_func()
return h->Item_param_set_from_value(thd, this, attr, val);
} }
bool set_limit_clause_param(longlong nr) bool set_limit_clause_param(longlong nr)
{ {
value.set_handler(&type_handler_longlong);
set_int(nr, MY_INT64_NUM_DECIMAL_DIGITS); set_int(nr, MY_INT64_NUM_DECIMAL_DIGITS);
return !unsigned_flag && value.integer < 0; return !unsigned_flag && value.integer < 0;
} }
...@@ -3316,7 +3389,8 @@ class Item_param :public Item_basic_value, ...@@ -3316,7 +3389,8 @@ class Item_param :public Item_basic_value,
} }
bool has_int_value() const bool has_int_value() const
{ {
return state == INT_VALUE; return state == SHORT_DATA_VALUE &&
value.type_handler()->cmp_type() == INT_RESULT;
} }
/* /*
This method is used to make a copy of a basic constant item when This method is used to make a copy of a basic constant item when
...@@ -3571,7 +3645,7 @@ class Item_string :public Item_basic_constant ...@@ -3571,7 +3645,7 @@ class Item_string :public Item_basic_constant
protected: protected:
void fix_from_value(Derivation dv, const Metadata metadata) void fix_from_value(Derivation dv, const Metadata metadata)
{ {
fix_charset_and_length_from_str_value(dv, metadata); fix_charset_and_length(str_value.charset(), dv, metadata);
// it is constant => can be used without fix_fields (and frequently used) // it is constant => can be used without fix_fields (and frequently used)
fixed= 1; fixed= 1;
} }
......
...@@ -751,7 +751,6 @@ void Item_param::setup_conversion(THD *thd, uchar param_type) ...@@ -751,7 +751,6 @@ void Item_param::setup_conversion(THD *thd, uchar param_type)
if (!h) if (!h)
h= &type_handler_string; h= &type_handler_string;
set_handler(h); set_handler(h);
value.set_handler(h);
h->Item_param_setup_conversion(thd, this); h->Item_param_setup_conversion(thd, this);
} }
......
...@@ -5015,7 +5015,6 @@ bool Type_handler_real_result:: ...@@ -5015,7 +5015,6 @@ bool Type_handler_real_result::
{ {
param->unsigned_flag= attr->unsigned_flag; param->unsigned_flag= attr->unsigned_flag;
param->set_double(val->value.m_double); param->set_double(val->value.m_double);
param->set_handler(&type_handler_double);
return false; return false;
} }
...@@ -5027,8 +5026,7 @@ bool Type_handler_int_result:: ...@@ -5027,8 +5026,7 @@ bool Type_handler_int_result::
const st_value *val) const const st_value *val) const
{ {
param->unsigned_flag= attr->unsigned_flag; param->unsigned_flag= attr->unsigned_flag;
param->set_int(val->value.m_longlong, MY_INT64_NUM_DECIMAL_DIGITS); param->set_int(val->value.m_longlong, attr->max_length);
param->set_handler(&type_handler_longlong);
return false; return false;
} }
...@@ -5041,7 +5039,6 @@ bool Type_handler_decimal_result:: ...@@ -5041,7 +5039,6 @@ bool Type_handler_decimal_result::
{ {
param->unsigned_flag= attr->unsigned_flag; param->unsigned_flag= attr->unsigned_flag;
param->set_decimal(&val->m_decimal, attr->unsigned_flag); param->set_decimal(&val->m_decimal, attr->unsigned_flag);
param->set_handler(&type_handler_newdecimal);
return false; return false;
} }
...@@ -5058,7 +5055,6 @@ bool Type_handler_string_result:: ...@@ -5058,7 +5055,6 @@ bool Type_handler_string_result::
Exact value of max_length is not known unless data is converted to Exact value of max_length is not known unless data is converted to
charset of connection, so we have to set it later. charset of connection, so we have to set it later.
*/ */
param->set_handler(&type_handler_varchar);
return param->set_str(val->m_string.ptr(), val->m_string.length(), return param->set_str(val->m_string.ptr(), val->m_string.length(),
attr->collation.collation, attr->collation.collation,
attr->collation.collation); attr->collation.collation);
...@@ -5073,7 +5069,6 @@ bool Type_handler_temporal_result:: ...@@ -5073,7 +5069,6 @@ bool Type_handler_temporal_result::
{ {
param->unsigned_flag= attr->unsigned_flag; param->unsigned_flag= attr->unsigned_flag;
param->set_time(&val->value.m_time, attr->max_length, attr->decimals); param->set_time(&val->value.m_time, attr->max_length, attr->decimals);
param->set_handler(this);
return false; return false;
} }
...@@ -5087,7 +5082,6 @@ bool Type_handler_geometry:: ...@@ -5087,7 +5082,6 @@ bool Type_handler_geometry::
{ {
param->unsigned_flag= false; param->unsigned_flag= false;
param->setup_conversion_blob(thd); param->setup_conversion_blob(thd);
param->set_handler(&type_handler_geometry);
param->set_geometry_type(attr->uint_geometry_type()); param->set_geometry_type(attr->uint_geometry_type());
return param->set_str(val->m_string.ptr(), val->m_string.length(), return param->set_str(val->m_string.ptr(), val->m_string.length(),
&my_charset_bin, &my_charset_bin); &my_charset_bin, &my_charset_bin);
......
...@@ -2838,6 +2838,10 @@ class Type_handler_hybrid_field_type ...@@ -2838,6 +2838,10 @@ class Type_handler_hybrid_field_type
Type_handler_hybrid_field_type(const Type_handler_hybrid_field_type *other) Type_handler_hybrid_field_type(const Type_handler_hybrid_field_type *other)
:m_type_handler(other->m_type_handler) :m_type_handler(other->m_type_handler)
{ } { }
void swap(Type_handler_hybrid_field_type &other)
{
swap_variables(const Type_handler *, m_type_handler, other.m_type_handler);
}
const Type_handler *type_handler() const { return m_type_handler; } const Type_handler *type_handler() const { return m_type_handler; }
enum_field_types real_field_type() const enum_field_types real_field_type() const
{ {
......
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