length字段的值来源于两方面:
* 初始值
* 推导运算
初始值,也来源于两方面:
* 常量定义
* table column定义
常量定义
见mysql-5.6/sql/sql_yacc.yy,NUM_literal,literal等的定义,如下,Item_int的第三个参数,就是length,可以看到,它的处理方式很直接粗暴,取了literal的length值。
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;
}
mysql> select -003;
Field 1: `-003`
Catalog: `def`
Database: ``
Table: ``
Org_table: ``
Type: LONGLONG
Collation: binary (63)
Length: 4
Max_length: 2
Decimals: 0
Flags: NOT_NULL BINARY NUM
Table Column定义
代码还是在sql_yacc.cc中
type:
int_type opt_field_length field_options { $$=$1; }
| real_type opt_precision field_options { $$=$1; }
| FLOAT_SYM float_options field_options { $$=MYSQL_TYPE_FLOAT; }
| BIT_SYM
{
Lex->length= (char*) "1";
$$=MYSQL_TYPE_BIT;
}
| BIT_SYM field_length
{
$$=MYSQL_TYPE_BIT;
}
| BOOL_SYM
{
Lex->length= (char*) "1";
$$=MYSQL_TYPE_TINY;
}
| BOOLEAN_SYM
{
Lex->length= (char*) "1";
$$=MYSQL_TYPE_TINY;
}
| char field_length opt_binary
{
$$=MYSQL_TYPE_STRING;
}
| char opt_binary
{
Lex->length= (char*) "1";
$$=MYSQL_TYPE_STRING;
}
| nchar field_length opt_bin_mod
{
$$=MYSQL_TYPE_STRING;
Lex->charset=national_charset_info;
}
field_length:
'(' LONG_NUM ')' { Lex->length= $2.str; }
| '(' ULONGLONG_NUM ')' { Lex->length= $2.str; }
| '(' DECIMAL_NUM ')' { Lex->length= $2.str; }
| '(' NUM ')' { Lex->length= $2.str; };
opt_field_length:
/* empty */ { Lex->length=(char*) 0; /* use default length */ }
| field_length { }
;
全部都是字面长度。对于varchar,也是如此。不过在后面的解析推导过程中,需要注意collation,补乘上collation的最大字节宽度。
Create Table中对应的Column解析代码位于field.cc
/** Initialize a column definition object. Column definition objects can be used to construct Field objects. @param thd Session/Thread handle. @param fld_name Column name. @param fld_type Column type. @param fld_length Column length. @param fld_decimals Number of digits to the right of the decimal point (if any.) @param fld_type_modifier Additional type information. @param fld_default_value Column default expression (if any.) @param fld_on_update_value The expression in the ON UPDATE clause. @param fld_comment Column comment. @param fld_change Column change. @param fld_interval_list Interval list (if any.) @param fld_charset Column charset. @param fld_geom_type Column geometry type (if any.) @retval FALSE on success. @retval TRUE on error. */
bool Create_field::init(THD *thd, const char *fld_name,
enum_field_types fld_type, const char *fld_length,
const char *fld_decimals, uint fld_type_modifier,
Item *fld_default_value, Item *fld_on_update_value,
LEX_STRING *fld_comment, const char *fld_change,
List *fld_interval_list,
const CHARSET_INFO *fld_charset, uint fld_geom_type)
{
....
if (fld_length != NULL)
{
errno= 0;
length= strtoul(fld_length, NULL, 10); // SQL中fld_length都是用字符串表示,转成整数
if ((errno != 0) || (length > MAX_FIELD_BLOBLENGTH))
{
my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), fld_name, MAX_FIELD_BLOBLENGTH);
DBUG_RETURN(TRUE);
}
if (length == 0)
fld_length= NULL; /* purecov: inspected */
}
sign_len= fld_type_modifier & UNSIGNED_FLAG ? 0 : 1;
switch (fld_type) {
case MYSQL_TYPE_TINY:
if (!fld_length)
length= MAX_TINYINT_WIDTH+sign_len;
allowed_type_modifier= AUTO_INCREMENT_FLAG;
break;
case MYSQL_TYPE_SHORT:
if (!fld_length)
length= MAX_SMALLINT_WIDTH+sign_len;
allowed_type_modifier= AUTO_INCREMENT_FLAG;
break;
case MYSQL_TYPE_INT24:
if (!fld_length)
length= MAX_MEDIUMINT_WIDTH+sign_len;
allowed_type_modifier= AUTO_INCREMENT_FLAG;
break;
SELECT i+j FROM .. 中对应i+j的解析见:sql_yacc.yy
select_item:
remember_name table_wild remember_end
{
THD *thd= YYTHD;
if (add_item_to_list(thd, $2))
MYSQL_YYABORT;
}
| remember_name expr remember_end select_alias
{
THD *thd= YYTHD;
DBUG_ASSERT($1 < $3);
if (add_item_to_list(thd, $2))
MYSQL_YYABORT;
if ($4.str)
{
if (Lex->sql_command == SQLCOM_CREATE_VIEW &&
check_column_name($4.str))
{
my_error(ER_WRONG_COLUMN_NAME, MYF(0), $4.str);
MYSQL_YYABORT;
}
$2->item_name.copy($4.str, $4.length, system_charset_info, false);
}
else if (!$2->item_name.is_set())
{
$2->item_name.copy($1, (uint) ($3 - $1), thd->charset());
}
}
;
/* all possible expressions */
expr:
expr or expr %prec OR_SYM
{
/* Design notes: Do not use a manually maintained stack like thd->lex->xxx_list, but use the internal bison stack ($$, $1 and $3) instead. Using the bison stack is: - more robust to changes in the grammar, - guaranteed to be in sync with the parser state, - better for performances (no memory allocation). */
Item_cond_or *item1;
Item_cond_or *item3;
if (is_cond_or($1))
....
simple_ident:
ident
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
sp_pcontext *pctx = lex->get_sp_current_parsing_ctx();
sp_variable *spv;
if (pctx && (spv= pctx->find_variable($1, false)))
{
Lex_input_stream *lip= &thd->m_parser_state->m_lip;
sp_head *sp= lex->sphead;
DBUG_ASSERT(sp);
/* We're compiling a stored procedure and found a variable */
if (! lex->parsing_options.allows_variable)
{
my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
MYSQL_YYABORT;
}
$$=
create_item_for_sp_var(
thd, $1, spv,
sp->m_parser_data.get_current_stmt_start_ptr(),
lip->get_tok_start_prev(),
lip->get_tok_end());
if ($$ == NULL)
MYSQL_YYABORT;
lex->safe_to_cache_query= false;
}
else
{
SELECT_LEX *sel=Select;
if ((sel->parsing_place != IN_HAVING) ||
(sel->get_in_sum_expr() > 0))
{
$$= new (thd->mem_root) Item_field(Lex->current_context(),
NullS, NullS, $1.str);
}
else
{
$$= new (thd->mem_root) Item_ref(Lex->current_context(),
NullS, NullS, $1.str);
}
if ($$ == NULL)
MYSQL_YYABORT;
}
}
| simple_ident_q { $$= $1; }
;