MDEV-16351 JSON_OBJECT() treats hybrid functions with boolean arguments as numbers

Now the boolean data type is preserved in hybrid functions and MIN/MAX,
so COALESCE(bool_expr,bool_expr) and MAX(bool_expr) are correctly
detected by JSON_OBJECT() as being boolean rather than numeric expressions.
This commit is contained in:
Alexander Barkov 2018-05-31 18:52:32 +04:00
parent 3ceb4a54a1
commit ffe83e8e7b
15 changed files with 277 additions and 14 deletions

View File

@ -768,3 +768,91 @@ json_length json_depnth
#
# End of 10.3 tests
#
#
# Start of 10.4 tests
#
#
# MDEV-16351 JSON_OBJECT() treats hybrid functions with boolean arguments as numbers
#
SELECT
JSON_OBJECT("cond", true) AS j1,
JSON_OBJECT("cond", COALESCE(true, false)) j2,
JSON_OBJECT("cond", COALESCE(COALESCE(true, false))) j3;
j1 {"cond": true}
j2 {"cond": true}
j3 {"cond": true}
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2),(3);
SELECT JSON_OBJECT('x',(SELECT MAX(a)=4 FROM t1));
JSON_OBJECT('x',(SELECT MAX(a)=4 FROM t1))
{"x": false}
SELECT JSON_OBJECT('x',(SELECT MAX(a)=3 FROM t1));
JSON_OBJECT('x',(SELECT MAX(a)=3 FROM t1))
{"x": true}
SELECT JSON_OBJECT('x',(SELECT MAX(a)=2 FROM t1));
JSON_OBJECT('x',(SELECT MAX(a)=2 FROM t1))
{"x": false}
SELECT JSON_OBJECT('x',MAX(a=4)) FROM t1;
JSON_OBJECT('x',MAX(a=4))
{"x": false}
SELECT JSON_OBJECT('x',MAX(a=3)) FROM t1;
JSON_OBJECT('x',MAX(a=3))
{"x": true}
SELECT JSON_OBJECT('x',MAX(a=2)) FROM t1;
JSON_OBJECT('x',MAX(a=2))
{"x": true}
SELECT JSON_OBJECT('x',(SELECT MAX(a=4) FROM t1));
JSON_OBJECT('x',(SELECT MAX(a=4) FROM t1))
{"x": false}
SELECT JSON_OBJECT('x',(SELECT MAX(a=3) FROM t1));
JSON_OBJECT('x',(SELECT MAX(a=3) FROM t1))
{"x": true}
SELECT JSON_OBJECT('x',(SELECT MAX(a=2) FROM t1));
JSON_OBJECT('x',(SELECT MAX(a=2) FROM t1))
{"x": true}
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a)=4 FROM t1))='{"x": true}' THEN a END;
a
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a)=4 FROM t1))='{"x": false}' THEN a END;
a
1
2
3
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a)=3 FROM t1))='{"x": true}' THEN a END;
a
1
2
3
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a)=3 FROM t1))='{"x": false}' THEN a END;
a
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a)=2 FROM t1))='{"x": true}' THEN a END;
a
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a)=2 FROM t1))='{"x": false}' THEN a END;
a
1
2
3
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a=4) FROM t1))='{"x": true}' THEN a END;
a
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a=4) FROM t1))='{"x": false}' THEN a END;
a
1
2
3
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a=3) FROM t1))='{"x": true}' THEN a END;
a
1
2
3
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a=3) FROM t1))='{"x": false}' THEN a END;
a
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a=2) FROM t1))='{"x": true}' THEN a END;
a
1
2
3
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a=2) FROM t1))='{"x": false}' THEN a END;
a
DROP TABLE t1;
#
# End of 10.4 tests
#

View File

