From f2f748c6318f863d7f7002a1b4ec57112f0ea781 Mon Sep 17 00:00:00 2001
From: unknown <venu@myvenu.com>
Date: Fri, 4 Apr 2003 12:33:17 -0500
Subject: [PATCH] Fix to support update + bianry logs with prepared statements
 (Dynamic query)

sql/item.cc:
  query_val_str to return param item value in string format
sql/item.h:
  Misc defination changes for Item_param
sql/sql_class.h:
  Changes for PREP_STMT
sql/sql_string.cc:
  Duplicate String::replace to take char * and length as arguments
sql/sql_yacc.yy:
  Change to take param marker position to Item_param as an argument
sql/sql_prepare.cc:
  Fix for binary + update logs
sql/sql_string.h:
  Added new replace()
---
 sql/item.cc        |  73 ++++++++++++++++++++++++
 sql/item.h         |  13 +++--
 sql/sql_class.h    |   3 +
 sql/sql_prepare.cc | 136 +++++++++++++++++++++++++++++++++++++--------
 sql/sql_string.cc  |  19 +++++--
 sql/sql_string.h   |   1 +
 sql/sql_yacc.yy    |   2 +-
 7 files changed, 212 insertions(+), 35 deletions(-)

diff --git a/sql/item.cc b/sql/item.cc
index 30f610ea92a..2cecd247a0e 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -542,6 +542,79 @@ String *Item_param::val_str(String* str)
     return (String*) &str_value;
   }
 }
+
+/*
+  Return Param item values in string format, for generating the dynamic 
+  query used in update/binary logs
+*/
+
+String *Item_param::query_val_str(String* str) 
+{ 
+  switch (item_result_type) {
+  case INT_RESULT:
+    str->set(int_value, default_charset());
+    break;
+  case REAL_RESULT:
+    set->set(real_value, 2, default_charset());
+    break;
+  default:
+    str->set("'", 1, default_charset());
+    
+    if (!item_is_time)
+    {
+      str->append(str_value);
+      const char *from= str->ptr(); 
+      uint32 length= 1;
+      
+      // Escape misc cases
+      char *to= (char *)from, *end= (char *)to+str->length(); 
+      for (to++; to != end ; length++, to++)
+      {
+        switch(*to) {
+          case '\'':
+          case '"':  
+          case '\r':
+          case '\n':
+          case '\\': // TODO: Add remaining ..
+            str->replace(length,0,"\\",1); 
+            to++; end++; length++;
+            break;
+          default:     
+            break;
+        }
+      }
+    }
+    else
+    {
+      char buff[25];
+      
+      switch (ltime.time_type)  {
+        case TIMESTAMP_NONE:
+          break;
+        case TIMESTAMP_DATE:
+          sprintf(buff, "%04d-%02d-%02d", 
+                        ltime.year,ltime.month,ltime.day);
+          str->append(buff, 10);
+          break;
+        case TIMESTAMP_FULL:
+          sprintf(buff, "%04d-%02d-%02d %02d:%02d:%02d",
+ 	                ltime.year,ltime.month,ltime.day,
+	                ltime.hour,ltime.minute,ltime.second));
+          str->append(buff, 19);
+          break;
+        case TIMESTAMP_TIME:
+        {
+          sprintf(buff, "%02d:%02d:%02d",
+	  	        ltime.hour,ltime.minute,ltime.second));
+          str->append(buff, 8);
+          break;
+        }
+      }
+    }
+    str->append("'");
+  }
+  return str;
+}
 /* End of Item_param related */
 
 
diff --git a/sql/item.h b/sql/item.h
index 1b5fafc180f..6a7ebd506ac 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -239,15 +239,17 @@ public:
   enum Type item_type;
   enum enum_field_types buffer_type;
   bool item_is_time;
-  my_bool long_data_supplied;
+  bool long_data_supplied;
+  uint pos_in_query;
 
-  Item_param(char *name_par=0)
+  Item_param::Item_param(uint position)
   { 
-    name= name_par ? name_par : (char*) "?";
-    long_data_supplied= false;
+    name= (char*) "?";
+    pos_in_query= position;
     item_type= STRING_ITEM;
     item_result_type = STRING_RESULT;
     item_is_time= false;
+    long_data_supplied= false;
   }
   enum Type type() const { return item_type; }
   double val();
@@ -268,8 +270,9 @@ public:
   void (*setup_param_func)(Item_param *param, uchar **pos);
   enum Item_result result_type () const
   { return item_result_type; }
+  String *query_val_str(String *str);
   enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
-  Item *new_item() { return new Item_param(name); }
+  Item *new_item() { return new Item_param(pos_in_query); }
 };
 
 class Item_int :public Item
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 769daf257b3..4a87badcadf 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -324,11 +324,14 @@ typedef struct st_prep_stmt
   Item_param **param;
   Item *free_list;
   MEM_ROOT mem_root;
