From 5b71281467c85dc705da30f0fe4ebc094f0ff983 Mon Sep 17 00:00:00 2001
From: unknown <kaa@polly.local>
Date: Thu, 14 Dec 2006 20:58:07 +0300
Subject: [PATCH] Fix for bug #24117 "server crash on a FETCH with a cursor on
 a table which is not in the table cache"

Problem:
When creating a temporary field for a temporary table in create_tmp_field_from_field(), a resulting field is created as an exact copy of an original one (in Field::new_field()). However, Field_enum and Field_set contain a pointer (typelib) to memory allocated in the parent table's MEM_ROOT, which under some circumstances may be deallocated later by the time a temporary table is used.

Solution:
Override the new_field() method for Field_enum and Field_set and create a separate copy of the typelib structure in there.


include/typelib.h:
  Added copy_typelib() declaration
mysql-test/r/sp.result:
  Added a testcase for bug #24117 "server crash on a FETCH with a cursor on a table which is not in the table cache"
mysql-test/t/sp.test:
  Added a testcase for bug #24117 "server crash on a FETCH with a cursor on a table which is not in the table cache"
mysys/typelib.c:
  Added copy_typelib() definition
sql/field.cc:
  Create a copy of the internal 'typelib' structure when copying Field_enum of Field_set objects.
sql/field.h:
  Override new_field method in Field_enum (and Field_set) to copy the typelib structure.
---
 include/typelib.h      |  3 +++
 mysql-test/r/sp.result | 16 +++++++++++++
 mysql-test/t/sp.test   | 24 ++++++++++++++++++++
 mysys/typelib.c        | 51 ++++++++++++++++++++++++++++++++++++++++++
 sql/field.cc           | 10 +++++++++
 sql/field.h            |  1 +
 6 files changed, 105 insertions(+)

diff --git a/include/typelib.h b/include/typelib.h
index 4d6a90ad51e..fe19f1001d4 100644
--- a/include/typelib.h
+++ b/include/typelib.h
@@ -18,6 +18,8 @@
 #ifndef _typelib_h
 #define _typelib_h
 
