diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result
index 2824e3061d3..96f5131ca17 100644
--- a/mysql-test/main/derived_cond_pushdown.result
+++ b/mysql-test/main/derived_cond_pushdown.result
@@ -21784,4 +21784,37 @@ select * from v2 where greatest(a, default(a));
 a	count(*)
 drop view v2, v1;
 drop table t1;
+#
+# MDEV-29360 Crash when pushing condition with always false IS NULL
+#   into derived contains constant TRUE/FALSE as subformula
+#
+create table t1 (id int not null);
+insert into t1 values (0),(1),(3);
+create table t2 select * from t1;
+create table t3 select * from t1;
+set optimizer_trace='enabled=on';
+select * from (select id  from t1 group by id) dt1, (select id from t2) dt2, t3
+where dt2.id = t3.id and dt1.id between 0 and (dt2.id is null);
+id	id	id
+0	0	0
+0	1	1
+0	3	3
+select json_extract((select trace from information_schema.optimizer_trace),
+'$**.grouping_field_transformer_for_where') as trace;
+trace
+NULL
+select * from (select id  from t1 union select id from t2) dt1,
+(select id from t2) dt2,
+t3
+where dt2.id = t3.id and dt1.id between 0 and (dt2.id is null);
+id	id	id
+0	0	0
+0	1	1
+0	3	3
+select json_extract((select trace from information_schema.optimizer_trace),
+'$**.grouping_field_transformer_for_where') as trace;
+trace
+NULL
+set optimizer_trace='enabled=off';
+drop table t1,t2,t3;
 # End of 10.11 tests
diff --git a/mysql-test/main/derived_cond_pushdown.test b/mysql-test/main/derived_cond_pushdown.test
index f00cdde5df2..06cface82f0 100644
--- a/mysql-test/main/derived_cond_pushdown.test
+++ b/mysql-test/main/derived_cond_pushdown.test
@@ -1,5 +1,6 @@
 --source include/have_sequence.inc
 --source include/default_optimizer_switch.inc
+--source include/not_embedded.inc
 let $no_pushdown= set statement optimizer_switch='condition_pushdown_for_derived=off' for;
 set @@join_buffer_size=256*1024;
 
@@ -4351,4 +4352,34 @@ select * from v2 where greatest(a, default(a));
 drop view v2, v1;
 drop table t1;
 
+--echo #
+--echo # MDEV-29360 Crash when pushing condition with always false IS NULL
+--echo #   into derived contains constant TRUE/FALSE as subformula
+--echo #
+
+create table t1 (id int not null);
+insert into t1 values (0),(1),(3);
+create table t2 select * from t1;
+create table t3 select * from t1;
+
+set optimizer_trace='enabled=on';
+
+select * from (select id  from t1 group by id) dt1, (select id from t2) dt2, t3
+  where dt2.id = t3.id and dt1.id between 0 and (dt2.id is null);
+
+select json_extract((select trace from information_schema.optimizer_trace),
+  '$**.grouping_field_transformer_for_where') as trace;
+
+select * from (select id  from t1 union select id from t2) dt1,
+              (select id from t2) dt2,
+              t3
+  where dt2.id = t3.id and dt1.id between 0 and (dt2.id is null);
+
+select json_extract((select trace from information_schema.optimizer_trace),
+  '$**.grouping_field_transformer_for_where') as trace;
+
+set optimizer_trace='enabled=off';
+
+drop table t1,t2,t3;
+
 --echo # End of 10.11 tests
diff --git a/mysql-test/main/derived_view.test b/mysql-test/main/derived_view.test
index 4c02d0fa906..faa2c862352 100644
--- a/mysql-test/main/derived_view.test
+++ b/mysql-test/main/derived_view.test
@@ -252,6 +252,8 @@ select * from (select * from v7 group by 1) tt;
 
 --echo join of above two
 explain extended select * from v6 join v7 on f2=f1;
+#select sin(0);
+#--optimizer_trace
 explain format=json select * from v6 join v7 on f2=f1;
 select * from v6 join v7 on f2=f1;
 
diff --git a/sql/item.cc b/sql/item.cc
index a3ec31ceea8..d696f118ddb 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -8134,7 +8134,13 @@ Item *Item_direct_view_ref::derived_field_transformer_for_where(THD *thd,
   {
     st_select_lex *sel= (st_select_lex *)arg;
     Item *producing_item= find_producing_item(this, sel);
-    DBUG_ASSERT (producing_item != NULL);
+    if (!producing_item)
+    {
+      // intentionally returning an unpushable item.
+      DBUG_ASSERT(!(this->marker & MARKER_SUBSTITUTION));
+      return this;
+    }
+    //DBUG_ASSERT (producing_item != NULL);
     return producing_item->deep_copy_with_checks(thd);
   }
   return (*ref);
@@ -8153,7 +8159,8 @@ Item *Item_field::grouping_field_transformer_for_where(THD *thd, uchar *arg)
       producing_clone->marker|= MARKER_SUBSTITUTION;
     return producing_clone;
   }
-  return this;
+  return this; // FIX2: todo comment!
+//  return nullptr;
 }
 
 
@@ -8171,7 +8178,14 @@ Item_direct_view_ref::grouping_field_transformer_for_where(THD *thd,
   st_select_lex *sel= (st_select_lex *)arg;
   Field_pair *gr_field= find_matching_field_pair(this,
                                                  sel->grouping_tmp_fields);
-  return gr_field->corresponding_item->deep_copy_with_checks(thd);
+  if (gr_field)
+    return gr_field->corresponding_item->deep_copy_with_checks(thd);
+  
+  // FIX2: return an item without MARKER_SUBSTITUTION which signifes we produced
+  // an item which cannot be pushed)
+  DBUG_ASSERT(!(this->marker & MARKER_SUBSTITUTION));
+  return this;
+ // return nullptr; // FIX2: so we return here and object without MARKER_SUBSTUTITION....
 }
 
 void Item_field::print(String *str, enum_query_type query_type)
@@ -8616,7 +8630,7 @@ Item* Item_ref::transform(THD *thd, Item_transformer transformer, uchar *arg)
 {
   DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
   DBUG_ASSERT((*ref) != NULL);
-
+  // psergey-todo: how about trying to use a matching item_equal member?
   /* Transform the object we are referencing. */
   Item *new_item= (*ref)->transform(thd, transformer, arg);
   if (!new_item)
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 5f2c471de7f..349354c4c70 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -5753,6 +5753,15 @@ void Item_func_isnull::print(String *str, enum_query_type query_type)
   str->append(STRING_WITH_LEN(" is null"));
 }
 
+Item *Item_func_isnull::grouping_field_transformer_for_where(THD *thd, uchar *arg)
+{
+  // Can we use other option and just assign args[0]=new Item_int(1) ?
+  if (used_tables_cache==0 && const_item_cache==1)
+  {
+    return new (thd->mem_root) Item_int(thd, 0);
+  }
+  return this;
+}
 
 bool Item_is_not_null_test::val_bool()
 {
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 753d939f656..fc8d12a414e 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -2859,6 +2859,8 @@ class Item_func_isnull :public Item_func_null_predicate
       const_item_cache= args[0]->const_item();
     }
   }
+  Item *grouping_field_transformer_for_where(THD *thd, uchar *arg) override;
+
   COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
                         bool top_level) override;
   table_map not_null_tables() const override { return 0; }
