← Back to team overview

maria-developers team mailing list archive

Re: changes in virtual column patch

 

Hi!

Here is second version of changed virtual column patch:

 - syntax now looks like

    <name> <type> [GENERATED ALWAYS] AS <expression> [MATERIALIZED|VIRTUAL]
   if MATERIALIZED|VIRTUAL is omitted then default is MATERIALIZED in
oracle mode, in other modes it is VIRTUAL.

 - Virtual_column_info renamed
 - GENERATED, ALWAYS, MATERIALIZED, VIRTUAL made non-reserved words
 - spelling errors an spaces at the end of lines somewhere removed.
 - Removed assigning of variable 'item' which was not used.

Attachment: vcol-200903302229.tar.gz
Description: GNU Zip compressed data

=== modified file 'BUILD/SETUP.sh'
--- BUILD/SETUP.sh	2009-03-22 12:16:09 +0000
+++ BUILD/SETUP.sh	2009-03-24 16:06:06 +0000
@@ -169,7 +169,7 @@ max_no_embedded_configs="$SSL_LIBRARY --
 max_no_ndb_configs="$SSL_LIBRARY --with-plugins=max-no-ndb --with-embedded-server --with-libevent"
 max_configs="$SSL_LIBRARY --with-plugins=max --with-embedded-server -with-libevent"
 # Disable NDB in maria max builds
-max_configs=$max_no_ndb_configs
+#max_configs=$max_no_ndb_configs
 
 #
 # CPU and platform specific compilation flags.

=== modified file 'include/mysql_com.h'
--- include/mysql_com.h	2008-10-10 15:28:41 +0000
+++ include/mysql_com.h	2009-03-24 10:47:20 +0000
@@ -67,7 +67,14 @@ enum enum_server_command
   COM_END
 };
 
-
+/* The length of the header part for each virtual column in the .frm file. */
+#define FRM_VCOL_HEADER_SIZE 3
+/*
+  Maximum length of the expression statement defined for virtual columns.
+*/
+#define VIRTUAL_COLUMN_EXPRESSION_MAXLEN 255 - FRM_VCOL_HEADER_SIZE
+/* sql type field stored in .frm files for each virtual field. */
+#define MYSQL_TYPE_VIRTUAL 245
 /*
   Length of random string sent by server on handshake; this is also length of
   obfuscated password, recieved from client

=== modified file 'sql/field.cc'
--- sql/field.cc	2009-02-19 09:01:25 +0000
+++ sql/field.cc	2009-03-30 14:42:36 +0000
@@ -1312,7 +1312,8 @@ Field::Field(uchar *ptr_arg,uint32 lengt
    key_start(0), part_of_key(0), part_of_key_not_clustered(0),
    part_of_sortkey(0), unireg_check(unireg_check_arg),
    field_length(length_arg), null_bit(null_bit_arg), 
-   is_created_from_null_item(FALSE)
+   is_created_from_null_item(FALSE),
+   vcol_info(0), stored_in_db(TRUE)
 {
   flags=null_ptr ? 0: NOT_NULL_FLAG;
   comment.str= (char*) "";
@@ -9495,6 +9496,8 @@ void Create_field::init_for_tmp_table(en
               ((decimals_arg & FIELDFLAG_MAX_DEC) << FIELDFLAG_DEC_SHIFT) |
               (maybe_null ? FIELDFLAG_MAYBE_NULL : 0) |
               (is_unsigned ? 0 : FIELDFLAG_DECIMAL));
+  vcol_info= 0;
+  stored_in_db= TRUE;
 }
 
 
@@ -9514,6 +9517,7 @@ void Create_field::init_for_tmp_table(en
   @param fld_interval_list     Interval list (if any)
   @param fld_charset           Field charset
   @param fld_geom_type         Field geometry type (if any)
+  @param fld_vcol_info         Virtual column data
 
   @retval
     FALSE on success
@@ -9526,13 +9530,14 @@ bool Create_field::init(THD *thd, char *
                         uint fld_type_modifier, Item *fld_default_value,
                         Item *fld_on_update_value, LEX_STRING *fld_comment,
                         char *fld_change, List<String> *fld_interval_list,
-                        CHARSET_INFO *fld_charset, uint fld_geom_type)
+                        CHARSET_INFO *fld_charset, uint fld_geom_type,
+			Virtual_column_info *fld_vcol_info)
 {
   uint sign_len, allowed_type_modifier= 0;
   ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
 
   DBUG_ENTER("Create_field::init()");
-  
+
   field= 0;
   field_name= fld_name;
   def= fld_default_value;
@@ -9557,6 +9562,34 @@ bool Create_field::init(THD *thd, char *
   interval_list.empty();
 
   comment= *fld_comment;
+  vcol_info= fld_vcol_info;
+  stored_in_db= TRUE;
+
+  /* Initialize data for a virtual field */
+  if ((uchar)fld_type == (uchar)MYSQL_TYPE_VIRTUAL)
+  {
+    DBUG_ASSERT(vcol_info);
+    DBUG_ASSERT(vcol_info->expr_item);
+    stored_in_db= vcol_info->get_field_stored();
+    /*
+      Walk through the Item tree checking if all items are valid
+      to be part of the virtual column
+    */
+    if (vcol_info->expr_item->walk(&Item::check_vcol_func_processor, 0, NULL))
+    {
+      my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), field_name);
+      DBUG_RETURN(TRUE);
+    }
+
+    /*
+      Make a field created for the real type.
+      Note that "real" and virtual fields differ from each other
+      only by Field::vcol_info, which is always 0 for normal columns.
+      vcol_info is updated for fields later in procedure open_binary_frm.
+    */
+    sql_type= fld_type= vcol_info->get_real_type();
+  }
+
   /*
     Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and
     it is NOT NULL, not an AUTO_INCREMENT field and not a TIMESTAMP.
@@ -9847,7 +9880,7 @@ bool Create_field::init(THD *thd, char *
     }
   case MYSQL_TYPE_DECIMAL:
     DBUG_ASSERT(0); /* Was obsolete */
-  }
+ }
   /* Remember the value of length */
   char_length= length;
 
@@ -9946,7 +9979,6 @@ uint pack_length_to_packflag(uint type)
   return 0;					// This shouldn't happen
 }
 
-
 Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
 		  uchar *null_pos, uchar null_bit,
 		  uint pack_flag,
@@ -10140,6 +10172,8 @@ Create_field::Create_field(Field *old_fi
   charset=    old_field->charset();		// May be NULL ptr
   comment=    old_field->comment;
   decimals=   old_field->decimals();
+  vcol_info=  old_field->vcol_info;
+  stored_in_db= old_field->stored_in_db;
 
   /* Fix if the original table had 4 byte pointer blobs */
   if (flags & BLOB_FLAG)

=== modified file 'sql/field.h'
--- sql/field.h	2009-02-19 09:01:25 +0000
+++ sql/field.h	2009-03-30 14:42:36 +0000
@@ -45,6 +45,67 @@ inline uint get_set_pack_length(int elem
   return len > 4 ? 8 : len;
 }
 
+class Virtual_column_info: public Sql_alloc
+{
+public:
+  Item *expr_item;
+  LEX_STRING expr_str;
+  Item *item_free_list;
+  Virtual_column_info()
+  : expr_item(0), item_free_list(0),
+    field_type((enum enum_field_types)MYSQL_TYPE_VIRTUAL),
+    stored_in_db(FALSE), implicitly_stored_in_db(FALSE),data_inited(FALSE)
+  {
+    expr_str.str= NULL;
+    expr_str.length= 0;
+  };
+  ~Virtual_column_info() {}
+  enum_field_types get_real_type()
+  {
+    DBUG_ASSERT(data_inited);
+    return data_inited ? field_type : (enum enum_field_types)MYSQL_TYPE_VIRTUAL;
+  }
+  void set_field_type(enum_field_types fld_type)
+  {
+    /* Calling this function can only be done once. */
+    DBUG_ASSERT(!data_inited);
+    data_inited= TRUE;
+    field_type= fld_type;
+  }
+  bool get_field_stored()
+  {
+    DBUG_ASSERT(data_inited);
+    return data_inited ? stored_in_db : TRUE;
+  }
+  void set_field_stored(bool stored)
+  {
+    stored_in_db= stored;
+  }
+  bool is_field_implicitly_stored()
+  {
+    return implicitly_stored_in_db;
+  }
+  void set_field_implicitly_stored()
+  {
+    implicitly_stored_in_db= TRUE;
+  }
+private:
+  /*
+    The following data is only updated by the parser and read
+    when a Create_field object is created/initialized.
+  */
+  enum_field_types field_type;   /* Real field type*/
+  /* Indication that the field is physically stored in the database*/
+  my_bool stored_in_db;
+  /*  Indication that the field used in partitioning expression */
+  my_bool implicitly_stored_in_db;
+  /*
+    This flag is used to prevent other applications from
+    reading and using incorrect data.
+  */
+  my_bool data_inited;
+};
+
 class Field
 {
   Field(const Item &);				/* Prevent use of these */
@@ -57,7 +118,7 @@ public:
   uchar		*ptr;			// Position to field in record
   uchar		*null_ptr;		// Byte where null_bit is
   /*
-    Note that you can use table->in_use as replacement for current_thd member 
+    Note that you can use table->in_use as replacement for current_thd member
     only inside of val_*() and store() members (e.g. you can't use it in cons)
   */
   struct st_table *table;		// Pointer for table
@@ -67,10 +128,10 @@ public:
   /* Field is part of the following keys */
   key_map	key_start, part_of_key, part_of_key_not_clustered;
   key_map       part_of_sortkey;
-  /* 
-    We use three additional unireg types for TIMESTAMP to overcome limitation 
-    of current binary format of .frm file. We'd like to be able to support 
-    NOW() as default and on update value for such fields but unable to hold 
+  /*
+    We use three additional unireg types for TIMESTAMP to overcome limitation
+    of current binary format of .frm file. We'd like to be able to support
+    NOW() as default and on update value for such fields but unable to hold
     this info anywhere except unireg_check field. This issue will be resolved
     in more clean way with transition to new text based .frm format.
     See also comment for Field_timestamp::Field_timestamp().
@@ -103,6 +164,15 @@ public:
    */
   bool is_created_from_null_item;
 
+  /* Virtual column data */
+  Virtual_column_info *vcol_info;
+  /*
+    Indication that the field is physically stored in tables
+    rather than just generated on SQL queries.
+    As of now, FALSE can only be set for generated-only virtual columns.
+  */
+  bool stored_in_db;
+
   Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
         uchar null_bit_arg, utype unireg_check_arg,
         const char *field_name_arg);
@@ -2044,6 +2114,16 @@ public:
 
   uint8 row,col,sc_length,interval_id;	// For rea_create_table
   uint	offset,pack_flag;
+
+  /* Virtual column expression statement */
+  Virtual_column_info *vcol_info;
+  /*
+    Indication that the field is physically stored in tables
+    rather than just generated on SQL queries.
+    As of now, FALSE can only be set for generated-only virtual columns.
+  */
+  bool stored_in_db;
+
   Create_field() :after(0) {}
   Create_field(Field *field, Field *orig_field);
   /* Used to make a clone of this object for ALTER/CREATE TABLE */
@@ -2060,7 +2140,8 @@ public:
             char *decimals, uint type_modifier, Item *default_value,
             Item *on_update_value, LEX_STRING *comment, char *change,
             List<String> *interval_list, CHARSET_INFO *cs,
-            uint uint_geom_type);
+            uint uint_geom_type,
+	    Virtual_column_info *vcol_info);
 };
 
 

