Skip to content

Commit ff93b77

Browse files
committed
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).
1 parent 5a3a79c commit ff93b77

File tree

9 files changed

+87
-31
lines changed

9 files changed

+87
-31
lines changed

mysql-test/r/null.result

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,22 @@ nullif(count(col1),0)
15421542
3
15431543
drop view v1;
15441544
drop table t1;
1545+
select nullif((select 1), (select 2));
1546+
nullif((select 1), (select 2))
1547+
1
1548+
create table t1 (f int);
1549+
insert into t1 values (1),(2);
1550+
select nullif( not f, 1 ) from t1;
1551+
nullif( not f, 1 )
1552+
0
1553+
0
1554+
drop table t1;
1555+
set names utf8;
1556+
create table t1 (f1 varchar(10));
1557+
insert into t1 values ('2015-12-31');
1558+
select power( timestamp( nullif( '2002-09-08', f1 ) ), 24 ) from t1;
1559+
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)'
1560+
drop table t1;
15451561
#
15461562
# End of 10.1 tests
15471563
#

mysql-test/t/null.test

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,25 @@ select nullif(count(col1),0) from t1;
969969
drop view v1;
970970
drop table t1;
971971

972+
#
973+
# MDEV-9644 Assertion `args[0] == args[2] || thd->stmt_arena->is_stmt_execute()' failed in Item_func_nullif::fix_length_and_dec()
974+
#
975+
select nullif((select 1), (select 2));
976+
create table t1 (f int);
977+
insert into t1 values (1),(2);
978+
select nullif( not f, 1 ) from t1;
979+
drop table t1;
980+
981+
#
982+
# MDEV-9641 Assertion `args[0] == args[2] || _current_thd()->lex->context_analysis_only' failed in Item_func_nullif::print(String*, enum_query_type)
983+
#
984+
set names utf8;
985+
create table t1 (f1 varchar(10));
986+
insert into t1 values ('2015-12-31');
987+
--error ER_DATA_OUT_OF_RANGE
988+
select power( timestamp( nullif( '2002-09-08', f1 ) ), 24 ) from t1;
989+
drop table t1;
990+
972991
--echo #
973992
--echo # End of 10.1 tests
974993
--echo #