+  String *query;
   ulong stmt_id;
   uint param_count;
   uint last_errno;
   char last_error[MYSQL_ERRMSG_SIZE];
   bool error_in_prepare, long_data_used;
+  bool log_full_query;
+  bool (*setup_params)(st_prep_stmt *stmt, uchar *pos, uchar *read_pos);
 } PREP_STMT;
 
 
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index eea0d853132..c3e84849431 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -74,7 +74,10 @@ Long data handling:
 
 #define IS_PARAM_NULL(pos, param_no) pos[param_no/8] & (1 << param_no & 7)
 
+#define STMT_QUERY_LOG_LENGTH 8192
+
 extern int yyparse(void *thd);
+static String null_string("NULL", 4, default_charset_info);
 
 /*
   Find prepared statement in thd
@@ -129,6 +132,8 @@ int compare_prep_stmt(void *not_used, PREP_STMT *stmt, ulong *key)
 void free_prep_stmt(PREP_STMT *stmt, TREE_FREE mode, void *not_used)
 {     
   my_free((char *)stmt->param, MYF(MY_ALLOW_ZERO_PTR));
+  if (stmt->query)
+    stmt->query->free();
   free_items(stmt->free_list);
   free_root(&stmt->mem_root, MYF(0));
 }
@@ -374,8 +379,8 @@ static void setup_param_functions(Item_param *param, uchar param_type)
     param->setup_param_func= setup_param_date;
     param->item_result_type= STRING_RESULT;
     break;
-  case FIELD_TYPE_DATETIME:
-  case FIELD_TYPE_TIMESTAMP:
+  case MYSQL_TYPE_DATETIME:
+  case MYSQL_TYPE_TIMESTAMP:
     param->setup_param_func= setup_param_datetime;
     param->item_result_type= STRING_RESULT;
     break;
@@ -386,10 +391,82 @@ static void setup_param_functions(Item_param *param, uchar param_type)
 }
 
 /*
-  Update the parameter markers by reading the data           
-  from client ..                                             
+  Update the parameter markers by reading data from client packet 
+  and if binary/update log is set, generate the valid query.
 */
 
+static bool insert_params_withlog(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
+{
+  THD *thd= stmt->thd;
+  List<Item> &params= thd->lex.param_list;
+  List_iterator<Item> param_iterator(params);
+  Item_param *param;
+  DBUG_ENTER("insert_params_withlog"); 
+  
+  String str, *res, *query= new String(stmt->query->alloced_length());  
+  query->copy(*stmt->query);
+  
+  ulong param_no= 0;  
+  uint32 length= 0;
+  
+  while ((param= (Item_param *)param_iterator++))
+  {
+    if (param->long_data_supplied)
+      res= param->query_val_str(&str);       
+    
+    else
+    {
+      if (IS_PARAM_NULL(pos,param_no))
+      {
+        param->maybe_null= param->null_value= 1;
+        res= &null_string;
+      }
+      else
+      {
+        param->maybe_null= param->null_value= 0;
+        param->setup_param_func(param,&read_pos);
+        res= param->query_val_str(&str);
+      }
+    }
+    if (query->replace(param->pos_in_query+length, 1, *res))
+      DBUG_RETURN(1);
+    
+    length+= res->length()-1;
+    param_no++;
+  }
+  if (alloc_query(stmt->thd, (char *)query->ptr(), query->length()+1))
+    DBUG_RETURN(1);
+  
+  query->free();
+  DBUG_RETURN(0);
+}
+
+static bool insert_params(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
+{
+  THD *thd= stmt->thd;
+  List<Item> &params= thd->lex.param_list;
+  List_iterator<Item> param_iterator(params);
+  Item_param *param;
+  DBUG_ENTER("insert_params"); 
+  
+  ulong param_no= 0;  
+  while ((param= (Item_param *)param_iterator++))
+  {
+    if (!param->long_data_supplied)   
+    {
+      if (IS_PARAM_NULL(pos,param_no))
+        param->maybe_null= param->null_value= 1;
+      else
+      {
+        param->maybe_null= param->null_value= 0;
+        param->setup_param_func(param,&read_pos);
+      }
+    }
+    param_no++;
+  }
+  DBUG_RETURN(0);
+}
+
 static bool setup_params_data(PREP_STMT *stmt)
 {                                       
   THD *thd= stmt->thd;
@@ -418,21 +495,7 @@ static bool setup_params_data(PREP_STMT *stmt)
     }
     param_iterator.rewind();
   }    
-  ulong param_no= 0;
-  while ((param= (Item_param *)param_iterator++))
-  {
-    if (!param->long_data_supplied)
-    {
-      if (IS_PARAM_NULL(pos,param_no))
-        param->maybe_null= param->null_value= 1;
-      else
-      {
-        param->maybe_null= param->null_value= 0;
-        param->setup_param_func(param,&read_pos);
-      }
-    }
-    param_no++;
-  }
+  stmt->setup_params(stmt,pos,read_pos);
   DBUG_RETURN(0);
 }
 
