List:Internals« Previous MessageNext Message »
From:Petr Chardin Date:August 25 2005 4:14pm
Subject:bk commit into 5.0 tree (petr:1.1925)
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of cps. When cps does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.1925 05/08/25 18:14:38 petr@stripped +3 -0
  Merge pchardin@stripped:/home/bk/mysql-5.0
  into  mysql.com:/home/cps/mysql/devel/mysql-5.0-sp11333

  sql/sp_head.cc
    1.172 05/08/25 18:14:29 petr@stripped +0 -0
    Auto merged

  sql/item.h
    1.164 05/08/25 18:14:29 petr@stripped +0 -0
    Auto merged

  sql/item.cc
    1.165 05/08/25 18:14:28 petr@stripped +0 -1
    Auto merged

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	petr
# Host:	owlet.
# Root:	/home/cps/mysql/devel/mysql-5.0-sp11333/RESYNC

--- 1.164/sql/item.cc	2005-08-25 15:53:02 +04:00
+++ 1.165/sql/item.cc	2005-08-25 18:14:28 +04:00
@@ -790,12 +790,15 @@
 }
 
 
+/*****************************************************************************
+  Item_splocal methods
+*****************************************************************************/
 double Item_splocal::val_real()
 {
   DBUG_ASSERT(fixed);
   Item *it= this_item();
   double ret= it->val_real();
-  Item::null_value= it->null_value;
+  null_value= it->null_value;
   return ret;
 }
 
@@ -805,7 +808,7 @@
   DBUG_ASSERT(fixed);
   Item *it= this_item();
   longlong ret= it->val_int();
-  Item::null_value= it->null_value;
+  null_value= it->null_value;
   return ret;
 }
 
@@ -815,7 +818,7 @@
   DBUG_ASSERT(fixed);
   Item *it= this_item();
   String *ret= it->val_str(sp);
-  Item::null_value= it->null_value;
+  null_value= it->null_value;
   return ret;
 }
 
@@ -825,7 +828,7 @@
   DBUG_ASSERT(fixed);
   Item *it= this_item();
   my_decimal *val= it->val_decimal(decimal_value);
-  Item::null_value= it->null_value;
+  null_value= it->null_value;
   return val;
 }
 
@@ -834,7 +837,7 @@
 {
   Item *it= this_item();
   bool ret= it->is_null();
-  Item::null_value= it->null_value;
+  null_value= it->null_value;
   return ret;
 }
 
@@ -898,6 +901,97 @@
   str->qs_append(m_offset);
 }
 