@ -424,3 +424,53 @@ SELECT
--echo #
--echo # End of 10.3 tests
--echo #
--echo #
--echo # Start of 10.4 tests
--echo #
--echo #
--echo # MDEV-16351 JSON_OBJECT() treats hybrid functions with boolean arguments as numbers
--echo #
--vertical_results
SELECT
JSON_OBJECT("cond", true) AS j1,
JSON_OBJECT("cond", COALESCE(true, false)) j2,
JSON_OBJECT("cond", COALESCE(COALESCE(true, false))) j3;
--horizontal_results
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2),(3);
SELECT JSON_OBJECT('x',(SELECT MAX(a)=4 FROM t1));
SELECT JSON_OBJECT('x',(SELECT MAX(a)=3 FROM t1));
SELECT JSON_OBJECT('x',(SELECT MAX(a)=2 FROM t1));
SELECT JSON_OBJECT('x',MAX(a=4)) FROM t1;
SELECT JSON_OBJECT('x',MAX(a=3)) FROM t1;
SELECT JSON_OBJECT('x',MAX(a=2)) FROM t1;
SELECT JSON_OBJECT('x',(SELECT MAX(a=4) FROM t1));
SELECT JSON_OBJECT('x',(SELECT MAX(a=3) FROM t1));
SELECT JSON_OBJECT('x',(SELECT MAX(a=2) FROM t1));
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a)=4 FROM t1))='{"x": true}' THEN a END;
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a)=4 FROM t1))='{"x": false}' THEN a END;
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a)=3 FROM t1))='{"x": true}' THEN a END;
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a)=3 FROM t1))='{"x": false}' THEN a END;
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a)=2 FROM t1))='{"x": true}' THEN a END;
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a)=2 FROM t1))='{"x": false}' THEN a END;
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a=4) FROM t1))='{"x": true}' THEN a END;
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a=4) FROM t1))='{"x": false}' THEN a END;
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a=3) FROM t1))='{"x": true}' THEN a END;
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a=3) FROM t1))='{"x": false}' THEN a END;
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a=2) FROM t1))='{"x": true}' THEN a END;
SELECT * FROM t1 WHERE CASE WHEN JSON_OBJECT('x', (SELECT MAX(a=2) FROM t1))='{"x": false}' THEN a END;
DROP TABLE t1;
--echo #
--echo # End of 10.4 tests
--echo #

View File

@ -223,7 +223,7 @@ st_u
MULTIPOLYGON(((525400 18370,525000.9677614468 183300,525400 183300,525400 18370)),((525000 183300,525000 183700,525000.9677614468 183300,525000 183300)),((525265.58 183481.95,525263.95 183484.75,525260.7 183491.55,525276.79 183500,525278.39 183500.84,525278.63 183500.97,525280.98 183502.26,525283.17 183503.47,525289.11 183506.62,525296.42 183510.31,525296.57 183510.39,525298.67 183511.53,525302.81 183513.8,525304.5 183510.83,525307.85 183504.95,525304.45 183504.25,525301.75 183509.35,525283.55 183500,525282.2 183499.3,525282.3 183499.1,525280.35 183498.2,525275.5 183495.7,525276.5 183493.45,525278.97 183488.73,525265.58 183481.95),(525266.99 183484.33,525263.26 183491.55,525266.15 183493.04,525269.88 183485.82,525266.99 183484.33),(525272.06 183488.37,525268.94 183494.51,525271.94 183496.03,525275.06 183489.89,525272.06 183488.37)))
SET @a=0x0000000001030000000200000005000000000000000000000000000000000000000000000000002440000000000000000000000000000024400000000000002440000000000000000000000000000024400000000000000000000000000000000000000000000000000000F03F000000000000F03F0000000000000040000000000000F03F00000000000000400000000000000040000000000000F03F0000000000000040000000000000F03F000000000000F03F;
SELECT ASTEXT(TOUCHES(@a, GEOMFROMTEXT('point(0 0)'))) t;
ERROR HY000: Illegal parameter data type int for operation 'st_astext'
ERROR HY000: Illegal parameter data type boolean for operation 'st_astext'
SELECT astext(ST_UNION (
PolyFromText('POLYGON(( 2 2 ,3 2,2 7,2 2),( 0 0,8 2,1 9,0 0))'),
ExteriorRing( Envelope( MultiLineStringFromText('MULTILINESTRING((3 4,5 3),(3 0,0 5))')))));

View File