=== modified file 'sql/filesort.cc'
--- sql/filesort.cc	2009-02-19 09:01:25 +0000
+++ sql/filesort.cc	2009-03-24 10:47:20 +0000
@@ -563,6 +563,8 @@ static ha_rows find_all_keys(SORTPARAM *
     {
       if ((error= select->quick->get_next()))
         break;
+      if (!error)
+        update_virtual_fields_marked_for_write(sort_form);
       file->position(sort_form->record[0]);
       DBUG_EXECUTE_IF("debug_filesort", dbug_print_record(sort_form, TRUE););
     }
@@ -580,6 +582,8 @@ static ha_rows find_all_keys(SORTPARAM *
       else
       {
 	error=file->rnd_next(sort_form->record[0]);
+	if (!error)
+	  update_virtual_fields_marked_for_write(sort_form);
 	if (!flag)
 	{
 	  my_store_ptr(ref_pos,ref_length,record); // Position to row

=== modified file 'sql/ha_partition.cc'
--- sql/ha_partition.cc	2009-02-19 09:01:25 +0000
+++ sql/ha_partition.cc	2009-03-24 10:47:20 +0000
@@ -2408,7 +2408,7 @@ int ha_partition::open(const char *name,
     DBUG_RETURN(1);
   m_start_key.length= 0;
   m_rec0= table->record[0];
-  m_rec_length= table_share->reclength;
+  m_rec_length= table_share->stored_rec_length;
   alloc_len= m_tot_parts * (m_rec_length + PARTITION_BYTES_IN_POS);
   alloc_len+= table_share->max_key_length;
   if (!m_ordered_rec_buffer)

=== modified file 'sql/ha_partition.h'
--- sql/ha_partition.h	2009-02-19 09:01:25 +0000
+++ sql/ha_partition.h	2009-03-24 10:47:20 +0000
@@ -245,6 +245,7 @@ public:
     DBUG_RETURN(0);
   }
   virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share);
+  bool check_if_supported_virtual_columns(void) { return TRUE;}
   virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
                                           uint table_changes);
 private:

=== modified file 'sql/handler.h'
--- sql/handler.h	2009-02-19 09:01:25 +0000
+++ sql/handler.h	2009-03-24 10:47:20 +0000
@@ -1755,6 +1755,12 @@ public:
 
   LEX_STRING *engine_name() { return hton_name(ht); }
 
+  /*
+    This procedure defines if the storage engine supports virtual columns.
+    Default FALSE means "not supported".
+  */
+  virtual bool check_if_supported_virtual_columns(void) { return FALSE;}
+
 protected:
   /* Service methods for use by storage engines. */
   void ha_statistic_increment(ulong SSV::*offset) const;

=== modified file 'sql/item.cc'
--- sql/item.cc	2009-02-19 09:01:25 +0000
+++ sql/item.cc	2009-03-26 23:39:01 +0000
@@ -677,9 +677,26 @@ bool Item_field::register_field_in_read_
   TABLE *table= (TABLE *) arg;
   if (field->table == table || !table)
     bitmap_set_bit(field->table->read_set, field->field_index);
+  if (field->vcol_info && field->vcol_info->expr_item)
+    return field->vcol_info->expr_item->walk(&Item::register_field_in_read_map, 
+                                             1, arg);
   return 0;
 }
 
+/*
+  Mark field in bitmap supplied as *arg
+
+*/
+
+bool Item_field::register_field_in_bitmap(uchar *arg)
+{
+  MY_BITMAP *bitmap= (MY_BITMAP *) arg;
+  DBUG_ASSERT(bitmap);
+  if (!bitmap)
+    return 1;
+  bitmap_set_bit(bitmap, field->field_index);
+  return 0;
+}
 
 bool Item::check_cols(uint c)
 {
@@ -4188,6 +4205,20 @@ error:
   return TRUE;
 }
 
+/**
+  Marks virtual columns as implicitly stored
+*/
+
+bool Item_field::vcol_in_partition_func_processor(uchar *int_arg)
+{
+  DBUG_ASSERT(fixed);
+  if (field->vcol_info)
+  {
+    field->vcol_info->set_field_implicitly_stored();
+  }
+  return FALSE;
+}
+
 
 Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs)
 {

=== modified file 'sql/item.h'
--- sql/item.h	2009-02-19 09:01:25 +0000
+++ sql/item.h	2009-03-26 23:39:00 +0000
@@ -889,6 +889,11 @@ public:
   virtual bool is_expensive_processor(uchar *arg) { return 0; }
   virtual bool register_field_in_read_map(uchar *arg) { return 0; }
   /*
+    The next function differs from the previous one that a bitmap to be updated
+    is passed as uchar *arg.
+  */
+  virtual bool register_field_in_bitmap(uchar *arg) { return 0; }
+  /*
     Check if a partition function is allowed
     SYNOPSIS
       check_partition_func_processor()
@@ -940,11 +945,31 @@ public:
     fields.
   */
   virtual bool check_partition_func_processor(uchar *bool_arg) { return TRUE;}
+  virtual bool vcol_in_partition_func_processor(uchar *bool_arg)
+  {
+    return FALSE;
+  }
   virtual bool subst_argument_checker(uchar **arg)
-  { 
+  {
     if (*arg)
-      *arg= NULL; 
-    return TRUE;     
+      *arg= NULL;
+    return TRUE;
+  }
+  /*
+    Check if an expression/function is allowed for a virtual column
+    SYNOPSIS
+      check_vcol_func_processor()
+      int_arg is just ignored
+    RETURN VALUE
+      TRUE                           Function not accepted
+      FALSE                          Function accepted
+  */
+  virtual bool check_vcol_func_processor(uchar *int_arg)
+  {
+    DBUG_ENTER("Item::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
   }
 
   virtual Item *equal_fields_propagator(uchar * arg) { return this; }
@@ -1298,6 +1323,13 @@ public:
   {
     return value_item->send(protocol, str);
   }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_name_const::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 bool agg_item_collations(DTCollation &c, const char *name,
@@ -1315,6 +1347,7 @@ public:
   virtual Item_num *neg()= 0;
   Item *safe_charset_converter(CHARSET_INFO *tocs);
   bool check_partition_func_processor(uchar *int_arg) { return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 #define NO_CACHED_FIELD_INDEX ((uint)(-1))
@@ -1474,7 +1507,10 @@ public:
   bool collect_item_field_processor(uchar * arg);
   bool find_item_in_field_list_processor(uchar *arg);
   bool register_field_in_read_map(uchar *arg);
+  bool register_field_in_bitmap(uchar *arg);
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool vcol_in_partition_func_processor(uchar *int_arg);
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
   void cleanup();
   bool result_as_longlong()
   {
@@ -1534,6 +1570,7 @@ public:
 
   Item *safe_charset_converter(CHARSET_INFO *tocs);
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 class Item_null_result :public Item_null
@@ -1547,7 +1584,14 @@ public:
     save_in_field(result_field, no_conversions);
   }
   bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
-};  
+  bool check_vcol_func_processor(uchar *int_arg)
+  {
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_PRINT("info", ("  Item name: %s", full_name()));
+    return TRUE;
+  }
+};
 
 /* Item represents one placeholder ('?') of prepared statement */
 
@@ -1688,6 +1732,7 @@ public:
   /** Item is a argument to a limit clause. */
   bool limit_clause_param;
   void set_param_type_and_swap_value(Item_param *from);
+
 };
 
 
@@ -1723,6 +1768,7 @@ public:
   { return (uint)(max_length - test(value < 0)); }
   bool eq(const Item *, bool binary_cmp) const;
   bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -1741,6 +1787,7 @@ public:
   Item_num *neg ();
   uint decimal_precision() const { return max_length; }
   bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -1782,6 +1829,7 @@ public:
   bool eq(const Item *, bool binary_cmp) const;
   void set_decimal_value(my_decimal *value_par);
   bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -1939,6 +1987,7 @@ public:
   }
   virtual void print(String *str, enum_query_type query_type);
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 
   /**
     Return TRUE if character-set-introducer was explicitly specified in the
@@ -2006,6 +2055,13 @@ public:
   }
 
   bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_static_string_func::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -2017,6 +2073,14 @@ public:
                                   CHARSET_INFO *cs= NULL):
     Item_string(name_arg, length, cs)
   {}
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_partition_func_safe_string::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_ASSERT(0); /* this is not possible */
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -2096,6 +2160,7 @@ public:
   bool eq(const Item *item, bool binary_cmp) const;
   virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -2126,6 +2191,7 @@ public:
     save_in_field(result_field, no_conversions);
   }
   void cleanup();
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -2251,7 +2317,14 @@ public:
     if (ref && result_type() == ROW_RESULT)
       (*ref)->bring_value();
   }
-
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_ref::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_ASSERT(0); /* this is not possible */
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -2484,6 +2557,14 @@ public:
   table_map used_tables() const { return (table_map) 1L; }
   bool const_item() const { return 0; }
   bool is_null() { return null_value; }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_sum::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_ASSERT(0); /* this is not possible */
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -2614,6 +2695,13 @@ public:
     return arg->walk(processor, walk_subquery, args) ||
 	    (this->*processor)(args);
   }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_insert_value::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -2710,6 +2798,14 @@ private:
     BEFORE INSERT of BEFORE UPDATE trigger.
   */
   bool read_only;
+  virtual bool check_vcol_func_processor(uchar *int_arg)
+  {
+    DBUG_ENTER("Item_trigger_field::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_ASSERT(0); /* this is not possible */
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -2769,6 +2865,15 @@ public:
   {
     return this == item;
   }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_cache::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_ASSERT(0); /* this is not possible */
+    DBUG_RETURN(TRUE);
+  }
+
 };
 
 

=== modified file 'sql/item_func.cc'
--- sql/item_func.cc	2009-02-19 09:01:25 +0000
+++ sql/item_func.cc	2009-03-24 10:47:20 +0000
@@ -3894,10 +3894,30 @@ bool Item_func_set_user_var::register_fi
     TABLE *table= (TABLE *) arg;
     if (result_field->table == table || !table)
       bitmap_set_bit(result_field->table->read_set, result_field->field_index);
+    if (result_field->vcol_info && result_field->vcol_info->expr_item)
+      return result_field->vcol_info->
+               expr_item->walk(&Item::register_field_in_read_map, 1, arg);
   }
   return 0;
 }
 
+/*
+  Mark field in bitmap supplied as *arg
+
+*/
+
+bool Item_func_set_user_var::register_field_in_bitmap(uchar *arg)
+{
+  MY_BITMAP *bitmap = (MY_BITMAP *) arg;
+  DBUG_ASSERT(bitmap);
+  if (result_field)
+  {
+    if (!bitmap)
+      return 1;
+    bitmap_set_bit(bitmap, result_field->field_index);
+  }
+  return 0;
+}
 
 /**
   Set value to user variable.

=== modified file 'sql/item_func.h'
--- sql/item_func.h	2009-02-19 09:01:25 +0000
+++ sql/item_func.h	2009-03-24 20:19:28 +0000
@@ -339,6 +339,7 @@ public:
   void fix_length_and_dec();
   bool fix_fields(THD *thd, Item **ref);
   longlong val_int() { DBUG_ASSERT(fixed == 1); return value; }
+  bool check_vcol_func_processor(uchar *int_arg) { return TRUE;}
 };
 
 
@@ -398,6 +399,7 @@ public:
   Item_func_additive_op(Item *a,Item *b) :Item_num_op(a,b) {}
   void result_precision();
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -433,6 +435,7 @@ public:
   my_decimal *decimal_op(my_decimal *);
   void result_precision();
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -465,6 +468,7 @@ public:
   }
 
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -479,6 +483,7 @@ public:
   void result_precision();
   void fix_length_and_dec();
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -495,6 +500,7 @@ public:
   void fix_num_length_and_dec();
   uint decimal_precision() const { return args[0]->decimal_precision(); }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -508,6 +514,7 @@ public:
   const char *func_name() const { return "abs"; }
   void fix_length_and_dec();
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 // A class to handle logarithmic and trigonometric functions
@@ -663,6 +670,7 @@ public:
   double real_op();
   my_decimal *decimal_op(my_decimal *);
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -675,6 +683,7 @@ public:
   double real_op();
   my_decimal *decimal_op(my_decimal *);
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 /* This handles round and truncate */
@@ -704,6 +713,13 @@ public:
   bool const_item() const { return 0; }
   void update_used_tables();
   bool fix_fields(THD *thd, Item **ref);
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_rand::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 private:
   void seed_random (Item * val);  
 };
@@ -986,6 +1002,13 @@ public:
       max_length= args[0]->max_length;
   }
   bool fix_fields(THD *thd, Item **ref);
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_last_insert_id::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -999,6 +1022,13 @@ public:
   const char *func_name() const { return "benchmark"; }
   void fix_length_and_dec() { max_length=1; maybe_null=0; }
   virtual void print(String *str, enum_query_type query_type);
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_benchmark::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -1014,6 +1044,13 @@ public:
     used_tables_cache|= RAND_TABLE_BIT;
   }
   longlong val_int();
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_sleep::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -1263,6 +1300,13 @@ class Item_func_get_lock :public Item_in
   longlong val_int();
   const char *func_name() const { return "get_lock"; }
   void fix_length_and_dec() { max_length=1; maybe_null=1;}
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_get_lock::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 class Item_func_release_lock :public Item_int_func
@@ -1273,6 +1317,13 @@ public:
   longlong val_int();
   const char *func_name() const { return "release_lock"; }
   void fix_length_and_dec() { max_length=1; maybe_null=1;}
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_release_lock::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 /* replication functions */
@@ -1286,6 +1337,13 @@ public:
   longlong val_int();
   const char *func_name() const { return "master_pos_wait"; }
   void fix_length_and_dec() { max_length=21; maybe_null=1;}
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_master_pos_wait::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -1356,6 +1414,7 @@ public:
   }
   void save_org_in_field(Field *field) { (void)save_in_field(field, 1, 0); }
   bool register_field_in_read_map(uchar *arg);
+  bool register_field_in_bitmap(uchar *arg);
   bool set_entry(THD *thd, bool create_if_not_exists);
   void cleanup();
 };
@@ -1528,6 +1587,14 @@ public:
 
   bool fix_index();
   void init_search(bool no_order);
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    /* TODO: consider adding in support for the MATCH-based virtual columns */
+    DBUG_ENTER("Item_func_match::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -1547,6 +1614,13 @@ public:
   longlong val_int();
   const char *func_name() const { return "is_free_lock"; }
   void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;}
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_is_free_lock::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 class Item_func_is_used_lock :public Item_int_func
@@ -1557,6 +1631,13 @@ public:
   longlong val_int();
   const char *func_name() const { return "is_used_lock"; }
   void fix_length_and_dec() { decimals=0; max_length=10; maybe_null=1;}
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_is_used_lock::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 /* For type casts */
@@ -1576,6 +1657,13 @@ public:
   longlong val_int();
   const char *func_name() const { return "row_count"; }
   void fix_length_and_dec() { decimals= 0; maybe_null=0; }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_row_count::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -1684,6 +1772,13 @@ public:
   {
     return sp_result_field;
   }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_sp::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -1694,6 +1789,13 @@ public:
   longlong val_int();
   const char *func_name() const { return "found_rows"; }
   void fix_length_and_dec() { decimals= 0; maybe_null=0; }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_found_rows::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -1708,5 +1810,12 @@ public:
   void fix_length_and_dec()
   { max_length= 21; unsigned_flag=1; }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_uuid_short::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 

=== modified file 'sql/item_row.h'
--- sql/item_row.h	2008-02-22 10:30:33 +0000
+++ sql/item_row.h	2009-03-24 10:47:20 +0000
@@ -76,4 +76,5 @@ public:
   bool check_cols(uint c);
   bool null_inside() { return with_null; };
   void bring_value();
-};
+  bool check_vcol_func_processor(uchar *int_arg) {return FALSE; } 
+ };

=== modified file 'sql/item_strfunc.h'
--- sql/item_strfunc.h	2009-02-19 09:01:25 +0000
+++ sql/item_strfunc.h	2009-03-24 10:47:20 +0000
@@ -335,6 +335,14 @@ public:
   String *val_str(String *);
   void fix_length_and_dec() { maybe_null=1; max_length = 13; }
   const char *func_name() const { return "encrypt"; }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_encrypt::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
+
 };
 
 #include "sql_crypt.h"
@@ -372,6 +380,13 @@ public:
     call
   */
   virtual const char *fully_qualified_func_name() const = 0;
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_sysconst::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -635,6 +650,13 @@ public:
     maybe_null=1;
     max_length=MAX_BLOB_WIDTH;
   }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_load_file::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -831,5 +853,12 @@ public:
   }
   const char *func_name() const{ return "uuid"; }
   String *val_str(String *);
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_uuid::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 

=== modified file 'sql/item_subselect.h'
--- sql/item_subselect.h	2008-02-22 10:30:33 +0000
+++ sql/item_subselect.h	2009-03-26 19:13:42 +0000
@@ -126,6 +126,13 @@ public:
   virtual void reset_value_registration() {}
   enum_parsing_place place() { return parsing_place; }
   bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_sum::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 
   /**
     Get the SELECT_LEX structure associated with this Item.

=== modified file 'sql/item_sum.h'
--- sql/item_sum.h	2008-12-09 19:43:10 +0000
+++ sql/item_sum.h	2009-03-25 21:24:46 +0000
@@ -384,6 +384,13 @@ public:  
   Item *get_arg(int i) { return args[i]; }
   Item *set_arg(int i, THD *thd, Item *new_val);
   uint get_arg_count() { return arg_count; }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_sum::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -664,6 +671,13 @@ public:
   }
   void fix_length_and_dec() {}
   enum Item_result result_type () const { return hybrid_type; }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_avg_field::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -732,6 +746,13 @@ public:
   }
   void fix_length_and_dec() {}
   enum Item_result result_type () const { return hybrid_type; }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_variance_field::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 

=== modified file 'sql/item_timefunc.h'
--- sql/item_timefunc.h	2008-12-23 14:21:01 +0000
+++ sql/item_timefunc.h	2009-03-24 10:47:20 +0000
@@ -70,6 +70,7 @@ public:
   enum_monotonicity_info get_monotonicity_info() const;
   longlong val_int_endpoint(bool left_endp, bool *incl_endp);
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -86,6 +87,7 @@ public:
     maybe_null=1; 
   }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -111,6 +113,7 @@ public:
     maybe_null=1; 
   }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -124,6 +127,7 @@ public:
   enum Item_result result_type () const { return STRING_RESULT; }
   void fix_length_and_dec();
   bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
+  bool check_vcol_func_processor(uchar *int_arg) {return FALSE;}
 };
 
 
@@ -140,6 +144,7 @@ public:
     maybe_null=1; 
   }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -156,6 +161,7 @@ public:
     maybe_null=1;
   }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -172,6 +178,7 @@ public:
     maybe_null=1;
   }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -188,6 +195,7 @@ public:
      maybe_null=1;
   }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -204,6 +212,7 @@ public:
     maybe_null=1;
   }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -234,6 +243,7 @@ public:
     maybe_null=1;
   }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -252,6 +262,7 @@ public:
     maybe_null=1;
   }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -282,6 +293,7 @@ public:
     maybe_null=1;
   }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 class Item_func_dayname :public Item_func_weekday
@@ -294,6 +306,7 @@ class Item_func_dayname :public Item_fun
   enum Item_result result_type () const { return STRING_RESULT; }
   void fix_length_and_dec();
   bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -310,6 +323,18 @@ public:
     decimals=0;
     max_length=10*MY_CHARSET_BIN_MB_MAXLEN;
   }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    /*
+      TODO: Allow UNIX_TIMESTAMP called with an argument to be a part
+      of the expression for a virtual column
+    */
+    DBUG_ENTER("Item_func_unix_timestamp::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
+
 };
 
 