sql/item_cmpfunc.cc

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2566,8 +2566,15 @@ void Item_func_nullif::update_used_tables()
25662566
void
25672567
Item_func_nullif::fix_length_and_dec()
25682568
{
2569-
if (!args[2]) // Only false if EOM
2570-
return;
2569+
/*
2570+
If this is the first invocation of fix_length_and_dec(), create the
2571+
third argument as a copy of the first. This cannot be done before
2572+
fix_fields(), because fix_fields() might replace items,
2573+
for exampe NOT x --> x==0, or (SELECT 1) --> 1.
2574+
See also class Item_func_nullif declaration.
2575+
*/
2576+
if (arg_count == 2)
2577+
args[arg_count++]= args[0];
25712578

25722579
THD *thd= current_thd;
25732580
/*
@@ -2706,7 +2713,7 @@ Item_func_nullif::fix_length_and_dec()
27062713
m_cache= args[0]->cmp_type() == STRING_RESULT ?
27072714
new (thd->mem_root) Item_cache_str_for_nullif(thd, args[0]) :
27082715
Item_cache::get_cache(thd, args[0]);
2709-
m_cache->setup(current_thd, args[0]);
2716+
m_cache->setup(thd, args[0]);
27102717
m_cache->store(args[0]);
27112718
m_cache->set_used_tables(args[0]->used_tables());
27122719
thd->change_item_tree(&args[0], m_cache);
@@ -2718,7 +2725,7 @@ Item_func_nullif::fix_length_and_dec()
27182725
unsigned_flag= args[2]->unsigned_flag;
27192726
fix_char_length(args[2]->max_char_length());
27202727
maybe_null=1;
2721-
setup_args_and_comparator(current_thd, &cmp);
2728+
setup_args_and_comparator(thd, &cmp);
27222729
}
27232730

27242731

@@ -2737,26 +2744,23 @@ void Item_func_nullif::print(String *str, enum_query_type query_type)
27372744
Therefore, after equal field propagation args[0] and args[2] can point
27382745
to different items.
27392746
*/
2740-
if (!(query_type & QT_ITEM_FUNC_NULLIF_TO_CASE) || args[0] == args[2])
2747+
if ((query_type & QT_ITEM_ORIGINAL_FUNC_NULLIF) || args[0] == args[2])
27412748
{
27422749
/*
2743-
If no QT_ITEM_FUNC_NULLIF_TO_CASE is requested,
2750+
If QT_ITEM_ORIGINAL_FUNC_NULLIF is requested,
27442751
that means we want the original NULLIF() representation,
27452752
e.g. when we are in:
27462753
SHOW CREATE {VIEW|FUNCTION|PROCEDURE}
27472754
27482755
The original representation is possible only if
27492756
args[0] and args[2] still point to the same Item.
27502757
2751-
The caller must pass call print() with QT_ITEM_FUNC_NULLIF_TO_CASE
2758+
The caller must never pass call print() with QT_ITEM_ORIGINAL_FUNC_NULLIF
27522759
if an expression has undergone some optimization
27532760
(e.g. equal field propagation done in optimize_cond()) already and
27542761
NULLIF() potentially has two different representations of "a":
27552762
- one "a" for comparison
27562763
- another "a" for the returned value!
2757-
2758-
Note, the EXPLAIN EXTENDED and EXPLAIN FORMAT=JSON routines
2759-
do pass QT_ITEM_FUNC_NULLIF_TO_CASE to print().
27602764
*/
27612765
DBUG_ASSERT(args[0] == args[2] || current_thd->lex->context_analysis_only);
27622766
str->append(func_name());
@@ -5789,7 +5793,7 @@ bool Item_func_not::fix_fields(THD *thd, Item **ref)
57895793
args[0]->under_not(this);
57905794
if (args[0]->type() == FIELD_ITEM)
57915795
{
5792-
/* replace "NOT <field>" with "<filed> == 0" */
5796+
/* replace "NOT <field>" with "<field> == 0" */
57935797
Query_arena backup, *arena;
57945798
Item *new_item;
57955799
bool rc= TRUE;

sql/item_cmpfunc.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -998,11 +998,18 @@ class Item_func_nullif :public Item_func_hybrid_field_type
998998
Item_cache *m_cache;
999999
int compare();
10001000
public:
1001-
// Put "a" to args[0] for comparison and to args[2] for the returned value.
1001+
/*
1002+
Here we pass three arguments to the parent constructor, as NULLIF
1003+
is a three-argument function, it needs two copies of the first argument
1004+
(see above). But fix_fields() will be confused if we try to prepare the
1005+
same Item twice (if args[0]==args[2]), so we hide the third argument
1006+
(decrementing arg_count) and copy args[2]=args[0] again after fix_fields().
1007+
See also Item_func_nullif::fix_length_and_dec().
1008+
*/
10021009
Item_func_nullif(THD *thd, Item *a, Item *b):
10031010
Item_func_hybrid_field_type(thd, a, b, a),
10041011
m_cache(NULL)
1005-
{}
1012+
{ arg_count--; }
10061013
bool date_op(MYSQL_TIME *ltime, uint fuzzydate);
10071014
double real_op();
10081015
longlong int_op();

sql/mysqld.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -665,15 +665,16 @@ enum enum_query_type
665665
/// If NULLIF(a,b) should print itself as
666666
/// CASE WHEN a_for_comparison=b THEN NULL ELSE a_for_return_value END
667667
/// when "a" was replaced to two different items
668-
/// (e.g. by equal fields propagation in optimize_cond()).
669-
/// The default behaviour is to print as NULLIF(a_for_return, b)
670-
/// which should be Ok for SHOW CREATE {VIEW|PROCEDURE|FUNCTION}
671-
/// as they are not affected by WHERE optimization.
672-
QT_ITEM_FUNC_NULLIF_TO_CASE= (1 <<6),
668+
/// (e.g. by equal fields propagation in optimize_cond())
669+
/// or always as NULLIF(a, b).
670+
/// The default behaviour is to use CASE syntax when
671+
/// a_for_return_value is not the same as a_for_comparison.
672+
/// SHOW CREATE {VIEW|PROCEDURE|FUNCTION} and other cases where the
673+
/// original representation is required, should set this flag.
674+
QT_ITEM_ORIGINAL_FUNC_NULLIF= (1 <<6),
673675

674676
/// This value means focus on readability, not on ability to parse back, etc.
675677
QT_EXPLAIN= QT_TO_SYSTEM_CHARSET |
676-
QT_ITEM_FUNC_NULLIF_TO_CASE |
677678
QT_ITEM_IDENT_SKIP_CURRENT_DATABASE |
678679
QT_ITEM_CACHE_WRAPPER_SKIP_DETAILS |
679680
QT_ITEM_SUBSELECT_ID_ONLY,
@@ -682,7 +683,7 @@ enum enum_query_type
682683
/// Be more detailed than QT_EXPLAIN.
683684
/// Perhaps we should eventually include QT_ITEM_IDENT_SKIP_CURRENT_DATABASE
684685
/// here, as it would give better readable results
685-
QT_EXPLAIN_EXTENDED= QT_TO_SYSTEM_CHARSET | QT_ITEM_FUNC_NULLIF_TO_CASE
686+
QT_EXPLAIN_EXTENDED= QT_TO_SYSTEM_CHARSET
686687
};
687688

688689

sql/sp_head.cc

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3272,7 +3272,8 @@ sp_instr_set::print(String *str)
32723272
}
32733273
str->qs_append(m_offset);
32743274
str->qs_append(' ');
3275-
m_value->print(str, QT_ORDINARY);
3275+
m_value->print(str, enum_query_type(QT_ORDINARY |
3276+
QT_ITEM_ORIGINAL_FUNC_NULLIF));
32763277
}
32773278