@ -4920,3 +4920,25 @@ ERROR HY000: Illegal parameter data type geometry for operation 'is_used_lock'
#
# End of 10.3 tests
#
#
# Start of 10.4 tests
#
#
# MDEV-16351 JSON_OBJECT() treats hybrid functions with boolean arguments as numbers
#
SELECT ST_SRID(TRUE);
ERROR HY000: Illegal parameter data type boolean for operation 'srid'
SELECT ST_SRID(COALESCE(TRUE,TRUE));
ERROR HY000: Illegal parameter data type boolean for operation 'srid'
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2),(3);
SELECT ST_SRID((SELECT MAX(a)>3 FROM t1));
ERROR HY000: Illegal parameter data type boolean for operation 'srid'
SELECT ST_SRID(MAX(a>3)) FROM t1;
ERROR HY000: Illegal parameter data type boolean for operation 'srid'
SELECT ST_SRID((SELECT MAX(a>3) FROM t1));
ERROR HY000: Illegal parameter data type boolean for operation 'srid'
DROP TABLE t1;
#
# End of 10.4 tests
#

View File

@ -2595,7 +2595,6 @@ SELECT ST_SRID(1);
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
SELECT ST_SRID('test');
--echo # Item_bool_func_args_geometry
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
@ -2990,3 +2989,30 @@ SELECT IS_USED_LOCK(POINT(1,1));
--echo #
--echo # End of 10.3 tests
--echo #
--echo #
--echo # Start of 10.4 tests
--echo #
--echo #
--echo # MDEV-16351 JSON_OBJECT() treats hybrid functions with boolean arguments as numbers
--echo #
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
SELECT ST_SRID(TRUE);
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
SELECT ST_SRID(COALESCE(TRUE,TRUE));
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2),(3);
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
SELECT ST_SRID((SELECT MAX(a)>3 FROM t1));
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
SELECT ST_SRID(MAX(a>3)) FROM t1;
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
SELECT ST_SRID((SELECT MAX(a>3) FROM t1));
DROP TABLE t1;
--echo #
--echo # End of 10.4 tests
--echo #

View File

@ -216,7 +216,7 @@ st_u
MULTIPOLYGON(((525400 18370,525000.9677614468 183300,525400 183300,525400 18370)),((525000 183300,525000 183700,525000.9677614468 183300,525000 183300)),((525265.58 183481.95,525263.95 183484.75,525260.7 183491.55,525276.79 183500,525278.39 183500.84,525278.63 183500.97,525280.98 183502.26,525283.17 183503.47,525289.11 183506.62,525296.42 183510.31,525296.57 183510.39,525298.67 183511.53,525302.81 183513.8,525304.5 183510.83,525307.85 183504.95,525304.45 183504.25,525301.75 183509.35,525283.55 183500,525282.2 183499.3,525282.3 183499.1,525280.35 183498.2,525275.5 183495.7,525276.5 183493.45,525278.97 183488.73,525265.58 183481.95),(525266.99 183484.33,525263.26 183491.55,525266.15 183493.04,525269.88 183485.82,525266.99 183484.33),(525272.06 183488.37,525268.94 183494.51,525271.94 183496.03,525275.06 183489.89,525272.06 183488.37)))
SET @a=0x0000000001030000000200000005000000000000000000000000000000000000000000000000002440000000000000000000000000000024400000000000002440000000000000000000000000000024400000000000000000000000000000000000000000000000000000F03F000000000000F03F0000000000000040000000000000F03F00000000000000400000000000000040000000000000F03F0000000000000040000000000000F03F000000000000F03F;
SELECT ST_ASTEXT(ST_TOUCHES(@a, ST_GEOMFROMTEXT('point(0 0)'))) t;
ERROR HY000: Illegal parameter data type int for operation 'st_astext'
ERROR HY000: Illegal parameter data type boolean for operation 'st_astext'
DROP TABLE IF EXISTS p1;
CREATE PROCEDURE p1(dist DOUBLE, geom TEXT)
BEGIN

View File

