Commit 452fed70 authored by Georgi Kodinov's avatar Georgi Kodinov
Browse files

Bug #37348: Crash in or immediately after JOIN::make_sum_func_list

      
The optimizer pulls up aggregate functions which should be aggregated in
an outer select. At some point it may substitute such a function for a field
in the temporary table. The setup_copy_fields function doesn't take this
into account and may overrun the copy_field buffer.
      
Fixed by filtering out the fields referenced through the specialized
reference for aggregates (Item_aggregate_ref).
Added an assertion to make sure bugs that cause similar discrepancy 
don't go undetected.
parent 0be194e5
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -1416,4 +1416,41 @@ SELECT AVG(a), CAST(AVG(a) AS DECIMAL) FROM t1;
AVG(a)	CAST(AVG(a) AS DECIMAL)
15	15
DROP TABLE t1;
CREATE TABLE derived1 (a bigint(21));
INSERT INTO derived1 VALUES (2);
CREATE TABLE D (
pk int(11) NOT NULL AUTO_INCREMENT,
int_nokey int(11) DEFAULT NULL,
int_key int(11) DEFAULT NULL,
filler blob,
PRIMARY KEY (pk),
KEY int_key (int_key)
);
INSERT INTO D VALUES 
(39,40,4,repeat('  X', 42)),
(43,56,4,repeat('  X', 42)),
(47,12,4,repeat('  X', 42)),
(71,28,4,repeat('  X', 42)),
(76,54,4,repeat('  X', 42)),
(83,45,4,repeat('  X', 42)),
(105,53,12,NULL);
SELECT 
(SELECT COUNT( int_nokey ) 
FROM derived1 AS X 
WHERE 
X.int_nokey < 61 
GROUP BY pk 
LIMIT 1) 
FROM D AS X 
WHERE X.int_key < 13  
GROUP BY int_nokey LIMIT 1;
(SELECT COUNT( int_nokey ) 
FROM derived1 AS X 
WHERE 
X.int_nokey < 61 
GROUP BY pk 
LIMIT 1)
1
DROP TABLE derived1;
DROP TABLE D;
End of 5.0 tests
+40 −0
Original line number Diff line number Diff line
@@ -933,5 +933,45 @@ SELECT AVG(a), CAST(AVG(a) AS DECIMAL) FROM t1;

DROP TABLE t1;

#
# Bug #37348: Crash in or immediately after JOIN::make_sum_func_list
#

CREATE TABLE derived1 (a bigint(21));
INSERT INTO derived1 VALUES (2);


CREATE TABLE D (
  pk int(11) NOT NULL AUTO_INCREMENT,
  int_nokey int(11) DEFAULT NULL,
  int_key int(11) DEFAULT NULL,
  filler blob,
  PRIMARY KEY (pk),
  KEY int_key (int_key)
);

INSERT INTO D VALUES 
  (39,40,4,repeat('  X', 42)),
  (43,56,4,repeat('  X', 42)),
  (47,12,4,repeat('  X', 42)),
  (71,28,4,repeat('  X', 42)),
  (76,54,4,repeat('  X', 42)),
  (83,45,4,repeat('  X', 42)),
  (105,53,12,NULL);

SELECT 
  (SELECT COUNT( int_nokey ) 
   FROM derived1 AS X 
   WHERE 
     X.int_nokey < 61 
   GROUP BY pk 
   LIMIT 1) 
FROM D AS X 
WHERE X.int_key < 13  
GROUP BY int_nokey LIMIT 1;

DROP TABLE derived1;
DROP TABLE D;

###
--echo End of 5.0 tests
+1 −0
Original line number Diff line number Diff line
@@ -1338,6 +1338,7 @@ class Item_aggregate_ref : public Item_ref
    else
      Item_ident::print(str, query_type);
  }
  virtual Ref_Type ref_type() { return AGGREGATE_REF; }
};


+1 −1
Original line number Diff line number Diff line
@@ -2126,7 +2126,7 @@ class Item_ref :public Item_ident
protected:
  void set_properties();
public:
  enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF };
  enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF, AGGREGATE_REF };
  Field *result_field;			 /* Save result here */
  Item **ref;
  Item_ref(Name_resolution_context *context_arg,
+10 −1
Original line number Diff line number Diff line
@@ -14804,6 +14804,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
  Item *pos;
  List_iterator_fast<Item> li(all_fields);
  Copy_field *copy= NULL;
  IF_DBUG(Copy_field *copy_start);
  res_selected_fields.empty();
  res_all_fields.empty();
  List_iterator_fast<Item> itr(res_all_fields);
@@ -14816,12 +14817,19 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
    goto err2;
  param->copy_funcs.empty();
  IF_DBUG(copy_start= copy);
  for (i= 0; (pos= li++); i++)
  {
    Field *field;
    uchar *tmp;
    Item *real_pos= pos->real_item();
    if (real_pos->type() == Item::FIELD_ITEM)
    /*
      Aggregate functions can be substituted for fields (by e.g. temp tables).
      We need to filter those substituted fields out.
    */
    if (real_pos->type() == Item::FIELD_ITEM &&
        !(real_pos != pos &&
          ((Item_ref *)pos)->ref_type() == Item_ref::AGGREGATE_REF))
    {
      Item_field *item;
      if (!(item= new Item_field(thd, ((Item_field*) real_pos))))
@@ -14868,6 +14876,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
	  goto err;
        if (copy)
        {
          DBUG_ASSERT (param->field_count > (uint) (copy - copy_start));
          copy->set(tmp, item->result_field);
          item->result_field->move_field(copy->to_ptr,copy->to_null_ptr,1);
#ifdef HAVE_purify