MySQL Meta中的length字段 -- (1) 初始值的length计算

MySQL Meta中的length字段 – (1) 初始值的length计算
MySQL Meta中的length字段 – (2) length的推导
MySQL Meta中的length字段 – (3) length的推导举例
MySQL Meta中的length字段 – (4) 玩儿MySQL代码
后来的故事 - 想到哪写到哪百孔千疮漏洞百出MySQL MetaData Length实现
后来的故事 - Precision和Length对MySQL执行结果的影响


官方对于length的解释

unsigned int max_length

The maximum length of the result. The default max_length value differs depending on the result type of the function. For string functions, the default is the length of the longest argument. For integer functions, the default is 21 digits. For real functions, the default is 13 plus the number of decimal digits indicated by initid->decimals. (For numeric functions, the length includes any sign or decimal point characters.)

If you want to return a blob value, you can set max_length to 65KB or 16MB. This memory is not allocated, but the value is used to decide which data type to use if there is a need to temporarily store the data.

⬆️来源: http://docs.oracle.com/cd/E17952_01/refman-5.5-en/udf-calling.html

length是什么?

登陆mysql时,带上 --column-type-info 选项,那么对于任意一条有结果集输出的MySQL语句,它都会先输出结果集的MetaData。

例如:shell > mysql -h localhost -uroot -p111 --database=we  --column-type-info;
mysql> desc t1;
Field   1:  `Field`
Catalog:    `def`
Database:   ``
Table:      `COLUMNS`
Org_table:  ``
Type:       VAR_STRING
Collation:  latin1_swedish_ci (8)
Length:     64
Max_length: 11
Decimals:   0
Flags:      NOT_NULL


Field   2:  `Type`
Catalog:    `def`
Database:   ``
Table:      `COLUMNS`
Org_table:  ``
Type:       BLOB
Collation:  latin1_swedish_ci (8)
Length:     196605
Max_length: 14
Decimals:   0
Flags:      NOT_NULL BLOB


Field   3:  `Null`
Catalog:    `def`
Database:   ``
Table:      `COLUMNS`
Org_table:  ``
Type:       VAR_STRING
Collation:  latin1_swedish_ci (8)
Length:     3
Max_length: 3
Decimals:   0
Flags:      NOT_NULL


Field   4:  `Key`
Catalog:    `def`
Database:   ``
Table:      `COLUMNS`
Org_table:  ``
Type:       VAR_STRING
Collation:  latin1_swedish_ci (8)
Length:     3
Max_length: 3
Decimals:   0
Flags:      NOT_NULL


Field   5:  `Default`
Catalog:    `def`
Database:   ``
Table:      `COLUMNS`
Org_table:  ``
Type:       BLOB
Collation:  latin1_swedish_ci (8)
Length:     196605
Max_length: 0
Decimals:   0
Flags:      BLOB


Field   6:  `Extra`
Catalog:    `def`
Database:   ``
Table:      `COLUMNS`
Org_table:  ``
Type:       VAR_STRING
Collation:  latin1_swedish_ci (8)
Length:     27
Max_length: 0
Decimals:   0
Flags:      NOT_NULL




+-------------+----------------+------+-----+---------+-------+
| Field       | Type           | Null | Key | Default | Extra |
+-------------+----------------+------+-----+---------+-------+
| id          | int(11)        | NO   | PRI | NULL    |       |
| binaryvalue | varbinary(255) | YES  |     | NULL    |       |
| charvalue   | char(2)        | YES  |     | NULL    |       |
| bin2        | binary(3)      | YES  |     | NULL    |       |
+-------------+----------------+------+-----+---------+-------+
4 rows in set (0.00 sec)






mysql> select * from t1;
Field   1:  `id`
Catalog:    `def`
Database:   `we`
Table:      `t1`
Org_table:  `t1`
Type:       LONG
Collation:  binary (63)
Length:     11
Max_length: 1
Decimals:   0
Flags:      NOT_NULL PRI_KEY NO_DEFAULT_VALUE NUM PART_KEY

Field   2:  `binaryvalue`
Catalog:    `def`
Database:   `we`
Table:      `t1`
Org_table:  `t1`
Type:       VAR_STRING
Collation:  binary (63)
Length:     255
Max_length: 2
Decimals:   0
Flags:      BINARY

Field   3:  `charvalue`
Catalog:    `def`
Database:   `we`
Table:      `t1`
Org_table:  `t1`
Type:       STRING
Collation:  latin1_swedish_ci (8)
Length:     2
Max_length: 2
Decimals:   0
Flags:

Field   4:  `bin2`
Catalog:    `def`
Database:   `we`
Table:      `t1`
Org_table:  `t1`
Type:       STRING
Collation:  binary (63)
Length:     3
Max_length: 3
Decimals:   0
Flags:      BINARY


+----+-------------+-----------+------+
| id | binaryvalue | charvalue | bin2 |
+----+-------------+-----------+------+
|  1 | hi          | wo        | go   |
+----+-------------+-----------+------+
1 row in set (0.00 sec)

显示一个字段需要的最小字节数。
显示,意味着要转化为字符串;最小,意味着大一点没关系,小了不可以。

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<String> *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; }
        ;                           
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值