@@ -325,6 +350,7 @@ public:
     max_length=10*MY_CHARSET_BIN_MB_MAXLEN;
   }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -444,6 +470,14 @@ public:
   */
   virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0;
   bool result_as_longlong() { return TRUE; }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_curtime::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
+
 };
 
 
@@ -480,6 +514,13 @@ public:
   void fix_length_and_dec();
   bool get_date(MYSQL_TIME *res, uint fuzzy_date);
   virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0;
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_curdate::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -520,6 +561,13 @@ public:
   void fix_length_and_dec();
   bool get_date(MYSQL_TIME *res, uint fuzzy_date);
   virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0;
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_func_now::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 
@@ -577,6 +625,7 @@ public:
   const char *func_name() const { return "from_days"; }
   bool get_date(MYSQL_TIME *res, uint fuzzy_date);
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -704,6 +753,7 @@ class Item_extract :public Item_int_func
   bool eq(const Item *item, bool binary_cmp) const;
   virtual void print(String *str, enum_query_type query_type);
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 
@@ -952,6 +1002,7 @@ public:
     maybe_null=1;
   }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+  bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
 };
 
 

=== modified file 'sql/item_xmlfunc.cc'
--- sql/item_xmlfunc.cc	2009-01-31 21:22:44 +0000
+++ sql/item_xmlfunc.cc	2009-03-24 21:10:53 +0000
@@ -220,6 +220,14 @@ public:
     collation.collation= pxml->charset();
   }
   const char *func_name() const { return "nodeset"; }
+  bool check_vcol_func_processor(uchar *int_arg)
+  {
+    DBUG_ENTER("Item_nodeset_func::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
+
 };
 
 
@@ -526,6 +534,13 @@ public:
   enum Type type() const { return XPATH_NODESET_CMP; };
   const char *func_name() const { return "xpath_nodeset_to_const_comparator"; }
   bool is_bool_func() { return 1; }
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_nodeset_to_const_comparator::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 
   longlong val_int()
   {

=== modified file 'sql/item_xmlfunc.h'
--- sql/item_xmlfunc.h	2007-11-21 12:00:09 +0000
+++ sql/item_xmlfunc.h	2009-03-24 10:47:20 +0000
@@ -40,6 +40,13 @@ public:
   }
   void fix_length_and_dec();
   String *parse_xml(String *raw_xml, String *parsed_xml_buf);
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_xml_str_func::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_RETURN(TRUE);
+  }
 };
 
 

=== modified file 'sql/lex.h'
--- sql/lex.h	2008-04-28 16:24:05 +0000
+++ sql/lex.h	2009-03-27 12:33:10 +0000
@@ -62,6 +62,7 @@ static SYMBOL symbols[] = {
   { "ALL",		SYM(ALL)},
   { "ALGORITHM",	SYM(ALGORITHM_SYM)},
   { "ALTER",		SYM(ALTER)},
+  { "ALWAYS",           SYM(ALWAYS_SYM)},
   { "ANALYZE",		SYM(ANALYZE_SYM)},
   { "AND",		SYM(AND_SYM)},
   { "ANY",              SYM(ANY_SYM)},
@@ -220,6 +221,7 @@ static SYMBOL symbols[] = {
   { "FULL",		SYM(FULL)},
   { "FULLTEXT",		SYM(FULLTEXT_SYM)},
   { "FUNCTION",		SYM(FUNCTION_SYM)},
+  { "GENERATED",        SYM(GENERATED_SYM)},
   { "GEOMETRY",		SYM(GEOMETRY_SYM)},
   { "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION)},
   { "GET_FORMAT",       SYM(GET_FORMAT)},
@@ -320,6 +322,7 @@ static SYMBOL symbols[] = {
   { "MASTER_SSL_KEY",   SYM(MASTER_SSL_KEY_SYM)},
   { "MASTER_SSL_VERIFY_SERVER_CERT", SYM(MASTER_SSL_VERIFY_SERVER_CERT_SYM)},
   { "MASTER_USER",           SYM(MASTER_USER_SYM)},
+  { "MATERIALIZED",     SYM(MATERIALIZED_SYM)},
   { "MATCH",		SYM(MATCH)},
   { "MAX_CONNECTIONS_PER_HOUR", SYM(MAX_CONNECTIONS_PER_HOUR)},
   { "MAX_QUERIES_PER_HOUR", SYM(MAX_QUERIES_PER_HOUR)},
@@ -388,6 +391,7 @@ static SYMBOL symbols[] = {
   { "PARSER",           SYM(PARSER_SYM)},
   { "PAGE",	        SYM(PAGE_SYM)},
   { "PAGE_CHECKSUM",    SYM(PAGE_CHECKSUM_SYM)},
+  { "PARSE_VCOL_EXPR",  SYM(PARSE_VCOL_EXPR_SYM)},
   { "PARTIAL",		SYM(PARTIAL)},
   { "PARTITION",        SYM(PARTITION_SYM)},
   { "PARTITIONING",     SYM(PARTITIONING_SYM)},
@@ -581,6 +585,7 @@ static SYMBOL symbols[] = {
   { "VARCHARACTER",	SYM(VARCHAR)},
   { "VARIABLES",	SYM(VARIABLES)},
   { "VARYING",		SYM(VARYING)},
+  { "VIRTUAL",          SYM(VIRTUAL_SYM)},
   { "WAIT",		SYM(WAIT_SYM)},
   { "WARNINGS",		SYM(WARNINGS)},
   { "WEEK",		SYM(WEEK_SYM)},

=== modified file 'sql/mysql_priv.h'
--- sql/mysql_priv.h	2009-03-12 22:27:35 +0000
+++ sql/mysql_priv.h	2009-03-30 14:42:36 +0000
@@ -1312,6 +1312,8 @@ find_field_in_table(THD *thd, TABLE *tab
                     bool allow_rowid, uint *cached_field_index_ptr);
 Field *
 find_field_in_table_sef(TABLE *table, const char *name);
+int update_virtual_fields_marked_for_write(TABLE *table,
+                                           bool ignore_stored= TRUE);
 
 #endif /* MYSQL_SERVER */
 
@@ -1432,14 +1434,16 @@ bool add_field_to_list(THD *thd, LEX_STR
 		       LEX_STRING *comment,
 		       char *change, List<String> *interval_list,
 		       CHARSET_INFO *cs,
-		       uint uint_geom_type);
+		       uint uint_geom_type,
+                       Virtual_column_info *vcol_info);
 Create_field * new_create_field(THD *thd, char *field_name, enum_field_types type,
 				char *length, char *decimals,
 				uint type_modifier, 
 				Item *default_value, Item *on_update_value,
 				LEX_STRING *comment, char *change, 
 				List<String> *interval_list, CHARSET_INFO *cs,
-				uint uint_geom_type);
+				uint uint_geom_type,
+				Virtual_column_info *vcol_info);
 void store_position_for_column(const char *name);
 bool add_to_list(THD *thd, SQL_LIST &list,Item *group,bool asc);
 bool push_new_name_resolution_context(THD *thd,

=== modified file 'sql/procedure.h'
--- sql/procedure.h	2008-03-21 15:48:28 +0000
+++ sql/procedure.h	2009-03-24 21:48:56 +0000
@@ -43,6 +43,14 @@ public:
     init_make_field(tmp_field,field_type());
   }
   unsigned int size_of() { return sizeof(*this);}  
+  bool check_vcol_func_processor(uchar *int_arg) 
+  {
+    DBUG_ENTER("Item_sum::check_vcol_func_processor");
+    DBUG_PRINT("info",
+      ("check_vcol_func_processor returns TRUE: unsupported function"));
+    DBUG_ASSERT(0); /* this is not possible */
+    DBUG_RETURN(TRUE);
+  }
 };
 
 class Item_proc_real :public Item_proc

=== modified file 'sql/records.cc'
--- sql/records.cc	2008-07-17 18:26:55 +0000
+++ sql/records.cc	2009-03-24 10:47:20 +0000
@@ -323,6 +323,7 @@ static int rr_quick(READ_RECORD *info)
       break;
     }
   }
+  update_virtual_fields_marked_for_write(info->table);
   return tmp;
 }
 
@@ -395,6 +396,8 @@ int rr_sequential(READ_RECORD *info)
       break;
     }
   }
+  if (!tmp)
+    update_virtual_fields_marked_for_write(info->table);
   return tmp;
 }
 

=== modified file 'sql/share/errmsg.txt'
--- sql/share/errmsg.txt	2008-12-10 09:02:25 +0000
+++ sql/share/errmsg.txt	2009-03-25 21:24:46 +0000
@@ -6154,3 +6154,30 @@ WARN_PLUGIN_BUSY
 
 ER_VARIABLE_IS_READONLY
   eng "%s variable '%s' is read-only. Use SET %s to assign the value"
+
+ER_VCOL_BASED_ON_VCOL
+        eng "A computed column cannot be based on a computed column"
+
+ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED
+        eng "Function or expression is not allowed for column '%s'."
+
+ER_DATA_CONVERSION_ERROR_FOR_VIRTUAL_COLUMN
+        eng "Generated value for computed column '%s' cannot be converted to type '%s'."
+
+ER_PRIMARY_KEY_BASED_ON_VIRTUAL_COLUMN
+        eng "Primary key cannot be defined upon a computed column."
+
+ER_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN
+        eng "Key/Index cannot be defined on a non-stored computed column."
+
+ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN
+        eng "Cannot define foreign key with %s clause on a computed column."
+
+ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN
+        eng "The value specified for computed column '%s' in table '%s' ignored."
+
+ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN
+        eng "'%s' is not yet supported for computed columns."
+
+ER_CONST_EXPR_IN_VCOL
+         eng "Constant expression in computed column function is not allowed."

=== modified file 'sql/sp_head.cc'
--- sql/sp_head.cc	2009-02-19 09:01:25 +0000
+++ sql/sp_head.cc	2009-03-24 10:47:20 +0000
@@ -815,6 +815,8 @@ sp_head::create_result_field(uint field_
                       m_return_field_def.interval,
                       field_name ? field_name : (const char *) m_name.str);
 
+  field->vcol_info= m_return_field_def.vcol_info;
+  field->stored_in_db= m_return_field_def.stored_in_db;
   if (field)
     field->init(table);
   
@@ -2194,7 +2196,8 @@ sp_head::fill_field_definition(THD *thd,
                       &lex->interval_list,
                       lex->charset ? lex->charset :
                                      thd->variables.collation_database,
-                      lex->uint_geom_type))
+                      lex->uint_geom_type,
+		      lex->vcol_info))
     return TRUE;
 
   if (field_def->interval_list.elements)

