这个问题附上:

我给你留一个问题吧,如果表T中没有字段k,而你执行了这个语句 select * from T where k=1, 那肯定是会报“不存在这个列”的错误: “Unknown column ‘k’ in ‘where clause’”。你觉得这个错误是在我们上面提到的哪个阶段报出来的呢?

问题的备选答案,按照这一课里的内容,是个二选一的问题:分析器阶段,还是执行器阶段。这两个阶段做的事,我也从讲义里拿出来,放在这供阅读参考:

分析器

如果没有命中查询缓存,就要开始真正执行语句了。首先,MySQL需要知道你要做什么,因此需要对SQL语句做解析。

分析器先会做“词法分析”。你输入的是由多个字符串和空格组成的一条SQL语句,MySQL需要识别出里面的字符串分别是什么,代表什么。

MySQL从你输入的”select”这个关键字识别出来,这是一个查询语句。它也要把字符串“T”识别成“表名T”,把字符串“ID”识别成“列ID”。

做完了这些识别以后,就要做“语法分析”。根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个SQL语句是否满足MySQL语法。

如果你的语句不对,就会收到“You have an error in your SQL syntax”的错误提醒,比如下面这个语句select少打了开头的字母“s”。

mysql> ELECT * FROM t WHERE ID=1;

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ELECT * FROM t WHERE ID=1' at line 1

执行器

MySQL通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。

开始执行的时候,要先判断一下你对这个表T有没有执行查询的权限,如果没有,就会返回没有权限的错误,如下所示。

mysql> SELECT * FROM T WHERE ID=10;

ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'

如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。

比如我们这个例子中的表T中,ID字段没有索引,那么执行器的执行流程是这样的:

  1. 调用InnoDB引擎接口取这个表的第一行,判断ID值是不是10,如果不是则跳过,如果是则将这行存在结果集中;
  2. 调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
  3. 执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。

至此,这个语句就执行完成了。


网上对那一题的回答,能找到的版本是https://www.cnblogs.com/hoxis/p/10006871.html里提到的讨论,说是分析器。不过我对这个结论是有异议的。我给出我的分析过程:

我们考虑一个普通的数据库用户去对他没有权限的表进行SELECT操作的场景。建立这个场景的完整SQL建表语句附在下方。

root账户下执行以下的SQL语句:

DROP DATABASE IF EXISTS play;
CREATE DATABASE play;
USE play;
CREATE TABLE a(
    a1 CHAR(10)
);
CREATE TABLE b(
    b1 VARCHAR(10)
);
INSERT INTO a VALUES('aaa');
INSERT INTO b VALUES('bbb');
# I have another user named "college_manager" in my database system. Change the name to the user you have created
GRANT SELECT ON a TO college_manager@'%';

college_manager用户下执行这些SQL语句来产生实验结果:

USE DATABASE play;
# A validation
SELECT a1 FROM a;
# Error for unknown column
SELECT a2 FROM a;
# Note that college_manager doesn't have SELECT privilege on table b
# b1 exists, while b2 doesn't
SELECT b1 FROM b;
SELECT b2 FROM b;

关键的实验结果就是,对college_manager执行的最后两行SQL语句,全部都是报如下所示相同的错误:

ERROR 1142 (42000): SELECT command denied to user 'college_manager'@'localhost' for table 'b'

结合那篇网上的教程,如果Unknown Column的错误是在分析器阶段被侦测出来,那意味着在语法分析阶段,错误就应该被发现,那样的话,对b表的两次SELECT,应该是报不同的错误,因为权限判断是在执行器阶段,在分析器阶段之后,而对不存在的列,如果在分析器阶段就被发现,那应该是报列不存在的错误,而不是两次全部报没有SELECT权限。

如果带入在执行器阶段被侦测到错误的想法去分析,那整个过程就变得很make sense。执行器阶段首先判断执行SQL语句的用户,有没有对目标表执行那些SQL语句的权限。如果没有,就报权限错误。如果有,再打开表,通过存储引擎提供的接口,对表进行操作。而获知一个表是否存在某个列,是需要在打开一个表之后才能知道这个列到底是有没有。这样分析后,整个逻辑分析的链条就串起来了。