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;
This diff is collapsed.
...@@ -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