Commit 21a58840 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-17776 CAST(x AS INTERVAL DAY_SECOND(N))

parent dde2ca4a
......@@ -208,6 +208,7 @@ void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type);
#define MAX_DATE_STRING_REP_LENGTH 30
#define AUTO_SEC_PART_DIGITS DECIMAL_NOT_SPECIFIED
int my_interval_DDhhmmssff_to_str(const MYSQL_TIME *, char *to, uint digits);
int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint digits);
int my_date_to_str(const MYSQL_TIME *l_time, char *to);
int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint digits);
......
This diff is collapsed.
......@@ -65,6 +65,7 @@ SELECT a, b, EXTRACT(MICROSECOND FROM a), EXTRACT(MICROSECOND FROM b) FROM t1 WH
--echo # Detailed results
SELECT
a,
CAST(a AS INTERVAL DAY_SECOND(6)) AS cidm,
EXTRACT(DAY FROM a) * 24 + EXTRACT(HOUR FROM a) AS dh,
EXTRACT(DAY_HOUR FROM a),
EXTRACT(DAY FROM a),
......@@ -75,6 +76,7 @@ SELECT
FROM t1;
SELECT
b,
CAST(b AS INTERVAL DAY_SECOND(6)) AS cidm,
EXTRACT(DAY FROM b) * 24 + EXTRACT(HOUR FROM b) AS dh,
EXTRACT(DAY_HOUR FROM b),
EXTRACT(DAY FROM b),
......@@ -105,6 +107,7 @@ DROP TABLE t1;
CREATE TABLE t1 (a VARCHAR(64));
INSERT INTO t1 VALUES ('');
SELECT a,
CAST(a AS INTERVAL DAY_SECOND(6)) AS cidm,
EXTRACT(DAY_HOUR FROM a),
EXTRACT(DAY_MINUTE FROM a),
EXTRACT(DAY_SECOND FROM a),
......@@ -246,5 +249,9 @@ INSERT INTO t1 VALUES
('01:02:03/'),
('20 10:20:30');
SELECT EXTRACT(DAY FROM a), EXTRACT(DAY_SECOND FROM a), a FROM t1;
SELECT
EXTRACT(DAY FROM a),
EXTRACT(DAY_SECOND FROM a), a,
CAST(a AS INTERVAL DAY_SECOND(6)) AS cidm
FROM t1;
DROP TABLE t1;
#
# Start of 10.4 tests
#
#
# MDEV-17776 CAST(x AS INTERVAL DAY_SECOND(N))
#
CREATE TABLE t1 (a VARCHAR(128));
INSERT INTO t1 VALUES
('00:00:00'),
('+00:00:01'),
('-00:00:01'),
('838:59:59'),
('839:00:00'),
('2018:01:02'),
('87649415:59:59'),
('3652058 23:59:59'),
('87649416:00:00'),
('3652059 00:00:00');
SELECT
EXTRACT(DAY FROM a) AS d,
EXTRACT(HOUR FROM a) AS h,
a,
CAST(a AS INTERVAL DAY_SECOND(6)) AS cast_itds
FROM t1;
d h a cast_itds
0 0 00:00:00 00:00:00.000000
0 0 +00:00:01 00:00:01.000000
0 0 -00:00:01 -00:00:01.000000
34 22 838:59:59 34 22:59:59.000000
34 23 839:00:00 34 23:00:00.000000
84 2 2018:01:02 84 02:01:02.000000
3652058 23 87649415:59:59 3652058 23:59:59.000000
3652058 23 3652058 23:59:59 3652058 23:59:59.000000
NULL NULL 87649416:00:00 NULL
NULL NULL 3652059 00:00:00 NULL
Warnings:
Warning 1292 Incorrect interval value: '87649416:00:00'
Warning 1292 Incorrect interval value: '87649416:00:00'
Warning 1292 Incorrect INTERVAL DAY TO SECOND value: '87649416:00:00'
Warning 1292 Incorrect interval value: '3652059 00:00:00'
Warning 1292 Incorrect interval value: '3652059 00:00:00'
Warning 1292 Incorrect INTERVAL DAY TO SECOND value: '3652059 00:00:00'
DROP TABLE t1;
CREATE TABLE t1 (a DECIMAL(32,9));
INSERT INTO t1 VALUES
(0),
(1),
(-1),
(8385959),
(8390000),
(20180102),
(876494155959),
(876494160000);
SELECT
EXTRACT(DAY FROM a) AS d,
EXTRACT(HOUR FROM a) AS h,
a,
CAST(a AS INTERVAL DAY_SECOND(6)) AS cast_itds
FROM t1;
d h a cast_itds
0 0 0.000000000 00:00:00.000000
0 0 1.000000000 00:00:01.000000
0 0 -1.000000000 -00:00:01.000000
34 22 8385959.000000000 34 22:59:59.000000
34 23 8390000.000000000 34 23:00:00.000000
84 2 20180102.000000000 84 02:01:02.000000
3652058 23 876494155959.000000000 3652058 23:59:59.000000
NULL NULL 876494160000.000000000 NULL
Warnings:
Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '0.000000000'
Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '1.000000000'
Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '-1.000000000'
Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '8385959.000000000'
Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '8390000.000000000'
Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '20180102.000000000'
Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '876494155959.000000000'
Warning 1292 Incorrect interval value: '876494160000.000000000' for column 'a' at row 8
Warning 1292 Incorrect interval value: '876494160000.000000000' for column 'a' at row 8
Warning 1292 Incorrect INTERVAL DAY TO SECOND value: '876494160000.000000000'
DROP TABLE t1;
#
# End of 10.4 tests
#
--echo #
--echo # Start of 10.4 tests
--echo #
--echo #
--echo # MDEV-17776 CAST(x AS INTERVAL DAY_SECOND(N))
--echo #
CREATE TABLE t1 (a VARCHAR(128));
INSERT INTO t1 VALUES
('00:00:00'),
('+00:00:01'),
('-00:00:01'),
('838:59:59'),
('839:00:00'),
('2018:01:02'),
('87649415:59:59'),
('3652058 23:59:59'),
('87649416:00:00'),
('3652059 00:00:00');
SELECT
EXTRACT(DAY FROM a) AS d,
EXTRACT(HOUR FROM a) AS h,
a,
CAST(a AS INTERVAL DAY_SECOND(6)) AS cast_itds
FROM t1;
DROP TABLE t1;
CREATE TABLE t1 (a DECIMAL(32,9));
INSERT INTO t1 VALUES
(0),
(1),
(-1),
(8385959),
(8390000),
(20180102),
(876494155959),
(876494160000);
SELECT
EXTRACT(DAY FROM a) AS d,
EXTRACT(HOUR FROM a) AS h,
a,
CAST(a AS INTERVAL DAY_SECOND(6)) AS cast_itds
FROM t1;
DROP TABLE t1;
--echo #
--echo # End of 10.4 tests
--echo #
......@@ -1465,6 +1465,46 @@ static char* fmt_number(uint val, char *out, uint digits)
}
static int my_mmssff_to_str(const MYSQL_TIME *ltime, char *to, uint fsp)
{
char *pos= to;
if (fsp == AUTO_SEC_PART_DIGITS)
fsp= ltime->second_part ? TIME_SECOND_PART_DIGITS : 0;
DBUG_ASSERT(fsp <= TIME_SECOND_PART_DIGITS);
pos= fmt_number(ltime->minute, pos, 2);
*pos++= ':';
pos= fmt_number(ltime->second, pos, 2);
if (fsp)
{
*pos++= '.';
pos= fmt_number((uint)sec_part_shift(ltime->second_part, fsp), pos, fsp);
}
return (int) (pos - to);
}
int my_interval_DDhhmmssff_to_str(const MYSQL_TIME *ltime, char *to, uint fsp)
{
uint hour= ltime->day * 24 + ltime->hour;
char *pos= to;
DBUG_ASSERT(!ltime->year);
DBUG_ASSERT(!ltime->month);
if(ltime->neg)
*pos++= '-';
if (hour >= 24)
{
pos= longlong10_to_str((longlong) hour / 24, pos, 10);
*pos++= ' ';
}
pos= fmt_number(hour % 24, pos, 2);
*pos++= ':';
pos+= my_mmssff_to_str(ltime, pos, fsp);
*pos= 0;
return (int) (pos-to);
}
/*
Functions to convert time/date/datetime value to a string,
using default format.
......@@ -1482,11 +1522,6 @@ int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
uint hour= day * 24 + l_time->hour;
char*pos= to;
if (digits == AUTO_SEC_PART_DIGITS)
digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0;
DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS);
if(l_time->neg)
*pos++= '-';
......@@ -1497,17 +1532,7 @@ int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
pos= fmt_number(hour, pos, 2);
*pos++= ':';
pos= fmt_number(l_time->minute, pos, 2);
*pos++= ':';
pos= fmt_number(l_time->second, pos, 2);
if (digits)
{
*pos++= '.';
pos= fmt_number((uint)sec_part_shift(l_time->second_part, digits),
pos, digits);
}
pos+= my_mmssff_to_str(l_time, pos, digits);
*pos= 0;
return (int) (pos-to);
}
......@@ -1529,12 +1554,6 @@ int my_date_to_str(const MYSQL_TIME *l_time, char *to)
int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
{
char *pos= to;
if (digits == AUTO_SEC_PART_DIGITS)
digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0;
DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS);
pos= fmt_number(l_time->year, pos, 4);
*pos++='-';
pos= fmt_number(l_time->month, pos, 2);
......@@ -1543,17 +1562,7 @@ int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
*pos++=' ';
pos= fmt_number(l_time->hour, pos, 2);
*pos++= ':';
pos= fmt_number(l_time->minute, pos, 2);
*pos++= ':';
pos= fmt_number(l_time->second, pos, 2);
if (digits)
{
*pos++='.';
pos= fmt_number((uint) sec_part_shift(l_time->second_part, digits), pos,
digits);
}
pos+= my_mmssff_to_str(l_time, pos, digits);
*pos= 0;
return (int)(pos - to);
}
......
......@@ -1106,6 +1106,24 @@ class Item_char_typecast :public Item_str_func
};
class Item_interval_DDhhmmssff_typecast :public Item_char_typecast
{
uint m_fsp;
public:
Item_interval_DDhhmmssff_typecast(THD *thd, Item *a, uint fsp)
:Item_char_typecast(thd, a,Interval_DDhhmmssff::max_char_length(fsp),
&my_charset_latin1),
m_fsp(fsp)
{ }
String *val_str(String *to)
{
Interval_DDhhmmssff it(current_thd, args[0]);
null_value= !it.is_valid_interval_DDhhmmssff();
return it.to_string(to, m_fsp);
}
};
class Item_date_typecast :public Item_datefunc
{
public:
......
......@@ -68,6 +68,8 @@ Type_handler_long_blob type_handler_long_blob;
Type_handler_blob type_handler_blob;
static Type_handler_blob_compressed type_handler_blob_compressed;
Type_handler_interval_DDhhmmssff type_handler_interval_DDhhmmssff;
#ifdef HAVE_SPATIAL
Type_handler_geometry type_handler_geometry;
#endif
......@@ -6568,6 +6570,20 @@ Item *Type_handler_long_blob::
return new (thd->mem_root) Item_char_typecast(thd, item, len, real_cs);
}
Item *Type_handler_interval_DDhhmmssff::
create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const
{
if (attr.decimals() > MAX_DATETIME_PRECISION)
{
wrong_precision_error(ER_TOO_BIG_PRECISION, item, attr.decimals(),
MAX_DATETIME_PRECISION);
return 0;
}
return new (thd->mem_root) Item_interval_DDhhmmssff_typecast(thd, item,
attr.decimals());
}
/***************************************************************************/
void Type_handler_string_result::Item_param_setup_conversion(THD *thd,
......
......@@ -954,6 +954,16 @@ class Interval_DDhhmmssff: public Temporal
{
return TIME_MAX_INTERVAL_HOUR;
}
static uint max_int_part_char_length()
{
// e.g. '+3652058 23:59:59'
return 1/*sign*/ + TIME_MAX_INTERVAL_DAY_CHAR_LENGTH + 1 + 8/*hh:mm:ss*/;
}
static uint max_char_length(uint fsp)
{
DBUG_ASSERT(fsp <= TIME_SECOND_PART_DIGITS);
return max_int_part_char_length() + (fsp ? 1 : 0) + fsp;
}
public:
Interval_DDhhmmssff(THD *thd, Status *st, bool push_warnings,
Item *item, ulong max_hour);
......@@ -975,6 +985,17 @@ class Interval_DDhhmmssff: public Temporal
{
return time_type == MYSQL_TIMESTAMP_NONE || is_valid_interval_DDhhmmssff();
}
String *to_string(String *str, uint dec) const
{
if (!is_valid_interval_DDhhmmssff())
return NULL;
str->set_charset(&my_charset_numeric);
if (!str->alloc(MAX_DATE_STRING_REP_LENGTH))
str->length(my_interval_DDhhmmssff_to_str(this,
const_cast<char*>(str->ptr()),
dec));
return str;
}
};
......@@ -5311,6 +5332,16 @@ class Type_handler_set: public Type_handler_typelib
};
// A pseudo type handler, mostly for test purposes for now
class Type_handler_interval_DDhhmmssff: public Type_handler_long_blob
{
public:
Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const;
};
/**
A handler for hybrid type functions, e.g.
COALESCE(), IF(), IFNULL(), NULLIF(), CASE,
......@@ -5443,6 +5474,9 @@ extern MYSQL_PLUGIN_IMPORT Type_handler_blob type_handler_blob;
extern MYSQL_PLUGIN_IMPORT Type_handler_medium_blob type_handler_medium_blob;
extern MYSQL_PLUGIN_IMPORT Type_handler_long_blob type_handler_long_blob;
extern MYSQL_PLUGIN_IMPORT Type_handler_interval_DDhhmmssff
type_handler_interval_DDhhmmssff;
class Type_aggregator
{
bool m_is_commutative;
......
......@@ -11703,6 +11703,10 @@ cast_type_temporal:
DATE_SYM { $$.set(&type_handler_newdate); }
| TIME_SYM opt_field_length { $$.set(&type_handler_time2, 0, $2); }
| DATETIME opt_field_length { $$.set(&type_handler_datetime2, 0, $2); }
| INTERVAL_SYM DAY_SECOND_SYM field_length
{
$$.set(&type_handler_interval_DDhhmmssff, 0, $3);
}
;
opt_expr_list:
......
......@@ -11737,6 +11737,10 @@ cast_type_temporal:
DATE_SYM { $$.set(&type_handler_newdate); }
| TIME_SYM opt_field_length { $$.set(&type_handler_time2, 0, $2); }
| DATETIME opt_field_length { $$.set(&type_handler_datetime2, 0, $2); }
| INTERVAL_SYM DAY_SECOND_SYM field_length
{
$$.set(&type_handler_interval_DDhhmmssff, 0, $3);
}
;
opt_expr_list:
......
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