官方文档地址: 9.2.5 Function Name Parsing and Resolution
MySQL 支持内置(native
)函数、用户定义函数(UDFs
)和存储函数。本节描述服务器如何识别内置函数的名称是作为函数调用使用还是作为标识符使用,以及当不同类型的函数具有给定名称时,服务器如何确定使用哪个函数。
内置函数名称解析
解析器使用默认规则来解析内置函数的名称。可以通过启用IGNORE_SPACE
SQL 模式来更改这些规则。
当解析器遇到内置函数名称的单词时,它必须确定该名称是表示函数调用,还是表示对标识符(如表名或列名)的非表达式引用。例如,在下面的语句中,count
的第一个引用是一个函数调用,而第二个引用是一个表名:
SELECT COUNT(*) FROM mytable;
CREATE TABLE count (i INT);
解析器应该将内置函数的名称识别为仅在解析预期为表达式的内容时指示函数调用。也就是说,在非表达式上下文中,函数名允许作为标识符。
然而,一些内置函数有特殊的解析或实现考虑,因此解析器默认使用以下规则来区分它们的名称是作为函数调用使用,还是作为非表达式上下文中的标识符使用:
- 要在表达式中使用名称作为函数调用,名称和后面的
(
括号字符之间必须没有空格。 - 相反地,要使用函数名作为标识符,它后面不能紧跟着圆括号。
函数调用必须在名称和括号之间没有空格,这一要求只适用于有特殊考虑的内置函数。COUNT
就是这样一个名称。源文件sql/lex.h
列出了这些特殊函数的名称,下面的空格决定了它们的解释:由SYM_FN()
宏在symbols[]
数组中定义的名称。
下面列出了 MySQL 8.0 中受IGNORE_SPACE
设置影响的函数,并在sql/lex.h
源文件中作为特殊函数列出。您可能会发现最简单的做法是将无空格要求应用于所有函数调用。
- ADDDATE
- BIT_AND
- BIT_OR
- BIT_XOR
- CAST
- COUNT
- CURDATE
- CURTIME
- DATE_ADD
- DATE_SUB
- EXTRACT
- GROUP_CONCAT
- MAX
- MID
- MIN
- NOW
- POSITION
- SESSION_USER
- STD
- STDDEV
- STDDEV_POP
- STDDEV_SAMP
- SUBDATE
- SUBSTR
- SUBSTRING
- SUM
- SYSDATE
- SYSTEM_USER
- TRIM
- VARIANCE
- VAR_POP
- VAR_SAMP
对于在sql/lex.h
中没有特殊列出的函数,空格无关紧要。它们只有在表达式上下文中使用时才被解释为函数调用,否则可以自由地用作标识符。ASCII
就是这样一个名称。然而,对于这些未受影响的函数名,在表达式上下文中可能会有不同的解释:如果有一个给定的名称,func_name()
将被解释为内置函数;如果不是,func_name()
将被解释为用户定义的函数,如果存在同名的存储函数,则被解释为存储函数。
IGNORE_SPACE
SQL 模式可用于修改解析器如何处理对空格敏感的函数名:
禁用IGNORE_SPACE
后,当名称和后面的圆括号之间没有空格时,解析器将名称解释为函数调用。即使在非表达式上下文中使用函数名也会出现这种情况:
mysql> CREATE TABLE count(i INT);
ERROR 1064 (42000): You have an error in your SQL syntax ...
near 'count(i INT)'
为了消除错误并将名称作为标识符处理,可以在名称后面使用空格或将其写成带引号的标识符(或两者都使用):
CREATE TABLE count (i INT);
CREATE TABLE `count`(i INT);
CREATE TABLE `count` (i INT);
启用IGNORE_SPACE
后,解析器放宽了在函数名和后面括号之间不存在空格的要求。这为编写函数调用提供了更大的灵活性。例如,下面的函数调用都是合法的:
SELECT COUNT(*) FROM mytable;
SELECT COUNT (*) FROM mytable;
但是,启用IGNORE_SPACE
还有一个副作用,即解析器将受影响的函数名视为保留字,参见 9.3 关键字和保留字。这意味着名称后面的空格不再表示它用作标识符。该名称可以在函数调用中使用,后跟有或不带空格,但在非表达式上下文中会导致语法错误,除非它被引用。例如,在启用IGNORE_SPACE
的情况下,以下两条语句都因语法错误而失败,因为解析器将count
解释为保留字:
CREATE TABLE count(i INT);
CREATE TABLE count (i INT);
要在非表达式上下文中使用函数名,请将其写成带引号的标识符:
CREATE TABLE `count`(i INT);
CREATE TABLE `count` (i INT);
启用IGNORE_SPACE
SQL 模式,使用以下语句:
SET sql_mode = 'IGNORE_SPACE';
IGNORE_SPACE
也可以被其他组合模式启用,比如ANSI
,它的值中包含了IGNORE_SPACE
:
SET sql_mode = 'ANSI';
查看 5.1.11 服务器 SQL 模式,查看哪些复合模式启用了IGNORE_SPACE
。
为了最小化 SQL 代码对IGNORE_SPACE
设置的依赖,使用以下准则:
避免创建与内置函数同名的 UDFs 或存储函数。
避免在非表达式上下文中使用函数名。例如,这些语句使用count
(受IGNORE_SPACE
影响的函数名之一),因此,如果IGNORE_SPACE
被启用,它们在名称后面有或没有空格时都会失败:
CREATE TABLE count(i INT);
CREATE TABLE count (i INT);
如果必须在非表达式上下文中使用函数名,请将其写成带引号的标识符:
CREATE TABLE `count`(i INT);
CREATE TABLE `count` (i INT);
函数名称解析
以下规则描述了服务器如何在函数创建和调用时解析对函数名的引用:
- 内置函数和用户定义函数。
如果试图创建与内置函数同名的 UDFs,则会出现错误。 - 内置函数和存储函数。
可以创建与内置函数同名的存储函数,但要调用存储函数,必须使用模式名对其进行限定。例如,如果您在test
模式中创建了一个名为PI
的存储函数,将其作为test.PI()
调用,因为服务器解析PI()
时没有将限定符作为对内置函数的引用。如果存储函数名与内置函数名冲突,服务器将生成警告。可以使用SHOW WARNINGS
来显示警告。 - 用户定义函数和存储函数。
用户定义函数和存储函数共享相同的名称空间,因此不能使用相同的名称创建 UDF 和存储函数。
上述函数名解析规则对于升级到实现新的内置函数的 MySQL 版本有影响:
- 如果您已经用给定的名称创建了一个用户定义函数,并将 MySQL 升级到实现了一个具有相同名称的新内置函数的版本,那么 UDF 将变得不可访问。要纠正这个问题,请使用
DROP
函数来删除 UDF,使用CREATE
函数用一个不冲突的名称重新创建 UDF。然后修改任何受影响的代码以使用新名称。 - 如果一个新版本的 MySQL 实现了一个与现有存储函数同名的内置函数,你有两个选择:重命名存储的函数,以使用不冲突的名称,或更改对函数的调用,以便使用模式限定符(也就是说,使用
schema_name.func_name()
语法)。无论哪种情况,都要相应地修改任何受影响的代码。