@ -917,6 +917,27 @@ const Type_handler *
Type_handler::aggregate_for_result_traditional(const Type_handler *a,
const Type_handler *b)
{
if (a == b)
{
/*
If two traditional handlers are equal, quickly return "a".
Some handlers (e.g. Type_handler_bool) pretend to be traditional,
but in fact they are not traditional in full extent, they are
only sub-types for now (and don't have a corresponding Field_xxx yet).
Here we preserve such handlers during aggregation.
As a result, COALESCE(true,true) preserves the "boolean" data type.
Need to do this conversion for deprecated data types,
similar to what field_type_merge_rules[][] does.
*/
switch (a->field_type()) {
case MYSQL_TYPE_DECIMAL: return &type_handler_newdecimal;
case MYSQL_TYPE_DATE: return &type_handler_newdate;
case MYSQL_TYPE_VAR_STRING: return &type_handler_varchar;
default: break;
}
return a;
}
enum_field_types ta= a->traditional_merge_field_type();
enum_field_types tb= b->traditional_merge_field_type();
enum_field_types res= field_types_merge_rules[merge_type2index(ta)]

View File

@ -918,6 +918,21 @@ public:
return type_handler()->field_type();
}
virtual const Type_handler *type_handler() const= 0;
/**
Detects if an Item has a fixed data type which is known
even before fix_fields().
Currently it's important only to find Items with a fixed boolean
data type. More item types can be marked in the future as having
a fixed data type (e.g. all literals, all fixed type functions, etc).
@retval NULL if the Item type is not known before fix_fields()
@retval the pointer to the data type handler, if the data type
is known before fix_fields().
*/
virtual const Type_handler *fixed_type_handler() const
{
return NULL;
}
virtual uint field_flags() const
{
return 0;
@ -1633,7 +1648,6 @@ public:
table during query processing (grouping and so on)
*/
virtual bool is_result_field() { return 0; }
virtual bool is_bool_type() { return false; }
virtual bool is_json_type() { return false; }
virtual bool is_bool_literal() const { return false; }
/* This is to handle printing of default values */
@ -3862,9 +3876,12 @@ public:
Item_bool(THD *thd, const char *str_arg, longlong i):
Item_int(thd, str_arg, i, 1) {}
Item_bool(THD *thd, bool i) :Item_int(thd, (longlong) i, 1) { }
bool is_bool_type() { return true; }
bool is_bool_literal() const { return true; }
Item *neg_transformer(THD *thd);
const Type_handler *type_handler() const
{ return &type_handler_bool; }
const Type_handler *fixed_type_handler() const
{ return &type_handler_bool; }
};

View File

@ -215,8 +215,8 @@ public:
Item_bool_func(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c) {}
Item_bool_func(THD *thd, List<Item> &list): Item_int_func(thd, list) { }
Item_bool_func(THD *thd, Item_bool_func *item) :Item_int_func(thd, item) {}
const Type_handler *type_handler() const { return &type_handler_long; }
bool is_bool_type() { return true; }
const Type_handler *type_handler() const { return &type_handler_bool; }
const Type_handler *fixed_type_handler() const { return &type_handler_bool; }
virtual CHARSET_INFO *compare_collation() const { return NULL; }
void fix_length_and_dec() { decimals=0; max_length=1; }
uint decimal_precision() const { return 1; }

View File