@@ -707,21 +770,42 @@ static bool parse_prepare_query(PREP_STMT *stmt,
 
 static bool init_param_items(PREP_STMT *stmt)
 {
-  List<Item> &params= stmt->thd->lex.param_list;
+  THD *thd= stmt->thd;
+  List<Item> &params= thd->lex.param_list;
   Item_param **to;
+  uint32 length= thd->query_length;
  
-  stmt->lex=  stmt->thd->lex;
+  stmt->lex=  thd->lex;
+
+  if (mysql_bin_log.is_open() || mysql_update_log.is_open())
+  {
+    stmt->log_full_query= 1;
+    stmt->setup_params= insert_params_withlog;
+  }
+  else
+    stmt->setup_params= insert_params; // not fully qualified query
+   
   if (!stmt->param_count)
     stmt->param= (Item_param **)0;
   else
-  {
+  {    
     if (!(stmt->param= to= (Item_param **)
           my_malloc(sizeof(Item_param *)*(stmt->param_count+1), 
                     MYF(MY_WME))))
       return 1;
+
+    if (stmt->log_full_query)
+    {
+      length= thd->query_length+(stmt->param_count*2)+1;
+ 
+      if ( length < STMT_QUERY_LOG_LENGTH ) 
+        length= STMT_QUERY_LOG_LENGTH;
+    }
     List_iterator<Item> param_iterator(params);
     while ((*(to++)= (Item_param *)param_iterator++));
-  }
+  }  
+  stmt->query= new String(length);
+  stmt->query->copy(thd->query, thd->query_length, default_charset_info);
   return 0;
 }
 
@@ -741,6 +825,12 @@ static void init_stmt_execute(PREP_STMT *stmt)
   */  
   for (; tables ; tables= tables->next)
     tables->table= 0; //safety - nasty init
+  
+  if (!(stmt->log_full_query && stmt->param_count))
+  {
+    thd->query= stmt->query->c_ptr();
+    thd->query_length= stmt->query->length();
+  }
 }
 
 /*
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index ffa272713de..ca38651b3b6 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -508,14 +508,20 @@ skipp:
 
 bool String::replace(uint32 offset,uint32 arg_length,const String &to)
 {
-  long diff = (long) to.length()-(long) arg_length;
+  return replace(offset,arg_length,to.ptr(),to.length());
+}
+
+bool String::replace(uint32 offset,uint32 arg_length,
+                     const char *to,uint32 length)
+{
+  long diff = (long) length-(long) arg_length;
   if (offset+arg_length <= str_length)
   {
     if (diff < 0)
     {
-      if (to.length())
-	memcpy(Ptr+offset,to.ptr(),to.length());
-      bmove(Ptr+offset+to.length(),Ptr+offset+arg_length,
+      if (length)
+	memcpy(Ptr+offset,to,length);
+      bmove(Ptr+offset+length,Ptr+offset+arg_length,
 	    str_length-offset-arg_length);
     }
     else
@@ -527,14 +533,15 @@ bool String::replace(uint32 offset,uint32 arg_length,const String &to)
 	bmove_upp(Ptr+str_length+diff,Ptr+str_length,
 		  str_length-offset-arg_length);
       }
-      if (to.length())
-	memcpy(Ptr+offset,to.ptr(),to.length());
+      if (length)
+	memcpy(Ptr+offset,to,length);
     }
     str_length+=(uint32) diff;
   }
   return FALSE;
 }
 
+
 // added by Holyfoot for "geometry" needs
 int String::reserve(uint32 space_needed, uint32 grow_by)
 {
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 469574ca2e4..9c494600058 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -186,6 +186,7 @@ public:
   int strstr(const String &search,uint32 offset=0); // Returns offset to substring or -1
   int strstr_case(const String &s,uint32 offset=0);
   int strrstr(const String &search,uint32 offset=0); // Returns offset to substring or -1
+  bool replace(uint32 offset,uint32 arg_length,const char *to,uint32 length);
   bool replace(uint32 offset,uint32 arg_length,const String &to);
   inline bool append(char chr)
   {
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index d730807464f..dda63020b13 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -3932,7 +3932,7 @@ param_marker:
 	  LEX *lex=Lex;
           if (YYTHD->prepare_command)
           {
-            lex->param_list.push_back($$=new Item_param());
+            lex->param_list.push_back($$=new Item_param((uint)(lex->tok_start-(uchar *)YYTHD->query)));
             lex->param_count++;
           }
           else