+
+/*****************************************************************************
+  Item_name_const methods
+*****************************************************************************/
+double Item_name_const::val_real()
+{
+  DBUG_ASSERT(fixed);
+  double ret= value_item->val_real();
+  null_value= value_item->null_value;
+  return ret;
+}
+
+
+longlong Item_name_const::val_int()
+{
+  DBUG_ASSERT(fixed);
+  longlong ret= value_item->val_int();
+  null_value= value_item->null_value;
+  return ret;
+}
+
+
+String *Item_name_const::val_str(String *sp)
+{
+  DBUG_ASSERT(fixed);
+  String *ret= value_item->val_str(sp);
+  null_value= value_item->null_value;
+  return ret;
+}
+
+
+my_decimal *Item_name_const::val_decimal(my_decimal *decimal_value)
+{
+  DBUG_ASSERT(fixed);
+  my_decimal *val= value_item->val_decimal(decimal_value);
+  Item::null_value= value_item->null_value;
+  return val;
+}
+
+
+bool Item_name_const::is_null()
+{
+  bool ret= value_item->is_null();
+  Item::null_value= value_item->null_value;
+  return ret;
+}
+
+Item::Type Item_name_const::type() const
+{
+  return value_item->type();
+}
+
+
+bool Item_name_const::fix_fields(THD *thd, Item **)
+{
+  char buf[128];
+  String *item_name;
+  String s(buf, sizeof(buf), &my_charset_bin);
+  s.length(0);
+
+  if (value_item->fix_fields(thd, &value_item) ||
+      name_item->fix_fields(thd, &name_item))
+    return TRUE;
+  if (!(value_item->const_item() && name_item->const_item()))
+    return TRUE;
+
+  if (!(item_name= name_item->val_str(&s)))
+    return TRUE; /* Can't have a NULL name */
+
+  set_name(item_name->ptr(), (uint) item_name->length(), system_charset_info);
+  max_length= value_item->max_length;
+  decimals= value_item->decimals;
+  fixed= 1;
+  return FALSE;
+}
+
+
+void Item_name_const::cleanup()
+{
+  fixed= 0;
+}
+
+
+void Item_name_const::print(String *str)
+{
+  str->append("NAME_CONST(");
+  name_item->print(str);
+  str->append(',');
+  value_item->print(str);
+  str->append(')');
+}
 
 
 /*

--- 1.163/sql/item.h	2005-08-25 15:53:02 +04:00
+++ 1.164/sql/item.h	2005-08-25 18:14:29 +04:00
@@ -700,20 +700,40 @@
 };
 
 
-// A local SP variable (incl. parameters), used in runtime
+/*
+  A reference to local SP variable (incl. reference to SP parameter), used in
+  runtime.
+  
+  NOTE
+    This item has a "value" item, defined as 
+      this_item() = thd->spcont->get_item(m_offset)
+    and it delegates everything to that item (if !this_item() then this item
+    poses as Item_null) except for name, which is the name of SP local
+    variable.
+*/
+
 class Item_splocal : public Item
 {
-private:
-  
   uint m_offset;
+public:
   LEX_STRING m_name;
 
-public:
+  /* 
+    Position of this reference to SP variable in the statement (the
+    statement itself is in sp_instr_stmt::m_query).
+    This is valid only for references to SP variables in statements,
+    excluding DECLARE CURSOR statement. It is used to replace references to SP
+    variables with NAME_CONST calls when putting statements into the binary
+    log.
+    Value of 0 means that this object doesn't corresponding to reference to
+    SP variable in query text.
+  */
+  int pos_in_query;
 
-  Item_splocal(LEX_STRING name, uint offset)
-    : m_offset(offset), m_name(name)
+  Item_splocal(LEX_STRING name, uint offset, int pos_in_q=0)
+    : m_offset(offset), m_name(name), pos_in_query(pos_in_q)
   {
-    Item::maybe_null= TRUE;
+    maybe_null= TRUE;
   }
 
   /* For error printing */
@@ -750,7 +770,7 @@
   bool is_null();
   void print(String *str);
 
-  inline void make_field(Send_field *field)
+  void make_field(Send_field *field)
   {
     Item *it= this_item();
 
@@ -761,27 +781,83 @@
     it->make_field(field);
   }
 
-  inline Item_result result_type() const
+  Item_result result_type() const
   {
     return this_const_item()->result_type();
   }
 
-  inline bool const_item() const
+  bool const_item() const
   {
     return TRUE;
   }
 
-  inline int save_in_field(Field *field, bool no_conversions)
+  int save_in_field(Field *field, bool no_conversions)
   {
     return this_item()->save_in_field(field, no_conversions);
   }
 
-  inline bool send(Protocol *protocol, String *str)
+  bool send(Protocol *protocol, String *str)
   {
     return this_item()->send(protocol, str);
   }
 };
 
