mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
MDEV-9641 MDEV-9644 NULLIF assertions
* only copy args[0] to args[2] after fix_fields (when all item substitutions have already happened) * change QT_ITEM_FUNC_NULLIF_TO_CASE (that allows to print NULLIF as CASE) to QT_ITEM_ORIGINAL_FUNC_NULLIF (that prohibits it). So that NULLIF-to-CASE is allowed by default and only disabled explicitly for SHOW VIEW|FUNCTION|PROCEDURE and mysql_make_view. By default it is allowed (in particular in error messages and debug output, that can happen anytime before or after optimizer).
This commit is contained in:
@@ -1542,6 +1542,22 @@ nullif(count(col1),0)
|
|||||||
3
|
3
|
||||||
drop view v1;
|
drop view v1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
select nullif((select 1), (select 2));
|
||||||
|
nullif((select 1), (select 2))
|
||||||
|
1
|
||||||
|
create table t1 (f int);
|
||||||
|
insert into t1 values (1),(2);
|
||||||
|
select nullif( not f, 1 ) from t1;
|
||||||
|
nullif( not f, 1 )
|
||||||
|
0
|
||||||
|
0
|
||||||
|
drop table t1;
|
||||||
|
set names utf8;
|
||||||
|
create table t1 (f1 varchar(10));
|
||||||
|
insert into t1 values ('2015-12-31');
|
||||||
|
select power( timestamp( nullif( '2002-09-08', f1 ) ), 24 ) from t1;
|
||||||
|
ERROR 22003: DOUBLE value is out of range in 'pow(cast((case when '2002-09-08' = '2015-12-31' then NULL else '2002-09-08' end) as datetime(6)),24)'
|
||||||
|
drop table t1;
|
||||||
#
|
#
|
||||||
# End of 10.1 tests
|
# End of 10.1 tests
|
||||||
#
|
#
|
||||||
|
@@ -969,6 +969,25 @@ select nullif(count(col1),0) from t1;
|
|||||||
drop view v1;
|
drop view v1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# MDEV-9644 Assertion `args[0] == args[2] || thd->stmt_arena->is_stmt_execute()' failed in Item_func_nullif::fix_length_and_dec()
|
||||||
|
#
|
||||||
|
select nullif((select 1), (select 2));
|
||||||
|
create table t1 (f int);
|
||||||
|
insert into t1 values (1),(2);
|
||||||
|
select nullif( not f, 1 ) from t1;
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# MDEV-9641 Assertion `args[0] == args[2] || _current_thd()->lex->context_analysis_only' failed in Item_func_nullif::print(String*, enum_query_type)
|
||||||
|
#
|
||||||
|
set names utf8;
|
||||||
|
create table t1 (f1 varchar(10));
|
||||||
|
insert into t1 values ('2015-12-31');
|
||||||
|
--error ER_DATA_OUT_OF_RANGE
|
||||||
|
select power( timestamp( nullif( '2002-09-08', f1 ) ), 24 ) from t1;
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
--echo # End of 10.1 tests
|
--echo # End of 10.1 tests
|
||||||
--echo #
|
--echo #
|
||||||
|
@@ -2566,8 +2566,15 @@ void Item_func_nullif::update_used_tables()
|
|||||||
void
|
void
|
||||||
Item_func_nullif::fix_length_and_dec()
|
Item_func_nullif::fix_length_and_dec()
|
||||||
{
|
{
|
||||||
if (!args[2]) // Only false if EOM
|
/*
|
||||||
return;
|
If this is the first invocation of fix_length_and_dec(), create the
|
||||||
|
third argument as a copy of the first. This cannot be done before
|
||||||
|
fix_fields(), because fix_fields() might replace items,
|
||||||
|
for exampe NOT x --> x==0, or (SELECT 1) --> 1.
|
||||||
|
See also class Item_func_nullif declaration.
|
||||||
|
*/
|
||||||
|
if (arg_count == 2)
|
||||||
|
args[arg_count++]= args[0];
|
||||||
|
|
||||||
THD *thd= current_thd;
|
THD *thd= current_thd;
|
||||||
/*
|
/*
|
||||||
@@ -2706,7 +2713,7 @@ Item_func_nullif::fix_length_and_dec()
|
|||||||
m_cache= args[0]->cmp_type() == STRING_RESULT ?
|
m_cache= args[0]->cmp_type() == STRING_RESULT ?
|
||||||
new (thd->mem_root) Item_cache_str_for_nullif(thd, args[0]) :
|
new (thd->mem_root) Item_cache_str_for_nullif(thd, args[0]) :
|
||||||
Item_cache::get_cache(thd, args[0]);
|
Item_cache::get_cache(thd, args[0]);
|
||||||
m_cache->setup(current_thd, args[0]);
|
m_cache->setup(thd, args[0]);
|
||||||
m_cache->store(args[0]);
|
m_cache->store(args[0]);
|
||||||
m_cache->set_used_tables(args[0]->used_tables());
|
m_cache->set_used_tables(args[0]->used_tables());
|
||||||
thd->change_item_tree(&args[0], m_cache);
|
thd->change_item_tree(&args[0], m_cache);
|
||||||
@@ -2718,7 +2725,7 @@ Item_func_nullif::fix_length_and_dec()
|
|||||||
unsigned_flag= args[2]->unsigned_flag;
|
unsigned_flag= args[2]->unsigned_flag;
|
||||||
fix_char_length(args[2]->max_char_length());
|
fix_char_length(args[2]->max_char_length());
|
||||||
maybe_null=1;
|
maybe_null=1;
|
||||||
setup_args_and_comparator(current_thd, &cmp);
|
setup_args_and_comparator(thd, &cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2737,10 +2744,10 @@ void Item_func_nullif::print(String *str, enum_query_type query_type)
|
|||||||
Therefore, after equal field propagation args[0] and args[2] can point
|
Therefore, after equal field propagation args[0] and args[2] can point
|
||||||
to different items.
|
to different items.
|
||||||
*/
|
*/
|
||||||
if (!(query_type & QT_ITEM_FUNC_NULLIF_TO_CASE) || args[0] == args[2])
|
if ((query_type & QT_ITEM_ORIGINAL_FUNC_NULLIF) || args[0] == args[2])
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
If no QT_ITEM_FUNC_NULLIF_TO_CASE is requested,
|
If QT_ITEM_ORIGINAL_FUNC_NULLIF is requested,
|
||||||
that means we want the original NULLIF() representation,
|
that means we want the original NULLIF() representation,
|
||||||
e.g. when we are in:
|
e.g. when we are in:
|
||||||
SHOW CREATE {VIEW|FUNCTION|PROCEDURE}
|
SHOW CREATE {VIEW|FUNCTION|PROCEDURE}
|
||||||
@@ -2748,15 +2755,12 @@ void Item_func_nullif::print(String *str, enum_query_type query_type)
|
|||||||
The original representation is possible only if
|
The original representation is possible only if
|
||||||
args[0] and args[2] still point to the same Item.
|
args[0] and args[2] still point to the same Item.
|
||||||
|
|
||||||
The caller must pass call print() with QT_ITEM_FUNC_NULLIF_TO_CASE
|
The caller must never pass call print() with QT_ITEM_ORIGINAL_FUNC_NULLIF
|
||||||
if an expression has undergone some optimization
|
if an expression has undergone some optimization
|
||||||
(e.g. equal field propagation done in optimize_cond()) already and
|
(e.g. equal field propagation done in optimize_cond()) already and
|
||||||
NULLIF() potentially has two different representations of "a":
|
NULLIF() potentially has two different representations of "a":
|
||||||
- one "a" for comparison
|
- one "a" for comparison
|
||||||
- another "a" for the returned value!
|
- another "a" for the returned value!
|
||||||
|
|
||||||
Note, the EXPLAIN EXTENDED and EXPLAIN FORMAT=JSON routines
|
|
||||||
do pass QT_ITEM_FUNC_NULLIF_TO_CASE to print().
|
|
||||||
*/
|
*/
|
||||||
DBUG_ASSERT(args[0] == args[2] || current_thd->lex->context_analysis_only);
|
DBUG_ASSERT(args[0] == args[2] || current_thd->lex->context_analysis_only);
|
||||||
str->append(func_name());
|
str->append(func_name());
|
||||||
@@ -5789,7 +5793,7 @@ bool Item_func_not::fix_fields(THD *thd, Item **ref)
|
|||||||
args[0]->under_not(this);
|
args[0]->under_not(this);
|
||||||
if (args[0]->type() == FIELD_ITEM)
|
if (args[0]->type() == FIELD_ITEM)
|
||||||
{
|
{
|
||||||
/* replace "NOT <field>" with "<filed> == 0" */
|
/* replace "NOT <field>" with "<field> == 0" */
|
||||||
Query_arena backup, *arena;
|
Query_arena backup, *arena;
|
||||||
Item *new_item;
|
Item *new_item;
|
||||||
bool rc= TRUE;
|
bool rc= TRUE;
|
||||||
|
@@ -998,11 +998,18 @@ class Item_func_nullif :public Item_func_hybrid_field_type
|
|||||||
Item_cache *m_cache;
|
Item_cache *m_cache;
|
||||||
int compare();
|
int compare();
|
||||||
public:
|
public:
|
||||||
// Put "a" to args[0] for comparison and to args[2] for the returned value.
|
/*
|
||||||
|
Here we pass three arguments to the parent constructor, as NULLIF
|
||||||
|
is a three-argument function, it needs two copies of the first argument
|
||||||
|
(see above). But fix_fields() will be confused if we try to prepare the
|
||||||
|
same Item twice (if args[0]==args[2]), so we hide the third argument
|
||||||
|
(decrementing arg_count) and copy args[2]=args[0] again after fix_fields().
|
||||||
|
See also Item_func_nullif::fix_length_and_dec().
|
||||||
|
*/
|
||||||
Item_func_nullif(THD *thd, Item *a, Item *b):
|
Item_func_nullif(THD *thd, Item *a, Item *b):
|
||||||
Item_func_hybrid_field_type(thd, a, b, a),
|
Item_func_hybrid_field_type(thd, a, b, a),
|
||||||
m_cache(NULL)
|
m_cache(NULL)
|
||||||
{}
|
{ arg_count--; }
|
||||||
bool date_op(MYSQL_TIME *ltime, uint fuzzydate);
|
bool date_op(MYSQL_TIME *ltime, uint fuzzydate);
|
||||||
double real_op();
|
double real_op();
|
||||||
longlong int_op();
|
longlong int_op();
|
||||||
|
15
sql/mysqld.h
15
sql/mysqld.h
@@ -665,15 +665,16 @@ enum enum_query_type
|
|||||||
/// If NULLIF(a,b) should print itself as
|
/// If NULLIF(a,b) should print itself as
|
||||||
/// CASE WHEN a_for_comparison=b THEN NULL ELSE a_for_return_value END
|
/// CASE WHEN a_for_comparison=b THEN NULL ELSE a_for_return_value END
|
||||||
/// when "a" was replaced to two different items
|
/// when "a" was replaced to two different items
|
||||||
/// (e.g. by equal fields propagation in optimize_cond()).
|
/// (e.g. by equal fields propagation in optimize_cond())
|
||||||
/// The default behaviour is to print as NULLIF(a_for_return, b)
|
/// or always as NULLIF(a, b).
|
||||||
/// which should be Ok for SHOW CREATE {VIEW|PROCEDURE|FUNCTION}
|
/// The default behaviour is to use CASE syntax when
|
||||||
/// as they are not affected by WHERE optimization.
|
/// a_for_return_value is not the same as a_for_comparison.
|
||||||
QT_ITEM_FUNC_NULLIF_TO_CASE= (1 <<6),
|
/// SHOW CREATE {VIEW|PROCEDURE|FUNCTION} and other cases where the
|
||||||
|
/// original representation is required, should set this flag.
|
||||||
|
QT_ITEM_ORIGINAL_FUNC_NULLIF= (1 <<6),
|
||||||
|
|
||||||
/// This value means focus on readability, not on ability to parse back, etc.
|
/// This value means focus on readability, not on ability to parse back, etc.
|
||||||
QT_EXPLAIN= QT_TO_SYSTEM_CHARSET |
|
QT_EXPLAIN= QT_TO_SYSTEM_CHARSET |
|
||||||
QT_ITEM_FUNC_NULLIF_TO_CASE |
|
|
||||||
QT_ITEM_IDENT_SKIP_CURRENT_DATABASE |
|
QT_ITEM_IDENT_SKIP_CURRENT_DATABASE |
|
||||||
QT_ITEM_CACHE_WRAPPER_SKIP_DETAILS |
|
QT_ITEM_CACHE_WRAPPER_SKIP_DETAILS |
|
||||||
QT_ITEM_SUBSELECT_ID_ONLY,
|
QT_ITEM_SUBSELECT_ID_ONLY,
|
||||||
@@ -682,7 +683,7 @@ enum enum_query_type
|
|||||||
/// Be more detailed than QT_EXPLAIN.
|
/// Be more detailed than QT_EXPLAIN.
|
||||||
/// Perhaps we should eventually include QT_ITEM_IDENT_SKIP_CURRENT_DATABASE
|
/// Perhaps we should eventually include QT_ITEM_IDENT_SKIP_CURRENT_DATABASE
|
||||||
/// here, as it would give better readable results
|
/// here, as it would give better readable results
|
||||||
QT_EXPLAIN_EXTENDED= QT_TO_SYSTEM_CHARSET | QT_ITEM_FUNC_NULLIF_TO_CASE
|
QT_EXPLAIN_EXTENDED= QT_TO_SYSTEM_CHARSET
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@@ -3272,7 +3272,8 @@ sp_instr_set::print(String *str)
|
|||||||
}
|
}
|
||||||
str->qs_append(m_offset);
|
str->qs_append(m_offset);
|
||||||
str->qs_append(' ');
|
str->qs_append(' ');
|
||||||
m_value->print(str, QT_ORDINARY);
|
m_value->print(str, enum_query_type(QT_ORDINARY |
|
||||||
|
QT_ITEM_ORIGINAL_FUNC_NULLIF));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3304,9 +3305,11 @@ void
|
|||||||
sp_instr_set_trigger_field::print(String *str)
|
sp_instr_set_trigger_field::print(String *str)
|
||||||
{
|
{
|
||||||
str->append(STRING_WITH_LEN("set_trigger_field "));
|
str->append(STRING_WITH_LEN("set_trigger_field "));
|
||||||
trigger_field->print(str, QT_ORDINARY);
|
trigger_field->print(str, enum_query_type(QT_ORDINARY |
|
||||||
|
QT_ITEM_ORIGINAL_FUNC_NULLIF));
|
||||||
str->append(STRING_WITH_LEN(":="));
|
str->append(STRING_WITH_LEN(":="));
|
||||||
value->print(str, QT_ORDINARY);
|
value->print(str, enum_query_type(QT_ORDINARY |
|
||||||
|
QT_ITEM_ORIGINAL_FUNC_NULLIF));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3432,7 +3435,8 @@ sp_instr_jump_if_not::print(String *str)
|
|||||||
str->qs_append('(');
|
str->qs_append('(');
|
||||||
str->qs_append(m_cont_dest);
|
str->qs_append(m_cont_dest);
|
||||||
str->qs_append(STRING_WITH_LEN(") "));
|
str->qs_append(STRING_WITH_LEN(") "));
|
||||||
m_expr->print(str, QT_ORDINARY);
|
m_expr->print(str, enum_query_type(QT_ORDINARY |
|
||||||
|
QT_ITEM_ORIGINAL_FUNC_NULLIF));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3528,7 +3532,8 @@ sp_instr_freturn::print(String *str)
|
|||||||
str->qs_append(STRING_WITH_LEN("freturn "));
|
str->qs_append(STRING_WITH_LEN("freturn "));
|
||||||
str->qs_append((uint)m_type);
|
str->qs_append((uint)m_type);
|
||||||
str->qs_append(' ');
|
str->qs_append(' ');
|
||||||
m_value->print(str, QT_ORDINARY);
|
m_value->print(str, enum_query_type(QT_ORDINARY |
|
||||||
|
QT_ITEM_ORIGINAL_FUNC_NULLIF));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4000,7 +4005,8 @@ sp_instr_set_case_expr::print(String *str)
|
|||||||
str->qs_append(STRING_WITH_LEN(") "));
|
str->qs_append(STRING_WITH_LEN(") "));
|
||||||
str->qs_append(m_case_expr_id);
|
str->qs_append(m_case_expr_id);
|
||||||
str->qs_append(' ');
|
str->qs_append(' ');
|
||||||
m_case_expr->print(str, QT_ORDINARY);
|
m_case_expr->print(str, enum_query_type(QT_ORDINARY |
|
||||||
|
QT_ITEM_ORIGINAL_FUNC_NULLIF));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint
|
uint
|
||||||
|
@@ -2689,7 +2689,7 @@ void st_select_lex::print_order(String *str,
|
|||||||
{
|
{
|
||||||
if (order->counter_used)
|
if (order->counter_used)
|
||||||
{
|
{
|
||||||
if (query_type != QT_VIEW_INTERNAL)
|
if (!(query_type & QT_VIEW_INTERNAL))
|
||||||
{
|
{
|
||||||
char buffer[20];
|
char buffer[20];
|
||||||
size_t length= my_snprintf(buffer, 20, "%d", order->counter);
|
size_t length= my_snprintf(buffer, 20, "%d", order->counter);
|
||||||
|
@@ -2352,7 +2352,8 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff)
|
|||||||
We can't just use table->query, because our SQL_MODE may trigger
|
We can't just use table->query, because our SQL_MODE may trigger
|
||||||
a different syntax, like when ANSI_QUOTES is defined.
|
a different syntax, like when ANSI_QUOTES is defined.
|
||||||
*/
|
*/
|
||||||
table->view->unit.print(buff, QT_ORDINARY);
|
table->view->unit.print(buff, enum_query_type(QT_ORDINARY |
|
||||||
|
QT_ITEM_ORIGINAL_FUNC_NULLIF));
|
||||||
|
|
||||||
if (table->with_check != VIEW_CHECK_NONE)
|
if (table->with_check != VIEW_CHECK_NONE)
|
||||||
{
|
{
|
||||||
|
@@ -901,9 +901,11 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
|
|||||||
ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
|
ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
|
||||||
thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
|
thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
|
||||||
|
|
||||||
lex->unit.print(&view_query, QT_VIEW_INTERNAL);
|
lex->unit.print(&view_query, enum_query_type(QT_VIEW_INTERNAL |
|
||||||
lex->unit.print(&is_query,
|
QT_ITEM_ORIGINAL_FUNC_NULLIF));
|
||||||
enum_query_type(QT_TO_SYSTEM_CHARSET | QT_WITHOUT_INTRODUCERS));
|
lex->unit.print(&is_query, enum_query_type(QT_TO_SYSTEM_CHARSET |
|
||||||
|
QT_WITHOUT_INTRODUCERS |
|
||||||
|
QT_ITEM_ORIGINAL_FUNC_NULLIF));
|
||||||
|
|
||||||
thd->variables.sql_mode|= sql_mode;
|
thd->variables.sql_mode|= sql_mode;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user