MySQL中的一切表达式都是继承自Item类,常量也不外乎如此。以Item_float为例子说明MySQL如何初始化常量Item。
首先在Parser里面:
NUM_literal:
NUM
{
int error;
$$= new (YYTHD->mem_root)
Item_int($1,
(longlong) my_strtoll10($1.str, NULL, &error),
$1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
| LONG_NUM
{
int error;
$$= new (YYTHD->mem_root)
Item_int($1,
(longlong) my_strtoll10($1.str, NULL, &error),
$1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
| ULONGLONG_NUM
{
$$= new (YYTHD->mem_root) Item_uint($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
| DECIMAL_NUM
{
$$= new (YYTHD->mem_root) Item_decimal($1.str, $1.length,
YYTHD->charset());
if (($$ == NULL) || (YYTHD->is_error()))
{
MYSQL_YYABORT;
}
}
| FLOAT_NUM
{
$$= new (YYTHD->mem_root) Item_float($1.str, $1.length);
if (($$ == NULL) || (YYTHD->is_error()))
{
MYSQL_YYABORT;
}
}
;
FLOAT_NUM部分编译后变成如下代码:
case 1946:
/* Line 1455 of yacc.c */
#line 13534 "/export/home/pb2/build/sb_0-15908920-1436909309.31
# /mysql-5.6.26-release-export-8213452_gpl/sql/sql_yacc.yy"
{
(yyval.item_num)=
new (YYTHD->mem_root) Item_float((yyvsp[(1) - (1)].lex_str).str,
(yyvsp[(1) - (1)].lex_str).length);
if (((yyval.item_num) == NULL) || (YYTHD->is_error()))
{
MYSQL_YYABORT;
}
}
break;
对应的Item_float构造函数为:
/**
This function is only called during parsing:
- when parsing SQL query from sql_yacc.yy
- when parsing XPath query from item_xmlfunc.cc
We will signal an error if value is not a true double value (overflow):
eng: Illegal %s '%-.192s' value found during parsing
Note: str_arg does not necessarily have to be a null terminated string,
e.g. it is NOT when called from item_xmlfunc.cc or sql_yacc.yy.
*/
Item_float::Item_float(const char *str_arg, uint length)
{
int error;
char *end_not_used;
value= my_strntod(&my_charset_bin, (char*) str_arg,
length, &end_not_used,
&error);
if (error)
{
char tmp[NAME_LEN + 1];
my_snprintf(tmp, sizeof(tmp), "%.*s", length, str_arg);
my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "double", tmp);
}
presentation.copy(str_arg, length);
item_name.copy(str_arg, length);
decimals=(uint8) nr_of_decimals(str_arg, str_arg+length);
max_length=length;
fixed= 1;
}
可见,对于Float、Int、Decimal等,都是先传入字符串及其长度,然后他们内部进行解析,以确定max_length、decimals等值。
既然说到了Item_float,就顺便说一下里面的一个函数nr_of_decimals,它用来计算浮点数中小数点后面的位数,然而,如代码中的注释所言,这个函数跟没有啥鸟用,是Decimal出现之前的产物。在Decimal出现之前 1.35会被当做一个浮点数,下面的函数会计算出decimal=2;有了Decimal之后,1.35根本不会被当做float,而是直接当做Decimal处理。
static uint nr_of_decimals(const char *str, const char *end)
{
const char *decimal_point;
/* Find position for '.' */
for (;;)
{
if (str == end)
return 0;
if (*str == 'e' || *str == 'E')
return NOT_FIXED_DEC;
if (*str++ == '.')
break;
}
decimal_point= str;
for ( ; str < end && my_isdigit(system_charset_info, *str) ; str++)
;
if (str < end && (*str == 'e' || *str == 'E'))
return NOT_FIXED_DEC;
/*
QQ:
The number of decimal digist in fact should be (str - decimal_point - 1).
But it seems the result of nr_of_decimals() is never used!
In case of 'e' and 'E' nr_of_decimals returns NOT_FIXED_DEC.
In case if there is no 'e' or 'E' parser code in sql_yacc.yy
never calls Item_float::Item_float() - it creates Item_decimal instead.
The only piece of code where we call Item_float::Item_float(str, len)
without having 'e' or 'E' is item_xmlfunc.cc, but this Item_float
never appears in metadata itself. Changing the code to return
(str - decimal_point - 1) does not make any changes in the test results.
This should be addressed somehow.
Looks like a reminder from before real DECIMAL times.
*/
return (uint) (str - decimal_point);
}
所以,对于float来说,这个函数总是返回NOT_FIXED_DEC(=31)
fix variable decimals which always is NOT_FIXED_DEC
关于NOT_FIXED_DEC,会在下一篇博客中详细讨论。
标签:YYTHD,初始化,error,float,Item,length,str,MySQL From: https://blog.51cto.com/u_16162111/6492516