+
+/*
+  NAME_CONST(given_name, const_value). 
+  This 'function' has all properties of the supplied const_value (which is 
+  assumed to be a literal constant), and the name given_name. 
+
+  This is used to replace references to SP variables when we write PROCEDURE
+  statements into the binary log.
+
+  TODO
+    Together with Item_splocal and Item::this_item() we can actually extract
+    common a base of this class and Item_splocal. Maybe it is possible to
+    extract a common base with class Item_ref, too.
+*/
+
+class Item_name_const : public Item
+{
+  Item *value_item;
+  Item *name_item;
+public:
+  Item_name_const(Item *name, Item *val): value_item(val), name_item(name)
+  {
+    Item::maybe_null= TRUE;
+  }
+
+  bool fix_fields(THD *, Item **);
+  void cleanup();
+
+  enum Type type() const;
+  double val_real();
+  longlong val_int();
+  String *val_str(String *sp);
+  my_decimal *val_decimal(my_decimal *);
+  bool is_null();
+  void print(String *str);
+
+  Item_result result_type() const
+  {
+    return value_item->result_type();
+  }
+
+  bool const_item() const
+  {
+    return TRUE;
+  }
+
+  int save_in_field(Field *field, bool no_conversions)
+  {
+    return  value_item->save_in_field(field, no_conversions);
+  }
+
+  inline bool send(Protocol *protocol, String *str)
+  {
+    return value_item->send(protocol, str);
+  }
+};
 
 bool agg_item_collations(DTCollation &c, const char *name,
                          Item **items, uint nitems, uint flags= 0);

--- 1.171/sql/sp_head.cc	2005-08-25 16:06:45 +04:00
+++ 1.172/sql/sp_head.cc	2005-08-25 18:14:29 +04:00
@@ -603,8 +603,155 @@
   DBUG_RETURN(field);
 }
 
-int
-sp_head::execute(THD *thd)
+
+int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
+{
+  return (int)((*a)->pos_in_query - (*b)->pos_in_query);
+}
+
+
+/*
+  StoredRoutinesBinlogging
+  Top-down overview:
+
+  1. Statements
+
+  Statements that have is_update_query(stmt) == TRUE are written into the
+  binary log verbatim.
+  Examples:
+    UPDATE tbl SET tbl.x = spfunc_w_side_effects()
+    UPDATE tbl SET tbl.x=1 WHERE spfunc_w_side_effect_that_returns_false(tbl.y)
+
+  Statements that have is_update_query(stmt) == FALSE (e.g. SELECTs) are not
+  written into binary log. Instead we catch function calls the statement
+  makes and write it into binary log separately (see #3).
+  
+  We actually can easily write SELECT statements into the binary log in the 
+  right order (we don't have issues with const tables being unlocked early
+  because SELECTs that use FUNCTIONs unlock all tables at once) We don't do 
+  it because replication slave thread currently can't execute SELECT
+  statements. Fixing this is on the TODO.
+  
+  2. PROCEDURE calls
+
+  CALL statements are not written into binary log. Instead
+  * Any FUNCTION invocation (in SET, IF, WHILE, OPEN CURSOR and other SP
+    instructions) is written into binlog separately.
+
+  * Each statement executed in SP is binlogged separately, according to rules
+    in #1, with the exception that we modify query string: we replace uses
+    of SP local variables with NAME_CONST('spvar_name', <spvar-value>) calls.
+    This substitution is done in subst_spvars().
+
+  3. FUNCTION calls
+  
+  In sp_head::execute_function(), we check 
+   * If this function invocation is done from a statement that is written
+     into the binary log.
+   * If there were any attempts to write events to the binary log during
+     function execution.
+   If the answers are No and Yes, we write the function call into the binary
+   log as "DO spfunc(<param1value>, <param2value>, ...)"
+
+*/
+
+
+/*
+  Replace thd->query{_length} with a string that one can write to the binlog.
+ 
+  SYNOPSIS
+    subst_spvars()
+      thd        Current thread. 
+      instr      Instruction (we look for Item_splocal instances in
+                 instr->free_list)
+      query_str  Original query string
+     
+  DESCRIPTION
+
+  The binlog-suitable string is produced by replacing references to SP local 
+  variables with NAME_CONST('sp_var_name', value) calls.
+ 
+  RETURN
+    0  Ok, thd->query{_length} either has been appropraiately replaced or
+       there is no need for replacements.
+    1  Out of memory error.
+*/
+
+static bool subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
+{
+  DBUG_ENTER("subst_spvars");
+  if (thd->prelocked_mode == NON_PRELOCKED && mysql_bin_log.is_open())
+  {
+    Dynamic_array<Item_splocal*> sp_vars_uses;
+
+    /* Find all instances of item_splocal used in this statement */
+    for (Item *item= instr->free_list; item; item= item->next)
+    {
+      if (item->is_splocal() && ((Item_splocal*)item)->pos_in_query)
+        sp_vars_uses.append((Item_splocal*)item);
+    }
+    if (!sp_vars_uses.elements())
+      DBUG_RETURN(0);
+      
+    /* Sort SP var refs by their occurences in the query */
+    sp_vars_uses.sort(cmp_splocal_locations);
+
+    /* 
+      Construct a statement string where SP local var refs are replaced
+      with "NAME_CONST(name, value)"
+    */
+    char buffer[512];
+    String qbuf(buffer, sizeof(buffer), &my_charset_bin);
+    qbuf.length(0);
+    
+    char *cur= query_str->str;
+    int prev_pos= 0;
+    int res= 0;
+    for (Item_splocal **splocal= sp_vars_uses.front(); 
+         splocal < sp_vars_uses.back(); splocal++)
+    {
+      /* append the text between sp ref occurences */
+      res |= qbuf.append(cur + prev_pos, (*splocal)->pos_in_query - prev_pos);
+      prev_pos= (*splocal)->pos_in_query + (*splocal)->m_name.length;
+      
+      /* append the spvar substitute */
+      res |= qbuf.append(" NAME_CONST('");
+      res |= qbuf.append((*splocal)->m_name.str, (*splocal)->m_name.length);
+      res |= qbuf.append("',");
+      Item *val= (*splocal)->this_item();
+      DBUG_PRINT("info", ("print %p", val));
+      val->print(&qbuf);
+      res |= qbuf.append(')');
+      if (res)
+        break;
+    }
+    res |= qbuf.append(cur + prev_pos, query_str->length - prev_pos);
+    if (res)
+      DBUG_RETURN(1);
+
+    char *pbuf= thd->alloc(qbuf.length()+1);
+
+    if (!pbuf)
+      DBUG_RETURN(1);
+
+    memcpy(pbuf, qbuf.ptr(), qbuf.length()+1);
+    thd->query= pbuf;
+    thd->query_length= qbuf.length();
+  }
+  DBUG_RETURN(0);
+}
+
+
+/*
+  Execute the routine. The main instruction jump loop is there 
+  Assume the parameters already set.
+  
+  RETURN
+    -1  on error
+
+*/
+
+int sp_head::execute(THD *thd)
 {
   DBUG_ENTER("sp_head::execute");
   char olddb[128];
@@ -812,9 +959,31 @@
 }
 
 
