Commit 02a4bbb4 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-11913 Split sp_get_item_value() into methods in Type_handler

This patch also fixes:
 MDEV-11815 SP variables of temporal data types do not replicate correctly
Temporal values are now printed in temporal literal format, with the
SQL-standard data type prefix:
  TIME'10:20:30', DATE'2001-01-01', TIMESTAMP'2001-01-01 10:20:30'
Previously temporal values were printed using the text string notation, e.g.
 _latin1'10:20:30' COLLATE latin1_swedish_ci, hence the bug.
parent 68235f2c
#
# MDEV-11815 SP variables of temporal data types do not replicate correctly
#
CREATE TABLE t1(a INT);
CREATE PROCEDURE p1()
BEGIN
DECLARE i INT DEFAULT 123;
DECLARE b8 BIT(8) DEFAULT 0x61;
DECLARE t0 TIME DEFAULT '01:01:01';
DECLARE t6 TIME(6) DEFAULT '01:01:01.123456';
DECLARE d DATE DEFAULT '2001-01-01';
DECLARE dt0 DATETIME DEFAULT '2001-01-01 01:01:01';
DECLARE dt6 DATETIME(6) DEFAULT '2001-01-01 01:01:01.123456';
DECLARE ts0 TIMESTAMP DEFAULT '2001-01-01 01:01:01';
DECLARE ts6 TIMESTAMP(6) DEFAULT '2001-01-01 01:01:01.123456';
INSERT INTO t1 VALUES (i=0x61);
INSERT INTO t1 VALUES (b8=0x61);
INSERT INTO t1 VALUES (t0=10101);
INSERT INTO t1 VALUES (t6=10101);
INSERT INTO t1 VALUES (d=20010101);
INSERT INTO t1 VALUES (dt0=20010101010101);
INSERT INTO t1 VALUES (dt6=20010101010101);
INSERT INTO t1 VALUES (ts0=20010101010101);
INSERT INTO t1 VALUES (ts6=20010101010101);
END;
$$
CALL p1;
DROP TABLE t1;
DROP PROCEDURE p1;
include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t1(a INT)
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`()
BEGIN
DECLARE i INT DEFAULT 123;
DECLARE b8 BIT(8) DEFAULT 0x61;
DECLARE t0 TIME DEFAULT '01:01:01';
DECLARE t6 TIME(6) DEFAULT '01:01:01.123456';
DECLARE d DATE DEFAULT '2001-01-01';
DECLARE dt0 DATETIME DEFAULT '2001-01-01 01:01:01';
DECLARE dt6 DATETIME(6) DEFAULT '2001-01-01 01:01:01.123456';
DECLARE ts0 TIMESTAMP DEFAULT '2001-01-01 01:01:01';
DECLARE ts6 TIMESTAMP(6) DEFAULT '2001-01-01 01:01:01.123456';
INSERT INTO t1 VALUES (i=0x61);
INSERT INTO t1 VALUES (b8=0x61);
INSERT INTO t1 VALUES (t0=10101);
INSERT INTO t1 VALUES (t6=10101);
INSERT INTO t1 VALUES (d=20010101);
INSERT INTO t1 VALUES (dt0=20010101010101);
INSERT INTO t1 VALUES (dt6=20010101010101);
INSERT INTO t1 VALUES (ts0=20010101010101);
INSERT INTO t1 VALUES (ts6=20010101010101);
END
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('i',123)=0x61)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('b8',_binary'a' COLLATE 'binary')=0x61)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('t0',TIME'01:01:01')=10101)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('t6',TIME'01:01:01.123456')=10101)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('d',DATE'2001-01-01')=20010101)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('dt0',TIMESTAMP'2001-01-01 01:01:01')=20010101010101)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('dt6',TIMESTAMP'2001-01-01 01:01:01.123456')=20010101010101)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('ts0',TIMESTAMP'2001-01-01 01:01:01')=20010101010101)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('ts6',TIMESTAMP'2001-01-01 01:01:01.123456')=20010101010101)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; DROP PROCEDURE p1
--source include/have_binlog_format_statement.inc
--disable_query_log
reset master; # get rid of previous tests binlog
--enable_query_log
--echo #
--echo # MDEV-11815 SP variables of temporal data types do not replicate correctly
--echo #
CREATE TABLE t1(a INT);
DELIMITER $$;
CREATE PROCEDURE p1()
BEGIN
DECLARE i INT DEFAULT 123;
DECLARE b8 BIT(8) DEFAULT 0x61;
DECLARE t0 TIME DEFAULT '01:01:01';
DECLARE t6 TIME(6) DEFAULT '01:01:01.123456';
DECLARE d DATE DEFAULT '2001-01-01';
DECLARE dt0 DATETIME DEFAULT '2001-01-01 01:01:01';
DECLARE dt6 DATETIME(6) DEFAULT '2001-01-01 01:01:01.123456';
DECLARE ts0 TIMESTAMP DEFAULT '2001-01-01 01:01:01';
DECLARE ts6 TIMESTAMP(6) DEFAULT '2001-01-01 01:01:01.123456';
INSERT INTO t1 VALUES (i=0x61);
INSERT INTO t1 VALUES (b8=0x61);
INSERT INTO t1 VALUES (t0=10101);
INSERT INTO t1 VALUES (t6=10101);
INSERT INTO t1 VALUES (d=20010101);
INSERT INTO t1 VALUES (dt0=20010101010101);
INSERT INTO t1 VALUES (dt6=20010101010101);
INSERT INTO t1 VALUES (ts0=20010101010101);
INSERT INTO t1 VALUES (ts6=20010101010101);
END;
$$
DELIMITER ;$$
CALL p1;
DROP TABLE t1;
DROP PROCEDURE p1;
--let $binlog_file = LAST
source include/show_binlog_events.inc;
include/master-slave.inc
[connection master]
#
# MDEV-11815 SP variables of temporal data types do not replicate correctly
#
connection master;
CREATE TABLE t1(a INT);
CREATE PROCEDURE p1()
BEGIN
DECLARE a TIME DEFAULT '01:01:01';
INSERT INTO t1 VALUES (a=10101);
END;
$$
CALL p1;
SELECT * FROM t1;
a
1
connection slave;
SELECT * FROM t1;
a
1
connection master;
DROP TABLE t1;
DROP PROCEDURE p1;
connection slave;
include/rpl_end.inc
--source include/have_binlog_format_statement.inc
--source include/master-slave.inc
--echo #
--echo # MDEV-11815 SP variables of temporal data types do not replicate correctly
--echo #
connection master;
CREATE TABLE t1(a INT);
DELIMITER $$;
CREATE PROCEDURE p1()
BEGIN
DECLARE a TIME DEFAULT '01:01:01';
INSERT INTO t1 VALUES (a=10101);
END;
$$
DELIMITER ;$$
CALL p1;
SELECT * FROM t1;
sync_slave_with_master;
SELECT * FROM t1;
connection master;
DROP TABLE t1;
DROP PROCEDURE p1;
sync_slave_with_master;
--source include/rpl_end.inc
...@@ -92,65 +92,6 @@ sp_map_item_type(enum enum_field_types type) ...@@ -92,65 +92,6 @@ sp_map_item_type(enum enum_field_types type)
} }
/**
Return a string representation of the Item value.
@param thd thread handle
@param str string buffer for representation of the value
@note
If the item has a string result type, the string is escaped
according to its character set.
@retval
NULL on error
@retval
non-NULL a pointer to valid a valid string on success
*/
static String *
sp_get_item_value(THD *thd, Item *item, String *str)
{
switch (item->result_type()) {
case REAL_RESULT:
case INT_RESULT:
case DECIMAL_RESULT:
if (item->field_type() != MYSQL_TYPE_BIT)
return item->val_str(str);
else {/* Bit type is handled as binary string */}
case STRING_RESULT:
{
String *result= item->val_str(str);
if (!result)
return NULL;
{
StringBuffer<STRING_BUFFER_USUAL_SIZE> buf(result->charset());
CHARSET_INFO *cs= thd->variables.character_set_client;
buf.append('_');
buf.append(result->charset()->csname);
if (cs->escape_with_backslash_is_dangerous)
buf.append(' ');
append_query_string(cs, &buf, result->ptr(), result->length(),
thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
buf.append(" COLLATE '");
buf.append(item->collation.collation->name);
buf.append('\'');
str->copy(buf);
return str;
}
}
case ROW_RESULT:
default:
return NULL;
}
}
bool Item_splocal::append_for_log(THD *thd, String *str) bool Item_splocal::append_for_log(THD *thd, String *str)
{ {
if (fix_fields(thd, NULL)) if (fix_fields(thd, NULL))
...@@ -165,7 +106,9 @@ bool Item_splocal::append_for_log(THD *thd, String *str) ...@@ -165,7 +106,9 @@ bool Item_splocal::append_for_log(THD *thd, String *str)
return true; return true;
StringBuffer<STRING_BUFFER_USUAL_SIZE> str_value_holder(&my_charset_latin1); StringBuffer<STRING_BUFFER_USUAL_SIZE> str_value_holder(&my_charset_latin1);
String *str_value= sp_get_item_value(thd, this_item(), &str_value_holder); Item *item= this_item();
String *str_value= item->type_handler()->print_item_value(thd, item,
&str_value_holder);
if (str_value) if (str_value)
return str->append(*str_value) || str->append(')'); return str->append(*str_value) || str->append(')');
else else
...@@ -1758,9 +1701,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, ...@@ -1758,9 +1701,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
if (arg_no) if (arg_no)
binlog_buf.append(','); binlog_buf.append(',');
str_value= sp_get_item_value(thd, nctx->get_item(arg_no), Item *item= nctx->get_item(arg_no);
&str_value_holder); str_value= item->type_handler()->print_item_value(thd, item,
&str_value_holder);
if (str_value) if (str_value)
binlog_buf.append(*str_value); binlog_buf.append(*str_value);
else else
......
...@@ -2050,3 +2050,108 @@ bool Type_handler_temporal_result:: ...@@ -2050,3 +2050,108 @@ bool Type_handler_temporal_result::
return func->get_date_native(ltime, fuzzydate); return func->get_date_native(ltime, fuzzydate);
} }
/***************************************************************************/
/**
Get a string representation of the Item value.
See sql_type.h for details.
*/
String *Type_handler_row::
print_item_value(THD *thd, Item *item, String *str) const
{
DBUG_ASSERT(0);
return NULL;
}
/**
Get a string representation of the Item value,
using the character string format with its charset and collation, e.g.
latin1 'string' COLLATE latin1_german2_ci
*/
String *Type_handler::
print_item_value_csstr(THD *thd, Item *item, String *str) const
{
String *result= item->val_str(str);
if (!result)
return NULL;
StringBuffer<STRING_BUFFER_USUAL_SIZE> buf(result->charset());
CHARSET_INFO *cs= thd->variables.character_set_client;
buf.append('_');
buf.append(result->charset()->csname);
if (cs->escape_with_backslash_is_dangerous)
buf.append(' ');
append_query_string(cs, &buf, result->ptr(), result->length(),
thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
buf.append(" COLLATE '");
buf.append(item->collation.collation->name);
buf.append('\'');
str->copy(buf);
return str;
}
String *Type_handler_numeric::
print_item_value(THD *thd, Item *item, String *str) const
{
return item->val_str(str);
}
String *Type_handler::
print_item_value_temporal(THD *thd, Item *item, String *str,
const Name &type_name, String *buf) const
{
String *result= item->val_str(buf);
return !result ||
str->realloc(type_name.length() + result->length() + 2) ||
str->copy(type_name.ptr(), type_name.length(), &my_charset_latin1) ||
str->append('\'') ||
str->append(result->ptr(), result->length()) ||
str->append('\'') ?
NULL :
str;
}
String *Type_handler_time_common::
print_item_value(THD *thd, Item *item, String *str) const
{
StringBuffer<MAX_TIME_FULL_WIDTH+1> buf;
return print_item_value_temporal(thd, item, str,
Name(C_STRING_WITH_LEN("TIME")), &buf);
}
String *Type_handler_date_common::
print_item_value(THD *thd, Item *item, String *str) const
{
StringBuffer<MAX_DATE_WIDTH+1> buf;
return print_item_value_temporal(thd, item, str,
Name(C_STRING_WITH_LEN("DATE")), &buf);
}
String *Type_handler_datetime_common::
print_item_value(THD *thd, Item *item, String *str) const
{
StringBuffer<MAX_DATETIME_FULL_WIDTH+1> buf;
return print_item_value_temporal(thd, item, str,
Name(C_STRING_WITH_LEN("TIMESTAMP")), &buf);
}
String *Type_handler_timestamp_common::
print_item_value(THD *thd, Item *item, String *str) const
{
StringBuffer<MAX_DATETIME_FULL_WIDTH+1> buf;
return print_item_value_temporal(thd, item, str,
Name(C_STRING_WITH_LEN("TIMESTAMP")), &buf);
}
/***************************************************************************/
...@@ -259,6 +259,9 @@ class Name: private LEX_CSTRING ...@@ -259,6 +259,9 @@ class Name: private LEX_CSTRING
class Type_handler class Type_handler
{ {
protected: protected:
String *print_item_value_csstr(THD *thd, Item *item, String *str) const;
String *print_item_value_temporal(THD *thd, Item *item, String *str,
const Name &type_name, String *buf) const;
void make_sort_key_longlong(uchar *to, void make_sort_key_longlong(uchar *to,
bool maybe_null, bool null_value, bool maybe_null, bool null_value,
bool unsigned_flag, bool unsigned_flag,
...@@ -347,6 +350,24 @@ class Type_handler ...@@ -347,6 +350,24 @@ class Type_handler
virtual uint32 max_display_length(const Item *item) const= 0; virtual uint32 max_display_length(const Item *item) const= 0;
virtual int Item_save_in_field(Item *item, Field *field, virtual int Item_save_in_field(Item *item, Field *field,
bool no_conversions) const= 0; bool no_conversions) const= 0;
/**
Return a string representation of the Item value.
@param thd thread handle
@param str string buffer for representation of the value
@note
If the item has a string result type, the string is escaped
according to its character set.
@retval
NULL on error
@retval
non-NULL a pointer to a a valid string on success
*/
virtual String *print_item_value(THD *thd, Item *item, String *str) const= 0;
/** /**
Check if Check if
WHERE expr=value AND expr=const WHERE expr=value AND expr=const
...@@ -481,6 +502,7 @@ class Type_handler_row: public Type_handler ...@@ -481,6 +502,7 @@ class Type_handler_row: public Type_handler
DBUG_ASSERT(0); DBUG_ASSERT(0);
return 1; return 1;
} }
String *print_item_value(THD *thd, Item *item, String *str) const;
bool can_change_cond_ref_to_const(Item_bool_func2 *target, bool can_change_cond_ref_to_const(Item_bool_func2 *target,
Item *target_expr, Item *target_value, Item *target_expr, Item *target_value,
Item_bool_func2 *source, Item_bool_func2 *source,
...@@ -585,6 +607,7 @@ class Type_handler_numeric: public Type_handler ...@@ -585,6 +607,7 @@ class Type_handler_numeric: public Type_handler
const Type_handler *handler) const Type_handler *handler)
const; const;
public: public:
String *print_item_value(THD *thd, Item *item, String *str) const;
double Item_func_min_max_val_real(Item_func_min_max *) const; double Item_func_min_max_val_real(Item_func_min_max *) const;
longlong Item_func_min_max_val_int(Item_func_min_max *) const; longlong Item_func_min_max_val_int(Item_func_min_max *) const;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
...@@ -788,6 +811,10 @@ class Type_handler_string_result: public Type_handler ...@@ -788,6 +811,10 @@ class Type_handler_string_result: public Type_handler
SORT_FIELD_ATTR *attr) const; SORT_FIELD_ATTR *attr) const;
uint32 max_display_length(const Item *item) const; uint32 max_display_length(const Item *item) const;
int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
String *print_item_value(THD *thd, Item *item, String *str) const
{
return print_item_value_csstr(thd, item, str);
}
bool can_change_cond_ref_to_const(Item_bool_func2 *target, bool can_change_cond_ref_to_const(Item_bool_func2 *target,
Item *target_expr, Item *target_value, Item *target_expr, Item *target_value,
Item_bool_func2 *source, Item_bool_func2 *source,
...@@ -934,6 +961,10 @@ class Type_handler_bit: public Type_handler_int_result ...@@ -934,6 +961,10 @@ class Type_handler_bit: public Type_handler_int_result
const Name name() const { return m_name_bit; } const Name name() const { return m_name_bit; }
enum_field_types field_type() const { return MYSQL_TYPE_BIT; } enum_field_types field_type() const { return MYSQL_TYPE_BIT; }
uint32 max_display_length(const Item *item) const; uint32 max_display_length(const Item *item) const;
String *print_item_value(THD *thd, Item *item, String *str) const
{
return print_item_value_csstr(thd, item, str);
}
Field *make_conversion_table_field(TABLE *, uint metadata, Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const; const Field *target) const;
}; };
...@@ -975,6 +1006,7 @@ class Type_handler_time_common: public Type_handler_temporal_result ...@@ -975,6 +1006,7 @@ class Type_handler_time_common: public Type_handler_temporal_result
enum_field_types field_type() const { return MYSQL_TYPE_TIME; } enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
const Type_handler *type_handler_for_comparison() const; const Type_handler *type_handler_for_comparison() const;
int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
String *print_item_value(THD *thd, Item *item, String *str) const;
bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
Item **items, uint nitems) const; Item **items, uint nitems) const;
cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
...@@ -1019,6 +1051,7 @@ class Type_handler_date_common: public Type_handler_temporal_with_date ...@@ -1019,6 +1051,7 @@ class Type_handler_date_common: public Type_handler_temporal_with_date
virtual ~Type_handler_date_common() {} virtual ~Type_handler_date_common() {}
const Name name() const { return m_name_date; } const Name name() const { return m_name_date; }
enum_field_types field_type() const { return MYSQL_TYPE_DATE; } enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
String *print_item_value(THD *thd, Item *item, String *str) const;
bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
Item **items, uint nitems) const; Item **items, uint nitems) const;
}; };
...@@ -1048,6 +1081,7 @@ class Type_handler_datetime_common: public Type_handler_temporal_with_date ...@@ -1048,6 +1081,7 @@ class Type_handler_datetime_common: public Type_handler_temporal_with_date
virtual ~Type_handler_datetime_common() {} virtual ~Type_handler_datetime_common() {}
const Name name() const { return m_name_datetime; } const Name name() const { return m_name_datetime; }
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
String *print_item_value(THD *thd, Item *item, String *str) const;
bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
Item **items, uint nitems) const; Item **items, uint nitems) const;
}; };
...@@ -1079,6 +1113,7 @@ class Type_handler_timestamp_common: public Type_handler_temporal_with_date ...@@ -1079,6 +1113,7 @@ class Type_handler_timestamp_common: public Type_handler_temporal_with_date
virtual ~Type_handler_timestamp_common() {} virtual ~Type_handler_timestamp_common() {}
const Name name() const { return m_name_timestamp; } const Name name() const { return m_name_timestamp; }
enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; } enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; }
String *print_item_value(THD *thd, Item *item, String *str) const;
bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
Item **items, uint nitems) const; Item **items, uint nitems) 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