从gorose丢失反斜杠说起

从gorose丢失反斜杠说起

  

一、现象

  
在向 MySql 插入文章内容(varchar类型)的时候,发现文章内容中的反斜杠 \ 会丢失。为了确定问题的原因,下面开始查看相关的源码。

此项目使用的是 Go 1.11,Orm 使用的是 gorose v1.0.4, Mysql 版本是 5.7 。

  
它在处理 MySql 请求时,是采用了先拼装请求成为完整的 Sql 语句、然后执行此语句的方案。

一个典型的 Insert 请求代码栈如下, Update 同理。

// 1 调用 db session 的 insert 函数
dba.ExecuteAct("insert")

// 2 进入 Sql 拼接流程
dba.BuildSql(operType)

// 3 转入builder包
NewBuilder(dba.OrmApi, operType...)

// 4 选择MySql的builder适配类进行实际拼装
builder.BuildExecute(api, operType[0])

// 5 同时拼装 Sql 语句的各动态部分
update, insertkey, insertval = buildData(ormApi)

// 6 给字符串字段添加单引号,且进行转义处理
dataObj = append(dataObj, key+"="+utils.AddSingleQuotes(val))

  
第6层的添加单引号函数内容如下:

func AddSingleQuotes(data interface{}) string {
    switch data.(type) {
    case int,int64,int32,uint32,uint64:
        return ParseStr(data)
    default:
        return "'" + strings.Replace(ParseStr(data), "'", `\'`, -1) + "'"
    }
}

  
通过上面代码可以发现,gorose 在添加单引号的时候,只对其中包含的单引号进行了转义,其他所有字符均保持原样。

  

二、做个引号转义实验

  
gorose 在添加单引号的时候,仅仅对单引号进行了转义,这样就足够了么?
  
下面我们做个试验看下效果。

首先构造一个文章表,如下。我们用它的 content 字段进行实验,看下转义字符的表现。

CREATE TABLE `paper` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `content` varchar(500) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

  
实验1:单引号中的裸反斜杠 (\ ) 、 裸双引号 (")、转义反斜杠 (\ \ )、 转义双引号 (\ ") 。

mysql> insert into paper values(1, '  \  "  \\  \" ');
Query OK, 1 row affected

mysql> select * from paper where id=1;
+----+--------------+
| id | content      |
+----+--------------+
|  1 |     "  \  "  |
+----+--------------+
1 row in set

可以发现,裸反斜杠 (\ )丢失了,裸双引号 (")、转义反斜杠 (\ \ )、 转义双引号 (\ ")保留了下来。

  
实验2:双引号中的裸反斜杠 (\ ) 、 裸单引号 (')、转义反斜杠 (\ \ )、 转义单引号 (\ ') 。

mysql> insert into paper values(2, "  \  '  \\  \' ");
Query OK, 1 row affected

mysql> select * from paper where id=2;
+----+--------------+
| id | content      |
+----+--------------+
|  2 |     '  \  '  |
+----+--------------+
1 row in set

可以发现,裸反斜杠 (\ )丢失了,裸单引号 (')、转义反斜杠 (\ \ )、 转义单引号 (\ ') 保留了下来。

  
通过上面的实验可以得出以下结论:

  1. 单引号双引号都没有禁止转义的功能
  2. 插入反斜杠 (\ )必须进行转义
  3. 单引号内部插入双引号,双引号转义与否均可
  4. 双引号内部插入单引号,单引号转义与否均可

  

三、MySql官方的引号使用规则

  
MySql 5.7 特别说明了嵌套引号的方法。其规则翻译如下:

  1. 单引号包围中的一个单引号 ' 可以使用两个连续的单引号 ' ' 表示
  2. 双引号包围中的一个双引号 " 可以使用两个连续的双引号 " " 表示
  3. 嵌套引号可以使用反斜杠加字符的方法,比如 \ ' \ "
  4. 单引号包围中的双引号可以不用特殊处理,双引号包围中的单引号也可以不用特殊处理

  
此规则的第三第四条和我们的实验结果相符。

  

另外,这里引用了 MySql 5.7 的官方表格。此表都是引号包围中,可以使用的转义字符。

Escape Sequence Character Represented by Sequence
\0 An ASCII NUL (X'00') character
\' A single quote (') character
\" A double quote (") character
\b A backspace character
\n A newline (linefeed) character
\r A carriage return character
\t A tab character
\Z ASCII 26 (Control+Z); see note following the table
\\ A backslash (\) character
\% A % character; see note following the table
\_ A _ character; see note following the table

  

四、特殊的第一条和第二条

  
不了解第一条第二条规则的同学,可能觉得单引号直接嵌套单引号的写法会提示语法错误。
  
下面来实验下第一条和第二条规则。

mysql> insert into paper values(3, "  "" ");
Query OK, 1 row affected

mysql> select * from paper where id=3
;
+----+---------+
| id | content |
+----+---------+
|  3 |   "     |
+----+---------+
1 row in set
mysql> insert into paper values(4, '     ''     ');
Query OK, 1 row affected

mysql> select * from paper where id=4;
+----+-------------+
| id | content     |
+----+-------------+
|  4 |      '      |
+----+-------------+
1 row in set

  
由结果可知,官方说明的方法是有效的,不会造成语法错误。

  

五、结论

  
封装 Orm 的时候,需要特别注意转义的处理,且要把转义字符全都处理掉。
  
对于 gorose 的处理,我最后的方法是自行转义反斜杠和双引号,然后再走一次 gorose 的 Sql 拼接过程,这样就可以了。

添加的处理函数如下:

func AddSlashes(str string) string {
    str = strings.Replace(str, "\\", "\\\\", -1)
    str = strings.Replace(str, "\"", "\\\"", -1)
    return str
}

  
  
全文完
  
  

阅读感受肿么样?

平均分: / 5. 打分次数:

发表评论

电子邮件地址不会被公开。 必填项已用*标注