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_type
str_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 源码分析的更多相关文章

  1. C# DateTime的11种构造函数 [Abp 源码分析]十五、自动审计记录 .Net 登陆的时候添加验证码 使用Topshelf开发Windows服务、记录日志 日常杂记——C#验证码 c#_生成图片式验证码 C# 利用SharpZipLib生成压缩包 Sql2012如何将远程服务器数据库及表、表结构、表数据导入本地数据库

    C# DateTime的11种构造函数   别的也不多说没直接贴代码 using System; using System.Collections.Generic; using System.Glob ...

  2. MySQL源码分析以及目录结构 2

    原文地址:MySQL源码分析以及目录结构作者:jacky民工 主要模块及数据流经过多年的发展,mysql的主要模块已经稳定,基本不会有大的修改.本文将对MySQL的整体架构及重要目录进行讲述. 源码结 ...

  3. MySQL源码分析以及目录结构

    原文地址:MySQL源码分析以及目录结构作者:jacky民工 主要模块及数据流经过多年的发展,mysql的主要模块已经稳定,基本不会有大的修改.本文将对MySQL的整体架构及重要目录进行讲述. 源码结 ...

  4. (3.10)mysql基础深入——mysqld 服务器与客户端连接过程 源码分析【待写】

    (3.10)mysql基础深入——mysqld 服务器与客户端连接过程 源码分析[待写]

  5. mysql复制那点事(2)-binlog组提交源码分析和实现

    mysql复制那点事(2)-binlog组提交源码分析和实现 [TOC] 0. 参考文献 序号 文献 1 MySQL 5.7 MTS源码分析 2 MySQL 组提交 3 MySQL Redo/Binl ...

  6. mysql源码分析-启动过程

    mysql源码分析-启动过程 概要 # sql/mysqld.cc, 不包含psi的初始化过程 mysqld_main: // 加载my.cnf和my.cnf.d,还有命令行参数 if (load_d ...

  7. JFinal 源码分析 [DB+ActiveRecord]

    我记得以前有人跟我说,“面试的时候要看spring的源码,要看ioc.aop的源码"那为什么要看这些开源框架的源码呢,其实很多人都是"应急式"的去读,就像读一篇文章一下, ...

  8. 深度 Mybatis 3 源码分析(一)SqlSessionFactoryBuilder源码分析

    MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java ...

  9. Flask框架 (四)—— 请求上下文源码分析、g对象、第三方插件(flask_session、flask_script、wtforms)、信号

    Flask框架 (四)—— 请求上下文源码分析.g对象.第三方插件(flask_session.flask_script.wtforms).信号 目录 请求上下文源码分析.g对象.第三方插件(flas ...

随机推荐

  1. 气死人不偿命,Q_OBJECT导致的C++报错,而且还看不明白(#ifdef没控制好,导致什么都不认识了)

    为了代码可以同时适应VC++和MingW编译器,我改动了我的代码,变成: #ifdef _MSC_VER #pragma comment(lib, "crypt32.lib") / ...

  2. 图片缩放时java.lang.IllegalArgumentException: pointerIndex out of range解决方案

    版权声明:本文为博主原创文章,未经博主允许不得转载. 06-03 20:45:24.143: E/AndroidRuntime(1230): FATAL EXCEPTION: main06-03 20 ...

  3. RHEL双网卡绑定

    新增绑定网卡文件: vi /etc/sysconfig/network-scripts/ifcfg-bond0 DEVICE=bond0 ONBOOT=yes TYPE=Ethernet BOOTPR ...

  4. BZOJ_1606_ [Usaco2008_Dec]_Hay_For_Sale _购买干草_(背包)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1606 价值和重量相等的01背包问题. 分析 ... #include <bits/std ...

  5. PHP 'ext/gd/gd.c'信息泄漏漏洞

    漏洞版本: PHP 5.5.x 漏洞描述: CVE ID:CVE-2014-2020 PHP是一种HTML内嵌式的语言. PHP 'ext/gd/gd.c'没有检查数据类型,允许远程攻击者使用字符串或 ...

  6. 致改变——总结&规划(2016·一)

    今天是立夏,过完这一天意味着农历2016年的第一季度已经过去了,也意味着真正的夏天已经来了.如果说春天是作物的播种期的话,那夏天可以看做是作物的成长期,也是农民伯伯们最繁忙的时期.本文主要对自己过去的 ...

  7. Word对象模型 (.Net Perspective)

    本文主要针对在Visual Studio中使用C# 开发关于Word的应用程序 来源:Understandingthe Word Object Model from a .NET Developer' ...

  8. HTMLParser 使用详解

    htmlparser是一个纯的java写的html解析的库,它不依赖于其它的java库文件,主要用于改造或   提取html.它能超高速解析html,而且不会出错.现在htmlparser最新版本为2 ...

  9. Nginx安装及配置简介

    前言 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行.由俄罗斯的程序设计师Igor Sysoev所开发,供俄国大 ...

  10. POJ1061 青蛙的约会 扩展欧几里得

    模板题,这题有一点需要注意,因为要求非负,ax=b(mod L) 得保证 a>=0 #include <stdio.h> #include <iostream> #inc ...