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. SPRING IN ACTION 第4版笔记-第七章Advanced Spring MVC-002- 在xml中引用Java配置文件,声明DispatcherServlet、ContextLoaderListener

    一.所有声明都用xml 1. <?xml version="1.0" encoding="UTF-8"?> <web-app version= ...

  2. ANDROID_MARS学习笔记_S01原始版_006_ListView

    一.代码1.xml(1)main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayo ...

  3. mapreduce实现"浏览该商品的人大多数还浏览了"经典应用

    输入: 日期    ...cookie id.        ...商品id.. xx            xx                        xx 输出: 商品id        ...

  4. MapReduce 模式、算法和用例(MapReduce Patterns, Algorithms, and Use Cases)

    在新文章“MapReduce模式.算法和用例”中,Ilya Katsov提供了一个系统化的综述,阐述了能够应用MapReduce框架解决的问题. 文章开始描述了一个非常简单的.作为通用的并行计算框架的 ...

  5. 【转】VC中对文件的读写

    原文网址:http://www.cnblogs.com/LJWJL/archive/2012/10/06/2712466.html 注意: 1.由于C是缓冲写 所以要在关闭或刷新后才能看到文件内容 2 ...

  6. Orchard中的多语言功能

    在Orchard中支持了两种本地化的方法: 1.对Orchard应用程序和模块中的一些文本字符串进行本地化.这个就相当程序本身的多语言支持,大多数的CMS系统都支持这一功能,如:DotNetNuke. ...

  7. BrnShop开源网上商城第四讲:自定义插件

    重要通知:BrnShop企业版NOSQL设计(基于Redis)已经开源!源码内置于最新版的BrnShop中,感兴趣的园友可以去下载来看看.官网地址:www.brnshop.com. 好了现在进入今天的 ...

  8. 【原】Windows中使用Redis基本入门教程

    Redis是c编写基于Unix平台开发的一种内存KV数据库,官网上并没有给出Window的安装包,但MS基于redis发布了Windows版本. 下载链接: https://github.com/MS ...

  9. vijosP1359 Superprime

    vijosP1359 Superprime 链接:https://vijos.org/p/1359 [思路] 搜索+数学. 很明显的搜索,依次确定每一个数,用参数sum记录dfs即可. 本题的关键在于 ...

  10. 《Genesis-3D开源游戏引擎--横版格斗游戏制作教程01: 资源导入》

    1. 资源导入 概述: 制作一款游戏需要用到很多资源,比如:模型.纹理.声音和脚本等.通常都是用其它相关制作资源软件,完成前期资源的收集工作.比如通常用的三维美术资源,会在Max.MAYA等相应软件中 ...