+/*
+  Execute a function:
+   - evaluate parameters
+   - call sp_head::execute
+   - evaluate the return value
+
+  SYNOPSIS
+    sp_head::execute_function()
+      thd        Thread handle
+      argp       Passed arguments (these are items from containing statement?)
+      argcount   Number of passed arguments. We need to check if this is
+                 correct.
+      resp   OUT Put result item here (q: is it a constant Item always?) 
+   
+  RETURN
+    0      on OK
+    other  on error
+*/
+
 int
 sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
 {
+  Item **param_values;
+  ulonglong binlog_save_options;
+  bool need_binlog_call;
   DBUG_ENTER("sp_head::execute_function");
   DBUG_PRINT("info", ("function %s", m_name.str));
   uint csize = m_pcont->max_pvars();
@@ -838,6 +1007,8 @@
     goto end;
   }
 
+  if (!(param_values= (Item**)thd->alloc(sizeof(Item*)*argcount)))
+    DBUG_RETURN(-1);
 
   // QQ Should have some error checking here? (types, etc...)
   if (!(nctx= new sp_rcontext(csize, hmax, cmax)))
@@ -846,6 +1017,7 @@
   {
     sp_pvar_t *pvar = m_pcont->find_pvar(i);
     Item *it= sp_eval_func_item(thd, argp++, pvar->type, NULL, FALSE);
+    param_values[i]= it;
 
     if (!it)
       goto end;                                 // EOM error
@@ -870,7 +1042,47 @@
   }
   thd->spcont= nctx;
 