@ -1397,7 +1397,7 @@ null_return:
static int append_json_value(String *str, Item *item, String *tmp_val)
{
if (item->is_bool_type())
if (item->type_handler()->is_bool_type())
{
longlong v_int= item->val_int();
const char *t_f;

View File

@ -855,7 +855,9 @@ String *Item_nodeset_func_elementbyindex::val_nodeset(String *nodeset)
flt->pos,
size);
int index= (int) (args[1]->val_int()) - 1;
if (index >= 0 && (flt->pos == (uint) index || args[1]->is_bool_type()))
if (index >= 0 &&
(flt->pos == (uint) index ||
(args[1]->type_handler()->is_bool_type())))
((XPathFilter*)nodeset)->append_element(flt->num, pos++);
}
return nodeset;
@ -1803,7 +1805,8 @@ my_xpath_parse_AxisSpecifier_NodeTest_opt_Predicate_list(MY_XPATH *xpath)
xpath->item= nodeset2bool(xpath, xpath->item);
if (xpath->item->is_bool_type())
const Type_handler *fh;
if ((fh= xpath->item->fixed_type_handler()) && fh->is_bool_type())
{
xpath->context= new (xpath->thd->mem_root)
Item_nodeset_func_predicate(xpath->thd, prev_context,

View File

@ -13031,7 +13031,8 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
if (args[0] && args[1]) // this is a binary function or BETWEEN
{
DBUG_ASSERT(pred->is_bool_type());
DBUG_ASSERT(pred->fixed_type_handler());
DBUG_ASSERT(pred->fixed_type_handler()->is_bool_type());
Item_bool_func *bool_func= (Item_bool_func*) pred;
Field *field= min_max_arg_item->field;
if (!args[2]) // this is a binary function

View File

@ -9664,8 +9664,9 @@ Item *negate_expression(THD *thd, Item *expr)
{
/* it is NOT(NOT( ... )) */
Item *arg= ((Item_func *) expr)->arguments()[0];
const Type_handler *fh= arg->fixed_type_handler();
enum_parsing_place place= thd->lex->current_select->parsing_place;
if (arg->is_bool_type() || place == IN_WHERE || place == IN_HAVING)
if ((fh && fh->is_bool_type()) || place == IN_WHERE || place == IN_HAVING)
return arg;
/*
if it is not boolean function then we have to emulate value of

View File

@ -26,6 +26,7 @@ Type_handler_row type_handler_row;
Type_handler_null type_handler_null;
Type_handler_bool type_handler_bool;
Type_handler_tiny type_handler_tiny;
Type_handler_short type_handler_short;
Type_handler_long type_handler_long;
@ -452,6 +453,7 @@ const Name
Type_handler_set::m_name_set(STRING_WITH_LEN("set"));
const Name
Type_handler_bool::m_name_bool(STRING_WITH_LEN("boolean")),
Type_handler_tiny::m_name_tiny(STRING_WITH_LEN("tinyint")),
Type_handler_short::m_name_short(STRING_WITH_LEN("smallint")),
Type_handler_long::m_name_int(STRING_WITH_LEN("int")),
@ -2818,8 +2820,21 @@ bool Type_handler_typelib::
TYPELIB *typelib= NULL;
for (uint i= 0; i < nitems; i++)
{
if ((typelib= items[i]->get_typelib()))
break;
TYPELIB *typelib2;
if ((typelib2= items[i]->get_typelib()))
{
if (typelib)
{
/*
Two ENUM/SET columns found. We convert such combinations to VARCHAR.
This may change in the future to preserve ENUM/SET
if typelib definitions are equal.
*/
handler->set_handler(&type_handler_varchar);
return func->aggregate_attributes_string(func_name, items, nitems);
}
typelib= typelib2;
}
}
DBUG_ASSERT(typelib); // There must be at least one typelib
func->set_typelib(typelib);
@ -2977,6 +2992,13 @@ bool Type_handler_int_result::
}
bool Type_handler_bool::
Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
{
return Item_sum_hybrid_fix_length_and_dec_numeric(func, &type_handler_bool);
}
bool Type_handler_real_result::
Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
{

View File

@ -1125,6 +1125,7 @@ public:
virtual bool can_return_text() const { return true; }
virtual bool can_return_date() const { return true; }
virtual bool can_return_time() const { return true; }
virtual bool is_bool_type() const { return false; }
virtual bool is_general_purpose_string_type() const { return false; }
virtual uint Item_time_precision(Item *item) const;
virtual uint Item_datetime_precision(Item *item) const;
@ -2333,6 +2334,16 @@ public:
};
class Type_handler_bool: public Type_handler_long
{
static const Name m_name_bool;
public:
const Name name() const { return m_name_bool; }
bool is_bool_type() const { return true; }
bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const;
};
class Type_handler_longlong: public Type_handler_general_purpose_int
{
static const Name m_name_longlong;
@ -3615,6 +3626,7 @@ 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_blob type_handler_blob;
extern MYSQL_PLUGIN_IMPORT Type_handler_bool type_handler_bool;
extern MYSQL_PLUGIN_IMPORT Type_handler_tiny type_handler_tiny;
extern MYSQL_PLUGIN_IMPORT Type_handler_short type_handler_short;
extern MYSQL_PLUGIN_IMPORT Type_handler_int24 type_handler_int24;