MySQL對於datetime 源碼分析

來源:互聯網
上載者:User

標籤:

http://tsecer.blog.163.com/blog/static/150181720160117355684/  一、時間比較的文法分析在mysql中,通常時間是一個必不可少的類型,而這種類型的一個特殊地方就在於它的比較函數。其實,即使對於字串的比較函數和對於int的比較也是完全不同的,但是這個差別並不想以datetime儲存的時間這麼具有視覺的震撼性和表現的這麼明顯。這裡還有一個問題在於,當mysql的yacc在進行文法分析的時候,這個時候文法分析器並沒有讀取一個field中的結構,或者說並沒有對fiedl進行fix操作,所以當parser看到一個 field > "2016-01-11 00:00:00"這樣的字串的時候,它並不知道這個地方是要進行一個字串的比較還是一個時間的比較,而需要在對於一個sql的運算式的各個field進行fix之後才能夠找到這個對應的field的類型,並進而進行類型的判斷,這裡主要說明的是一個比較函數延遲設定的問題。由於mysql有內建的mysql.event表格包含了有datetime類型的欄位,所以使用這個內建的類型進行比較說明是比較合適的:select SQL_NO_CACHE * from event where created > "2016-01-10 00:00:00";在sql_yacc.yy的文法分析函數中,此時使用不同的creator建立了比較的item:bool_pri:          bool_pri IS NULL_SYM %prec IS          {            $$= new (YYTHD->mem_root) Item_func_isnull($1);            if ($$ == NULL)              MYSQL_YYABORT;          }……        | bool_pri comp_op predicate %prec EQ          {            $$= (*$2)(0)->create($1,$3);            if ($$ == NULL)              MYSQL_YYABORT;          } comp_op:          EQ     { $$ = &comp_eq_creator; }        | GE     { $$ = &comp_ge_creator; }        | GT_SYM { $$ = &comp_gt_creator; }        | LE     { $$ = &comp_le_creator; }        | LT     { $$ = &comp_lt_creator; }        | NE     { $$ = &comp_ne_creator; }        ;Item_bool_func2* Ge_creator::create(Item *a, Item *b) const{  return new Item_func_ge(a, b);} 二、具體比較方法的確定setup_conds-->>Item_bool_func2::fix_length_and_dec-->>Item_bool_func2::set_cmp_func-->>Arg_comparator::set_cmp_func-->>可以看到的是,在進入函數之後,優先判斷的datetime類型的比較類型,這個優先順序最高,如果比較的兩方一個是datetime類型,而另一個是string類型,則會執行對string向時間的轉換。 int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,                                        Item **a1, Item **a2,                                        Item_result type){  enum enum_date_cmp_type cmp_type;  ulonglong const_value= (ulonglong)-1;  thd= current_thd;  owner= owner_arg;  set_null= set_null && owner_arg;  a= a1;  b= a2;  thd= current_thd;   if ((cmp_type= can_compare_as_dates(*a, *b, &const_value)))  {    a_type= (*a)->field_type();    b_type= (*b)->field_type();    a_cache= 0;    b_cache= 0;     if (const_value != (ulonglong)-1)    {      /*        cache_converted_constant can‘t be used here because it can‘t        correctly convert a DATETIME value from string to int representation.      */      Item_cache_int *cache= new Item_cache_int(MYSQL_TYPE_DATETIME);      /* Mark the cache as non-const to prevent re-caching. */      cache->set_used_tables(1);      if (!(*a)->is_datetime())      {        cache->store((*a), const_value);        a_cache= cache;        a= (Item **)&a_cache;      }      else      {        cache->store((*b), const_value);        b_cache= cache;        b= (Item **)&b_cache;      }    }    is_nulls_eq= is_owner_equal_func();    func= &Arg_comparator::compare_datetime;    get_value_a_func= &get_datetime_value;    get_value_b_func= &get_datetime_value;    return 0;  }  else if (type == STRING_RESULT && (*a)->field_type() == MYSQL_TYPE_TIME &&           (*b)->field_type() == MYSQL_TYPE_TIME)  {    /* Compare TIME values as integers. */    a_cache= 0;    b_cache= 0;    is_nulls_eq= is_owner_equal_func();    func= &Arg_comparator::compare_datetime;    get_value_a_func= &get_time_value;    get_value_b_func= &get_time_value;    return 0;  }  else if (type == STRING_RESULT &&           (*a)->result_type() == STRING_RESULT &&           (*b)->result_type() == STRING_RESULT)  {    DTCollation coll;    coll.set((*a)->collation.collation);    if (agg_item_set_converter(coll, owner->func_name(),                               b, 1, MY_COLL_CMP_CONV, 1))      return 1;  }  else if (try_year_cmp_func(type))    return 0;   a= cache_converted_constant(thd, a, &a_cache, type);  b= cache_converted_constant(thd, b, &b_cache, type);  return set_compare_func(owner_arg, type);}如果datefield的另一端不是string類型,此時比較進入另一份分支,按照int類型來比較:void Item_bool_func2::fix_length_and_dec()……  if (!thd->lex->is_ps_or_view_context_analysis())  {    if (args[0]->real_item()->type() == FIELD_ITEM)    {      Item_field *field_item= (Item_field*) (args[0]->real_item());      if (field_item->field->can_be_compared_as_longlong() &&          !(field_item->is_datetime() &&            args[1]->result_type() == STRING_RESULT))      {        if (convert_constant_item(thd, field_item, &args[1]))        {          cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,                           INT_RESULT); // Works for all types.          args[0]->cmp_context= args[1]->cmp_context= INT_RESULT;          return;        }      }    }  三、datetime類型的解析enum enum_mysql_timestamp_typestr_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,                uint flags, int *was_cut){……  for (i = start_loop;       i < MAX_DATE_PARTS-1 && str != end &&         my_isdigit(&my_charset_latin1,*str);       i++)  {……    while (str != end &&           (my_ispunct(&my_charset_latin1,*str) ||            my_isspace(&my_charset_latin1,*str)))    {      if (my_isspace(&my_charset_latin1,*str))      {        if (!(allow_space & (1 << i)))        {          *was_cut= 1;          DBUG_RETURN(MYSQL_TIMESTAMP_NONE);        }        found_space= 1;      }      str++;      found_delimitier= 1;                      /* Should be a ‘normal‘ date */    }這裡可以看到,它對各個欄位的分隔字元並沒有和我們常見的所謂“YYYY-MM-DD HH:MM:SS”,而是任意的一個分隔字元都可以,一我們預設的latin字元集為例,可以看到大量的字元都可以作為分隔字元,下面所有和0x10邏輯與之後非零的字元都可以具有分隔字元的功能,MysQL對於這個沒有任何要求,也就是它並不挑食mysql-5.1.61\strings\ctype-latin1.c:static uchar ctype_latin1[] = {    0,   32, 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 32, 32,   32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,   72, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,  132,132,132,132,132,132,132,132,132,132, 16, 16, 16, 16, 16, 16,   16,129,129,129,129,129,129,  1,  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, 16, 16, 16, 16, 16,   16,130,130,130,130,130,130,  2,  2,  2,  2,  2,  2,  2,  2,  2,    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2, 16, 16, 16, 16, 32,   16,  0, 16,  2, 16, 16, 16, 16, 16, 16,  1, 16,  1,  0,  1,  0,    0, 16, 16, 16, 16, 16, 16, 16, 16, 16,  2, 16,  2,  0,  2,  1,   72, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,   16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1, 16,  1,  1,  1,  1,  1,  1,  1,  2,    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,    2,  2,  2,  2,  2,  2,  2, 16,  2,  2,  2,  2,  2,  2,  2,  2};mysql> select date( "2016|01!10 00%00%00"),  time( "2016-01-10 11%22$33");+------------------------------+------------------------------+| date( "2016|01!10 00%00%00") | time( "2016-01-10 11%22$33") |+------------------------------+------------------------------+| 2016-01-10                   | 11:22:33                     |+------------------------------+------------------------------+1 row in set (0.00 sec) mysql> 四、datetime的儲存這個非常有意思,跳出了我們常見的utc儲存的常規思路,那麼為什麼它就可以使用這麼簡潔直觀的方式呢?究其原因,就是有空間就是任性,現在的Unix時間通常是按照long儲存的,但是早期的long類型都是32位元組,所以即使從1970年開始,到2038年就會產生負數,所以必須進行壓縮,也就是轉換為從1970開始的秒數,現在使用更長的儲存空間來儲存,所以這個方法就是直接使用這種格式儲存就可以了/* Convert time value to integer in YYYYMMDDHHMMSS format */ ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *my_time){  return ((ulonglong) (my_time->year * 10000UL +                       my_time->month * 100UL +                       my_time->day) * ULL(1000000) +          (ulonglong) (my_time->hour * 10000UL +                       my_time->minute * 100UL +                       my_time->second));}mysql> select cast( cast(‘2007-12-25‘ as DATETIME)  as UNSIGNED INT);+--------------------------------------------------------+| cast( cast(‘2007-12-25‘ as DATETIME)  as UNSIGNED INT) |+--------------------------------------------------------+|                                         20071225000000 |+--------------------------------------------------------+1 row in set (0.01 sec) mysql> 五、比較中使用哪種類型的選擇和很多的非強制類型一樣,mysql允許在多種不同的類型之間進行自動轉換,這裡就涉及到選擇結構中的資料類型是向哪一個靠攏的問題,當然如果說兩個類型都是相同的話,那麼皆大歡喜。問題在於經常會遇到類型不一致的兩個變數進行操作,此時就需要判斷結果到底是什麼類型,這裡需要在具體的環境下進行具體的分析,但是大致來說有一個大致的原則在一個函數中體現mysql-5.1.61\sql\item.cc: Item_result item_cmp_type(Item_result a,Item_result b){  if (a == STRING_RESULT && b == STRING_RESULT)    return STRING_RESULT;  if (a == INT_RESULT && b == INT_RESULT)    return INT_RESULT;  else if (a == ROW_RESULT || b == ROW_RESULT)    return ROW_RESULT;  if ((a == INT_RESULT || a == DECIMAL_RESULT) &&      (b == INT_RESULT || b == DECIMAL_RESULT))    return DECIMAL_RESULT;  return REAL_RESULT;}這裡可以看到的是,如果兩個都是string,那麼結果就是string,但是如果兩者不一致,只要有任何一個是一個int類型,那麼盡量向int類型靠攏。 六、總結總起來說,這裡講到的問題在實際中的意義並不大,可以說沒有什麼意義。只是說為了這個問題遇到的情況比較多,或者說這裡看到的問題比較常見,所以希望大家如果從這裡能夠看到一些其它的更為需要的流程來說,就非常有意義了。

MySQL對於datetime 源碼分析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.