+  binlog_save_options= thd->options;
+  need_binlog_call= mysql_bin_log.is_open() && (thd->options &
OPTION_BIN_LOG);
+  if (need_binlog_call)
+    mysql_bin_log.start_union_events(thd);
+    
+  thd->options&= ~OPTION_BIN_LOG;
   ret= execute(thd);
+  thd->options= binlog_save_options;
+  
+  if (need_binlog_call)
+    mysql_bin_log.stop_union_events(thd);
+
+  if (thd->binlog_evt_union.unioned_events)
+  {
+    char buf[64];
+    String bufstr(buf, sizeof(buf), &my_charset_bin);
+    bufstr.length(0);
+    bufstr.append("DO ", 3);
+    append_identifier(thd, &bufstr, m_name.str, m_name.length);
+    bufstr.append('(');
+    for (uint i=0; i < argcount; i++)
+    {
+      if (i)
+        bufstr.append(',');
+      param_values[i]->print(&bufstr);
+    }
+    bufstr.append(')');
+    
+    if (mysql_bin_log.is_open())
+    {
+      bool transactional_table= FALSE;
+      Query_log_event qinfo(thd, bufstr.ptr(), bufstr.length(),
+                            thd->binlog_evt_union.unioned_events_trans, FALSE);
+      if (mysql_bin_log.write(&qinfo) && transactional_table)
+      {
+        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+                    "Invoked ROUTINE modified a transactional table but MYSQL"
+                    "failed to reflect this change in the binary log.");
+      }
+    }
+  }
 
   if (m_type == TYPE_ENUM_FUNCTION && ret == 0)
   {
@@ -908,6 +1120,25 @@
 }
 
 
+/*
+  Execute a procedure. 
+  SYNOPSIS
+    sp_head::execute_procedure()
+      thd    Thread handle
+      args   List of values passed as arguments.
+      
+  DESCRIPTION
+
+  The function does the following steps:
+   - Set all parameters 
+   - call sp_head::execute
+   - copy back values of INOUT and OUT parameters
+
+  RETURN
+    0   Ok
+    -1  Error
+*/
+
 int sp_head::execute_procedure(THD *thd, List<Item> *args)
 {
   int ret= 0;
@@ -943,7 +1174,7 @@
     thd->spcont= save_spcont;
     DBUG_RETURN(-1);
   }
-
+  
   if (csize > 0 || hmax > 0 || cmax > 0)
   {
     Item_null *nit= NULL;	// Re-use this, and only create if needed
@@ -1120,7 +1351,7 @@
   nctx->pop_all_cursors();	// To avoid memory leaks after an error
   delete nctx;                                  // Does nothing
   thd->spcont= save_spcont;
-
+  
   DBUG_RETURN(ret);
 }
 
@@ -1462,8 +1693,12 @@
   DBUG_RETURN(res);
 }
 
-void
-sp_head::optimize()
+
+/*
+  TODO: what does this do??
+*/
+
+void sp_head::optimize()
 {
   List<sp_instr> bp;
   sp_instr *i;
@@ -1651,7 +1886,6 @@
   return 0;
 }
 
-
 /*
   sp_instr_stmt class functions
 */
@@ -1661,9 +1895,9 @@
 {
   char *query;
   uint32 query_length;
+  int res;
   DBUG_ENTER("sp_instr_stmt::execute");
   DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command()));
-  int res;
 
   query= thd->query;
   query_length= thd->query_length;
@@ -1672,8 +1906,10 @@
     if (query_cache_send_result_to_client(thd,
 					  thd->query, thd->query_length) <= 0)
     {
+      thd->query_str_binlog_unsuitable= subst_spvars(thd, this, &m_query);
       res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);
       query_cache_end_of_result(thd);
+      thd->query_str_binlog_unsuitable= FALSE;
     }
     else
       *nextp= m_ip+1;
Thread
bk commit into 5.0 tree (petr:1.1925)Petr Chardin25 Aug