+#include "my_alloc.h"
+
 typedef struct st_typelib {	/* Different types saved here */
   unsigned int count;		/* How many types */
   const char *name;		/* Name of typelib */
@@ -28,6 +30,7 @@ typedef struct st_typelib {	/* Different types saved here */
 extern int find_type(char *x,TYPELIB *typelib,unsigned int full_name);
 extern void make_type(char *to,unsigned int nr,TYPELIB *typelib);
 extern const char *get_type(TYPELIB *typelib,unsigned int nr);
+extern TYPELIB *copy_typelib(MEM_ROOT *root, TYPELIB *from);
 
 extern TYPELIB sql_protocol_typelib;
 
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result
index 9db4325aea2..793abb417fb 100644
--- a/mysql-test/r/sp.result
+++ b/mysql-test/r/sp.result
@@ -5708,4 +5708,20 @@ DROP TABLE bug23760, bug23760_log|
 DROP PROCEDURE bug23760_update_log|
 DROP PROCEDURE bug23760_test_row_count|
 DROP FUNCTION bug23760_rc_test|
+DROP PROCEDURE IF EXISTS bug24117|
+DROP TABLE IF EXISTS t3|
+CREATE TABLE t3(c1 ENUM('abc'))|
+INSERT INTO t3 VALUES('abc')|
+CREATE PROCEDURE bug24117()
+BEGIN
+DECLARE t3c1 ENUM('abc');
+DECLARE mycursor CURSOR FOR SELECT c1 FROM t3;
+OPEN mycursor;
+FLUSH TABLES;
+FETCH mycursor INTO t3c1;
+CLOSE mycursor;
+END|
+CALL bug24117()|
+DROP PROCEDURE bug24117|
+DROP TABLE t3|
 drop table t1,t2;
diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test
index 524ee9cf66c..95bb9f7c376 100644
--- a/mysql-test/t/sp.test
+++ b/mysql-test/t/sp.test
@@ -6662,6 +6662,30 @@ DROP PROCEDURE bug23760_update_log|
 DROP PROCEDURE bug23760_test_row_count|
 DROP FUNCTION bug23760_rc_test|
 
+#
+# BUG#24117: server crash on a FETCH with a cursor on a table which is not in
+#            the table cache
+#
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS bug24117|
+DROP TABLE IF EXISTS t3|
+--enable_warnings
+CREATE TABLE t3(c1 ENUM('abc'))|
+INSERT INTO t3 VALUES('abc')|
+CREATE PROCEDURE bug24117()
+BEGIN
+  DECLARE t3c1 ENUM('abc');
+  DECLARE mycursor CURSOR FOR SELECT c1 FROM t3;
+  OPEN mycursor;
+  FLUSH TABLES;
+  FETCH mycursor INTO t3c1;
+  CLOSE mycursor;
+END|
+CALL bug24117()|
+DROP PROCEDURE bug24117|
+DROP TABLE t3|
+
 #
 # NOTE: The delimiter is `|`, and not `;`. It is changed to `;`
 #       at the end of the file!
diff --git a/mysys/typelib.c b/mysys/typelib.c
index 90a093b0b32..230a2989cbd 100644
--- a/mysys/typelib.c
+++ b/mysys/typelib.c
@@ -119,3 +119,54 @@ const char *get_type(TYPELIB *typelib, uint nr)
     return(typelib->type_names[nr]);
   return "?";
 }
+
+
+/*
+  Create a copy of a specified TYPELIB structure.
+
+  SYNOPSIS
+    copy_typelib()
+    root	pointer to a MEM_ROOT object for allocations
+    from	pointer to a source TYPELIB structure
+
+  RETURN
+    pointer to the new TYPELIB structure on successful copy, or
+    NULL otherwise
+*/
+
+TYPELIB *copy_typelib(MEM_ROOT *root, TYPELIB *from)
+{
+  TYPELIB *to;
+  uint i;
+
+  if (!from)
+    return NULL;
+
+  if (!(to= (TYPELIB*) alloc_root(root, sizeof(TYPELIB))))
+    return NULL;
+
+  if (!(to->type_names= (const char **)
+        alloc_root(root, (sizeof(char *) + sizeof(int)) * (from->count + 1))))
+    return NULL;
+  to->type_lengths= (unsigned int *)(to->type_names + from->count + 1);
+  to->count= from->count;
+  if (from->name)
+  {
+    if (!(to->name= strdup_root(root, from->name)))
+      return NULL;
+  }
+  else
+    to->name= NULL;
+
+  for (i= 0; i < from->count; i++)
+  {
+    if (!(to->type_names[i]= strmake_root(root, from->type_names[i],
+                                          from->type_lengths[i])))
+      return NULL;
+    to->type_lengths[i]= from->type_lengths[i];
+  }
+  to->type_names[to->count]= NULL;
+  to->type_lengths[to->count]= 0;
+
+  return to;
+}
diff --git a/sql/field.cc b/sql/field.cc
index ec97bc92d24..684ce5602d4 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -7672,6 +7672,16 @@ void Field_enum::sql_type(String &res) const
 }
 
 
+Field *Field_enum::new_field(MEM_ROOT *root, struct st_table *new_table,
+                             bool keep_type)
+{
+  Field_enum *res= (Field_enum*) Field::new_field(root, new_table, keep_type);
+  if (res)
+    res->typelib= copy_typelib(root, typelib);
+  return res;
+}
+
+
 /*
    set type.
    This is a string which can have a collection of different values.
diff --git a/sql/field.h b/sql/field.h
index b79c2bf77a8..01b05d886a8 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1277,6 +1277,7 @@ class Field_enum :public Field_str {
   {
       flags|=ENUM_FLAG;
   }
+  Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type);
   enum_field_types type() const { return FIELD_TYPE_STRING; }
   enum Item_result cmp_type () const { return INT_RESULT; }
   enum Item_result cast_to_int_type () const { return INT_RESULT; }
-- 
2.30.9