=== modified file 'sql/sql_base.cc'
--- sql/sql_base.cc	2009-02-19 09:01:25 +0000
+++ sql/sql_base.cc	2009-03-30 14:42:36 +0000
@@ -5875,6 +5875,23 @@ find_field_in_table(THD *thd, TABLE *tab
 
   if (field_ptr && *field_ptr)
   {
+    if ((*field_ptr)->vcol_info)
+    {
+      if (thd->mark_used_columns != MARK_COLUMNS_NONE)
+      {
+        Item *vcol_item= (*field_ptr)->vcol_info->expr_item;
+        DBUG_ASSERT(vcol_item);
+        vcol_item->walk(&Item::register_field_in_read_map, 1, (uchar *) 0);
+        /*
+          Set the virtual field for write here if
+          1) this procedure is called for a read-only operation (SELECT), and
+          2) the virtual column is not physically stored in the table
+        */
+        if (thd->mark_used_columns != MARK_COLUMNS_WRITE &&
+            !(*field_ptr)->stored_in_db)
+          bitmap_set_bit((*field_ptr)->table->write_set, (*field_ptr)->field_index);
+      }
+    }
     *cached_field_index_ptr= field_ptr - table->field;
     field= *field_ptr;
   }
@@ -7857,6 +7874,17 @@ insert_fields(THD *thd, Name_resolution_
       {
         /* Mark fields as used to allow storage engine to optimze access */
         bitmap_set_bit(field->table->read_set, field->field_index);
+        /*
+          Mark virtual fields for write and others that the virtual fields
+          depend on for read.
+        */
+        if (field->vcol_info)
+        {
+          Item *vcol_item= field->vcol_info->expr_item;
+          DBUG_ASSERT(vcol_item);
+          vcol_item->walk(&Item::register_field_in_read_map, 1, (uchar *) 0);
+          bitmap_set_bit(field->table->write_set, field->field_index);
+        }
         if (table)
         {
           table->covering_keys.intersect(field->part_of_key);
@@ -8068,7 +8096,10 @@ fill_record(THD * thd, List<Item> &field
   Item *value, *fld;
   Item_field *field;
   TABLE *table= 0;
+  List<TABLE> tbl_list;
+  bool abort_on_warning_saved= thd->abort_on_warning;
   DBUG_ENTER("fill_record");
+  tbl_list.empty();
 
   /*
     Reset the table->auto_increment_field_not_null as it is valid for
@@ -8102,14 +8133,54 @@ fill_record(THD * thd, List<Item> &field
     table= rfield->table;
     if (rfield == table->next_number_field)
       table->auto_increment_field_not_null= TRUE;
+    if (rfield->vcol_info && 
+        value->type() != Item::DEFAULT_VALUE_ITEM && 
+        value->type() != Item::NULL_ITEM &&
+        table->s->table_category != TABLE_CATEGORY_TEMPORARY)
+    {
+      thd->abort_on_warning= FALSE;
+      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                          ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN,
+                          ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN),
+                          rfield->field_name, table->s->table_name.str);
+      thd->abort_on_warning= abort_on_warning_saved;
+    }
     if ((value->save_in_field(rfield, 0) < 0) && !ignore_errors)
     {
       my_message(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), MYF(0));
       goto err;
     }
+    tbl_list.push_back(table);
+  }
+  /* Update virtual fields*/
+  thd->abort_on_warning= FALSE;
+  if (tbl_list.head())
+  {
+    List_iterator_fast<TABLE> t(tbl_list);
+    TABLE *prev_table= 0;
+    while ((table= t++))
+    {
+      /*
+        Do simple optimization to prevent unnecessary re-generating 
+        values for virtual fields
+      */
+      if (table != prev_table)
+      {
+        prev_table= table;
+        if (table->vfield)
+        {
+          if (update_virtual_fields_marked_for_write(table, FALSE))
+          {
+            goto err;
+          }
+        }
+      }
+    }
   }
+  thd->abort_on_warning= abort_on_warning_saved;
   DBUG_RETURN(thd->is_error());
 err:
+  thd->abort_on_warning= abort_on_warning_saved;
   if (table)
     table->auto_increment_field_not_null= FALSE;
   DBUG_RETURN(TRUE);
@@ -8145,9 +8216,31 @@ fill_record_n_invoke_before_triggers(THD
                                      Table_triggers_list *triggers,
                                      enum trg_event_type event)
 {
-  return (fill_record(thd, fields, values, ignore_errors) ||
-          (triggers && triggers->process_triggers(thd, event,
-                                                  TRG_ACTION_BEFORE, TRUE)));
+  bool result;
+  result= (fill_record(thd, fields, values, ignore_errors) ||
+           (triggers && triggers->process_triggers(thd, event,
+                                                   TRG_ACTION_BEFORE, TRUE)));
+  /*
+    Re-calculate virtual fields to cater for cases when base columns are
+    updated by the triggers.
+  */
+  if (!result && triggers)
+  {
+    TABLE *table= 0;
+    List_iterator_fast<Item> f(fields);
+    Item *fld;
+    Item_field *item_field;
+    if (fields.elements)
+    {
+      fld= (Item_field*)f++;
+      item_field= fld->filed_for_view_update();
+      if (item_field && item_field->field &&
+          (table= item_field->field->table) &&
+        table->vfield)
+        result= update_virtual_fields_marked_for_write(table, FALSE);
+    }
+  }
+  return result;
 }
 
 
@@ -8175,11 +8268,14 @@ bool
 fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors)
 {
   List_iterator_fast<Item> v(values);
+  List<TABLE> tbl_list;
   Item *value;
   TABLE *table= 0;
+  bool abort_on_warning_saved= thd->abort_on_warning;
   DBUG_ENTER("fill_record");
 
   Field *field;
+  tbl_list.empty();
   /*
     Reset the table->auto_increment_field_not_null as it is valid for
     only one row.
@@ -8199,12 +8295,52 @@ fill_record(THD *thd, Field **ptr, List<
     table= field->table;
     if (field == table->next_number_field)
       table->auto_increment_field_not_null= TRUE;
+    if (field->vcol_info && 
+        value->type() != Item::DEFAULT_VALUE_ITEM && 
+        value->type() != Item::NULL_ITEM &&
+        table->s->table_category != TABLE_CATEGORY_TEMPORARY)
+    {
+      thd->abort_on_warning= FALSE;
+      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                          ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN,
+                          ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN),
+                          field->field_name, table->s->table_name.str);
+      thd->abort_on_warning= abort_on_warning_saved;
+    }
     if (value->save_in_field(field, 0) < 0)
       goto err;
+    tbl_list.push_back(table);
   }
+  /* Update virtual fields*/
+  thd->abort_on_warning= FALSE;
+  if (tbl_list.head())
+  {
+    List_iterator_fast<TABLE> t(tbl_list);
+    TABLE *prev_table= 0;
+    while ((table= t++))
+    {
+      /*
+        Do simple optimization to prevent unnecessary re-generating 
+        values for virtual fields
+      */
+      if (table != prev_table)
+      {
+        prev_table= table;
+        if (table->vfield)
+        {
+          if (update_virtual_fields_marked_for_write(table, FALSE))
+          {
+            goto err;
+          }
+        }
+      }
+    }
+  }
+  thd->abort_on_warning= abort_on_warning_saved;
   DBUG_RETURN(thd->is_error());
 
 err:
+  thd->abort_on_warning= abort_on_warning_saved;
   if (table)
     table->auto_increment_field_not_null= FALSE;
   DBUG_RETURN(TRUE);
@@ -8240,9 +8376,22 @@ fill_record_n_invoke_before_triggers(THD
                                      Table_triggers_list *triggers,
                                      enum trg_event_type event)
 {
-  return (fill_record(thd, ptr, values, ignore_errors) ||
-          (triggers && triggers->process_triggers(thd, event,
-                                                  TRG_ACTION_BEFORE, TRUE)));
+  bool result;
+  result= (fill_record(thd, ptr, values, ignore_errors) ||
+           (triggers && triggers->process_triggers(thd, event,
+                                                   TRG_ACTION_BEFORE, TRUE)));
+  /*
+    Re-calculate virtual fields to cater for cases when base columns are
+    updated by the triggers.
+  */
+  if (!result && triggers && *ptr)
+  {
+    TABLE *table= (*ptr)->table;
+    if (table->vfield)
+      result= update_virtual_fields_marked_for_write(table, FALSE);
+  }
+  return result;
+
 }
 
 

=== modified file 'sql/sql_class.cc'
--- sql/sql_class.cc	2009-03-12 22:27:35 +0000
+++ sql/sql_class.cc	2009-03-24 10:47:20 +0000
@@ -193,6 +193,57 @@ bool foreign_key_prefix(Key *a, Key *b)
 #endif
 }
 
+/*
+  Check if the foreign key options are compatible with columns
+  on which the FK is created.
+
+  RETURN
+    0   Key valid
+    1   Key invalid
+*/
+bool Foreign_key::validate(List<Create_field> &table_fields)
+{
+  Create_field  *sql_field;
+  Key_part_spec *column;
+  List_iterator<Key_part_spec> cols(columns);
+  List_iterator<Create_field> it(table_fields);
+  DBUG_ENTER("Foreign_key::validate");
+  while ((column= cols++))
+  {
+    it.rewind();
+    while ((sql_field= it++) &&
+           my_strcasecmp(system_charset_info,
+                         column->field_name,
+                         sql_field->field_name)) {}
+    if (!sql_field)
+    {
+      my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
+      DBUG_RETURN(TRUE);
+    }
+    if (type == Key::FOREIGN_KEY && sql_field->vcol_info)
+    {
+      if (delete_opt == FK_OPTION_SET_NULL)
+      {
+        my_error(ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN, MYF(0), 
+                 "ON DELETE SET NULL");
+        DBUG_RETURN(TRUE);
+      }
+      if (update_opt == FK_OPTION_SET_NULL)
+      {
+        my_error(ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN, MYF(0), 
+                 "ON UPDATE SET NULL");
+        DBUG_RETURN(TRUE);
+      }
+      if (update_opt == FK_OPTION_CASCADE)
+      {
+        my_error(ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN, MYF(0), 
+                 "ON UPDATE CASCADE");
+        DBUG_RETURN(TRUE);
+      }
+    }
+  }
+  DBUG_RETURN(FALSE);
+}
 
 /****************************************************************************
 ** Thread specific functions

=== modified file 'sql/sql_class.h'
--- sql/sql_class.h	2009-03-12 22:27:35 +0000
+++ sql/sql_class.h	2009-03-24 10:47:20 +0000
@@ -249,6 +249,8 @@ public:
   */
   virtual Key *clone(MEM_ROOT *mem_root) const
   { return new (mem_root) Foreign_key(*this, mem_root); }
+  /* Used to validate foreign key options */
+  bool validate(List<Create_field> &table_fields);
 };
 
 typedef struct st_mysql_lock

=== modified file 'sql/sql_handler.cc'
--- sql/sql_handler.cc	2008-02-19 12:58:08 +0000
+++ sql/sql_handler.cc	2009-03-24 10:47:20 +0000
@@ -636,6 +636,8 @@ retry:
       }
       goto ok;
     }
+    /* Generate values for virtual fields */
+    update_virtual_fields_marked_for_write(table);
     if (cond && !cond->val_int())
       continue;
     if (num_rows >= offset_limit_cnt)

=== modified file 'sql/sql_insert.cc'
--- sql/sql_insert.cc	2009-02-19 09:01:25 +0000
+++ sql/sql_insert.cc	2009-03-24 10:47:21 +0000
@@ -279,6 +279,9 @@ static int check_insert_fields(THD *thd,
                        table->timestamp_field->field_index);
       }
     }
+    /* Mark all virtual columns for write*/
+    if (table->vfield)
+      table->mark_virtual_columns();
   }
   // For the values we need select_priv
 #ifndef NO_EMBEDDED_ACCESS_CHECKS

=== modified file 'sql/sql_lex.cc'
--- sql/sql_lex.cc	2009-02-15 10:58:34 +0000
+++ sql/sql_lex.cc	2009-03-24 10:47:21 +0000
@@ -340,6 +340,7 @@ void lex_start(THD *thd)
   lex->reset_query_tables_list(FALSE);
   lex->expr_allows_subselect= TRUE;
   lex->use_only_table_context= FALSE;
+  lex->parse_vcol_expr= FALSE;
 
   lex->name.str= 0;
   lex->name.length= 0;

=== modified file 'sql/sql_lex.h'
--- sql/sql_lex.h	2009-01-15 18:11:25 +0000
+++ sql/sql_lex.h	2009-03-30 14:42:36 +0000
@@ -1521,6 +1521,7 @@ typedef struct st_lex : public Query_tab
   LEX_USER *grant_user;
   XID *xid;
   THD *thd;
+  Virtual_column_info *vcol_info;
 
   /* maintain a list of used plugins for this LEX */
   DYNAMIC_ARRAY plugins;
@@ -1603,6 +1604,14 @@ typedef struct st_lex : public Query_tab
     syntax error back.
   */
   bool expr_allows_subselect;
+  /*
+    A special command "PARSE_VCOL_EXPR" is defined for the parser 
+    to translate an expression statement of a virtual column \
+    (stored in the *.frm file as a string) into an Item object.
+    The following flag is used to prevent other applications to use 
+    this command.
+  */
+  bool parse_vcol_expr;
 
   thr_lock_type lock_option;
   enum SSL_type ssl_type;			/* defined in violite.h */

=== modified file 'sql/sql_parse.cc'
--- sql/sql_parse.cc	2009-02-19 09:01:25 +0000
+++ sql/sql_parse.cc	2009-03-30 14:42:36 +0000
@@ -5907,7 +5907,8 @@ bool add_field_to_list(THD *thd, LEX_STR
                        LEX_STRING *comment,
 		       char *change,
                        List<String> *interval_list, CHARSET_INFO *cs,
-		       uint uint_geom_type)
+		       uint uint_geom_type,
+		       Virtual_column_info *vcol_info)
 {
   register Create_field *new_field;
   LEX  *lex= thd->lex;
@@ -5993,7 +5994,7 @@ bool add_field_to_list(THD *thd, LEX_STR
   if (!(new_field= new Create_field()) ||
       new_field->init(thd, field_name->str, type, length, decimals, type_modifier,
                       default_value, on_update_value, comment, change,
-                      interval_list, cs, uint_geom_type))
+                      interval_list, cs, uint_geom_type, vcol_info))
     DBUG_RETURN(1);
 
   lex->alter_info.create_list.push_back(new_field);

=== modified file 'sql/sql_partition.cc'
--- sql/sql_partition.cc	2009-02-15 10:58:34 +0000
+++ sql/sql_partition.cc	2009-03-26 21:13:30 +0000
@@ -964,8 +964,9 @@ bool fix_fields_part_func(THD *thd, Item
 
   save_use_only_table_context= thd->lex->use_only_table_context;
   thd->lex->use_only_table_context= TRUE;
-  
+
   error= func_expr->fix_fields(thd, (Item**)0);
+  func_expr->walk(&Item::vcol_in_partition_func_processor, 0, NULL);
 
   thd->lex->use_only_table_context= save_use_only_table_context;
 

=== modified file 'sql/sql_select.cc'
--- sql/sql_select.cc	2009-02-19 09:01:25 +0000
+++ sql/sql_select.cc	2009-03-24 10:47:21 +0000
@@ -11717,6 +11717,7 @@ join_read_system(JOIN_TAB *tab)
       empty_record(table);			// Make empty record
       return -1;
     }
+    update_virtual_fields_marked_for_write(table);
     store_record(table,record[1]);
   }
   else if (!table->status)			// Only happens with left join
@@ -11765,6 +11766,7 @@ join_read_const(JOIN_TAB *tab)
 	return report_error(table, error);
       return -1;
     }
+    update_virtual_fields_marked_for_write(table);
     store_record(table,record[1]);
   }
   else if (!(table->status & ~STATUS_NULL_ROW))	// Only happens with left join
@@ -11854,6 +11856,8 @@ join_read_always_key(JOIN_TAB *tab)
       return report_error(table, error);
     return -1; /* purecov: inspected */
   }
+  if (!error)
+    update_virtual_fields_marked_for_write(table);
   return 0;
 }
 
@@ -11881,6 +11885,7 @@ join_read_last_key(JOIN_TAB *tab)
       return report_error(table, error);
     return -1; /* purecov: inspected */
   }
+  update_virtual_fields_marked_for_write(table);
   return 0;
 }
 
@@ -11909,6 +11914,7 @@ join_read_next_same(READ_RECORD *info)
     table->status= STATUS_GARBAGE;
     return -1;
   }
+  update_virtual_fields_marked_for_write(table);
   return 0;
 }
 
@@ -11922,12 +11928,14 @@ join_read_prev_same(READ_RECORD *info)
 
   if ((error=table->file->index_prev(table->record[0])))
     return report_error(table, error);
+  update_virtual_fields_marked_for_write(table);
   if (key_cmp_if_same(table, tab->ref.key_buff, tab->ref.key,
                       tab->ref.key_length))
   {
     table->status=STATUS_NOT_FOUND;
     error= -1;
   }
+  update_virtual_fields_marked_for_write(table);
   return error;
 }
 
@@ -11996,6 +12004,8 @@ join_read_first(JOIN_TAB *tab)
       report_error(table, error);
     return -1;
   }
+  if (not error)
+    update_virtual_fields_marked_for_write(tab->table);
   return 0;
 }
 
@@ -12006,6 +12016,8 @@ join_read_next(READ_RECORD *info)
   int error;
   if ((error=info->file->index_next(info->record)))
     return report_error(info->table, error);
+  if (not error)
+    update_virtual_fields_marked_for_write(info->table);
   return 0;
 }
 
@@ -12031,6 +12043,8 @@ join_read_last(JOIN_TAB *tab)
     table->file->ha_index_init(tab->index, 1);
   if ((error= tab->table->file->index_last(tab->table->record[0])))
     return report_error(table, error);
+  if (not error)
+    update_virtual_fields_marked_for_write(tab->table);
   return 0;
 }
 
@@ -12041,6 +12055,8 @@ join_read_prev(READ_RECORD *info)
   int error;
   if ((error= info->file->index_prev(info->record)))
     return report_error(info->table, error);
+  if (not error)
+    update_virtual_fields_marked_for_write(info->table);
   return 0;
 }
 
@@ -12062,6 +12078,8 @@ join_ft_read_first(JOIN_TAB *tab)
 
   if ((error= table->file->ft_read(table->record[0])))
     return report_error(table, error);
+  if (not error)
+    update_virtual_fields_marked_for_write(table);
   return 0;
 }
 