32783279

@@ -3304,9 +3305,11 @@ void
33043305
sp_instr_set_trigger_field::print(String *str)
33053306
{
33063307
str->append(STRING_WITH_LEN("set_trigger_field "));
3307-
trigger_field->print(str, QT_ORDINARY);
3308+
trigger_field->print(str, enum_query_type(QT_ORDINARY |
3309+
QT_ITEM_ORIGINAL_FUNC_NULLIF));
33083310
str->append(STRING_WITH_LEN(":="));
3309-
value->print(str, QT_ORDINARY);
3311+
value->print(str, enum_query_type(QT_ORDINARY |
3312+
QT_ITEM_ORIGINAL_FUNC_NULLIF));
33103313
}
33113314

33123315
/*
@@ -3432,7 +3435,8 @@ sp_instr_jump_if_not::print(String *str)
34323435
str->qs_append('(');
34333436
str->qs_append(m_cont_dest);
34343437
str->qs_append(STRING_WITH_LEN(") "));
3435-
m_expr->print(str, QT_ORDINARY);
3438+
m_expr->print(str, enum_query_type(QT_ORDINARY |
3439+
QT_ITEM_ORIGINAL_FUNC_NULLIF));
34363440
}
34373441

34383442

@@ -3528,7 +3532,8 @@ sp_instr_freturn::print(String *str)
35283532
str->qs_append(STRING_WITH_LEN("freturn "));
35293533
str->qs_append((uint)m_type);
35303534
str->qs_append(' ');
3531-
m_value->print(str, QT_ORDINARY);
3535+
m_value->print(str, enum_query_type(QT_ORDINARY |
3536+
QT_ITEM_ORIGINAL_FUNC_NULLIF));
35323537
}
35333538

35343539
/*
@@ -4000,7 +4005,8 @@ sp_instr_set_case_expr::print(String *str)
40004005
str->qs_append(STRING_WITH_LEN(") "));
40014006
str->qs_append(m_case_expr_id);
40024007
str->qs_append(' ');
4003-
m_case_expr->print(str, QT_ORDINARY);
4008+
m_case_expr->print(str, enum_query_type(QT_ORDINARY |
4009+
QT_ITEM_ORIGINAL_FUNC_NULLIF));
40044010
}
40054011

40064012
uint

sql/sql_lex.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2689,7 +2689,7 @@ void st_select_lex::print_order(String *str,
26892689
{
26902690
if (order->counter_used)
26912691
{
2692-
if (query_type != QT_VIEW_INTERNAL)
2692+
if (!(query_type & QT_VIEW_INTERNAL))
26932693
{
26942694
char buffer[20];
26952695
size_t length= my_snprintf(buffer, 20, "%d", order->counter);

sql/sql_show.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2352,7 +2352,8 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff)
23522352
We can't just use table->query, because our SQL_MODE may trigger
23532353
a different syntax, like when ANSI_QUOTES is defined.
23542354
*/
2355-
table->view->unit.print(buff, QT_ORDINARY);
2355+
table->view->unit.print(buff, enum_query_type(QT_ORDINARY |
2356+
QT_ITEM_ORIGINAL_FUNC_NULLIF));
23562357

23572358
if (table->with_check != VIEW_CHECK_NONE)
23582359
{

sql/sql_view.cc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -901,9 +901,11 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
901901
ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
902902
thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
903903

904-
lex->unit.print(&view_query, QT_VIEW_INTERNAL);
905-
lex->unit.print(&is_query,
906-
enum_query_type(QT_TO_SYSTEM_CHARSET | QT_WITHOUT_INTRODUCERS));
904+
lex->unit.print(&view_query, enum_query_type(QT_VIEW_INTERNAL |
905+
QT_ITEM_ORIGINAL_FUNC_NULLIF));
906+
lex->unit.print(&is_query, enum_query_type(QT_TO_SYSTEM_CHARSET |
907+
QT_WITHOUT_INTRODUCERS |
908+
QT_ITEM_ORIGINAL_FUNC_NULLIF));
907909

908910
thd->variables.sql_mode|= sql_mode;
909911
}

0 commit comments

Comments
 (0)