@@ -12071,6 +12089,7 @@ join_ft_read_next(READ_RECORD *info)
   int error;
   if ((error= info->file->ft_read(info->table->record[0])))
     return report_error(info->table, error);
+  update_virtual_fields_marked_for_write(info->table);
   return 0;
 }
 

=== modified file 'sql/sql_show.cc'
--- sql/sql_show.cc	2009-02-19 09:01:25 +0000
+++ sql/sql_show.cc	2009-03-27 12:45:15 +0000
@@ -1175,6 +1175,19 @@ int store_create_info(THD *thd, TABLE_LI
     field->sql_type(type);
     packet->append(type.ptr(), type.length(), system_charset_info);
 
+    if (field->vcol_info)
+    {
+      packet->append(STRING_WITH_LEN(" AS ("));
+      packet->append(field->vcol_info->expr_str.str,
+                     field->vcol_info->expr_str.length,
+                     system_charset_info);
+      packet->append(STRING_WITH_LEN(")"));
+      if (field->stored_in_db)
+        packet->append(STRING_WITH_LEN(" MATERIALIZED"));
+      else
+        packet->append(STRING_WITH_LEN(" VIRTUAL"));
+    }
+
     if (field->has_charset() &&
         !(thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)))
     {
@@ -1205,7 +1218,8 @@ int store_create_info(THD *thd, TABLE_LI
       packet->append(STRING_WITH_LEN(" NULL"));
     }
 
-    if (get_field_default_value(thd, table, field, &def_value, 1))
+    if (!field->vcol_info &&
+        get_field_default_value(thd, table, field, &def_value, 1))
     {
       packet->append(STRING_WITH_LEN(" DEFAULT "));
       packet->append(def_value.ptr(), def_value.length(), system_charset_info);
@@ -3876,6 +3890,8 @@ static int get_schema_column_record(THD 
         field->unireg_check != Field::TIMESTAMP_DN_FIELD)
       table->field[16]->store(STRING_WITH_LEN("on update CURRENT_TIMESTAMP"),
                               cs);
+    if (field->vcol_info)
+      table->field[16]->store(STRING_WITH_LEN("VIRTUAL"), cs);
 
     table->field[18]->store(field->comment.str, field->comment.length, cs);
     if (schema_table_store_record(thd, table))

=== modified file 'sql/sql_table.cc'
--- sql/sql_table.cc	2009-02-19 09:01:25 +0000
+++ sql/sql_table.cc	2009-03-26 21:17:10 +0000
@@ -2166,7 +2166,8 @@ int prepare_create_field(Create_field *s
                           (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
     break;
   }
-  if (!(sql_field->flags & NOT_NULL_FLAG))
+  if (!(sql_field->flags & NOT_NULL_FLAG) ||
+      (sql_field->vcol_info))  /* Make virtual columns allow NULL values */
     sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL;
   if (sql_field->flags & NO_DEFAULT_VALUE_FLAG)
     sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT;
@@ -2480,6 +2481,8 @@ mysql_prepare_create_table(THD *thd, HA_
             null_fields--;
 	  sql_field->flags=		dup_field->flags;
           sql_field->interval=          dup_field->interval;
+          sql_field->vcol_info=         dup_field->vcol_info;
+          sql_field->stored_in_db=      dup_field->stored_in_db;
 	  it2.remove();			// Remove first (create) definition
 	  select_field_pos--;
 	  break;
@@ -2512,7 +2515,23 @@ mysql_prepare_create_table(THD *thd, HA_
     sql_field->offset= record_offset;
     if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
       auto_increment++;
-    record_offset+= sql_field->pack_length;
+    /*
+      For now skip fields that are not physically stored in the database
+      (virtual fields) and update their offset later 
+      (see the next loop).
+    */
+    if (sql_field->stored_in_db)
+      record_offset+= sql_field->pack_length;
+  }
+  /* Update virtual fields' offset*/
+  it.rewind();
+  while ((sql_field=it++))
+  {
+    if (!sql_field->stored_in_db)
+    {
+      sql_field->offset= record_offset;
+      record_offset+= sql_field->pack_length;
+    }
   }
   if (timestamps_with_niladic > 1)
   {
@@ -2562,6 +2581,8 @@ mysql_prepare_create_table(THD *thd, HA_
     if (key->type == Key::FOREIGN_KEY)
     {
       fk_key_count++;
+      if (((Foreign_key *)key)->validate(alter_info->create_list))
+        DBUG_RETURN(TRUE);
       Foreign_key *fk_key= (Foreign_key*) key;
       if (fk_key->ref_columns.elements &&
 	  fk_key->ref_columns.elements != fk_key->columns.elements)
@@ -2848,6 +2869,17 @@ mysql_prepare_create_table(THD *thd, HA_
 	  }
 	}
 #endif
+        if (!sql_field->stored_in_db)
+        {
+          /* Key fields must always be physically stored. */
+          my_error(ER_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN, MYF(0));
+          DBUG_RETURN(TRUE);
+        }
+        if (key->type == Key::PRIMARY && sql_field->vcol_info)
+        {
+          my_error(ER_PRIMARY_KEY_BASED_ON_VIRTUAL_COLUMN, MYF(0));
+          DBUG_RETURN(TRUE);
+        }
 	if (!(sql_field->flags & NOT_NULL_FLAG))
 	{
 	  if (key->type == Key::PRIMARY)
@@ -5357,6 +5389,18 @@ compare_tables(TABLE *table,
       DBUG_RETURN(0);
     }
 
+    /*
+      Check if the altered column is a stored virtual field.
+      TODO: Mark such a column with an alter flag only if
+      the expression functions are not equal.
+    */
+    if (field->stored_in_db && field->vcol_info ||
+        field->vcol_info && field->vcol_info->is_field_implicitly_stored())
+    {
+      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+      DBUG_RETURN(0);
+    }
+
     /* Don't pack rows in old tables if the user has requested this. */
       if (create_info->row_type == ROW_TYPE_DYNAMIC ||
           (tmp_new_field->flags & BLOB_FLAG) ||
@@ -5713,6 +5757,13 @@ mysql_prepare_alter_table(THD *thd, TABL
     if (def)
     {						// Field is changed
       def->field=field;
+      if (field->stored_in_db != def->stored_in_db)
+      {
+        my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN,
+                 MYF(0),
+                 "Changing the STORED status");
+        goto err;
+      }
       if (!def->after)
       {
 	new_create_list.push_back(def);
@@ -5925,6 +5976,9 @@ mysql_prepare_alter_table(THD *thd, TABL
     Key *key;
     while ((key=key_it++))			// Add new keys
     {
+      if (key->type == Key::FOREIGN_KEY &&
+          ((Foreign_key *)key)->validate(new_create_list))
+        goto err;
       if (key->type != Key::FOREIGN_KEY)
         new_key_list.push_back(key);
       if (key->name &&
@@ -7327,6 +7381,12 @@ copy_data_between_tables(TABLE *from,TAB
       copy_ptr->do_copy(copy_ptr);
     }
     prev_insert_id= to->file->next_insert_id;
+    update_virtual_fields_marked_for_write(to, FALSE);
+    if (thd->is_error())
+    {
+      error= 1;
+      break;
+    }
     error=to->file->ha_write_row(to->record[0]);
     to->auto_increment_field_not_null= FALSE;
     if (error)

=== modified file 'sql/sql_yacc.yy'
--- sql/sql_yacc.yy	2009-02-19 09:01:25 +0000
+++ sql/sql_yacc.yy	2009-03-30 14:42:36 +0000
@@ -543,6 +543,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  ALGORITHM_SYM
 %token  ALL                           /* SQL-2003-R */
 %token  ALTER                         /* SQL-2003-R */
+%token  ALWAYS_SYM
 %token  ANALYZE_SYM
 %token  AND_AND_SYM                   /* OPERATOR */
 %token  AND_SYM                       /* SQL-2003-R */
@@ -709,6 +710,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  FULLTEXT_SYM
 %token  FUNCTION_SYM                  /* SQL-2003-R */
 %token  GE
+%token  GENERATED_SYM
 %token  GEOMETRYCOLLECTION
 %token  GEOMETRY_SYM
 %token  GET_FORMAT                    /* MYSQL-FUNC */
@@ -809,6 +811,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  MASTER_SSL_VERIFY_SERVER_CERT_SYM
 %token  MASTER_SYM
 %token  MASTER_USER_SYM
+%token  MATERIALIZED_SYM
 %token  MATCH                         /* SQL-2003-R */
 %token  MAX_CONNECTIONS_PER_HOUR
 %token  MAX_QUERIES_PER_HOUR
@@ -886,6 +889,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  PAGE_CHECKSUM_SYM
 %token  PARAM_MARKER
 %token  PARSER_SYM
+%token  PARSE_VCOL_EXPR_SYM
 %token  PARTIAL                       /* SQL-2003-N */
 %token  PARTITIONING_SYM
 %token  PARTITIONS_SYM
@@ -1084,6 +1088,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  VARIANCE_SYM
 %token  VARYING                       /* SQL-2003-R */
 %token  VAR_SAMP_SYM
+%token  VIRTUAL_SYM
 %token  VIEW_SYM                      /* SQL-2003-N */
 %token  WAIT_SYM
 %token  WARNINGS
@@ -1143,7 +1148,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
         text_string opt_gconcat_separator
 
 %type <num>
-        type int_type real_type order_dir lock_option
+        type int_type real_type order_dir lock_option field_def
         udf_type if_exists opt_local opt_table_options table_options
         table_option opt_if_not_exists opt_no_write_to_binlog
         delete_option opt_temporary all_or_any opt_distinct
@@ -1299,6 +1304,8 @@ bool my_yyoverflow(short **a, YYSTYPE **
         init_key_options key_options key_opts key_opt key_using_alg
         server_def server_options_list server_option
         definer_opt no_definer definer
+        parse_vcol_expr vcol_opt_attribute vcol_opt_attribute_list
+        vcol_attribute
 END_OF_INPUT
 
 %type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
@@ -1430,6 +1437,7 @@ statement:
         | lock
         | optimize
         | keycache
+        | parse_vcol_expr
         | partition_entry
         | preload
         | prepare
@@ -4735,8 +4743,9 @@ field_spec:
             lex->default_value= lex->on_update_value= 0;
             lex->comment=null_lex_str;
             lex->charset=NULL;
+	    lex->vcol_info= 0;
           }
-          type opt_attribute
+          field_def
           {
             LEX *lex=Lex;
             if (add_field_to_list(lex->thd, &$1, (enum enum_field_types) $3,
@@ -4744,11 +4753,97 @@ field_spec:
                                   lex->default_value, lex->on_update_value, 
                                   &lex->comment,
                                   lex->change,&lex->interval_list,lex->charset,
-                                  lex->uint_geom_type))
+                                  lex->uint_geom_type,
+                                  lex->vcol_info))
               MYSQL_YYABORT;
           }
         ;
 
+field_def:
+          type opt_attribute {}
+        | type opt_generated_always AS '(' virtual_column_func ')'
+          {
+            Lex->vcol_info->set_field_stored(YYTHD->variables.sql_mode &
+                                             MODE_ORACLE);
+          }
+          vcol_opt_attribute
+          {
+            $$= (enum enum_field_types)MYSQL_TYPE_VIRTUAL;
+            Lex->vcol_info->set_field_type((enum enum_field_types) $1);
+          }
+        ;
+
+opt_generated_always:
+          /* empty */
+        | GENERATED_SYM ALWAYS_SYM {}
+        ;
+
+vcol_opt_attribute:
+          /* empty */ {}
+        | vcol_opt_attribute_list {}
+        ;
+
+vcol_opt_attribute_list:
+          vcol_opt_attribute_list vcol_attribute {}
+        | vcol_attribute
+        ;
+
+vcol_attribute:
+          UNIQUE_SYM
+          {
+            LEX *lex=Lex;
+            lex->type|= UNIQUE_FLAG;
+            lex->alter_info.flags|= ALTER_ADD_INDEX;
+          }
+        | UNIQUE_SYM KEY_SYM
+          {
+            LEX *lex=Lex;
+            lex->type|= UNIQUE_KEY_FLAG;
+            lex->alter_info.flags|= ALTER_ADD_INDEX;
+          }
+        | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; }
+        | MATERIALIZED_SYM
+          {
+            Lex->vcol_info->set_field_stored(TRUE);
+          }
+        | VIRTUAL_SYM
+          {
+            Lex->vcol_info->set_field_stored(FALSE);
+          }
+        ;
+
+parse_vcol_expr:
+          PARSE_VCOL_EXPR_SYM '(' virtual_column_func ')'
+          {
+            /*
+              "PARSE_VCOL_EXPR" can only be used by the SQL server
+              when reading a '*.frm' file.
+              Prevent the end user from invoking this command.
+            */
+            if (!Lex->parse_vcol_expr)
+            {
+              my_message(ER_SYNTAX_ERROR, ER(ER_SYNTAX_ERROR), MYF(0));
+              MYSQL_YYABORT;
+            }
+          }
+        ;
+
+virtual_column_func:
+          remember_name expr remember_end
+          {
+            Lex->vcol_info= new Virtual_column_info();
+            if (!Lex->vcol_info)
+            {
+              mem_alloc_error(sizeof(Virtual_column_info));
+              MYSQL_YYABORT;
+            }
+            uint expr_len= (uint)($3 - $1) - 1;
+            Lex->vcol_info->expr_str.str= (char* ) sql_memdup($1 + 1, expr_len);
+            Lex->vcol_info->expr_str.length= expr_len;
+            Lex->vcol_info->expr_item= $2;
+          }
+        ;
+
 type:
           int_type opt_field_length field_options { $$=$1; }
         | real_type opt_precision field_options { $$=$1; }
@@ -5836,8 +5931,9 @@ alter_list_item:
             lex->comment=null_lex_str;
             lex->charset= NULL;
             lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
+	    lex->vcol_info= 0;
           }
-          type opt_attribute
+          field_def
           {
             LEX *lex=Lex;
             if (add_field_to_list(lex->thd,&$3,
@@ -5846,7 +5942,8 @@ alter_list_item:
                                   lex->default_value, lex->on_update_value,
                                   &lex->comment,
                                   $3.str, &lex->interval_list, lex->charset,
-                                  lex->uint_geom_type))
+                                  lex->uint_geom_type,
+                                  lex->vcol_info))
               MYSQL_YYABORT;
           }
           opt_place
@@ -11384,6 +11481,7 @@ keyword_sp:
         | AGAINST                  {}
         | AGGREGATE_SYM            {}
         | ALGORITHM_SYM            {}
+        | ALWAYS_SYM               {}
         | ANY_SYM                  {}
         | AT_SYM                   {}
         | AUTHORS_SYM              {}
@@ -11453,6 +11551,7 @@ keyword_sp:
         | FIRST_SYM                {}
         | FIXED_SYM                {}
         | FRAC_SECOND_SYM          {}
+        | GENERATED_SYM            {}
         | GEOMETRY_SYM             {}
         | GEOMETRYCOLLECTION       {}
         | GET_FORMAT               {}
@@ -11499,6 +11598,7 @@ keyword_sp:
         | MASTER_SSL_CERT_SYM      {}
         | MASTER_SSL_CIPHER_SYM    {}
         | MASTER_SSL_KEY_SYM       {}
+        | MATERIALIZED_SYM         {}
         | MAX_CONNECTIONS_PER_HOUR {}
         | MAX_QUERIES_PER_HOUR     {}
         | MAX_SIZE_SYM             {}
@@ -11633,6 +11733,7 @@ keyword_sp:
         | USE_FRM                  {}
         | VARIABLES                {}
         | VIEW_SYM                 {}
+        | VIRTUAL_SYM              {}
         | VALUE_SYM                {}
         | WARNINGS                 {}
         | WAIT_SYM                 {}
@@ -13274,6 +13375,7 @@ sf_tail:
             lex->length= lex->dec= NULL;
             lex->interval_list.empty();
             lex->type= 0;
+            lex->vcol_info= 0;
           }
           type /* $11 */
           { /* $12 */

=== modified file 'sql/table.cc'
--- sql/table.cc	2009-02-19 09:01:25 +0000
+++ sql/table.cc	2009-03-30 18:25:22 +0000
@@ -33,6 +33,9 @@ LEX_STRING GENERAL_LOG_NAME= {C_STRING_W
 /* SLOW_LOG name */
 LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")};
 
+/* Keyword for parsing virtual column functions */
+LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
+
 	/* Functions defined in this file */
 
 void open_table_error(TABLE_SHARE *share, int error, int db_errno,
@@ -663,10 +666,11 @@ static int open_binary_frm(THD *thd, TAB
   uint interval_count, interval_parts, read_length, int_length;
   uint db_create_options, keys, key_parts, n_length;
   uint key_info_length, com_length, null_bit_pos;
+  uint vcol_screen_length;
   uint extra_rec_buf_length;
   uint i,j;
   bool use_hash;
-  char *keynames, *names, *comment_pos;
+  char *keynames, *names, *comment_pos, *vcol_screen_pos;
   uchar *record;
   uchar *disk_buff, *strpos, *null_flags, *null_pos;
   ulong pos, record_offset, *rec_per_key, rec_buff_length;
@@ -836,6 +840,7 @@ static int open_binary_frm(THD *thd, TAB
   strpos+= (strmov(keynames, (char *) strpos) - keynames)+1;
 
   share->reclength = uint2korr((head+16));
+  share->stored_rec_length= share->reclength;
   if (*(head+26) == 1)
     share->system= 1;				/* one-record-database */
 #ifdef HAVE_CRYPTED_FRM
@@ -1040,24 +1045,28 @@ static int open_binary_frm(THD *thd, TAB
   int_length= uint2korr(head+274);
   share->null_fields= uint2korr(head+282);
   com_length= uint2korr(head+284);
+  vcol_screen_length= uint2korr(head+286);
+  share->vfields= 0;
+  share->stored_fields= share->fields;
   share->comment.length=  (int) (head[46]);
   share->comment.str= strmake_root(&share->mem_root, (char*) head+47,
                                    share->comment.length);
 
-  DBUG_PRINT("info",("i_count: %d  i_parts: %d  index: %d  n_length: %d  int_length: %d  com_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length));
-
+  DBUG_PRINT("info",("i_count: %d  i_parts: %d  index: %d  n_length: %d  int_length: %d  com_length: %d  vcol_screen_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length, vcol_screen_length));
   if (!(field_ptr = (Field **)
 	alloc_root(&share->mem_root,
 		   (uint) ((share->fields+1)*sizeof(Field*)+
 			   interval_count*sizeof(TYPELIB)+
 			   (share->fields+interval_parts+
 			    keys+3)*sizeof(char *)+
-			   (n_length+int_length+com_length)))))
+			   (n_length+int_length+com_length+
+			       vcol_screen_length)))))
     goto err;                                   /* purecov: inspected */
 
   share->field= field_ptr;
   read_length=(uint) (share->fields * field_pack_length +
-		      pos+ (uint) (n_length+int_length+com_length));
+		      pos+ (uint) (n_length+int_length+com_length+
+		                   vcol_screen_length));
   if (read_string(file,(uchar**) &disk_buff,read_length))
     goto err;                                   /* purecov: inspected */
 #ifdef HAVE_CRYPTED_FRM
@@ -1078,7 +1087,11 @@ static int open_binary_frm(THD *thd, TAB
   memcpy((char*) names, strpos+(share->fields*field_pack_length),
 	 (uint) (n_length+int_length));
   comment_pos= names+(n_length+int_length);
-  memcpy(comment_pos, disk_buff+read_length-com_length, com_length);
+  memcpy(comment_pos, disk_buff+read_length-com_length-vcol_screen_length, 
+         com_length);
+  vcol_screen_pos= names+(n_length+int_length+com_length);
+  memcpy(vcol_screen_pos, disk_buff+read_length-vcol_screen_length, 
+         vcol_screen_length);
 
   fix_type_pointers(&interval_array, &share->fieldnames, 1, &names);
   if (share->fieldnames.count != share->fields)
@@ -1146,10 +1159,13 @@ static int open_binary_frm(THD *thd, TAB
   for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++)
   {
     uint pack_flag, interval_nr, unireg_type, recpos, field_length;
+    uint vcol_info_length=0;
     enum_field_types field_type;
     CHARSET_INFO *charset=NULL;
     Field::geometry_type geom_type= Field::GEOM_GEOMETRY;
     LEX_STRING comment;
+    Virtual_column_info *vcol_info= 0;
+    bool fld_stored_in_db= TRUE;
 
     if (new_frm_ver >= 3)
     {
@@ -1184,6 +1200,18 @@ static int open_binary_frm(THD *thd, TAB
           goto err;
         }
       }
+
+      if ((uchar)field_type == (uchar)MYSQL_TYPE_VIRTUAL)
+      {
+        DBUG_ASSERT(interval_nr); // Expect non-null expression
+        /* 
+          The interval_id byte in the .frm file stores the length of the
+          expression statement for a virtual column.
+        */
+        vcol_info_length= interval_nr;
+        interval_nr= 0;
+      }
+
       if (!comment_length)
       {
 	comment.str= (char*) "";
@@ -1195,6 +1223,33 @@ static int open_binary_frm(THD *thd, TAB
 	comment.length= comment_length;
 	comment_pos+=   comment_length;
       }
+
+      if (vcol_info_length)
+      {
+        /*
+          Get virtual column data stored in the .frm file as follows:
+          byte 1      = 1 (always 1 to allow for future extensions)
+          byte 2      = sql_type
+          byte 3      = flags (as of now, 0 - no flags, 1 - field is physically stored)
+          byte 4-...  = virtual column expression (text data)
+        */
+        vcol_info= new Virtual_column_info();
+        if ((uint)vcol_screen_pos[0] != 1)
+        {
+          error= 4;
+          goto err;
+        }
+        field_type= (enum_field_types) (uchar) vcol_screen_pos[1];
+        fld_stored_in_db= (bool) (uint) vcol_screen_pos[2];
+        vcol_info->expr_str.str= (char *)memdup_root(&share->mem_root,
+                                                     vcol_screen_pos+
+                                                       (uint)FRM_VCOL_HEADER_SIZE,
+                                                     vcol_info_length-
+                                                       (uint)FRM_VCOL_HEADER_SIZE);
+        vcol_info->expr_str.length= vcol_info_length-(uint)FRM_VCOL_HEADER_SIZE;
+        vcol_screen_pos+= vcol_info_length;
+        share->vfields++;
+      }
     }
     else
     {
@@ -1285,6 +1340,8 @@ static int open_binary_frm(THD *thd, TAB
 
     reg_field->field_index= i;
     reg_field->comment=comment;
+    reg_field->vcol_info= vcol_info;
+    reg_field->stored_in_db= fld_stored_in_db;
     if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag))
     {
       if ((null_bit_pos+= field_length & 7) > 7)
@@ -1309,8 +1366,17 @@ static int open_binary_frm(THD *thd, TAB
     if (use_hash)
       (void) my_hash_insert(&share->name_hash,
                             (uchar*) field_ptr); // never fail
+    if (!reg_field->stored_in_db)
+    {
+      share->stored_fields--;
+      if (share->stored_rec_length>=recpos)
+        share->stored_rec_length= recpos-1;
+    }
   }
   *field_ptr=0;					// End marker
+  /* Sanity checks: */
+  DBUG_ASSERT(share->fields>=share->stored_fields);
+  DBUG_ASSERT(share->reclength>=share->stored_rec_length);
 
   /* Fix key->name and key_part->field */
   if (key_parts)
@@ -1597,6 +1663,268 @@ static int open_binary_frm(THD *thd, TAB
   DBUG_RETURN(error);
 } /* open_binary_frm */
 
+/*
+  Clear flag GET_FIXED_FIELDS_FLAG in all fields of the table.
+  This routine is used for error handling purposes.
+
+  SYNOPSIS
+    clear_field_flag()
+    table                TABLE object for which virtual columns are set-up
+
+  RETURN VALUE
+    NONE
+*/
+static void clear_field_flag(TABLE *table)
+{
+  Field **ptr;
+  DBUG_ENTER("clear_field_flag");
+
+  for (ptr= table->field; *ptr; ptr++)
+    (*ptr)->flags&= (~GET_FIXED_FIELDS_FLAG);
+  DBUG_VOID_RETURN;
+}
+
+/*
+  The function uses the feature in fix_fields where the flag 
+  GET_FIXED_FIELDS_FLAG is set for all fields in the item tree.
+  This field must always be reset before returning from the function
+  since it is used for other purposes as well.
+
+  SYNOPSIS
+    fix_fields_vcol_func()
+    thd                  The thread object
+    vcol_info            The item tree reference of the virtual columnfunction
+    table                The table object
+    field_name           The name of the processed field
+
+  RETURN VALUE
+    TRUE                 An error occurred, something was wrong with the
+                         function.
+    FALSE                Ok, a partition field array was created
+*/
+
+bool fix_fields_vcol_func(THD *thd,
+                          Virtual_column_info *vcol_info,
+                          TABLE *table,
+                          const char *field_name)
+{
+  Item* func_expr= vcol_info->expr_item;
+  uint dir_length, home_dir_length;
+  bool result= TRUE;
+  TABLE_LIST tables;
+  TABLE_LIST *save_table_list, *save_first_table, *save_last_table;
+  int error;
+  Name_resolution_context *context;
+  const char *save_where;
+  char* db_name;
+  char db_name_string[FN_REFLEN];
+  bool save_use_only_table_context;
+  Field **ptr, *field;
+  enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
+  DBUG_ASSERT(func_expr);
+  DBUG_ENTER("fix_fields_vcol_func");
+
+  /*
+    Set-up the TABLE_LIST object to be a list with a single table
+    Set the object to zero to create NULL pointers and set alias
+    and real name to table name and get database name from file name.
+  */
+
+  bzero((void*)&tables, sizeof(TABLE_LIST));
+  tables.alias= tables.table_name= (char*) table->s->table_name.str;
+  tables.table= table;
+  tables.next_local= 0;
+  tables.next_name_resolution_table= 0;
+  strmov(db_name_string, table->s->normalized_path.str);
+  dir_length= dirname_length(db_name_string);
+  db_name_string[dir_length - 1]= 0;
+  home_dir_length= dirname_length(db_name_string);
+  db_name= &db_name_string[home_dir_length];
+  tables.db= db_name;
+
+  thd->mark_used_columns= MARK_COLUMNS_NONE;
+
+  context= thd->lex->current_context();
+  table->map= 1; //To ensure correct calculation of const item
+  table->get_fields_in_item_tree= TRUE;
+  save_table_list= context->table_list;
+  save_first_table= context->first_name_resolution_table;
+  save_last_table= context->last_name_resolution_table;
+  context->table_list= &tables;
+  context->first_name_resolution_table= &tables;
+  context->last_name_resolution_table= NULL;
+  func_expr->walk(&Item::change_context_processor, 0, (uchar*) context);
+  save_where= thd->where;
+  thd->where= "virtual column function";
+
+  /* Save the context before fixing the fields*/
+  save_use_only_table_context= thd->lex->use_only_table_context;
+  thd->lex->use_only_table_context= TRUE;
+  /* Fix fields referenced to by the virtual column function */
+  error= func_expr->fix_fields(thd, (Item**)0);
+  /* Restore the original context*/
+  thd->lex->use_only_table_context= save_use_only_table_context;
+  context->table_list= save_table_list;
+  context->first_name_resolution_table= save_first_table;
+  context->last_name_resolution_table= save_last_table;
+
+  if (unlikely(error))
+  {
+    DBUG_PRINT("info", ("Field in virtual column function not part of table"));
+    clear_field_flag(table);
+    goto end;
+  }
+  thd->where= save_where;
+#ifdef PARANOID
+  /*
+    Walk through the Item tree checking if all items are valid
+   to be part of the virtual column
+  */
+  error= func_expr->walk(&Item::check_vcol_func_processor, 0, NULL);
+  if (error)
+  {
+    my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), field_name);
+    clear_field_flag(table);
+    goto end;
+  }
+#endif
+  if (unlikely(func_expr->const_item()))
+  {
+    my_error(ER_CONST_EXPR_IN_VCOL, MYF(0));
+    clear_field_flag(table);
+    goto end;
+  }
+  /* Ensure that this virtual column is not based on another virtual field. */
+  ptr= table->field;
+  while ((field= *(ptr++))) 
+  {
+    if ((field->flags & GET_FIXED_FIELDS_FLAG) &&
+        (field->vcol_info))
+    {
+      my_error(ER_VCOL_BASED_ON_VCOL, MYF(0));
+      clear_field_flag(table);
+      goto end;
+    }
+  }
+  /*
+    Cleanup the fields marked with flag GET_FIXED_FIELDS_FLAG
+    when calling fix_fields.
+  */
+  clear_field_flag(table);
+  result= FALSE;
+
+end:
+  table->get_fields_in_item_tree= FALSE;
+  thd->mark_used_columns= save_mark_used_columns;
+  table->map= 0; //Restore old value
+  DBUG_RETURN(result);
+}
+
+/*
+  Unpack the definition of a virtual column
+
+  SYNOPSIS
+    unpack_vcol_info_from_frm()
+    thd                  Thread handler
+    table                Table with the checked field
+    field                Pointer to Field object
+    error_reported       updated flag for the caller that no other error
+                         messages are to be generated.
+
+  RETURN VALUES
+    TRUE            Failure
+    FALSE           Success
+*/
+bool unpack_vcol_info_from_frm(THD *thd,
+                               TABLE *table,
+                               Field *field,
+                               LEX_STRING *vcol_expr,
+                               bool *error_reported)
+{
+  DBUG_ENTER("unpack_vcol_info_from_frm");
+  DBUG_ASSERT(vcol_expr);
+
+  /* 
+    Step 1: Construct a statement for the parser.
+    The parsed string needs to take the following format:
+    "PARSE_VCOL_EXPR (<expr_string_from_frm>)"
+  */
+  char *vcol_expr_str;
+  int str_len= 0;
+  CHARSET_INFO *old_character_set_client;
+  
+  if (!(vcol_expr_str= (char*) alloc_root(&table->mem_root,
+                                          vcol_expr->length + 
+                                            parse_vcol_keyword.length + 3)))
+  {
+    DBUG_RETURN(TRUE);
+  }
+  memcpy(vcol_expr_str,
+         (char*) parse_vcol_keyword.str,
+         parse_vcol_keyword.length);
+  str_len= parse_vcol_keyword.length;
+  memcpy(vcol_expr_str + str_len, "(", 1);
+  str_len++;
+  memcpy(vcol_expr_str + str_len, 
+         (char*) vcol_expr->str, 
+         vcol_expr->length);
+  str_len+= vcol_expr->length;
+  memcpy(vcol_expr_str + str_len, ")", 1);
+  str_len++;
+  memcpy(vcol_expr_str + str_len, "\0", 1);
+  str_len++;
+  Parser_state parser_state(thd, vcol_expr_str, str_len);
+
+  /* 
+    Step 2: Setup thd for parsing.
+  */
+  Query_arena *backup_stmt_arena_ptr= thd->stmt_arena;
+  Query_arena backup_arena;
+  Query_arena vcol_arena(&table->mem_root, Query_arena::INITIALIZED);
+  thd->set_n_backup_active_arena(&vcol_arena, &backup_arena);
+  thd->stmt_arena= &vcol_arena;
+
+  thd->lex->parse_vcol_expr= TRUE;
+  old_character_set_client= thd->variables.character_set_client;
+
+  /* 
+    Step 3: Use the parser to build an Item object from.
+  */
+  if (parse_sql(thd, &parser_state, NULL))
+  {
+    goto parse_err;
+  }
+  /* From now on use vcol_info generated by the parser. */
+  field->vcol_info= thd->lex->vcol_info;
+
+  /* Validate the Item tree. */
+  if (fix_fields_vcol_func(thd,
+                           field->vcol_info,
+                           table,
+                           field->field_name))
+  {
+    *error_reported= TRUE;
+    field->vcol_info= 0;
+    goto parse_err;
+  }
+  thd->stmt_arena= backup_stmt_arena_ptr;
+  thd->restore_active_arena(&vcol_arena, &backup_arena);
+  field->vcol_info->item_free_list= vcol_arena.free_list;
+
+  DBUG_RETURN(FALSE);
+
+parse_err:
+  thd->lex->parse_vcol_expr= FALSE;
+  thd->free_items();
+  thd->stmt_arena= backup_stmt_arena_ptr;
+  thd->restore_active_arena(&vcol_arena, &backup_arena);
+  thd->variables.character_set_client= old_character_set_client;
+  DBUG_RETURN(TRUE);
+}
+
+/*
+  Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE
+*/
 
 /*
   Open a table based on a TABLE_SHARE
@@ -1631,7 +1959,7 @@ int open_table_from_share(THD *thd, TABL
   uint records, i, bitmap_size;
   bool error_reported= FALSE;
   uchar *record, *bitmaps;
-  Field **field_ptr;
+  Field **field_ptr, **vfield_ptr;
   DBUG_ENTER("open_table_from_share");
   DBUG_PRINT("enter",("name: '%s.%s'  form: 0x%lx", share->db.str,
                       share->table_name.str, (long) outparam));
@@ -1784,6 +2112,34 @@ int open_table_from_share(THD *thd, TABL
     }
   }
 
+  /*
+    Process virtual columns, if any.
+  */
+  if (!(vfield_ptr = (Field **) alloc_root(&outparam->mem_root,
+                                          (uint) ((share->vfields+1)*
+                                                  sizeof(Field*)))))
+    goto err;
+
+  outparam->vfield= vfield_ptr;
+  
+  for (field_ptr= outparam->field; *field_ptr; field_ptr++)
+  {
+    if ((*field_ptr)->vcol_info)
+    {
+      if (unpack_vcol_info_from_frm(thd,
+                                    outparam,
+                                    *field_ptr,
+                                    &(*field_ptr)->vcol_info->expr_str,
+                                    &error_reported))
+      {
+        error= 4; // in case no error is reported
+        goto err;
+      }
+      *(vfield_ptr++)= *field_ptr;
+    }
+  }
+  *vfield_ptr= 0;                              // End marker
+
 #ifdef WITH_PARTITION_STORAGE_ENGINE
   if (share->partition_info_len && outparam->file)
   {
@@ -1851,6 +2207,20 @@ partititon_err:
   }
 #endif
 
+  /* Check virtual columns against table's storage engine. */
+  if (share->vfields && 
+        ((outparam->file && 
+          !outparam->file->check_if_supported_virtual_columns()) ||
+	 (!outparam->file && share->db_type() && 
+	   share->db_type()->db_type == DB_TYPE_CSV_DB))) // Workaround for CSV
+  {
+    my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN,
+             MYF(0), 
+             "Specified storage engine");
+    error_reported= TRUE;
+    goto err;
+  }
+
   /* Allocate bitmaps */
 
   bitmap_size= share->column_bitmap_size;
@@ -1965,7 +2335,11 @@ int closefrm(register TABLE *table, bool
   if (table->field)
   {
     for (Field **ptr=table->field ; *ptr ; ptr++)
+    {
+      if ((*ptr)->vcol_info)
+        free_items((*ptr)->vcol_info->item_free_list);
       delete *ptr;
+    }
     table->field= 0;
   }
   delete table->file;
@@ -3934,6 +4308,25 @@ const char *Field_iterator_table::name()
   return (*ptr)->field_name;
 }
 
+Item *create_table_vcol_field(THD *thd, Item **field_ref,
+                              const char *fld_name,
+                              const char *tbl_name)
+{
+  Item *field= *field_ref;
+  DBUG_ENTER("create_table_vcol_field");
+  DBUG_ASSERT(field);
+
+  if (!field->fixed)
+  {
+    if (field->fix_fields(thd, field_ref))
+    {
+      DBUG_RETURN(0);
+    }
+    field= *field_ref;
+  }
+  field->set_name(fld_name, strlen(fld_name), system_charset_info);
+  DBUG_RETURN(field);
+}
 
 Item *Field_iterator_table::create_item(THD *thd)
 {
@@ -4388,7 +4781,14 @@ void st_table::mark_columns_used_by_inde
   KEY_PART_INFO *key_part_end= (key_part +
                                 key_info[index].key_parts);
   for (;key_part != key_part_end; key_part++)
+  {
     bitmap_set_bit(bitmap, key_part->fieldnr-1);
+    if (key_part->field->vcol_info &&
+        key_part->field->vcol_info->expr_item)
+      key_part->field->vcol_info->
+               expr_item->walk(&Item::register_field_in_bitmap, 
+                               1, (uchar *) bitmap);
+  }
 }
 
 
@@ -4515,6 +4915,8 @@ void st_table::mark_columns_needed_for_u
       file->column_bitmaps_signal();
     }
   }
+  /* Mark all virtual columns as writable */
+  mark_virtual_columns();
   DBUG_VOID_RETURN;
 }
 
@@ -4541,8 +4943,42 @@ void st_table::mark_columns_needed_for_i
   }
   if (found_next_number_field)
     mark_auto_increment_column();
+  /* Mark all virtual columns as writable */
+  mark_virtual_columns();
 }
 
+/* 
+  @brief Update the write and read table bitmap to allow
+         using procedure save_in_field for all virtual columns
+         in the table.
+
+  @return       void
+
+  @detail
+    Each virtual field is set in the write column map.
+    All fields that the virtual columns are based on are set in the
+    read bitmap.
+*/
+
+void st_table::mark_virtual_columns(void)
+{
+  Field **vfield_ptr, *tmp_vfield;
+  bool bitmap_updated= FALSE;
+
+  for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
+  {
+    tmp_vfield= *vfield_ptr;
+    DBUG_ASSERT(tmp_vfield->vcol_info && tmp_vfield->vcol_info->expr_item);
+    tmp_vfield->vcol_info->expr_item->walk(&Item::register_field_in_read_map, 
+                                           1, (uchar *) 0);
+    bitmap_set_bit(read_set, tmp_vfield->field_index);
+    bitmap_set_bit(write_set, tmp_vfield->field_index);
+    // TODO: consider updating column maps for index
+    bitmap_updated= TRUE;
+  }
+  if (bitmap_updated)
+    file->column_bitmaps_signal();
+}
 
 /**
   @brief Check if this is part of a MERGE table with attached children.
@@ -4797,6 +5233,54 @@ size_t max_row_length(TABLE *table, cons
   return length;
 }
 
+/*
+  Calculate data for each virtual field marked for write in the
+  corresponding column map.
+
+  SYNOPSIS
+    update_virtual_fields_marked_for_write()
+    table                  The TABLE object
+    ignore_stored          Indication whether physically stored virtual
+                           fields do not need updating.
+                           This value is false when during INSERT and UPDATE
+                           and true in all other cases.
+ 
+  RETURN
+    0  - Success
+    >0 - Error occurred during the generation/calculation of a virtual field value
+
+*/
+
+int update_virtual_fields_marked_for_write(TABLE *table,
+                                           bool ignore_stored)
+{
+  DBUG_ENTER("update_virtual_fields_marked_for_write");
+  Field **vfield_ptr, *vfield;
+  int error= 0;
+  if (!table || !table->vfield)
+    DBUG_RETURN(0);
+
+  /* Iterate over virtual fields in the table */
+  for (vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++)
+  {
+    vfield= (*vfield_ptr);
+    DBUG_ASSERT(vfield->vcol_info && vfield->vcol_info->expr_item);
+    /* Only update those fields that are marked in the write_set bitmap */
+    if (bitmap_is_set(table->write_set, vfield->field_index) &&
+            (not (ignore_stored && vfield->stored_in_db)))
+    {
+      /* Generate the actual value of the virtual fields */
+      error= vfield->vcol_info->expr_item->save_in_field(vfield, 0);
+      DBUG_PRINT("info", ("field '%s' - updated", vfield->field_name));
+    }
+    else
+    {
+      DBUG_PRINT("info", ("field '%s' - skipped", vfield->field_name));
+    }
+  }
+  DBUG_RETURN(0);
+}
+
 /*****************************************************************************
 ** Instansiate templates
 *****************************************************************************/

=== modified file 'sql/table.h'
--- sql/table.h	2009-02-19 09:01:25 +0000
+++ sql/table.h	2009-03-24 10:47:21 +0000
@@ -352,6 +352,8 @@ typedef struct st_table_share
   ulong   version, mysql_version;
   ulong   timestamp_offset;		/* Set to offset+1 of record */
   ulong   reclength;			/* Recordlength */
+  ulong   stored_rec_length;            /* Stored record length 
+                                           (no generated-only virtual fields) */
 
   plugin_ref db_plugin;			/* storage engine plugin */
   inline handlerton *db_type() const	/* table_type for handler */
@@ -370,6 +372,8 @@ typedef struct st_table_share
   uint key_block_size;			/* create key_block_size, if used */
   uint null_bytes, last_null_bit_pos;
   uint fields;				/* Number of fields */
+  uint stored_fields;                   /* Number of stored fields 
+                                           (i.e. without generated-only ones) */
   uint rec_buff_length;                 /* Size of table->record[] buffer */
   uint keys, key_parts;
   uint max_key_length, max_unique_length, total_key_length;
@@ -391,6 +395,7 @@ typedef struct st_table_share
   uint error, open_errno, errarg;       /* error from open_table_def() */
   uint column_bitmap_size;
   uchar frm_version;
+  uint vfields;                         /* Number of virtual fields */
   bool null_field_first;
   bool system;                          /* Set if system table (one record) */
   bool crypted;                         /* If .frm file is crypted */
@@ -655,6 +660,7 @@ struct st_table {
   Field *next_number_field;		/* Set if next_number is activated */
   Field *found_next_number_field;	/* Set on open */
   Field_timestamp *timestamp_field;
+  Field **vfield;                       /* Pointer to virtual fields*/
 
   /* Table's triggers, 0 if there are no of them */
   Table_triggers_list *triggers;
@@ -811,6 +817,7 @@ struct st_table {
   void mark_columns_needed_for_update(void);
   void mark_columns_needed_for_delete(void);
   void mark_columns_needed_for_insert(void);
+  void mark_virtual_columns(void);
   inline void column_bitmaps_set(MY_BITMAP *read_set_arg,
                                  MY_BITMAP *write_set_arg)
   {

=== modified file 'sql/unireg.cc'
--- sql/unireg.cc	2009-02-19 09:01:25 +0000
+++ sql/unireg.cc	2009-03-30 18:27:41 +0000
@@ -584,7 +584,7 @@ static bool pack_header(uchar *forminfo,
 {
   uint length,int_count,int_length,no_empty, int_parts;
   uint time_stamp_pos,null_fields;
-  ulong reclength, totlength, n_length, com_length;
+  ulong reclength, totlength, n_length, com_length, vcol_info_length;
   DBUG_ENTER("pack_header");
 
   if (create_fields.elements > MAX_FIELDS)
@@ -595,8 +595,8 @@ static bool pack_header(uchar *forminfo,
 
   totlength= 0L;
   reclength= data_offset;
-  no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields=
-    com_length=0;
+  no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields=0;
+  com_length=vcol_info_length=0;
   n_length=2L;
 
 	/* Check fields */
@@ -625,6 +625,27 @@ static bool pack_header(uchar *forminfo,
                           ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), buff);
       field->comment.length= tmp_len;
     }
+    if (field->vcol_info)
+    {
+      tmp_len= system_charset_info->cset->charpos(system_charset_info,
+                                                  field->vcol_info->expr_str.str,
+                                                  field->vcol_info->expr_str.str +
+                                                  field->vcol_info->expr_str.length,
+                                                  VIRTUAL_COLUMN_EXPRESSION_MAXLEN);
+
+      if (tmp_len < field->vcol_info->expr_str.length)
+      {
+        my_error(ER_WRONG_STRING_LENGTH, MYF(0),
+                 field->vcol_info->expr_str.str,"VIRTUAL COLUMN EXPRESSION",
+                 (uint) VIRTUAL_COLUMN_EXPRESSION_MAXLEN);
+        DBUG_RETURN(1);
+      }
+      /*
+        Sum up the length of the expression string and mandatory header bytes
+        to the total length.
+      */
+      vcol_info_length+= field->vcol_info->expr_str.length+(uint)FRM_VCOL_HEADER_SIZE;
+    }
 
     totlength+= field->length;
     com_length+= field->comment.length;
@@ -644,8 +665,6 @@ static bool pack_header(uchar *forminfo,
 	!time_stamp_pos)
       time_stamp_pos= (uint) field->offset+ (uint) data_offset + 1;
     length=field->pack_length;
-    /* Ensure we don't have any bugs when generating offsets */
-    DBUG_ASSERT(reclength == field->offset + data_offset);
     if ((uint) field->offset+ (uint) data_offset+ length > reclength)
       reclength=(uint) (field->offset+ data_offset + length);
     n_length+= (ulong) strlen(field->field_name)+1;
@@ -712,7 +731,8 @@ static bool pack_header(uchar *forminfo,
   /* Hack to avoid bugs with small static rows in MySQL */
   reclength=max(file->min_record_length(table_options),reclength);
   if (info_length+(ulong) create_fields.elements*FCOMP+288+
-      n_length+int_length+com_length > 65535L || int_count > 255)
+      n_length+int_length+com_length+vcol_info_length > 65535L || 
+      int_count > 255)
   {
     my_message(ER_TOO_MANY_FIELDS, ER(ER_TOO_MANY_FIELDS), MYF(0));
     DBUG_RETURN(1);
@@ -720,7 +740,7 @@ static bool pack_header(uchar *forminfo,
 
   bzero((char*)forminfo,288);
   length=(info_length+create_fields.elements*FCOMP+288+n_length+int_length+
-	  com_length);
+	  com_length+vcol_info_length);
   int2store(forminfo,length);
   forminfo[256] = (uint8) screens;
   int2store(forminfo+258,create_fields.elements);
@@ -737,7 +757,8 @@ static bool pack_header(uchar *forminfo,
   int2store(forminfo+280,22);			/* Rows needed */
   int2store(forminfo+282,null_fields);
   int2store(forminfo+284,com_length);
-  /* Up to forminfo+288 is free to use for additional information */
+  int2store(forminfo+286,vcol_info_length);
+  /* forminfo+288 is free to use for additional information */
   DBUG_RETURN(0);
 } /* pack_header */
 
@@ -776,7 +797,7 @@ static bool pack_fields(File file, List<
                         ulong data_offset)
 {
   reg2 uint i;
-  uint int_count, comment_length=0;
+  uint int_count, comment_length, vcol_info_length=0;
   uchar buff[MAX_FIELD_WIDTH];
   Create_field *field;
   DBUG_ENTER("pack_fields");
@@ -789,6 +810,7 @@ static bool pack_fields(File file, List<
   while ((field=it++))
   {
     uint recpos;
+    uint cur_vcol_expr_len= 0;
     buff[0]= (uchar) field->row;
     buff[1]= (uchar) field->col;
     buff[2]= (uchar) field->sc_length;
@@ -811,6 +833,17 @@ static bool pack_fields(File file, List<
       buff[14]= (uchar) field->charset->number;
     else
       buff[14]= 0;				// Numerical
+    if (field->vcol_info)
+    {
+      /* 
+        Use the interval_id place in the .frm file to store the length of
+        virtual field's data.
+      */
+      buff[12]= cur_vcol_expr_len= field->vcol_info->expr_str.length +
+                (uint)FRM_VCOL_HEADER_SIZE;
+      vcol_info_length+= cur_vcol_expr_len+(uint)FRM_VCOL_HEADER_SIZE;
+      buff[13]= (uchar) MYSQL_TYPE_VIRTUAL;
+    }
     int2store(buff+15, field->comment.length);
     comment_length+= field->comment.length;
     set_if_bigger(int_count,field->interval_id);
@@ -905,6 +938,34 @@ static bool pack_fields(File file, List<
 	  DBUG_RETURN(1);
     }
   }
+  if (vcol_info_length)
+  {
+    it.rewind();
+    int_count=0;
+    while ((field=it++))
+    {
+      /*
+        Pack each virtual field as follows:
+        byte 1      = 1 (always 1 to allow for future extensions)
+        byte 2      = sql_type
+        byte 3      = flags (as of now, 0 - no flags, 1 - field is physically stored)
+        byte 4-...  = virtual column expression (text data)
+      */
+      if (field->vcol_info && field->vcol_info->expr_str.length)
+      {
+        buff[0]= (uchar)1;
+        buff[1]= (uchar) field->sql_type;
+        buff[2]= (uchar) field->stored_in_db;
+        if (my_write(file, buff, 3, MYF_RW))
+          DBUG_RETURN(1);
+        if (my_write(file,
+                     (uchar*) field->vcol_info->expr_str.str,
+                     field->vcol_info->expr_str.length,
+                     MYF_RW))
+          DBUG_RETURN(1);
+      }
+    }
+  }
   DBUG_RETURN(0);
 }
 

=== modified file 'storage/innobase/handler/ha_innodb.cc'
--- storage/innobase/handler/ha_innodb.cc	2009-02-19 09:01:25 +0000
+++ storage/innobase/handler/ha_innodb.cc	2009-03-24 10:47:21 +0000
@@ -2437,12 +2437,12 @@ ha_innobase::open(
 	}
 
 	/* Create buffers for packing the fields of a record. Why
-	table->reclength did not work here? Obviously, because char
+	table->stored_rec_length did not work here? Obviously, because char
 	fields when packed actually became 1 byte longer, when we also
 	stored the string length as the first byte. */
 
 	upd_and_key_val_buff_len =
-				table->s->reclength + table->s->max_key_length
+				table->s->stored_rec_length + table->s->max_key_length
 							+ MAX_REF_PARTS * 3;
 	if (!(uchar*) my_multi_malloc(MYF(MY_WME),
 			&upd_buff, upd_and_key_val_buff_len,
@@ -2517,7 +2517,7 @@ retry:
 
 	prebuilt = row_create_prebuilt(ib_table);
 
-	prebuilt->mysql_row_len = table->s->reclength;
+	prebuilt->mysql_row_len = table->s->stored_rec_length;;
 	prebuilt->default_rec = table->s->default_values;
 	ut_ad(prebuilt->default_rec);
 
@@ -3220,11 +3220,11 @@ build_template(
 	dict_index_t*	clust_index;
 	mysql_row_templ_t* templ;
 	Field*		field;
-	ulint		n_fields;
+	ulint		n_fields, n_stored_fields;
 	ulint		n_requested_fields	= 0;
 	ibool		fetch_all_in_key	= FALSE;
 	ibool		fetch_primary_key_cols	= FALSE;
-	ulint		i;
+	ulint		i, sql_idx, innodb_idx=0;
 	/* byte offset of the end of last requested column */
 	ulint		mysql_prefix_len	= 0;
 
@@ -3285,11 +3285,12 @@ build_template(
 	}
 
 	n_fields = (ulint)table->s->fields; /* number of columns */
+	n_stored_fields= (ulint)table->s->stored_fields; /* number of stored columns */
 
 	if (!prebuilt->mysql_template) {
 		prebuilt->mysql_template = (mysql_row_templ_t*)
 						mem_alloc_noninline(
-					n_fields * sizeof(mysql_row_templ_t));
+					n_stored_fields * sizeof(mysql_row_templ_t));
 	}
 
 	prebuilt->template_type = templ_type;
@@ -3299,15 +3300,17 @@ build_template(
 
 	/* Note that in InnoDB, i is the column number. MySQL calls columns
 	'fields'. */
-	for (i = 0; i < n_fields; i++) {
+	for (sql_idx = 0; sql_idx < n_fields; sql_idx++) {
 		templ = prebuilt->mysql_template + n_requested_fields;
-		field = table->field[i];
+		field = table->field[sql_idx];
+		if (!field->stored_in_db)
+		  goto skip_field;
 
 		if (UNIV_LIKELY(templ_type == ROW_MYSQL_REC_FIELDS)) {
 			/* Decide which columns we should fetch
 			and which we can skip. */
 			register const ibool	index_contains_field =
-				dict_index_contains_col_or_prefix(index, i);
+				dict_index_contains_col_or_prefix(index, innodb_idx);
 
 			if (!index_contains_field && prebuilt->read_just_key) {
 				/* If this is a 'key read', we do not need
@@ -3322,8 +3325,8 @@ build_template(
 				goto include_field;
 			}
 
-                        if (bitmap_is_set(table->read_set, i) ||
-                            bitmap_is_set(table->write_set, i)) {
+                        if (bitmap_is_set(table->read_set, sql_idx) ||
+                            bitmap_is_set(table->write_set, sql_idx)) {
 				/* This field is needed in the query */
 
 				goto include_field;
@@ -3331,7 +3334,7 @@ build_template(
 
 			if (fetch_primary_key_cols
 				&& dict_table_col_in_clustered_key(
-					index->table, i)) {
+					index->table, innodb_idx)) {
 				/* This field is needed in the query */
 
 				goto include_field;
@@ -3344,14 +3347,14 @@ build_template(
 include_field:
 		n_requested_fields++;
 
-		templ->col_no = i;
+		templ->col_no = innodb_idx;
 
 		if (index == clust_index) {
 			templ->rec_field_no = dict_col_get_clust_pos_noninline(
-				&index->table->cols[i], index);
+				&index->table->cols[innodb_idx], index);
 		} else {
 			templ->rec_field_no = dict_index_get_nth_col_pos(
-								index, i);
+								index, innodb_idx);
 		}
 
 		if (templ->rec_field_no == ULINT_UNDEFINED) {
@@ -3377,7 +3380,7 @@ include_field:
 			mysql_prefix_len = templ->mysql_col_offset
 				+ templ->mysql_col_len;
 		}
-		templ->type = index->table->cols[i].mtype;
+		templ->type = index->table->cols[innodb_idx].mtype;
 		templ->mysql_type = (ulint)field->type();
 
 		if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
@@ -3386,16 +3389,18 @@ include_field:
 		}
 
 		templ->charset = dtype_get_charset_coll_noninline(
-				index->table->cols[i].prtype);
-		templ->mbminlen = index->table->cols[i].mbminlen;
-		templ->mbmaxlen = index->table->cols[i].mbmaxlen;
-		templ->is_unsigned = index->table->cols[i].prtype
+				index->table->cols[innodb_idx].prtype);
+		templ->mbminlen = index->table->cols[innodb_idx].mbminlen;
+		templ->mbmaxlen = index->table->cols[innodb_idx].mbmaxlen;
+		templ->is_unsigned = index->table->cols[innodb_idx].prtype
 							& DATA_UNSIGNED;
 		if (templ->type == DATA_BLOB) {
 			prebuilt->templ_contains_blob = TRUE;
 		}
 skip_field:
-		;
+                if (field->stored_in_db) {
+                    innodb_idx++;
+                }
 	}
 
 	prebuilt->n_template = n_requested_fields;
@@ -3848,7 +3853,7 @@ calc_row_difference(
 	ulint		n_changed = 0;
 	dfield_t	dfield;
 	dict_index_t*	clust_index;
-	uint		i;
+	uint		sql_idx, innodb_idx= 0;
 
 	n_fields = table->s->fields;
 	clust_index = dict_table_get_first_index_noninline(prebuilt->table);
@@ -3856,8 +3861,10 @@ calc_row_difference(
 	/* We use upd_buff to convert changed fields */
 	buf = (byte*) upd_buff;
 
-	for (i = 0; i < n_fields; i++) {
-		field = table->field[i];
+	for (sql_idx = 0; sql_idx < n_fields; sql_idx++) {
+		field = table->field[sql_idx];
+		if (!field->stored_in_db)
+		  continue;
 
 		o_ptr = (byte*) old_row + get_field_offset(table, field);
 		n_ptr = (byte*) new_row + get_field_offset(table, field);
@@ -3875,7 +3882,7 @@ calc_row_difference(
 
 		field_mysql_type = field->type();
 
-		col_type = prebuilt->table->cols[i].mtype;
+		col_type = prebuilt->table->cols[innodb_idx].mtype;
 
 		switch (col_type) {
 
@@ -3930,7 +3937,7 @@ calc_row_difference(
 			/* Let us use a dummy dfield to make the conversion
 			from the MySQL column format to the InnoDB format */
 
-			dict_col_copy_type_noninline(prebuilt->table->cols + i,
+			dict_col_copy_type_noninline(prebuilt->table->cols + innodb_idx,
 						     &dfield.type);
 
 			if (n_len != UNIV_SQL_NULL) {
@@ -3951,9 +3958,11 @@ calc_row_difference(
 
 			ufield->exp = NULL;
 			ufield->field_no = dict_col_get_clust_pos_noninline(
-				&prebuilt->table->cols[i], clust_index);
+				&prebuilt->table->cols[innodb_idx], clust_index);
 			n_changed++;
 		}
+                if (field->stored_in_db)
+                  innodb_idx++;
 	}
 
 	uvect->n_fields = n_changed;
@@ -4939,7 +4948,7 @@ create_table_def(
 	/* We pass 0 as the space id, and determine at a lower level the space
 	id where to store the table */
 
-	table = dict_mem_table_create(table_name, 0, n_cols, flags);
+	table = dict_mem_table_create(table_name, 0, form->s->stored_fields, flags);
 
 	if (path_of_temp_table) {
 		table->dir_path_of_temp_table =
@@ -4948,6 +4957,8 @@ create_table_def(
 
 	for (i = 0; i < n_cols; i++) {
 		field = form->field[i];
+		if (!field->stored_in_db)
+		  continue;
 
 		col_type = get_innobase_type_from_mysql_type(&unsigned_type,
 									field);
@@ -5236,7 +5247,7 @@ ha_innobase::create(
 	}
 #endif
 
-	if (form->s->fields > 1000) {
+	if (form->s->stored_fields > 1000) {
 		/* The limit probably should be REC_MAX_N_FIELDS - 3 = 1020,
 		but we play safe here */
 
@@ -5746,10 +5757,10 @@ ha_innobase::records_in_range(
 	KEY*		key;
 	dict_index_t*	index;
 	uchar*		key_val_buff2	= (uchar*) my_malloc(
-						  table->s->reclength
+						  table->s->stored_rec_length
 					+ table->s->max_key_length + 100,
 								MYF(MY_FAE));
-	ulint		buff2_len = table->s->reclength
+	ulint		buff2_len = table->s->stored_rec_length
 					+ table->s->max_key_length + 100;
 	dtuple_t*	range_start;
 	dtuple_t*	range_end;

=== modified file 'storage/innobase/handler/ha_innodb.h'
--- storage/innobase/handler/ha_innodb.h	2008-12-14 20:59:50 +0000
+++ storage/innobase/handler/ha_innodb.h	2009-03-24 10:47:21 +0000
@@ -199,6 +199,7 @@ class ha_innobase: public handler
 	int cmp_ref(const uchar *ref1, const uchar *ref2);
 	bool check_if_incompatible_data(HA_CREATE_INFO *info,
 					uint table_changes);
+	bool check_if_supported_virtual_columns(void) { return TRUE;}
 };
 
 /* Some accessor functions which the InnoDB plugin needs, but which

=== modified file 'storage/myisam/ha_myisam.cc'
--- storage/myisam/ha_myisam.cc	2008-10-10 15:28:41 +0000
+++ storage/myisam/ha_myisam.cc	2009-03-24 10:47:21 +0000
@@ -230,7 +230,7 @@ int table2myisam(TABLE *table_arg, MI_KE
   record= table_arg->record[0];
   recpos= 0;
   recinfo_pos= recinfo;
-  while (recpos < (uint) share->reclength)
+  while (recpos < (uint) share->stored_rec_length)
   {
     Field **field, *found= 0;
     minpos= share->reclength;

=== modified file 'storage/myisam/ha_myisam.h'
--- storage/myisam/ha_myisam.h	2008-06-28 12:45:15 +0000
+++ storage/myisam/ha_myisam.h	2009-03-24 10:47:22 +0000
@@ -132,6 +132,7 @@ class ha_myisam: public handler
   int assign_to_keycache(THD* thd, HA_CHECK_OPT* check_opt);
   int preload_keys(THD* thd, HA_CHECK_OPT* check_opt);
   bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes);
+  bool check_if_supported_virtual_columns(void) { return TRUE;}
 #ifdef HAVE_REPLICATION
   int dump(THD* thd, int fd);
   int net_read_dump(NET* net);


References